Estimators
Both estimators are discrete and are stepped once per fixed-step iteration, the observer before the outer loop and the Kalman estimator alongside it.
The load-torque Kalman estimator is built around J * dω/dt = Te + TL - B * ω, which fixes A12 = Ts/J — not -Ts/J. See sections 9 and 11 of the architecture page.
Rotor-flux and torque observer
InductionMachineDrives.RotorFluxObserverDiscreteOutput — Type
RotorFluxObserverDiscreteOutput(; kwargs...)Result of one rotor_flux_observer_step! call, returned fresh each sample.
This is the block that makes the rest of the loop field-oriented: theta_e is the angle everything else rotates by, and i_sd_e/i_sq_e are the dq currents the controllers regulate. A trailing _e means "in the estimated flux frame", _r means "in the rotor frame", and alpha/beta means the stationary frame.
Fields
lambda_rd_e,lambda_rq_e: rotor flux linkage in Wb in the estimated flux frame. By constructionlambda_rq_eis ~0 andlambda_rd_ecarries the whole flux — how close it stays to zero is a direct check on the observer.theta_e: estimated rotor flux angle in rad, wrapped to[0, 2π). Used for the Park and inverse-Park transforms in the loop.Te_raw: estimated electromagnetic torque in N·m,1.5*p*k*lambda_rd_e*i_sq_e. Positive accelerates toward positive speed, perJ*dω/dt = Te + TL - B*ω.Te_filt_speed,Te_filt_torque: the two independently filtered torque estimates in N·m. Equal toTe_rawwhen the correspondingtau_Te_*is0.flux_r_mod: rotor flux magnitude in Wb in the stationary frame.i_sd_e,i_sq_e: stator currents in A projected onto the estimated flux frame — the flux-producing and torque-producing components fed back tocurrent_controller_step!.psi_sd_e,psi_sq_e: stator flux linkage in Wb in the same frame.flux_s_mod: stator flux magnitude in Wb.i_alpha,i_beta: the stationary-frame input currents in A, passed through for logging.i_sd_r,i_sq_r: stator currents in A in the rotor frame, the inputs to the two flux filters.lambda_r_alpha,lambda_r_beta: rotor flux in Wb back in the stationary frame, from whichtheta_eis taken.omega_sl: estimated slip speed in electrical rad/s.omega_e: estimated synchronous electrical speed in rad/s,p*omega_m + omega_sl. This is what the current controller's decoupling feedforward and the F1 field-weakening logic should be fed.
See also RotorFluxObserverDiscreteParams.
InductionMachineDrives.RotorFluxObserverDiscreteParams — Type
RotorFluxObserverDiscreteParams(; kwargs...)Machine constants of the discrete rotor-flux and torque observer, consumed by rotor_flux_observer_step!.
These are the observer's assumed machine parameters. The hybrid simulators deliberately allow them to differ from the plant's, through their obs_*_scale keywords, which is how rotor-resistance detuning is studied: tau_r depends on Rr, and a wrong tau_r biases the estimated flux angle and hence the whole field-oriented decomposition.
Fields
Lm: magnetizing inductance in H, the gain from rotor-frame current to rotor flux.Lss: stator self-inductance in H, used only for the stator flux output.Lrr: rotor self-inductance in H. Sets the coupling coefficientk = Lm/Lrrin the torque estimate.tau_r: rotor time constantLrr/Rrin s, the pole of both flux filters and the gain of the slip estimate. Internally floored atTs.p: pole pairs, converting mechanical angle and speed to electrical.Ts: sample time in s.tau_Te_speed: time constant in s of the torque filter intended for a speed loop.0(the default) passesTe_rawthrough unfiltered.tau_Te_torque: same for the torque-loop consumer.0disables it.flux_eps: small flux magnitude in Wb below which the angle is not computed (theta_eis held at0), and which is added to the denominator of the slip estimate. It keeps the observer finite while the machine is still unfluxed at startup.
See also RotorFluxObserverDiscreteState, RotorFluxObserverDiscreteOutput.
InductionMachineDrives.RotorFluxObserverDiscreteState — Type
RotorFluxObserverDiscreteState(; kwargs...)Mutable state of the discrete rotor-flux and torque observer, advanced in place by rotor_flux_observer_step!.
The flux estimate is held in the rotor reference frame, where the rotor flux dynamics reduce to a plain first-order lag driven by the rotor-frame currents. That is what makes the observer two scalar filters rather than a matrix integration.
Fields
lambda_rd_r,lambda_rq_r: rotor flux linkage in Wb, expressed in the rotor frame (trailing_r). Not to be confused with the_equantities in the output, which are in the estimated flux frame.Te_filt_speed_prev,Te_filt_torque_prev: previous outputs in N·m of the two independent optional torque filters. They exist so a speed loop and a torque loop can consume differently smoothed versions of the same estimate.
See also RotorFluxObserverDiscreteParams, RotorFluxObserverDiscreteOutput.
InductionMachineDrives.rotor_flux_observer_step! — Method
rotor_flux_observer_step!(state, p; i_alpha, i_beta, theta_m, omega_m,
reset = false)Advance the discrete rotor-flux and torque observer by one sample period p.Ts.
This is the first block of every hybrid iteration: it turns measured stationary-frame currents and the mechanical angle into the flux angle theta_e, the dq currents, the flux estimate and the torque estimate that all downstream blocks consume. It is the Julia counterpart of the MATLAB function obs_flujo_L1_alfabeta.
It is a current-model observer — it uses currents and rotor position only, no voltage measurement — so it works down to zero speed but inherits the accuracy of the assumed tau_r.
state is mutated in place; the result is a fresh RotorFluxObserverDiscreteOutput.
Arguments
state::RotorFluxObserverDiscreteState: rotor-frame flux estimate and the two torque-filter memories, mutated.p::RotorFluxObserverDiscreteParams: assumed machine constants.i_alpha,i_beta: measured stator currents in A in the stationary alpha-beta frame.theta_m: mechanical rotor angle in rad. Multiplied byp.pto get the electrical rotor angle; it need not be wrapped.omega_m: mechanical rotor speed in rad/s, used only foromega_e.reset: whentrue, zero the flux estimate and both torque filters before stepping.
Steps
- Stationary → rotor frame. Rotate the currents by
theta_r = p*theta_m. - Flux filters. In the rotor frame the rotor flux obeys
tau_r*dλ/dt + λ = Lm*i, discretised asλ ← (1-β)*λ + β*Lm*iwithβ = Ts/tau_rclamped to[0,1]. Two scalars, one per axis. - Rotor → stationary frame, giving
lambda_r_alpha/lambda_r_betaand hencetheta_e = atan(lambda_r_beta, lambda_r_alpha), wrapped to[0, 2π). Belowflux_epsthe angle is held at0rather than taken from noise. - Stationary → estimated flux frame. Project currents and fluxes onto
theta_e, producingi_sd_e,i_sq_e,lambda_rd_eandlambda_rq_e. - Torque and stator flux.
Te_raw = 1.5*p*k*lambda_rd_e*i_sq_ewithk = Lm/Lrr, then the two optional one-pole torque filters. - Slip and synchronous speed.
omega_sl = (Lm*i_sq_e)/(tau_r*lambda_rd_e), withflux_epsguarding the denominator, andomega_e = p*omega_m + omega_sl.
See also current_controller_step!, which consumes i_sd_e, i_sq_e, omega_e and lambda_rd_e, and load_torque_kalman_step!, which consumes the torque estimate.
Load-torque Kalman estimator
InductionMachineDrives.LoadTorqueKalmanOutput — Type
LoadTorqueKalmanOutput(; kwargs...)Result of one load_torque_kalman_step! call, returned fresh each sample.
Fields
TL_hat: estimated load torque in N·m, positive when it accelerates toward positive speed. This is the value to feed to a speed loop'sTL_est.omega_hat: estimated mechanical speed in rad/s after the measurement update.innovation: measurement residual in rad/s,omega - omega_pred. The natural health check — it should look like zero-mean noise. A persistent offset meansJ,BorTsdo not match the real mechanics.K1,K2: the two Kalman gains applied to the innovation this sample.K2is the one that drivesTL_hat; watching it settle shows the filter converging.
See also LoadTorqueKalmanParams.
InductionMachineDrives.LoadTorqueKalmanParams — Type
LoadTorqueKalmanParams(; kwargs...)Model and tuning of the load-torque Kalman estimator, consumed by load_torque_kalman_step!.
J and B define the mechanical model the filter inverts, so an error in them shows up directly as a bias in TL_hat. The three noise variances are the only real tuning knobs: the ratio q_TL/R sets how fast the estimate tracks a changing load versus how much measurement noise it lets through.
Fields
J: total inertia in kg·m², motor plus load.B: viscous friction coefficient in N·m·s/rad.Ts: sample time in s.R: speed measurement noise variance in (rad/s)². Raise it for a noisy encoder; non-positive values fall back to0.01.q_omega: process noise variance on the speed state. Absorbs unmodelled mechanical effects; non-positive values fall back to0.1.q_TL: process noise variance on the load-torque state. The load is modelled as a random walk (TL[k+1] = TL[k]), so this is what allows it to move at all — larger means faster tracking and more noise. Non-positive values fall back to6.0.limit_positive: whentrue, clampTL_hatat zero, as the MATLAB original did for a load that could only brake. Set this tofalsefor AWES profiles, where the tether torque genuinely changes sign between reel-out and reel-in; leaving ittruewould silently discard half the signal.
See also LoadTorqueKalmanState, LoadTorqueKalmanOutput.
InductionMachineDrives.LoadTorqueKalmanState — Type
LoadTorqueKalmanState(; kwargs...)Mutable state of the load-torque Kalman estimator, advanced in place by load_torque_kalman_step!: the two-element state vector x = [omega_hat, TL_hat] plus its 2×2 covariance, stored element by element to keep the block allocation-free.
Fields
omega_hat: estimated mechanical speed in rad/s. Also the measured quantity, so this one is well determined.TL_hat: estimated load torque in N·m, positive when it accelerates toward positive speed (J*dω/dt = Te + TL - B*ω). This is the quantity of interest — it is never measured, only inferred from how the speed deviates from what the applied torque alone would produce.P11,P12,P21,P22: error covariance entries.P22starts large (100.0) because the initial load torque is genuinely unknown, which lets the filter converge quickly at startup.initialized:falseuntil the first step. The first call therefore self-initialises, seedingomega_hatwith the measured speed even ifresetwas not passed.
See also LoadTorqueKalmanParams, LoadTorqueKalmanOutput.
InductionMachineDrives.load_torque_kalman_step! — Method
load_torque_kalman_step!(st, p; omega, Te, reset = false)Advance the load-torque Kalman estimator by one sample period p.Ts and return the current estimate of the unmeasured load torque.
Julia counterpart of the MATLAB function l3_kalman. The load torque is treated as a second state driven by a random walk, so it can be reconstructed from the speed measurement and the known applied torque:
ω[k+1] = (1 - B*Ts/J)*ω[k] + (Ts/J)*TL[k] + (Ts/J)*Te[k]
TL[k+1] = TL[k]
y = ωwhich is the discrete Euler form of J*dω/dt = Te + TL - B*ω. Note the sign that follows from it: the coupling from TL into the speed state is +Ts/J, not -Ts/J, because a positive load torque accelerates toward positive speed in this convention. Getting that backwards makes the estimate converge to the negative of the true load, which then silently reverses the feedforward that consumes it.
st is mutated in place; the result is a fresh LoadTorqueKalmanOutput.
Arguments
st::LoadTorqueKalmanState: state vector and covariance, mutated.p::LoadTorqueKalmanParams: mechanical model and noise tuning.omega: measured mechanical speed in rad/s.Te: applied electromagnetic torque in N·m — the known input. In the hybrid loop this is the observer's torque estimate, not a measurement.reset: whentrue, reinitialise the filter withomega_hat = omega,TL_hat = 0and the default covariance. This also happens automatically on the first call, sincest.initializedstartsfalse.
Steps
- Predict the state through the model above and propagate the covariance, adding
q_omegaandq_TL. - Update with the speed measurement:
S = P11 + R, gainsK1 = P11/SandK2 = P21/S, correction byinnovation = omega - omega_pred. - Clamp, if
p.limit_positive,TL_hatat zero. Leave this off for signed AWES torque profiles. - Symmetrise the covariance, averaging
P12andP21, to keep it from drifting apart numerically over long runs.
Both speed outer loops accept the result as their TL_est argument — see outer_speed_flux_f1_step! and outer_speed_flux_mtpa_step!. Feedforward only engages when their use_load_feedforward is true.