idex_modes: Fixed the case when carriages home in the same direction (#6310)

Previous version of the code assumed that dual carriages home away
from each other, which is not true on some machines, which have the
second dual carriage homing on the first carriage. The new code
correctly identifies the relative order of the carriages now.

This fixes discrepancies between the documentation and the actual
implementation of the carriages kinematic ranges calculation.

Notes about dual_carriage homing and proximity checks changes

Fixed clearing of homing state after homing in certain modes

In case of multi-MCU homing it is possible that the carriage position
will end up outside of the allowed motion range due to latencies in data
transmission between MCUs. Selecting certain modes after homing could
result in home state clearing instead of blocking the motion of the
active carriage. This commit fixes this undesired behavior.

Signed-off-by: Dmitry Butyugin <dmbutyugin@google.com>
This commit is contained in:
Dmitry Butyugin 2023-09-30 02:46:42 +09:00 committed by GitHub
parent 21b7842979
commit a4cd0336bd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 62 additions and 8 deletions

View File

@ -8,6 +8,15 @@ All dates in this document are approximate.
## Changes ## Changes
20230826: If `safe_distance` is set or calculated to be 0 in `[dual_carriage]`,
the carriages proximity checks will be disabled as per documentation. A user
may wish to configure `safe_distance` explicitly to prevent accidental crashes
of the carriages with each other. Additionally, the homing order of the primary
and the dual carriage is changed in some configurations (certain configurations
when both carriages home in the same direction, see
[[dual_carriage] configuration reference](./Config_Reference.md#dual_carriage)
for more details).
20230810: The flash-sdcard.sh script now supports both variants of the 20230810: The flash-sdcard.sh script now supports both variants of the
Bigtreetech SKR-3, STM32H743 and STM32H723. For this, the original tag Bigtreetech SKR-3, STM32H743 and STM32H723. For this, the original tag
of btt-skr-3 now has changed to be either btt-skr-3-h743 or btt-skr-3-h723. of btt-skr-3 now has changed to be either btt-skr-3-h743 or btt-skr-3-h723.

View File

@ -2065,6 +2065,14 @@ in this section (CARRIAGE=0 will return activation to the primary carriage).
Dual carriage support is typically combined with extra extruders - the Dual carriage support is typically combined with extra extruders - the
SET_DUAL_CARRIAGE command is often called at the same time as the SET_DUAL_CARRIAGE command is often called at the same time as the
ACTIVATE_EXTRUDER command. Be sure to park the carriages during deactivation. ACTIVATE_EXTRUDER command. Be sure to park the carriages during deactivation.
Note that during G28 homing, typically the primary carriage is homed first
followed by the carriage defined in the `[dual_carriage]` config section.
However, the `[dual_carriage]` carriage will be homed first if both carriages
home in a positive direction and the [dual_carriage] carriage has a
`position_endstop` greater than the primary carriage, or if both carriages home
in a negative direction and the `[dual_carriage]` carriage has a
`position_endstop` less than the primary carriage.
Additionally, one could use "SET_DUAL_CARRIAGE CARRIAGE=1 MODE=COPY" or Additionally, one could use "SET_DUAL_CARRIAGE CARRIAGE=1 MODE=COPY" or
"SET_DUAL_CARRIAGE CARRIAGE=1 MODE=MIRROR" commands to activate either copying "SET_DUAL_CARRIAGE CARRIAGE=1 MODE=MIRROR" commands to activate either copying
or mirroring mode of the dual carriage, in which case it will follow the or mirroring mode of the dual carriage, in which case it will follow the

View File

@ -65,7 +65,13 @@ class DualCarriages:
kin.update_limits(self.axis, target_dc.get_rail().get_range()) kin.update_limits(self.axis, target_dc.get_rail().get_range())
def home(self, homing_state): def home(self, homing_state):
kin = self.printer.lookup_object('toolhead').get_kinematics() kin = self.printer.lookup_object('toolhead').get_kinematics()
for i, dc_rail in enumerate(self.dc): enumerated_dcs = list(enumerate(self.dc))
if (self.get_dc_order(0, 1) > 0) != \
self.dc[0].get_rail().get_homing_info().positive_dir:
# The second carriage must home first, because the carriages home in
# the same direction and the first carriage homes on the second one
enumerated_dcs.reverse()
for i, dc_rail in enumerated_dcs:
self.toggle_active_dc_rail(i, override_rail=True) self.toggle_active_dc_rail(i, override_rail=True)
kin.home_axis(homing_state, self.axis, dc_rail.get_rail()) kin.home_axis(homing_state, self.axis, dc_rail.get_rail())
# Restore the original rails ordering # Restore the original rails ordering
@ -78,9 +84,15 @@ class DualCarriages:
axes_pos = [dc.get_axis_position(pos) for dc in self.dc] axes_pos = [dc.get_axis_position(pos) for dc in self.dc]
dc0_rail = self.dc[0].get_rail() dc0_rail = self.dc[0].get_rail()
dc1_rail = self.dc[1].get_rail() dc1_rail = self.dc[1].get_rail()
range_min = dc0_rail.position_min if mode != PRIMARY or self.dc[0].is_active():
range_max = dc0_rail.position_max range_min = dc0_rail.position_min
range_max = dc0_rail.position_max
else:
range_min = dc1_rail.position_min
range_max = dc1_rail.position_max
safe_dist = self.safe_dist safe_dist = self.safe_dist
if not safe_dist:
return (range_min, range_max)
if mode == COPY: if mode == COPY:
range_min = max(range_min, range_min = max(range_min,
@ -88,7 +100,7 @@ class DualCarriages:
range_max = min(range_max, range_max = min(range_max,
axes_pos[0] - axes_pos[1] + dc1_rail.position_max) axes_pos[0] - axes_pos[1] + dc1_rail.position_max)
elif mode == MIRROR: elif mode == MIRROR:
if dc0_rail.get_homing_info().positive_dir: if self.get_dc_order(0, 1) > 0:
range_min = max(range_min, range_min = max(range_min,
0.5 * (sum(axes_pos) + safe_dist)) 0.5 * (sum(axes_pos) + safe_dist))
range_max = min(range_max, range_max = min(range_max,
@ -102,14 +114,39 @@ class DualCarriages:
# mode == PRIMARY # mode == PRIMARY
active_idx = 1 if self.dc[1].is_active() else 0 active_idx = 1 if self.dc[1].is_active() else 0
inactive_idx = 1 - active_idx inactive_idx = 1 - active_idx
if active_idx: if self.get_dc_order(active_idx, inactive_idx) > 0:
range_min = dc1_rail.position_min
range_max = dc1_rail.position_max
if self.dc[active_idx].get_rail().get_homing_info().positive_dir:
range_min = max(range_min, axes_pos[inactive_idx] + safe_dist) range_min = max(range_min, axes_pos[inactive_idx] + safe_dist)
else: else:
range_max = min(range_max, axes_pos[inactive_idx] - safe_dist) range_max = min(range_max, axes_pos[inactive_idx] - safe_dist)
if range_min > range_max:
# During multi-MCU homing it is possible that the carriage
# position will end up below position_min or above position_max
# if position_endstop is too close to the rail motion ends due
# to inherent latencies of the data transmission between MCUs.
# This can result in an invalid range_min > range_max range
# in certain modes, which may confuse the kinematics code.
# So, return an empty range instead, which will correctly
# block the carriage motion until a different mode is selected
# which actually permits carriage motion.
return (range_min, range_min)
return (range_min, range_max) return (range_min, range_max)
def get_dc_order(self, first, second):
if first == second:
return 0
# Check the relative order of the first and second carriages and
# return -1 if the first carriage position is always smaller
# than the second one and 1 otherwise
first_rail = self.dc[first].get_rail()
second_rail = self.dc[second].get_rail()
first_homing_info = first_rail.get_homing_info()
second_homing_info = second_rail.get_homing_info()
if first_homing_info.positive_dir != second_homing_info.positive_dir:
# Carriages home away from each other
return 1 if first_homing_info.positive_dir else -1
# Carriages home in the same direction
if first_rail.position_endstop > second_rail.position_endstop:
return 1
return -1
def activate_dc_mode(self, index, mode): def activate_dc_mode(self, index, mode):
toolhead = self.printer.lookup_object('toolhead') toolhead = self.printer.lookup_object('toolhead')
toolhead.flush_step_generation() toolhead.flush_step_generation()