Architecture

Julia simulation project for building and testing induction-machine control structures for AWES ground-station studies.

The project currently contains two modelling/simulation paths:

  1. ModelingToolkit symbolic models for simple scalar V/f validation.
  2. Hybrid FOC simulations with a continuous induction-machine plant and discrete controllers/observers, closer to the intended real implementation.

The current focus is the second path: a discrete FOC control structure with speed control, current control, rotor-flux/torque observation, optional load-torque estimation, and AWES profile playback from CSV.


1. Current folder structure

InductionMachineDrives_jl/
├── docs/
│   └── README_InductionMachineDrives_jl.md
├── profiles/
│   └── delta_kite_13_ms_profiles.csv
├── results/
├── scripts/
│   ├── run_foc_current_hybrid_steps.jl
│   ├── run_foc_current_steps.jl
│   ├── run_foc_speed_f1_160kw.jl
│   ├── run_foc_speed_f1_awes_profile.jl
│   ├── run_foc_speed_f1_awes_profile_efficiency.jl
│   ├── run_foc_speed_f1_ramp_load_estimator.jl
│   ├── run_foc_speed_f1_ramp_load_steps.jl
│   ├── run_foc_speed_mtpa_awes_profile.jl
│   ├── run_foc_torque_f1_steps.jl
│   ├── run_scalar_im.jl
│   ├── run_scalar_frequency_steps.jl
│   └── run_scalar_frequency_steps_load_steps.jl
└── src/
    ├── controls/
    │   ├── scalar_vf_control.jl
    │   └── FOC/
    │       ├── current_controller.jl
    │       ├── current_controller_discrete.jl
    │       ├── outer_speed_flux_f1_discrete.jl
    │       └── outer_torque_flux_f1_discrete.jl
    ├── estimators/
    │   ├── load_torque_kalman_discrete.jl
    │   ├── rotor_flux_observer_discrete.jl
    │   └── rotor_flux_observer.jl
    ├── plants/
    │   └── induction_machine_alpha_beta.jl
    ├── profiles/
    │   ├── frequency_profiles.jl
    │   └── load_profiles.jl
    ├── simulators/
    │   ├── hybrid_foc_current_simulator.jl
    │   ├── hybrid_foc_speed_f1_simulator.jl
    │   └── hybrid_foc_torque_f1_simulator.jl
    ├── systems/
    │   ├── foc_current_im_system.jl
    │   └── scalar_im_system.jl
    └── InductionMachineDrives_jl.jl

2. Main project entry point

The main module file is:

src/InductionMachineDrives_jl.jl

This file includes all submodules/files and exports the main simulation functions.

Typical include structure:

include("profiles/frequency_profiles.jl")
include("profiles/load_profiles.jl")

include("controls/scalar_vf_control.jl")
include("controls/FOC/current_controller.jl")
include("controls/FOC/current_controller_discrete.jl")
include("controls/FOC/outer_torque_flux_f1_discrete.jl")
include("controls/FOC/outer_speed_flux_f1_discrete.jl")

include("plants/induction_machine_alpha_beta.jl")

include("estimators/rotor_flux_observer.jl")
include("estimators/rotor_flux_observer_discrete.jl")
include("estimators/load_torque_kalman_discrete.jl")

include("systems/scalar_im_system.jl")
include("systems/foc_current_im_system.jl")

include("simulators/hybrid_foc_current_simulator.jl")
include("simulators/hybrid_foc_torque_f1_simulator.jl")
include("simulators/hybrid_foc_speed_f1_simulator.jl")

The most relevant exported simulation functions are:

simulate_scalar_im(...)   # needs `using ModelingToolkit, OrdinaryDiffEq` (package extension)
simulate_foc_current_hybrid(...)
simulate_foc_torque_f1_hybrid(...)
simulate_foc_speed_f1_hybrid(...)

3. Mechanical sign convention

The project now uses the following mechanical equation:

TL + Te = J*dω/dt + B*ω

or equivalently:

J*dω/dt = Te + TL - B*ω

Meaning:

Te > 0      electromagnetic torque accelerates toward positive speed
TL > 0      external/load torque also pulls toward positive speed
Bω          viscous friction opposes motion

This is important for all plant, estimator, and feedforward logic.

For the speed-loop feedforward:

Te_ff = J*dωref/dt + B*ωref - TL_est

Therefore, in speed-loop run scripts:

load_ff_sign = -1.0

For AWES profile playback:

profile_torque_sign = 1.0

should be used if the CSV torque profile already follows the convention:

positive torque_ref_Nm pulls toward positive speed

Use:

profile_torque_sign = -1.0

only if the CSV torque has the opposite sign convention.


4. Plant model

File

src/plants/induction_machine_alpha_beta.jl

This is the symbolic/MTK induction-machine model in stationary alpha-beta coordinates.

It defines the induction-machine equations:

stator voltage equations
rotor squirrel-cage equations
flux-current relations
electromagnetic torque
mechanical dynamics

The mechanical equation should use the project convention:

J * D(ωm) ~ Te + Tload_cmd - B * ωm

Hybrid plant

The hybrid FOC simulations use a hand-written plant derivative function in:

src/simulators/hybrid_foc_current_simulator.jl

The corresponding mechanical derivative should be:

dωm = (Te + Tload - p.B * x.ωm) / p.J

The speed and torque FOC hybrid simulators reuse this plant implementation.


5. Scalar V/f model path

This path is useful for basic plant validation and comparison against Simulink.

Key files

src/controls/scalar_vf_control.jl
src/profiles/frequency_profiles.jl
src/profiles/load_profiles.jl
src/systems/scalar_im_system.jl

Main scripts

scripts/run_scalar_frequency_steps.jl
scripts/run_scalar_frequency_steps_load_steps.jl
scripts/run_scalar_im.jl

Concept

The scalar system builds a symbolic MTK system:

frequency profile -> scalar V/f control -> alpha-beta induction machine plant
load profile -----------------------------> plant mechanical input

The scalar V/f controller generates alpha-beta stator voltages directly from a frequency command.


6. Hybrid FOC simulation path

The hybrid FOC path is the preferred architecture for controller development.

It uses:

continuous induction-machine plant
+
discrete observers/controllers
+
fixed-step simulation loop

This is closer to the real implementation, where controllers run at a fixed sampling time.

Core idea

At every sample time:

1. Read plant currents and mechanical speed/angle
2. Update rotor-flux/torque observer
3. Optionally update load-torque estimator
4. Update outer loop, if present
5. Update inner current controller
6. Convert dq voltage command to alpha-beta
7. Integrate the plant over one sample period
8. Store results

7. Discrete inner current controller

File

src/controls/FOC/current_controller_discrete.jl

Purpose

This is the discrete FOC current controller. It receives:

isd_ref, isq_ref
isd_meas, isq_meas
omega_e
lambda_rd

and outputs:

vsd, vsq

It includes:

current reference limiting
optional measurement filtering
PI control
optional feedforward decoupling
optional voltage saturation
optional anti-windup

Main types/functions

CurrentControllerDiscreteState
CurrentControllerDiscreteParams
CurrentControllerDiscreteOutput
current_controller_step!(...)

8. Rotor-flux and torque observer

File

src/estimators/rotor_flux_observer_discrete.jl

Purpose

This observer estimates rotor flux and torque from measured alpha-beta currents and mechanical rotor angle.

It outputs:

lambda_rd_e
lambda_rq_e
theta_e
i_sd_e
i_sq_e
Te_raw
omega_e

The current controller uses the observer outputs as its synchronous-frame measurements.

Main types/functions

RotorFluxObserverDiscreteState
RotorFluxObserverDiscreteParams
RotorFluxObserverDiscreteOutput
rotor_flux_observer_step!(...)

9. Load-torque Kalman estimator

File

src/estimators/load_torque_kalman_discrete.jl

Purpose

This estimator estimates the external torque TL from speed and electromagnetic torque.

It is based on the mechanical model:

J*dω/dt = Te + TL - B*ω

Discrete model:

ω[k+1] = (1 - B*Ts/J)*ω[k] + (Ts/J)*TL[k] + (Ts/J)*Te[k]
TL[k+1] = TL[k]

Therefore, the estimator should use:

A12 = Ts / J

not -Ts/J.

Main types/functions

LoadTorqueKalmanState
LoadTorqueKalmanParams
LoadTorqueKalmanOutput
load_torque_kalman_step!(...)

For AWES profiles, where torque may be signed, it is usually safer to use:

TL_kalman_limit_positive = false

10. Outer torque loop, F1 constant flux

File

src/controls/FOC/outer_torque_flux_f1_discrete.jl

Purpose

Simplified outer-loop case equivalent to:

mode_control = 1   torque control
mode_torque  = 1   simple torque-to-iq conversion
mode_flux    = 1   constant id reference

It converts:

Te_ref -> isd_ref, isq_ref

with:

isd_ref = isd_nom
isq_ref = Te_ref / Kt_nom

and current-circle limitation.

Used by

src/simulators/hybrid_foc_torque_f1_simulator.jl
scripts/run_foc_torque_f1_steps.jl

11. Outer speed loop, F1 constant flux

File

src/controls/FOC/outer_speed_flux_f1_discrete.jl

Purpose

Simplified speed-loop case equivalent to:

mode_control = 2   speed control
mode_torque  = 1   simple torque-to-iq conversion
mode_flux    = 1   constant id reference

It converts:

speed_ref -> Te_ref -> isd_ref, isq_ref

using a discrete speed PI and optional load-torque feedforward.

With the current mechanical convention:

J*dω/dt = Te + TL - B*ω

load feedforward should use:

load_ff_sign = -1.0

so that:

Te_ff = J*dωref/dt + B*ωref - TL_est

Used by

src/simulators/hybrid_foc_speed_f1_simulator.jl
scripts/run_foc_speed_f1_ramp_load_steps.jl
scripts/run_foc_speed_f1_ramp_load_estimator.jl
scripts/run_foc_speed_f1_awes_profile.jl

12. Hybrid current-loop simulator

File

src/simulators/hybrid_foc_current_simulator.jl

Purpose

Tests only the inner current controller:

isd_ref, isq_ref -> current controller -> voltage command -> plant

It is useful for debugging current tracking before adding torque or speed outer loops.

Main script

scripts/run_foc_current_hybrid_steps.jl

Typical reference sequence

0–1 s      id = 0 A, iq = 0 A
1–3 s      id = 5 A, iq = 0 A
3–6 s      id = 5 A, iq = 5 A
6–9 s      id = 5 A, iq = -5 A
9–12 s     id = 5 A, iq = 0 A

13. Hybrid torque-loop simulator

File

src/simulators/hybrid_foc_torque_f1_simulator.jl

Purpose

Tests the torque outer loop plus inner current loop:

Te_ref -> outer torque F1 -> isd_ref, isq_ref -> current controller -> plant

Main script

scripts/run_foc_torque_f1_steps.jl

Typical reference sequence

0–2 s       Te_ref = 0 Nm
2–6 s       Te_ref = +20 Nm
6–10 s      Te_ref = -20 Nm
10–12 s     Te_ref = 0 Nm

14. Hybrid speed-loop simulator

File

src/simulators/hybrid_foc_speed_f1_simulator.jl

Purpose

Tests speed outer-loop FOC with constant-flux F1 mode:

speed_ref -> speed PI -> Te_ref -> isd_ref, isq_ref -> current controller -> plant

It supports:

internal ramp speed references
internal load-step profiles
external CSV speed/load profiles
optional load-torque Kalman estimator
optional load-torque feedforward
abc reconstruction
electrical/mechanical power calculation

Main scripts

scripts/run_foc_speed_f1_ramp_load_steps.jl
scripts/run_foc_speed_f1_ramp_load_estimator.jl
scripts/run_foc_speed_f1_awes_profile.jl

15. AWES CSV profile playback

Input file

profiles/delta_kite_13_ms_profiles.csv

Expected columns:

t_s
speed_ref_rpm
speed_ref_rad_s
torque_ref_Nm

The current AWES run script uses:

speed_ref_rpm      -> speed reference profile
torque_ref_Nm      -> external torque profile

The simulator linearly interpolates between profile points at each simulation time, similar to Simulink timeseries behavior.

Main script

scripts/run_foc_speed_f1_awes_profile.jl

Important settings

speed_reference_source = :profile
load_source = :profile
profile_torque_sign = 1.0
wm_dot_max = 1e6
load_estimator = :kalman
use_load_feedforward = true
load_ff_sign = -1.0
TL_kalman_limit_positive = false

wm_dot_max = 1e6 effectively disables internal speed-reference ramp limiting, so the speed reference used by the PI follows the CSV profile closely.


16. Power and abc current reconstruction

The hybrid speed simulator reconstructs abc quantities from alpha-beta quantities assuming zero-sequence is zero:

ia = isα
ib = -0.5 * isα + sqrt(3)/2 * isβ
ic = -0.5 * isα - sqrt(3)/2 * isβ

va = vsα
vb = -0.5 * vsα + sqrt(3)/2 * vsβ
vc = -0.5 * vsα - sqrt(3)/2 * vsβ

Instantaneous stator electrical power:

Pelec = va*ia + vb*ib + vc*ic

Mechanical electromagnetic power:

Pmech = Te * ωm

External torque power:

Pload = TL * ωm

Friction loss:

Pfric = B * ωm^2

With the current sign convention:

Pload > 0  external torque injects mechanical power into the shaft
Pload < 0  external torque extracts mechanical power from the shaft

17. Running simulations

The dedicated 160 kW induction-machine FOC F1 test is described in the 160 kW FOC F1 case.

From the project root, start Julia:

jl

Then run simulations from the Julia prompt with include():

include("scripts/run_scalar_im.jl")
include("scripts/run_foc_current_hybrid_steps.jl")
include("scripts/run_foc_torque_f1_steps.jl")
include("scripts/run_foc_speed_f1_ramp_load_steps.jl")
include("scripts/run_foc_speed_f1_ramp_load_estimator.jl")
include("scripts/run_foc_speed_f1_awes_profile.jl")
include("scripts/run_foc_speed_f1_160kw.jl")

For the AWES profile case:

include("scripts/run_foc_speed_f1_awes_profile.jl")

The result CSV is saved in:

results/foc_speed_f1_awes_profile.csv

18. Output results

Simulation outputs are saved in:

results/

The AWES profile script saves:

results/foc_speed_f1_awes_profile.csv

This CSV includes:

speed reference and measured speed
torque reference, plant torque, observed torque, AWES torque, estimated load torque
dq currents
alpha-beta currents and voltages
reconstructed abc currents and voltages
electrical/mechanical/load/friction power
rotor flux and angle
saturation/debug signals

Step 1: Validate current loop

include("scripts/run_foc_current_hybrid_steps.jl")

Check:

id tracking
iq tracking
positive iq gives positive torque
negative iq gives negative torque

Step 2: Validate torque loop

include("scripts/run_foc_torque_f1_steps.jl")

Check:

Te_ref -> iq_ref conversion
torque response
speed acceleration/deceleration

Step 3: Validate speed loop without load estimator

load_estimator = :none
use_load_feedforward = false

Step 4: Validate load estimator only

load_estimator = :kalman
use_load_feedforward = false

Check whether TL_est tracks Tload.

Step 5: Validate load feedforward

load_estimator = :kalman
use_load_feedforward = true
load_ff_sign = -1.0

Check whether speed error improves when the load torque changes.

Step 6: Run AWES profile

include("scripts/run_foc_speed_f1_awes_profile.jl")

Check:

speed profile tracking
AWES torque sign
estimated load torque
abc currents
Pelec, Pmech, Pload

20. Common issues

Wrong load-torque sign

If enabling load feedforward makes the speed tracking worse, check:

load_ff_sign = -1.0
profile_torque_sign = 1.0

and verify the mechanical convention:

J*dω/dt = Te + TL - Bω

AWES profile does not match input

Check:

speed_reference_source = :profile
load_source = :profile
wm_dot_max = 1e6

Remember:

res.wm_ref       = raw CSV speed reference after interpolation
res.wm_ref_ramp  = ramp-limited reference used internally by the speed PI

Load estimator cannot estimate negative torque

Set:

TL_kalman_limit_positive = false

21. Current development status

Implemented:

scalar V/f MTK system
alpha-beta induction-machine plant
hybrid FOC current-loop simulation
discrete current controller
discrete rotor-flux/torque observer
discrete load-torque Kalman estimator
outer torque loop, T1 + F1
outer speed loop, T1 + F1
AWES speed/torque profile playback from CSV
abc current/voltage reconstruction
stator electrical power and mechanical power calculation

Not yet implemented:

advanced FOC modes from the full MATLAB controller
MTPA / loss minimization modes
field-weakening
full voltage/current constraint management beyond the current controller level
higher-level AWES cycle energy metrics
comparison automation across controller options
parameter sweep / robustness batch scripts