1. Дефолтная схема ..которая нам не подходит, но дает понять что такое Tractive Effort VS Speed , Tractive Effort vs Throttle и Max Force
Simulation -конфиг содержит разделы Tractive Effort VS Speed и Tractive Effort vs Throttle Как можно вычитать из мануалов в этих разделах указывается csv-файлы с парами значений: «скорость» - «процент тяги» и «позиция контроллера машиниста» - «процент тяги». По этим значениям определяется кусочно-линейные аппроксимации кривых тяга-скорость, тяга-позиция. Через эти кривые рассчитывается тяговое усилие в текущий момент времени при заданной скорости и заданном значении контрола Regulator. Итоговые коэффициенты перемножаются, плюс учитывается сила трения и скатывание. В кривых используется "процент тяги". Значение максимальной тяги локомотива в единицах физических величин задается отдельным полем Max Force в конфиге.
Естественный недостаток схемы в том, что эта схема никак не соответствует реальным тяговым характеристикам локомотива. На самом то деле на каждую позицию существует своя кривая тяга-скорость и часто на каждой позиции она имеет уникальный вид. А кривой «тяга-позиция» в справочных данных найти еще ни разу не удалось. Наверное потому, что позиция – это просто условный порядковый номер выбранной электрической схемы соединений тяговых двигателей (ТЭД). В электровозах постоянного тока – это и указание на схему их соединения – сириесное, сириес-параллельное, параллельное, и в то же время указание на величину сопротивления, включенного последовательно с якорем тягового двигателя при реостатном регулировании напряжения на якоре. На тепловозе с электропередачей – позиция – это опять же указание на схему соединения ТЭД и одновременно указание на ток возбуждения главного генератора (управляемый непосредственно от контроллера либо через амплистат). В общем, позицию контроллера машиниста никак нельзя сравнить с педалью газа в автомобиле, на что получилось похожа кривая Tractive Effort vs Throttle. Но, благодаря тому, что значением контрола 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, за уточнения по дефолтной схеме
|