Add 'safe_z_lift' module for safe lifting of (unhomed) Z axis#842
Add 'safe_z_lift' module for safe lifting of (unhomed) Z axis#842rschaeuble wants to merge 18 commits intoKalicoCrew:mainfrom
Conversation
fad9443 to
559a727
Compare
|
Before this gets merged, it would be great if a few people could (carefully) test this with their particular setup. I didn't have any issues with this so far, but I can't test any possible setup out there. |
|
This is of course not the only way to implement this. Alterantives:
Looking forward to input on this. |
| return [safe_point1, safe_point2] | ||
|
|
||
| def _safe_lift(self, lift_dist, speed, force_unhomed=False): | ||
| safe_lift = self.printer.lookup_object("safe_z_lift", None) |
There was a problem hiding this comment.
Make this an instance variable - we have a connect handler where we can do this lookup once, so it doesn't need to be done for every lift.
| safe_lift.move_to_safe_z( | ||
| self.toolhead, lift_dist, speed, force_unhomed=force_unhomed | ||
| ) | ||
| else: |
There was a problem hiding this comment.
This can be merged with the inner if, so we end up with if..elif..else
| ) | ||
| return [safe_point1, safe_point2] | ||
|
|
||
| def _safe_lift(self, lift_dist, speed, force_unhomed=False): |
There was a problem hiding this comment.
Should just be called _z_lift because it internally figures out if it can be done safely or not.
| gcode = self.printer.lookup_object("gcode") | ||
| gcode.register_command("G28", self.cmd_G28) | ||
|
|
||
| def register_axis_steppers(self, toolhead, mcu_endstop, axis): |
There was a problem hiding this comment.
There are a bunch of places that use this pattern, so I think this should be introduced in a different patch if we want to do it.
There was a problem hiding this comment.
Inlined for now.
| homing = self.printer.lookup_object("homing") | ||
|
|
||
| # Ensure Z-axis steppers are registered with the auxiliary endstop. | ||
| if not self.z_steppers_registered: |
There was a problem hiding this comment.
Do this in klippy:mcu_identify handler instead. As the system is currently set up, that's the only safe place to register endstop-motor bindings. See e.g. bltouch implementation for example.
| if self.move_to_previous: | ||
| toolhead.manual_move(prevpos[:2], self.speed) | ||
|
|
||
| def _safe_lift(self, toolhead, lift_dist, safe_lift, force_unhomed=False): |
There was a problem hiding this comment.
Call this _lift or _z_lift as it might not be safe and caller shouldn't care.
I think this approach is a little nicer. It lets you set different endstops for different motors, so I guess you could make it easier to express having multiple endstops and stopping at the first one that triggers. It would need an always-on module to handle the shared code.
If there's a shared module it becomes easier - the shared module can find all motors that touch the axis that is moving(e.g. z), and then add the endstops for all those to the group of endstops being moved towards. If you want to implement the more general approach that'd be great, but as the approach you are showing here could be trivially extended in the future to be more general, I don't particularly mind moving forward with the current approach. Best regards, |
|
@dalegaard: if you can assist with some guidance, I'm certainly willing to make this a more generic module. I think the most complex topic is extending the current endstop config. We don't have Having this would allow a much more generic command though. Something like SAVE_MOVE AXIS="x" DIST=100. Maybe with an additional ALLOW_UNSAFE=0|1 flag for cases in which you'd like a safe move, but would be fine with an unsafe one (like safe_z_home and dockable_probe) require it internally. Naming: I suggest naming this module "safe_move". Here's a basic implementation plan:
Does that sound reasonable? |
ea2e020 to
a61abb1
Compare
|
I rewrote it, going for the more generic approach. Still has a few open points/issues:
|
|
Ok, so I just realized that in the direction configured for homing this is basically a homing procedure. In -Z direction this means that on my printer it will attach the probe (if not yet attached) before and detach it afterwards. Now what does that mean for the @dalegaard (and other Kalico maintainers): I'd like to hear your opinion on that. |
|
I think it would make more sense config wise if the user either set I think this also solves the other question, because the endstop pins are now equivalent and are simply picked between when homing starts. That way we can even use sensorless homing for this safety functionality(though we may need to add some extra stuff there to make homing current per-endstop-and-stepper or something). For now I don't think we need to let users actually interact directly with the added endstop - as long as dockable probe, safe_z_home, and friends can use it, that's fine imo. Best regards, |
|
I reworked stepper.py; it now wants endstop_{min,max}_pin or endstop_pin. I think the main remaining issue with using sensorless for that is duplicate pin usage. Not sure if we can allow that without the user having to configure duplicate_pin_override. Haven't really spent time looking into this yet, though. My motivation for exposing SAFE_MOVE is twofolder: it makes testing easier/quicker, and it could be useful for homing_override macros. We can remove if at the end, though, if you'd prefer that. The latest changes are not yet really tested; going to do that later today. Just wanted to know if that's going in the right direction. |
| # # If True, the Y axis will home first. The default is False. | ||
| ``` | ||
|
|
||
| ### [safe_move] |
There was a problem hiding this comment.
I'll keep this until everything else is fine and we make the final decision about the SAFE_MOVE Gcode command.
|
For the actual endstops, I think it should be reworked so we now always group endstops in to either min or max. The legacy endstop is used as either min or max depending on the homing direction. It should be possible to completely forget about the legacy endstop once the config has been read and stuff has been set up. I would introduce an Primary endstop means the endstop of the first motor on the rail, so e.g. Best regards, |
|
@dalegaard: I implemented your suggestion with EndstopCollection. At least I hope so ;-) This still needs testing, and I will make another pass trying to simplify it (and to review for correctness), but that might have to wait until the weekend. Would be nice to get some quick feedback though if that's going in the right direction. I will look at your suggestions about moving get_endstops_for_direction to the kinematics after we're happy with stepper.py. One large construction site at a time ;-) |
|
I improved the code in steppers.py. At least for me readability is better now. Tested it thoroughly as well (with the hardware configuration I have available here). @dalegaard: is this now more or less how you envisioned it? If yes, I'd look at moving get_endstops_for_direction to the kinematics. |
dalegaard
left a comment
There was a problem hiding this comment.
This is looking really good overall! I think getting the direction coordination moved to kinematics will tie it together nicely. Right now we still can't use this with deltas or any other kinematic that doesn't have 1-1 rail-axis mapping. That includes wanting to use this new system for x or y on a CoreXY.
Great job on this!
Best regards,
Lasse
| "Endstop move failed due to printer shutdown" | ||
| ) | ||
| raise | ||
| return epos, hmove.check_no_movement() |
There was a problem hiding this comment.
The endstop might trigger AR exactly the end position the caller requested. Then the positions would be the same but the endstop may or may not have triggered. The caller may want to do something different depending on if the endstop was hot or not, so I think returning it is good. The second return element could be a string/enum thing with "full_move", "hit_endstop", or "already_at_endstop" to signify what happened.
|
|
||
| toolhead = self.printer.lookup_object("toolhead") | ||
| try: | ||
| self.move(toolhead, axis, dist, speed) |
There was a problem hiding this comment.
Might be good to expose "allow_unsafe" to gcode as well, so macro packs can benefit from safe home when the printer supports it.
There was a problem hiding this comment.
Implemented both changes.
the "move result" is not yet exposed further up the chain, but I plan to export stats for the safe_move module.
|
I started work on a get endstops method in Kinematics. A few observation:
Overall, I feel confident implementing this for CoreXY, CoreXZ and non-IDEX Cartesian. So I did that; for everything else an error is raised. If should be relatively easy for someone with a respective printer to add support for SAFE_MOVE for the other kinematics. Once we're happy with this part as well, I'm going to collect the other, smaller issues that are still open, add a few more, and put them into a new comment, so it's easier to keep an overview of what still has to be done. But I think the most difficult part is done :-) |
3a4d261 to
a27eb45
Compare
|
I think all comments/suggestions/issues of the discussion so far have been handled/implemented. Here are a few more points from my notes
@dalegaard: I would appreciate your feedback on these points. |
7c6eefb to
b9ceace
Compare
079a675 to
3606934
Compare
Introduce new module: safe_z_lift.
When the printer is at or near Z-max, operations that require a Z hop (like
safe_z_homeordockable_probedeployment) can crash into the axis' mechanical limits. Thesafe_z_liftmodule allows the printer toperform these lifting moves while monitoring a specific endstop (Z-max endstop). If the endstop is triggered during the
lift, the movement stops immediately, but the operation proceeds without error.
Checklist