Just wanted to spread the joy to those who haven’t heard the news about the latest release. I’m late to the klipper party, in fact still haven’t tried it yet. So I was happy when I saw Marlin added input shaping.
I haven’t played with it much. It can’t make use of an accelerometer yet, but the devs are already discussing how to improve tuning. Also you can’t select the model yet, which is another thing devs seem hot on adding soon. I’m not waiting though… I am starting the process of manual tuning using ringing towers. The guide on the github PR explains the process as it relates to Marlinfw, using Prusaslicer to generate stl’s. I’m hoping I can get my printer to go faster with it.
MarlinFirmware:bugfix-2.1.x
← tombrazier:input_shaping
opened 09:17PM - 24 Sep 22 UTC
### Description
Use record and replay of Bresenham algorithm calculations to … effect ZV input shaping for X and Y axes.
It's quite rough and ready at present. At least the following are on my mind:
- It requires record buffers which consume more SRAM than I like. Longer term there are ways of doing this with no buffers.
- The "echo" comes after the main signal. I think there should really be two "pulses" one before and one after the logic for non-shaped axes for better alignment between the shaped and non-shaped axes.
#### How it works
Input shaping involves scaling a input control signal and overlaying it with one or more scaled and time-shifted "echoes" of itself to produce an output signal that damps oscillations. For example if an acceleration of 1000mm/s^2 started at time t, input shaping might change that to 500mm/s^2 at time t and then a boost of another 500mm/s^2 at time t + x. The value of x would be related to a resonant frequency of the physical printer.
The input signal could be anything that scales linearly. So it could be a time sequence of accelerations, as in the example above, or of velocities or of axis positions or even of stepper pulses. Input shaping works with all of these.
This solution treats calls to `Stepper::pulse_phase_isr()` as the input signal. For the shaped axes, it scales the effect of calling `Stepper::pulse_phase_isr()` and records and replays these calls. This is a simple place to insert the input shaping logic in Marlin and none of the rest of Marlin needs to know about it at all.
`Stepper::pulse_phase_isr()` is where the Bresenham logic happens. Each time the function is called it increments position counters for all the axes. Whenever a counter for any particular axis passes a specific value, a stepper pulse is generated for that axis. By overlaying two or more sequences of calls to the `Stepper::pulse_phase_isr()` logic, the rate of pulse generation is dependent on both the original input signal and the echo.
One concern that might spring to mind is whether overlaying two sets of logic that generate pulses might cause irregular timings of stepper pulses. Such irregular pulse trains cause uneven movement and are also known to cause some TMC stepper drivers to go to sleep. However this approach does not directly overlay pulse trains. Instead it overlays Bresenham logic and this results in clean pulse trains.
Here's a picture of how stepper pulses are generated for a move only in one axis, say X. The green line represents calls to `Stepper::pulse_phase_isr()`, the yellow line is the delayed echo which results in calls to `Stepper::inputshaping_isr_x()` (which duplicates the Bresenham logic), blue is the Bresenham counter which resets every second time it is incremented and red is the output stepper pulses which are generated whenever the Bresenham counter resets.
![Figure_1](https://user-images.githubusercontent.com/68918209/192245273-dba9efb6-35c1-4145-8923-d7b1d2bf3534.png)
In that case the delay works out such that calls to `Stepper::inputshaping_isr_x()` do not causes stepper pulses but just make them happen on the next call to `Stepper::pulse_phase_isr()`. Here is an example where it's the other way around, i.e. `Stepper::inputshaping_isr_x()` generates pulses and `Stepper::pulse_phase_isr()` just increments the counter.
![Figure_1-1](https://user-images.githubusercontent.com/68918209/192245434-696c38c2-be06-495c-af62-f0b80786fe3f.png)
Either way we do not get double pulses or irregular pulse distances.
#### Changing direction
When there is a direction change for an axis between two segments, this complicates matters. With the echo, this direction change could occur anywhere in the time overlap between the segment ending and its echoes ending. This has two consequences:
- The Bresenham counter now needs to be signed and able to count in either direction.
- The Bresenham logic needs to recognise when the net effects of the original signal and the echo have changed from increasing to decreasing or vice versa.
As a first step, the logic in `Stepper::block_phase_isr()` which normally handles direction changes still does so if the echo buffers are empty when a segment is started. This will happen for the first segment printed in a run of segments. It will also happen for the first segment after a call to `Planner::synchronize()`. The latter is critical for homing where the direction change needs to be noted by the stepper module as soon as a bump segment starts printing. Otherwise the endstop logic will think he stepper module is still moving the print head towards the endstop.
More generally, the Bresenham logic can know for sure that a direction change has occurred if the Bresenham counter becomes sufficiently positive when the direction is backwards or sufficiently negative when the direction is forwards. This can introduce a minor delay in issuing the first step after a direction change but it does not appear to be problematic.
#### Compatibility
Input shaping introduces sections at the start and end of segments where the speed is combined with the speed of the previous/next segment or, in the case of a first or last segment, is halved. Whether laser power needs adjusting in these segments and how do so needs attention.
Input shaping may or may not work with direct stepping. This needs looking at.
For now, this PR introduces a sanity check preventing input shaping from being used with either of these two features.
### The future
A better implementation would eliminate the buffer(s) that hold the echo data.
I think we could insert a step between the planner and the stepper modules which plans individual motions. One segment would produce up to three of these, for the accel, cruise and decel. So the stepper module would stop knowing about the three phases and only concern itself with single motions each of which is governed by one equation of motion. For an accel/decel phase the equation would be d = u.t + a/2 . t^2. (d is distance, u is initial speed and a is acceleration). For cruise phase it would be the same with a = 0. For SCURVE it would be the same but a higher order polynomial. The beauty of this is the IS can then be implemented simply by modifying this stream of equations of motion because overlapping signals can be summed just by summing the coefficients of the overlapping polynomials.
### Testing / tuning
Tuning follows a similar process to [that for Klipper](https://www.klipper3d.org/Resonance_Compensation.html).. We still need to document this for Marlin. Klipper's [tuning tower](https://www.klipper3d.org/prints/ringing_tower.stl) can be used for tuning with `M593`. Print as a single or double perimeter with little or no infill. You may need to set the fan on full to reduce warping.
When slicing the tower, watch out that the slicer doesn't slow or smooth the corners. Things to watch out for:
- Set a high speed (80-100 mm/s).
- Set high accelerations (>= 1000mm/s^2) for X and Y. Watch out for the many slicer settings that can affect this. e.g. acceleration limits for print and travel moves and generate `M204` codes as well as per axis limits that generate `M201` codes.
- For both speed and acceleration, also look out for perimeter specific settings.
- Make sure the cooling settings do not specify slowing down to increase per layer printing time.
- Set the junction deviation as high as possible (with custom gcode `M205 J0.3`). Or, if using classic jerk, set the jerk settings as high as you can.
- Do not use the Arachne engine - it smooths out corners.
`M593`'s parameters are
```
* D<factor> Set the zeta/damping factor. If axes (X, Y, etc.) are not specified, set for all axes.
* F<frequency> Set the frequency. If axes (X, Y, etc.) are not specified, set for all axes.
* T[map] Input Shaping type, 0:ZV, 1:EI, 2:2H EI (not implemented yet)
* X<1> Set the given parameters only for the X axis.
* Y<1> Set the given parameters only for the Y axis.
```
If using PrusaSlicer the following will print a frequency tuning tower, ranging acceleration from 1500 to 7000:
```
; Input Shaping Frequency Testing
{if layer_z==layer_height}M204 P1500; Set Acceleration at Layer 1{endif}
{if layer_z==5}M204 P2000; Set Acceleration at Layer 5{endif}
{if layer_z==10}M204 P2500; Set Acceleration at Layer 10{endif}
{if layer_z==15}M204 P3000; Set Acceleration at Layer 15{endif}
{if layer_z==20}M204 P3500; Set Acceleration at Layer 20{endif}
{if layer_z==25}M204 P4000; Set Acceleration at Layer 25{endif}
{if layer_z==30}M204 P4500; Set Acceleration at Layer 30{endif}
{if layer_z==35}M204 P5000; Set Acceleration at Layer 35{endif}
{if layer_z==40}M204 P5500; Set Acceleration at Layer 40{endif}
{if layer_z==45}M204 P6000; Set Acceleration at Layer 45{endif}
{if layer_z==50}M204 P6500; Set Acceleration at Layer 50{endif}
{if layer_z==55}M204 P7000; Set Acceleration at Layer 55{endif}
```
Paste the code block above into "Printer Settings" -> "Custom G-code" -> "Before layer change G-code" under any existing code you might have there.
Also set your X & Y max acceleration in your start g-code with M201 X7000 Y7000 and disable Linear Advance (if enabled) with M900 K0.
And the following will print a tuning tower for damping ratio (or zeta) with values 0.1, 0.15, 0.2, 0.25, 0.3, 0.35, 0.4, 0.5, 0.6, 0.7, 0.8 and 0.9:
```
; Input Shaping Zeta Testing
{if layer_z==layer_height}M593 D0.1; Set Input Shaping Zeta at Layer 1{endif}
{if layer_z==5}M593 D0.15; Set Input Shaping Zeta at Layer 5{endif}
{if layer_z==10}M593 D0.2; Set Input Shaping Zeta at Layer 10{endif}
{if layer_z==15}M593 D0.25; Set Input Shaping Zeta at Layer 15{endif}
{if layer_z==20}M593 D0.3; Set Input Shaping Zeta at Layer 20{endif}
{if layer_z==25}M593 D0.35; Set Input Shaping Zeta at Layer 25{endif}
{if layer_z==30}M593 D0.4; Set Input Shaping Zeta at Layer 30{endif}
{if layer_z==35}M593 D0.5; Set Input Shaping Zeta at Layer 35{endif}
{if layer_z==40}M593 D0.6; Set Input Shaping Zeta at Layer 40{endif}
{if layer_z==45}M593 D0.7; Set Input Shaping Zeta at Layer 45{endif}
{if layer_z==50}M593 D0.8; Set Input Shaping Zeta at Layer 50{endif}
{if layer_z==55}M593 D0.9; Set Input Shaping Zeta at Layer 55{endif}
```
Tune zeta after tuning the frequencies and enabling IS. Use a fairly high constant value for acceleration so that you still see ringing even with IS.
### Requirements
Any printer.
### Benefits
Cleaner prints with less ringing.
### Configurations
### Related Issues
#16531
On a side note, that guide showed me how much Slicer has come along over the past few years. I’m switching back to it from cura now. I used Cura because slic3r was missing some key features back then, like placeholders and ‘at layer injections’. Now slicer has that, and then some like conditional gcode and per filament gcodes (I missed per filaments using cura… slicer has always had that).
[edit: Since I mentioned it, I may as well post an example slicer script that actually works:
; Input Shaping Frequency Testing
{if layer_z<=5}M204 P1500; Set Acceleration at 0mm
{elsif layer_z<=10}M204 P2000; Set Acceleration at 5mm
{elsif layer_z<=15}M204 P2500; Set Acceleration at 10mm
{elsif layer_z<=20}M204 P3000; Set Acceleration at 15
{elsif layer_z<=25}M204 P3500; Set Acceleration at 20
{elsif layer_z<=30}M204 P4000; Set Acceleration at 25
{elsif layer_z<=35}M204 P4500; Set Acceleration at 30
{elsif layer_z<=40}M204 P5000; Set Acceleration at 35
{elsif layer_z<=45}M204 P5500; Set Acceleration at 40
{elsif layer_z<=50}M204 P6000; Set Acceleration at 45
{elsif layer_z<=55}M204 P6500; Set Acceleration at 50
{else}M204 P7000; Set Acceleration at 55
{endif}
LOL, the one posted by Tom doesn’t work.
2 Likes
vicious1
(Ryan)
December 21, 2022, 4:50pm
2
Very interesting. I am using CoreXY so this tidbit is going to come in handy.
“do you have a CoreXY machine? You will need to rotate your tuning model 45 degrees. Shaping works per motor, not per axis.”
I am interested to try it soon, My print speeds are near my extruder max, but this will let me increase accels, and possibly make me get a high flow nozzle (and better cooling).
1 Like
jeffeb3
(Jeffeb3)
December 21, 2022, 5:24pm
3
I hope you mean prusa slicer, which is forked from slic3r. Unless there are updates to slic3r I don’t know about, most of the improvements have been by the sw team at prusa.
1 Like
I was referencing both, adding the traditional 3
in there to denote the older stale version. I did call prusaslicer simply ‘slicer’, with e not 3, because prepending prusa is so much work hehe. By no means was I attempting to slight or in any way minimize the excellent work that the prusa sw team has done.
On a side note… 2.1.2 may have some bug with max31865 using sw spi on my gtr 1.0 board (using a breakout for a 3-wire pt100). It keeps giving me max temp errors at bootup… and randomly will boot succesfully. Must me some timing issue that was introduced… trying to sort it now. It worked 100% before.