Реализуем простейшую тяговую характеристику
Активные движущие силы и способ их задания
В RRS, чтобы заставить локомотив двигаться, необходимо подать на его колесные пары крутящий момент. В классе Vehicle существует специальная переменная Q_a, которая носит название вектор обобщенных активных сил. Этот вектор хранит NumAxis + 1 значений, а если вы помните, то NumAxis определяется в конфиге локомотива как число его осей.
Значение Q_a[0] описывает линейную силу, приложенную к кузову подвижной единицы, а значения с Q_a[1] по Q_a[NumAxis] — как раз таки крутящие моменты, приложенный к колесным парам, с номерами от 1 до NumAxis, от тягового привода.
Для чего нужна линейная сила Q_a[0]. Например, у вас появится желание сделать модель поезда с реактивной тягой (а такой поезд реально испытывался на отечественной железной дороге и кабину его вагона с реактивными двигателями можно увидеть в качестве памятника перед главной проходной Тверского вагоностроительного завода). Вот тогда нам потребуется передача активного тягового усилия через кузов. Через этот параметр можно передавать различные тестовые усилия. В обычных условиях он равен нулю и не используется, но кто знает для каких целей он может понадобится разработчику…
Стоит подать на колесные пары крутящий момент отличный от нуля, и наш локомотив поедет. Попробуем это реализовать.
Для начала переопределим в классе SimpleLoco метод step() вызываемый движком на каждом шаге интегрирования модели поезда. В нем происходит основная работа по вычислению параметров, обеспечивающих движение подвижного состава.
1 2 3 4 5 6 7 8 9 10 11 |
private: /// Заданный уровень тягового усилия double ref_traction_level; /// Обработка нажатия клавиш void keyProcess<code>(); /// Шаг моделирования систем локомотива void step(double t, double dt); }; |
В качестве параметров этот метод принимает от движка игры текущее время симуляции t и текущий шаг интегрирования по времени dt. Эти параметры необходимы нам для описания процессов, происходящих в подвижном составе, так как все процессы в оборудовании разворачиваются во времени, и движок любезно предоставляет нам все параметры отсчета времени, актуальные на данный момент. Реализуем метод step() написав такой код
1 2 3 4 5 6 7 8 9 |
void SimpleLoco::step(double t, double dt) { double torque = 5000.0; for (size_t i = 1; i < Q_a.size(); ++i) { Q_a[i] = ref_traction_level * torque; } } |
Что мы сделали? Мы задали некоторый постоянный момент torque равный 5000 Н · м, и приложили к каждой колесной паре нашего локомотива, с учетом текущего заданного уровня тяги, которым мы управляем с клавиатуры. Компилируем библиотеку, запускаем симулятор и теперь мы увидим, как наш локомотив, повинуясь командам с клавиатуры берет разбег и начинает движение!
Тяговая характеристика локомотива
Все что мы проделали конечно замечательно, но мы приняли очень грубое допущение о постоянстве крутящего момента, прикладываемого к колесной паре. На самом деле это не так, и этот момент определяется не только тем, какой уровень тягового усилия мы задали, но зависит от текущей скорости движения.
Тяговая характеристика локомотива — кривая, задающая зависимость силы тяги, реализуемой локомотивом от его скорости и различного рода ограничений (по сцеплению и по мощности, развиваемой приводом)
Типичная тяговая характеристика может выглядеть так, как показано на рисунке ниже
Идеальная тяговая характеристика имеет форму гиперболы, и на данном графике вторая половина кривой имеет гиперболическую форму. Почему? Потому, что любой тяговый привод имеет ограниченную мощность — никакой двигатель не в состоянии выдать мощность более той, на которую он рассчитан. Скажем, тяговые двигатели локомотива суммарно обеспечивают мощность равную P_nom. А мощность, как известно, определяется как произведение силы тяги на скорость
P_nom = F · v
Тогда максимальная сила тяги, которую может реализовать локомотив при данной мощности может быть вычислена так
F = P_nom / v
то есть сила тяги обратно пропорциональна скорости. Поэтому, для наиболее полного использования мощности привода все локомотивы имеют гиперболическую или близкую к ней форму тяговой характеристики.
Однако, кроме ограничения по мощности есть еще ограничения по сцеплению колес с рельсами и по электрическим параметрам тяговых машин, что не дает возможность реализовать большую тягу, даже при достаточной мощности привода. Это ограничение (очень условно) можно выразить в виде наклонной прямой, из которой состоит первая половина графика.
Теперь, зная все это, скорректируем код нашего локомотива, чтобы реализовать правильную форму его тяговой характеристики. Введем в наш класс ряд параметров
1 2 3 4 5 6 7 8 9 10 11 12 13 |
private: /// Заданный уровень тягового усилия double ref_traction_level; /// Максимальная реализуемая сила тяги, кН double F_max; /// Номинальная сила тяги (соотвествующая продолжительной мощности), кН double F_min; /// Номинальная скорость (соотвествующая часовой мощности), км/ч double V_nom; |
Инициализируем эти переменные некими значениями, соответствующими приближенно тяговой характеристики, типичной для реальных локомотивов
1 2 3 4 5 6 7 8 |
SimpleLoco::SimpleLoco(QObject *parent) : Vehicle (parent) , ref_traction_level(0.0) , F_max(450.0) , F_min(350.0) , V_nom(80.0) { } |
Теперь реализуем в классе приватный метод, вычисляющий силу тяги в зависимости от скорости
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
double SimpleLoco::trac_char(double v) { // Переводим номинальную скорость в м/с double v_nom = V_nom / Physics::kmh; double traction_force = 0; // Вычисляем силу тяги, в зависимости от величины // текущей скорости по отношению к номинальной. // Учитываем, что параметры заданной характеристики мы брали в кН, // а движку требуются величины в системе СИ, поэтому домножаем на 1000 // переводя килоньютоны в ньютоны if (abs(v) < v_nom) { traction_force = (F_max + (F_nom - F_max) * abs(v) / v_nom) * 1000.0; } else { traction_force = F_nom * v_nom * 1000.0 / v; } return traction_force; } |
Теперь у нас есть функция, позволяющая рассчитать силу тяги, которую может развить локомотив при данной скорости. Но это еще не всё — в методе step() пишем следующий код
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
void SimpleLoco::step(double t, double dt) { // Вычисляем силу тяги, которую реализует локомотив в данный момент double trac_force = ref_traction_level * trac_char(velocity); // Вычисляем момент, приходящийся на одну колесную пару double torque = trac_force * wheel_diameter / 2.0 / num_axis; // Задаем момент каждой колесной паре for (size_t i = 1; i < Q_a.size(); ++i) { Q_a[i] = torque; } } |
Поясню этот код. Во-первых, локомотив выдает тягу не максимальной величины, а в зависимости от того уровня, который мы задали клавишами, поэтому величину, получаемую из тяговой характеристики мы умножаем на этот уровень.
Во-вторых, на колесные пары мы подаем момент, реализуемый приводом этой колесной пары, а тяговая характеристика дает полную тягу локомотива. Поэтому эту полную тягу следует пересчитать в момент приходящийся на одну КП. Для этого всю тягу мы делим на число осей, а потом умножаем на радиус колеса, так как момент приложенный к КП и сила тяги, приходящаяся на эту ось находятся в соотношении
Mкп = Fкп · rколеса
Радиус колеса мы можем вычислить как половина диаметра, используя стандартную переменную wheel_diameter, в которой хранится диаметр колеса, заданный нами через конфиг. Аналогично, число осей локомотива хранится в переменной num_axis.
Теперь, если мы запустим симулятор и дадим тягу, то увидим, как наш локомотив разгоняется, причем довольно шустро. Выведем на экран, через отладочную строку несколько дополнительных параметров, для чего перенесем инициализацию отладочной строки в метод step()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
void SimpleLoco::step(double t, double dt) { // Вычисляем силу тяги, которую реализует локомотив в данный момент double trac_force = ref_traction_level * trac_char(velocity); // Вычисляем момент, приходящийся на одну колесную пару double torque = trac_force * wheel_diameter / 2.0 / num_axis; // Задаем момент каждой колесной паре for (size_t i = 1; i < Q_a.size(); ++i) { Q_a[i] = torque; } DebugMsg = QString("Время: %1 с Зад. тяга: %2 Скорость: %3 км/ч Сила тяги: %4 кН") .arg(t, 10, 'f', 1) .arg(ref_traction_level, 4, 'f', 2) .arg(velocity * Physics::kmh, 6, 'f', 2) .arg(trac_force / 1000.0, 6, 'f', 1); } |
Теперь внизу, в отладочной строке, можно увидеть, как меняется сила тяги по мере разгона, в точном соответствии с заданной нами характеристикой.