..которая нам не подходит, но дает понять что такое Tractive Effort VS Speed , Tractive Effort vs Throttle и Max Force
Но, благодаря тому, что значением контрола Regulator можно управлять скриптом была придумана:
2. Нормальная схема №1.
Автор ее мне доподлинно неизвестен, но первую реализацию в железе (т.е. скрипте) я увидел за авторством местного товарища supermax (спасибо!), который давным давно благоразумно послал весь этот симуляторный цирк, и кстати правильно сделал.
Кривые Tractive Effort VS Speed и Tractive Effort vs Throttle задаются в csv-файле константами.
- Код: Выделить всё
Tractive Effort VS Speed:
0,100
160,100
Здесь 0 и 160 – это скорость равная (0 км/ч) и 160 (км/ч)
- Код: Выделить всё
Tractive Effort vs Throttle
0,100
100,100
Аналогично Здесь 0 и 100 – это процент контролаRegulator (0 % и 100 %)
Обе кривые получаются параллельными оси Х прямыми (т.е. теми самыми константами).
Контроллеру машиниста и органам управления ослаблением поля присваивается по отдельному контролу. Дефолтный Regulator напрямую не используется.
В скрипте создаются таблицы "тяга-скорость" по числу позиций и для каждого режима ослабления поля.
Из этих таблиц для текущего значения скорости и выбранной позиции вычисляется значение тяги, которое присваивается контролу Regulator и двигает то, что должно двигаться.
Так получаются разные значения тяги которые правдоподобно определяются и выбранной позицией и текущей скоростью и имеют возможности реализации всевозможных схем управления ТЭД.
Единственный недостаток схемы – нельзя управлять тягой двигателя красненькой ручкой на HUD и само положение ручки будет плавать автоматически в зависимости от рассчитанного значения контрола Regulator.
3. Реализация схемы № 1 в скрипте
Для аппроксимации каждой кривой "тяга-скорость" она делится на отрезки, на каждом из них она может быть представлена прямой линией F = k*v+b, построенной по известным оконечным значениям отрезка (опорным точкам).
На каждом отрезке нужно найти коэффициенты k и b. В общем случае для этого используются известные значения v и F и система уравнений:
F1 = k*v1+b
F2 = k*v2+b
Откуда k = (F1-F2)/(v1-v2) и b = F1 – v1 * k
Уравнение F(v) = k * v + b примет в итоге вид: F(v) = F1 + (F1-F2)*(v-v1)/(v2-v1)
v – текущая скорость.
Эту формулу можно применить для любого из отрезков любой кривой, но нужно каждому значению v (1/2) сопоставить значение F(1/2). Для каждого отрезка, каждой кривой.
Из формулы F(v) видно, что в ней используются индексы 1 и 2. Значению v1 соответствует значение F1, v2 соответствует F2.
В разделенной на отрезки кривой смежные отрезки имеют общие точки. Поэтому для каждой позиции всю кривую можно задать через две таблицы с одинаковым количеством индексов (по числу опорных точек кривой). В первой в порядке возрастания содержатся опорные значения координаты скорости. Во второй – соответствующие им координаты тяги.
Для расчета тяги сначала в первой таблице для заданной контроллером машиниста позиции происходит поиск смежных значений скорости, между которыми находится текущая скорость. Они подставляются в формулу как значения v1 и v2.
Определяются их индексы.
Из второй таблицы выбираются значения с этими же индексами и подставляются в формулу как значения F1 и F2.
Рассчитывается F(v) при текущей скорости и передается как значение контрола Regulator.
3. Пример. Электровоз ЧС2
Тяговые характеристики:
Позиции 1-20 сириесного соединения ТЭД. 20-я позиция ходовая (без реостатов последовательно с ТЭД)
Позиции 21 – 33 сириес-параллельного соединениия ТЭД. 33-я ходовая
Позиции 34 – 42 параллельного соединения ТЭД. ходовая – 42-я.
На каждой ходовой позиции по пять ступеней ослабления поля обмоток возбуждения (на графике указаны только для 42-й позиции)
Код:
- Код: Выделить всё
-- Множитель применяемый для многосекционных локомотивов
gTractiveFactor = 1 -- единица т.к. односекционный локомотив. Для двухсекционных было бы значение 0.5, если справочные данные указывались бы для двух секций
-- значение, соответствующее 100% тяги по графику
gMaxTractiveValue = 50
-- кривые на графике ограничены значением 35 тс, но в физическом смылсе они бесконечны до пределов срабатывания защиты от перегрузок по току поэтому за 100% тяги принято значение 50 тс.
-- это значение указано в конфиге как Max Force. Если лок слишком рьяно рвет с места, то изменением одного значения это легко изменить.
-- таблица, в которой хранятся параметры, описывающие тяговые характеристики
tRegulator = {
Value = 0, -- текущее значение, которое присваивается контролу Regulator. При старте сценария равно 0
Position = 0, -- текущая позиция контролллера машиниста в кабине. При старте сценария равно 0
NoSMode = 0, -- статус режима "без С" если он реализован в скрипте. Если не актуален, то равен 0
Shunt = 0, -- текущая позиция ручки шунта
X0 = 0, -- текущая начальная координата по оси Х (скорость) отрезка кривой тяга-скорость. При старте сценария равна 0
X1 = 0, -- текущая конечная координата по оси Х (скорость) отрезка кривой тяга-скорость. При старте сценария равна 0
Y0 = 0, -- текущая начальная координата по оси Y (тяга) отрезка кривой тяга-скорость. При старте сценария равна 0
Y1 = 0 -- текущая конечная координата по оси Y (тяга) отрезка кривой тяга-скорость. При старте сценария равна 0
}
-- Таблицы, в которых хранится численное представление кривыъ тяга-скорость для каждой позиции
-- объявление "глобальной таблицы" индексы этой таблицы означают номер позиции контроллера машиниста + 100 * номер позиции ослабления поля (единая индексация промежуточных и шунтовых позиций)
tTVS = {}
-- каждый элемент таблицы tTVS тоже таблица с двумя именованными ключами
-- S = таблица, содержащая индексированные значения скорости в км/ч из графика
-- T = таблица, содержащая индексированные значения тяги в тс из графика
-- количество индексов в паре S-T одинаковое
-- позиция 1:
tTVS[1] = { S = {0, 28.6}, -- таблица скорости
T = {6.4, 0 } } -- таблица тяги
-- позиция 2:
tTVS[2] = { S = {0, 10, 20, 33.2}, -- таблица скорости
T = {10.5, 6.9, 3.8, 0 } } -- таблица тяги
-- и т.д.
tTVS[3] = { S = {0, 10, 20, 36 },
T = {14.1, 9.5, 5.4, 0 } }
tTVS[4] = { S = {0, 10, 20, 30, 38.9},
T = {17.8, 12, 6.7, 2.8, 0 } }
tTVS[5] = { S = {0, 10, 20, 30, 41.6},
T = {20.8, 14.2, 8, 3.7, 0} }
tTVS[6] = { S = {0, 10, 20, 30, 43.4},
T = {24, 16, 9, 4.3, 0 } }
tTVS[7] = { S = {0, 10, 15, 20, 25, 30, 35, 40, 47.3},
T = {29, 19, 14.2, 10.6, 7.7, 5.1, 3.2, 1.6, 0 } }
tTVS[8] = { S = {0, 10, 15, 20, 25, 30, 35, 40, 45, 50},
T = {31.2, 21, 15.7, 11.6, 8.3, 5.7, 3.5, 1.9, 0.8, 0} }
tTVS[9] = { S = {3, 10, 15, 20, 25, 30, 35, 40, 45, 50, 57.1},
T = {32.5, 23.5, 17.5, 12.8, 9.2, 6.4, 4.2, 2.5, 1.3, 0.4, 0} }
tTVS[10] = { S = {5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 65},
T = {32, 25.1, 18.9, 13.9, 10, 6.8, 4.7, 3.1, 1.7, 0.9, 0} }
tTVS[11] = { S = {7, 10, 15, 20, 25, 30, 35, 40, 45, 50, 67},
T = {33.8, 28.6, 20.6, 14.9, 10.6, 7.2, 5, 3.3, 2, 1.1, 0} }
tTVS[12] = { S = {10, 11.2, 14.4, 20, 24, 30, 40, 45, 50, 60, 72},
T = {33.6, 30.2, 24.2, 16.4, 12.9, 7.4, 3.5, 2.2, 1.4, 0.8, 0} }
tTVS[13] = { S = {13, 16, 20, 25, 30, 35, 40, 45, 50, 60, 74},
T = {31.6, 22.8, 17.9, 12, 8.3, 5.7, 3.9, 2.6, 1.8, 1.1, 0} }
tTVS[14] = { S = {14, 16.5, 20, 25, 30, 35, 40, 45, 50, 60, 80.3},
T = {31.8, 25, 19.6, 13, 9, 6.1, 4.2, 3, 2, 1.3, 0} }
tTVS[15] = { S = {17, 20, 22.5, 26, 30, 35, 40, 45, 50, 60, 87.7},
T = {32, 23, 18.7, 13.6, 9.7, 6.4, 4.4, 3, 2.2, 1.5, 0} }
tTVS[16] = { S = {18.2, 20, 23, 27, 30, 35, 40, 45, 50, 60, 98},
T = {32, 26.2, 19.2, 13.6, 10.3, 6.6, 4.7, 3.3, 2.6, 1.7, 0} }
tTVS[17] = { S = {20.2, 23.8, 27, 30, 35, 40, 45, 50, 60, 105},
T = {32, 22.1, 15, 11.4, 7.3, 5, 3.6, 3, 2, 0} }
tTVS[18] = { S = {22, 24, 26, 30, 35, 40, 45, 50, 60, 113},
T = {32, 25, 17.1, 12.1, 7.5, 5.3, 4, 3.3, 2.3, 0} }
tTVS[19] = { S = {23, 25.5, 27, 30, 33, 37, 40, 45, 50, 60, 115},
T = {32, 22.8, 17.1, 12.9, 10, 7.2, 5.7, 4.2, 3.5, 2.5, 0} }
-- 20-я (сириесная) ходовая позиция
tTVS[20] = { S = {24, 25, 27, 30, 34, 37, 40, 45, 50, 70, 120},
T = {32, 25, 20, 13.6, 10, 7.6, 6, 4.7, 3.8, 2.1, 0} }
-- и т.д.
tTVS[21] = { S = {0, 20, 40, 50, 60, 80, 125.1},
T = {24.4, 16.3, 8.9, 5.7, 3.6, 2.2, 0} }
tTVS[22] = { S = {0, 20, 40, 50, 60, 80, 127},
T = {31.8, 21, 10.9, 7.1, 5, 3, 0} }
tTVS[23] = { S = {9.2, 20, 30, 40, 50, 60, 80, 129},
T = {31.8, 24.4, 17.9, 12.8, 8.6, 6, 3.2, 0} }
tTVS[24] = { S = {19, 25, 30, 40, 50, 60, 80, 132},
T = {31.6, 26.2, 22.2, 15.7, 10.6, 7, 3.4, 0} }
tTVS[25] = { S = {24, 30, 40, 50, 60, 80, 136},
T = {32, 26.1, 18.3, 12.4, 8.2, 3.6, 0} }
tTVS[26] = { S = {24, 30, 40, 50, 60, 80, 136},
T = {32, 26.1, 18.3, 12.4, 8.2, 3.6, 0} }
tTVS[27] = { S = {32, 40, 45, 50, 55, 60, 70, 80, 90, 100},
T = {32, 22.3, 18.2, 14.6, 11.9, 9.8, 6.4, 3.9, 2.5, 1.5} }
tTVS[28] = { S = {37, 40, 45, 50, 55, 60, 70, 80, 90, 100},
T = {32, 25.9, 20.7, 16.2, 13.1, 10.6, 7, 4.5, 3, 2} }
tTVS[29] = { S = {41, 45, 50, 55, 60, 70, 80, 90, 100},
T = {32, 25.3, 19.2, 15.2, 12.1, 8.2, 5.1, 3.4, 2.7} }
tTVS[30] = { S = {44, 47, 50, 55, 60, 70, 80, 90, 100, 142},
T = {32, 24.9, 21.6, 16.4, 13, 8.4, 5.6, 3.8, 3.5, 0} }
tTVS[31] = { S = {46, 50, 55, 60, 65, 70, 80, 90, 100, 160},
T = {32, 24.5, 18.2, 14, 10.9, 8.8, 5.9, 4.3, 3, 0} }
tTVS[32] = { S = {48, 50, 55, 60, 65, 70, 80, 90, 100, 160},
T = {32, 27.9, 20.2, 15.2, 11.5, 9.3, 6.3, 4.8, 3.4, 0.4} }
-- 33-я (сириесно-параллельная) ходовая позиция
tTVS[33] = { S = {52, 54, 57, 60, 65, 70, 75, 80, 90, 100, 120, 160},
T = {32, 26.9, 19, 16.1, 12.6, 10, 8.3, 7, 5.4, 4, 3.4, 1.1} }
-- и т.д.
tTVS[34] = { S = {32, 50, 70, 80, 90, 110, 130, 160},
T = {32, 22.2, 13.3, 9.9, 7.5, 4.3, 2.3, 2} }
tTVS[35] = { S = {39, 60, 70, 80, 90, 100, 110, 130, 160},
T = {32, 20.1, 15.3, 11.4, 8.2, 6.3, 4.8, 2.7, 2.2} }
tTVS[36] = { S = {47, 60, 70, 80, 90, 100, 110, 130, 160},
T = {32, 23.3, 17.5, 12.9, 9.3, 6.8, 5.2, 3, 2.4} }
tTVS[37] = { S = {53, 70, 80, 90, 100, 110, 130, 160},
T = {32, 19.7, 14.4, 10.3, 7.2, 5.5, 3.4, 2.6} }
tTVS[38] = { S = {57, 70, 80, 90, 100, 110, 130, 160},
T = {32, 21.9, 15.7, 11.3, 8, 6.1, 3.8, 2.8} }
tTVS[39] = { S = {62, 70, 80, 90, 100, 110, 130, 160},
T = {32, 25.3, 17.9, 12.5, 8.6, 6.7, 4.2, 3} }
tTVS[40] = { S = {67, 70, 80, 90, 100, 110, 130, 160},
T = {32, 28.6, 19.8, 14, 9.6, 7.3, 4.5, 3.2} }
tTVS[41] = { S = {73, 80, 85, 90, 100, 110, 130, 160},
T = {32, 23.3, 19.2, 15.8, 10.7, 7.9, 4.9, 3.4} }
-- последняя 42-я (параллельная) ходовая позиция из графика без шунтов
tTVS[42] = { S = {75, 80, 85, 90, 95, 100, 105, 110, 120, 140, 160},
T = {32, 25.8, 21.1, 17.2, 14, 11.7, 9.8, 8.5, 6.5, 4.5, 3.6} }
-- кривые для 42-й позиции при задействовании ослабления возбуждения
-- 42-я позиция, 1-я ступень ослабления
tTVS[142] = { S = {81, 85, 90, 95, 100, 110, 130, 160},
T = {32, 25.8, 20.9, 17.2, 14.2, 10.5, 6.6, 4} }
-- 42-я позиция, 2-я ступень ослабления
tTVS[242] = { S = {87, 90, 94, 100, 110, 120, 130, 140, 160},
T = {32, 26.3, 21.8, 17.2, 12.8, 10.2, 8.2, 6.8, 5.1} }
-- и т.д.
tTVS[342] = { S = {94, 97, 100, 105, 110, 120, 130, 150, 160},
T = {32, 27, 22.6, 18.9, 16.7, 12.4, 10.1, 7.3, 6.4} }
tTVS[442] = { S = {104, 106, 110, 115, 120, 130, 140, 160},
T = {32, 26.3, 21.4, 18.2, 15.7, 12.2, 9.9, 7.8} }
tTVS[542] = { S = {115, 117, 120, 125, 130, 140, 150, 160},
T = {32, 26.8, 22.3, 18.6, 16.4, 12.8, 10.5, 8.9} }
------------------------------------------------------------
-- функция, вызываемая из функции Update(t) для расчета тяги
-- технически эта функция содержится в таблице tRegulator
function tRegulator:Calculation()
-- неиспользуемый в данном случае код (подсчет количества секций локомотива)
--gSections = Call("*:SetControlValue", "Number_sections", 0)
-- self означает, что используется ключ самой таблицы tRegulator
self.NoSMode = 0--Call("*:GetControlValue", "tm_mode_S", 0)
self.Shunt = 0 --Call("*:GetControlValue", "lvr_Shunt", 0)
-- расчет индекса таблицы tTVS
self.Position = self.Shunt * 100 + self:Shift(Call("*:GetControlValue", "Throttle", 0 )) -- тут есть хитрость (см. в конце)
if self.Position == 0 then
-- если схема не собрана (индекс = 0, положение контроллера машиниста 0, ручки шунтов 0) значение тяги 0
self.Value = 0
-- если схема собрана
else
-- в таблицу gS (не важно существовала ли она ранее или нет в коде) передается таблица скорости для заданной позиции
gS = tTVS[self.Position].S
-- в таблицу gT(не важно существовала ли она ранее или нет в коде) передается таблица тяги для заданной позиции
gT = tTVS[self.Position].T
-- опреляется число индексво в таблице gS, т.е то же количество индексов что и в таблицах tTVS[текущая позиция].S, tTVS[текущая позиция].T
gN = table.getn(gS)
--(в скрипте Update(time) gCurrentSpeed = Call("*:GetControlValue", "SpeedometerKPH", 0)) текущая скорость
-- если текущая скорость больше чем скорость для последнего индекса из таблицы скорости, то опорные точки берутся как для последнего отрезка
if gCurrentSpeed >= gS[gN] then self.X0, self.X1, self.Y0, self.Y1 = gS[gN-1], gS[gN], gT[gN-1], gT[gN]
-- если текущая скорость меньше чем скорость для первого индекса из таблицы скорости, то опорные точки берутся как для первого отрезка
elseif gCurrentSpeed < gS[1] then self.X0, self.X1, self.Y0, self.Y1 = gS[1], gS[2], gT[1], gT[2]
-- иначе перебираются все отрезки скоростей из таблицы скорости
else
for i = 1, gN - 1 do
-- если текущая скорость между значениями скоростей с соседними индексами в таблице то
-- в качестве опорных значений скосростей выбираются эти значения
-- в качестве опорных значений тяги выбираются значения с такими же индексами в таблице тяги
if gS[i] <= gCurrentSpeed and gCurrentSpeed < gS[i+1] then self.X0, self.X1, self.Y0, self.Y1 = gS[i], gS[i+1], gT[i], gT[i+1] end
end -- for
end -- if
-- текущее значение тяги рассчитывается по выбранным опорным значениям скорости и тяги и если меньше нуля, то становится равным нулю
self.Value = math.max(0, self.Y0 + (self.Y1 - self.Y0)*(gCurrentSpeed - self.X0) / (self.X1 - self.X0))
-- и приводится к максимальному значению тяги
self.Value = self.Value * gTractiveFactor / gMaxTractiveValue
end -- if
-- контролу Regulator присваиваетс рассчитанное значение
Call("*:SetControlValue", "Regulator", 0, self.Value)
end -- func
-- контроллер ЧС2 имеет при смене типа соединений ТЭД промежуточные позиции в конфиге они фиксируемые и всего их 47 и на них расчет тяги не применяется
-- поэтому для более простого сопоставления графика тяги и индексированных по позициям таблиц используется сведение номера позиции из конфига к позиции из графика
function tRegulator:Shift(p)
if p <=1 then p = 0
elseif 2 <= p and p <= 21 then p = p - 1
elseif 22 <= p and p <= 23 then p = 0
elseif 24 <= p and p <= 36 then p = p - 3
elseif 37 <= p and p <= 38 then p = 0
elseif 39 <= p and p <= 47 then p = p - 5
end
return p
end -- func
P.S. От оригинального скрипта Макса тут ничего не осталось - только идея, ибо, во-первых, он был для ВЛ10К-690, а, во-вторых, этот по написанию и оптимизирован, и практически унифицирован.
P.P.S. Отдельное спасибо, Skif, за уточнения по дефолтной схеме