Visualization

SRMP provides two visualization backends that wrap PlannerInterface with automatic scene tracking and 3D rendering:

Both classes inherit from PlannerInterface, so every planning method (add_articulation, add_box, plan, read_sim, …) is available and the scene is automatically kept in sync with the 3D view.

Installation

MeshCat visualizer:

$ pip install meshcat

Viser visualizer:

$ pip install viser trimesh

Note

The visualization dependencies are optional. When they are not installed, importing VisualPlannerInterface or ViserPlannerInterface is silently skipped and srmp.PlannerInterface remains fully functional.

VisualPlannerInterface (MeshCat)

VisualPlannerInterface connects to a MeshCat WebGL viewer running in the browser. It is a good choice for quick scene inspection and trajectory animation when interactive widget editing is not required.

Limitations

  • MeshCat does not support reading slider values from the browser back into Python. Use the set_gui_* helper methods to drive GUI state programmatically.

Basic Usage

from srmp import VisualPlannerInterface
import numpy as np

# Create planner with MeshCat visualization
planner = VisualPlannerInterface()

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

box_pose = planner.Pose() if hasattr(planner, 'Pose') else __import__('srmp').Pose()

import srmp
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)

# Open the 3D viewer (prints the browser URL)
planner.visualize()

# Animate a planned trajectory
planner.make_planner(["panda"], {"planner_id": "wAstar", "weight": "10."})
start = np.radians([0, -45, 0, -135, 0, 90, 45])

goal_pose = srmp.Pose()
goal_pose.p = np.array([0.6, 0.0, 0.5])
goal_pose.q = np.array([1.0, 0.0, 0.0, 0.0])
goal = srmp.GoalConstraint(srmp.GoalType.POSE, [goal_pose])

trajectory = planner.plan(start, goal)
planner.animate_trajectory(trajectory, dt=0.05)

GUI Controls (MeshCat)

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

# Must call visualize() first
planner.visualize()
planner.add_gui_controls()

# Set obstacle properties in Python, then add to the scene
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.1)
planner.set_gui_object_name("my_box")
planner.add_obstacle_from_gui()

# Load an existing object into the GUI state for editing
planner.load_object_to_gui("my_box")
planner.set_gui_position(0.6, 0.0, 0.5)
planner.update_object_from_gui("my_box")

# Access the browser URL
print(planner.url)

ViserPlannerInterface (Viser)

ViserPlannerInterface uses Viser, a modern 3D web visualizer that provides true bidirectional communication. Browser sliders, dropdowns, and buttons trigger Python callbacks in real time, making this backend ideal for interactive scene authoring and IK-driven teleoperation.

Basic Usage

from srmp import ViserPlannerInterface
import srmp
import numpy as np

# Start Viser server (default port 8080)
planner = ViserPlannerInterface(port=8080)

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

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)

# Open the 3D viewer
planner.visualize()
print(planner.url)  # → http://localhost:8080

# Animate a trajectory
planner.make_planner(["panda"], {"planner_id": "wAstar", "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)
planner.animate_trajectory(trajectory, dt=0.05)

# Stop the server when done
planner.stop()

Interactive Joint Controls

add_robot_controls() creates per-joint sliders in the browser sidebar. Moving a slider immediately updates both the Viser geometry and the planner backend:

planner.visualize()
planner.add_robot_controls("panda")

# The browser now shows joint sliders with a Reset button and
# visibility toggles for visual / collision meshes.

End-Effector Drag Control (IK)

add_ee_drag_control() places a 6-DOF transform handle at the robot’s end-effector. Dragging the gizmo in the browser triggers real-time IK:

planner.visualize()
planner.add_robot_controls("panda")   # optional — syncs joint sliders
planner.add_ee_drag_control("panda")

# The browser shows a 3-axis gizmo at the end-effector.
# Drag it to move the robot via IK.  On failure the gizmo
# snaps back to the actual EE pose.

import time
time.sleep(60)  # Keep server alive while interacting

GUI Object Controls (Viser)

add_gui_controls() adds a browser panel with dropdowns, sliders, text inputs, and Add / Update / Remove buttons that directly invoke Python:

planner.visualize()
planner.add_gui_controls()

# In the browser:
# 1. Select Object Type → "box"
# 2. Adjust Position, Width/Radius, Height, Depth sliders
# 3. Type a name in the "Object Name" field
# 4. Click "Add Object"  → calls planner.add_obstacle_from_gui()
#    Click "Update Object" → calls planner.update_object_from_gui(name)
#    Click "Remove Object" → calls planner.remove_object(name)

# You can also load an existing object into the GUI for editing:
planner.load_object_to_gui("obstacle")

# For mesh objects, click "Browse Mesh File..." to open a file dialog
# (requires tkinter) or type the path directly.

import time
time.sleep(120)  # Keep server alive

Shareable URLs

Pass share=True to generate a publicly accessible Viser share URL:

planner = ViserPlannerInterface(port=8080, share=True)
planner.visualize()
# Prints both the local URL and a public share URL

MeshCat vs Viser Comparison

Feature

VisualPlannerInterface

ViserPlannerInterface

Dependency

meshcat

viser, trimesh

Browser → Python communication

One-way (Python drives GUI)

Bidirectional

Interactive joint sliders

Display only

Fully interactive (callbacks)

End-effector drag / IK

Not available

Available

Object editing buttons

Python-driven only

Buttons trigger Python callbacks

Shareable URLs

Not available

Available (share=True)

Mesh loading

STL, OBJ, DAE

Any format supported by trimesh