加入Z轴运行控制,加快五轴运行速度

This commit is contained in:
2026-10-10 17:16:19 +08:00
parent cf86bd0702
commit 51accb9196
8 changed files with 58 additions and 49 deletions
@@ -10,10 +10,10 @@ matters:
:class:`~orisys_autocal.motion.poller.MotionPoller` is built, and it is why a stop
is possible at all: a mover that sat in a loop inside one command could not be
interrupted, because the stop would have to wait for the loop it is meant to abort.
* **This module** owns the waiting. An execute plan starts yaw, pitch and table X/Y
in one command so they move together; a return plan still retracts one axis at a
time. It watches the snapshots the poller already produces and gives up on its
own if an axis stops short of its target.
* **This module** owns the waiting. Execute and return-to-zero both start yaw, pitch,
table X/Y and the press axis in one command so the five axes move together. The
mover watches the snapshots the poller already produces and gives up on its own
if an axis stops short of its target.
That shape is the whole reason for the class: ``stop`` is submitted as an ordinary
command, so it runs at the next tick and stops the axis that is moving *now*.
@@ -51,11 +51,8 @@ STALL_GRACE_S = 2.0
#: The order a role move is issued in, for the reason in the module docstring.
MOVE_ORDER: tuple[str, ...] = ("yaw", "pitch", "x", "y", "z")
#: The order "return to the initial state" retracts in: the press axis first, so the
#: tool is clear of the sensor before anything rotates, then the stages, then the
#: table. Returning in one coordinated swoop would move five axes through poses
#: nobody has simulated.
RETURN_ORDER: tuple[str, ...] = ("z", "pitch", "yaw", "x", "y")
#: Axes a pose execute and a return-to-zero command together, including the press axis.
ALIGN_ORDER: tuple[str, ...] = ("yaw", "pitch", "x", "y", "z")
@dataclass(frozen=True)
@@ -82,7 +79,6 @@ class MovePlan:
#: information the operator needs, not an omission.
held: tuple[str, ...] = ()
#: When set, every step is commanded in one call and they travel together.
#: Return-to-zero leaves this off so the press axis clears before anything turns.
together: bool = False
def describe(self) -> str:
@@ -153,11 +149,12 @@ def plan_pose_move(
def plan_return_to_initial(service: MotionService) -> MovePlan:
"""Every axis to zero, press axis first, in :data:`RETURN_ORDER`."""
"""Yaw, pitch, table X/Y and the press axis back to zero together."""
return plan_pose_move(
service,
{role: 0.0 for role in RETURN_ORDER},
order=RETURN_ORDER,
{role: 0.0 for role in ALIGN_ORDER},
order=ALIGN_ORDER,
together=True,
)
@@ -242,7 +239,7 @@ class PoseMover(QObject):
self.stepStarted.emit(step)
def _issue_together(self, plan: MovePlan) -> None:
"""Command every step in one worker call. Z is absent from the plan."""
"""Command every step in one worker call."""
moves: list[tuple[int, float]] = []
for step in plan.steps:
if not step.units == step.units:
@@ -317,7 +314,7 @@ class PoseMover(QObject):
)
def _on_snapshot_together(self, snapshot: Snapshot) -> None:
"""Finish when every commanded axis has arrived. Z is not in the plan."""
"""Finish when every commanded axis has arrived."""
plan = self.state.plan
if plan is None:
return
@@ -750,13 +750,8 @@ class FingerPanel(QGroupBox):
The two numbers that matter are the *table's* move and the stage angles: the
table translation is what the stages are resolved against, and it is what the
preview drew as the cyan path's end.
z is returned as *held*: executing a simulated pose moves the stages and the
table, it does not press. Pressing is P7 and has its own guards — contact
detection, press depth, the sensor's second opinion — none of which exist
here, so a move that reached the contact height would be a press with none of
them.
preview drew as the cyan path's end. Z is the contact height, commanded with
the other four axes in the same move.
"""
pose = self._last_pose
if pose is None:
@@ -767,8 +762,9 @@ class FingerPanel(QGroupBox):
"y": float(table_y),
"yaw": float(pose.yaw_deg),
"pitch": float(pose.pitch_deg),
"z": float(pose.contact_z_mm),
}
return targets, ("z",)
return targets, ()
def _sync_sim_button(self) -> None:
self.sim_button.setText(
+4 -5
View File
@@ -1045,10 +1045,9 @@ class AutoCalMainWindow(QMainWindow):
def _on_execute_pose(self) -> None:
"""Move the platform to the solved pose of the finger screen's selection.
Yaw, pitch and table X/Y start together. The press axis is deliberately
not commanded, so this is workflow P6's dry run for one point — it cannot
reach the sensor. The numbers shown in the confirmation are the ones the
preview drew, so the operator checks the simulation against the machine.
Yaw, pitch, table X/Y and the press axis start together. The Z target is
the solved contact height. The numbers in the confirmation are the ones
the preview drew.
"""
solve = self.finger_panel.solved_pose()
if solve is None:
@@ -1065,7 +1064,7 @@ class AutoCalMainWindow(QMainWindow):
self._start_move(plan, t("move.execute"))
def _on_return_to_initial(self) -> None:
"""Every drive back to zero, press axis first."""
"""Yaw, pitch, table X/Y and the press axis back to zero together."""
plan = plan_return_to_initial(self.poller.service)
self._start_move(plan, t("move.return_initial"))