Experimental PID improvement changes

That run was actually done with TOLERANCE=0.02

I fully agree, I don’t think +/- 1 °C would be of significance with a good quality and well suspended bed.

In the case of my CR-10S Pro the effect was compounded by the thin and crappy rolled aluminum sheet bed with integrated PCB heater (i.e. residual stresses that change the bed shape when heated combined with rapid heat transfer into one side of the said crappy bed) and the questionable bed suspension system (over-constraining the XY expansion & creating XY forces in the flimsy bed).

The effect was very real and I was getting Z artifacts in the printed layers, dependent on the object location on the bed, object size, etc.

It will be interesting to see how my Voron 2.4 behaves once I have it operational since the bed is on a fully kinematic (Mandala Rose) suspension.

I’m an idiot!

When i updated the calibration class yesterday, i used the constant instead of the passed in value. It’s fixed now.

https://github.com/dans98/klipper/commit/1054d5a7bf5a28f4230de6041364e77797c2c16b

LOL! In the meantime I was wondering why the code was not bailing out immediately on reaching the 0.02 tolerance but with family here had no time to look at anything serious…

actually i’d ask that you hold off till i get a little more time to review the code. I’m dealing with a fussy toddler today.

I want to run a test or two, but i need to get someone down for a nap!

2 Likes

Sometimes I wish back this time. Just had a discussion with my 14 year old that we wants to take a 12 hour train trip with a friend hang out in a foreign city, find a youth hostel and return the night after. Anyway good luck

2 Likes

@ReXT3D good to go now!

I superficially tested the hot end PID control with mainline and with your changes, both using my previously derived PID constants. I have not had the time to inspect the temperature and PWM logs beyond the Fluidd display. It looks like your code has slightly less “noisy” PWM output while maintaining roughly the same level of temperature control. Subjectively (based on my visual observations) the temperature control seems to be perhaps a little bit worse, but this observation may be totally wrong.

Klipper mainline PID control:

DanS PID control:

Note in both of the above cases I turned on the parts fan at approximately 3 minutes after reaching temp target and then turned the fan off about two minutes later. You can see the approximate times by looking at the PWM power output.

It is worth noting that I am using a PT1000 RTD with Duet3D Mini 5+ (no daughter card). This results in more “noisy” PWM output than the 3950 NTC thermistor that I used before. I suspect that this is largely due to the 0.5-0.6 deg.C ADC resolution (and noise of course) at around 200-220C, vs. the NTC thermistor resolution of approximately 0.1 deg.C in the same temperature range.

Both plots used the default value of smooth_time.

Installed a new 70W heating cartridge.

0. Old 50W cartridge with new logging:

  • As tests above
  • Heat loss too big for proper tuning
  • No further logs in attached zip
08:14:48  $ PID_CALIBRATE HEATER=extruder TARGET=260 WRITE_FILE=1 TOLERANCE=0.02
08:18:00  // sample:1 pwm:1.0000 asymmetry:-1.0882 tolerance:n/a
08:18:28  // sample:2 pwm:1.0000 asymmetry:-1.1609 tolerance:n/a
08:18:56  // sample:3 pwm:1.0000 asymmetry:-1.1247 tolerance:n/a
08:19:24  // sample:4 pwm:1.0000 asymmetry:-0.9974 tolerance:0.0000
08:19:24  // PID parameters: pid_Kp=34.568 pid_Ki=2.487 pid_Kd=120.123

1. New cartridge:

  • No fan
  • No silicone sock
  • _1 files in zip
15:01:59  $ PID_CALIBRATE HEATER=extruder TARGET=260 WRITE_FILE=1 TOLERANCE=0.02
15:03:51  // sample:1 pwm:1.0000 asymmetry:1.4065 tolerance:n/a
15:04:15  // sample:2 pwm:0.8048 asymmetry:0.1490 tolerance:n/a
15:04:39  // sample:3 pwm:0.7851 asymmetry:-0.0149 tolerance:n/a
15:05:02  // sample:4 pwm:0.7871 asymmetry:-0.0514 tolerance:0.2149
15:05:26  // sample:5 pwm:0.7939 asymmetry:-0.1239 tolerance:0.0197
15:05:27  // PID parameters: pid_Kp=25.811 pid_Ki=2.164 pid_Kd=76.949

2. New cartridge:

  • 100% part fan
  • No silicone sock
  • Heat loss still too big for proper tuning
  • _2 files in zip
15:10:02  $ PID_CALIBRATE HEATER=extruder TARGET=260 WRITE_FILE=1 TOLERANCE=0.02
15:12:01  // sample:1 pwm:1.0000 asymmetry:-1.6130 tolerance:n/a
15:12:25  // sample:2 pwm:1.0000 asymmetry:-1.6129 tolerance:n/a
15:12:48  // sample:3 pwm:1.0000 asymmetry:-1.5220 tolerance:n/a
15:13:11  // sample:4 pwm:1.0000 asymmetry:-1.4130 tolerance:0.0000
15:13:11  // PID parameters: pid_Kp=28.353 pid_Ki=2.460 pid_Kd=81.692

3. New cartridge:

  • 100% part fan
  • Silicone sock installed
  • _3 files in zip
 15:23:13  $ PID_CALIBRATE HEATER=extruder TARGET=260 WRITE_FILE=1 TOLERANCE=0.02
15:25:15  // sample:1 pwm:1.0000 asymmetry:0.3326 tolerance:n/a
15:25:37  // sample:2 pwm:0.9520 asymmetry:0.0412 tolerance:n/a
15:26:00  // sample:3 pwm:0.9462 asymmetry:0.0046 tolerance:n/a
15:26:22  // sample:4 pwm:0.9455 asymmetry:0.0951 tolerance:0.0545
15:26:45  // sample:5 pwm:0.9315 asymmetry:0.1501 tolerance:0.0205
15:27:07  // sample:6 pwm:0.9104 asymmetry:-0.0325 tolerance:0.0358
15:27:30  // sample:7 pwm:0.9151 asymmetry:-0.0869 tolerance:0.0351
15:27:52  // sample:8 pwm:0.9276 asymmetry:-0.0325 tolerance:0.0211
15:28:15  // sample:9 pwm:0.9324 asymmetry:-0.0316 tolerance:0.0220
15:28:38  // sample:10 pwm:0.9368 asymmetry:0.0407 tolerance:0.0217
15:29:00  // sample:11 pwm:0.9309 asymmetry:-0.1050 tolerance:0.0092
15:29:00  // PID parameters: pid_Kp=27.851 pid_Ki=2.476 pid_Kd=78.33

Raw Data: heater_new.zip (133.6 KB)

those all look as expected to me.

Imo, thermal losses is something a lot of the hot end designers don’t pay much attention to. it baffles me, as it’s as important to print quality as anything else is.

I printed s small item at 250°C with the new cartridge and the PID values from run #3 (with silicone sock). I have the feeling that the temperature control is less stable than what I’m used to.
To be fair, I never scrutinized this so in depth, before you brought the attention to it, so I would have to compare to pristine Klipper.

Following graph is the hot end temp from the print:

Edit:
From the “stable” print phase the basic stats are:

  • Min: 249,3
  • Max 250,6
  • Average: 249,9918122

Have to revert the feeling. Printed the same part again with Klipper main line and also main line tuning:

  • Main line PID: pid_Kp=28.283 pid_Ki=2.548 pid_Kd=78.487
  • Same setting as #3 above. Values are quite close to each other
  • Min: 249,3
  • Max: 250,6
  • Average: 249,9966891

My summary so far:

  • Your tuning is on my system neither better nor worse compared to main line
  • I like the added information about the symmetry as I think it is a valuable information to judge the performance

That is pretty much what I expect, my updates to the standard positional controller only show up in edgecase conditions, and even then they are very nuanced.

I now have a working version of the velocity form, and i’m testing it on my machine. I will then ask a few people I know to test it out, and assuming that goes well i will post it here for the general community to test with.

1 Like

While I wait on some supplies to arrive, i thought i’d share what I’ve been working on, and the issues I’m facing.

I’ve mentioned the velocity form several times, but in case people don’t know what that means i’m going to post the derivation of it.

The basics/definitions.
001
002
003
004

The standard PID definition
005

The positional form currently used by Klipper that negates derivative kick.
006

The derivative form of the above.
007

The derivative form with proportional kick negated, that I’m using.
008

The benefit of the positional form, is that it eliminates the potential of integral windup, because it doesn’t use an integral. additionally for each time step you calculate how much the control signal (PWM output) should be changed by.

The down side of the velocity form is that it is more susceptible to noise, because not only does it use a derivative, but a second derivative as well. while it’s the superior algorithm, it requires more advanced filtering/smoothing, and a a clean temperature signal. Thus it can’t be a drop in replacement for positional form pwm, and would need to be added to Klipper as an optional control algorithm.

Over the last several weeks I’ve tried numerous filtering/smoothing algorithms, and untimely I settled on using a Savitzky–Golay filter. An SG filter is basically a least squares polynomial fit, solved for the point in the middle of the window (more on the later).

A major benefit of using an SG filter, is because it’s based on a convolution its very computationally cheap to calculate, even when fitting an extremely large data set. Another benefit is that when calculated at the edge of the window, you generate no lag/delay no matter how many previous points you fit.

When originally conceived Savitzky & Golay only considerd the point in the middle of the window important, as they where interested in smoothing data after the fact. Over the years people have developed algorithms that allows you to calculate values for any point in the window. A particularity well written and easy to understand paper is the one written by Peter Gorry in 1990, and thus the one I incorporated into my code.

Here are some comparison plots of the current positional form controller, my updated positional controller and the velocity velocity form controller run on my hot end with the same pid parameters.



As you can see the pwm signals fluctuates a lot more even for a clean signal because of the use of a second derivative.

Here are the test runs overlaid.

Here is a close up showing the complete negation of integral windup.

The next plot shows that for a given set of pid parameters the velocity form is better at limiting temperature fluctuations, even though the pwm signal fluctuates more. This is mainly because the velocity form is a more traditional second order differential equation, that’s takes acceleration into account.

for reference a dead flat line would be perfection (no variance).

My Issues
As I’ve shown in previous posts my bed is incredibly noisy. This is because my bed uses a mains powered heater (60hz), and thus i have 60hz interference as the heater wires and sensor wires are bundled together. In my tests no matter how much filtering I’ve applied, the signal is just to noisy to use the derivative form. Thus, I’m going to attempt to minimize the noise by wrapping that sensor wires in copper sleeving and then grounding the sleeving.

Once I’ve got this done and tested, I’ll post my code for everyone to try.

A question I have for @koconnor or anyone else that has a more in-depth knowledge of the code base, is why is the pwm sampling frequency hard coded to 0.3 seconds / 3.33hz?

I know some oversampling happens at the adc level, and their are timing considerations.

However allowing users to increase the sampling rate, would give users who care, greater control over input noise, and how well controller maintains the target temperature.

1 Like

Do you see a repetitive pattern in the noise when the heater is on? (moiré pattern from the 60Hz and the sampling period)

You could try to set this in adc_temperature.py:

- SAMPLE_TIME = 0.001
+ SAMPLE_TIME = 1. / (60 * 8) # ~ 2.08ms

This should sample the waveform such the 60Hz cross-talk cancels out when the samples are averaged, independently of the phase difference. Although, I haven’t tried it, and I don’t know if the timer is precise enough for this to work.

edit: Sorry, I messed up the denominator, fixed it.

I did validate the noise that way, i used some external hardware to log a signal at a high sample rate, and then used fft analysis to see the problematic frequencies.

however here is a quick test with the pwm value was hardcoded.

run 1 is stock, run 2 is the modification you suggested above. Edit: I subtracted 1C in the plot from run 2 to separate the runs.

The low frequency beat looks attenuated, but I’m not sure if it’s originating from temperature changes or from the moiré effect of mains frequency.
Anyway, this probably doesn’t help much in your case, since I’m guessing the high frequency noise components are the ones causing the problems with the double derivative.

No particular reason, other than ~3 samples a second seems to work in general. It’s been 300ms since the first release of Klipper, and I likely chose it to roughly match what other firmware at the time did.

Cheers,
-Kevin

Just to be clear, the current code can not be set dramatically lower, as the current code schedules the pwm update after the next scheduled sensor reading. So, with 300ms updates, the host has to meet a 300ms round-trip-time from sensor reading to pwm update. That time can be reduced, but there is a limit to how small it can be before “timer too close” errors occur (at least in the current code).

-Kevin

I thought i would give an update.

The physical noise reduction did almost nothing, and i thought a chance of that would exist as 6" of the leads can’t be shielded as they are embedded into the bed.

However I am making a some progress.

When I discretized the velocity form I tried both sq derivatives and finite difference approximations. Since those didn’t work I’ve found several presentations, whitepapers etc, that show people aren’t using standard mathematical methods of calculating a derivative.

here are is a good document showing how to calculate derivatives using a finite differecnes.

Note, that for first order approximations you divide by h (the step size) for the 1st derivative, and h^2 for the second derivative.

if you check this white paper from Rockwell automation.

or this derivation video by a university professor.

you will see for the first derivative they aren’t dividing by h, and for the second derivative they are dividing by h instead of h^2.

while i find this simplification/hack odd, it seems lots of people are doing it this way, and it does limit the amount of filtering/smoothing needed as it reduced the magnitude of the derivatives significantly.

I still don’t have it working with my worst case scenario bed, but I’m much close than i was previously.