fix: 更新 README 和配置文件,调整模式和工具设置

This commit is contained in:
2026-08-04 18:11:19 +08:00
parent b24165640d
commit 5a6ec47e6c
4 changed files with 199 additions and 139 deletions
+2 -2
View File
@@ -9,7 +9,7 @@ set_initial_tool_state: false
tools_in_ee:
scissor:
# x, y, z, qx, qy, qz, qw
pose: [0.0, 0.0, 0.19, 0.0, 0.0, 0.0, 1.0]
pose: [0.0, 0.0, 0.165, 0.0, 0.0, 0.0, 1.0]
# mass, center_x, center_y, center_z, reserved...
load: [0.66, 0.0, 0.0, 0.06, 0.0, 0.0, 0.0]
omnipic:
@@ -24,6 +24,6 @@ tools_in_ee:
arms:
left:
scissorgripper: 2
scissorgripper: 0
right:
scissorgripper: 1
@@ -13,6 +13,100 @@ assert SPEC.loader is not None
SPEC.loader.exec_module(launcher_ui)
class LauncherCommandsTest(unittest.TestCase):
def test_modes_expose_the_confirmed_command_matrix(self) -> None:
expected_titles = {
"Simulation": [
"Dual Arm Mock Launch",
"XRobotoolkit UDP Bridge (90 Hz)",
"Sample UDP Sender (Both Staggered, 60s)",
"Open Controller Hz Monitor",
"Open ROS Topic/Node List Monitor",
"Open Controller Topic Monitor",
],
"MuJoCo": [
"Dual Arm MuJoCo Mock Launch",
"Dual Arm MuJoCo Real Hardware Launch",
"XRobotoolkit UDP Bridge (90 Hz)",
"Open Controller Hz Monitor",
"Open ROS Topic/Node List Monitor",
"Open Controller Topic Monitor",
],
"Real Hardware": [
"Ping Left RM75",
"Ping Right RM75",
"Left Arm RealMan Launch",
"Right Arm RealMan Launch",
"Dual Arm RealMan Launch",
"XRobotoolkit UDP Bridge (90 Hz)",
"Left Gripper Open",
"Left Gripper Close",
"Right Gripper Open",
"Right Gripper Close",
"Open ROS Topic/Node List Monitor",
"Open Controller Topic Monitor",
],
"Diagnostics": [
"ROS Doctor Report",
"XR-RM Bringup Prefix",
"XR-RM Input Prefix",
"XR-RM Teleop Prefix",
"XR-RM MuJoCo Prefix",
"Open Controller Position Monitor",
"Open Controller Hz Monitor",
"Open ROS Topic/Node List Monitor",
"Open Controller Topic Monitor",
],
}
self.assertEqual(launcher_ui.MODES, list(expected_titles))
for mode, titles in expected_titles.items():
actual = [
indexed_title.split(". ", 1)[1]
for indexed_title, _command in launcher_ui.build_commands_by_mode(mode)
]
self.assertEqual(actual, titles)
def test_mujoco_launches_distinguish_mock_and_real_hardware(self) -> None:
commands = dict(launcher_ui.build_commands_by_mode("MuJoCo"))
self.assertIn(
"arm:=both use_mock:=true use_mujoco:=true",
commands["1. Dual Arm MuJoCo Mock Launch"],
)
self.assertIn(
"arm:=both use_mock:=false use_mujoco:=true",
commands["2. Dual Arm MuJoCo Real Hardware Launch"],
)
def test_cmd_vel_monitor_is_completely_removed(self) -> None:
self.assertFalse(hasattr(launcher_ui, "CMD_VEL_MONITOR_ACTION"))
for mode in launcher_ui.MODES:
self.assertNotIn("cmd_vel", repr(launcher_ui.build_commands_by_mode(mode)))
def test_environment_check_includes_mujoco_package(self) -> None:
app = object.__new__(launcher_ui.LauncherApp)
app.workspace_root = launcher_ui._find_workspace_root()
app.terminal_command = lambda _title, _script: ["terminal"]
app.x_terminal_target = lambda: "terminator"
checked_packages = []
app._ros_package_available = lambda package: checked_packages.append(package) or True
app.show_text_dialog = lambda *_args: None
app.status = mock.Mock()
with mock.patch.object(
launcher_ui.shutil,
"which",
return_value="/usr/bin/x-terminal-emulator",
):
app.check_prerequisites()
self.assertEqual(
checked_packages,
["xr_rm_bringup", "xr_rm_input", "xr_rm_teleop", "xr_rm_mujoco"],
)
class LauncherCleanupTest(unittest.TestCase):
def test_xrobotoolkit_cleanup_policy_only_stops_service_on_window_close(self) -> None:
stop_all_patterns = set(
@@ -89,6 +183,34 @@ class LauncherCleanupTest(unittest.TestCase):
],
)
def test_stop_all_and_window_close_stop_mujoco(self) -> None:
for stop_pc_service in (False, True):
app = object.__new__(launcher_ui.LauncherApp)
app.status = mock.Mock()
app.close_related_terminal_windows = lambda: 0
def fake_check_output(command, **_kwargs):
if command == ["pgrep", "-f", "dual_arm_simulator"]:
return "404\n"
raise subprocess.CalledProcessError(1, command)
with (
mock.patch.object(
launcher_ui.subprocess,
"check_output",
side_effect=fake_check_output,
),
mock.patch.object(launcher_ui.os, "kill") as kill,
mock.patch.object(launcher_ui.time, "sleep"),
):
app.stop_launched_processes(
confirm=False,
notify=False,
stop_pc_service=stop_pc_service,
)
kill.assert_called_once_with(404, signal.SIGTERM)
if __name__ == "__main__":
unittest.main()
+60 -124
View File
@@ -1,8 +1,8 @@
#!/usr/bin/env python3
"""XR-RM 桌面调试启动器。
提供 Tkinter 图形界面,按“仿真/左臂/右臂/双臂/诊断”组织常用 ROS2
launch、sample_udp_sender、topic 监控和环境检查命令,降低现场调试时的命令输入成本。
提供 Tkinter 图形界面,按“仿真/MuJoCo/真机/诊断”组织常用 ROS2 launch、
sample_udp_sender、topic 监控和环境检查命令,降低现场调试时的命令输入成本。
"""
from __future__ import annotations
@@ -44,8 +44,6 @@ SAMPLE_SENDER_ARGS = (
TERMINAL_TITLE_PREFIX = "XR-RM Terminal - "
TOPIC_MONITOR_TITLE = "XR-RM Topic Monitor"
TOPIC_MONITOR_ACTION = "__xr_rm_topic_monitor__"
CMD_VEL_MONITOR_TITLE = "XR-RM Target Velocity Monitor"
CMD_VEL_MONITOR_ACTION = "__xr_rm_cmd_vel_monitor__"
ROS_GRAPH_MONITOR_TITLE = "XR-RM ROS Graph Monitor"
ROS_GRAPH_MONITOR_ACTION = "__xr_rm_ros_graph_monitor__"
@@ -54,11 +52,6 @@ TOPIC_MONITORS = [
("Right Controller", "/xr/right_controller"),
]
CMD_VEL_MONITORS = [
("Left Target Vel", "/xr_rm/left_rm75/cmd_vel"),
("Right Target Vel", "/xr_rm/right_rm75/cmd_vel"),
]
CONTROLLER_POSITION_MONITOR_TITLE = "XR-RM Controller Position Monitor"
CONTROLLER_POSITION_MONITOR_ACTION = "__xr_rm_controller_position_monitor__"
CONTROLLER_HZ_MONITOR_TITLE = "XR-RM Controller Hz Monitor"
@@ -81,9 +74,8 @@ ROS_GRAPH_MONITORS = [
MODES = [
"Simulation",
"Left Arm",
"Right Arm",
"Dual Arm",
"MuJoCo",
"Real Hardware",
"Diagnostics",
]
@@ -177,21 +169,6 @@ def _source_lines(workspace_root: Path) -> list[str]:
return lines
def _one_click_mock(arm: str, hand: str) -> str:
sender_seconds = SAMPLE_SENDER_STAGGERED_SECONDS if hand == "both" else SAMPLE_SENDER_SECONDS
both_mode = "staggered" if hand == "both" else "synchronized"
return "\n".join([
f"ros2 launch xr_rm_bringup arm_debug.launch.py arm:={arm} use_mock:=true &",
"launch_pid=$!",
"sleep 2",
_sample_udp_sender_command(hand, sender_seconds, both_mode),
"echo",
"echo 'Sample sender finished. The launch process is still running in this terminal.'",
"echo 'Press Ctrl-C here, or use the cleanup button in the launcher, to stop it.'",
"wait \"$launch_pid\"",
])
def _diagnostic_commands() -> list[tuple[str, str]]:
return [
("Open ROS Topic/Node List Monitor", ROS_GRAPH_MONITOR_ACTION),
@@ -202,14 +179,9 @@ def _topic_monitor_item() -> tuple[str, str]:
return ("Open Controller Topic Monitor", TOPIC_MONITOR_ACTION)
def _cmd_vel_monitor_item() -> tuple[str, str]:
return ("Open Target Velocity Monitor", CMD_VEL_MONITOR_ACTION)
def _is_topic_monitor_action(action: str) -> bool:
return action in (
TOPIC_MONITOR_ACTION,
CMD_VEL_MONITOR_ACTION,
CONTROLLER_POSITION_MONITOR_ACTION,
CONTROLLER_HZ_MONITOR_ACTION,
)
@@ -232,14 +204,6 @@ def _topic_monitor_spec(action: str) -> tuple[str, list[tuple[str, str]], str, s
"xr_rm_controller_hz_monitor_",
"controller hz topic",
)
if action == CMD_VEL_MONITOR_ACTION:
return (
CMD_VEL_MONITOR_TITLE,
[(title, f"ros2 topic echo {topic}") for title, topic in CMD_VEL_MONITORS],
"xr_rm_cmd_vel_monitor",
"xr_rm_cmd_vel_monitor_",
"target velocity topic",
)
return (
TOPIC_MONITOR_TITLE,
[(title, f"ros2 topic echo {topic}") for title, topic in TOPIC_MONITORS],
@@ -249,16 +213,10 @@ def _topic_monitor_spec(action: str) -> tuple[str, list[tuple[str, str]], str, s
)
def _finalize_items(
items: list[tuple[str, str]],
one_click: tuple[str, str] | None = None,
) -> list[tuple[str, str]]:
def _finalize_items(items: list[tuple[str, str]]) -> list[tuple[str, str]]:
final_items = items + _diagnostic_commands() + [
_topic_monitor_item(),
_cmd_vel_monitor_item(),
]
if one_click is not None:
final_items.append(one_click)
return _with_index(final_items)
@@ -266,25 +224,64 @@ def build_commands_by_mode(mode: str) -> list[tuple[str, str]]:
# UI 列表只维护命令模板;真正执行时统一套上工作空间 source 和终端包装。
if mode == "Simulation":
items = [
("Left Arm Mock Launch", "ros2 launch xr_rm_bringup arm_debug.launch.py arm:=left use_mock:=true"),
("Right Arm Mock Launch", "ros2 launch xr_rm_bringup arm_debug.launch.py arm:=right use_mock:=true"),
("Dual Arm Mock Launch", "ros2 launch xr_rm_bringup arm_debug.launch.py arm:=both use_mock:=true"),
("XRobotoolkit UDP Bridge (90 Hz)", _xrobotoolkit_bridge_command()),
(
"Sample UDP Sender (Left, 30s)",
_sample_udp_sender_command("left"),
),
(
"Sample UDP Sender (Right, 30s)",
_sample_udp_sender_command("right"),
),
(
"Sample UDP Sender (Both Staggered, 60s)",
_sample_udp_sender_command("both", SAMPLE_SENDER_STAGGERED_SECONDS, "staggered"),
),
("One-Click Left Mock Demo", _one_click_mock("left", "left")),
("One-Click Right Mock Demo", _one_click_mock("right", "right")),
("One-Click Dual Mock Demo", _one_click_mock("both", "both")),
(
"Open Controller Hz Monitor",
CONTROLLER_HZ_MONITOR_ACTION,
),
]
elif mode == "MuJoCo":
items = [
(
"Dual Arm MuJoCo Mock Launch",
"ros2 launch xr_rm_bringup arm_debug.launch.py "
"arm:=both use_mock:=true use_mujoco:=true",
),
(
"Dual Arm MuJoCo Real Hardware Launch",
"ros2 launch xr_rm_bringup arm_debug.launch.py "
"arm:=both use_mock:=false use_mujoco:=true",
),
("XRobotoolkit UDP Bridge (90 Hz)", _xrobotoolkit_bridge_command()),
(
"Open Controller Hz Monitor",
CONTROLLER_HZ_MONITOR_ACTION,
),
]
elif mode == "Real Hardware":
items = [
("Ping Left RM75", f"ping -c 4 {DEFAULT_LEFT_IP}"),
("Ping Right RM75", f"ping -c 4 {DEFAULT_RIGHT_IP}"),
(
"Left Arm RealMan Launch",
"ros2 launch xr_rm_bringup arm_debug.launch.py arm:=left use_mock:=false",
),
(
"Right Arm RealMan Launch",
"ros2 launch xr_rm_bringup arm_debug.launch.py arm:=right use_mock:=false",
),
(
"Dual Arm RealMan Launch",
"ros2 launch xr_rm_bringup arm_debug.launch.py arm:=both use_mock:=false",
),
("XRobotoolkit UDP Bridge (90 Hz)", _xrobotoolkit_bridge_command()),
("Left Gripper Open", _tool_command("left", True)),
("Left Gripper Close", _tool_command("left", False)),
("Right Gripper Open", _tool_command("right", True)),
("Right Gripper Close", _tool_command("right", False)),
]
else:
items = [
("ROS Doctor Report", "ros2 doctor --report"),
("XR-RM Bringup Prefix", "ros2 pkg prefix xr_rm_bringup"),
("XR-RM Input Prefix", "ros2 pkg prefix xr_rm_input"),
("XR-RM Teleop Prefix", "ros2 pkg prefix xr_rm_teleop"),
("XR-RM MuJoCo Prefix", "ros2 pkg prefix xr_rm_mujoco"),
(
"Open Controller Position Monitor",
CONTROLLER_POSITION_MONITOR_ACTION,
@@ -294,64 +291,7 @@ def build_commands_by_mode(mode: str) -> list[tuple[str, str]]:
CONTROLLER_HZ_MONITOR_ACTION,
),
]
one_click = None
elif mode == "Left Arm":
items = [
("Ping Left RM75", f"ping -c 4 {DEFAULT_LEFT_IP}"),
(
"Left Arm RealMan Launch",
"ros2 launch xr_rm_bringup arm_debug.launch.py arm:=left use_mock:=false",
),
("XRobotoolkit UDP Bridge (90 Hz)", _xrobotoolkit_bridge_command()),
("Left Tool Open", _tool_command("left", True)),
("Left Tool Close", _tool_command("left", False)),
(
"Sample UDP Sender (Left, 30s)",
_sample_udp_sender_command("left"),
),
]
one_click = None
elif mode == "Right Arm":
items = [
("Ping Right RM75", f"ping -c 4 {DEFAULT_RIGHT_IP}"),
(
"Right Arm RealMan Launch",
"ros2 launch xr_rm_bringup arm_debug.launch.py arm:=right use_mock:=false",
),
("XRobotoolkit UDP Bridge (90 Hz)", _xrobotoolkit_bridge_command()),
("Right Tool Open", _tool_command("right", True)),
("Right Tool Close", _tool_command("right", False)),
(
"Sample UDP Sender (Right, 30s)",
_sample_udp_sender_command("right"),
),
]
one_click = None
elif mode == "Dual Arm":
items = [
("Ping Left RM75", f"ping -c 4 {DEFAULT_LEFT_IP}"),
("Ping Right RM75", f"ping -c 4 {DEFAULT_RIGHT_IP}"),
(
"Dual Arm RealMan Launch",
"ros2 launch xr_rm_bringup arm_debug.launch.py arm:=both use_mock:=false",
),
("XRobotoolkit UDP Bridge (90 Hz)", _xrobotoolkit_bridge_command()),
(
"Sample UDP Sender (Both Staggered, 60s)",
_sample_udp_sender_command("both", SAMPLE_SENDER_STAGGERED_SECONDS, "staggered"),
),
]
one_click = None
else:
items = [
("ROS Doctor Report", "ros2 doctor --report"),
("XR-RM Bringup Prefix", "ros2 pkg prefix xr_rm_bringup"),
("XR-RM Input Prefix", "ros2 pkg prefix xr_rm_input"),
("XR-RM Teleop Prefix", "ros2 pkg prefix xr_rm_teleop"),
("XRobotoolkit UDP Bridge (90 Hz)", _xrobotoolkit_bridge_command()),
]
one_click = None
return _finalize_items(items, one_click)
return _finalize_items(items)
class LauncherApp:
@@ -608,7 +548,7 @@ class LauncherApp:
else:
warnings.append("[WARN] Could not identify x-terminal-emulator target.")
for package in ("xr_rm_bringup", "xr_rm_input", "xr_rm_teleop"):
for package in ("xr_rm_bringup", "xr_rm_input", "xr_rm_teleop", "xr_rm_mujoco"):
if self._ros_package_available(package):
ok.append(f"[OK] ROS package available: {package}")
else:
@@ -1254,7 +1194,6 @@ class LauncherApp:
title_patterns = (
TERMINAL_TITLE_PREFIX,
TOPIC_MONITOR_TITLE,
CMD_VEL_MONITOR_TITLE,
CONTROLLER_POSITION_MONITOR_TITLE,
CONTROLLER_HZ_MONITOR_TITLE,
ROS_GRAPH_MONITOR_TITLE,
@@ -1326,24 +1265,21 @@ class LauncherApp:
*_xrobotoolkit_cleanup_patterns(stop_pc_service=stop_pc_service),
TERMINAL_TITLE_PREFIX,
TOPIC_MONITOR_TITLE,
CMD_VEL_MONITOR_TITLE,
ROS_GRAPH_MONITOR_TITLE,
"xr_rm_topic_monitor_",
"xr_rm_cmd_vel_monitor_",
"xr_rm_controller_position_monitor_",
"xr_rm_controller_hz_monitor_",
"xr_rm_ros_graph_monitor_",
"udp_controller_receiver",
"sample_udp_sender",
"single_arm_velocity_teleop",
"dual_arm_simulator",
"ros2 topic echo /xr/left_controller --field pose.position",
"ros2 topic echo /xr/right_controller --field pose.position",
"ros2 topic echo /xr/left_controller",
"ros2 topic echo /xr/right_controller",
"ros2 topic hz /xr/left_controller",
"ros2 topic hz /xr/right_controller",
"ros2 topic echo /xr_rm/left_rm75/cmd_vel",
"ros2 topic echo /xr_rm/right_rm75/cmd_vel",
"ros2 topic list",
"ros2 node list",
]