Invalid Control
andrewcharnley opened this issue · 5 comments
Just having a play with this but not having any joy.
int32_t kp = 100;
int32_t ki = 0; //1320*1000;
int32_t kd = 0; //5280 * 1000;
uint8_t qn = 22; // Set QN to 32 - DAC resolution
Pid::PID pidController = Pid::PID(0, kp, ki, kd, qn);
// within init
pidController.setOutputMin(0);
pidController.setOutputMax(4095);
pidController.init(0);
// motion start
pidController.setSetpoint(motion.target); // (83)
Loop fn (64Hz)
uint16_t step = pidController.compute(0);
//printf("request %d, step %d \n", motion.kph, step);
MOTION_PWM_SET(step); // ERROR -> step is always around the 400 mark.
Hi,
thanks for getting in touch. Which platform are you using? ARM + DSP or something else? This might affect the code path taken.
At first glance I see a problem with the code you have posted.
- qn is set to 22 (this assumes a 10 bit DAC)
- setOutputMax() is set to 4095 (12 bit DAC)
I presume you meant to set qn to 20, because this was copied over from the example.
The output of the compute() method is an offset binary signed integer. It is intended to be fed to a DAC, whose midscale output represents 0.
Using your numbers, I get the expected output of 511 (qn=22) or 2047 (qn=20), which is midscale. The reason is, that kp was set to 100, which in Q10.22 (or Q12.20) notation is either 100/(2^22) or 100/(2^20). Multiplied with the input (83), this is far from 1, so it will be rounded down to zero. Hence, the output will stay at midscale.
Now you say compute() returned something around 400? Can you post a more complete code example, because I cannot confirm that for the numbers given. I do get the expected 511/2047.
Hi,
To be honest I had trouble making CMSIS Pid work (still do, persistently tracks incorrectly).
I wasn't sure what qn was and left it as is. I've since removed the code for your library but am willing to give it another try. The code for CMSIS Pid is -. I''m calling it at 16Hz, it''s controlling a motor with a large flywheel, slow with lots of inertia (likes to PID overshoot). motion.kph is only updated at about 2Hz. PWM is 0-4095.
arm_pid_instance_f32 pid;
void motionInit (void) {
pid.Kp = 20;
pid.Kd = 5;
pid.Ki = 0;
arm_pid_init_f32(&pid, 1);
}
void pidTasker (void) {
float32_t value = arm_pid_f32(&pid, motion.target - motion.kph);
if (value < 0.0) {
MOTION_PWM_SET(0);
} else {
uint16_t pwm = value;
if (pwm > CFG_PWM_COUNT) {
pwm = CFG_PWM_COUNT;
}
MOTION_PWM_SET(pwm);
}
}
I do recommend updating the PID at the same frequency as the process variable.
QN is the fixed point binary format used: https://en.wikipedia.org/wiki/Q_(number_format)
More precicely Q32-N.N. Internally the library uses a 32 bit integer to do the math, this can then be split in 32-N integer bits and N fractional bits. I should probably document this better.
The output of the library is designed to be fed to a DAC should have zero drive at midpoint.
I assume in your situation, you do not have a means to break and rely on friction only. Is that correct?
If so, if would suggest the following setup parameters:
QN = 32 - 13; // Yes, that is a 13-bit DAC.
pidController.setOutputMin(4096);
pidController.setOutputMax(8191);
When calculating subtract 4095
uint16_t step = pidController.compute(motion.kph) - 4096;
The reason, I set QN to 32-13 is, that you must get rid of the negative plane (if you cannot break). The PID internally uses a signed integer for its calculations, but limits the output to the positive plane (min=4096).
If your flywheel has lots or inertia, I would suggesst setting kd = 0 and try a PI controller first.
The issue seems to be resolved. Closing.
Apologies for my delay, lcd went off and had to sort a replacement. Aim is to implement this weekend.
You are correct I have no brake, for now. The flywheel has plenty of interia and yes I'll be aiming for PI first then the tuning - usual PID approach I think!