-
Notifications
You must be signed in to change notification settings - Fork 17
This issue was moved to a discussion.
You can continue the conversation there. Go to discussion →
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Noisy thermocouple input #3
Comments
Oh, I'm always interested in test results, the log files are very helpful. I see the resolution is ±0.25 and as I scroll down through the settling mode data, it jumps by 2 resolution-steps max, but follows a very gradual warming trend. Yes, this test algorithm is quite tolerant to noise ... on the TCLab I was able to get results with a step change of 1, but had the board in a covered case so any moving air wouldn't cause a disturbance. In the next release, It shouldn't be a problem to add a reset function, option to print instantaneous values and a looser tolerance for settling so the next step test can begin sooner. |
I'm afraid that the auto-tuned parameters did not work at all. I think it might be a scaling problem. Let's say I use the values from the pid4.log file, Kp 0.0230 K 0.0013 Kd 0.0145 The output went from 100 to 400 (out of a possible 2000). But when I use those values in my program, the PWM out value is around 3 (out of 2000) even if the temperature is 30C away from the goal and dropping. after 100 seconds, PWM out is 5, and the temperature keeps dropping fast (it requires at least 300 to keep an higher than room temperature stable) What am I doing wrong? Is there a scaling parameter I'm missing? |
Not sure what board you're using ... is it 8-bit PWM? Seems that the process gain is too low .. might need to scale the output range and/or min and max output limits. This is sTune's internal calculation for process gain: // calculate the process gain
_Ku = abs((pvMax - pvStart) / (_outputStep - _outputStart)); Also might need to use Arduino's I'll have a new revision ready shortly that addresses the issues (tested on my TCLab setup) ...
|
I've pushed an update to the repository. |
Sorry, I mentioned it elsewhere. My system is made of a 300w heating plate controlled by an SSR. Since you can't really PWM an SSR, I used what most people do: I selected a 2000msec window, and the PWM range is 0 to 2000. When the output is, say, 1000, the SSR is on for 1 second, off for one. With 1500, on for 1.5 seconds, off for 0.5 seconds. I read the temperature every second. It's a very slow system. If the temperature is dropping, say from 100C and I apply 100% power, the temperature keeps dropping another 5-10 seconds (2-3C drop) before it starts stabilizing, and it takes at least another 5-6 seconds to cross 100C again So I need to understand what range your code uses (i.e. what is 0% and 100%), and map it to my values. That is for both the range and the pvStart/pvStop values. And also select the right values for pvStart and pvEnd It would be very easy for users if your code always assumed, say, 0 to 100 range, so that I could map 0 to 0 and 100 to 2000. And, once the parameters are calculated, then I could use the same scaling in the code (basically, in my case, multiply everything by 20). It might be already the case, btw, I'm just a bit lost at this point, I'm afraid |
Ah, sorry ... I was mistakenly thinking of hardware PWM and PID output range and limits. sTune only directly sets an output value as entered. In this case, its just a slowly timed output with an on time of 0-2000ms. sTune works with seconds for time units, so this might explain the low tuning values. After manually recalculating results from pid4.log, I get: Output Control Range is 0.000 to 2.000 seconds
Output Start: 0.10
Output Step: 0.40
Process Gain: 38.33
Kp: 23.0
Ki: 1.3
Kd: 14.5 No mapping required as the process gain is just ... |
The part I'm still not sure about, is if the above are values are out by a factor of 60. The tuning rule constants (I'm quite sure) are based on minutes. In sTune.cpp lines 193-194, I've already divided by 60 to convert to seconds which works well with true PWM based control. However, controlling a digital output based on time might require a different conversion ... I'll need to do some research on this as I'm not a PID guru. |
One thing I'm noticing, using Kp 23, Ki 1.3, and Kd 14.5 is that the system overshoots (expected), then slowly turns the output lower until 0% output. Let's say my set temperature is 100C. The PID algorithm starts from output 20%, say, al the way to 100%. at around 90C, the output starts dropping, but not fast enough, and it overshoots by 15-20C. Then the output goes to 0 and the temperature drops. I would expect that around 105C or so the PID output started rising, to slow the undershoot. But it stays at 0 until the measure temperature drops below 100C, and only at that point it slowly starts raising the output, and it undershoots by at least 10C. My system is such that to maintain a 100C temperature, it needs a constant 20-25% output. So now it starts again to increase the output until it overshoots to, say 110C. But from this point on it never converges, as it always waits for the temperature to drop below 100C to start heating again I have an espresso coffee machine with a PID, which in many ways it's similar to my plate heater: very slow to heat, and requires constant output to maintain the temperature once reached. That PID was auto tuned, and it works exceptionally well. IT overshoots target by a bit on the first rise, starts slowing down the drop before the setpoint, undershoots and overshoots once more by 2-3C, then it's rock solid. The key is that I can see its starting to increase the output while the temperature drops, well before it drops under the setpoint. That is a commercial PID, and it uses an SSR as well I have an encyclopedic ignorance when it comes to PIDs (i.e. what I don't know can fill a volume :), so I have no idea what changes I should make to help "brake" the descent below the setpoint. |
To add: the PID for the espresso machine is a Fuji PXR3 (https://americas.fujielectric.com/files/prod_selector_v/Micro%20Controller%20X%20Operation%20Manual%20Model%20PXR3%20(ECNO%20409d).pdf?r=false) and according to the manual the ranges for P, I and D are P 0.0 to 999.9% and my autotuned values for that are P=4.3, I=95 and D=18.3 But P doesn't seem to be the same as in your library, since the manual says "When is too small, control will be unstable, and when is too large, the response will be delayed.", and from what I can see this behaves differently in my code |
This sounds good. Should expect up to 25% overshoot using |
The overshoot is not the problem, sorry if I was not clear. That will get smaller over time, if the undershoot also improves. The current problem is that it always undershoots by 10C, no matter what the starting point is. Since it always undershoots the same amount, it never converges. And the problem is that the output doesn't start increasing until after the input temperature goes below the setpoint. To avoid undershoot, the system should start increasing the output once the temperature is dropping and it's 2-3C above setpoint. Instead it only starts after the drop. And the system inertia means that it wll always undershoot the same amount unless the PID output starts increasing sooner |
In this case, P = 4.3 x 100 = 430% |
I'll take a closer look at the code when I get a chance later ... perhaps the output temporarily reverts to outputStart value in between step tests or from step test completion to initializing and starting the PID. Or perhaps the PID needs to start with the outputStep value so it doesn't try to climb from 0 output. |
I ran a few tests using the old Autotune library https://github.com/br3ttb/Arduino-PID-AutoTune-Library. The average of a few runs (even when using wildly different tuning ranges) was Kp=33, Ki=0.17 and Kd=0. Using those parameters, I get really good performance: minimal overshot, stabilizes without oscillations (even if the output stays at 0 when dropping). I'm enclosing the data from my program (not the autotune) I first set a holding temperature of 100C from room temperature. Once it stabilizes at 100C, I drop the setpoint to 70C and wait for stable output again. Considering how slow my system is to cool (and can heat much faster than cooling), a great performance. Not sure why the original algorithm found much better values. Happy to run any test for you if you like |
Interesting. I haven't used the PID-AutoTune-Library before but I have seen it. Do you have an example of how you're using it in code? Also curious how long the AutoTune test run takes. If you're using QuickPID, note that it defaults to a more advanced anti-windup mode that reduces overshoot, so this could partially be the cause of the minimal overshoot and no oscillations.
Thanks ... I still need to catch up a bit with more testing with my setup. Ive just finished comparing several settle time values here. It looks looks like you have the PID-AutoTune-Library controller mode set to PI, which uses different constants than PID mode. I think its using Ziegler Nichols rules for a relay step test ... I'll look more closely later and compare with the reaction curve method and constants that sTune uses. So far, sTune seems to be working great with others and on my setup with PWM control, but has some issue with the constants when using "bang-bang" output control for a relay. |
I think I've found the main issue with the tuning constants. Currently sTune is using tuning constants based on ZN closed-loop, but sTune is open loop. I've found some enlightening information here They have a downloadable PID Tuner that provides a wealth of information and capabilities. This will require a new sTune 1.2.0 release and will take some time (a few days to a week) to have it ready and tested. |
sTune 2.0.0 is published ... new open loop tuning methods and updated examples. |
Just released a new version that should work with relay, MOSFET or transistor PID control. |
I just tried the latest release (using the latest sTune 2.1.1 and QuickPID 3.1.0) My setup uses a MAX6675 k type thermocouple to control a 300W heating plate (https://www.amazon.com/gp/product/B07W1ZZH8T/) using a SSR. I had to slightly modify your example code because you cannot read the MAX6675 more than every 500msec or so (I read it every second). And the SSR uses, like in your example, a 200msec window Depending on the plate current temperature, the PWM output to keep the temperature stable changes. For example, at just above room temperature, a value of 200 (i.e. 10%) is enough. At 100C, it might need 700 (35%). Brett's autotune is working based on temperature intervals: set pwm output to step value, once it reaches the upper temperature band (usually a degree or two over the thermocouple noise), turn off the PWM, and see how much it overshoots. That works well with slow systems like mine with a lot of thermal inertia In your case, you set a PWM value and wait until an inflection is noticed. The problem is that the PWM value cannot be too high, or the system will go into thermal runout. For example, in my case, values above 700 (35%) will result in an ever increasing temperature with no limit (incidentally, might be worth having an "emergency shutoff" in your code, when the instant temperature is much above a limit, to turn off the PWM and abort) If I try your code with a 700 step, it completes the run (see below), then goes into normal PID mode. With the values it calculated, it started from a 45C temperature at the end of the auto tuning steps, trying to keep a 100C temperature. As you can see, it overshoot by more than 100C (I pulled the plug above 200C instant temperature, the averaged reading only show 195C). While at 190C, the PWM was still well above 800 (40%), i.e. still increasing the temperature. The auto-tuned parameters clearly were very wrong (I tried a few other runs before, with a range of parameters very close and also wrong, added at the end) If you have a clothes iron and get a MAX6675 module plus a k type thermocouple, you could simulate a system like mine. Set the iron thermostat to Linen (usually above 180C) and try to maintain a 100C temperature using a thermocouple connected to the bottom plate. Most PIDs used for temperature control operate with parameters similar to mine: a very powerful heater heating water (e.g. sous vide) or a system with lot of thermal inertia (like a reflow oven for PCBs). So I think that having your code work in this type of system would benefit many people Here's my code (temperatures in C)
And here's the run
Here are all the other values from previous runs
|
Thank you for your suggestions and detailed test data! I should easily be able to closely replicate your setup after getting a few missing parts. I have a MAX31856 thermocouple amplifier board and some type-K thermocouples on hand that I haven't used yet, so all I'll need to get is a zero-crossing type SSR and the wife's iron. I like (and could use) the 300W heating plate you've referenced, so I'll get one of those (or similar) on order.
Yes, I agree ... I'll report back after testing a similar setup. |
Cool! The MAX31856 is even better than the MAX6675. Cheap thermocouples are not always calibrated and are noisy in the range I use them. A K type thermocouple has a -200 to 1260C range, so even a low 0.5% noise can easily be more than 1C, even if used with a better amplifier than the MAX6675 If you get one of those heating plates, please keep in mind that above ~200C they become non linear. The heating element is a PTC, and the resistance increases with the temperature. So they never get above 240-250C even with a 100% PWM signal. I kinda like this feature for my project, because no matter what, I won't set anything on fire :). As such it's basically impossible to have a PID controller that can use the same parameters below 200C and above 200C. Most of my use is below 165C (for low temp solder rework), so those plates are still mostly linear in the range of my interest Those plates are very cheap from Aliexpress and with a lot of sizes and wattages. And can get hot FAST. I chose that specific one because it had silicone feet, which protect the surface. The metal feet get hot, so if you get one without the silicone, make sure to have something to protect the surface they are on |
I'm sorry that wedging to your conversation. I'm trying to use sTune for solving similar to robcazzaro problem. (even with tottaly same hardware: MAX6675, K-Thermocouples and SSR. Heaters slightly slower but with wider temp range (Flat Ceramic heaters) Right now we are trying to implement autotune function in software, but faicing with difficulties - due to noizy thermocouples, 10 to 30 seconds dead time(caused by thermocouple location), and inertial heaters. So there are at least 20 more persons who interested in your project =) BTW, during manual tuning I'ts pretty easy to determine wich part of PID cause overshoot: just print calculated pTerm, iTerm and dTerm separetely in addition to total Output. |
Hello and thank you for your interesting post. I assume the flat ceramic heaters are PTC type, which at this point, I've had no experience working with. However, now I've just received one of these PTC heating plates (140W/110V) and a suitable SSR as I was unwilling to wait until March for delivery of the larger PTC heater. So now I have the necessary components ... just need to find some time to get some testing done. |
@Dlloydev, PTC heaters behave pretty much like any other heater until almost at the cutoff temperature, at which point the internal resistance increases exponentially (literally). Actually a PTC resistance gets slightly lower with the increase of the temperature, only to increase dramatically close to the self-stable point (see attached curve, for a unit similar to yours). For all practical purposes, a PTC heater behaves like a normal heater until T-10C or so The critical points of our type of devices are the high cooling inertia compared to the heating one, and the SSR control, slower in general then a pwm control (1-2 seconds cycle). A heating plate or a PCB oven can increase temperature by tens of degrees in a few seconds when at max power, but only cool a fraction of a degree in the same time. That makes the PID control more challenging and requires aggressively slowing (and less so increasing) the output the closer one gets to the target set point @geleos27 thanks for the tip to print the calculated pTerms during the loop. I did not think about it before, and I'm loudly slapping my head now :) |
@robcazzaro, yes, I can see how the critical points you've mentioned make PID control more challenging - thanks for explaining them in detail as this is very helpful. I have some ideas I'd like to try to help equalize the temperature rise and fall times. Offhand, regarding the 1-2 sec PID cycle, I don't see why the PID windowSize couldn't be shortened to say 500ms for a 2Hz sample rate. I have a zero cross type SSR, so with a 60Hz system, the output control would be in steps of 8.33ms, giving 60 steps of power control within the 500ms window or maybe there would be increased benefit by using a 4Hz sample rate with only 30 steps of power control. I hope to get some testing done in the next few days. |
I've just tested my 140W PTC / SSR setup to see what output value is needed to bring the temp up to max. Also, I've found another link to the 300W PTC heater where they specify the constant power used to reach the max temp. Here's a comparison of the TCLab (which works with sTune and PID) vs PTC Heaters:
The output control range and useable steps for PTC heaters is significantly limited. I think this is the main reason for tuning and PID control difficulties. I haven't tried tuning or PID yet (just timed digitalWrite on/off). Since the control resolution is so poor, I'll wait for a heatsink I've ordered to arrive and see what improvements that will make. May also need to go with a larger windowSize rather than smaller. I've tested with nothing added to the aluminum shell, but if it were heating something up, I can see how this should only help to boost the useable control range. If the cost of a suitable DC power supply isn't an issue I'm thinking that something like this might work well (no heatsink required) and perhaps operate it with a MOSFET switching only 12VDC. Interesting that the negative comment offers a clue as to the natural heat dissipation (about 150W), so this should be easy to control at much higher speeds and resolution. |
Thank you for engagement. Actually there are lot's of projects and chineese BGA stations use heaters this of this type: They much slower and more inertial than PTC's |
Good info @Dlloydev I'm not entirely sure why you say that you can only use 10% of the power for control and that control resolution is poor. Control resolution is still 0-100%, and with a 2000 msec window you have 2000 possible values to play with. In my case (where the PTC cartridge is connected to an aluminum plate), if I connect the plate to 110V with no PID, it takes a good 40-50 seconds to reach 240C, so there's plenty of time to start from 100% and dial down to whatever is required for a stable temperature I used one of those REX C100 PIDs to control my PTC heater to 100C, and with the default settings it started from 100% on, after 60-70C started dialing down, overshot by 10C or so, then stabilized very quickly at 100C. It's just a matter of finding the right parameters. Currently your sTune is not capable of handling this, while the older Brett's Autotune does a much better job (no disrespect meant, just trying to provide data). With Brett's Autotune, I can get much closer to optimize the system I also gave you before the example of my espresso machine, with a 1500W heater in a small boiler, working the same way: 100% output when cold, progressively lower the closer it gets to the target. I estimate that when my espresso machine is at a stable temperature, the heater is on around 5% of the cycle. All the systems based on a heater have a heater that is an order of magnitude more powerful than what's needed to simply maintain temperature. The PID controls on/off so that it gets stable. It works very differently than a PID for a motor or other similar processes As such, the PID must use 100% of the potential control range, simply increasing or decreasing as needed |
Thanks @robcazzaro, that clarifies things really well - that auto-tuning and PID control with the PTC heaters (unloaded?) can work OK. Poor choice of words "useable" for control range ... just meant to compare the % output required for max temp. The 10% of the power for control for the 140W PTC was just from my tests with timed output (on 200ms, off 1800ms) where this would bring the temperature up to about 209 deg C. If I were to rely only on natural heat dissipation, then there's only 10% range of control to work with, because in this case 11-100% output has no effect on temperature. I think another benefit to adding heat-sink might be helping to balance (equalize) the temp rise and fall response. With heat-sink, the PID would have to work harder (increased output value) to get to the same setpoint. Then when it needs to lower the temp, heat would dissipate much quicker and the fall time reduced and more responsive. Thinking ahead, after getting the PTCs to work with tuning and PID control, I'd like to try some sort of dual tuning setup, similar to the PID_AdaptiveTunings example, except that it will use separate "heating" and "cooling" tuning parameters depending on if the input is below or above setpoint. |
Yes, in pretty much all this category of systems, 10% power output is more than enough to reach max temperature (and beyond for systems based on a traditional heater instead of a PTC). Think about a clothes iron: if you listen to the mechanical thermostat, you will notice that after reaching stable temperature, it clicks on and off very rapidly, spending most of the time in the off state. Even in my system, around 10% power is more than enough to keep increasing temperature until the PTC cutoff. No matter how big the heat sink, these systems will always have an order of magnitude more power than needed. It's the best way to quickly reach the target temperature and to be able to handle environmental changes Pretty much every sous vide in use and every PCB oven in the world (plus a huge percent of good espresso machines) operate this way. And I'm sure pretty much every industrial oven or heated tank as well |
I ran the With the setpoint temperature set to 150°C and the input still reading more than 150°C due to a previous test, this is the plot: You can see that the PID brings the temperature into regulation, but it wanders for a period of time, usually opposite to the direction of the output. I've marked a few points on the plot to illustrate: ① This is when the input temperature crosses the setpoint (the error goes from positive to negative). The PID takes corrective action by increasing the output. ① to ② The temperature keeps drifting lower while the output keeps increasing. This continues significantly beyond the deadtime of the process, but it shouldn't. The deadtime is about 20 seconds and ① to ② represents about 40 samples at 5 sec/sample, so about 200 seconds. ② This is where the input stops decreasing, so it finally begins responding to the increasing output. ⬍ This shows the difference in output required to get a response on the input. It appears to be around 8-10 ms. Note that the output value is in milliseconds and the windowSize (samplePeriod) is 5000ms. It will require a change in output of 8.33 ms (any direction) before the SSR will be able to add or subtract ½ AC cycle of output power. This is the plot with setpoint at 80°C and the same tuning constants: Same effect where the input drifts in opposite direction to the direction of output control. If it's due to the ½ AC cycle of power resolution, then I don't think its possible to tune this variation away, although using a smaller window size might help. In the next plot, I tried oscillating an offset to the output in order to coerce a response from the input. I used a new variable (outputCorr) for the softPWM output control function, so as not to interfere with the Output variable seen by the PID. It works using logic by offsetting outputCorr by -9 while the error is positive and +9 while the error is negative and using offset of 0 when there is no error. Using the same tuning constants, here's the results: Now there's just a small amount of ripple left in the input temperature due to the slow PID rate, but this should improve by reducing the window size. |
"It works using logic by offsetting outputCorr by -9 while the error is positive and +9 while the error is negative and using offset of 0 when there is no error." - really interesting approach. How did you found offset value "9"? Aproximate output difference between points 1 and 2? |
Yes, but not measured, just the next integer higher than ±8.33ms which is the timing for ½ cycle @ 60Hz AC. For 50Hz AC, ½ cycle is 10ms, so I'll try a value of 12 which should work with SSRs connected to 50Hz or 60Hz AC plus an extra ±1ms for software timing variances. The idea is to force ±½ cycle power control while operating within the dead-band area between each AC ½ cycle. Basically it's to enhance power control resolution. For the next test, I'll post a plot with setpoint at only 50°C using a 10x shorter window size (500ms) and using ±12 offset. Edit: The best improvement was to use an offset just lower than the ½ cycle timing so here's the plot using ZN_PID, setpoint = 50°C, window size 500ms and ±8 offset: The software PWM function for SSR relay control: void softPwm() {
if (Input > Setpoint) outputCorr = Output - 8;
else if (Input < Setpoint) outputCorr = Output + 8;
else outputCorr = Output;
if (outputCorr < 0) outputCorr = 0;
if (!relayStatus && outputCorr > (msNow - windowStartTime)) {
if (msNow > nextSwitchTime) {
nextSwitchTime = msNow + debounce;
relayStatus = true;
digitalWrite(relayPin, HIGH);
}
} else if (relayStatus && outputCorr < (msNow - windowStartTime)) {
if (msNow > nextSwitchTime) {
nextSwitchTime = msNow + debounce;
relayStatus = false;
digitalWrite(relayPin, LOW);
}
}
} |
Ive updated sTune and examples. The Just today I've received a MAX6675 module, so I'll be able to eventually create examples and wiki page for this (time permitting). |
sTune 2.3.1New examples and reference is ready for the MAX6675 hardware setup. |
I've updated your previous example in case you're interested in giving it a run with the latest revision of sTune (2.3.2). It would be interesting to see the plot results. /********************************************************************************
sTune QuickPID Adaptive Control Example (robcazzaro)
This sketch does on-the-fly tunning and PID SSR control of a PTC heater
using the ZN_PID tuning method. When sTune completes, we need the input
to approach setpoint for the first time. As the input gets closer, the gains
(tunings) are gradually applied. This helps keep the integral term to a
minimum to help reduce initial overshoot. Final overshoot correction is made
at the first crossover from setpoint. Here, a full AC cyle is dropped from
the output and 100% PID control resumes with ½ AC cycle optimized SSR control.
Open the serial plotter to view the graphical results.
Reference: https://github.com/Dlloydev/sTune/wiki/Examples_MAX6675_PTC_SSR
*******************************************************************************/
#include <Arduino.h>
#include <sTune.h>
#include <QuickPID.h>
#include "Wire.h"
#include <LiquidCrystal.h>
#include <max6675.h>
// ***** PIN ASSIGNMENT *****
int ssrPin = 11;
int thermocoupleSOPin = A3;
int thermocoupleCSPin = A4;
int thermocoupleCLKPin = A5;
int lcdRsPin = 8;
int lcdEPin = 9;
int lcdD4Pin = 4;
int lcdD5Pin = 5;
int lcdD6Pin = 6;
int lcdD7Pin = 7;
// pins
const uint8_t inputPin = 0;
const uint8_t outputPin = ssrPin;
const uint8_t ledPin = LED_BUILTIN;
// user settings
uint32_t settleTimeSec = 15;
uint32_t testTimeSec = 1000;
const uint16_t samples = 500;
const float inputSpan = 240;
const float outputSpan = 2000;
float outputStart = 0;
float outputStep = 400;
float tempLimit = 150;
uint8_t debounce = 1;
uint8_t startup = 0;
// variables
float Input, Output, Setpoint = 100, Kp, Ki, Kd;
sTune tuner = sTune(&Input, &Output, tuner.ZN_PID, tuner.direct5T, tuner.printALL);
QuickPID myPID(&Input, &Output, &Setpoint);
LiquidCrystal lcd(lcdRsPin, lcdEPin, lcdD4Pin, lcdD5Pin, lcdD6Pin, lcdD7Pin);
MAX6675 thermocouple(thermocoupleCLKPin, thermocoupleCSPin, thermocoupleSOPin);
void setup()
{
pinMode(outputPin, OUTPUT);
pinMode(ledPin, OUTPUT);
digitalWrite(outputPin, LOW);
Serial.begin(57600);
tuner.Configure(inputSpan, outputSpan, outputStart, outputStep, testTimeSec, settleTimeSec, samples);
tuner.SetEmergencyStop(tempLimit);
// Start-up splash
lcd.begin(16, 2);
lcd.clear();
}
void loop()
{
float optimumOutput = tuner.softPwm(outputPin, Input, Output, Setpoint, outputSpan, debounce);
switch (tuner.Run())
{
case tuner.sample: // active once per sample during test
Input = thermocouple.readCelsius();
lcd.setCursor(0, 1);
lcd.print(Input);
lcd.print("C pwm=");
lcd.print((int)Output);
lcd.print(" ");
//Serial.println(Input);
tuner.plotter(Input, Output * 0.5, Setpoint, 1, 3);
break;
case tuner.tunings: // active just once when sTune is done
tuner.GetAutoTunings(&Kp, &Ki, &Kd); // sketch variables updated by sTune
myPID.SetOutputLimits(0, outputSpan * 0.5);
myPID.SetSampleTimeUs(outputSpan * 1000 * 0.2);
debounce = 0; // switch to SSR optimum cycle mode
myPID.SetMode(myPID.Control::automatic); // the PID is turned on
myPID.SetProportionalMode(myPID.pMode::pOnMeas);
myPID.SetAntiWindupMode(myPID.iAwMode::iAwClamp);
myPID.SetTunings(Kp, 0, 0); // update PID with the new Kp (P-controller)
break;
case tuner.runPid: // active once per sample after case "tunings"
Input = thermocouple.readCelsius();
lcd.setCursor(0, 1);
lcd.print(Input);
lcd.print("C pwm=");
lcd.print((int)Output);
lcd.print(" ");
//Serial.println(Input);
if (Input > Setpoint && startup == 6) {
Output -= 9; // drop half AC cycle
myPID.SetMode(myPID.Control::manual); // toggle PID control mode
myPID.SetMode(myPID.Control::automatic); // now PID uses the new output value
startup++;
} else if (Input > Setpoint - 3 && startup == 5) {
myPID.SetTunings(Kp, Ki, Kd); // 100% of gains
startup++;
} else if (Input > Setpoint - 6 && startup == 4) {
myPID.SetTunings(Kp, Ki * 0.8, Kd * 0.8); // 80% of gains
startup++;
} else if (Input > Setpoint - 9 && startup == 3) {
myPID.SetTunings(Kp, Ki * 0.6, Kd * 0.6); // 60% of gains
startup++;
} else if (Input > Setpoint - 12 && startup == 2) {
myPID.SetTunings(Kp, Ki * 0.4, Kd * 0.4); // 40% of gains
startup++;
} else if (Input > Setpoint - 15 && startup == 1) {
myPID.SetTunings(Kp, Ki * 0.2, Kd * 0.2); // 20% of gains
startup++;
} else if (Input > Setpoint - 18 && startup == 0) {
myPID.SetTunings(Kp, Ki * 0.1, Kd * 0.1); // 10% of gains
startup++;
}
myPID.Compute();
tuner.plotter(Input, optimumOutput * 0.5, Setpoint, 1, 3);
break;
}
} |
I am trying to use sTune in my setup which is a Keurig coffee heating tank
with original thermistor and an SSR. The tank heats up very quickly (< 15
seconds to 100*º*C) but is very slow to cool. With an experimental setpoint
at 70 or 80 I get big overshoot with any setting (well, any I come up with)
and very long times to settle. I am even trying bringing it close to
temperature via other methods then invoking your code. I will gladly keep
playing and testing and get you any data that can be helpful to you (to
help others and me!), but can you guess at some good starting parameters
and settings for me to try?
Thanks for any suggestions.
-Craig
…On Sat, Feb 5, 2022 at 1:02 AM Dlloydev ***@***.***> wrote:
@robcazzaro <https://github.com/robcazzaro>
I've updated your previous example in case you're interested in giving it
a run with the latest revision of sTune (2.3.2). It would be interesting to
see the plot results.
/********************************************************************************
sTune QuickPID Adaptive Control Example (robcazzaro)
This sketch does on-the-fly tunning and PID SSR control of a PTC heater
using the ZN_PID tuning method. When sTune completes, we need the input
to approach setpoint for the first time. As the input gets closer, the gains
(tunings) are gradually applied. This helps keep the integral term to a
minimum to help reduce initial overshoot. Final overshoot correction is made
at the first crossover from setpoint. Here, a full AC cyle is dropped from
the output and 100% PID control resumes with ½ AC cycle optimized SSR control.
Open the serial plotter to view the graphical results.
Reference: https://github.com/Dlloydev/sTune/wiki/Examples_MAX6675_PTC_SSR
*******************************************************************************/
#include <Arduino.h>
#include <sTune.h>
#include <QuickPID.h>
#include "Wire.h"
#include <LiquidCrystal.h>
#include <max6675.h>
// ***** PIN ASSIGNMENT *****
int ssrPin = 11;
int thermocoupleSOPin = A3;
int thermocoupleCSPin = A4;
int thermocoupleCLKPin = A5;
int lcdRsPin = 8;
int lcdEPin = 9;
int lcdD4Pin = 4;
int lcdD5Pin = 5;
int lcdD6Pin = 6;
int lcdD7Pin = 7;
// pins
const uint8_t inputPin = 0;
const uint8_t outputPin = ssrPin;
const uint8_t ledPin = LED_BUILTIN;
// user settings
uint32_t settleTimeSec = 15;
uint32_t testTimeSec = 1000;
const uint16_t samples = 500;
const float inputSpan = 240;
const float outputSpan = 2000;
float outputStart = 0;
float outputStep = 400;
float tempLimit = 150;
uint8_t debounce = 1;
uint8_t startup = 0;
// variables
float Input, Output, Setpoint = 100, Kp, Ki, Kd;
sTune tuner = sTune(&Input, &Output, tuner.ZN_PID, tuner.directIP, tuner.printALL);
QuickPID myPID(&Input, &Output, &Setpoint);
LiquidCrystal lcd(lcdRsPin, lcdEPin, lcdD4Pin, lcdD5Pin, lcdD6Pin, lcdD7Pin);
MAX6675 thermocouple(thermocoupleCLKPin, thermocoupleCSPin, thermocoupleSOPin);
void setup()
{
pinMode(outputPin, OUTPUT);
pinMode(ledPin, OUTPUT);
digitalWrite(outputPin, LOW);
Serial.begin(57600);
tuner.Configure(inputSpan, outputSpan, outputStart, outputStep, testTimeSec, settleTimeSec, samples);
tuner.SetEmergencyStop(tempLimit);
// Start-up splash
lcd.begin(16, 2);
lcd.clear();
}
void loop()
{
float optimumOutput = tuner.softPwm(outputPin, Input, Output, Setpoint, outputSpan, debounce);
switch (tuner.Run())
{
case tuner.sample: // active once per sample during test
Input = thermocouple.readCelsius();
lcd.setCursor(0, 1);
lcd.print(Input);
lcd.print("C pwm=");
lcd.print((int)Output);
lcd.print(" ");
//Serial.println(Input);
tuner.plotter(Input, Output * 0.5, Setpoint, 1, 3);
break;
case tuner.tunings: // active just once when sTune is done
tuner.GetAutoTunings(&Kp, &Ki, &Kd); // sketch variables updated by sTune
myPID.SetOutputLimits(0, outputSpan * 0.5);
myPID.SetSampleTimeUs(outputSpan * 1000 * 0.2);
debounce = 0; // switch to SSR optimum cycle mode
myPID.SetMode(myPID.Control::automatic); // the PID is turned on
myPID.SetProportionalMode(myPID.pMode::pOnMeas);
myPID.SetAntiWindupMode(myPID.iAwMode::iAwClamp);
myPID.SetTunings(Kp, 0, 0); // update PID with the new Kp (P-controller)
break;
case tuner.runPid: // active once per sample after case "tunings"
Input = thermocouple.readCelsius();
lcd.setCursor(0, 1);
lcd.print(Input);
lcd.print("C pwm=");
lcd.print((int)Output);
lcd.print(" ");
//Serial.println(Input);
if (Input > Setpoint && startup == 6) {
Output -= 9; // drop half AC cycle
myPID.SetMode(myPID.Control::manual); // toggle PID control mode
myPID.SetMode(myPID.Control::automatic); // now PID uses the new output value
startup++;
} else if (Input > Setpoint - 3 && startup == 5) {
myPID.SetTunings(Kp, Ki, Kd); // 100% of gains
startup++;
} else if (Input > Setpoint - 6 && startup == 4) {
myPID.SetTunings(Kp, Ki * 0.8, Kd * 0.8); // 80% of gains
startup++;
} else if (Input > Setpoint - 9 && startup == 3) {
myPID.SetTunings(Kp, Ki * 0.6, Kd * 0.6); // 60% of gains
startup++;
} else if (Input > Setpoint - 12 && startup == 2) {
myPID.SetTunings(Kp, Ki * 0.4, Kd * 0.4); // 40% of gains
startup++;
} else if (Input > Setpoint - 15 && startup == 1) {
myPID.SetTunings(Kp, Ki * 0.2, Kd * 0.2); // 20% of gains
startup++;
} else if (Input > Setpoint - 18 && startup == 0) {
myPID.SetTunings(Kp, Ki * 0.1, Kd * 0.1); // 10% of gains
startup++;
}
myPID.Compute();
tuner.plotter(Input, optimumOutput * 0.5, Setpoint, 1, 3);
break;
}
}
—
Reply to this email directly, view it on GitHub
<#3 (comment)>, or
unsubscribe
<https://github.com/notifications/unsubscribe-auth/AHKHBZDSPG3RZGFVT575GF3UZS4O5ANCNFSM5LNU3TWA>
.
Triage notifications on the go with GitHub Mobile for iOS
<https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675>
or Android
<https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub>.
You are receiving this because you are subscribed to this thread.Message
ID: ***@***.***>
--
Craig Haller, Special Education Advocate
Haller Advocacy
617.435.1759
Fax: 617.738.8197
SPEDadvocate.com <http://spedadvocate.com/>
|
@craig02445 |
Apologies for the delay in replying, @Dlloydev , but I was working on another project and didn't have access to the hardware. I cut&pasted your modified code and run it as is. The results are really puzzling, not just for how slow the auto tuning was (35 minutes!), but also when switching to normal mode, the output was never above 200 (out of 2000), so the temperature stays around 80C, never reaching the 100C set temperature. I'm including the whole log as a txt file (given how long it is) The calculated parameters (Kp: 6.75, Ki: 0.08, Kd: 0.32) are roughly one order of magnitude too small. It looks as if there's a missing 0 somewhere in the example parameters (it's very slow to increase temperature, and still overshoots to above 130C) |
Thanks, The modified code did a direct5T test, so this actually completes in 3-4 time constants, so that's 3-4 x 9.4 minutes on your system. I had the test run with a lower step value so that it would do a complete tuning test below setpoint, then gradually apply the new PID gains, but yes, the gains did come out too small. I've still got some work to do with the software and lot's of testing. I've reformatted your latest test results and pasted / saved it here in the online PID Tuner which is a great tool. Here, you can view the plot, view or change / re-scale its identified gains and more. It identified the time constant tau to be 848.6 sec (14 min). Using the "scale gains" slider, the overshoot would disappear with Kp = 25, Ki = 0.03, Kd = 0. If I manually entered the gains from sTune, the plot would reveal the oscillations. Click the "back" button 3 times to see the data table and the plot in more detail. |
Thanks for the additional info. I tried using Kp=25, Ki=0.03 and Kd=0, but the system is super slow to reach the 100C set temperature. It reaches 90C after roughly 180 seconds, but reaches 100C only after 1300 seconds (more than 20 minutes), which is way too long. No overshot, clearly :) I got these values from running Brett's autotune, Kp=28, Ki=0.17 and Kd=15, and with those I get a 10C overshot after roughly 120 seconds, then it stabilizes at 100C after 500 seconds. My ideal tuning would be for a system that reaches 100C in under 240 seconds, with no more than 5C overshot. |
Oh, that was just playing with the settings to dial out the overshoot.
This specification helps ... here's some gains I've tried with each example getting more aggressive. Note the plot's time scale is zoomed in to max to reveal the response in just the first 342 seconds. Kp = 50, Ki = 0.0625, Kd = 0 Kp = 100, Ki = 0.125, Kd = 0 Kp = 150, Ki = 0.1875, Kd = 0
I've made some fixes to sTune and will push an update after I get a chance to do more testing. My goal is to have sTune determine gains that are a closer match to the gains determined by the online PID Tuner. |
pidtuner really helpfull, but please pay attention to bottom graph also. Sometimes to achieve faster response pidtuner offers both positive and negative CV, but for heaters it's impossible =( and there is no way in pidtuner to consider any constrains (if you have any). Which may drammaticaly cause on results while using gains provided to pidtuner. If robcazzaro have transition from room ambient ~25C degree to 100C we will get 75 degree transition.
looks like with provided gains CV will be limited and results may be confusing. |
sTune 2.4.0 is published. Includes various fixes, updated and new examples and documentation. |
This issue was moved to a discussion.
You can continue the conversation there. Go to discussion →
Not sure if this is a real issue, but I thought that in any case you would be interested in more data.
I'm building a hot plate for SMD soldering with a k-type thermocouple connected to a MAX6675 and a plate heater controlled by a SSR. I use a relay-like control for the SSR, with 2 sec window and the SSR on for a fraction of that time. I built an Arduino program to auto tune my physical setup.
Unfortunately the MAX6675 output is very noisy, due to the thermocouple limitations. The output can easily change by a degree centigrade just due to noise. When looking at Brett's old autoune library, there was a specific note about noise http://brettbeauregard.com/blog/2012/01/arduino-pid-autotune-library/. The library had a setting for dealing with noise SetNoiseBand()
You are clearly using a different algorithm, and I'm not sure how impacted it is by noise. I also mentioned in another issue that it's very hard for a setup like mine to reach steady state. So even if I'm trying to use always the same conditions (5% output stepping up to 20%, but since I use a 2000 msec PWM window, the values you will see are 100 and 400), the actual values change quite a lot.
I modified your library to also print the thermocouple input value, not just the average, so you can see how noisy the system is. I'm attaching 4 different runs with identical settings but different starting conditions. The suggested K values are actually very consistent, considering the noise and starting conditions
//user settings
const uint32_t testTimeSec = 600;
float outputStart = 100; // 5% start
float outputStep = 400; // 20% step
const uint16_t samples = 600;
const uint32_t settleTimeSec = 300; //5 minutes to settle
pid2.log
pid3.log
pid4.log
pid5.log
The text was updated successfully, but these errors were encountered: