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

I’m dealing with a very similar problem at the moment, where I’m trying to implement a closed-loop system for moving optics components, such as kinematic mirror mounts. Mirror mount - Wikipedia

What we essentially ended up doing was strapping stepper motors to the output shafts of these mirror mounts and using an AS5147U encoder to get a closed-loop servo. The servo was implemented with a pretty trivial PID loop that updates at roughly 100Hz. It demonstrated extremely promising results that exceeded the performance of expensive commercial systems.

Yet before I consider proceeding, my supervisor wants to try to get such a system working on readily available hardware without requiring other labs to solder together and manufacture the custom board required. I’m inclined to use Klipper to achieve this endeavour, as it’s software with a large user base that might be able to replicate the system if they are also in optics labs.

I’m curious if you were able to get this feedback control working in Klipper. Given that these optics components do not need to move quickly but rather require very precise positioning, I’m curious about Kevin’s thoughts on the best course of action.

All the best,

Andrew

It seems to me that due to the delay inherent in the Host <> MCU environment that Klipper requires any (near) real time closed loop control is nearly impossible. In order to keep 4 axis synchronized klipper MUST “trust” that the motors moves as instructed. Waiting for conformation is going to always going to be painfully slow.

If closed loop control is required (and klipper is the chosen system) the loop has to be between the MCU and the motor/encoder. While far beyond my technical skills it seems a “smart” motor driver could be built to accept the classic enable, dir, step output from the MCU and modify them as needed to correct for position errors.

That exposes the next question. Is a stepper motor the best solution? Perhaps a “brushless DC” motor (3 phase AC actually) be better suited to the task at hand. Also the feedback encoder should be installed such that the servo control can account for hysteresis between the motor and the tool.

And finally, there are open source Arduino libraries to implement closed loop control of a steeper motor. There are still Arduino printer (and laser engraver) boards available.

Yes, I agree with this. I’m essentially presented with two choices: rework the klippers’ motion planner or handle it on the MCU side. Considering that I already built an external program that ran an outer loop in Python and sort of gave satisfactory results, but were slow to achieve them, providing limited use cases, I might try writing something in C that adds corrective steps and have it present itself to Klipper as a normal stepper motor.

There is a good reason to use Klipper: an optical table may have hundreds of optical components that need to be actuated. Klipper and the software suite enable different boards to communicate with one another.

Also want to avoid using BLDC motor because I need to measure the final drive of a system with a 2000:11 gear ratio.

IMO the way to do closed loop in klipper is to modify stepcompress to output encoder positions at the position loop’s constant update rate instead of step positions at a time varying rate. Then each MCU is responsible for maintaining a bound on basically the I term of the position loop PID and throwing a shutdown or “motion stop” trsync signal if that bound ever fails.

So high level planning and kins and everything change minimally, and as long as your system has enough stiffness to not bounce around too badly and you tune it well / set the right bounds for said bouncing multiple MCUs should stay in sync.

in practice you’ll need to tune the “lag term” at speed per axis to get circular circles etc if you want coordinated multi axis moves but that’s doable automagically I think.

hello. check my project New open source high end closed loop driver klipper compatible

1 Like