Comparing klipper and RRF input shaper data collection
-
Hi in another thread (https://forum.duet3d.com/topic/25931/input-shaping-testing-and-thoughts) I reported my progress with using input shaping to deal with some of the ringing on my printer. As a result of these tests I decided to take a closer look at how klipper and RRF collect resonance data to be used as the basis for analysis of ringing and to help a user configure input shaping. Hopefully this will provide some useful input to @DC42 and @mfs12 in the production of the RRF input shaper plugin.
So both systems basically run some sort of movement to excite resonances in the printer and use an accelerometer to collect the data, In particular:
- RRF performs a simple linear move at the maximum current movement speed and when that move stops it then collects typically 1000 samples (taking less than a second) which is about 46kBytes of data.
- klipper runs what they call a resonance test that basically moves the print head from side to side a short distance ramping up the speed (and acceleration) of these movements to scan through a set of frequencies (the default range is 5 to 133Hz). This generates about 130 seconds worth of data, which is typically 170,000 samples, 3.6MBytes of data.
To allow comparison of the two I created a script that generates a klipper style set of movements to allow me to record a set of sample use RRF.
I collected two sets of data on my CoreXY printer. The first graph shows the RRF FFT results:
The second is the klipper style data:
As can be seen the give very different results!I also modified the sample files to allow them to be processed by the klipper calibrate_shaper.py script. For the RRF style data it gave:
The klipper style data gives:
With such different outcomes it is hard to really decide which is correct. I've not tested the recommendation based on the klipper style data (RRF does not currently support the ei shaper). However based on my previous tests (and on measuring the ringing on the prints), the main visible problem is the hump that can be seen around 40Hz on the klipper style data. Some further thoughts and comments....
- Looking at the raw samples the current RRF data set only has a very small number of actual "real" values, in the file shown here only the first 100 points are above the noise floor. I'm not sure if this is really enough data to allow for an accurate analysis of what is happening over the given frequency range.
- The current input shaper plugin performs the test using the maximum movement speed, in my case this was 200mm/s. I'm not sure that this is a good idea. On my machine this introduces a considerable amount of energy into the frame of the printer such that it rocks. I'm pretty sure that this is responsible for the very low frequency peaks shown in the output. Reducing the maximum speed gives a different output (with not such a large low frequency peak), but has even fewer samples above the noise.
- To check that the "klipper" data was working correctly I monitored the test using a "spectrum analyser" app on my phone and it did indeed show a rising frequency from around 5Hz to 133Hz. So I think it is working correctly.
- To allow me to collect the larger volume of data needed for the klipper runs I had to modify RRF (in this case the STM32 port) to allow more than 64K of samples and also to fix an overflow problem in the data rate calculation when there are a large number of samples being saved.
I will continue to investigate this a little further, but I thought this data might be of interest and I'd welcome any thoughts or feedback.
-
@gloomyandy I would expect that 40Hz resonance to show up in the data collected by the input shaping plugin. Does it? It may show up after a movement in X but not after a movement in Y, or vice versa.
-
@dc42 Hi David, the first graph above is collected using the input shaper plugin with movement in X with my movement speed set to 200mm/s and set to collect 2000 samples (because the klipper script throws errors if you only give it 1000!). It is displayed here using the accelerometer plugin because I could not find any way to get the current input shaper plugin to display existing files (to allow a comparison with the "klipper style" data). If you look carefully there is a sort of peak visible around 40Hz, but it is not very obvious. It is I'd say more obvious in the "klipper style" data.
For comparison here is the graph taken from my previous investigation for Y (if anything the ringing is worse on Y than X). This time the graph is displayed by the input shaper plugin. Again if you look closely you can just about see a peak, but it is not very obvious:
-
@gloomyandy, wow, nice work!
can you share the script which creates you all the klipper style test movements? Could be handy to integrate it at some point.
-
I think doing continuous recording over longer periods might help to find issues which are not seen at the moment with the short burst recordings.
-
@mfs12 Here is the script. It is pretty much a simple translation of the klipper code. A few notes:
- I originally hoped to just have it drive the printer, but the script was too slow (at least in SBC mode), so instead it generates a gcode file that you then need to print.
- I modified it to use relative coordinates so that you can just position the print head before you run the script.
- There is something a little odd going off with the generated gcode, it does not run for as long as I would expect (approx 130 seconds). I'm not totally sure why, it could be because at the higher frequencies the movement is very small. It may be that jerk has some impact. I'm not sure if this problem is due to my script or the original klipper code.
- If you want to use it to collect acceleration data you will need a modified version of RRF that allows more than 64K samples to be collected and that fixes the overflow bug in the data rate calculation.
- Use it with care, and be ready to hit emergency stop!
var title = "Klipper style vibration test V0.2" var axis="X" var datarate=1320 var stime = state.upTime var min_freq = 5.0 var max_freq = 10000/75 var accel_per_hz = 75 var hz_per_sec = 1 var freq = var.min_freq var max_accel = var.max_freq * var.accel_per_hz var X = 150.0 var Y = 150.0 var sign = 1.0 var t_seg = 0 var accel = 0 var max_v = 0 var L = 0 var nX = 0 var old_freq = 0 echo "max_accel " ^ var.max_accel ^ " min freq " ^ var.min_freq ^ " max freq " ^ var.max_freq echo > "/gcodes/vtest.gcode" " " ;echo >>"/gcodes/vtest.gcode" "G1 X" ^ var.X ^ " Y" ^ var.Y echo >>"/gcodes/vtest.gcode" "M201 X" ^ var.max_accel ^ " Y" ^ var.max_accel echo >>"/gcodes/vtest.gcode" "G91" echo >>"/gcodes/vtest.gcode" "M400" echo >>"/gcodes/vtest.gcode" "G4 S1" echo >>"/gcodes/vtest.gcode" "M956 P0 A0 S" ^ (var.max_freq - var.min_freq) * var.datarate * var.hz_per_sec ;G1 X{var.X} Y{var.Y} echo "Testing " ^ var.freq while var.freq < var.max_freq + 0.000001 set var.t_seg = 0.25 / var.freq set var.accel = var.accel_per_hz * var.freq set var.max_v = var.accel * var.t_seg ;echo >>"/jobs/vtest.gcode" "M204 P" ^ {var.accel} echo >>"/gcodes/vtest.gcode" "M204 P" ^ var.accel set var.L = 0.5 * var.accel * (var.t_seg*var.t_seg) set var.nX = (var.sign * var.L) ;echo "freq " ^ var.freq ^ " t_seg " ^ var.t_seg ^ " accel " ^ var.accel ^ " max_v " ^ var.max_v ^ " L " ^ var.L ^ " nX " ^ var.nX ^ " {nX} " ^ {var.nX} ;echo >>"vtest.gcode" "G1 X" ^ {var.nX} ^ " F" ^ {var.max_v*60} echo >>"/gcodes/vtest.gcode" "G1 X" ^ var.nX ^ " F" ^ var.max_v*60 echo >>"/gcodes/vtest.gcode" "G1 X" ^ -var.nX set var.sign = -var.sign set var.old_freq = var.freq set var.freq = var.freq + (2. * var.t_seg * var.hz_per_sec) if floor(var.freq) > floor(var.old_freq) echo "Testing " ^ var.freq echo >>"/gcodes/vtest.gcode" "G90" echo "total time " ^ state.upTime - var.stime
-
@gloomyandy is this a powershell script?
-
BTW klipper filters the low frequencies, so everything below 10Hz and above 200Hz is filtered. This might be the reason the hump at 40Hz in Klipper's data is visible.
-
@mfs12 The script is a RRF gcode script, it creates another gcode file that actually performs the resonance test. This version uses the defaults klipper uses so anly runs from 5Hz to 130Hz (but you can change some of that in the script). Yes I'm sure that klipper performs some filtering (and that probably makes sense). However if you look at the second chart above, which is a RRF accelerometer chart of the "klipper style" data (and so has not been filtered) I think the 40Hz hump is fairly easy to spot.
Just to be clear I'm not trying to say that the "klipper style" data is necessarily correct, but I do think it is worth looking at the two different ways of collecting data. The relatively small number of useful data points that the current RRF mechanism generates along with the large peaks at relatively low frequencies (on my printer at least and from what I've seen on some other printers as well), concerns me.
-
@gloomyandy
I nominate this as macro of the month -
I've been trying out your script and I think it's working well but I have a couple of questions/comments.
When viewing the resulting .csv file in the Accelerometer plugin the sampling rate (from the last line of the csv file) is auto-set to 28. This is too low and the "Analyze" button does not activate (the minimum appears to be 400). I assume I should change this to 1320 (from "var datarate=1320" in the script) but I have to wonder why the M956 command is writing "Rate 28" to the end of the csv.
In order to make use of the "var axis" variable, lines 41 and 42 should be changed from:
echo >>"/gcodes/vtest.gcode" "G1 X" ^ var.nX ^ " F" ^ var.max_v*60 echo >>"/gcodes/vtest.gcode" "G1 X" ^ -var.nX
to:
echo >>"/gcodes/vtest.gcode" "G1 " ^ var.axis ^ var.nX ^ " F" ^ var.max_v*60 echo >>"/gcodes/vtest.gcode" "G1 " ^ var.axis ^ -var.nX
Thanks for the good work! This is really cool. I especially like that it's collecting so many data points and from both positive and negative directions of movement.
Also, so far sampling with Accelerometer plugin, Input Shaper plugin and your script all agree showing peaks @53 (+/-2) for X&Y on my delta.
-
@ajdtreyd said in Comparing klipper and RRF input shaper data collection:
When viewing the resulting .csv file in the Accelerometer plugin the sampling rate (from the last line of the csv file) is auto-set to 28. This is too low and the "Analyze" button does not activate (the minimum appears to be 400). I assume I should change this to 1320 (from "var datarate=1320" in the script) but I have to wonder why the M956 command is writing "Rate 28" to the end of the csv.
See my notes above:
"To allow me to collect the larger volume of data needed for the klipper runs I had to modify RRF (in this case the STM32 port) to allow more than 64K of samples and also to fix an overflow problem in the data rate calculation when there are a large number of samples being saved."What you are seeing is the effect of the overflow I mentioned in this. I've drawn it to DC42's attention and hopefully it will be fixed in a future version of RRF (it will certainly be fixed in the next release of the STM32 port).
Thanks for the fix, to the variable axis code, this was a late addition and had not been tested when I posted the code!
-
@gloomyandy
Is there a chance to squeeze out even more info with a LIS3D-S-H ?
What would I need to change in the macro (sampling rate)? -
Folks, please be aware that if you use this script with a the current version of RRF (V3.4beta6) or a previous version then you will only be able to collect a maximum of 64K samples (so approx 50 seconds of data, with a sample rate of approx 1320), given that the gcode generated by this script runs for longer than this you may not have good coverage of higher frequencies (as they come later in the test sequence). In my tests I am using a modified (and currently unreleased) version of RRF. Later versions of RRF may make it possible to collect more samples but that is up to @dc42. You may be able to experiment with a capturing the full range by setting your accelerometer to use a lower sampling rate, but this is not something I have tried.
As noted above for reasons I don't currently understand the generated gcode executes faster than simple calculations based on the selected acceleration and distance would indicate. So the best way to select the number of samples to capture (and perhaps make an adjustment to the sample rate to keep it below the 64K sample limit), is probably to time how long the gcode takes to run and then modify the M956 command in the gcode file to adjust the number of samples to collect, you may also want to modify your M955 command if you need to a lower sample rate to capture all of the data.
@o_lampe Hard to say, I doubt if the slightly higher sampling rate or resolution will make much difference in this application. If your accelerometer operates at different sampling rate to 1320 then yes you will need to change that. But it is used to calculate how many samples to collect. But please see the note above about the max number of samples you can currently collect.
-
@gloomyandy said in Comparing klipper and RRF input shaper data collection:
Folks, please be aware that if you use this script with a the current version of RRF (V3.4beta6) or a previous version then you will only be able to collect a maximum of 64K samples (so approx 50 seconds of data, with a sample rate of approx 1320), given that the gcode generated by this script runs for longer than this you may not have good coverage of higher frequencies (as they come later in the test sequence). In my tests I am using a modified (and currently unreleased) version of RRF. Later versions of RRF may make it possible to collect more samples but that is up to @dc42. You may be able to experiment with a capturing the full range by setting your accelerometer to use a lower sampling rate, but this is not something I have tried.
It's been a while since I have had my signal processing classes, so feel free to correct any mistakes, but based on theory each acceleration and deceleration contain all the frequency components. (Ok, this is only true for an infinite acceleration / deceleration, but a fast move should be close enough to capture the frequency range of interest.)
The Klipper approach wiggling the head about is mainly giving you more data and hence a higher resolution of the spectrum which is why you see more noise in the plot.
Btw,. reducing the sampling rate "to capture the full range" will have the opposite effect: the Nyquist–Shannon sampling theorem applies and reducing the sampling rate reduces the maximum frequency that can be sampled which is f_sampling/2.
-
One thing I should have mentioned: these are two fundamentally different approaches to achieve the same thing.
-
Klipper does a sweep where they try to stimulate the system at different frequencies. The ideal input signal for this measurement is a perfect sinusoidal not containing other frequency components, the unknown factor here is how well the sinusoid is approximated by the acceleration/deceleration curves.
-
RRF measures an impulse response based on a fast movement. The ideal input signal here is a very fast acceleration which equally contains all the frequency components of interest. The question here is how well that is achieved.
Both should come to a similar conclusion.
-
-
@pixelpieper Thanks for pointing this out.
Honestly I'm not sure if ramping the frequency helps capture a better data set or not, that is simply the way in which klipper does it and was what I was aiming to investigate. I was just pointing out that at the moment if you use standard RRF and my script you will not be capturing all of that data. From your second post I would have thought that this may be a bad thing if the klipper sweep is operating correctly?
As to the actual sampling rate used again this is a difference between RRF and klipper (klipper typically samples at 3200 samples/s). I have no idea if this is significant or not (it certainly means that klipper will have more samples to process). The shaper tuning only seems to consider relatively low frequencies (a max of 200Hz for RRF and I think something similar for klipper), so in theory so long as the sample rate is above 400 samples/second then it should be usable.
I suspect that one of the key advantages that the klipper method has is that it just provides more usable samples than that produced by the current RRF method. Perhaps it might be better to collect several datasets using the RRF method and combine them to increase the number of usable samples? I'd also note that the current plugin (and the how to use instructions) do not make any mention of what acceleration to use, perhaps it would be better to set a higher acceleration if using this method? I wonder if it would be possibly to apply a higher acceleration value just for the deceleration phase of the move as I assume it is this that provides the impulse in this case?
One final thought, I've seen a number of plots that have been produced by myself and other posters in which there seems to be a significant low frequency peak when using the current RRF method, this does not seem to be present with the klipper method. See the first two graphs above, one has a peak around 16Hz that does not seem to be present in the klipper data. I'm not sure which one is correct, but from measuring the ringing on actual prints this peak does not seem match.
-
@pixelpieper said in Comparing klipper and RRF input shaper data collection:
based on theory each acceleration and deceleration contain all the frequency components
Wouldn't it take a certain time for the frame to get into resonance? If so, the frequency in question would need to be applied for longer than just the short accel/deccel time?
-
A quick update. In a previous post I mentioned
There is something a little odd going off with the generated gcode, it does not run for as long as I would expect (approx 130 seconds). I'm not totally sure why, it could be because at the higher frequencies the movement is very small. It may be that jerk has some impact. I'm not sure if this problem is due to my script or the original klipper code.
Jerk does indeed have some impact but that was not the major problem. After running various additional tests and trying to figure out what was going on I reached out to @dc42 and he spotted that the moves being used by this test are not printing moves (as they do not include any extrusion), but I was setting the acceleration for a printing move using M204 P<aaaa>. I have now corrected this by specifying both P and T (just in case!), in the M204 commands. With this change the gcode file now runs for the correct period. Many thanks for your help David!
This has made a small change to the collected data as shown below:
and the data as processed by klipper:
The updated script (which also includes some other small corrections) is:
var title = "Klipper style vibration test V0.3" var axis="Y" var datarate=1320 var stime = state.upTime var min_freq = 5.0 var max_freq = 10000/75 var accel_per_hz = 75 var hz_per_sec = 1 var freq = var.min_freq var max_accel = var.max_freq * var.accel_per_hz var X = 150.0 var Y = 150.0 var sign = 1.0 var t_seg = 0 var accel = 0 var max_v = 0 var L = 0 var nX = 0 var old_freq = 0 var filename = "/gcodes/vtest_" ^ var.axis ^ ".gcode" echo "max_accel " ^ var.max_accel ^ " min freq " ^ var.min_freq ^ " max freq " ^ var.max_freq echo > var.filename " " echo >>var.filename "M201 X" ^ var.max_accel ^ " Y" ^ var.max_accel echo >>var.filename "M205 X0 Y0" echo >>var.filename "G91" echo >>var.filename "M400" echo >>var.filename "G4 S1" echo >>var.filename "M956 P0 A0 S" ^ (var.max_freq - var.min_freq) * var.datarate * var.hz_per_sec echo "Testing " ^ floor(var.freq) echo >> var.filename "echo " ^ var.freq while var.freq < var.max_freq + 0.000001 set var.t_seg = 0.25 / var.freq set var.accel = var.accel_per_hz * var.freq set var.max_v = var.accel * var.t_seg echo >>var.filename "M204 P" ^ var.accel ^ " T" ^ var.accel set var.L = 0.5 * var.accel * (var.t_seg*var.t_seg) set var.nX = (var.sign * var.L) echo >>var.filename "G1 " ^ var.axis ^ var.nX ^ " F" ^ var.max_v*60 echo >>var.filename "G1 " ^ var.axis ^ -var.nX set var.sign = -var.sign set var.old_freq = var.freq set var.freq = var.freq + (2. * var.t_seg * var.hz_per_sec) if floor(var.freq) > floor(var.old_freq) ;echo >> var.filename "echo " ^ floor(var.freq) echo "Testing " ^ floor(var.freq) echo >>var.filename "G90" echo "total time " ^ state.upTime - var.stime
-
@gloomyandy Great, thanks!
Just a comment. you have to change the:
echo >>var.filename blabla
to
echo >>{var.filename} blabla
As described in the meta commands docs:
echo ><filename> <expression>, <expression>, ... where <filename> is either a quoted string or an expression enclosed in { } that evaluates to a string.