feat: Implement UDP feedback for RM75 robot arms

This commit is contained in:
2026-07-29 15:26:59 +08:00
parent 687a0b401a
commit 08996434e5
16 changed files with 1600 additions and 329 deletions
+48 -11
View File
@@ -121,6 +121,17 @@ def _xrobotoolkit_bridge_command() -> str:
)
def _xrobotoolkit_cleanup_patterns(*, stop_pc_service: bool) -> tuple[str, ...]:
patterns = (
XROBOTOOLKIT_BRIDGE_EXECUTABLE,
"RobotLinuxDemo.x86_64",
"PXREAClientUnity",
)
if stop_pc_service:
return (*patterns, "RoboticsServiceProcess")
return patterns
def _tool_command(arm: str, open_tool: bool) -> str:
arm_name = "left_rm75" if arm == "left" else "right_rm75"
value = "true" if open_tool else "false"
@@ -289,8 +300,7 @@ def build_commands_by_mode(mode: str) -> list[tuple[str, str]]:
("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 "
f"left_robot_ip:={DEFAULT_LEFT_IP}",
"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)),
@@ -306,8 +316,7 @@ def build_commands_by_mode(mode: str) -> list[tuple[str, str]]:
("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 "
f"right_robot_ip:={DEFAULT_RIGHT_IP}",
"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)),
@@ -324,9 +333,7 @@ def build_commands_by_mode(mode: str) -> list[tuple[str, str]]:
("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 "
f"left_robot_ip:={DEFAULT_LEFT_IP} right_robot_ip:={DEFAULT_RIGHT_IP} "
"move_to_initial_pose_on_connect:=false",
"ros2 launch xr_rm_bringup arm_debug.launch.py arm:=both use_mock:=false",
),
("XRobotoolkit UDP Bridge (90 Hz)", _xrobotoolkit_bridge_command()),
(
@@ -1283,13 +1290,27 @@ class LauncherApp:
return closed
def on_close_requested(self) -> None:
if self.stop_launched_processes(confirm=True, notify=False):
if self.stop_launched_processes(
confirm=True,
notify=False,
stop_pc_service=True,
):
self.root.destroy()
def kill_launched_processes(self) -> None:
self.stop_launched_processes(confirm=True, notify=True)
self.stop_launched_processes(
confirm=True,
notify=True,
stop_pc_service=False,
)
def stop_launched_processes(self, *, confirm: bool, notify: bool) -> bool:
def stop_launched_processes(
self,
*,
confirm: bool,
notify: bool,
stop_pc_service: bool,
) -> bool:
if confirm and not messagebox.askyesno(
"Confirm Stop",
"Stop XR-RM launcher terminals, topic monitors, ROS nodes, and bridge processes started from this workspace?",
@@ -1302,7 +1323,7 @@ class LauncherApp:
"ros2 run xr_rm_input",
"ros2 run xr_rm_teleop",
"XR_RM_LAUNCHER_SESSION=1",
XROBOTOOLKIT_BRIDGE_EXECUTABLE,
*_xrobotoolkit_cleanup_patterns(stop_pc_service=stop_pc_service),
TERMINAL_TITLE_PREFIX,
TOPIC_MONITOR_TITLE,
CMD_VEL_MONITOR_TITLE,
@@ -1326,6 +1347,20 @@ class LauncherApp:
"ros2 topic list",
"ros2 node list",
]
pc_service_keep_pid: int | None = None
if not stop_pc_service:
try:
output = subprocess.check_output(
["pgrep", "-o", "-f", "RoboticsServiceProcess"],
text=True,
)
pc_service_keep_pid = int(output.strip())
patterns.append("RoboticsServiceProcess")
except (subprocess.CalledProcessError, ValueError):
pass
except Exception as exc:
print(f"Failed to find the oldest RoboticsServiceProcess: {exc}")
protected = {os.getpid(), os.getppid()}
killed: set[int] = set()
@@ -1345,6 +1380,8 @@ class LauncherApp:
continue
if pid in protected or pid in killed:
continue
if pattern == "RoboticsServiceProcess" and pid == pc_service_keep_pid:
continue
try:
os.kill(pid, signal.SIGTERM)
killed.add(pid)