Visualization Examples

These examples show how to use SRMP’s two visualization backends. See the Visualization page for installation instructions and a feature comparison.

MeshCat Visualization

Basic scene setup and trajectory animation using VisualPlannerInterface:

from srmp import VisualPlannerInterface
import srmp
import numpy as np

# Create planner with MeshCat visualization
planner = VisualPlannerInterface()

# Add robot
planner.add_articulation(
    name="panda",
    end_effector="panda_hand",
    urdf_path="/path/to/panda.urdf",
    srdf_path="/path/to/panda.srdf",   # optional
)

# Add obstacles
box_pose = srmp.Pose()
box_pose.p = np.array([0.5, 0.2, 0.4])
planner.add_box("obstacle", np.array([0.1, 0.1, 0.4]), box_pose)

sphere_pose = srmp.Pose()
sphere_pose.p = np.array([0.3, -0.3, 0.6])
planner.add_sphere("sphere_obs", 0.08, sphere_pose)

# Open the viewer in your browser
planner.visualize()
print(f"Open: {planner.url}")

# Plan a trajectory
planner.make_planner(["panda"], {
    "planner_id": "wAstar",
    "heuristic": "bfs",
    "weight": "10."
})

start = np.radians([0, -45, 0, -135, 0, 90, 45])
goal = srmp.GoalConstraint(
    srmp.GoalType.JOINTS,
    [np.radians([45, -30, 0, -120, 0, 90, 0])]
)
trajectory = planner.plan(start, goal)

# Animate the trajectory in the viewer
planner.animate_trajectory(trajectory, dt=0.05)

MeshCat GUI Controls

Because MeshCat cannot relay browser input back to Python, obstacle editing is driven from Python using the set_gui_* helpers:

from srmp import VisualPlannerInterface
import srmp
import numpy as np

planner = VisualPlannerInterface()
planner.add_articulation("panda", "panda_hand", "/path/to/panda.urdf")
planner.visualize()

# Enable the GUI panel
planner.add_gui_controls()

# Add a box using Python-driven GUI state
planner.set_gui_object_type("box")
planner.set_gui_position(0.5, 0.0, 0.5)
planner.set_gui_size(0.1, 0.1, 0.2)
planner.set_gui_object_name("table_leg")
planner.add_obstacle_from_gui()

# Move an existing object
planner.load_object_to_gui("table_leg")
planner.set_gui_position(0.6, 0.0, 0.5)
planner.update_object_from_gui("table_leg")

# Add a sphere
planner.set_gui_object_type("sphere")
planner.set_gui_position(0.3, 0.3, 0.6)
planner.set_gui_size(0.08, 0.08, 0.08)  # width used as radius
planner.set_gui_object_name("ball_1")
planner.add_obstacle_from_gui()

Viser Interactive Visualization

Full interactive visualization with joint sliders and end-effector drag using ViserPlannerInterface:

from srmp import ViserPlannerInterface
import srmp
import numpy as np
import time

# Create planner with Viser visualization
planner = ViserPlannerInterface(port=8080)

# Add robot
planner.add_articulation(
    name="panda",
    end_effector="panda_hand",
    urdf_path="/path/to/panda.urdf",
)

# Add obstacles (appear immediately in the browser)
box_pose = srmp.Pose()
box_pose.p = np.array([0.5, 0.2, 0.4])
planner.add_box("obstacle", np.array([0.1, 0.1, 0.4]), box_pose)

# Start the Viser server
planner.visualize()
print(f"Open: {planner.url}")   # → http://localhost:8080

# Add per-joint sliders (drag them in the browser to move the robot)
planner.add_robot_controls("panda")

# Add an end-effector drag gizmo (drag to move the robot via IK)
planner.add_ee_drag_control("panda")

# Keep the server alive for interactive use
time.sleep(120)

planner.stop()

Viser GUI Object Controls

from srmp import ViserPlannerInterface
import srmp
import numpy as np
import time

planner = ViserPlannerInterface(port=8080)
planner.add_articulation("panda", "panda_hand", "/path/to/panda.urdf")
planner.visualize()

# Add the interactive "Object Controls" panel to the browser sidebar.
# Clicking "Add Object", "Update Object", or "Remove Object" in the
# browser directly calls the corresponding Python method.
planner.add_gui_controls()

# You can still load an existing object into the GUI from Python
box_pose = srmp.Pose()
box_pose.p = np.array([0.4, 0.0, 0.5])
planner.add_box("my_box", np.array([0.1, 0.1, 0.1]), box_pose)
planner.load_object_to_gui("my_box")

print(f"Open {planner.url} and use the Object Controls panel")
time.sleep(120)

Viser Multi-Robot Animation

from srmp import ViserPlannerInterface
import srmp
import numpy as np

planner = ViserPlannerInterface(port=8080)

# Add two robots
for i in range(2):
    planner.add_articulation(
        name=f"panda{i}",
        end_effector=f"panda{i}_hand",
        urdf_path=f"/path/to/panda{i}.urdf",
    )

# Set base poses
pose0 = srmp.Pose()
pose0.p = np.array([-0.5, 0.5, 0.0])
pose0.q = np.array([1, 0, 0, 0])
planner.set_base_pose("panda0", pose0)

pose1 = srmp.Pose()
pose1.p = np.array([0.5, 0.5, 0.0])
pose1.q = np.array([0, 0, 0, 1])
planner.set_base_pose("panda1", pose1)

planner.visualize()

# Configure and run multi-robot planner
names = ["panda0", "panda1"]
planner_context = {
    "planner_id": "xECBS",
    "weight_low_level_heuristic": "55.0",
    "high_level_focal_suboptimality": "1.8",
    "low_level_focal_suboptimality": "1.0",
}
for name in names:
    planner_context[f"heuristic_{name}"] = "joint_euclidean_remove_time"
    planner_context[f"mprim_path_{name}"] = "/path/to/manip_7dof_timed_mprim.yaml"

planner.make_planner(names, planner_context)

start_states = {
    "panda0": np.radians([-40, 0, 0, -85, 0, 57, 0]),
    "panda1": np.radians([-40, 0, 0, -85, 0, 57, 0]),
}
goal_states = {
    "panda0": np.radians([40, 0, 0, -70, 0, 50, 0]),
    "panda1": np.radians([40, 0, 0, -95, 0, 67, 0]),
}
goal_constraints = {
    name: srmp.GoalConstraint(srmp.GoalType.JOINTS, [goal_states[name]])
    for name in names
}

trajectories = planner.plan_multi(start_states, goal_constraints)

# Animate all robots simultaneously in the browser
planner.animate_trajectory(trajectories, dt=0.05)

planner.stop()