Fast closed-loop controllers on the MCU?

I’m working on a project to control the printing process based on sensor signals and to do so I need to control the E and/or Z axes rapidly based on sensor feedback.

I want to do a lot of things with this, but one representative example is controlling the plastic flow based on Z force on the tool to get denser solid infill, particularly at the interface between infill and complex perimeters (eg avoiding voids at the roots of gear teeth).

We have a proof of concept that works OK at the klippy level but the control signal has to wait for the whole motion queue before any commands can take effect so it’s super slow.

This is an unfortunately open ended question, but does anyone see a way to cleanly mix fast feedback control on the MCU with klipper’s beautiful look-ahead planned-on-the-pi architecture?

The two paths that I’ve thought of so far are:

  • Inject extra steps into the stepper queue on the MCU. IE add a “live offset” variable for each stepper object which can be written to by other MCU objects such as a feedback controller, and during the handling of each step event check if the commanded live offset and the current live offset differ, and interleave extra steps in whichever direction as soon as feasible to close that gap. If done well this could maybe be used to make SET_GCODE_OFFSET respond near-instantly, which would be a cool bonus. But, this seems unlikely to work well (can you even inject steps into an existing schedule like that?), and very likely to break non-cartesian kinematics without a lot of work. It might be feasible specifically for the E axis though?

  • “Hand-off” control of a given stepper motor entirely to the closed loop controller, so there’s an open-loop control mode and a closed-loop control mode. IE we would add a command to disable the standard klippy-planned stepper controller and enable a MCU object which does step timing calculations based on the controller target, and then a second command would disable the feedback controller, transfer the current stepper position back to the MCUStepper, and restore open loop control.

Like I said, super open ended question, but if there’s no clean solution here we might have to move this project back onto Marlin which would be a big loss on many other dimensions, so I wanted to get folks’ feedback.

Thanks,
-Nick

For what it is worth, I attempted the second option in the experimental mechaduino code ( Mechaduino experiment ). That is, I translated the step pulses into a “virtual position” on the micro-controller and then used a fixed frequency loop in the micro-controller to set the actual stepper position based on that “virtual position”.

In the more general case, I think the most important question is - what is the desired response time? Anything under 50ms and the logic likely needs to be written in C code on the micro-controller. A response time between 50-100ms may be possible to implement in C code on the host (eg, the “trsync” code used during “multi-mcu homing” is implemented as a mix of C code on the host and micro-controller - it maintains a maximum of 25ms latency for overshoot). For response times above 100ms there is a good chance regular host Python code can be used.

The lookahead queue currently tries to stay 2 seconds ahead. It should be possible to reduce that - 1 second should be fine, and 500ms is likely achievable with a reasonable host computer (eg, rpi3 or faster).

So, there are options depending on the required response time and the amount of development effort one is willing to invest.

Cheers,
-Kevin