askuric HF Staff commited on
Commit
f22d17e
·
1 Parent(s): f1ad1fa

a better working example

Browse files
Files changed (2) hide show
  1. app.py +92 -21
  2. robots.py +23 -13
app.py CHANGED
@@ -1,12 +1,50 @@
1
  import gradio as gr
2
  import numpy as np
3
  from robots import LiveRobot
 
4
 
5
- ROBOTS = ["panda", "ur5", "ur10", "talos"]
 
6
 
7
- # single global instance to keep the MeshCat connection alive
 
8
  robot = LiveRobot("panda")
9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
  with gr.Blocks(title="Pinocchio + MeshCat — Live") as demo:
11
  gr.Markdown("### 🤖 Pinocchio + MeshCat — Live Viewer\n"
12
  "Sliders update the robot in real time (no reload).")
@@ -17,41 +55,74 @@ with gr.Blocks(title="Pinocchio + MeshCat — Live") as demo:
17
 
18
  viewer = gr.HTML(robot.iframe())
19
 
20
- # sliders are created based on current robot model
21
- slider_group = gr.Group()
22
  sliders = []
23
- for label, (lo, hi), qi in zip(robot.labels, robot.bounds, robot.idxs):
24
- sliders.append(gr.Slider(minimum=lo, maximum=hi, step=0.01,
25
- value=float(robot.q[qi]), label=label))
 
 
 
 
 
 
 
26
 
27
  def on_sliders(*vals):
28
- robot.set_joints(vals)
29
- # return nothing for viewer; iframe stays connected
 
 
 
30
  return gr.update()
31
 
 
32
  for s in sliders:
33
- s.change(on_sliders, inputs=sliders, outputs=viewer, queue=False)
34
 
35
  def on_neutral():
36
- # reset joints and reflect slider values
37
- values = robot.neutral()
38
- updates = [gr.update(value=v) for v in values]
39
- return [gr.update()] + updates
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
40
 
41
  neutral_btn.click(on_neutral, outputs=[viewer] + sliders, queue=False)
42
 
43
  def on_change_robot(name):
 
44
  global robot
45
  robot = LiveRobot(name)
46
- # rebuild slider values to the new robot's neutral
47
  viewer_html = robot.iframe()
48
- updates = [gr.update(value=(lo+hi)/2, label=label, minimum=lo, maximum=hi)
49
- for (label,(lo,hi)) in zip(robot.labels, robot.bounds)]
50
- # Return viewer and new sliders
51
- return [viewer_html] + updates
 
 
 
 
 
52
 
53
- robot_dd.change(on_change_robot, inputs=robot_dd,
54
- outputs=[viewer] + sliders, queue=False)
55
 
56
  if __name__ == "__main__":
57
  demo.launch(server_name="0.0.0.0", server_port=8501)
 
1
  import gradio as gr
2
  import numpy as np
3
  from robots import LiveRobot
4
+ import example_robot_data as erd
5
 
6
+ #ROBOTS = ["erd", "panda", "ur10" , "double_pendulum", "anymal", "talos"]
7
+ ROBOTS = [r for r in erd.ROBOTS]
8
 
9
+
10
+ # keep one MeshCat session alive
11
  robot = LiveRobot("panda")
12
 
13
+ # how many sliders we allow at most (raise if you have bigger robots)
14
+ MAX_SLIDERS = 32
15
+
16
+ def _visible_dofs():
17
+ """Number of actuated DoFs for current robot."""
18
+ return len(robot.idxs)
19
+
20
+ def _slider_updates_for_current_robot():
21
+ """Return a list of gr.update() objects of length MAX_SLIDERS
22
+ to (re)configure all sliders for the current robot."""
23
+ updates = []
24
+ k = _visible_dofs()
25
+ for i in range(MAX_SLIDERS):
26
+ if i < k:
27
+ label = robot.labels[i]
28
+ lo, hi = robot.bounds[i]
29
+ if lo==hi:
30
+ lo = -np.pi/2
31
+ hi = np.pi/2
32
+ qi = robot.idxs[i]
33
+ val = float((lo+hi)/2)
34
+ updates.append(
35
+ gr.update(
36
+ minimum=float(lo),
37
+ maximum=float(hi),
38
+ value=val,
39
+ label=label,
40
+ step=0.01,
41
+ visible=True,
42
+ )
43
+ )
44
+ else:
45
+ updates.append(gr.update(visible=False))
46
+ return updates
47
+
48
  with gr.Blocks(title="Pinocchio + MeshCat — Live") as demo:
49
  gr.Markdown("### 🤖 Pinocchio + MeshCat — Live Viewer\n"
50
  "Sliders update the robot in real time (no reload).")
 
55
 
56
  viewer = gr.HTML(robot.iframe())
57
 
58
+ # Pre-create a fixed pool of sliders (initially hidden)
 
59
  sliders = []
60
+ with gr.Group():
61
+ for _ in range(MAX_SLIDERS):
62
+ sliders.append(
63
+ gr.Slider(
64
+ minimum=-3.14, maximum=3.14, value=0.0,
65
+ step=0.01, label="joint", visible=False
66
+ )
67
+ )
68
+
69
+ # ---------- Callbacks ----------
70
 
71
  def on_sliders(*vals):
72
+ """Apply first k slider values to robot joints."""
73
+ k = _visible_dofs()
74
+ # only use the first k values (ignore hidden/unused sliders)
75
+ robot.set_joints(vals[:k])
76
+ # viewer iframe persists; nothing to update there
77
  return gr.update()
78
 
79
+ # Wire all sliders to the same handler (fixed signature via *vals)
80
  for s in sliders:
81
+ s.change(on_sliders, inputs=sliders, outputs=viewer, queue=True)
82
 
83
  def on_neutral():
84
+ """Reset robot to neutral and reflect in visible sliders."""
85
+ values = robot.neutral() # returns values for actuated DoFs, length k
86
+ bounds = robot.bounds
87
+
88
+ for i, v in enumerate(values):
89
+ lo, hi = bounds[i]
90
+ if not np.isfinite(lo): lo = -3.14
91
+ if not np.isfinite(hi): hi = 3.14
92
+
93
+ # clamp neutral value to slider range
94
+ if v < lo: v = lo
95
+ if v > hi: v = hi
96
+
97
+ values[i] = v
98
+ k = len(values)
99
+ # viewer stays the same + slider value updates for first k; hide the rest
100
+ updates = [gr.update()] # for viewer
101
+ for i in range(MAX_SLIDERS):
102
+ if i < k:
103
+ updates.append(gr.update(value=float(values[i]), visible=True))
104
+ else:
105
+ updates.append(gr.update(visible=False))
106
+ return updates
107
 
108
  neutral_btn.click(on_neutral, outputs=[viewer] + sliders, queue=False)
109
 
110
  def on_change_robot(name):
111
+ """Rebuild slider labels/ranges/values/visibility to match selected robot."""
112
  global robot
113
  robot = LiveRobot(name)
 
114
  viewer_html = robot.iframe()
115
+ # 1) update viewer HTML
116
+ # 2) update ALL sliders to reflect new robot
117
+ return [viewer_html] + _slider_updates_for_current_robot()
118
+
119
+ robot_dd.change(on_change_robot, inputs=robot_dd, outputs=[viewer] + sliders, queue=True)
120
+
121
+ # On app load, configure sliders for the initial robot
122
+ def _initial():
123
+ return [robot.iframe()] + _slider_updates_for_current_robot()
124
 
125
+ demo.load(_initial, outputs=[viewer] + sliders, queue=False)
 
126
 
127
  if __name__ == "__main__":
128
  demo.launch(server_name="0.0.0.0", server_port=8501)
robots.py CHANGED
@@ -28,30 +28,35 @@ class LiveRobot:
28
  Starts a local MeshCat ZMQ+Web server and keeps a persistent connection.
29
  The web UI is proxied at /meshcat/ by nginx (see nginx.conf).
30
  """
31
- def __init__(self, robot_name="ur5"):
32
  self.vis = meshcat.Visualizer(zmq_url="tcp://127.0.0.1:6001")
33
  print(f"Zmq URL: {self.vis.window.zmq_url}, web URL: {self.vis.window.web_url}")
34
 
35
- self.vis.delete()
36
- time.sleep(1.0) # wait for the viewer to be ready
37
-
38
  self.rdata = erd.load(robot_name)
39
  self.model = self.rdata.model
40
  self.data = self.model.createData()
41
-
 
 
 
42
  self.viz = MeshcatVisualizer(
43
  self.model, self.rdata.collision_model, self.rdata.visual_model
44
  )
45
- self.viz.initViewer(self.vis)
46
- self.viz.reset()
47
- time.sleep(1.0) # wait for the viewer to be ready
 
 
48
 
49
  self.viz.loadViewerModel(rootNodeName="robot")
50
- time.sleep(1.0) # wait for the viewer to be ready
51
-
52
  self.q = pin.neutral(self.model)
53
- self.viz.display(self.q)
 
 
 
54
 
 
55
  self.idxs, self.labels, self.bounds = _actuated_dof_indices(self.model)
56
 
57
 
@@ -59,8 +64,13 @@ class LiveRobot:
59
  q = self.q.copy()
60
  for v, qi in zip(vals, self.idxs):
61
  q[qi] = float(v)
62
- self.q = q
63
- self.viz.display(self.q)
 
 
 
 
 
64
 
65
  def neutral(self):
66
  self.q = pin.neutral(self.model)
 
28
  Starts a local MeshCat ZMQ+Web server and keeps a persistent connection.
29
  The web UI is proxied at /meshcat/ by nginx (see nginx.conf).
30
  """
31
+ def __init__(self, robot_name="panda"):
32
  self.vis = meshcat.Visualizer(zmq_url="tcp://127.0.0.1:6001")
33
  print(f"Zmq URL: {self.vis.window.zmq_url}, web URL: {self.vis.window.web_url}")
34
 
 
 
 
35
  self.rdata = erd.load(robot_name)
36
  self.model = self.rdata.model
37
  self.data = self.model.createData()
38
+
39
+ self.vis.delete()
40
+ time.sleep(0.5) # wait for the robot to appear
41
+
42
  self.viz = MeshcatVisualizer(
43
  self.model, self.rdata.collision_model, self.rdata.visual_model
44
  )
45
+
46
+ self.viz.initViewer(self.vis)
47
+
48
+ self.viz.clean()
49
+ time.sleep(0.5) # wait for the robot to appear
50
 
51
  self.viz.loadViewerModel(rootNodeName="robot")
52
+
 
53
  self.q = pin.neutral(self.model)
54
+ try:
55
+ self.viz.display(self.q)
56
+ except Exception as e:
57
+ print(f"Error setting robot joints: {e}")
58
 
59
+
60
  self.idxs, self.labels, self.bounds = _actuated_dof_indices(self.model)
61
 
62
 
 
64
  q = self.q.copy()
65
  for v, qi in zip(vals, self.idxs):
66
  q[qi] = float(v)
67
+
68
+ try:
69
+ self.viz.display(q)
70
+ self.q = q
71
+ except Exception as e:
72
+ print(f"Error setting robot joints: {e}")
73
+
74
 
75
  def neutral(self):
76
  self.q = pin.neutral(self.model)