Obs

OBS #

Brief #

The obs component estimates the flux angle and speed from voltage and current, for a PMSM and an induction motor alike. It generalises sensorless (which stays for old configs) and fixes its undamped loop.

Model #

In a frame at angle pos turning at vel, the back emf is

ed = ud - r id - ld d(id)/dt + vel lq iq eq = uq - r iq - lq d(iq)/dt - vel ld id

PMSM: ld, lq, and the emf is vel * psi on q. Induction motor: ld = lq = sigma*Ls, and the emf is the rotor flux’s, vel * lmr * i_mr on q. Either way a frame lagging the flux by d reads ed = -e sin d, eq = e cos d times the sign of vel, so the angle error is -sign(vel) ed / |e|. ed/eq alone would also lock half a turn off.

Frames #

The feedback (hv0.id_fb …) is in the frame hv0 used, pos_ref (angle0.pos) plus vel_ref * adv (hv0.adv). obs rotates it into its own frame first, so it can run as a shadow while angle0 follows the encoder (src 0 or 1), and pos_err = pos - (pos_ref + vel_ref adv) shows how far it is from the frame hv0 used, the encoder’s angle in shadow. Run obs before angle0 (lower rt_prio): it then reads the angle the feedback was taken with. A constant pos_err growing with speed is timing, not model, and adv trims it. hv0 adds vel * adv again, so commutate from pos_c, which takes it off: in shadow pos_c is the encoder’s angle.

Loop #

A PI phase locked loop on the angle error: pos += (vel + kp err) T, vel += ki err T, kp = 1.4 bw, ki = bw^2. Below e_min volts of emf the error is not trusted: the loop holds its speed (ok = 0). Start it from a known angle and direction (track, or I/f): at standstill the sign of vel is a guess. track = 1 sets pos to the feedback frame, pos_ref + vel_ref * adv (the frame the free running pos is given in, so pos_err reads 0), and vel to vel_ref, for a sequencer that hands over to it.

vel_m is the rotor speed, (vel - slip) / polecount. vel only follows a slip step through the loop, so the slip is passed through a copy of the loop before it is taken off; taken straight, a step in iq would dip vel_m for about 1 / bw, the speed loop would ask for more iq, and with little flux (deep field weakening) that rings at about the observer bw. With track = 1 the slip is taken straight.

max_vel (default 3000 rad/s electrical, 477 Hz) clamps vel. On the F4 (5 kHz) that is about 10 samples per electrical period, which is also about where the F4 observer runs out. There is no faster path: an F3 observer overran the F3 rt on the spindle (fault 9) and was removed, so keep conf0.max_vel below this. On X the angle error stayed flat (0.015-0.021 rad) from 200 to 800 rad/s electrical.

Pins #

NameDescription
rparameter, winding resistance [ohm]
r_wparameter, d loss growing with speed [ohm per rad/s electrical], r + r_w * abs(vel), 0 = none
ldparameter, d inductance [H] (induction motor: sigma*Ls)
lqparameter, q inductance [H], 0 = ld
polecountparameter, pole pairs
bwparameter, loop bandwidth [rad/s]
klparameter, current derivative low pass, 0..0.99
e_minparameter, emf below which the angle is not trusted [V]
max_velparameter, speed clamp [rad/s electrical]
advparameter, feedback frame lead over pos_ref [s], hv0.adv
idinput, hv0.id_fb
iqinput, hv0.iq_fb
udinput, hv0.ud_fb
uqinput, hv0.uq_fb
pos_refinput, angle the feedback frame was at, angle0.pos
vel_refinput, angle0.vel
slipinput, induction motor slip [rad/s electrical], 0 on a PMSM
trackinput, 1 = pos follows pos_ref + vel_ref * adv, vel follows vel_ref
pos_chkinput, encoder angle to compare with while commutating from obs
posoutput, flux angle in the feedback frame [rad electrical]
pos_coutput, pos - vel * adv, the angle to commutate with, angle0.pos_obs
veloutput, synchronous speed [rad/s electrical]
vel_moutput, rotor speed [rad/s mechanical], (vel - slip) / polecount, slip with the loop’s lag
edoutput, d emf in the observer’s frame [V]
eqoutput, q emf [V]
erroutput, angle error the loop sees [rad]
pos_erroutput, pos - (pos_ref + vel_ref * adv) [rad]
chk_erroutput, pos - (pos_chk + vel_ref * adv) [rad]
okoutput, 1 = emf above e_min for 50 ms

References #

Source: .c File