forked from M-Labs/artiq
suservo: swap transfer function parametrization
The integrator is now parametrized through its gain and not the PI corner frequency. The integrator gain limit is given in absolute gain units and not relative to the proportional gain. close #1033
This commit is contained in:
parent
e408241233
commit
87d3ac9d25
|
@ -348,7 +348,7 @@ class Channel:
|
||||||
self.servo.write(base + 7, b0)
|
self.servo.write(base + 7, b0)
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def set_iir(self, profile, adc, gain, corner=0., limit=0., delay=0.):
|
def set_iir(self, profile, adc, kp, ki=0., g=0., delay=0.):
|
||||||
"""Set profile IIR coefficients.
|
"""Set profile IIR coefficients.
|
||||||
|
|
||||||
This method advances the timeline by four servo memory accesses.
|
This method advances the timeline by four servo memory accesses.
|
||||||
|
@ -361,25 +361,23 @@ class Channel:
|
||||||
coefficient quantization errors):
|
coefficient quantization errors):
|
||||||
|
|
||||||
.. math::
|
.. math::
|
||||||
H(s) = K \\frac{1 + \\frac{s}{\\omega_0}}
|
H(s) = k_p + \\frac{k_i}{s + \\frac{k_i}{g}}
|
||||||
{\\frac{1}{g} + \\frac{s}{\\omega_0}}
|
|
||||||
|
|
||||||
Where:
|
Where:
|
||||||
* :math:`s = \\sigma + i\\omega` is the complex frequency
|
* :math:`s = \\sigma + i\\omega` is the complex frequency
|
||||||
* :math:`K` is the proportional gain
|
* :math:`k_p` is the proportional gain
|
||||||
* :math:`\\omega_0 = 2\\pi f_0` is the integrator corner frequency
|
* :math:`k_i` is the integrator gain
|
||||||
* :math:`g` is the integrator gain limit
|
* :math:`g` is the integrator gain limit
|
||||||
|
|
||||||
:param profile: Profile number (0-31)
|
:param profile: Profile number (0-31)
|
||||||
:param adc: ADC channel to take IIR input from (0-7)
|
:param adc: ADC channel to take IIR input from (0-7)
|
||||||
:param gain: Proportional gain (1). This is usually negative (closed
|
:param kp: Proportional gain (1). This is usually negative (closed
|
||||||
loop, positive ADC voltage, positive setpoint). When 0, this
|
loop, positive ADC voltage, positive setpoint). When 0, this
|
||||||
implements a pure I controller with unit gain frequency at
|
implements a pure I controller.
|
||||||
`corner` (use the sign of `corner` for overall gain sign).
|
:param ki: Integrator gain (rad/s). When 0 (the default)
|
||||||
:param corner: Integrator corner frequency (Hz). When 0 (the default)
|
this implements a pure P controller. Same sign as ``kp``.
|
||||||
this implements a pure P controller.
|
:param g: Integrator gain limit (1). When 0 (the default) the
|
||||||
:param limit: Integrator gain limit (1). When 0 (the default) the
|
integrator gain limit is infinite. Same sign as ``ki``.
|
||||||
integrator gain limit is infinite. Positive.
|
|
||||||
:param delay: Delay (in seconds, 0-300 µs) before allowing IIR updates
|
:param delay: Delay (in seconds, 0-300 µs) before allowing IIR updates
|
||||||
after invoking :meth:`set`. This is rounded to the nearest number
|
after invoking :meth:`set`. This is rounded to the nearest number
|
||||||
of servo cycles (~1.2 µs). Since the RF switch (:meth:`set`) can be
|
of servo cycles (~1.2 µs). Since the RF switch (:meth:`set`) can be
|
||||||
|
@ -390,47 +388,34 @@ class Channel:
|
||||||
"""
|
"""
|
||||||
B_NORM = 1 << COEFF_SHIFT + 1
|
B_NORM = 1 << COEFF_SHIFT + 1
|
||||||
A_NORM = 1 << COEFF_SHIFT
|
A_NORM = 1 << COEFF_SHIFT
|
||||||
PI_TS = 3.1415927*T_CYCLE
|
|
||||||
COEFF_MAX = 1 << COEFF_WIDTH - 1
|
COEFF_MAX = 1 << COEFF_WIDTH - 1
|
||||||
|
|
||||||
gain *= B_NORM
|
kp *= B_NORM
|
||||||
corner *= PI_TS
|
if ki == 0.:
|
||||||
|
|
||||||
if corner == 0.:
|
|
||||||
# pure P
|
# pure P
|
||||||
a1_ = 0
|
a1 = 0
|
||||||
b1_ = 0
|
b1 = 0
|
||||||
b0_ = int(round(gain))
|
b0 = int(round(kp))
|
||||||
else:
|
else:
|
||||||
a1_ = A_NORM
|
# I or PI
|
||||||
if gain == 0.:
|
ki *= B_NORM*T_CYCLE/2.
|
||||||
# pure I
|
if g == 0.:
|
||||||
b0 = (2*B_NORM)*corner
|
c = 1.
|
||||||
b1_ = 0
|
a1 = A_NORM
|
||||||
else:
|
else:
|
||||||
# PI
|
c = 1./(1. + ki/(g*B_NORM))
|
||||||
k = gain*corner
|
a1 = int(round((2.*c - 1.)*A_NORM))
|
||||||
b1 = k - gain
|
b0 = int(round(kp + ki*c))
|
||||||
b0 = k + gain
|
b1 = int(round(kp + (ki - 2.*kp)*c))
|
||||||
if limit != 0.:
|
if b1 == -b0:
|
||||||
# PI with limit
|
raise ValueError("low integrator gain and/or gain limit")
|
||||||
q = corner/limit
|
|
||||||
qr = 1./(1. + q)
|
|
||||||
a1_ = int(round(a1_*(1. - q)*qr))
|
|
||||||
b0 *= qr
|
|
||||||
b1 *= qr
|
|
||||||
b1_ = int(round(b1))
|
|
||||||
b0_ = int(round(b0))
|
|
||||||
|
|
||||||
if b1_ == -b0_:
|
if (b0 >= COEFF_MAX or b0 < -COEFF_MAX or
|
||||||
raise ValueError("low corner, gain, limit")
|
b1 >= COEFF_MAX or b1 < -COEFF_MAX):
|
||||||
|
raise ValueError("high gains")
|
||||||
if (b0_ >= COEFF_MAX or b0_ < -COEFF_MAX or
|
|
||||||
b1_ >= COEFF_MAX or b1_ < -COEFF_MAX):
|
|
||||||
raise ValueError("high corner, gain, limit")
|
|
||||||
|
|
||||||
dly = int(round(delay/T_CYCLE))
|
dly = int(round(delay/T_CYCLE))
|
||||||
self.set_iir_mu(profile, adc, a1_, b0_, b1_, dly)
|
self.set_iir_mu(profile, adc, a1, b0, b1, dly)
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def get_profile_mu(self, profile, data):
|
def get_profile_mu(self, profile, data):
|
||||||
|
|
|
@ -50,9 +50,9 @@ class SUServo(EnvExperiment):
|
||||||
self.suservo0_ch0.set_iir(
|
self.suservo0_ch0.set_iir(
|
||||||
profile=0,
|
profile=0,
|
||||||
adc=7, # take data from Sampler channel 7
|
adc=7, # take data from Sampler channel 7
|
||||||
gain=-.1, # -0.1 P gain
|
kp=-.1, # -0.1 P gain
|
||||||
corner=70*Hz, # very low corner frequency
|
ki=-300./s, # low integrator gain
|
||||||
limit=0., # unlimited integrator gain
|
g=0., # no integrator gain limit
|
||||||
delay=0. # no IIR update delay after enabling
|
delay=0. # no IIR update delay after enabling
|
||||||
)
|
)
|
||||||
# setpoint 0.5 (5 V with above PGIA gain setting)
|
# setpoint 0.5 (5 V with above PGIA gain setting)
|
||||||
|
|
Loading…
Reference in New Issue