调整传感器位置

This commit is contained in:
2026-10-14 19:40:22 +08:00
parent 812dd14715
commit 1ca1bb739a
7 changed files with 107 additions and 37 deletions
@@ -148,11 +148,16 @@ def plan_pose_move(
return MovePlan(steps=tuple(steps), title=title, held=tuple(hold), together=together)
def plan_return_to_initial(service: MotionService) -> MovePlan:
"""Yaw, pitch, table X/Y and the press axis back to zero together."""
def plan_return_to_initial(
service: MotionService, *, x_mm: float = 0.0, y_mm: float = 0.0
) -> MovePlan:
"""Yaw, pitch and the press axis back to zero. Table X/Y park at ``x_mm`` and ``y_mm``."""
targets = {role: 0.0 for role in ALIGN_ORDER}
targets["x"] = float(x_mm)
targets["y"] = float(y_mm)
return plan_pose_move(
service,
{role: 0.0 for role in ALIGN_ORDER},
targets,
order=ALIGN_ORDER,
together=True,
)
@@ -64,16 +64,16 @@ AXIS_ROLES: tuple[str, ...] = ("x", "y", "z", "yaw", "pitch")
LINEAR_ROLES: tuple[str, ...] = ("x", "y", "z")
ROTARY_ROLES: tuple[str, ...] = ("yaw", "pitch")
#: Default mapping, from the bench: drives 0-4 are x, y, pitch, yaw, z.
#: Default mapping, from the bench: drives 0-4 are x, y, yaw, pitch, z.
#:
#: Deliberately *not* the same as the model's own order. It is written the way the
#: machine is wired, so reading it against the terminal strip is a check rather than
#: a translation.
#: Pitch and yaw were first wired the other way around. Jogging the machine
#: showed drive 2 is yaw (回转) and drive 3 is pitch (俯仰). The degree scales
#: stay on the drive numbers in ``units.py``.
DEFAULT_AXIS_ROLES: dict[str, int] = {
"x": 0,
"y": 1,
"pitch": 2,
"yaw": 3,
"pitch": 3,
"yaw": 2,
"z": 4,
}
@@ -905,10 +905,11 @@ class MotionService:
),
)
try:
sent = float(value)
if absolute:
self._transport.move_abs(axis, float(value))
self._transport.move_abs(axis, sent)
else:
self._transport.move_rel(axis, float(value))
self._transport.move_rel(axis, sent)
except (ZAuxError, OSError) as exc:
code = getattr(exc, "code", 0)
return self._logged(
+44 -16
View File
@@ -99,11 +99,10 @@ def _default_safety_limits() -> dict[str, float]:
#: How far the axis may descend looking for contact before the run is
#: aborted (workflow P7, no-contact abort).
"contact_search_mm": 3.0,
#: Stage envelope, from the confirmed bench values: the yaw stage turns a
#: half-turn either way, the pitch stage a full turn. These are the limits a
#: pose move is checked against before any axis is commanded.
#: Stage envelope. Yaw turns ±180. Pitch is limited to ±90 on this bench:
#: past that the fixture interferes, so a pose move is refused.
"max_yaw_deg": 180.0,
"max_pitch_deg": 360.0,
"max_pitch_deg": 90.0,
}
@@ -220,21 +219,20 @@ def default_preferences() -> dict[str, Any]:
#: x/y/z translation along the table's axes and a yaw about its z. **The z
#: is the mounting plate**, so the plate is not a setting of its own — it
#: lifts the sensing face and does nothing else, and two fields for one
#: number drift apart.
"finger_sensor_offset_mm": [0.0, 0.0, 0.0],
#: number drift apart. The bench plate under the finger is 8 mm thick.
"finger_sensor_offset_mm": [0.0, 0.0, 8.0],
"finger_sensor_yaw_deg": 0.0,
#: Where the tool is, in the table frame, mm. Straight above the table
#: origin unless the real machine says otherwise; every XY move is measured
#: against it, so it is what makes the reported move match the bench. A tool
#: tip offset belongs here — it used to be a separate field, and having two
#: of them is what let the picture and the numbers disagree.
#: Where the tool is, in the table frame, mm. A tool-tip offset belongs
#: here. The bench X shift is the initial position, not this number.
"finger_tool_xy_mm": [0.0, 3.0],
#: Stage travel, degrees. Confirmed at the bench: the vertical stage turns
#: a full half-turn either way (±180), and the horizontal stage turns
#: continuously. An earlier value of 90 was wrong and made half the patch
#: look unreachable when it is not.
#: Where 「回初始位」 parks the table, mm. X is 1 mm toward the negative
#: direction. Y is 15.0 mm forward of the old zero.
"table_initial_x_mm": -1.0,
"table_initial_y_mm": 15.0,
#: Stage travel, degrees. Yaw turns ±180. Pitch is held to ±90 because the
#: fixture interferes outside that.
"finger_yaw_limit_deg": 180.0,
"finger_pitch_limit_deg": 360.0,
"finger_pitch_limit_deg": 90.0,
}
@@ -277,6 +275,10 @@ def load_preferences() -> dict[str, Any]:
offset.append(0.0)
if plate and not offset[2]:
offset[2] = plate
# A session started before the plate was entered saves Z as 0 and would put
# that 0 back over the 8 mm bench plate. Keep a stored non-zero thickness.
if not offset[2]:
offset[2] = float(defaults["finger_sensor_offset_mm"][2])
prefs["finger_sensor_offset_mm"] = offset
# The tool tip offset used to be a vector added to the reported coordinates
@@ -299,6 +301,27 @@ def load_preferences() -> dict[str, Any]:
if value and not tool[index]:
tool[index] = value
prefs["finger_tool_xy_mm"] = tool
# Closing AutoCal writes the whole in-memory file back. A session that started
# on the previous initial position would otherwise put that old Y over this one.
prefs["table_initial_x_mm"] = defaults["table_initial_x_mm"]
prefs["table_initial_y_mm"] = defaults["table_initial_y_mm"]
# 360° was the old pitch envelope. This fixture interferes outside ±90, and a
# session started on 360 would write that envelope back on close.
limits = prefs.get("safety_limits")
if isinstance(limits, dict):
try:
if float(limits.get("max_pitch_deg")) == 360.0:
limits["max_pitch_deg"] = 90.0
except (TypeError, ValueError):
limits["max_pitch_deg"] = 90.0
try:
if float(prefs.get("finger_pitch_limit_deg")) == 360.0:
prefs["finger_pitch_limit_deg"] = 90.0
except (TypeError, ValueError):
prefs["finger_pitch_limit_deg"] = 90.0
# Yaw travel is ±180. The same-sign pitch solution sits near ±150° of yaw,
# and a stored ±90 drops it, leaving the opposite pitch sign on one side.
prefs["finger_yaw_limit_deg"] = float(defaults["finger_yaw_limit_deg"])
# A hand-edited task number must not reach the controller as typed.
prefs["bas_task"] = clamp_bas_task(prefs.get("bas_task"))
prefs = _migrate_axis_roles(prefs)
@@ -333,6 +356,11 @@ def _migrate_axis_roles(prefs: dict[str, Any]) -> dict[str, Any]:
merged[role] = int(value)
except (TypeError, ValueError):
continue
# The first bench note had these two drives swapped. A session that still
# has that pair would otherwise put it back on close.
if merged.get("pitch") == 2 and merged.get("yaw") == 3:
merged["pitch"] = 3
merged["yaw"] = 2
prefs["axis_roles"] = merged
return prefs
+15 -4
View File
@@ -278,8 +278,8 @@ class AutoCalMainWindow(QMainWindow):
# platform connected. Its stack carries the bench's stage travel limits,
# because a pose outside them is not a pose.
self.finger_stack = PlatformStack(
yaw_limit_deg=float(self.prefs.get("finger_yaw_limit_deg") or 90.0),
pitch_limit_deg=float(self.prefs.get("finger_pitch_limit_deg") or 360.0),
yaw_limit_deg=float(self.prefs.get("finger_yaw_limit_deg") or 180.0),
pitch_limit_deg=float(self.prefs.get("finger_pitch_limit_deg") or 90.0),
)
self.finger_panel = FingerPanel(
self.finger_stack,
@@ -1104,8 +1104,19 @@ class AutoCalMainWindow(QMainWindow):
)
def _on_return_to_initial(self) -> None:
"""Yaw, pitch, table X/Y and the press axis back to zero together."""
plan = plan_return_to_initial(self.poller.service)
"""Yaw, pitch and Z back to zero. Table X/Y park at the stored initial."""
def initial(key: str) -> float:
try:
return float(self.prefs.get(key) or 0.0)
except (TypeError, ValueError):
return 0.0
plan = plan_return_to_initial(
self.poller.service,
x_mm=initial("table_initial_x_mm"),
y_mm=initial("table_initial_y_mm"),
)
self._start_move(plan, t("move.return_initial"))
def _start_move(self, plan, title: str, *, report=None) -> None: