Spaces:
Running on Zero
Running on Zero
FLUX 3 Action SO-101 MuJoCo demo
Browse files- .gitattributes +14 -0
- README.md +13 -7
- app.py +168 -0
- assets/base_motor_holder_so101_v1.stl +3 -0
- assets/base_so101_v2.stl +3 -0
- assets/motor_holder_so101_base_v1.stl +3 -0
- assets/motor_holder_so101_wrist_v1.stl +3 -0
- assets/moving_jaw_so101_v1.stl +3 -0
- assets/rotation_pitch_so101_v1.stl +3 -0
- assets/sts3215_03a_no_horn_v1.stl +3 -0
- assets/sts3215_03a_v1.stl +3 -0
- assets/under_arm_so101_v1.stl +3 -0
- assets/upper_arm_so101_v1.stl +3 -0
- assets/waveshare_mounting_plate_so101_v2.stl +0 -0
- assets/wrist_camera_mount_so101_v1.stl +3 -0
- assets/wrist_camera_so101_v1.stl +3 -0
- assets/wrist_roll_follower_so101_v1.stl +3 -0
- assets/wrist_roll_pitch_so101_v2.stl +3 -0
- packages.txt +2 -0
- requirements.txt +5 -0
- sim.py +219 -0
- so101.xml +139 -0
.gitattributes
CHANGED
|
@@ -33,3 +33,17 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 36 |
+
assets/base_motor_holder_so101_v1.stl filter=lfs diff=lfs merge=lfs -text
|
| 37 |
+
assets/base_so101_v2.stl filter=lfs diff=lfs merge=lfs -text
|
| 38 |
+
assets/motor_holder_so101_base_v1.stl filter=lfs diff=lfs merge=lfs -text
|
| 39 |
+
assets/motor_holder_so101_wrist_v1.stl filter=lfs diff=lfs merge=lfs -text
|
| 40 |
+
assets/moving_jaw_so101_v1.stl filter=lfs diff=lfs merge=lfs -text
|
| 41 |
+
assets/rotation_pitch_so101_v1.stl filter=lfs diff=lfs merge=lfs -text
|
| 42 |
+
assets/sts3215_03a_no_horn_v1.stl filter=lfs diff=lfs merge=lfs -text
|
| 43 |
+
assets/sts3215_03a_v1.stl filter=lfs diff=lfs merge=lfs -text
|
| 44 |
+
assets/under_arm_so101_v1.stl filter=lfs diff=lfs merge=lfs -text
|
| 45 |
+
assets/upper_arm_so101_v1.stl filter=lfs diff=lfs merge=lfs -text
|
| 46 |
+
assets/wrist_camera_mount_so101_v1.stl filter=lfs diff=lfs merge=lfs -text
|
| 47 |
+
assets/wrist_camera_so101_v1.stl filter=lfs diff=lfs merge=lfs -text
|
| 48 |
+
assets/wrist_roll_follower_so101_v1.stl filter=lfs diff=lfs merge=lfs -text
|
| 49 |
+
assets/wrist_roll_pitch_so101_v2.stl filter=lfs diff=lfs merge=lfs -text
|
README.md
CHANGED
|
@@ -1,13 +1,19 @@
|
|
| 1 |
---
|
| 2 |
-
title:
|
| 3 |
-
emoji:
|
| 4 |
-
colorFrom:
|
| 5 |
-
colorTo:
|
| 6 |
sdk: gradio
|
| 7 |
sdk_version: 6.28.0
|
| 8 |
-
python_version: '3.12'
|
| 9 |
app_file: app.py
|
| 10 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 11 |
---
|
| 12 |
|
| 13 |
-
|
|
|
|
|
|
|
|
|
| 1 |
---
|
| 2 |
+
title: FLUX 3 Action SO-101 Sim
|
| 3 |
+
emoji: 🦾
|
| 4 |
+
colorFrom: yellow
|
| 5 |
+
colorTo: gray
|
| 6 |
sdk: gradio
|
| 7 |
sdk_version: 6.28.0
|
|
|
|
| 8 |
app_file: app.py
|
| 9 |
+
python_version: "3.12"
|
| 10 |
+
short_description: FLUX 3 Action drives a simulated SO-101 arm in MuJoCo
|
| 11 |
+
startup_duration_timeout: 1h
|
| 12 |
+
models:
|
| 13 |
+
- black-forest-labs/flux-3-action-so101
|
| 14 |
+
- black-forest-labs/flux-3-action-base
|
| 15 |
---
|
| 16 |
|
| 17 |
+
Closed-loop demo of [FLUX 3 Action SO-101](https://huggingface.co/black-forest-labs/flux-3-action-so101) controlling a simulated SO-101 arm in MuJoCo.
|
| 18 |
+
|
| 19 |
+
Robot model and meshes: [TheRobotStudio/SO-ARM100](https://github.com/TheRobotStudio/SO-ARM100) (`so101_new_calib_camera.xml`, Apache-2.0).
|
app.py
ADDED
|
@@ -0,0 +1,168 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import importlib.util
|
| 2 |
+
import os
|
| 3 |
+
import subprocess
|
| 4 |
+
import sys
|
| 5 |
+
|
| 6 |
+
os.environ.setdefault("MUJOCO_GL", "osmesa")
|
| 7 |
+
os.environ.setdefault("PYOPENGL_PLATFORM", "osmesa")
|
| 8 |
+
os.environ.setdefault("PYTORCH_CUDA_ALLOC_CONF", "expandable_segments:True")
|
| 9 |
+
|
| 10 |
+
import spaces
|
| 11 |
+
import torch
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
def _ensure_natten():
|
| 15 |
+
if importlib.util.find_spec("natten") is not None:
|
| 16 |
+
return
|
| 17 |
+
base = torch.__version__.split("+")[0]
|
| 18 |
+
tag = "torch" + "".join(base.split(".")[:3]) + "cu" + (torch.version.cuda or "12.8").replace(".", "")
|
| 19 |
+
subprocess.check_call(
|
| 20 |
+
[sys.executable, "-m", "pip", "install", f"natten==0.21.6+{tag}", "-f", "https://whl.natten.org/"]
|
| 21 |
+
)
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
_ensure_natten()
|
| 25 |
+
|
| 26 |
+
import tempfile
|
| 27 |
+
import time
|
| 28 |
+
from collections import deque
|
| 29 |
+
|
| 30 |
+
import gradio as gr
|
| 31 |
+
import imageio.v2 as imageio
|
| 32 |
+
import numpy as np
|
| 33 |
+
from PIL import Image, ImageDraw
|
| 34 |
+
|
| 35 |
+
import sim
|
| 36 |
+
from flux_action.inference.so101 import load_policy
|
| 37 |
+
|
| 38 |
+
REPO = "black-forest-labs/flux-3-action-so101"
|
| 39 |
+
N_OBS = 8
|
| 40 |
+
N_EXEC = 32
|
| 41 |
+
CAM_W, CAM_H = 320, 240
|
| 42 |
+
|
| 43 |
+
policy = load_policy(REPO).to("cuda")
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
@spaces.GPU(duration=40)
|
| 47 |
+
def infer_chunk(scene_frames, wrist_frames, states, commands, prompt):
|
| 48 |
+
device = "cuda"
|
| 49 |
+
batch = {
|
| 50 |
+
"images.scene": torch.from_numpy(scene_frames).permute(0, 3, 1, 2)[None].to(device),
|
| 51 |
+
"images.wrist": torch.from_numpy(wrist_frames).permute(0, 3, 1, 2)[None].to(device),
|
| 52 |
+
"state": torch.from_numpy(states).float()[None].to(device),
|
| 53 |
+
"command_history": torch.from_numpy(commands).float()[None].to(device),
|
| 54 |
+
"task": [prompt],
|
| 55 |
+
}
|
| 56 |
+
with torch.inference_mode():
|
| 57 |
+
targets = policy.predict_normalized_targets(batch)
|
| 58 |
+
actions = policy.actions_from_targets(targets, batch["command_history"][:, -1])
|
| 59 |
+
return actions[0].float().cpu().numpy()
|
| 60 |
+
|
| 61 |
+
|
| 62 |
+
def compose(big, scene_img, wrist_img, caption):
|
| 63 |
+
h = big.shape[0]
|
| 64 |
+
side = h // 2
|
| 65 |
+
s = np.array(Image.fromarray(scene_img).resize((int(side * 4 / 3), side)))
|
| 66 |
+
w = np.array(Image.fromarray(wrist_img).resize((int(side * 4 / 3), side)))
|
| 67 |
+
frame = np.concatenate([big, np.concatenate([s, w], 0)], 1)
|
| 68 |
+
im = Image.fromarray(frame)
|
| 69 |
+
d = ImageDraw.Draw(im)
|
| 70 |
+
d.rectangle([0, h - 26, big.shape[1], h], fill=(0, 0, 0))
|
| 71 |
+
d.text((8, h - 20), caption, fill=(255, 255, 255))
|
| 72 |
+
d.text((big.shape[1] + 6, 4), "scene cam (model input)", fill=(255, 255, 255))
|
| 73 |
+
d.text((big.shape[1] + 6, side + 4), "wrist cam (model input)", fill=(255, 255, 255))
|
| 74 |
+
return np.array(im)
|
| 75 |
+
|
| 76 |
+
|
| 77 |
+
def rollout(prompt, scene, n_chunks, seed):
|
| 78 |
+
prompt = (prompt or "").strip()
|
| 79 |
+
if not prompt:
|
| 80 |
+
raise gr.Error("Enter a task instruction.")
|
| 81 |
+
env = sim.Sim(scene=scene, width=CAM_W, height=CAM_H, seed=int(seed))
|
| 82 |
+
history = deque(maxlen=N_OBS)
|
| 83 |
+
last_command = None
|
| 84 |
+
queue = deque()
|
| 85 |
+
frames = []
|
| 86 |
+
total_ticks = int(n_chunks) * N_EXEC
|
| 87 |
+
t0 = time.perf_counter()
|
| 88 |
+
gpu_time = 0.0
|
| 89 |
+
log = []
|
| 90 |
+
tmp = tempfile.NamedTemporaryFile(suffix=".mp4", delete=False).name
|
| 91 |
+
for tick in range(total_ticks):
|
| 92 |
+
scene_img = env.render("scene")
|
| 93 |
+
wrist_img = env.render("wrist")
|
| 94 |
+
state = sim.lerobot_to_model(env.state_deg())
|
| 95 |
+
command = state if last_command is None else last_command
|
| 96 |
+
entry = (scene_img, wrist_img, state, command)
|
| 97 |
+
for _ in range(N_OBS if not history else 1):
|
| 98 |
+
history.append(entry)
|
| 99 |
+
if not queue:
|
| 100 |
+
g0 = time.perf_counter()
|
| 101 |
+
chunk = infer_chunk(
|
| 102 |
+
np.stack([h[0] for h in history]),
|
| 103 |
+
np.stack([h[1] for h in history]),
|
| 104 |
+
np.stack([h[2] for h in history]).astype(np.float32),
|
| 105 |
+
np.stack([h[3] for h in history]).astype(np.float32),
|
| 106 |
+
prompt,
|
| 107 |
+
)
|
| 108 |
+
gpu_time += time.perf_counter() - g0
|
| 109 |
+
queue.extend(chunk[:N_EXEC])
|
| 110 |
+
log.append(f"chunk {len(log) + 1}: {time.perf_counter() - g0:.1f}s")
|
| 111 |
+
action = np.asarray(queue.popleft(), dtype=np.float64)
|
| 112 |
+
last_command = action
|
| 113 |
+
env.step(sim.model_to_lerobot(action))
|
| 114 |
+
big = env.render_big()
|
| 115 |
+
frame = compose(big, scene_img, wrist_img, f"{prompt} t={tick / sim.FPS:4.1f}s")
|
| 116 |
+
frames.append(frame)
|
| 117 |
+
if tick % 8 == 0:
|
| 118 |
+
yield frame, None, "\n".join(log)
|
| 119 |
+
env.close()
|
| 120 |
+
imageio.mimsave(tmp, frames, fps=sim.FPS, codec="libx264", quality=7, macro_block_size=8)
|
| 121 |
+
log.append(f"total {time.perf_counter() - t0:.1f}s, model {gpu_time:.1f}s")
|
| 122 |
+
yield frames[-1], tmp, "\n".join(log)
|
| 123 |
+
|
| 124 |
+
|
| 125 |
+
def preview(scene, seed):
|
| 126 |
+
env = sim.Sim(scene=scene, width=CAM_W, height=CAM_H, seed=int(seed))
|
| 127 |
+
out = compose(env.render_big(), env.render("scene"), env.render("wrist"), "initial state")
|
| 128 |
+
env.close()
|
| 129 |
+
return out
|
| 130 |
+
|
| 131 |
+
|
| 132 |
+
with gr.Blocks(title="FLUX 3 Action SO-101 in MuJoCo") as demo:
|
| 133 |
+
gr.Markdown(
|
| 134 |
+
"# FLUX 3 Action · SO-101 in simulation\n"
|
| 135 |
+
"[black-forest-labs/flux-3-action-so101](https://huggingface.co/black-forest-labs/flux-3-action-so101) "
|
| 136 |
+
"drives a simulated SO-101 arm in MuJoCo, in closed loop. Each step renders the scene and wrist "
|
| 137 |
+
"cameras, the policy predicts 42 joint commands from the last 8 observations and your instruction, "
|
| 138 |
+
"the first 32 run at 30 Hz, then it replans. The policy was trained only on real SO-100/101 "
|
| 139 |
+
"teleoperation data, so the simulator is out of its training distribution."
|
| 140 |
+
)
|
| 141 |
+
with gr.Row():
|
| 142 |
+
with gr.Column(scale=1):
|
| 143 |
+
prompt = gr.Textbox(label="Task instruction", value="Pick up the red cube and put it in the gray container")
|
| 144 |
+
scene = gr.Dropdown(list(sim.SCENES), value="Cubes and tray", label="Scene")
|
| 145 |
+
n_chunks = gr.Slider(2, 12, value=6, step=1, label="Chunks (32 steps each, about 1.07 s)")
|
| 146 |
+
seed = gr.Number(value=0, precision=0, label="Object placement seed")
|
| 147 |
+
run = gr.Button("Run rollout", variant="primary")
|
| 148 |
+
log = gr.Textbox(label="Timing", lines=6)
|
| 149 |
+
with gr.Column(scale=2):
|
| 150 |
+
live = gr.Image(label="Live", value=None, interactive=False)
|
| 151 |
+
video = gr.Video(label="Rollout", autoplay=True)
|
| 152 |
+
gr.Examples(
|
| 153 |
+
[
|
| 154 |
+
["Pick up the red cube and put it in the gray container", "Cubes and tray"],
|
| 155 |
+
["Put the blue cube in the box", "Cubes and tray"],
|
| 156 |
+
["Pick up the yellow block", "Cubes and tray"],
|
| 157 |
+
["Grab the red cube and place it in the container", "Single cube"],
|
| 158 |
+
["Stack the orange cube on the green cube", "Blocks without tray"],
|
| 159 |
+
],
|
| 160 |
+
[prompt, scene],
|
| 161 |
+
cache_examples=False,
|
| 162 |
+
)
|
| 163 |
+
run.click(rollout, [prompt, scene, n_chunks, seed], [live, video, log])
|
| 164 |
+
scene.change(preview, [scene, seed], live)
|
| 165 |
+
seed.change(preview, [scene, seed], live)
|
| 166 |
+
demo.load(preview, [scene, seed], live)
|
| 167 |
+
|
| 168 |
+
demo.queue(default_concurrency_limit=2).launch()
|
assets/base_motor_holder_so101_v1.stl
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:8cd2f241037ea377af1191fffe0dd9d9006beea6dcc48543660ed41647072424
|
| 3 |
+
size 1877084
|
assets/base_so101_v2.stl
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:bb12b7026575e1f70ccc7240051f9d943553bf34e5128537de6cd86fae33924d
|
| 3 |
+
size 471584
|
assets/motor_holder_so101_base_v1.stl
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:31242ae6fb59d8b15c66617b88ad8e9bded62d57c35d11c0c43a70d2f4caa95b
|
| 3 |
+
size 1129384
|
assets/motor_holder_so101_wrist_v1.stl
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:887f92e6013cb64ea3a1ab8675e92da1e0beacfd5e001f972523540545e08011
|
| 3 |
+
size 1052184
|
assets/moving_jaw_so101_v1.stl
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:785a9dded2f474bc1d869e0d3dae398a3dcd9c0c345640040472210d2861fa9d
|
| 3 |
+
size 1413584
|
assets/rotation_pitch_so101_v1.stl
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:9be900cc2a2bf718102841ef82ef8d2873842427648092c8ed2ca1e2ef4ffa34
|
| 3 |
+
size 883684
|
assets/sts3215_03a_no_horn_v1.stl
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:75ef3781b752e4065891aea855e34dc161a38a549549cd0970cedd07eae6f887
|
| 3 |
+
size 865884
|
assets/sts3215_03a_v1.stl
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:a37c871fb502483ab96c256baf457d36f2e97afc9205313d9c5ab275ef941cd0
|
| 3 |
+
size 954084
|
assets/under_arm_so101_v1.stl
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:d01d1f2de365651dcad9d6669e94ff87ff7652b5bb2d10752a66a456a86dbc71
|
| 3 |
+
size 1975884
|
assets/upper_arm_so101_v1.stl
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:475056e03a17e71919b82fd88ab9a0b898ab50164f2a7943652a6b2941bb2d4f
|
| 3 |
+
size 1303484
|
assets/waveshare_mounting_plate_so101_v2.stl
ADDED
|
Binary file (62.8 kB). View file
|
|
|
assets/wrist_camera_mount_so101_v1.stl
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:e17a626158951ac8cdf8d960962c1a3c8bf23b64635a02f88b62016fe895cef8
|
| 3 |
+
size 173884
|
assets/wrist_camera_so101_v1.stl
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:1147857bb136876c81f98d18b533342bae2383f0e23bec216064c9f093bd3577
|
| 3 |
+
size 128934
|
assets/wrist_roll_follower_so101_v1.stl
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:4b17b410a12d64ec39554abc3e8054d8a97384b2dc4a8d95a5ecb2a93670f5f4
|
| 3 |
+
size 1439884
|
assets/wrist_roll_pitch_so101_v2.stl
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:6c7ec5525b4d8b9e397a30ab4bb0037156a5d5f38a4adf2c7d943d6c56eda5ae
|
| 3 |
+
size 2699784
|
packages.txt
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
libosmesa6
|
| 2 |
+
libgl1
|
requirements.txt
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
flux-action[encoders] @ git+https://github.com/black-forest-labs/flux-action@e2dd1d8dbc5977b54315d61f7548c63c043d6d4f
|
| 2 |
+
mujoco
|
| 3 |
+
PyOpenGL
|
| 4 |
+
imageio[ffmpeg]
|
| 5 |
+
numpy<2.3
|
sim.py
ADDED
|
@@ -0,0 +1,219 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
from dataclasses import dataclass, field
|
| 3 |
+
|
| 4 |
+
import mujoco
|
| 5 |
+
import numpy as np
|
| 6 |
+
|
| 7 |
+
HERE = os.path.dirname(os.path.abspath(__file__))
|
| 8 |
+
FPS = 30
|
| 9 |
+
PHYSICS_DT = 1.0 / 600.0
|
| 10 |
+
SUBSTEPS = int(round(1.0 / FPS / PHYSICS_DT))
|
| 11 |
+
JOINTS = ["shoulder_pan", "shoulder_lift", "elbow_flex", "wrist_flex", "wrist_roll", "gripper"]
|
| 12 |
+
GRIPPER_CLOSED = -0.17453
|
| 13 |
+
GRIPPER_OPEN = 1.74533
|
| 14 |
+
REST_DEG = np.array([0.0, -92.0, 88.0, 72.0, 0.0, 2.0])
|
| 15 |
+
|
| 16 |
+
COLORS = {
|
| 17 |
+
"red": "0.80 0.12 0.10 1",
|
| 18 |
+
"blue": "0.10 0.25 0.75 1",
|
| 19 |
+
"green": "0.12 0.55 0.20 1",
|
| 20 |
+
"yellow": "0.95 0.78 0.10 1",
|
| 21 |
+
"white": "0.92 0.92 0.90 1",
|
| 22 |
+
"black": "0.08 0.08 0.08 1",
|
| 23 |
+
"orange": "0.95 0.45 0.08 1",
|
| 24 |
+
}
|
| 25 |
+
|
| 26 |
+
SCENES = {
|
| 27 |
+
"Cubes and tray": {
|
| 28 |
+
"objects": [
|
| 29 |
+
("red", "cube", (0.22, 0.02), 0.0),
|
| 30 |
+
("blue", "cube", (0.27, -0.08), 0.4),
|
| 31 |
+
("yellow", "cube", (0.17, -0.12), 0.9),
|
| 32 |
+
],
|
| 33 |
+
"tray": (0.16, 0.17),
|
| 34 |
+
},
|
| 35 |
+
"Single cube": {
|
| 36 |
+
"objects": [("red", "cube", (0.22, -0.04), 0.3)],
|
| 37 |
+
"tray": (0.16, 0.17),
|
| 38 |
+
},
|
| 39 |
+
"Blocks without tray": {
|
| 40 |
+
"objects": [
|
| 41 |
+
("green", "cube", (0.22, 0.08), 0.2),
|
| 42 |
+
("orange", "cube", (0.24, -0.06), 0.7),
|
| 43 |
+
("white", "box", (0.17, 0.0), 0.0),
|
| 44 |
+
],
|
| 45 |
+
"tray": None,
|
| 46 |
+
},
|
| 47 |
+
}
|
| 48 |
+
|
| 49 |
+
|
| 50 |
+
def _object_xml(i, color, kind, xy, yaw):
|
| 51 |
+
rgba = COLORS[color]
|
| 52 |
+
half = "0.015 0.015 0.015" if kind == "cube" else "0.02 0.03 0.015"
|
| 53 |
+
z = 0.0155
|
| 54 |
+
q = f"{np.cos(yaw / 2):.5f} 0 0 {np.sin(yaw / 2):.5f}"
|
| 55 |
+
return (
|
| 56 |
+
f'<body name="obj{i}" pos="{xy[0]} {xy[1]} {z}" quat="{q}">'
|
| 57 |
+
f'<freejoint name="obj{i}_free"/>'
|
| 58 |
+
f'<geom type="box" size="{half}" rgba="{rgba}" mass="0.02" friction="1.5 0.02 0.001" '
|
| 59 |
+
f'condim="4" solref="0.004 1" priority="1"/>'
|
| 60 |
+
f"</body>"
|
| 61 |
+
)
|
| 62 |
+
|
| 63 |
+
|
| 64 |
+
def _tray_xml(xy):
|
| 65 |
+
x, y = xy
|
| 66 |
+
hx, hy, hz, t = 0.075, 0.06, 0.022, 0.004
|
| 67 |
+
rgba = "0.55 0.56 0.58 1"
|
| 68 |
+
parts = [
|
| 69 |
+
f'<geom type="box" pos="0 0 {t / 2}" size="{hx} {hy} {t / 2}" rgba="{rgba}"/>',
|
| 70 |
+
f'<geom type="box" pos="{hx - t / 2} 0 {hz}" size="{t / 2} {hy} {hz}" rgba="{rgba}"/>',
|
| 71 |
+
f'<geom type="box" pos="{-hx + t / 2} 0 {hz}" size="{t / 2} {hy} {hz}" rgba="{rgba}"/>',
|
| 72 |
+
f'<geom type="box" pos="0 {hy - t / 2} {hz}" size="{hx} {t / 2} {hz}" rgba="{rgba}"/>',
|
| 73 |
+
f'<geom type="box" pos="0 {-hy + t / 2} {hz}" size="{hx} {t / 2} {hz}" rgba="{rgba}"/>',
|
| 74 |
+
]
|
| 75 |
+
return f'<body name="tray" pos="{x} {y} 0">' + "".join(parts) + "</body>"
|
| 76 |
+
|
| 77 |
+
|
| 78 |
+
def scene_xml(scene):
|
| 79 |
+
spec = SCENES[scene]
|
| 80 |
+
objects = "".join(_object_xml(i, *o) for i, o in enumerate(spec["objects"]))
|
| 81 |
+
tray = _tray_xml(spec["tray"]) if spec["tray"] else ""
|
| 82 |
+
return f"""
|
| 83 |
+
<mujoco model="so101_tabletop">
|
| 84 |
+
<include file="so101.xml"/>
|
| 85 |
+
<option timestep="{PHYSICS_DT}" cone="elliptic" impratio="10"/>
|
| 86 |
+
<visual>
|
| 87 |
+
<headlight diffuse="0.35 0.35 0.35" ambient="0.35 0.35 0.35" specular="0 0 0"/>
|
| 88 |
+
<quality shadowsize="4096"/>
|
| 89 |
+
<global offwidth="640" offheight="480"/>
|
| 90 |
+
<map znear="0.005"/>
|
| 91 |
+
</visual>
|
| 92 |
+
<asset>
|
| 93 |
+
<texture type="skybox" builtin="gradient" rgb1="0.85 0.86 0.88" rgb2="0.55 0.56 0.58" width="256" height="1536"/>
|
| 94 |
+
<texture name="cork" type="2d" builtin="flat" rgb1="0.80 0.66 0.48" mark="random" markrgb="0.70 0.55 0.38" random="0.12" width="512" height="512"/>
|
| 95 |
+
<material name="cork" texture="cork" texrepeat="4 4" reflectance="0.0"/>
|
| 96 |
+
<material name="desk" rgba="0.80 0.81 0.82 1"/>
|
| 97 |
+
<material name="wall" rgba="0.90 0.90 0.88 1"/>
|
| 98 |
+
<material name="white_part" rgba="0.93 0.93 0.91 1"/>
|
| 99 |
+
</asset>
|
| 100 |
+
<worldbody>
|
| 101 |
+
<light pos="0.3 -0.3 1.2" dir="-0.2 0.2 -1" directional="false" diffuse="0.6 0.6 0.6" castshadow="true"/>
|
| 102 |
+
<light pos="-0.4 0.4 1.0" dir="0.3 -0.3 -1" diffuse="0.3 0.3 0.3" castshadow="false"/>
|
| 103 |
+
<geom name="desk" type="box" pos="0.25 0 -0.012" size="0.7 0.7 0.004" material="desk"/>
|
| 104 |
+
<geom name="board" type="box" pos="0.22 0 -0.004" size="0.26 0.32 0.004" material="cork" friction="1 0.01 0.001"/>
|
| 105 |
+
<geom name="wall" type="box" pos="-0.35 0 0.4" size="0.01 1.2 0.5" material="wall" contype="0" conaffinity="0"/>
|
| 106 |
+
<geom name="wall2" type="box" pos="0.3 0.75 0.4" size="1.0 0.01 0.5" material="wall" contype="0" conaffinity="0"/>
|
| 107 |
+
<camera name="scene" pos="0.46 -0.24 0.34" mode="targetbody" target="scene_target" fovy="58"/>
|
| 108 |
+
<camera name="overview" pos="0.62 -0.42 0.45" mode="targetbody" target="scene_target" fovy="50"/>
|
| 109 |
+
<body name="scene_target" pos="0.14 0.03 0.05"/>
|
| 110 |
+
{objects}
|
| 111 |
+
{tray}
|
| 112 |
+
</worldbody>
|
| 113 |
+
</mujoco>
|
| 114 |
+
"""
|
| 115 |
+
|
| 116 |
+
|
| 117 |
+
def build_model(scene):
|
| 118 |
+
xml = scene_xml(scene)
|
| 119 |
+
cwd = os.getcwd()
|
| 120 |
+
os.chdir(HERE)
|
| 121 |
+
try:
|
| 122 |
+
model = mujoco.MjModel.from_xml_string(xml)
|
| 123 |
+
finally:
|
| 124 |
+
os.chdir(cwd)
|
| 125 |
+
return model
|
| 126 |
+
|
| 127 |
+
|
| 128 |
+
def deg_to_q(deg):
|
| 129 |
+
q = np.deg2rad(np.asarray(deg[:5], dtype=np.float64))
|
| 130 |
+
g = GRIPPER_CLOSED + np.clip(deg[5], 0.0, 100.0) / 100.0 * (GRIPPER_OPEN - GRIPPER_CLOSED)
|
| 131 |
+
return np.concatenate([q, [g]])
|
| 132 |
+
|
| 133 |
+
|
| 134 |
+
def q_to_deg(q):
|
| 135 |
+
deg = np.rad2deg(np.asarray(q[:5], dtype=np.float64))
|
| 136 |
+
g = (q[5] - GRIPPER_CLOSED) / (GRIPPER_OPEN - GRIPPER_CLOSED) * 100.0
|
| 137 |
+
return np.concatenate([deg, [g]])
|
| 138 |
+
|
| 139 |
+
|
| 140 |
+
def lerobot_to_model(deg):
|
| 141 |
+
out = np.array(deg, dtype=np.float64).copy()
|
| 142 |
+
out[1] = 90.0 - deg[1]
|
| 143 |
+
out[2] = deg[2] + 90.0
|
| 144 |
+
return out
|
| 145 |
+
|
| 146 |
+
|
| 147 |
+
def model_to_lerobot(val):
|
| 148 |
+
out = np.array(val, dtype=np.float64).copy()
|
| 149 |
+
out[1] = 90.0 - val[1]
|
| 150 |
+
out[2] = val[2] - 90.0
|
| 151 |
+
return out
|
| 152 |
+
|
| 153 |
+
|
| 154 |
+
@dataclass
|
| 155 |
+
class Sim:
|
| 156 |
+
scene: str = "Cubes and tray"
|
| 157 |
+
width: int = 256
|
| 158 |
+
height: int = 256
|
| 159 |
+
seed: int = 0
|
| 160 |
+
model: mujoco.MjModel = field(init=False)
|
| 161 |
+
data: mujoco.MjData = field(init=False)
|
| 162 |
+
|
| 163 |
+
def __post_init__(self):
|
| 164 |
+
self.model = build_model(self.scene)
|
| 165 |
+
self.data = mujoco.MjData(self.model)
|
| 166 |
+
self.qadr = np.array([self.model.joint(j).qposadr[0] for j in JOINTS])
|
| 167 |
+
self.renderer = mujoco.Renderer(self.model, self.height, self.width)
|
| 168 |
+
self.big = None
|
| 169 |
+
self.reset()
|
| 170 |
+
|
| 171 |
+
def reset(self):
|
| 172 |
+
mujoco.mj_resetData(self.model, self.data)
|
| 173 |
+
rng = np.random.default_rng(self.seed)
|
| 174 |
+
for i in range(self.model.nbody):
|
| 175 |
+
name = self.model.body(i).name
|
| 176 |
+
if name.startswith("obj"):
|
| 177 |
+
j = self.model.joint(f"{name}_free")
|
| 178 |
+
a = j.qposadr[0]
|
| 179 |
+
self.data.qpos[a : a + 2] += rng.uniform(-0.015, 0.015, 2)
|
| 180 |
+
q = deg_to_q(REST_DEG)
|
| 181 |
+
self.data.qpos[self.qadr] = q
|
| 182 |
+
self.data.ctrl[:] = q
|
| 183 |
+
mujoco.mj_forward(self.model, self.data)
|
| 184 |
+
for _ in range(120):
|
| 185 |
+
mujoco.mj_step(self.model, self.data)
|
| 186 |
+
|
| 187 |
+
def state_deg(self):
|
| 188 |
+
return q_to_deg(self.data.qpos[self.qadr])
|
| 189 |
+
|
| 190 |
+
def step(self, command_deg):
|
| 191 |
+
q = deg_to_q(command_deg)
|
| 192 |
+
lo = self.model.actuator_ctrlrange[:, 0]
|
| 193 |
+
hi = self.model.actuator_ctrlrange[:, 1]
|
| 194 |
+
self.data.ctrl[:] = np.clip(q, lo, hi)
|
| 195 |
+
for _ in range(SUBSTEPS):
|
| 196 |
+
mujoco.mj_step(self.model, self.data)
|
| 197 |
+
|
| 198 |
+
def render(self, camera):
|
| 199 |
+
self.renderer.update_scene(self.data, camera=camera)
|
| 200 |
+
return self.renderer.render().copy()
|
| 201 |
+
|
| 202 |
+
def render_big(self, camera="overview", width=640, height=480):
|
| 203 |
+
if self.big is None:
|
| 204 |
+
self.big = mujoco.Renderer(self.model, height, width)
|
| 205 |
+
self.big.update_scene(self.data, camera=camera)
|
| 206 |
+
return self.big.render().copy()
|
| 207 |
+
|
| 208 |
+
def close(self):
|
| 209 |
+
self.renderer.close()
|
| 210 |
+
if self.big is not None:
|
| 211 |
+
self.big.close()
|
| 212 |
+
|
| 213 |
+
def object_positions(self):
|
| 214 |
+
out = {}
|
| 215 |
+
for i in range(self.model.nbody):
|
| 216 |
+
name = self.model.body(i).name
|
| 217 |
+
if name.startswith("obj"):
|
| 218 |
+
out[name] = self.data.body(i).xpos.copy()
|
| 219 |
+
return out
|
so101.xml
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?xml version="1.0" ?>
|
| 2 |
+
<mujoco model="so101_new_calib_camera">
|
| 3 |
+
<compiler angle="radian" meshdir="assets" autolimits="true"/>
|
| 4 |
+
<default>
|
| 5 |
+
<default class="so101_new_calib">
|
| 6 |
+
<joint damping="1" frictionloss="0.1" armature="0.005"/>
|
| 7 |
+
<position kp="50"/>
|
| 8 |
+
<default class="visual">
|
| 9 |
+
<geom type="mesh" contype="0" conaffinity="0" group="2"/>
|
| 10 |
+
</default>
|
| 11 |
+
<default class="collision">
|
| 12 |
+
<geom group="3"/>
|
| 13 |
+
</default>
|
| 14 |
+
</default>
|
| 15 |
+
</default>
|
| 16 |
+
<default>
|
| 17 |
+
<default class="sts3215">
|
| 18 |
+
<geom contype="0" conaffinity="0"/>
|
| 19 |
+
<joint damping="0.60" frictionloss="0.052" armature="0.028"/>
|
| 20 |
+
<position kp="998.22" kv="2.731" forcerange="-2.94 2.94"/>
|
| 21 |
+
</default>
|
| 22 |
+
<default class="backlash">
|
| 23 |
+
<joint damping="0.01" frictionloss="0" armature="0.01" limited="true" range="-0.008726646259971648 0.008726646259971648"/>
|
| 24 |
+
</default>
|
| 25 |
+
</default>
|
| 26 |
+
<worldbody>
|
| 27 |
+
<body name="base" pos="0 0 0" quat="1 0 0 0" childclass="so101_new_calib">
|
| 28 |
+
<inertial pos="0.0137179 -5.19711e-05 0.0334843" mass="0.147" fullinertia="0.000114686 0.000136117 0.000130364 -4.59787e-07 4.97151e-06 9.75275e-08"/>
|
| 29 |
+
<geom type="mesh" class="visual" pos="-0.00636471 -9.94414e-05 -0.0024" quat="0.5 0.5 0.5 0.5" mesh="base_motor_holder_so101_v1" material="base_motor_holder_so101_v1_material"/>
|
| 30 |
+
<geom type="mesh" class="visual" pos="-0.00636471 -8.97657e-09 -0.0024" quat="0.5 0.5 0.5 0.5" mesh="base_so101_v2" material="base_so101_v2_material"/>
|
| 31 |
+
<geom type="mesh" class="visual" pos="0.0263353 -8.97657e-09 0.0437" quat="1 -2.85511e-16 -9.64433e-17 6.12908e-16" mesh="sts3215_03a_v1" material="sts3215_03a_v1_material"/>
|
| 32 |
+
<geom type="mesh" class="visual" pos="-0.0309827 -0.000199441 0.0474" quat="0.5 0.5 0.5 0.5" mesh="waveshare_mounting_plate_so101_v2" material="waveshare_mounting_plate_so101_v2_material"/>
|
| 33 |
+
<site group="3" name="baseframe" pos="8.67362e-19 9.55596e-18 3.46945e-18" quat="1 -8.17396e-19 3.78392e-17 2.22045e-16"/>
|
| 34 |
+
<body name="shoulder" pos="0.0388353 -8.97657e-09 0.0624" quat="3.56167e-16 1.22818e-15 -1 -4.14635e-16">
|
| 35 |
+
<joint axis="0 0 1" name="shoulder_pan" type="hinge" range="-1.9198621771937616 1.9198621771937634" class="sts3215"/>
|
| 36 |
+
<inertial pos="-0.0307604 -1.66727e-05 -0.0252713" mass="0.100006" fullinertia="8.3759e-05 8.10403e-05 2.39783e-05 7.55525e-08 -1.16342e-06 1.54663e-07"/>
|
| 37 |
+
<geom type="mesh" class="visual" pos="-0.0303992 0.000422241 -0.0417" quat="0.5 0.5 0.5 -0.5" mesh="sts3215_03a_v1" material="sts3215_03a_v1_material"/>
|
| 38 |
+
<geom type="mesh" class="collision" pos="-0.0303992 0.000422241 -0.0417" quat="0.5 0.5 0.5 -0.5" mesh="sts3215_03a_v1" material="sts3215_03a_v1_material"/>
|
| 39 |
+
<geom type="mesh" class="visual" pos="-0.0675992 -0.000177759 0.0158499" quat="0.5 0.5 -0.5 0.5" mesh="motor_holder_so101_base_v1" material="motor_holder_so101_base_v1_material"/>
|
| 40 |
+
<geom type="mesh" class="collision" pos="-0.0675992 -0.000177759 0.0158499" quat="0.5 0.5 -0.5 0.5" mesh="motor_holder_so101_base_v1" material="motor_holder_so101_base_v1_material"/>
|
| 41 |
+
<geom type="mesh" class="visual" pos="0.0122008 2.22413e-05 0.0464" quat="0.707107 -0.707107 -0 8.3163e-34" mesh="rotation_pitch_so101_v1" material="rotation_pitch_so101_v1_material"/>
|
| 42 |
+
<geom type="mesh" class="collision" pos="0.0122008 2.22413e-05 0.0464" quat="0.707107 -0.707107 -0 8.3163e-34" mesh="rotation_pitch_so101_v1" material="rotation_pitch_so101_v1_material"/>
|
| 43 |
+
<body name="upper_arm" pos="-0.0303992 -0.0182778 -0.0542" quat="0.5 -0.5 -0.5 -0.5">
|
| 44 |
+
<joint axis="0 0 1" name="shoulder_lift" type="hinge" range="-1.7453292519943224 1.7453292519943366" class="sts3215"/>
|
| 45 |
+
<inertial pos="-0.0898471 -0.00838224 0.0184089" mass="0.103" fullinertia="4.08002e-05 0.000147318 0.000142487 -1.97819e-05 -4.03016e-08 8.97326e-09"/>
|
| 46 |
+
<geom type="mesh" class="visual" pos="-0.11257 -0.0155 0.0187" quat="4.56308e-16 -0.707107 0.707107 -1.37383e-16" mesh="sts3215_03a_v1" material="sts3215_03a_v1_material"/>
|
| 47 |
+
<geom type="mesh" class="collision" pos="-0.11257 -0.0155 0.0187" quat="4.56308e-16 -0.707107 0.707107 -1.37383e-16" mesh="sts3215_03a_v1" material="sts3215_03a_v1_material"/>
|
| 48 |
+
<geom type="mesh" class="visual" pos="-0.065085 0.012 0.0182" quat="0 1 0 0" mesh="upper_arm_so101_v1" material="upper_arm_so101_v1_material"/>
|
| 49 |
+
<geom type="mesh" class="collision" pos="-0.065085 0.012 0.0182" quat="0 1 0 0" mesh="upper_arm_so101_v1" material="upper_arm_so101_v1_material"/>
|
| 50 |
+
<body name="lower_arm" pos="-0.11257 -0.028 1.73763e-16" quat="0.707107 -5.98613e-17 -2.58051e-17 0.707107">
|
| 51 |
+
<joint axis="0 0 1" name="elbow_flex" type="hinge" range="-1.69 1.69" class="sts3215"/>
|
| 52 |
+
<inertial pos="-0.0980701 0.00324376 0.0182831" mass="0.104" fullinertia="2.87438e-05 0.000159844 0.00014529 7.41152e-06 1.26409e-06 -4.90188e-08"/>
|
| 53 |
+
<geom type="mesh" class="visual" pos="-0.0648499 -0.032 0.0182" quat="0 1 0 0" mesh="under_arm_so101_v1" material="under_arm_so101_v1_material"/>
|
| 54 |
+
<geom type="mesh" class="collision" pos="-0.0648499 -0.032 0.0182" quat="0 1 0 0" mesh="under_arm_so101_v1" material="under_arm_so101_v1_material"/>
|
| 55 |
+
<geom type="mesh" class="visual" pos="-0.0648499 -0.032 0.018" quat="3.92687e-16 -1 -1.9186e-15 -6.38378e-16" mesh="motor_holder_so101_wrist_v1" material="motor_holder_so101_wrist_v1_material"/>
|
| 56 |
+
<geom type="mesh" class="collision" pos="-0.0648499 -0.032 0.018" quat="3.92687e-16 -1 -1.9186e-15 -6.38378e-16" mesh="motor_holder_so101_wrist_v1" material="motor_holder_so101_wrist_v1_material"/>
|
| 57 |
+
<geom type="mesh" class="visual" pos="-0.1224 0.0052 0.0187" quat="7.21645e-16 1.56949e-15 1 -3.33067e-16" mesh="sts3215_03a_v1" material="sts3215_03a_v1_material"/>
|
| 58 |
+
<geom type="mesh" class="collision" pos="-0.1224 0.0052 0.0187" quat="7.21645e-16 1.56949e-15 1 -3.33067e-16" mesh="sts3215_03a_v1" material="sts3215_03a_v1_material"/>
|
| 59 |
+
<body name="wrist" pos="-0.1349 0.0052 3.62355e-17" quat="0.707107 9.58722e-16 -7.51313e-16 -0.707107">
|
| 60 |
+
<joint axis="0 0 1" name="wrist_flex" type="hinge" range="-1.6580628494556928 1.6580627293335335" class="sts3215"/>
|
| 61 |
+
<inertial pos="-0.000103312 -0.0386143 0.0281156" mass="0.079" fullinertia="3.68263e-05 2.5391e-05 2.1e-05 1.7893e-08 -5.28128e-08 3.6412e-06"/>
|
| 62 |
+
<geom type="mesh" class="visual" pos="8.32667e-17 -0.0424 0.0306" quat="0.5 0.5 0.5 -0.5" mesh="sts3215_03a_no_horn_v1" material="sts3215_03a_no_horn_v1_material"/>
|
| 63 |
+
<geom type="mesh" class="collision" pos="8.32667e-17 -0.0424 0.0306" quat="0.5 0.5 0.5 -0.5" mesh="sts3215_03a_no_horn_v1" material="sts3215_03a_no_horn_v1_material"/>
|
| 64 |
+
<geom type="mesh" class="visual" pos="0 -0.028 0.0181" quat="0.5 -0.5 -0.5 -0.5" mesh="wrist_roll_pitch_so101_v2" material="wrist_roll_pitch_so101_v2_material"/>
|
| 65 |
+
<geom type="mesh" class="collision" pos="0 -0.028 0.0181" quat="0.5 -0.5 -0.5 -0.5" mesh="wrist_roll_pitch_so101_v2" material="wrist_roll_pitch_so101_v2_material"/>
|
| 66 |
+
<body name="gripper" pos="5.55112e-17 -0.0611 0.0181" quat="0.0172091 -0.0172091 0.706897 0.706897">
|
| 67 |
+
<camera name="wrist" pos="0.003 0.066 -0.024" xyaxes="-1 0 0 0 -0.906 0.424" fovy="80"/>
|
| 68 |
+
<joint axis="0 0 1" name="wrist_roll" type="hinge" range="-2.7438472969992493 2.841206309382605" class="sts3215"/>
|
| 69 |
+
<inertial pos="0.000213627 0.000245138 -0.025187" mass="0.087" fullinertia="2.75087e-05 4.33657e-05 3.45059e-05 -3.35241e-07 -5.7352e-06 -5.17847e-08"/>
|
| 70 |
+
<geom type="mesh" class="visual" pos="0.0077 0.0001 -0.0234" quat="0.707107 -0.707107 1.66015e-15 6.45094e-15" mesh="sts3215_03a_v1" material="sts3215_03a_v1_material"/>
|
| 71 |
+
<geom type="mesh" class="collision" pos="0.0077 0.0001 -0.0234" quat="0.707107 -0.707107 1.66015e-15 6.45094e-15" mesh="sts3215_03a_v1" material="sts3215_03a_v1_material"/>
|
| 72 |
+
<geom type="mesh" class="visual" pos="8.32667e-17 -0.000218214 0.000949706" quat="0 1 0 0" mesh="wrist_roll_follower_so101_v1" material="wrist_roll_follower_so101_v1_material"/>
|
| 73 |
+
<geom type="mesh" class="collision" pos="8.32667e-17 -0.000218214 0.000949706" quat="0 1 0 0" mesh="wrist_roll_follower_so101_v1" material="wrist_roll_follower_so101_v1_material"/>
|
| 74 |
+
<site group="3" name="gripperframe" pos="-0.0079 -0.000218121 -0.0981274" quat="0.707107 -0 0.707107 -2.37788e-17"/>
|
| 75 |
+
<body name="wrist_camera_mount" pos="-0.0150 0.0240 -0.0315" quat="0.4997981226682881 -0.5001999599912927 0.4998017973423795 -0.5001999599912927">
|
| 76 |
+
<inertial pos="0 0 0" mass="0.02" diaginertia="1e-5 1e-5 1e-5"/>
|
| 77 |
+
<geom type="mesh" class="visual" mesh="wrist_camera_mount_so101_v1" material="wrist_camera_mount_so101_v1_material"/>
|
| 78 |
+
<geom type="mesh" class="collision" mesh="wrist_camera_mount_so101_v1" material="wrist_camera_mount_so101_v1_material"/>
|
| 79 |
+
<body name="wrist_camera" pos="-0.04941 -0.03813 0.0175" quat="0.37978693649389456 -0.5964584309361936 -0.37978833153177 -0.596456240026065">
|
| 80 |
+
<inertial pos="0 0 0" mass="0.015" diaginertia="1e-5 1e-5 1e-5"/>
|
| 81 |
+
<geom type="mesh" class="visual" mesh="wrist_camera_so101_v1" material="wrist_camera_so101_v1_material"/>
|
| 82 |
+
<geom type="mesh" class="collision" mesh="wrist_camera_so101_v1" material="wrist_camera_so101_v1_material"/>
|
| 83 |
+
</body>
|
| 84 |
+
</body>
|
| 85 |
+
<body name="moving_jaw_so101_v1" pos="0.0202 0.0188 -0.0234" quat="0.707107 0.707107 -1.85362e-08 1.85362e-08">
|
| 86 |
+
<joint axis="0 0 1" name="gripper" type="hinge" range="-0.17453297762778586 1.7453291995659765" class="sts3215"/>
|
| 87 |
+
<inertial pos="-0.00157495 -0.0300244 0.0192755" mass="0.012" fullinertia="6.61427e-06 1.89032e-06 5.28738e-06 -3.19807e-07 -5.90717e-09 -1.09945e-07"/>
|
| 88 |
+
<geom type="mesh" class="visual" pos="-5.55112e-17 -5.55112e-17 0.0189" quat="1 -0 3.00524e-16 -2.00834e-17" mesh="moving_jaw_so101_v1" material="moving_jaw_so101_v1_material"/>
|
| 89 |
+
<geom type="mesh" class="collision" pos="-5.55112e-17 -5.55112e-17 0.0189" quat="1 -0 3.00524e-16 -2.00834e-17" mesh="moving_jaw_so101_v1" material="moving_jaw_so101_v1_material"/>
|
| 90 |
+
</body>
|
| 91 |
+
</body>
|
| 92 |
+
</body>
|
| 93 |
+
</body>
|
| 94 |
+
</body>
|
| 95 |
+
</body>
|
| 96 |
+
</body>
|
| 97 |
+
</worldbody>
|
| 98 |
+
<asset>
|
| 99 |
+
<mesh file="waveshare_mounting_plate_so101_v2.stl"/>
|
| 100 |
+
<mesh file="sts3215_03a_v1.stl"/>
|
| 101 |
+
<mesh file="motor_holder_so101_base_v1.stl"/>
|
| 102 |
+
<mesh file="wrist_roll_follower_so101_v1.stl"/>
|
| 103 |
+
<mesh file="moving_jaw_so101_v1.stl"/>
|
| 104 |
+
<mesh file="base_motor_holder_so101_v1.stl"/>
|
| 105 |
+
<mesh file="upper_arm_so101_v1.stl"/>
|
| 106 |
+
<mesh file="wrist_roll_pitch_so101_v2.stl"/>
|
| 107 |
+
<mesh file="under_arm_so101_v1.stl"/>
|
| 108 |
+
<mesh file="rotation_pitch_so101_v1.stl"/>
|
| 109 |
+
<mesh file="motor_holder_so101_wrist_v1.stl"/>
|
| 110 |
+
<mesh file="sts3215_03a_no_horn_v1.stl"/>
|
| 111 |
+
<mesh file="base_so101_v2.stl"/>
|
| 112 |
+
<mesh file="wrist_camera_mount_so101_v1.stl" scale="0.001 0.001 0.001"/>
|
| 113 |
+
<mesh file="wrist_camera_so101_v1.stl"/>
|
| 114 |
+
<material name="base_motor_holder_so101_v1_material" rgba="0.94 0.94 0.92 1"/>
|
| 115 |
+
<material name="base_so101_v2_material" rgba="0.94 0.94 0.92 1"/>
|
| 116 |
+
<material name="sts3215_03a_v1_material" rgba="0.1 0.1 0.1 1"/>
|
| 117 |
+
<material name="waveshare_mounting_plate_so101_v2_material" rgba="0.94 0.94 0.92 1"/>
|
| 118 |
+
<material name="motor_holder_so101_base_v1_material" rgba="0.94 0.94 0.92 1"/>
|
| 119 |
+
<material name="rotation_pitch_so101_v1_material" rgba="0.94 0.94 0.92 1"/>
|
| 120 |
+
<material name="upper_arm_so101_v1_material" rgba="0.94 0.94 0.92 1"/>
|
| 121 |
+
<material name="under_arm_so101_v1_material" rgba="0.94 0.94 0.92 1"/>
|
| 122 |
+
<material name="motor_holder_so101_wrist_v1_material" rgba="0.94 0.94 0.92 1"/>
|
| 123 |
+
<material name="sts3215_03a_no_horn_v1_material" rgba="0.1 0.1 0.1 1.0"/>
|
| 124 |
+
<material name="wrist_roll_pitch_so101_v2_material" rgba="0.94 0.94 0.92 1"/>
|
| 125 |
+
<material name="wrist_roll_follower_so101_v1_material" rgba="0.94 0.94 0.92 1"/>
|
| 126 |
+
<material name="moving_jaw_so101_v1_material" rgba="0.94 0.94 0.92 1"/>
|
| 127 |
+
<material name="wrist_camera_mount_so101_v1_material" rgba="0.94 0.94 0.92 1"/>
|
| 128 |
+
<material name="wrist_camera_so101_v1_material" rgba="0.1 0.1 0.1 1"/>
|
| 129 |
+
</asset>
|
| 130 |
+
<actuator>
|
| 131 |
+
<position class="sts3215" name="shoulder_pan" joint="shoulder_pan" forcerange="-3.35 3.35" ctrlrange="-1.91986 1.91986"/>
|
| 132 |
+
<position class="sts3215" name="shoulder_lift" joint="shoulder_lift" forcerange="-3.35 3.35" ctrlrange="-1.74533 1.74533"/>
|
| 133 |
+
<position class="sts3215" name="elbow_flex" joint="elbow_flex" forcerange="-3.35 3.35" ctrlrange="-1.69 1.69"/>
|
| 134 |
+
<position class="sts3215" name="wrist_flex" joint="wrist_flex" forcerange="-3.35 3.35" ctrlrange="-1.65806 1.65806"/>
|
| 135 |
+
<position class="sts3215" name="wrist_roll" joint="wrist_roll" forcerange="-3.35 3.35" ctrlrange="-2.74385 2.84121"/>
|
| 136 |
+
<position class="sts3215" name="gripper" joint="gripper" forcerange="-3.35 3.35" ctrlrange="-0.17453 1.74533"/>
|
| 137 |
+
</actuator>
|
| 138 |
+
<equality/>
|
| 139 |
+
</mujoco>
|