Probably a lot of our problems of the PID tuning of the cavity temperature controls (for controlling BN freq) come back back to our P, I and D constants being too course. This is a hangover from earlier implementations of this script where the precision of the constants was set by limits on the EPICS channels max significant figures.
We no longer use the finite difference approximation for our discreet digital PID controller and are free to use a lot more precision on the accumulated integrator values.
I've made a modified version of the PIDLocker_beta.py script that divides the value of the P, I and D slider values by 1000 before doing all the usual operations to compute the actuation value. Here the integrator variable in the python script holds the previous loops value over and adds the error on every loop cycle at maximum precision allowed by numpy. The value of the actuation is then only rounded to 5 SF at the very last step.
To assist further with fine control of the heating I also changed the way the process variable is rounded before writing out to EPICS. The current operation in PIDLocker_beta.py (see PSL:2142) is to round up based on the lowest digit so 0.770465 > 0.77047 and 0.770463 > 0.77046. A better way is to do probabilistic rounding. If the last digit to be rounded off is 3 then there is a 30% probability of rounding up and 70 % probability of rounding down. Its a kind of poor man's oversampling. It allows us to get intermediate bits between ADC levels.
The script is a hacked version of PIDLocker_beta.py and is called PIDLocker_cavcontrol.py located in the ~/Git/cit_ctnlab/ctn_scripts dir. I've attached a copy (zipped) below and have committed into the git. During testing the script is running on a tmux session on ws1 using:
> python PIDLocker_cavcontrol.py PIDConfig_CAVHeater.ini
We only need a gentle push from the integrator to hold the BN in place and before it was overkill. Hopefully by lowering the gain on the 'I' term the 1/f component of the PID will not exceed the corner frequency of the cavity+heater system and stop the instabilities that we were seeing to this point.
For reference the probabilistic rounding code is given below:
def truncFloat(x,dp):
'''Truncates a numpy float to given decimal places (dp)'''
return np.trunc(x * 10 ** dp) / 10 ** dp
def probRound(x, dp):
'''Rounds float to given decimal places (dp), with
last digit round done on a probabilistic basis.'''
xTrunc = truncFloat(x, dp) # Truncate to dp figures
xrem = x  xTrunc # find remainder of truncation
p = np.abs(xrem) * 10 ** dd # prob of rounding up based on trunc remainder
if np.random.uniform(0,1) < p:
xout = xTrunc + np.sign(x) * 10 ** dd
else:
xout = xTrunc
return xout
Quote: 
This test relay test was a failure. I got impatient late last night and downed the midpoint diff heating to 0.5732 W. There wasn't enough range on the upper side of the relay switch and BN flew past the 100 MHz setpoint and was around 400 MHz about 8:30 am this morning.
I did a bit of manual tuning driving the diff heater slider hard to 0.9732 W (common heater 0.48710 W) and then breaking the BN swing driving the opposite direction with diff heater of 0.4732 W for about 30 min. I'm now at about diff 0.73320 W and the BN is beginning to settle about 80 MHz on the other side of the BN 0 Hz crossing point. I think right now we should let it passively settle again so we can get another VCO=10 kHz/V BN measurement to see if recently reduced noise of NF1811 makes a difference (see PSL:2288).

