Spaces:
Sleeping
Sleeping
a better working example
Browse files
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 = ["
|
|
|
|
| 6 |
|
| 7 |
-
|
|
|
|
| 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 |
-
#
|
| 21 |
-
slider_group = gr.Group()
|
| 22 |
sliders = []
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 26 |
|
| 27 |
def on_sliders(*vals):
|
| 28 |
-
robot.
|
| 29 |
-
|
|
|
|
|
|
|
|
|
|
| 30 |
return gr.update()
|
| 31 |
|
|
|
|
| 32 |
for s in sliders:
|
| 33 |
-
s.change(on_sliders, inputs=sliders, outputs=viewer, queue=
|
| 34 |
|
| 35 |
def on_neutral():
|
| 36 |
-
|
| 37 |
-
values = robot.neutral()
|
| 38 |
-
|
| 39 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 52 |
|
| 53 |
-
|
| 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="
|
| 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 |
-
|
| 46 |
-
self.viz.
|
| 47 |
-
|
|
|
|
|
|
|
| 48 |
|
| 49 |
self.viz.loadViewerModel(rootNodeName="robot")
|
| 50 |
-
|
| 51 |
-
|
| 52 |
self.q = pin.neutral(self.model)
|
| 53 |
-
|
|
|
|
|
|
|
|
|
|
| 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 |
-
|
| 63 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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)
|