Files
2026-07-13 13:11:15 +08:00

172 lines
7.0 KiB
Python

from __future__ import annotations
from pathlib import Path
from types import SimpleNamespace
import tempfile
import unittest
from tests.python.support.paths import add_repo_path
add_repo_path("viewer/moveit2_server")
from moveit2_server.moveit_py import (
MoveItPyAdapter,
_joint_state_seed,
_native_start_joint_values,
_robot_description_for_moveit,
)
class MoveItPyAdapterHelperTests(unittest.TestCase):
def test_robot_description_rewrites_relative_meshes_to_file_uris(self) -> None:
with tempfile.TemporaryDirectory() as tempdir:
root = Path(tempdir)
urdf_path = root / "robot.urdf"
urdf_path.write_text(
"""
<robot name="robot">
<link name="base">
<visual>
<geometry>
<mesh filename="STL/base.stl" />
</geometry>
</visual>
</link>
</robot>
""",
encoding="utf-8",
)
description = _robot_description_for_moveit(urdf_path)
self.assertIn((root / "STL/base.stl").resolve().as_uri(), description)
self.assertNotIn('filename="STL/base.stl"', description)
def test_config_dict_builds_moveit2_config_from_srdf_and_request_settings(self) -> None:
with tempfile.TemporaryDirectory() as tempdir:
root = Path(tempdir)
urdf_path = root / "robot.urdf"
urdf_path.write_text('<robot name="robot"><link name="base" /></robot>', encoding="utf-8")
srdf_path = root / "robot.srdf"
srdf_path.write_text('<robot name="robot" />', encoding="utf-8")
request = SimpleNamespace(
context={
"urdfPath": str(urdf_path),
"srdfPath": str(srdf_path),
"srdf": {
"planningGroups": [
{"name": "arm", "jointNames": ["shoulder"]},
{"name": "arm_with_gripper", "subgroups": ["arm", "gripper"]},
]
},
},
command={
"ik": {"timeout": 0.2, "attempts": 3},
"planner": {
"pipeline": "ompl",
"plannerId": "RRTConnectkConfigDefault",
"planningTime": 2.0,
"maxVelocityScalingFactor": 0.5,
"maxAccelerationScalingFactor": 0.25,
},
},
)
config = MoveItPyAdapter()._config_dict(request)
self.assertEqual(config["robot_description_semantic"], '<robot name="robot" />')
self.assertEqual(config["robot_description_kinematics"]["arm"]["kinematics_solver_timeout"], 0.2)
self.assertTrue(config["robot_description_kinematics"]["arm"]["position_only_ik"])
self.assertNotIn("arm_with_gripper", config["robot_description_kinematics"])
self.assertEqual(config["planning_pipelines"]["pipeline_names"], ["ompl"])
self.assertEqual(config["plan_request_params"]["planning_attempts"], 3)
self.assertEqual(config["plan_request_params"]["max_velocity_scaling_factor"], 0.5)
self.assertIsNot(config["plan_request_params"], config["ompl_rrtc"]["plan_request_params"])
self.assertEqual(config["ompl_rrtc"]["plan_request_params"]["planning_attempts"], 3)
self.assertEqual(config["planning_scene_monitor_options"]["wait_for_initial_state_timeout"], 5.0)
def test_joint_state_seed_includes_all_active_urdf_joints(self) -> None:
with tempfile.TemporaryDirectory() as tempdir:
urdf_path = Path(tempdir) / "robot.urdf"
urdf_path.write_text(
"""
<robot name="robot">
<joint name="fixed_base" type="fixed" />
<joint name="shoulder" type="revolute" />
<joint name="slide" type="prismatic" />
</robot>
""",
encoding="utf-8",
)
request = SimpleNamespace(
context={"urdfPath": str(urdf_path)},
payload={"startJointValuesByNameDeg": {"shoulder": 90, "slide": 0.25}},
command={
"planningGroup": "arm",
"jointNames": ["shoulder"],
},
)
names, positions = _joint_state_seed(request)
self.assertEqual(names, ["shoulder", "slide"])
self.assertAlmostEqual(positions[0], 1.5707963267948966)
self.assertEqual(positions[1], 0.25)
def test_legacy_start_joint_values_only_convert_angular_joints(self) -> None:
with tempfile.TemporaryDirectory() as tempdir:
urdf_path = Path(tempdir) / "robot.urdf"
urdf_path.write_text(
"""
<robot name="robot">
<joint name="shoulder" type="revolute" />
<joint name="slide" type="prismatic" />
</robot>
""",
encoding="utf-8",
)
request = SimpleNamespace(
context={"urdfPath": str(urdf_path)},
payload={"startJointValuesByNameDeg": {"shoulder": 90, "slide": 0.25}},
command={"jointNames": ["shoulder", "slide"]},
)
values = _native_start_joint_values(request)
self.assertAlmostEqual(values["shoulder"], 1.5707963267948966)
self.assertEqual(values["slide"], 0.25)
def test_serializes_robot_trajectory_messages_with_fallback_timing(self) -> None:
class Duration:
sec = 0
nanosec = 0
class Point:
def __init__(self, positions: list[float]) -> None:
self.time_from_start = Duration()
self.positions = positions
class JointTrajectory:
joint_names = ["shoulder", "elbow"]
points = [Point([0.0, 0.0]), Point([1.5707963267948966, 0.0])]
class RobotTrajectoryMsg:
joint_trajectory = JointTrajectory()
class RobotTrajectory:
def get_robot_trajectory_msg(self) -> RobotTrajectoryMsg:
return RobotTrajectoryMsg()
request = SimpleNamespace(context={}, payload={}, command={"jointNames": ["shoulder", "elbow"]})
serialized = MoveItPyAdapter()._serialize_trajectory(RobotTrajectory(), ["shoulder", "elbow"], request)
self.assertEqual(serialized["jointNames"], ["shoulder", "elbow"])
self.assertEqual(serialized["points"][0]["timeFromStartSec"], 0.0)
self.assertGreater(serialized["points"][1]["timeFromStartSec"], 0.0)
self.assertAlmostEqual(serialized["points"][1]["positions"][0], 1.5707963267948966)
self.assertAlmostEqual(serialized["points"][1]["positionsDeg"][0], 90.0)
if __name__ == "__main__":
unittest.main()