feat: 添加手柄主键回初始位姿
This commit is contained in:
@@ -30,6 +30,76 @@ class FakeTime:
|
|||||||
return SimpleNamespace(nanoseconds=0)
|
return SimpleNamespace(nanoseconds=0)
|
||||||
|
|
||||||
|
|
||||||
|
def _primary_button_teleop(*, move_error=None):
|
||||||
|
events = []
|
||||||
|
errors = []
|
||||||
|
snapshot = JointStateSnapshot([0.2] * 7, time.monotonic())
|
||||||
|
|
||||||
|
class Adapter:
|
||||||
|
def move_to_initial_pose(self):
|
||||||
|
events.append("move")
|
||||||
|
if move_error is not None:
|
||||||
|
raise move_error
|
||||||
|
|
||||||
|
def read_joint_state(self):
|
||||||
|
events.append("read")
|
||||||
|
return snapshot
|
||||||
|
|
||||||
|
teleop = object.__new__(SingleArmVelocityTeleop)
|
||||||
|
teleop._arm_name = "right_rm75"
|
||||||
|
teleop._adapter = Adapter()
|
||||||
|
teleop._last_primary_pressed = None
|
||||||
|
teleop._grip_rearm_required = False
|
||||||
|
teleop._safe_stop = lambda reset_active: events.append(
|
||||||
|
("stop", reset_active)
|
||||||
|
)
|
||||||
|
teleop._reset_joint_state = lambda value: events.append(("sync", value))
|
||||||
|
teleop._handle_trigger_gripper = lambda msg: None
|
||||||
|
teleop.get_clock = lambda: SimpleNamespace(now=lambda: FakeTime())
|
||||||
|
teleop.get_logger = lambda: SimpleNamespace(
|
||||||
|
info=lambda message: None,
|
||||||
|
error=lambda message: errors.append(message),
|
||||||
|
)
|
||||||
|
return teleop, events, errors, snapshot
|
||||||
|
|
||||||
|
|
||||||
|
def test_primary_button_rising_edge_moves_once_and_resyncs() -> None:
|
||||||
|
teleop, events, _, snapshot = _primary_button_teleop()
|
||||||
|
released = SimpleNamespace(primary=False)
|
||||||
|
pressed = SimpleNamespace(primary=True)
|
||||||
|
|
||||||
|
teleop._on_controller(released)
|
||||||
|
teleop._on_controller(pressed)
|
||||||
|
teleop._on_controller(pressed)
|
||||||
|
teleop._on_controller(released)
|
||||||
|
teleop._on_controller(pressed)
|
||||||
|
|
||||||
|
expected_once = [
|
||||||
|
("stop", True),
|
||||||
|
"move",
|
||||||
|
"read",
|
||||||
|
("sync", snapshot),
|
||||||
|
]
|
||||||
|
assert events == expected_once * 2
|
||||||
|
assert teleop._grip_rearm_required
|
||||||
|
|
||||||
|
|
||||||
|
def test_primary_button_move_failure_logs_and_stays_stopped() -> None:
|
||||||
|
failure = RuntimeError("rm_movej failed")
|
||||||
|
teleop, events, errors, _ = _primary_button_teleop(
|
||||||
|
move_error=failure
|
||||||
|
)
|
||||||
|
|
||||||
|
teleop._on_controller(SimpleNamespace(primary=False))
|
||||||
|
teleop._on_controller(SimpleNamespace(primary=True))
|
||||||
|
|
||||||
|
assert events == [("stop", True), "move"]
|
||||||
|
assert teleop._grip_rearm_required
|
||||||
|
assert errors == [
|
||||||
|
"right_rm75 回初始位姿失败:rm_movej failed"
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
def test_startup_joint_query_initializes_qp_and_command_history() -> None:
|
def test_startup_joint_query_initializes_qp_and_command_history() -> None:
|
||||||
positions = [0.1] * 7
|
positions = [0.1] * 7
|
||||||
pose = np.eye(4)
|
pose = np.eye(4)
|
||||||
|
|||||||
@@ -295,6 +295,7 @@ class SingleArmVelocityTeleop(Node):
|
|||||||
self._control_fault_latched = False
|
self._control_fault_latched = False
|
||||||
self._stop_sent = True
|
self._stop_sent = True
|
||||||
self._trigger_tool_open = True
|
self._trigger_tool_open = True
|
||||||
|
self._last_primary_pressed: bool | None = None
|
||||||
self._last_trigger_pressed: bool | None = None
|
self._last_trigger_pressed: bool | None = None
|
||||||
self._tool_command_queue: queue.Queue[tuple[bool, str] | None] | None = None
|
self._tool_command_queue: queue.Queue[tuple[bool, str] | None] | None = None
|
||||||
self._tool_worker_stop = threading.Event()
|
self._tool_worker_stop = threading.Event()
|
||||||
@@ -514,8 +515,32 @@ class SingleArmVelocityTeleop(Node):
|
|||||||
def _on_controller(self, msg: XrController) -> None:
|
def _on_controller(self, msg: XrController) -> None:
|
||||||
self._last_msg = msg
|
self._last_msg = msg
|
||||||
self._last_msg_time = self.get_clock().now()
|
self._last_msg_time = self.get_clock().now()
|
||||||
|
self._handle_initial_pose_button(msg)
|
||||||
self._handle_trigger_gripper(msg)
|
self._handle_trigger_gripper(msg)
|
||||||
|
|
||||||
|
def _handle_initial_pose_button(self, msg: XrController) -> None:
|
||||||
|
if self._last_primary_pressed is None:
|
||||||
|
self._last_primary_pressed = msg.primary
|
||||||
|
return
|
||||||
|
|
||||||
|
rising_edge = msg.primary and not self._last_primary_pressed
|
||||||
|
self._last_primary_pressed = msg.primary
|
||||||
|
if not rising_edge:
|
||||||
|
return
|
||||||
|
|
||||||
|
self._grip_rearm_required = True
|
||||||
|
self._safe_stop(reset_active=True)
|
||||||
|
try:
|
||||||
|
self._adapter.move_to_initial_pose()
|
||||||
|
self._reset_joint_state(self._adapter.read_joint_state())
|
||||||
|
except Exception as exc:
|
||||||
|
self.get_logger().error(
|
||||||
|
f"{self._arm_name} 回初始位姿失败:{exc}"
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
self.get_logger().info(f"{self._arm_name} 已回到初始位姿。")
|
||||||
|
|
||||||
def _handle_trigger_gripper(self, msg: XrController) -> None:
|
def _handle_trigger_gripper(self, msg: XrController) -> None:
|
||||||
if not self._enable_tool_control or not self._enable_trigger_gripper_control:
|
if not self._enable_tool_control or not self._enable_trigger_gripper_control:
|
||||||
return
|
return
|
||||||
|
|||||||
Reference in New Issue
Block a user