1
0
mirror of https://github.com/metrostroi-repo/MetrostroiAddon.git synced 2026-05-02 00:42:29 +00:00
Files
MetrostroiAddon/lua/metrostroi/systems/sys_81_722_asyncinverter.lua
2021-01-02 15:32:05 +03:00

297 lines
11 KiB
Lua

--------------------------------------------------------------------------------
-- 81-722 async inverter and motors
--------------------------------------------------------------------------------
-- Copyright (C) 2013-2018 Metrostroi Team & FoxWorks Aerospace s.r.o.
-- Contains proprietary code. See license.txt for additional information.
--------------------------------------------------------------------------------
Metrostroi.DefineSystem("81_722_AsyncInverter")
TRAIN_SYSTEM.DontAccelerateSimulation = false
function TRAIN_SYSTEM:Initialize()
-- Train state/sensors
self.Voltage = 750 -- External voltage
self.Speed = 0 -- Speed of train in km/h
-- Physics state
self.RotationRate = 0.0 -- Rate of engine rotation, rpm
self.Torque = 0.0 -- Relative units of torque
self.TargetTorque = 0.0 -- Target torque, that inverter will hold
-- Inverter state
self.Mode = 0 -- 0: coast, 1: drive, -1: brake
self.Power = 0
self.EDone = 0
self.State = 0.0 -- Inverter on/off
self.InverterFrequency = 0.0 -- Output per-phase frequency, Hz
self.InverterVoltage = 0.0 -- Output per-phase voltage, V
--self.InverterGenFrequency = 0.0 -- Invertors generator frequency, Hz
self.Current = 0.0 -- Total electric current, A
-- Inverter input signals
self.Voltage = 750 -- Third rail voltage
self.Drive = 0 -- Drive mode signal
self.Brake = 0 -- Brake model signal
self.State = 0 -- Power level (PWM)
self.Power1 = 0
self.Power2 = 0
self.Power3 = 0
end
function TRAIN_SYSTEM:Inputs()
return { "Voltage", "Speed",
"Drive", "Brake", "Power",}
end
function TRAIN_SYSTEM:Outputs()
return { "Drive","Brake","Mode","Torque", "Current", "State", "InverterFrequency","EDone","Power" }
end
function TRAIN_SYSTEM:TriggerInput(name,value)
if self[name] then self[name] = value end
end
local function lerp(min,max, alpha)
return min + math.min(1,math.max(0,alpha))*(max-min)
end
local function interpolate(tbl, num)
for i=1,#tbl do
local curr,next = tbl[i],tbl[i+1]
if not next then
return curr[2]
elseif curr[1] <= num and num <= next[1] then
return curr[2] + (next[2]-curr[2])*((num-curr[1])/(next[1]-curr[1]))
end
end
end
TRAIN_SYSTEM.xF = {
{
{
{0, 0.80},
{1, 1.57},
{2, 2.90},
{3, 4.25},
{5, 7.01},
{7, 9.83},
{10, 14.19},
{12, 17.20},
{13, 18.70},
{15, 21.80},
{17, 24.25},
{18, 25.45},
{19, 26.70},
{20, 27.90},
{27, 36.55},
{30, 40.60},
{40, 54.15},
{60, 81.20},
{80, 108.30}
},{
{0, 0.35},
{1, 1.43},
{2, 2.598},
{3, 3.86},
{5, 6.38},
{7, 8.95},
{10, 12.865},
{12, 15.50},
{13, 16.83},
{15, 19.49},
{17, 21.92},
{18, 23.12},
{19, 24.34},
{20, 25.625},
{30, 38.44},
{40, 51.25},
{60, 76.87},
{80, 102.50}
},{
{300.374, 0}, {308.958, 0.05}, {318.122, 0.1}, {327.922, 0.15},
{338.424, 0.2}, {349.703, 0.25}, {361.842, 0.3}, {374.937, 0.35},
{389.096, 0.4}, {404.442, 0.45}, {421.117, 0.5}, {439.278, 0.55},
{459.104, 0.6}, {480.793, 0.65}, {504.563, 0.7}, {530.643, 0.75},
{559.258, 0.8}, {590.6, 0.85}, {624.772, 0.9}, {661.668, 0.95},
{700.77, 1}
}
},{
{
{0, 3.5},
{4.5, 3.50},
{5.0, 4.73},
{7.5, 7.5},
{10, 10.3},
{15, 15.95},
{20, 21.55},
{23, 25.25},
{25, 27.7},
{30, 33.9},
{35, 40.1},
{40, 46.3},
{50, 57.87},
{60, 69.45},
{70, 81.0},
{80, 92.6},
{100, 115.8}
},{
{0, 4.5},
{4.5, 4.50},
{5.0, 5.625},
{7.5, 8.66},
{10, 11.71},
{15, 17.80},
{20, 23.88},
{23, 27.68},
{25, 30.22},
{30, 36.34},
{35, 42.4},
{40, 48.46},
{45, 54.51},
{50, 60.57},
{60, 72.68},
{70, 84.8},
{80, 96.91},
{100, 121.13}
},{
{-699.034, 0}, {-663.137, 0.05}, {-629.719, 0.1}, {-598.679, 0.15},
{-569.872, 0.2}, {-543.14, 0.25}, {-518.319, 0.3}, {-495.252,0.35},
{-473.788, 0.4}, {-453.789, 0.45}, {-435.127, 0.5}, {-417.686, 0.55},
{-401.36, 0.6}, {-386.056, 0.65}, {-371.685,0.7}, {-358.172, 0.75}, {-345.446, 0.8},
{-333.444, 0.85}, {-322.109, 0.9}, {-311.39, 0.95}, {-301.239, 1}
}
}
}
function TRAIN_SYSTEM:Think(dT)
local Train = self.Train
local V = self.Speed
self.Voltage = Train.TR.Main750V
-- Generate on/off signal
local TargetMode = 0
if self.Brake*self.Power > 0.5 and (self.Mode<0 or self.Voltage>550) then
TargetMode = -1
elseif self.Drive*self.Power > 0.5 then
TargetMode = 1
end
self.EDone = self.Brake*((self.Speed<=5 or self.Speed<=10 and (self.Mode>=0 or self.EDone > 0) or self.Mode>=0 and self.Voltage<550) and 1 or 0)
-- Check correct mode
if TargetMode ~= self.Mode then
if self.State < 0.01 then
self.Mode = TargetMode
end
end
if self.Power == 0 or (self.Voltage < 550 and self.Mode > 0) then
self.Mode = 0
end
local Inverter_PWM0 = 1.5 -- PWM On
local Inverter_PWM1 = 2--TargetMode==0 and 0.5 or 1.5 -- PWM Off
-- PWM target command
-- Adjust state as defined by mode
if self.Mode == TargetMode and TargetMode ~= 0 and self.EDone ==0 then
local torque = math.abs(self.Torque)
--print(self.State,torque,self.TargetTorque,(self.TargetTorque-torque))
self.State = math.max(0, math.min(1, self.State + (self.TargetTorque-torque)*Inverter_PWM0*dT))
--[[ if torque < self.TargetTorque then
self.State = math.max(0, math.min(1, self.State + (self.TargetTorque-torque)*Inverter_PWM0*dT))
elseif torque > self.TargetTorque then
self.State = math.max(0, math.min(1, self.State + (self.TargetTorque-torque)*Inverter_PWM1*dT))
--self.State = math.max(0, math.min(1, self.State - Inverter_PWM1*dT*(self.TargetTorque-torque)))
end--]]
else
self.State = math.max(0, self.State - Inverter_PWM1*dT)
end
-- Generate voltage/frequency
local V = self.Speed
if self.Mode == 1 then -- Drive mode
local Power = lerp(0.0, 1.0, self.State)--- + 0.80*self.Power1 + 0.90*self.Power2 + 1.00*self.Power3)
local f1 = interpolate(self.xF[1][1], V)
local f2 = interpolate(self.xF[1][2], V)
local w = interpolate(self.xF[1][3], 700)--200+Train.Pneumatic.WeightLoadRatio*600)
self.InverterFrequency = f1*(1-w) + f2*w
local Vmin = 200
local Vtrans = 20
self.InverterVoltage = math.min(self.Voltage,lerp(Vmin, 750, V/Vtrans)*Power)
elseif self.Mode == -1 then -- Brake mode
local Power = lerp(0.0, 1.0, self.State)--- + 0.80*self.Power1 + 0.90*self.Power2 + 1.00*self.Power3)
local f1 = interpolate(self.xF[2][1], V)
local f2 = interpolate(self.xF[2][2], V)
local w = interpolate(self.xF[2][3], 700)--200+Train.Pneumatic.WeightLoadRatio*600)
self.InverterFrequency = f1*(1-w) + f2*w
local Vmin = 600
local Vtrans = 20
self.InverterVoltage = lerp(Vmin, 925*2, V/Vtrans)*Power
else -- Coast mode
self.InverterVoltage = 0
self.InverterFrequency = 0.1
end
--------------------------------------------------------------------------------------------------------------------
-- PWM generator model
--------------------------------------------------------------------------------------------------------------------
-- Voltage set by inverter
local V = self.InverterVoltage * self.State -- V
-- Frequency set by inverter
local f = self.InverterFrequency -- Hz
--[[
-- PWM pulses per sync period
local PWMn = 1
if self.Speed < 60 then PWMn = 2 end
if self.Speed < 40 then PWMn = 4 end
if self.Speed < 29 then PWMn = 6 end
if self.Speed < 23 then PWMn = 8 end
-- Get frequency of the generator
self.InverterGenFrequency = self.InverterFrequency*PWMn
self.InverterGenFrequency2 = self.InverterFrequency*PWMn]]
--------------------------------------------------------------------------------------------------------------------
-- Asynchronous inverter physics model/engine model
--------------------------------------------------------------------------------------------------------------------
-- Physical parameters for the engine
local P = 4 -- No of poles Poles in the engine
local Rs = 0.04 -- Ohm Active stator resistance
local Rr = 0.04 -- Ohm Active rotor resistance
local Xs = 1.4 -- Ohm reactive Reactive stator resistance
local Xr = 1.4 -- Ohm reactive Reactive rotor resistance
local Xm = 30 -- Ohm reactive Air gap reactive resistance
-- Get rate of engine rotation
local n = 3000 * (self.Speed/80)
self.RotationRate = self.RotationRate + 5.0 * (n - self.RotationRate) * dT
-- Synchronous RPM, synchronous rate and slip
local ns = 120*(f/P) -- rpm
local ws = (2*math.pi*ns)/60 -- rad/sec
local s = (ns - n)/ns -- slip
-- Asynchronous engine physics model
local K = 2*Rr*Rs*s*Xm^2 + Rr^2 * (Rs^2 + (Xm + Xs)^2) + s^2 * (Rs^2 * (Xm + Xr)^2 + (Xr*Xs + Xm*(Xr + Xs))^2)
local Is_real = ( V*(Rr^2 * Rs + Rr * s * Xm^2 + Rs * s^2 * (Xm + Xr)^2) ) / ( K )
local Is_imag = -( V*(Rr^2 * (Xm + Xs) + s^2 * (Xm + Xr) * (Xr*Xs + Xm*(Xr + Xs))) ) / ( K )
local Ir_real = ( s*V*Xm*(Rs*s*(Xm + Xr) + Rr*(Xm + Xs)) ) / ( K )
local Ir_imag = -( s*V*Xm*(-Rr*Rs + s*(Xr*Xs + Xm*(Xr + Xs))) ) / ( K )
-- Convert to real/phase shift
local Is_abs = math.sqrt(Is_real^2 + Is_imag^2)
local Ir_abs = math.sqrt(Ir_real^2 + Ir_imag^2)
local Is_arg = math.atan2(Is_imag, Is_real)
local Ir_arg = math.atan2(Ir_imag, Ir_real)
-- Get total current through stator, rotor
local Is_total = 2*3*Is_abs*(Is_arg + math.pi/2)
local Ir_total = 2*3*Ir_abs*(Ir_arg + math.pi/2)
-- Calculate total torque
local T_total = ( ( 2*3*((Ir_abs^2)*Rr)/(math.max(1e-5,ws)) )*(Ir_arg + math.pi/2) )/14.1
-- Output torque
self.Current = Is_total
self.Torque = T_total
--[[print(string.format("V=%.1f km/h Torque=%.2f m/s2 I=%.1f A s=%.4f | Inverter S=%.0f%% V=%.0f V F=%.1f Hz (%.1f Hz) %s",
self.Speed, T_total, Is_total, s, self.State*100, V, f, self.InverterGenFrequency, ((self.Mode == 1) and "Drive" or ((self.Mode == -1) and "Brake" or "Coast"))
))]]
end