Информация: Уважаемые посетители! В течение нескольких месяцев на форуме существовала проблема с регистрацией новых пользователей, о которой администрации стало известно недавно. Если вы ранее пытались зарегистрироваться на форуме, но не получили на ваш e-mail письмо с ссылкой для подтверждения регистрации, просим вас зарегистрироваться повторно. Приносим извинения за доставленные неудобства. Если вы все еще испытываете проблемы с регистрацией на форуме, обратитесь за помощью на e-mail: mr.angelo@railroadsim.net

LuaScript - для RS

Другие вопросы и проблемы разработки дополнений

Re: LuaScript - для RS

Сообщение i2GR » 12.10.2016, 15:37

Крутой костыль!
Только мне казалось, что в случаях если между локами вагон, то второй лок в середине надо делать IsDeadEngine в сценарии. Или он все равно "тянуть" будет?
З.Ы. и применять то не на чем, как всегда
Аватара пользователя
i2GR
 
Сообщения: 417
Зарегистрирован: 04.09.2008, 16:59
Благодарил (а): 159 раз.
Поблагодарили: 256 раз.
Блог: Просмотр блога (4)
Имя: Игорь

Re: LuaScript - для RS

Сообщение радиомастер » 12.10.2016, 17:12

i2GR писал(а):Или он все равно "тянуть" будет?

тянуть не будет , но вот и звуков наката тоже не будет , косяк такой, только стыки (сами удары)
Аватара пользователя
радиомастер
 
Сообщения: 2057
Зарегистрирован: 23.10.2010, 18:42
Откуда: Макеевка
Благодарил (а): 971 раз.
Поблагодарили: 1467 раз.
Блог: Просмотр блога (3)
Играю в: RailWorks
Роль: Разработчик
Имя: Костик

Re: LuaScript - для RS

Сообщение Света » 18.10.2016, 08:59

i2GR писал(а):...в случаях если между локами вагон, то второй лок в середине надо делать IsDeadEngine в сценарии.
Не обязательно, я помню, как через нашу станцию ходили составы лок+лок+вагоны+лок+лок+вагоны. Тогда ещё 2ТЭ10л использовались. В таком случае надо, чтобы тянули все 4 лока, а "сотрудничество" по переданных данных было только между парами.
Аватара пользователя
Света
 
Сообщения: 136
Зарегистрирован: 18.06.2016, 19:38
Благодарил (а): 63 раз.
Поблагодарили: 153 раз.
Играю в: RailWorks
Роль: Разработчик

Re: LuaScript - для RS

Сообщение Света » 05.11.2016, 14:08

Всем доброго дня :)

Очередная заготовка, облегчившая мне написание скрипта, которой я хочу поделиться. Это скрипт-модуль, предназначенный для управления яркостью источниками света (ИС).

Назначение
Управлять яркостью ИС можно, воздействуя на параметры каналов цветности RGB. Если установить значение всех каналов в 0, источник будет полностью погашен, если же в значения, заданные в конфиге ИС - будет светить максимально заданной яркостью и цветом.
Установкой значений RGB в промежуточные значения можно изменять яркость ИС от 0 до максимума.

Основные, ИМХО, трудности в этом методе управления:
1. Значения каналов одной модели практически всегда различные. Для того, чтобы при изменении яркости ИС не потерять баланс (и цвет), все значения каналов нужно изменять синхронно и пропорционально.
2. Необходимо иметь точные данные о установленных в конфиге значениях RGB - это определяет максимальные уровни яркости и цветовой баланс.
3. Управление ИС таким методом подразумевает отсчет временных интервалов. А это автоматически требует создания счетчиков-переменных, их инкрементирование/декрементирование, слежение за результатом, разрешение/запрет перезапуска и другие "прелести" ручной таймеризации в программе.
4. Нельзя обойтись без защитных механизмов, которые заблокируют обработку ИС в случае серии однотипных команд (если, например, будет сформировано 10 команд "выключить ИС", то должна обработаться только первая, остальные должны игнорироваться, причем, чем раньше, тем оптимальнее.
5. Все вышеуказанное требует создания достаточно запутанной схемы, которая для одного ИС ещё приемлема, но с увеличением количества ИС сложность и запутанность растет в разы.

Эти моменты побудили меня разработать логически завершенный блок, который взял бы всю нагрузку на себя. Возможность изменять яркость ИС вызвала желание сделать этот процесс плавным и управляемым по времени. Это получилось. К тому же, мне хотелось сделать простым добавление новых ИС под его "опеку". Это мне также удалось.


Принцип работы
При запуске программы в функции StartSetupLight () происходит начальная инициализация всех переменных. Также, используя данные из таблицы TabNames (где перечислены имена конфигов ИС как дочерних элементов), производится поиск и загрузка ключевых данных (в данном случае значения RGB).
Также создаются таблицы, используемые в процессе работы.
Одна из таблиц - LampSpeed. Здесь размещены имена ИС и их время на включение (ig) и выключение (qu) в нормальных условиях.

Для того, чтобы сделать управление каналами ИС пропорциональным и синхронным, логично использовать один множитель, который изменял бы свое значение от 0 до 1 и на который в процессе обработки следует умножать максимальное значение каждого канала.
Это переменная factor. По сути, её значение (от 0 до 1) определяет состояние ИС от полностью выключенного до включенного на полную яркость. Это значение постоянно хранится в таблице LampData.Factor_c[_имя ИС_] (c = current = "текущий").

Для того, чтобы один блок мог обрабатывать неограниченное количество ИС и при этом был независим от их параметров, обработчик использует формулу, в которую подставляет все данные.

Логически блок обработчика состоит из двух веток: function Lamp_Set (name, level, time) (эта функция запускает процесс изменения яркости) и function LampUpdate (time) (обработка изменения ИС во времени).

Lamp_Set (name, level, time)
При вызове этой функции первым делом подгружается текущее состояние ИС и сравнивается с заданным при вызове. Если эти значения равны (дубль команды), происходит завершение запуска обработки. В противном случае производится перезапуск ИС.
Для того, чтобы перезапустить ИС, сначала сравниваются текущее состояние и заданное. Это указывает, в какую сторону надо изменять factor - увеличивать или уменьшать яркость. Затем создаются контрольные точки - текущее состояние сохраняется как начальная точка (LampData.Factor_a[ch_n]), а требуемый уровень (level) - как конечная (LampData.Factor_b[ch_n]). Далее запускается таймер - на время, заданное в команде, или, если время не задано - на время по умолчанию, указанное в таблице LampSpeed. Дефолтное время также будет загружено в том случае, если значением time будет "none".
Также сбрасывается флаг LampSet - это будет свидетельствовать, что есть ИС, требующий обработку.

LampUpdate (time)
В первую очередь проверяется состояние флага LampSet. Если он установлен, это значит, что все ИС в статичном состоянии. В обработке нет необходимости, поэтому выход.
Если же флаг сброшен, значит есть как минимум 1 ИС, требующий просчета. В таком случае первым делом флаг LampSet устанавливается (авансом, чтобы если это ложный сбой, то при следующем входе в эту функцию не отрабатывать её снова) и запускается цикл, который последовательно переберет все ИС, прописанные в таблице TabNames.
В процессе перебора для каждого ИС считывается его состояние, если ИС статичен (status = "on" или status = "off"), этот ИС не обрабатывается. Иначе флаг LampSet сбрасывается (подтверждая, что есть необработанные ИС), и, в зависимости от статуса ИС ("ignition" или "quenching", то есть увеличение или снижение яркости) выполняется необходимая ветка, где на основании состояния таймера и ключевых точек производится расчет нового значения множителя factor. Если нужная точка яркости достигнута, это отмечается в статусе ИС, а канал таймера уничтожается за ненадобностью.
Дальше максимальные значения каналов цвета умножаются на множитель и передаются движку на управление ИС. Новое состояние ИС сохраняется.

Примечания
1. Количество подконтрольных ИС ограничено только здравым смыслом;
2. Яркость ИС можно "сдвинуть" в любую сторону в пределах от 0 до 1 (от минимума к максимуму) независимо от текущего;
3. Яркость может изменяться с любой скоростью, а если скорость не задана (или = "none"), подхватывается дефолтное значение;
4. Если все ИС статичны, обрабатывается всего 2 строки функции LampUpdate (time) за кадр;
5. Время изменения яркости стабильно, не зависит от fps. Но на очень слабых ПК не исключено ступенчатое изменение яркости;
6. Главный недостаток обработчика - время изменения яркости не зависит от начального состояния ИС. То есть, при заданном по умолчанию времени на выключение 0.2 сек, ИС будет выключатся именно 0.2 секунды, независимо от того, включен он на полную яркость или на 50%. Мне это не показалось критичным. В то же время, это исправимо;
7. Все ИС совершенно независимы друг от друга.


Подключение к скрипту и подготовка к использованию
Модуль можно разместить в любом удобном месте. В основном скрипте делается его подключение:
Код: Выделить всё
require "Assets/[...]/light_driver_script_v21.lua"
Если модуль скомпилирован, *.lua заменить на *.out.

В функцию инициализации основного скрипта необходимо вписать вызов функции инициализации:
Код: Выделить всё
StartSetupLight ()

А в функцию Update (time) вписать вызов функций таймера и обработчика:
Код: Выделить всё
   LampUpdate (time)
   Timer (time)


Дальше необходимо заполнить таблицу TabNames информацией о используемых ИС. Первое поле таблицы - это имя ИС, которое будет использоваться в командах. Любое удобное. Второе поле - это имя чайлда, задаётся в блоке child в поле "ChildName" основного конфига.
Затем для всех указанных в TabNames ИС нужно указать время на розжиг и на гашение в таблице LampSpeed.

В качестве примера я привожу 2 ИС, которые имеют имена в конфиге "Light_1" и "Light_2", я их буду называть в скрипте как Lampa_1 и Lampa_2, время розжига у обеих 0.2 сек, гашения - 0.3.


Использование
Для управления ИС используется вызов
Код: Выделить всё
   Lamp_Set (name, level, time),
где
   name - имя ИС, заданное Вами;
   level - требуемый уровень яркости, от 0 до 1;
   time - время, отведенное на это.


Время задается в секундах, допускаются дробные значения. Если время не задано или указан параметр "none", будет использовано время, заданное по умолчанию.


В модуле использована функция Timer (data_1, data_2, data_3), описанная мной раньше. Если Вы уже использовали её у себя, из модуля её нужно удалить. Это строки 21, 131 - 168.
Использование таймера не ограничивается только управлением ИС, его можно применять для контроля времени в других частях скрипта. Подробнее здесь.

Скрипт оттестирован.
За помощь в разработке формулы искренне благодарю пользователя maestro.
Также за поддержку и тестирование благодарю пользователя Cross из railworks2

Скрипт здесь.

С благодарностью приму советы, отзывы и пожелания.
Аватара пользователя
Света
 
Сообщения: 136
Зарегистрирован: 18.06.2016, 19:38
Благодарил (а): 63 раз.
Поблагодарили: 153 раз.
Играю в: RailWorks
Роль: Разработчик

Re: LuaScript - для RS

Сообщение Skif » 05.11.2016, 17:44

Есть подозрение, что синхронно с цветом источника надо менять и радиусы зоны светимости! Иначе эффект будет не полным.
Skif
 
Сообщения: 2593
Зарегистрирован: 01.10.2009, 17:42
Благодарил (а): 235 раз.
Поблагодарили: 706 раз.
Блог: Просмотр блога (2)
Имя: Дмитрий

Re: LuaScript - для RS

Сообщение Света » 05.11.2016, 19:48

Без проблем! Для этого надо добавить всего 3 - 4 строчки, но, чтобы в полной мере оценить эффект, нужна темная ночь в игровом мире, чего у меня, к сожалению, нет. А я подозреваю, что линейный закон для корректировки радиуса не подойдет. Если кто-нибудь захочет провести тестирования и имеет качественный ИС, реальную ночь в ТС, умеет работать со скриптами и доверяет своему зрению, пишите в ЛС, я покажу, как сделать необходимые правки.
Аватара пользователя
Света
 
Сообщения: 136
Зарегистрирован: 18.06.2016, 19:38
Благодарил (а): 63 раз.
Поблагодарили: 153 раз.
Играю в: RailWorks
Роль: Разработчик

Re: LuaScript - для RS

Сообщение Света » 04.12.2016, 12:12

Всем доброго дня :)

Представляю вниманию разработчиков функцию TIMER v2.0, предназначенную для вставки в скрипт.

С назначением и общим описанием первой версии функции можно ознакомиться здесь.
Отличие от первой версии: добавлен режим остановки счета (пауза).

Адаптация в скрипт
Функция вставляется в конец скрипта. Никакие подгонки/правки не нужны.

Для инициализации рабочей таблицы, вписываем её в функцию Initialise():
Код: Выделить всё
-- Data TIMER
TabTimerData = {}
TabTimerStatus = {}


Для обеспечения счёта периодически делаем вызов из функции Update ( time ):
Код: Выделить всё
 Timer (time)

Всё, таймер прописан и готов к работе.


Использование/вызов
Для того, чтобы создать канал таймер, надо придумать ему любое удобное имя и отправить запрос в его функцию (например, timer_1) вместе со значением нужной задержки в секундах (например, 15 сек):
Код: Выделить всё
Timer ("timer_1", 15)

Важно: пока таймер отсчитывает выдержку, его рестарт такой командой невозможен. Это сделано умышленно. Если нужно перезапустить отсчет сначала (а также снять таймер с паузы с обновлением задержки), применяется другая команда:
Код: Выделить всё
Timer ("timer_1", 15, "restart")

Контролировать окончание отсчета можно 2 способами. Первый способ - это контроль фактического окончания отсчета. Вариант использования:
Код: Выделить всё
   if Timer ( "timer_1" ) then
      -- блок операторов, если время вышло
   else
      -- блок операторов, если отсчет не завершен
   end

Обратите внимание - если опрашивается несуществующий канал таймера, функция также возвратит "false", как и при незавершенном отсчете.
Второй способ - контроль времени, которое осталось до окончания отсчета. Вариант использования:
Код: Выделить всё
   Pause = Timer ("timer_1", "ctrl")

В переменную Pause будет загружено оставшееся время (сек) в числовом формате. Если отсчет закончился, будет загружена строка "timeroff". В случае опроса несуществующего канала будет возвращено значение "nil".

Если необходимости в каком-либо канале таймера нет, его можно разрушить:
Код: Выделить всё
   Timer ("timer_1", "clear")

Такая команда уничтожит канал "timer_1". Это сэкономит время, затрачиваемое на обработку.
Автоматического удаления каналов с завершенным отсчетом нет.

Если надо остановить отсчет, используем команду
Код: Выделить всё
   Timer ("timer_1", "pause")

Для продолжения отсчета используем команду
Код: Выделить всё
   Timer ("timer_1", "run")
или 
   Timer ("timer_1", время, "restart")

Количество каналов ограничено объемом оперативной памяти.
Разрешающая способность (сек, грубо) - 1/fps.

Сам код:
Код: Выделить всё
function Timer (data_1, data_2, data_3)
   if type(data_1) == "number" then
      for k,v in pairs(TabTimerData) do
         if type(v) ~= "string" and TabTimerStatus[k] then
            v = v - data_1
            if v <= 0 then
               TabTimerData[k] = "timeroff"
            else
               TabTimerData[k] = v
            end
         end
      end
      return
   end

   if type(data_1) == "string" then
      if not data_2 then
         if TabTimerData[data_1] == "timeroff" then
            return true
         else
            return false
         end
      elseif data_2 == "ctrl" then
         return TabTimerData[data_1]
      elseif data_2 == "clear" then
         TabTimerData[data_1] = nil
      elseif data_2 == "pause" then
         TabTimerStatus[data_1] = false
      elseif data_2 == "run" then
         TabTimerStatus[data_1] = true
      elseif not TabTimerData[data_1] or TabTimerData[data_1] == "timeroff" or data_3 == "restart" then
         TabTimerData[data_1] = data_2
         TabTimerStatus[data_1] = true
      end
   end
end



Приветствуются пожелания по усовершенствованию. Также, несмотря на успешное тестирование, приветствуется любая информация по практическому использованию функции.

Эта версия успешно заменяет предыдущую без дополнительных исправлений кода.
Аватара пользователя
Света
 
Сообщения: 136
Зарегистрирован: 18.06.2016, 19:38
Благодарил (а): 63 раз.
Поблагодарили: 153 раз.
Играю в: RailWorks
Роль: Разработчик

Re: LuaScript - для RS

Сообщение gosha » 13.02.2017, 12:50

Из подсмотренного вчера:
1. Хоть и не документировано, но в ScenarioScript вполне себе зовется метод (функция) Initialise
2. У External Camera тоже есть LUA script. Initialise там зовется, но что там еще - пока мыслей нет, ибо документации тоже нет.
Аватара пользователя
gosha
 
Сообщения: 51
Зарегистрирован: 14.01.2008, 01:15
Откуда: Moscow
Благодарил (а): 1 раз.
Поблагодарили: 0 раз.
Играю в: RailWorks
Роль: Разработчик

Re: LuaScript - для RS

Сообщение Света » 01.03.2017, 18:25

Всем доброго дня :)

Представляю вниманию разработчиков функцию SetValueDelay, предназначенную для вставки в скрипт.

Функция предназначена для автоматической таймеризации управления контролами, также может использоваться для изменения значений контролов по нелинейных законах.
Зачем это нужно? Например, нужно включить насос для прокачки масла. Если создать условие, где при включенном тумблере насос включен, это будет рабочая схема, но не реалистична. Потому что между включением тумблера и непосредственным запуском насоса должно пройти некоторое время, обусловленное переходными процессами и инертностью системы.
Другой пример - включаем питание пульта, все стрелки должны с нулевых значений переместится на рабочие. Желательно, плавно. В идеале - по нелинейному закону, так как при правильной физике вначале должен быть разгон, в конце - замедление хода.
Решить эти вопросы для единичных случаев не проблема. Но если надо создать тайминги/задержки/преобразователи для многих приборов, скрипт начинает очень сильно захламляться. Ещё одна проблема персональных обработчиков - постоянная обработка каждого из них в функции обновления, что, так или иначе, сказывается на оптимизации.
Предлагаемая функция позволяет несколько упростить и оптимизировать этот процесс.

Особенности функции
1. Функция готова к использованию. Подгонки и правки не нужны.
2. Количество обслуживаемых контролов ограничивается лишь здравым смыслом.
3. Все контролы обслуживаются параллельно.
4. Имеется автоматическая очистка, данные об установленных контролах удаляются. Когда список контролов будет пуст, функция будет работать в холостом режиме, потребляя ресурсы только на 2 сравнения и 1 вызов.
5. Три варианта изменения контрола - триггерный, линейный, нелинейный.

Варианты изменения контролов
Контролы, в зависимости от физических свойств управляемых ими объектов, должны изменяться по разных законах. Для этого функция имеет три режима.
1. Триггер. В этом режиме контрол скачком примет заданное значение, когда истечет заданное время. Самый простой обработчик. Например, упомянутый выше масляный насос может заработать спустя некоторое время после нажатия кнопки.
2. Линейный. Здесь контрол будет менять свое значение от текущего к заданному по линейному закону в течении времени задержки. Может применяться для управления безинерционными приборами, например, цифровыми шкалами.
3. Кривая. Самый сложный режим. Значение контрола меняется по нелинейному закону. Например, может применяться для управления дворниками. Или упомянутым выше стрелочным прибором. В этом режиме функция работает по описанной через таблицу кривой.

Адаптация в скрипт
Функция вставляется в любое место скрипта. Никакие подгонки/правки не нужны. Также в любое место скрипта вставляется функция curve_yval.

Для инициализации в функцию Initialise() вписываем рабочую таблицу и контрольную переменную:
Код: Выделить всё
-- SetValueDelay
   ControlNameObj = {}
   record_mark = false

Для обеспечения счёта периодически делаем вызов из функции Update ( time ):
Код: Выделить всё
 SetValueDelay(time)

Всё,функция прописана и готова к работе.

Использование/вызов
Для установки контрола используется вызов:
Код: Выделить всё
SetValueDelay("имя контрола", target, time, "method", tab_curve),
   где
      "имя контрола" - имя, задаваемое контролу в конфиге
      target - конечное значение, в которое необходимо установить контрол
      time - время, в течение которого необходимо установить контрол
      "method" - режим установки
      tab_curve - имя таблицы, описывающей кривую (только для установки по кривой)

Примеры:
Установка контрола в триггерном режиме:
Код: Выделить всё
   SetValueDelay("oil_pump", 1, 3.5, "trigger")
Эта команда включит насос для прокачки масла (переведет в 1) через 3,5 секунды после вызова.

Установка контрола в линейном режиме:
Код: Выделить всё
   SetValueDelay("supply", 1500, 1.3, "linear")
Эта команда запустит процесс, при котором значение контрола "supply" будет изменятся с фиксированной скоростью и через 1,3 секунды после старта достигнет значения 1500.

Установка контрола по нелинейному закону:
Код: Выделить всё
   SetValueDelay("voltmeter", 50, 1, "curve", tab_voltmeter)
После запуска стрелка вольтметра будет двигаться к отметке 50 в течении секунды, скорость движения будет меняться по кривой, описанной в таблице tab_voltmeter.
Обратите внимание, для расчета кривых используется функция (curve_yval(val.curve, х)), используемая в паках от АР. Её я также привожу здесь. Эта функция представлена в оригинальном виде.
В последнем случае необходимо создать таблицу. Вот формат такой таблицы:
Код: Выделить всё
tab_voltmeter = {
   {x = 0, y = 0},
   {x = 1, y = 1}
}

Что она собой представляет? "х" - это относительное время, оставшееся на изменение контрола. Его диапазон всегда будет от 1 до 0 и будет линейно меняться с момента вызова функции. Разница между начальным и конечным значениями контрола - это диапазон изменения. В таблице эти значения масштабированы к дробным значениям "у". Если нам надо, чтобы контрол остался неизменным, берем у=1, если надо, чтобы контрол получил полностью новое значение, берем у=0. Соответственно, промежуточные числа установят в контрол значение, которое можно вычислить:
контрол = конечное_значение - диапазон * у
отсюда
у = (конечное_значение - текущее_значение) / диапазон

Чем больше промежуточных точек, тем качественнее кривая.
Обязательное условие - таблица обязательно должна перекрывать значения для "х" от 0 до 1 и значения "у" не должны выходить за диапазон от 0 до 1.


Разрешающая способность функции(сек, грубо) - 1/fps.

Код функции SetValueDelay:
Код: Выделить всё
function SetValueDelay(argument, target, time, method, tab_curve)
   if type(argument) == "string" then
      ControlNameObj[argument] = {}
      ControlNameObj[argument].control_name = argument
      ControlNameObj[argument].start_parameter = Call("*:GetControlValue", argument, 0)
      ControlNameObj[argument].end_parameter = target
      ControlNameObj[argument].time = time
      ControlNameObj[argument].full_time = time      
      ControlNameObj[argument].method = method
      if method == "curve" then
         ControlNameObj[argument].curve = tab_curve
      end
      record_mark = true
   else
      if record_mark then
         local count = 0
         for k, val in pairs (ControlNameObj) do
            val.time = val.time - argument
            if val.time <= 0 then
               if val.method == "trigger" then
                  Call("SetControlValue", k, 0, val.end_parameter)
               end
               ControlNameObj[k] = nil
            else         
               local diapasone = val.end_parameter - val.start_parameter
               local factor = val.end_parameter - diapasone * val.time/val.full_time
               if val.method == "linear" then
                  Call("SetControlValue", k, 0, factor)
               elseif val.method == "curve" then
                  factor = curve_yval(val.curve, val.time/val.full_time)
                  Call("SetControlValue", k, 0, val.end_parameter - diapasone * factor)
               end
            end
            count = count + 1
         end
         if count == 0 then
            record_mark = false
         end
      end
   end
end


Код функции curve_yval:
Код: Выделить всё
function curve_yval(curve, x)
   local low, high_index, high, last

   for i, v in ipairs(curve) do
      if x <= v.x then
         high = v
         high_index = i
         break
      end
      last = i
   end

   if not high then
      return curve[last].y
   end

   low = curve[high_index - 1]
   if not low then
      return high.y
   end

   return low.y + (high.y - low.y) * (x - low.x) / (high.x - low.x)
end


Приветствуются пожелания по усовершенствованию. Также, несмотря на успешное тестирование, приветствуется любая информация по практическому использованию функции.
Последний раз редактировалось Света 01.03.2017, 19:09, всего редактировалось 2 раз(а).
Аватара пользователя
Света
 
Сообщения: 136
Зарегистрирован: 18.06.2016, 19:38
Благодарил (а): 63 раз.
Поблагодарили: 153 раз.
Играю в: RailWorks
Роль: Разработчик

Re: LuaScript - для RS

Сообщение Skif » 01.03.2017, 18:31

Хорошая штука, полезная, у меня есть аналогичная, но только для линейного изменения значений контролов и переменных. Опционально - позволяет добавить к истинному значению синусоидальное колебание для имитации работы интегрирующих приборов вроде спидометра или тахометра, а также для имитации вибрации стрелок приборов от работы дизеля.

Касаемо управления дворниками - хорошая идея.
Skif
 
Сообщения: 2593
Зарегистрирован: 01.10.2009, 17:42
Благодарил (а): 235 раз.
Поблагодарили: 706 раз.
Блог: Просмотр блога (2)
Имя: Дмитрий

Re: LuaScript - для RS

Сообщение Света » 06.03.2017, 14:57

Приветствую всех участников форума :)

Возник вопрос по разработке скриптованной тяги для локомотива.
Многим известно, что на данный момент существует несколько вариантов расчета тягового усилия локомотива. Это готовые модули. Несмотря на то, что их несколько и они имеют разные формы написания, алгоритм у всех (известных мне, по крайней мере) одинаков. Суть расчета заключается в том, что из нескольких кривых "скорость-тяга" выбирается кривая, соответствующая текущей позиции контроллера тяги.
Тем не менее, это грубая ошибка.
Дело в том, что тяговое усилие зависит от тока ТЭД-ов, ток зависит от напряжения генератора, напряжение - от скорости вращения якоря (= скорости вращения двигателя), а скорость вращения - от позиции контроллера. То есть, логично, что в то время, как позиция меняется мгновенно, тяговый ток будет меняться в зависимости от оборотов двигателя, но никак не "ступенькой", которая неизбежно возникнет, если кривую выбирать по позиции.
Написанное проиллюстрирую графиком. Это тестовая поездка, суть которой заключалась в последовательном наборе позиций:
Изображение
На графике четко видно, что в то время, как частота вращения двигателя начинала увеличиваться после переключения позиции (цифры внизу), тяговое усилие растет ступенькой, а только после этого плавно снижается (это из-за того, что скорость локомотива увеличивается). То есть, явно видно, как в процесс вступает другая кривая ТХ.
Это ещё не все.
Вот ещё один рисунок - это увеличенный фрагмент графика:
Изображение
Здесь хорошо видно, как скорость вращения двигателя доходит до максимума и дальше идет горизонтальный отрезок, а тяговое усилие имеет стабильное снижение на всем участке. То есть, явно видно, что двигатель живет своей жизнью, а тяговый ток - своей.
Понятно, что так быть не должно и такой алгоритм не годится.
Возникает вопрос: что делать? У меня есть два варианта.
1. Переписать всю таблицу ТХ, подставив вместо позиций обороты двигателя.
Это самый очевидный и простой в программном смысле вариант. Такой подход позволит сделать тягу на 100% зависимой от оборотов двигателя. Единственный недостаток - у меня нет совершенно никаких данных, какое усилие должно быть при каких оборотах, и нет методов, которые позволили бы получить эти данные.
2. Используя готовые таблицы тяговых характеристик, рассчитывать тяговое усилие по соседних кривых (для предыдущей позиции и текущей) и интерполировать значение с привязкой к скорости вращения двигателя. Недостаток метода - количество вычислений процента тяги вырастает в три раза.

Как поступить? Может у кого-нибудь будут ещё варианты? Спасибо за участие.
Аватара пользователя
Света
 
Сообщения: 136
Зарегистрирован: 18.06.2016, 19:38
Благодарил (а): 63 раз.
Поблагодарили: 153 раз.
Играю в: RailWorks
Роль: Разработчик

Re: LuaScript - для RS

Сообщение Skif » 06.03.2017, 15:10

На графике четко видно, что в то время, как частота вращения двигателя начинала увеличиваться после переключения позиции


Справедливо только для локомотивов с косвенным регулированием при помощи гидромотора. Т.е. по схеме : контроллер управляет гидравлической золотниковой коробкой (через электромагнитные или на особо мощных тепловозах ажно через электропневматические клапаны, запитывая их катушки), коробка управляет гидромотором, гидромотор перемещает рейки топливного насоса. У таких тепловозов разгон и сброс оборотов дизеля происходит плавно.

У локомотивов с пневматичеким управлением обороты меняются скачком, и существующий метод дает достаточно точный результат. На самом деле, ступенька на практике вообще незаметна из-за кратковременности процесса в целом. Так что я считаю, что ей нужно либо пренебречь, либо использовать для Regulator вместо SetControlValue одну из разработанных нами с тобой функций плавного перевода на требуемое значение. Этого будет более чем достаточно, добавит некоторую инерционность процессу. Более того, ее можно менять, так как скорость перевода контрола (в моих функциях, например), можно менять при помощи переменной.

Для узкоколейных и промышленных тепловозов переходным процессом смело можно пренебрегать, так как все они имеют либо непосредственное (привод топливной рейки напрямую подсоединен к штурвалу или сектору контроллера), либо пневматическое регулирование оборотов дизеля. В пределах одной позиции обороты постоянны с точностью плюс-минус 5 об/мин, что обеспечивается центробежным регулятором Вудварта или электронным, установленным на дизеле.
Skif
 
Сообщения: 2593
Зарегистрирован: 01.10.2009, 17:42
Благодарил (а): 235 раз.
Поблагодарили: 706 раз.
Блог: Просмотр блога (2)
Имя: Дмитрий

Re: LuaScript - для RS

Сообщение TRam_ » 06.03.2017, 15:30

ток зависит от напряжения генератора, напряжение - от скорости вращения якоря (= скорости вращения двигателя)
Неправильно. И ток, и напряжение зависят от механической мощности двигателя и нагрузки (т.е. омического сопротивления ТЭДов за вычетом ЭДС индукции из подаваемого напряжения из-за вращения их якорей), их соотношением управляет объединённая система автоматического регулирования (уменьшающая возбуждение с увеличением подачи топлива если дизель "глохнет" и увеличивая возбуждение с уменьшением подачи топлива, если дизель разгоняется). Так что ничего сверхестественного в модели физики RW, что дизель мгновенно меняет свою мощность (именно из-за этого скачки) не меняя оборотов, нет. А вообще "обороты" определяют только ограничение на регулирование (больше оборотов - больше воздуха - можно больше или полнее сжигать топливо за такт), хотя тут ещё играет некоторую роль и обороты турбонаддува (т.к. когда топлива мало, воздух на выходе холоднее и слабее раскручивает турбину, потому на 1000 оборотах на холостом ходу в дизель поступает меньше воздуха, чем под нагрузкой).

"По секрету" - на холостом ходу на 15 позиции (1000 об/мин) дизель тепловоза 2ТЭ116 развивает всего ~ 350 - 400 л.с. При той же позиции на тех же оборотах, но когда его замедляет тяговый генератор, этот дизель развивает 3060 л.с. Просто потому что автоматическая система регулирования даёт ему больше топлива.

Ток можно получить из силы тяги (по формуле силы на проводник в магнитном поле, эта сила пропорциональна току и магнитной индукции, а при последовательном возбуждении ТЭДов магнитная индукция сама пропорциональна току, итого сила ~ квадрату тока, значит ток пропорционален корню силы тяги). Ну а напряжение - из касательной мощности и тока (касательная мощность - произведение силы тяги на скорость).
в z7 всё можно, а что нельзя - можно в sU
Аватара пользователя
TRam_
 
Сообщения: 1901
Зарегистрирован: 30.11.2007, 20:14
Благодарил (а): 2 раз.
Поблагодарили: 61 раз.
Играю в: Auran Trainz
Роль: Разработчик
Имя: Владимир

Re: LuaScript - для RS

Сообщение Skif » 06.03.2017, 16:26

Параметр RPM в TS вообще исключительно декоративный - единственная польза от него, это хоть какой-то расчет расхода горючего. Учитывая, что не у всех тепловозов зависимость оборотов от позиции контроллера линейная, я его вообще не использую нигде, кроме упомянутого расхода горючки. А так и дымогенератор, и приборы в кабине (тахометр непосредственно и все, у которых синхронно с дизелем вибрируют стрелки), и звуки (особенно звуки), и алгоритм пуска дизеля у меня работают через собственный контрол оборотов дизеля. Работал бы правильно системный - пользовал бы его, а так - увы.
Skif
 
Сообщения: 2593
Зарегистрирован: 01.10.2009, 17:42
Благодарил (а): 235 раз.
Поблагодарили: 706 раз.
Блог: Просмотр блога (2)
Имя: Дмитрий

Re: LuaScript - для RS

Сообщение Света » 09.03.2017, 14:57

Всем хорошего дня :)
В общем, как я понимаю, других вариантов нет.
Ладно, в таком случае я применю вариант "2" из предыдущего поста, но с некоторыми хитростями. Я буду проводить интерполяцию только при изменении скорости вращения двигателя. Таким образом количество операций вычисления тяги будет возрастать лишь при смене позиций, а в оставшееся время будут те же расчеты, что обычно. Это максимум, что я могу выжать в плане оптимизации.
Сейчас картина уже другая: Изображение
Это при неизменной скорости 20 км/ч. Скорость задана константой. Считывание данных проводилось на "макете".
Позиции с 5 по 7 пропущены - это для того, чтобы воспроизвести поведение системы в случае резкого перевода контроллера тяги через несколько позиций сразу (я знаю, так нельзя, но судя по видеороликах, есть "специалисты", которые могут подтормаживать экстренным, трогать с места на максимальной позиции, мне приходится учитывать все случаи). В общем, тяга на 100% зависит от скорости вращения двигателя, оставляю такой алгоритм.
Skif писал(а):У локомотивов с пневматичеким управлением обороты меняются скачком, и существующий метод дает достаточно точный результат.
Дело в том, что я хочу сделать алгоритм, который бы одинаково подходил для всех систем. Если привязать тяговое усилие к скорости вращения, то будет неважно, насколько быстро происходит приращение оборотов, программа будет корректно просчитывать любое приращение.
Если этого добиться, то скорость изменения оборотов можно будет устанавливать через функции плавного перевода на требуемое значение (выбирая скорость перевода для конкретного ПС) и не бояться сломать обработчик.
Skif писал(а): На самом деле, ступенька на практике вообще незаметна из-за кратковременности процесса в целом. Так что я считаю, что ей нужно либо пренебречь, либо использовать для Regulator вместо SetControlValue одну из разработанных нами с тобой функций плавного перевода на требуемое значение.
Пренебречь не получилось, очень чувствуется рассинхронизация при переводе контроллера. А функция плавного перевода не подошла, потому что система получилась очень громоздкой, к тому же надо синхронизировать прирост скорости вращения с приростом тяги, отладка очень проблемная. Ну и при резкой смене позиций все равно присутствуют рывки, хоть и сглаженные.
Skif писал(а):Параметр RPM в TS вообще исключительно декоративный - единственная польза от него, это хоть какой-то расчет расхода горючего.
Не применяю категорически. Сейчас у меня в конфиге его нет вообще. При высоких скоростях, если применена скриптованная тяга, его значение снижается до дефолтного минимума - это уже откровенная ложь в показаниях.
TRam_ писал(а):
ток зависит от напряжения генератора, напряжение - от скорости вращения якоря (= скорости вращения двигателя)
Неправильно. И ток, и напряжение зависят от механической мощности двигателя и нагрузки
Так что же неправильно? Если генератор сильнее раскрутить, он может выработать больший потенциал на выходе. Разве нет? Я понимаю, что чем ниже сопротивление нагрузки, тем больше просядет напряжение. Понимаю, что выходное сопротивление генератора также влияет на вырабатываемое под нагрузкой напряжение. Естественно, ток будет обратно пропорционален сопротивлению нагрузки. Но я эти моменты не затрагиваю, так как это всё выходит за рамки поднятого вопроса.
TRam_ писал(а):Так что ничего сверхестественного в модели физики RW, что дизель мгновенно меняет свою мощность (именно из-за этого скачки) не меняя оборотов, нет
Это если рассматривать работу дизеля под нагрузкой и без. Опять-таки, я говорю совершенно не об этом.
Вы пишете о работе системы, поддерживающей стабильные обороты дизеля. А я - о реакции локомотива на действия машиниста. Я рассматриваю случай, когда при стабильной скорости (когда ТЭДы стабильно нагружены) тяговое усилие рассчитывается скриптом для максимальных оборотов дизеля на текущей позиции. Даже если дизель ещё не набрал эти обороты. И, получается, переводим контроллер на следующую позицию, и локомотив уже тянет сильнее, хотя дизель ещё даже не отреагировал.
Я могу привести пример такой некорректной работы скрипта. Возьмите "Людмилу", растормозите, "станьте" рядом и включите первую позицию. Локомотив начнет двигаться до того, как дизель отреагирует на смену позиций. Это же ошибка, не может такого быть в реале.
Аватара пользователя
Света
 
Сообщения: 136
Зарегистрирован: 18.06.2016, 19:38
Благодарил (а): 63 раз.
Поблагодарили: 153 раз.
Играю в: RailWorks
Роль: Разработчик

Пред.След.

Вернуться в [RW] Другие вопросы

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 1