I've been attempting to use the ARM CMSIS PID Controller function in a project to control the temperature of a peltier. A little bit about the specs: I have three temperature values that need to be achieved, one about 15C, another about 5C, and a third at -10C. I'm actively controlling the cooling only and letting it heat on it's own. I have a temperature sensor attached to a small aluminum block which sits on the cold side of the peltier. I'm using PWM to control the power to the peltier through a MOSFET. If it makes any difference, the microcontroller is an STM32 Cortex M4F.
I would like to be able to control the temperature to within 0.1C of the set temperature (my testing so far has achieved this). The temperature sensor is good enough to resolve 0.01C, and the PWM values are between 0 and 3199 (due to the clock prescaling to get the frequency I want)
Everything works great except that the PID controller never quite reaches the set temperature. At the high temperature the error is not important, but is close to 0.2C too warm. At the middle temperature the temperature is also not terribly important, but the error is closer to 0.6C too warm. The coldest temperature is the most critical, and this is where the error offset is the greatest, around 1C too warm. By offset I mean that my set temperature is -10C and the PID function will stabilize at -9C, even though the error input into the PID function is 1C. I've gotten around this by subtracting an 'offset' value before passing the set temperature to the PID function, but I'm concerned that this may cause issues in the future as the peltier and the insulation around the cooler age.
Part of my confusion stems from the fact that I don't understand what kind of values the PID function should return and how to get them to map to my PWM values. What I've done is to scale the PID gain values until the numbers being output from the function are within reason and then limit them. Is there a better way to go about doing this? Is this part of my issue with the offsets?
Here's my cooler control function
void Cooler_Control(sensorStruct *cooler, arm_pid_instance_f32 *pid)
{
cooler->pid_error = cooler->temperature - (cooler->set_temperature - cooler->offset);
// Gain Scheduling: Change PID gains based on error difference
// Power should be 100% on or 100% off when temperature is
// outside 2C of set point
if (cooler->pid_error < 2.0 && cooler->pid_error > -2.0)
{
pid->Kp = 320; //25 | 10
pid->Ki = 0.32; //70 | .1
pid->Kd = 1600; //5 | 50
// Re-Initialize PID values
arm_pid_init_f32(pid, 1);
}
else
{
pid->Kp = 2500;
pid->Ki = 0;
pid->Kd = 0;
// Initialize PID values
arm_pid_init_f32(pid, 1);
}
cooler->pid_output = arm_pid_f32(pid, cooler->pid_error);
if (cooler->pid_output > 3199)
{
cooler->pwm_duty = 3199;
}
else if (cooler->pid_output < 0)
{
cooler->pwm_duty = 0;
}
else cooler->pwm_duty = (uint16_t)cooler->pid_output;
// Update Cooler PWM
set_cooler_pwm(cooler->pwm_duty);
}