Robot URDF visualizerΒΆ
Requires yourdfpy and robot_descriptions. Any URDF supported by yourdfpy should work.
The viser.extras.ViserUrdf
is a lightweight interface between yourdfpy
and viser. It can also take a path to a local URDF file as input.
1from __future__ import annotations
2
3import time
4from typing import Literal
5
6import numpy as np
7import tyro
8from robot_descriptions.loaders.yourdfpy import load_robot_description
9
10import viser
11from viser.extras import ViserUrdf
12
13
14def create_robot_control_sliders(
15 server: viser.ViserServer, viser_urdf: ViserUrdf
16) -> tuple[list[viser.GuiInputHandle[float]], list[float]]:
17 """Create slider for each joint of the robot. We also update robot model
18 when slider moves."""
19 slider_handles: list[viser.GuiInputHandle[float]] = []
20 initial_config: list[float] = []
21 for joint_name, (
22 lower,
23 upper,
24 ) in viser_urdf.get_actuated_joint_limits().items():
25 lower = lower if lower is not None else -np.pi
26 upper = upper if upper is not None else np.pi
27 initial_pos = 0.0 if lower < 0 and upper > 0 else (lower + upper) / 2.0
28 slider = server.gui.add_slider(
29 label=joint_name,
30 min=lower,
31 max=upper,
32 step=1e-3,
33 initial_value=initial_pos,
34 )
35 slider.on_update( # When sliders move, we update the URDF configuration.
36 lambda _: viser_urdf.update_cfg(
37 np.array([slider.value for slider in slider_handles])
38 )
39 )
40 slider_handles.append(slider)
41 initial_config.append(initial_pos)
42 return slider_handles, initial_config
43
44
45def main(
46 robot_type: Literal[
47 "panda",
48 "ur10",
49 "cassie",
50 "allegro_hand",
51 "barrett_hand",
52 "robotiq_2f85",
53 "atlas_drc",
54 "g1",
55 "h1",
56 "anymal_c",
57 "go2",
58 ] = "panda",
59) -> None:
60 # Start viser server.
61 server = viser.ViserServer()
62 server.scene.enable_default_lights(cast_shadow=True)
63
64 # Load URDF.
65 #
66 # This takes either a yourdfpy.URDF object or a path to a .urdf file.
67 viser_urdf = ViserUrdf(
68 server,
69 urdf_or_path=load_robot_description(robot_type + "_description"),
70 )
71
72 # Create sliders in GUI that help us move the robot joints.
73 with server.gui.add_folder("Joint position control"):
74 (slider_handles, initial_config) = create_robot_control_sliders(
75 server, viser_urdf
76 )
77
78 # Set initial robot configuration.
79 viser_urdf.update_cfg(np.array(initial_config))
80
81 # Create grid.
82 server.scene.add_grid(
83 "/grid",
84 width=2,
85 height=2,
86 position=(
87 0.0,
88 0.0,
89 # Get the minimum z value of the trimesh scene.
90 viser_urdf._urdf.scene.bounds[0, 2],
91 ),
92 )
93
94 # Create joint reset button.
95 reset_button = server.gui.add_button("Reset")
96
97 @reset_button.on_click
98 def _(_):
99 for s, init_q in zip(slider_handles, initial_config):
100 s.value = init_q
101
102 # Sleep forever.
103 while True:
104 time.sleep(10.0)
105
106
107if __name__ == "__main__":
108 tyro.cli(main)