Notice. New forum software under development. It's going to miss a few functions and look a bit ugly for a while, but I'm working on it full time now as the old forum was too unstable. Couple days, all good. If you notice any issues, please contact me.
|
Forum Index : Microcontroller and PC projects : PID in MMBasic
Author | Message | ||||
matherp Guru Joined: 11/12/2012 Location: United KingdomPosts: 9100 |
The Pico2 gives more power than we had previously and potentially opens up new applications. I note that we have serious users of control type technologies so I thought it might be helpful to embed some semi-automatic PID functionality in MMbasic that could run multiple PID loops in parallel. The concept could be as follows: DEVICE PID INIT channel%, pid_params!(), callback_function pid_params!() float Kp float Ki float Kd ' Derivative low-pass filter time constant float tau 'Output limits float limMin float limMax 'Integrator limits float limMinInt float limMaxInt 'Sample time (in milliseconds) float T 'Controller "memory" float integrator float prevError 'Required for integrator' float differentiator float prevMeasurement 'Required for differentiator 'Controller output float out DEVICE PID START channel DEVICE PID STOP channel output! = DEVICE(PID channel, setpoint!, measurement!) The PID init would set up a MMBasic function to be called every T milliseconds. Following DEVICE PID START the callback function (MMBasic interrupt) would be called as requested and the Basic program should update the algorithm using the DEVICE(PID function. It is left to the MMBasic user to derive the measurement (e.g. ADC input) and set the output (e.g. adjust PWM duty) in the callback function (NB: slow measurements like ADC input could be run in the main program so the value is always available for the PID callback). Does this make sense? Would it be useful? |
||||
PhenixRising Guru Joined: 07/11/2023 Location: United KingdomPosts: 856 |
Oh Wonderful If we want to include feedforwards (PID only reacts to error, FF anticipates), this can be added to the output |
||||
twofingers Guru Joined: 02/06/2014 Location: GermanyPosts: 1236 |
Yes, it is useful! Best regards Michael causality ≠ correlation ≠ coincidence |
||||
Mixtel90 Guru Joined: 05/10/2019 Location: United KingdomPosts: 6768 |
I'd have killed for something like that for temperature controllers before I retired. :) It was a real pain getting something usable on cheap PLCs at the time. Mick Zilog Inside! nascom.info for Nascom & Gemini Preliminary MMBasic docs & my PCB designs |
||||
PhenixRising Guru Joined: 07/11/2023 Location: United KingdomPosts: 856 |
You need to come out of retirement....opportunity abounds You have a secret weapon that others will turn their nose up at. |
||||
PhenixRising Guru Joined: 07/11/2023 Location: United KingdomPosts: 856 |
Pete, When someone recommended being very specific when asking a question of ChatGPT, I asked it for Galil Motion Control's notch filter in Basic code. Apparently, it can be used if one encounters a resonant frequency on a machine axis that the PID can't eliminate. Never experienced the problem, myself so I can't vouch for its efficacy. ' BASIC-style pseudocode for a notch filter ' Initialize parameters center_freq = 50 ' Center frequency in Hz (f_0) sampling_rate = 1000 ' Sampling rate in Hz bandwidth = 5 ' Bandwidth in Hz attenuation = -20 ' Attenuation in dB ' Derived constants omega_0 = 2 * PI * center_freq / sampling_rate ' Digital radian frequency bw = bandwidth / sampling_rate ' Bandwidth in terms of the sampling rate d = -attenuation / 20 ' Attenuation factor alpha = sin(omega_0) / (2 * bw) ' Filter sharpness ' Filter coefficients b0 = 1 b1 = -2 * cos(omega_0) b2 = 1 a0 = 1 + alpha a1 = -2 * cos(omega_0) a2 = 1 - alpha ' Normalize coefficients b0 = b0 / a0 b1 = b1 / a0 b2 = b2 / a0 a1 = a1 / a0 a2 = a2 / a0 ' Variables for storing past input/output values x_1 = 0 x_2 = 0 y_1 = 0 y_2 = 0 'Main loop (input is the signal you want to filter) DO WHILE (true) 'Assume input_signal is the current sample of the input signal input_signal = GET_INPUT() ' Get input signal value 'Apply the notch filter difference equation output_signal = b0 * input_signal + b1 * x_1 + b2 * x_2 - a1 * y_1 - a2 * y_2 'Update past values x_2 = x_1 x_1 = input_signal y_2 = y_1 y_1 = output_signal print input_signal print output_signal 'Output the filtered signal SEND_OUTPUT(output_signal) LOOP |
||||
matherp Guru Joined: 11/12/2012 Location: United KingdomPosts: 9100 |
OK, I'll give it a go. Thinking about it the functions should be MATH not DEVICE but otherwise the concept will do for a first cut. |
||||
matherp Guru Joined: 11/12/2012 Location: United KingdomPosts: 9100 |
Have a play with this VGARP2350 only for the moment. C code and example courtesy of Philip Salmony https://github.com/pms67/PID VGARP2350.zip Example code - should be self-explanatory Option explicit ' ' Array positions for PID parameters and working variables ' Const Kp=0 Const Ki=1 Const Kd=2 Const tau=3 Const limMin=4 Const limMax=5 Const limMinInt=6 Const limMaxInt=7 Const T=8 Const integrator=9 Const prevError=10 Const differentiator=11 Const prevMeasurement=12 Const out=13 ' Dim pid_params(13) ' pid_params(Kp)=2.0 pid_params(Ki)=0.5 pid_params(Kd)=0.25 ' Derivative low-pass filter time constant pid_params(tau)=0.02 'Output limits pid_params(limMin)=-10.0 pid_params(limMax)=10.0 'Integrator limits pid_params(limMinInt)=-5.0 pid_params(limMaxInt)=5.0 'Sample time (in seconds) pid_params(T)=0.01 'Controller "memory" pid_params(integrator)=0.0 pid_params(prevError)=0.0 'Required for integrator' pid_params(differentiator)=0.0 pid_params(prevMeasurement)=0.0 'Required for differentiator 'Controller output pid_params(out)=0.0 Dim float setpoint=1.0 Dim integer near_enough=0 Timer =0 ' ' Initialise a PID loop with a 10mSec loop time ' Math PID INIT 1,pid_params(),pidint ' ' Start the PID controller ' Math pid start 1 Do Loop Until near_enough Print "test over" Math pid stop 1 Math pid close 1 End ' 'Subroutine called every 10mSec ' Sub pidint Local float measurement=test(pid_params(out)) 'simulate an input measurement Local float output=Math(PID 1,setpoint,measurement) 'update the controller Print Int(Timer),Str$(measurement,8,5),Str$(output,8,5) If Abs(measurement-setpoint)<0.00001 Then near_enough=1 End Sub Function test(inp As float) As float Static float output=0.0 Const alpha=0.02 output=(pid_params(T)*inp + output)/(1.0 + alpha * pid_params(T)) test=output End Function Tested with T at 0.001 seconds and it still works fine. Of course you wouldn't have a print statement in the interrupt routine in a real-world application Edited 2024-09-18 02:54 by matherp |
||||
PhenixRising Guru Joined: 07/11/2023 Location: United KingdomPosts: 856 |
Can't test atm but to give an idea: I have: Full PID Offset (DAC bias) Integrator limit Feedforward Velocity calc Velocity profiler (accel, slew, decel) All happening ~400 usec No PWM writing (dog slow) How do we compare? Incidentally, I disagree with pretty much everyone who claims to be experienced in this field. The I term has no business being deployed during motion; feed forward does a much better job of keeping following error to a minimum. The I term should be deployed at ~steady state to eliminate any remaining error. |
||||
Mixtel90 Guru Joined: 05/10/2019 Location: United KingdomPosts: 6768 |
Did your system cost £5? I thought not. :) The I term is used a heck of a lot in H&V systems, particularly heating. At least it used to be - I'm out of touch now. Mick Zilog Inside! nascom.info for Nascom & Gemini Preliminary MMBasic docs & my PCB designs |
||||
PhenixRising Guru Joined: 07/11/2023 Location: United KingdomPosts: 856 |
Ah, you got me there. No, my Pico cost me £1.75 You can't compare heating with motion. I have been spinning motors since Volhout gave us quad decode. Dead nuts repeatability and stability... Even when I crank it down to a 4ms loop-rate. |
||||
PhenixRising Guru Joined: 07/11/2023 Location: United KingdomPosts: 856 |
Imagine being in your car and the shock absorber blows. You just set your D term to zero and you're bouncing all over the place due to the spring which is the P term but this spring becomes extra aggressive and puts you into a violent oscillation...that's your I term. |
||||
Mixtel90 Guru Joined: 05/10/2019 Location: United KingdomPosts: 6768 |
I is used for bang-bang control of heating, where the heating pulse rate is changed to control the heater rather than a linear controller. It used to be quite common in kilns where the high current was difficult (or even impossible) to control with linear devices. Mick Zilog Inside! nascom.info for Nascom & Gemini Preliminary MMBasic docs & my PCB designs |
||||
PhenixRising Guru Joined: 07/11/2023 Location: United KingdomPosts: 856 |
Well it ain't your friend during a motion profile. I have read countless papers where they specify an I term...did they ever run a motor actually coupled to a drive train? I highly doubt it. The proportional term reacts to a difference between command and actual and so, naturally, there is a lag. The most effective method of compensating for this is a feed forward term, external to the PID filter. Unlike the I term, loop stability can never be affected. Steady state, apply the I term and any residual error is wiped out. My error is not +1 count or -1 count, it's absolute zero. So geeked |
||||
Print this page |