tfrere HF Staff Cursor commited on
Commit
c8f39b4
Β·
1 Parent(s): 094478c

feat: standalone Microduck RL playground (MuJoCo WASM + ONNX policies)

Browse files
Files changed (50) hide show
  1. .gitattributes +1 -0
  2. README.md +39 -4
  3. duck.js +335 -0
  4. index.html +173 -17
  5. policies/BEST_alpha_sitstand.onnx +3 -0
  6. policies/BEST_alpha_walking.onnx +3 -0
  7. policies/roulade.onnx +3 -0
  8. rl.js +518 -0
  9. robot/mjlab/kinematics.json +2013 -0
  10. robot/mjlab/meshes/ankle_left.stl +3 -0
  11. robot/mjlab/meshes/ankle_right.stl +3 -0
  12. robot/mjlab/meshes/banana_pcb_locker.stl +3 -0
  13. robot/mjlab/meshes/bearing_roll.stl +3 -0
  14. robot/mjlab/meshes/bottom_head_shell.stl +3 -0
  15. robot/mjlab/meshes/elec_rpi_robot_hat_pcb.stl +3 -0
  16. robot/mjlab/meshes/face_part.stl +3 -0
  17. robot/mjlab/meshes/foot_left.stl +3 -0
  18. robot/mjlab/meshes/foot_right.stl +3 -0
  19. robot/mjlab/meshes/hip_l.stl +3 -0
  20. robot/mjlab/meshes/jaw.stl +3 -0
  21. robot/mjlab/meshes/jaw_soft.stl +3 -0
  22. robot/mjlab/meshes/left_shell.stl +3 -0
  23. robot/mjlab/meshes/leg.stl +3 -0
  24. robot/mjlab/meshes/lens.stl +3 -0
  25. robot/mjlab/meshes/m12_lens_holder.stl +3 -0
  26. robot/mjlab/meshes/motor_support.stl +3 -0
  27. robot/mjlab/meshes/neck.stl +3 -0
  28. robot/mjlab/meshes/neck_pitch.stl +3 -0
  29. robot/mjlab/meshes/noenoeil.stl +3 -0
  30. robot/mjlab/meshes/np_f970.stl +3 -0
  31. robot/mjlab/meshes/pcb__raspberry_pi_zero_2_w.stl +3 -0
  32. robot/mjlab/meshes/power_support.stl +3 -0
  33. robot/mjlab/meshes/right_shell.stl +3 -0
  34. robot/mjlab/meshes/seeed_bearing__configuration__22x16x4.stl +3 -0
  35. robot/mjlab/meshes/seeed_bearing__configuration_default.stl +3 -0
  36. robot/mjlab/meshes/soft_mouth_top.stl +3 -0
  37. robot/mjlab/meshes/sole_left.stl +3 -0
  38. robot/mjlab/meshes/sole_right.stl +3 -0
  39. robot/mjlab/meshes/speaker.stl +3 -0
  40. robot/mjlab/meshes/top_head_shell.stl +3 -0
  41. robot/mjlab/meshes/trunk_base.stl +3 -0
  42. robot/mjlab/meshes/upper_leg_left.stl +3 -0
  43. robot/mjlab/meshes/upper_leg_right.stl +3 -0
  44. robot/mjlab/meshes/upper_leg_rigidity_plate.stl +3 -0
  45. robot/mjlab/meshes/xl330.stl +3 -0
  46. robot/mjlab/meshes/yaw2roll.stl +3 -0
  47. robot/mjlab/meshes/yaw_roll_motion.stl +3 -0
  48. robot/mjlab/robot_allcollisions.xml +434 -0
  49. style.css +0 -28
  50. variants.js +212 -0
.gitattributes CHANGED
@@ -33,4 +33,5 @@ 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
 
36
  *.jpg 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
+ *.stl filter=lfs diff=lfs merge=lfs -text
37
  *.jpg filter=lfs diff=lfs merge=lfs -text
README.md CHANGED
@@ -1,10 +1,45 @@
1
  ---
2
  title: Microduck Sandbox
3
- emoji: πŸ†
4
- colorFrom: gray
5
- colorTo: indigo
6
  sdk: static
7
  pinned: false
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
  title: Microduck Sandbox
3
+ emoji: 🐀
4
+ colorFrom: yellow
5
+ colorTo: gray
6
  sdk: static
7
  pinned: false
8
  ---
9
 
10
+ # Microduck RL playground
11
+
12
+ Real trained RL policies for the Microduck robot, running fully in the
13
+ browser: MuJoCo compiled to WebAssembly steps the physics, onnxruntime-web
14
+ runs the policy network at 50 Hz. No server, no backend.
15
+
16
+ ## Policies
17
+
18
+ | Button | Checkpoint | What it does |
19
+ |--------|-----------|--------------|
20
+ | Run | `BEST_alpha_walking.onnx` | Velocity-tracking locomotion (arrows / WASD to steer) |
21
+ | Sit | `BEST_alpha_sitstand.onnx` | Sits down on its hull, stands back up |
22
+ | Roulade | `roulade.onnx` | Rolls over and recovers |
23
+
24
+ Policies and MJCF model from
25
+ [apirrone/microduck_runtime](https://github.com/apirrone/microduck_runtime)
26
+ and [apirrone/mjlab_microduck](https://github.com/apirrone/mjlab_microduck).
27
+
28
+ ## Controls
29
+
30
+ - Arrow up / down: forward / back
31
+ - Arrow left / right: turn
32
+ - A / E: strafe
33
+ - R: reset
34
+ - Drag to orbit, scroll to zoom
35
+ - Colour dots: repaint the duck (it quacks)
36
+
37
+ ## How it works
38
+
39
+ - `rl.js` fetches the MJCF (`robot_allcollisions.xml`), strips the visual
40
+ geoms, injects a floor and a STAND keyframe, and compiles it with the
41
+ official `@mujoco/mujoco` WASM bindings.
42
+ - The 61D observation (gyro, projected gravity, joint pos/vel, last action,
43
+ command) matches `mjlab_microduck/scripts/infer_policy.py`.
44
+ - Rendering is a three.js rig built from `kinematics.json` + decimated STL
45
+ meshes, driven directly from MuJoCo `qpos`.
duck.js ADDED
@@ -0,0 +1,335 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Standalone JS port of microduck_app's src/duck/kinematics.ts.
2
+ // Loads kinematics.json (built from the MJCF by build_kinematics.py) and
3
+ // builds an Object3D tree with one Group per body. Joints become per-body
4
+ // local rotations driven by setJointAngles / setJoint.
5
+
6
+ import * as THREE from "three";
7
+ import { STLLoader } from "three/addons/loaders/STLLoader.js";
8
+ import { mergeVertices, toCreasedNormals } from "three/addons/utils/BufferGeometryUtils.js";
9
+
10
+ // Cache-busting version appended to every STL request. python http.server
11
+ // sends no Cache-Control, so browsers apply heuristic caching keyed on
12
+ // Last-Modified and can keep serving stale mesh bytes even after the files
13
+ // change on disk. Bump this whenever the mesh files are regenerated.
14
+ export const MESH_VERSION = "8";
15
+
16
+ // Default model directory: the complete mjlab export (converted from
17
+ // robot_walk.xml by tools/mjcf_to_kinematics.py). robot/v1.5/ and
18
+ // robot/alpha/ stay on disk for reference but the code no longer
19
+ // targets them.
20
+ export const MODEL_DIR = "./robot/mjlab";
21
+
22
+ // Meshes fully occluded inside the shells at every demo camera angle,
23
+ // verified empirically by per-mesh pixel-diff (front 3/4, back, low
24
+ // front close-up): hiding each changes exactly 0 pixels. Skipped at
25
+ // load time so their bytes are never fetched. Load everything with
26
+ // `?all=1` for debugging.
27
+ const HIDDEN_MESHES = new Set([]);
28
+
29
+ export async function loadKinematics(url) {
30
+ // Same cache-buster as the STLs: force-cache would otherwise keep
31
+ // serving a stale kinematics.json after the mesh list changes.
32
+ const r = await fetch(`${url}?v=${MESH_VERSION}`, { cache: "force-cache" });
33
+ if (!r.ok) throw new Error(`kinematics fetch ${r.status}`);
34
+ const k = await r.json();
35
+ // Full-resolution meshes by default (the reduction will be redone
36
+ // interactively in Blender later). `?lite=1` opts into a decimated
37
+ // meshes-lite/ sibling if present.
38
+ if (new URLSearchParams(location.search).get("lite") === "1") {
39
+ k.mesh_dir = k.mesh_dir.replace(/\/meshes$/, "/meshes-lite");
40
+ }
41
+ return k;
42
+ }
43
+
44
+ // Materials are PBR (MeshStandardMaterial), same family as the Reachy
45
+ // mobile app's glb viewer. Optional silhouette outlines via three's
46
+ // OutlineEffect - see main.js.
47
+
48
+ export async function buildRig(k, opts = {}) {
49
+ // placer holds world-space position + yaw (no axis-conversion).
50
+ const placer = new THREE.Group();
51
+ placer.name = "duck_placer";
52
+ // root applies the MJCF +Z up -> three.js +Y up convention fix.
53
+ const root = new THREE.Group();
54
+ root.name = "duck_root";
55
+ root.rotation.x = -Math.PI / 2;
56
+ placer.add(root);
57
+
58
+ const bodies = new Map();
59
+ const joints = new Map();
60
+ const loader = new STLLoader();
61
+
62
+ // STL files carry per-facet normals, which shade as a faceted mess.
63
+ // Weld vertices, then rebuild normals with a crease angle: curved
64
+ // surfaces smooth out, machined edges stay hard - the cel look needs
65
+ // both. Two scale/attribute pitfalls here:
66
+ // - mergeVertices hashes ALL attributes, so the per-facet STL normals
67
+ // must be dropped first or coincident vertices never merge.
68
+ // - toCreasedNormals groups vertices on a fixed 0.01-unit hash grid.
69
+ // Our meshes are in meters, so that grid is 1 cm: normals of vertices
70
+ // up to a centimeter apart get averaged together, which shades like a
71
+ // heavily decimated mesh. Scaling to mm makes the grid 10 um.
72
+ const CREASE = Math.PI / 5; // 36 deg
73
+ const meshCache = new Map();
74
+ const loadMesh = (name) => {
75
+ if (!meshCache.has(name)) {
76
+ meshCache.set(
77
+ name,
78
+ loader.loadAsync(`${k.mesh_dir}/${name}?v=${MESH_VERSION}`).then((raw) => {
79
+ raw.deleteAttribute("normal");
80
+ const welded = mergeVertices(raw, 1e-4);
81
+ welded.scale(1000, 1000, 1000);
82
+ const display = toCreasedNormals(welded, CREASE);
83
+ display.scale(1e-3, 1e-3, 1e-3);
84
+ welded.scale(1e-3, 1e-3, 1e-3);
85
+ return { display, welded };
86
+ }),
87
+ );
88
+ }
89
+ return meshCache.get(name);
90
+ };
91
+
92
+ for (const b of k.bodies) {
93
+ const g = new THREE.Group();
94
+ g.name = b.name;
95
+ g.position.set(b.pos[0], b.pos[1], b.pos[2]);
96
+ g.quaternion.set(b.quat[1], b.quat[2], b.quat[3], b.quat[0]);
97
+ bodies.set(b.name, g);
98
+ }
99
+ for (const b of k.bodies) {
100
+ const g = bodies.get(b.name);
101
+ if (b.parent && bodies.has(b.parent)) bodies.get(b.parent).add(g);
102
+ else root.add(g);
103
+ }
104
+ for (const b of k.bodies) {
105
+ if (!b.joint || (b.joint.type && b.joint.type !== "hinge")) continue;
106
+ const g = bodies.get(b.name);
107
+ joints.set(b.joint.name, {
108
+ body: g,
109
+ axis: new THREE.Vector3(...b.joint.axis).normalize(),
110
+ baseQuat: g.quaternion.clone(),
111
+ range: b.joint.range ?? null,
112
+ });
113
+ }
114
+
115
+ // Cache materials by their resolved PBR props so identical parts share
116
+ // one GPU material instance.
117
+ const matCache = new Map();
118
+ // Optional (meshName, bodyName, rgba) -> material spec hook. A spec is
119
+ // { color: [r,g,b], roughness, metalness, opacity? }; plain rgba arrays
120
+ // are also accepted for backwards compat.
121
+ const materialForMesh = opts.materialForMesh ?? null;
122
+ const matFor = (spec) => {
123
+ const color = spec.color;
124
+ const roughness = spec.roughness ?? 0.5;
125
+ const metalness = spec.metalness ?? 0.0;
126
+ const opacity = spec.opacity ?? 1;
127
+ const key = `${color.join(",")}|${roughness}|${metalness}|${opacity}`;
128
+ const cached = matCache.get(key);
129
+ if (cached) return cached;
130
+ const m = new THREE.MeshStandardMaterial({
131
+ color: new THREE.Color(...color),
132
+ roughness,
133
+ metalness,
134
+ transparent: opacity < 1,
135
+ opacity,
136
+ });
137
+ matCache.set(key, m);
138
+ return m;
139
+ };
140
+ const toSpec = (v, fallbackRgba) => {
141
+ if (!v) return { color: fallbackRgba.slice(0, 3), opacity: fallbackRgba[3] ?? 1 };
142
+ if (Array.isArray(v)) return { color: v.slice(0, 3), opacity: v[3] ?? 1 };
143
+ return v;
144
+ };
145
+
146
+ // Optional interior ink lines: hard edges above the threshold angle
147
+ // drawn as line segments, comic style. Cached per mesh file.
148
+ const inkOpts = opts.inkEdges ?? null;
149
+ const inkMat = inkOpts
150
+ ? new THREE.LineBasicMaterial({
151
+ color: inkOpts.color ?? 0x0a0a0e,
152
+ transparent: true,
153
+ opacity: inkOpts.opacity ?? 0.55,
154
+ })
155
+ : null;
156
+ const edgeCache = new Map();
157
+ const edgesFor = (name, welded) => {
158
+ if (!edgeCache.has(name)) {
159
+ edgeCache.set(name, new THREE.EdgesGeometry(welded, inkOpts.threshold ?? 40));
160
+ }
161
+ return edgeCache.get(name);
162
+ };
163
+
164
+ const pending = [];
165
+ const loadAll = new URLSearchParams(location.search).get("all") === "1";
166
+ // The MJCF lists a few geoms twice with identical transforms (visual +
167
+ // collision copies of power_support, soles, legs); drawing both would
168
+ // only z-fight, so exact duplicates are skipped.
169
+ const seenGeoms = new Set();
170
+ for (const b of k.bodies) {
171
+ const g = bodies.get(b.name);
172
+ if (!g) continue;
173
+ for (const geom of b.geoms) {
174
+ if (geom.type && geom.type !== "mesh") continue;
175
+ if (!geom.mesh) continue;
176
+ if (!loadAll && HIDDEN_MESHES.has(geom.mesh)) continue;
177
+ const dupKey = `${b.name}|${geom.mesh}|${geom.pos}|${geom.quat}`;
178
+ if (seenGeoms.has(dupKey)) continue;
179
+ seenGeoms.add(dupKey);
180
+ pending.push(
181
+ loadMesh(geom.mesh).then(({ display, welded }) => {
182
+ const rgba = geom.color
183
+ ? [geom.color[0], geom.color[1], geom.color[2], geom.color[3] ?? 1]
184
+ : [0.85, 0.85, 0.85, 1];
185
+ const spec = toSpec(materialForMesh?.(geom.mesh, b.name, rgba), rgba);
186
+ const m = new THREE.Mesh(display, matFor(spec));
187
+ // Mesh filename tag so callers can re-skin materials in place
188
+ // (survives cloneRig: Object3D.copy deep-copies userData).
189
+ m.userData.meshName = geom.mesh;
190
+ if (geom.pos) m.position.set(...geom.pos);
191
+ if (geom.quat) m.quaternion.set(geom.quat[1], geom.quat[2], geom.quat[3], geom.quat[0]);
192
+ g.add(m);
193
+ if (inkMat) {
194
+ const lines = new THREE.LineSegments(edgesFor(geom.mesh, welded), inkMat);
195
+ lines.position.copy(m.position);
196
+ lines.quaternion.copy(m.quaternion);
197
+ g.add(lines);
198
+ }
199
+ }),
200
+ );
201
+ }
202
+ }
203
+ await Promise.all(pending);
204
+
205
+ const rig = { placer, root, bodies, joints };
206
+ setupJawPivot(rig);
207
+ return rig;
208
+ }
209
+
210
+ // ── Jaw hinge ───────────────────────────────────────────────────────────
211
+ // The mjlab model has no passive jaw joints: jaw.stl / jaw_soft.stl are
212
+ // rigid geoms of the head body (named "jaw_soft" in the MJCF, it carries
213
+ // the head_roll joint). The quack re-creates the hinge in JS: both jaw
214
+ // meshes are reparented into a "jaw_pivot" group
215
+ // whose origin sits on the rear top edge of their combined bounding box
216
+ // (the physical hinge line), and setJawOpen rotates that pivot about the
217
+ // robot's left-right axis so the beak tip swings down.
218
+ const JAW_MESH_NAMES = new Set(["jaw.stl", "jaw_soft.stl"]);
219
+ export const JAW_MAX_OPEN = 0.32; // rad at openness 1
220
+
221
+ function setupJawPivot(rig) {
222
+ const meshes = [];
223
+ rig.root.traverse((o) => {
224
+ if (o.isMesh && JAW_MESH_NAMES.has(o.userData.meshName)) meshes.push(o);
225
+ });
226
+ if (!meshes.length) return;
227
+ const body = meshes[0].parent;
228
+ // The placer is still untransformed right after buildRig, so world
229
+ // coords == placer coords here: robot forward is +X, up is +Y.
230
+ rig.placer.updateWorldMatrix(true, true);
231
+ const box = new THREE.Box3();
232
+ for (const m of meshes) box.expandByObject(m);
233
+ const hingeW = new THREE.Vector3(box.min.x, box.max.y, (box.min.z + box.max.z) / 2);
234
+ // +angle about -Z rotates the +X beak tip toward -Y (down).
235
+ const axisW = new THREE.Vector3(0, 0, -1);
236
+ const bodyQuatInv = body.getWorldQuaternion(new THREE.Quaternion()).invert();
237
+ const hingeL = body.worldToLocal(hingeW.clone());
238
+ const axisL = axisW.applyQuaternion(bodyQuatInv).normalize();
239
+ const pivot = new THREE.Group();
240
+ pivot.name = "jaw_pivot";
241
+ pivot.position.copy(hingeL);
242
+ // Plain array so Object3D.copy's JSON userData clone preserves it.
243
+ pivot.userData.jawAxis = axisL.toArray();
244
+ body.add(pivot);
245
+ for (const m of meshes) {
246
+ m.position.sub(hingeL);
247
+ pivot.add(m);
248
+ }
249
+ }
250
+
251
+ // Open the beak: 0 = closed, 1 = fully open (JAW_MAX_OPEN rad). The pivot
252
+ // is resolved lazily by name so clones from cloneRig work transparently.
253
+ const _jawAxis = new THREE.Vector3();
254
+ export function setJawOpen(rig, open) {
255
+ if (rig._jawPivot === undefined) {
256
+ rig._jawPivot = rig.placer.getObjectByName("jaw_pivot") ?? null;
257
+ }
258
+ const pivot = rig._jawPivot;
259
+ if (!pivot) return;
260
+ _jawAxis.fromArray(pivot.userData.jawAxis);
261
+ pivot.quaternion.setFromAxisAngle(_jawAxis, JAW_MAX_OPEN * open);
262
+ }
263
+
264
+ // Set one named joint, clamped to its MJCF range when known.
265
+ export function setJoint(rig, name, angle) {
266
+ const j = rig.joints.get(name);
267
+ if (!j) return;
268
+ let a = angle;
269
+ if (j.range) a = Math.min(j.range[1], Math.max(j.range[0], a));
270
+ const rot = _q.setFromAxisAngle(j.axis, a);
271
+ j.body.quaternion.copy(j.baseQuat).multiply(rot);
272
+ }
273
+ const _q = new THREE.Quaternion();
274
+
275
+ export function applyPose(rig, pose) {
276
+ for (const [name, ang] of Object.entries(pose)) setJoint(rig, name, ang);
277
+ }
278
+
279
+ // Deep-clone a built rig without re-parsing the STL files: Object3D.clone
280
+ // shares geometry and materials, so N clones cost almost nothing on top of
281
+ // the first buildRig. The bodies/joints maps are rebuilt by looking up the
282
+ // cloned nodes by name (body names are unique in the MJCF).
283
+ export function cloneRig(rig) {
284
+ const placer = rig.placer.clone(true);
285
+ const root = placer.getObjectByName("duck_root");
286
+ const bodies = new Map();
287
+ for (const name of rig.bodies.keys()) {
288
+ bodies.set(name, placer.getObjectByName(name));
289
+ }
290
+ const joints = new Map();
291
+ for (const [name, j] of rig.joints) {
292
+ joints.set(name, {
293
+ body: placer.getObjectByName(j.body.name),
294
+ axis: j.axis, // read-only, safe to share
295
+ baseQuat: j.baseQuat.clone(),
296
+ range: j.range,
297
+ });
298
+ }
299
+ return { placer, root, bodies, joints };
300
+ }
301
+
302
+ // Ground using the whole rig's bounding box (sitting pose folds the legs
303
+ // under the trunk, so the feet are not the lowest point).
304
+ const _box = new THREE.Box3();
305
+ export function groundFullBody(rig, floorY = 0) {
306
+ rig.placer.updateWorldMatrix(true, true);
307
+ _box.setFromObject(rig.placer);
308
+ if (!Number.isFinite(_box.min.y)) return 0;
309
+ rig.placer.position.y += floorY - _box.min.y;
310
+ return floorY - _box.min.y;
311
+ }
312
+
313
+ // "SIT" keyframe. The mjlab model shares alpha's conventions (same
314
+ // onshape-to-robot pipeline): neck_pitch range max is 1.0472, so
315
+ // neck_pitch sits just under it (headroom for the breathing oscillation,
316
+ // +-0.025) and head_pitch compensates to keep the head level-ish with a
317
+ // slight upward tilt toward the camera. NOTE: head_pitch sign is
318
+ // inverted vs v1.5 (positive = head down), so the compensation is
319
+ // positive here (verified visually).
320
+ export const SITTING_POSE = {
321
+ left_hip_yaw: 0.0,
322
+ left_hip_roll: 0.0,
323
+ left_hip_pitch: -0.5236,
324
+ left_knee: 1.0472,
325
+ left_ankle: 0.0,
326
+ neck_pitch: 1.02,
327
+ head_pitch: 0.9,
328
+ head_yaw: 0.0,
329
+ head_roll: 0.0,
330
+ right_hip_yaw: 0.0,
331
+ right_hip_roll: 0.0,
332
+ right_hip_pitch: 0.5236,
333
+ right_knee: -1.0472,
334
+ right_ankle: 0.0,
335
+ };
index.html CHANGED
@@ -1,19 +1,175 @@
1
  <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
  </html>
 
1
  <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
6
+ <title>Microduck RL playground</title>
7
+ <style>
8
+ :root {
9
+ --ink: #08080c;
10
+ --yellow: #ffd23f;
11
+ }
12
+ * { margin: 0; padding: 0; box-sizing: border-box; }
13
+ html, body { height: 100%; }
14
+ body {
15
+ background: var(--ink);
16
+ color: rgba(255, 255, 255, 0.85);
17
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
18
+ overflow: hidden;
19
+ }
20
+ #scene { position: fixed; inset: 0; }
21
+ #scene canvas { display: block; }
22
+ .halftone {
23
+ position: fixed;
24
+ inset: 0;
25
+ pointer-events: none;
26
+ background-image: radial-gradient(rgba(255, 255, 255, 0.05) 1px, transparent 1.5px);
27
+ background-size: 21px 21px;
28
+ mask-image: linear-gradient(to bottom, transparent 30%, black 100%);
29
+ -webkit-mask-image: linear-gradient(to bottom, transparent 30%, black 100%);
30
+ }
31
+ header {
32
+ position: fixed;
33
+ top: 2.6rem;
34
+ left: 0;
35
+ right: 0;
36
+ text-align: center;
37
+ pointer-events: none;
38
+ }
39
+ header .facts {
40
+ font-size: 0.72rem;
41
+ font-weight: 600;
42
+ letter-spacing: 0.14em;
43
+ text-transform: uppercase;
44
+ color: rgba(255, 255, 255, 0.55);
45
+ }
46
+ header .facts b { color: var(--yellow); font-weight: 600; }
47
+ header h1 {
48
+ margin-top: 0.5rem;
49
+ font-size: clamp(1.6rem, 4vw, 2.6rem);
50
+ font-weight: 700;
51
+ letter-spacing: -0.03em;
52
+ color: #fff;
53
+ }
54
+ header h1 #verb { color: var(--yellow); }
55
+ #loading {
56
+ position: fixed;
57
+ inset: 0;
58
+ display: flex;
59
+ align-items: center;
60
+ justify-content: center;
61
+ font-size: 0.8rem;
62
+ letter-spacing: 0.14em;
63
+ text-transform: uppercase;
64
+ color: rgba(255, 255, 255, 0.45);
65
+ text-align: center;
66
+ padding: 0 2rem;
67
+ z-index: 20;
68
+ }
69
+ /* Bottom control bar */
70
+ #hud {
71
+ position: fixed;
72
+ bottom: 1.4rem;
73
+ left: 0;
74
+ right: 0;
75
+ display: flex;
76
+ flex-direction: column;
77
+ align-items: center;
78
+ gap: 0.7rem;
79
+ z-index: 10;
80
+ }
81
+ #hud .row {
82
+ display: flex;
83
+ align-items: center;
84
+ gap: 0.5rem;
85
+ }
86
+ #hud button {
87
+ appearance: none;
88
+ border: 1px solid rgba(255, 255, 255, 0.14);
89
+ background: rgba(255, 255, 255, 0.04);
90
+ color: rgba(255, 255, 255, 0.8);
91
+ font: inherit;
92
+ font-size: 0.72rem;
93
+ font-weight: 600;
94
+ letter-spacing: 0.1em;
95
+ text-transform: uppercase;
96
+ padding: 0.5rem 1rem;
97
+ border-radius: 999px;
98
+ cursor: pointer;
99
+ transition: background 0.15s ease, color 0.15s ease, transform 0.15s ease;
100
+ }
101
+ #hud button:hover { background: rgba(255, 255, 255, 0.1); }
102
+ #hud button.on {
103
+ background: #ffc61e;
104
+ border-color: #ffc61e;
105
+ color: #08080c;
106
+ }
107
+ /* Colour swatches */
108
+ #swatches { gap: 0.6rem; }
109
+ #swatches button {
110
+ width: 1.5rem;
111
+ height: 1.5rem;
112
+ padding: 0;
113
+ border-radius: 50%;
114
+ border: 2px solid rgba(255, 255, 255, 0.18);
115
+ }
116
+ #swatches button:hover { transform: scale(1.15); }
117
+ #swatches button.on {
118
+ border-color: #fff;
119
+ transform: scale(1.15);
120
+ }
121
+ #hud .keys {
122
+ font-size: 0.7rem;
123
+ letter-spacing: 0.08em;
124
+ color: rgba(255, 255, 255, 0.4);
125
+ }
126
+ #hud .keys b { color: rgba(255, 255, 255, 0.75); font-weight: 600; }
127
+ #stats {
128
+ position: fixed;
129
+ top: 1.4rem;
130
+ right: 1.6rem;
131
+ z-index: 10;
132
+ font-size: 0.68rem;
133
+ letter-spacing: 0.08em;
134
+ text-align: right;
135
+ color: rgba(255, 255, 255, 0.4);
136
+ line-height: 1.7;
137
+ font-variant-numeric: tabular-nums;
138
+ }
139
+ #stats b { color: var(--yellow); font-weight: 600; }
140
+ </style>
141
+ </head>
142
+ <body>
143
+ <div id="scene"></div>
144
+ <div class="halftone"></div>
145
+ <header>
146
+ <div class="facts"><b>Microduck</b> &middot; real trained policies &middot; MuJoCo + ONNX, live in your browser</div>
147
+ <h1>This one actually learned to <span id="verb">run</span></h1>
148
+ </header>
149
+ <div id="stats"></div>
150
+ <div id="hud" hidden>
151
+ <div class="row">
152
+ <button id="btn-walk" class="on">Run</button>
153
+ <button id="btn-sit">Sit</button>
154
+ <button id="btn-roulade">Roulade</button>
155
+ <button id="btn-reset">Reset</button>
156
+ </div>
157
+ <div class="row" id="swatches"></div>
158
+ <div class="keys">
159
+ <b>&uarr;/&darr;</b> forward / back &nbsp; <b>&larr;/&rarr;</b> turn &nbsp;
160
+ <b>A/E</b> strafe &nbsp; <b>R</b> reset &nbsp; drag to orbit
161
+ </div>
162
+ </div>
163
+ <div id="loading">Compiling physics&hellip;</div>
164
+
165
+ <script type="importmap">
166
+ {
167
+ "imports": {
168
+ "three": "https://cdn.jsdelivr.net/npm/three@0.164.1/build/three.module.js",
169
+ "three/addons/": "https://cdn.jsdelivr.net/npm/three@0.164.1/examples/jsm/"
170
+ }
171
+ }
172
+ </script>
173
+ <script type="module" src="./rl.js"></script>
174
+ </body>
175
  </html>
policies/BEST_alpha_sitstand.onnx ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:c6c40e35e726eabd803d633e090d112994f469921152448367953fbaf9799bc8
3
+ size 793695
policies/BEST_alpha_walking.onnx ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:f5604bce126b01ebf341b0d1de2e409cb969895106b7715c27cefacccecd9972
3
+ size 793705
policies/roulade.onnx ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:3d60da08fc13f29c1b57f41977aa898132c0d60042100149d8e775affcbca32b
3
+ size 793685
rl.js ADDED
@@ -0,0 +1,518 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Microduck RL playground: the REAL trained policies, not a procedural waddle.
2
+ //
3
+ // Physics runs in MuJoCo compiled to WebAssembly (the official
4
+ // @mujoco/mujoco bindings), stepping the same MJCF the policies were
5
+ // trained on (apirrone/mjlab_microduck). The controller is one of the
6
+ // exported ONNX checkpoints from apirrone/microduck_runtime, executed with
7
+ // onnxruntime-web at 50 Hz (timestep 0.005 s, decimation 4) - exactly the
8
+ // loop from mjlab_microduck/scripts/infer_policy.py.
9
+ //
10
+ // Obs layout (61D, "new-cmd-obs" flavor, from the ONNX metadata):
11
+ // [base_ang_vel(3), projected_gravity(3), joint_pos(14), joint_vel(14),
12
+ // last_action(14), command(13)]
13
+ // command = [vx, vy, wz, head_pose(4), body_pose(6)]; for the sitstand
14
+ // policy, command[0] is the posture flag (1 = sit, 0 = stand).
15
+ // Action (14) = joint position targets relative to the default pose.
16
+
17
+ import * as THREE from "three";
18
+ import { OrbitControls } from "three/addons/controls/OrbitControls.js";
19
+ import { RoomEnvironment } from "three/addons/environments/RoomEnvironment.js";
20
+ import { buildRig, loadKinematics, setJoint, setJawOpen, MODEL_DIR, MESH_VERSION } from "./duck.js";
21
+ import { VARIANTS, VARIANT_NAMES, materialHookFor, randomVariantName, applyVariant, specToHex } from "./variants.js";
22
+ import loadMujoco from "https://cdn.jsdelivr.net/npm/@mujoco/mujoco@3.11.0/mujoco.js";
23
+ import * as ort from "https://cdn.jsdelivr.net/npm/onnxruntime-web@1.27.0/dist/ort.min.mjs";
24
+
25
+ ort.env.wasm.wasmPaths = "https://cdn.jsdelivr.net/npm/onnxruntime-web@1.27.0/dist/";
26
+ ort.env.wasm.numThreads = 1; // static hosting sends no COOP/COEP headers
27
+
28
+ const POLICY_DIR = "./policies";
29
+ const POLICIES = {
30
+ walk: `${POLICY_DIR}/BEST_alpha_walking.onnx`,
31
+ sitstand: `${POLICY_DIR}/BEST_alpha_sitstand.onnx`,
32
+ roulade: `${POLICY_DIR}/roulade.onnx`,
33
+ };
34
+
35
+ // From the ONNX metadata (identical for all alpha policies) and the STAND
36
+ // keyframe in mjlab's scene_walk.xml. Order matches the actuators in
37
+ // the MJCF.
38
+ const JOINT_NAMES = [
39
+ "left_hip_yaw", "left_hip_roll", "left_hip_pitch", "left_knee", "left_ankle",
40
+ "neck_pitch", "head_pitch", "head_yaw", "head_roll",
41
+ "right_hip_yaw", "right_hip_roll", "right_hip_pitch", "right_knee", "right_ankle",
42
+ ];
43
+ const DEFAULT_POSE = new Float32Array([
44
+ 0, -0.08726646259971647, -0.457924, -0.004940, 0.452984,
45
+ 0.3490658503988659, 0.3490658503988659, 0, 0,
46
+ 0, 0.08726646259971647, 0.457924, 0.004940, -0.452984,
47
+ ]);
48
+ const NUM_JOINTS = 14;
49
+ const OBS_SIZE = 61;
50
+ const CMD_SIZE = 13;
51
+ const ACTION_SCALE = 1.0;
52
+ const TIMESTEP = 0.005;
53
+ const DECIMATION = 4;
54
+ const CTRL_DT = TIMESTEP * DECIMATION; // 50 Hz
55
+
56
+ // Velocity command limits, same as infer_policy.py's keyboard mapping.
57
+ const VEL_FWD = 0.25, VEL_BACK = -0.2, VEL_LAT = 0.2, VEL_ANG = 1.0;
58
+
59
+ const mount = document.getElementById("scene");
60
+ const loadingEl = document.getElementById("loading");
61
+ const hudEl = document.getElementById("hud");
62
+ const statsEl = document.getElementById("stats");
63
+ const verbEl = document.getElementById("verb");
64
+ const setLoading = (msg) => { loadingEl.textContent = msg; };
65
+
66
+ // Surface boot failures on the page itself: a rejected top-level await
67
+ // otherwise leaves the loading screen up with no visible error.
68
+ window.addEventListener("unhandledrejection", (e) => {
69
+ const msg = e.reason?.stack || e.reason?.message || String(e.reason);
70
+ setLoading(`Boot failed: ${msg}`);
71
+ console.error("[rl] unhandled rejection", e.reason);
72
+ });
73
+ window.addEventListener("error", (e) => {
74
+ setLoading(`Boot failed: ${e.message}`);
75
+ });
76
+ const bootSteps = [];
77
+ const traced = (label, p) => {
78
+ bootSteps.push(label);
79
+ return p.then(
80
+ (v) => {
81
+ bootSteps.splice(bootSteps.indexOf(label), 1);
82
+ if (bootSteps.length) setLoading(`Loading ${bootSteps.join(", ")}\u2026`);
83
+ return v;
84
+ },
85
+ (err) => { console.error(`[rl] ${label} FAILED`, err); throw err; },
86
+ );
87
+ };
88
+
89
+ // ── MJCF preparation ────────────────────────────────────────────────────
90
+ // robot_allcollisions.xml is what infer_policy.py's scene.xml includes: it
91
+ // carries body/shell collision geoms that robot_walk.xml lacks, which the
92
+ // sitstand policy needs (a sit rests the trunk on the ground).
93
+ // The visual meshes are irrelevant to the dynamics: every body carries an
94
+ // explicit <inertial>, and visual geoms have contype=0 conaffinity=0.
95
+ // Stripping them means the MuJoCo VFS only needs the ~10 meshes referenced
96
+ // by collision geoms.
97
+ async function buildPhysicsXml() {
98
+ const src = await (await fetch(`${MODEL_DIR}/robot_allcollisions.xml`)).text();
99
+ const doc = new DOMParser().parseFromString(src, "text/xml");
100
+ for (const g of [...doc.querySelectorAll('geom[class="visual"]')]) g.remove();
101
+ const usedMeshes = new Set(
102
+ [...doc.querySelectorAll("geom[mesh]")].map((g) => g.getAttribute("mesh")),
103
+ );
104
+ for (const m of [...doc.querySelectorAll("asset > mesh")]) {
105
+ const name = m.getAttribute("name") ?? m.getAttribute("file").replace(/\.stl$/i, "");
106
+ if (!usedMeshes.has(name)) m.remove();
107
+ }
108
+ const root = doc.documentElement;
109
+ const el = (tag, attrs) => {
110
+ const e = doc.createElement(tag);
111
+ for (const [k, v] of Object.entries(attrs)) e.setAttribute(k, v);
112
+ return e;
113
+ };
114
+ root.appendChild(el("option", { timestep: String(TIMESTEP) }));
115
+ doc.querySelector("worldbody").appendChild(
116
+ el("geom", { name: "floor", type: "plane", size: "0 0 0.05", pos: "0 0 0" }),
117
+ );
118
+ // STAND keyframe from mjlab's scene_walk.xml (STAND2 pose).
119
+ const qposFree = "0 0 0.12 1 0 0 0";
120
+ const pose14 = Array.from(DEFAULT_POSE).join(" ");
121
+ const kf = doc.createElement("keyframe");
122
+ kf.appendChild(el("key", { name: "STAND", qpos: `${qposFree} ${pose14}`, ctrl: pose14 }));
123
+ root.appendChild(kf);
124
+ const meshFiles = [...doc.querySelectorAll("asset > mesh")].map((m) => m.getAttribute("file"));
125
+ return { xml: new XMLSerializer().serializeToString(doc), meshFiles };
126
+ }
127
+
128
+ // ── Boot physics + policy in parallel with the render rig ──────────────
129
+ setLoading("Loading MuJoCo WASM, policies and meshes\u2026");
130
+
131
+ const [mujoco, { xml, meshFiles }, k] = await Promise.all([
132
+ traced("mujoco wasm", loadMujoco()),
133
+ traced("physics xml", buildPhysicsXml()),
134
+ traced("kinematics", loadKinematics(`${MODEL_DIR}/kinematics.json`)),
135
+ ]);
136
+
137
+ const vfs = new mujoco.MjVFS();
138
+ await Promise.all(
139
+ meshFiles.map(async (f) => {
140
+ // Same cache-busted URL as duck.js so the browser reuses the render
141
+ // meshes instead of downloading the collision subset a second time.
142
+ const buf = await (await fetch(`${MODEL_DIR}/meshes/${f}?v=${MESH_VERSION}`, { cache: "force-cache" })).arrayBuffer();
143
+ // meshdir="assets" in the MJCF, so the compiler looks up "assets/<f>".
144
+ vfs.addBuffer(`assets/${f}`, new Uint8Array(buf));
145
+ }),
146
+ );
147
+
148
+ const sessions = {};
149
+ let currentVariant = randomVariantName();
150
+ const rigPromise = (async () => {
151
+ return buildRig(k, { materialForMesh: materialHookFor(VARIANTS[currentVariant]) });
152
+ })();
153
+ const sessionOpts = { executionProviders: ["wasm"] };
154
+ [sessions.walk, sessions.sitstand, sessions.roulade] = await Promise.all([
155
+ ort.InferenceSession.create(POLICIES.walk, sessionOpts),
156
+ ort.InferenceSession.create(POLICIES.sitstand, sessionOpts),
157
+ ort.InferenceSession.create(POLICIES.roulade, sessionOpts),
158
+ ]);
159
+
160
+ setLoading("Compiling physics\u2026");
161
+ const model = mujoco.MjModel.from_xml_string(xml, vfs);
162
+ const data = new mujoco.MjData(model);
163
+
164
+ // Addresses resolved once. qpos/qvel/sensordata views are re-read at each
165
+ // use: the WASM heap can grow and detach earlier TypedArray views.
166
+ // NOTE: unlike the Python bindings, these accessor fields are plain numbers.
167
+ const qposAdr = JOINT_NAMES.map((n) => model.jnt(n).qposadr);
168
+ const dofAdr = JOINT_NAMES.map((n) => model.jnt(n).dofadr);
169
+ const gyroAdr = model.sensor("imu_ang_vel").adr;
170
+ const trunkId = mujoco.mj_name2id(model, mujoco.mjtObj.mjOBJ_BODY.value, "trunk_base");
171
+ const standKeyId = mujoco.mj_name2id(model, mujoco.mjtObj.mjOBJ_KEY.value, "STAND");
172
+
173
+ let uiReady = false;
174
+ const lastAction = new Float32Array(NUM_JOINTS);
175
+ const obs = new Float32Array(OBS_SIZE);
176
+ const cmd = new Float32Array(CMD_SIZE); // [vx, vy, wz, head(4), body(6)]
177
+ const velCmd = new Float32Array(3); // twist command, driven by held keys
178
+ // Declared before the control loop starts: buildObs checks it to decide
179
+ // between the auto-run default and manual key control.
180
+ const held = new Set();
181
+
182
+ let mode = "walk"; // "walk" | "sitstand" | "roulade"
183
+ let sitFlag = 0;
184
+
185
+ function resetSim() {
186
+ mujoco.mj_resetDataKeyframe(model, data, standKeyId);
187
+ mujoco.mj_forward(model, data);
188
+ lastAction.fill(0);
189
+ sitFlag = 0;
190
+ // Buttons reflect sitFlag; keep them honest after auto-resets.
191
+ if (uiReady) syncButtons();
192
+ }
193
+ resetSim();
194
+
195
+ // ── Observation ─────────────────────────────────────────────────────────
196
+ const _q = new THREE.Quaternion();
197
+ const _g = new THREE.Vector3();
198
+
199
+ function buildObs() {
200
+ const qpos = data.qpos, qvel = data.qvel, sens = data.sensordata;
201
+ let i = 0;
202
+ // base_ang_vel: gyro sensor at the IMU site
203
+ for (let a = 0; a < 3; a++) obs[i++] = sens[gyroAdr + a];
204
+ // projected gravity: world -z rotated into the trunk frame
205
+ const xq = data.body(trunkId).xquat; // [w, x, y, z]
206
+ _q.set(xq[1], xq[2], xq[3], xq[0]).conjugate();
207
+ _g.set(0, 0, -1).applyQuaternion(_q);
208
+ obs[i++] = _g.x; obs[i++] = _g.y; obs[i++] = _g.z;
209
+ for (let j = 0; j < NUM_JOINTS; j++) obs[i++] = qpos[qposAdr[j]] - DEFAULT_POSE[j];
210
+ for (let j = 0; j < NUM_JOINTS; j++) obs[i++] = qvel[dofAdr[j]];
211
+ for (let j = 0; j < NUM_JOINTS; j++) obs[i++] = lastAction[j];
212
+ // command: walking/roulade use the twist; sitstand uses cmd[0] as the
213
+ // posture flag. "Run" means run: with no keys held the walking policy
214
+ // gets a forward velocity by default, keys override it.
215
+ cmd.fill(0, 0, 3);
216
+ if (mode === "walk") {
217
+ cmd[0] = held.size ? velCmd[0] : VEL_FWD;
218
+ cmd[1] = velCmd[1]; cmd[2] = velCmd[2];
219
+ } else if (mode === "roulade") {
220
+ cmd[0] = velCmd[0]; cmd[1] = velCmd[1]; cmd[2] = velCmd[2];
221
+ } else {
222
+ cmd[0] = sitFlag;
223
+ }
224
+ for (let c = 0; c < CMD_SIZE; c++) obs[i++] = cmd[c];
225
+ return obs;
226
+ }
227
+
228
+ // ── Control loop (50 Hz, async because ONNX inference is async) ────────
229
+ let ctrlHz = 0;
230
+ let fallenSince = null;
231
+
232
+ async function controlStep() {
233
+ const feeds = { obs: new ort.Tensor("float32", buildObs(), [1, OBS_SIZE]) };
234
+ const out = await sessions[mode].run(feeds);
235
+ const act = out.actions.data;
236
+ lastAction.set(act);
237
+ const ctrl = data.ctrl;
238
+ for (let j = 0; j < NUM_JOINTS; j++) ctrl[j] = DEFAULT_POSE[j] + act[j] * ACTION_SCALE;
239
+ for (let s = 0; s < DECIMATION; s++) mujoco.mj_step(model, data);
240
+
241
+ // Fall detection: walk/sitstand have no get-up skill, so auto-reset when
242
+ // down for good. Height alone would false-positive on a deep sit, so
243
+ // "fallen" = trunk tilted past ~60 deg (projected gravity z above -0.5)
244
+ // or sunk below the floor, sustained for over a second. The roulade rolls
245
+ // the trunk on purpose and recovers on its own, so it gets a much longer
246
+ // grace window before we call it stuck.
247
+ const z = data.qpos[2];
248
+ const now = performance.now();
249
+ const tipped = obs[5] > -0.5; // projected gravity z, from the last obs
250
+ const graceMs = mode === "roulade" ? 5000 : 1000;
251
+ if (tipped || z < 0.02) {
252
+ fallenSince ??= now;
253
+ if (now - fallenSince > graceMs) { resetSim(); fallenSince = null; }
254
+ } else {
255
+ fallenSince = null;
256
+ }
257
+ }
258
+
259
+ let running = true;
260
+ (async function controlLoop() {
261
+ let next = performance.now();
262
+ let count = 0, hzT0 = next;
263
+ while (running) {
264
+ await controlStep();
265
+ count++;
266
+ const now = performance.now();
267
+ if (now - hzT0 > 500) {
268
+ ctrlHz = (count * 1000) / (now - hzT0);
269
+ count = 0; hzT0 = now;
270
+ }
271
+ next += CTRL_DT * 1000;
272
+ const wait = next - performance.now();
273
+ if (wait > 0) await new Promise((r) => setTimeout(r, wait));
274
+ else next = performance.now(); // fell behind: don't spiral
275
+ }
276
+ })();
277
+
278
+ // ── Rendering (three.js rig driven by qpos) ─────────────────────────────
279
+ const scene = new THREE.Scene();
280
+ scene.background = new THREE.Color(0x08080c);
281
+
282
+ const camera = new THREE.PerspectiveCamera(40, 1, 0.02, 30);
283
+ camera.position.set(0.55, 0.35, 0.7);
284
+
285
+ const renderer = new THREE.WebGLRenderer({ antialias: true });
286
+ renderer.setPixelRatio(Math.min(2, window.devicePixelRatio));
287
+ renderer.toneMapping = THREE.ACESFilmicToneMapping;
288
+ mount.appendChild(renderer.domElement);
289
+
290
+ const pmrem = new THREE.PMREMGenerator(renderer);
291
+ scene.environment = pmrem.fromScene(new RoomEnvironment()).texture;
292
+ scene.environmentIntensity = 0.45;
293
+
294
+ scene.add(new THREE.AmbientLight(0xffffff, 0.6));
295
+ const keyLight = new THREE.DirectionalLight(0xffffff, 1.6);
296
+ keyLight.position.set(2, 4, 2);
297
+ scene.add(keyLight);
298
+ const fill = new THREE.DirectionalLight(0xffffff, 0.4);
299
+ fill.position.set(-2, 2, 1.5);
300
+ scene.add(fill);
301
+ const rim = new THREE.DirectionalLight(0xffb366, 0.7);
302
+ rim.position.set(0, 3, -2);
303
+ scene.add(rim);
304
+
305
+ function makeFloorTexture() {
306
+ const N = 256;
307
+ const c = document.createElement("canvas");
308
+ c.width = c.height = N;
309
+ const g = c.getContext("2d");
310
+ g.fillStyle = "#131009";
311
+ g.fillRect(0, 0, N, N);
312
+ g.strokeStyle = "rgba(255, 179, 102, 0.05)";
313
+ g.lineWidth = 2;
314
+ g.strokeRect(1, 1, N - 2, N - 2);
315
+ g.fillStyle = "rgba(255, 198, 30, 0.10)";
316
+ for (const x of [0, N]) {
317
+ for (const y of [0, N]) {
318
+ g.beginPath();
319
+ g.arc(x, y, 5, 0, Math.PI * 2);
320
+ g.fill();
321
+ }
322
+ }
323
+ const tex = new THREE.CanvasTexture(c);
324
+ tex.colorSpace = THREE.SRGBColorSpace;
325
+ tex.wrapS = tex.wrapT = THREE.RepeatWrapping;
326
+ tex.repeat.set(48, 48);
327
+ return tex;
328
+ }
329
+ const floor = new THREE.Mesh(
330
+ new THREE.PlaneGeometry(12, 12),
331
+ new THREE.MeshStandardMaterial({ map: makeFloorTexture(), roughness: 0.9, metalness: 0.0 }),
332
+ );
333
+ floor.rotation.x = -Math.PI / 2;
334
+ scene.add(floor);
335
+
336
+ const rig = await rigPromise;
337
+ scene.add(rig.placer);
338
+ const trunkGroup = rig.bodies.get("trunk_base");
339
+
340
+ const controls = new OrbitControls(camera, renderer.domElement);
341
+ controls.enableDamping = true;
342
+ controls.dampingFactor = 0.08;
343
+ controls.minDistance = 0.25;
344
+ controls.maxDistance = 3;
345
+ controls.maxPolarAngle = Math.PI / 2 - 0.03;
346
+
347
+ function resize() {
348
+ const w = mount.clientWidth, h = mount.clientHeight;
349
+ camera.aspect = w / h;
350
+ camera.updateProjectionMatrix();
351
+ renderer.setSize(w, h);
352
+ }
353
+ new ResizeObserver(resize).observe(mount);
354
+ resize();
355
+
356
+ // The rig's root already applies the MJCF Z-up -> three Y-up fix, so the
357
+ // trunk group can take the freejoint pose in raw MJCF coordinates.
358
+ const _target = new THREE.Vector3();
359
+ function syncRig() {
360
+ const qpos = data.qpos;
361
+ trunkGroup.position.set(qpos[0], qpos[1], qpos[2]);
362
+ trunkGroup.quaternion.set(qpos[4], qpos[5], qpos[6], qpos[3]);
363
+ for (let j = 0; j < NUM_JOINTS; j++) setJoint(rig, JOINT_NAMES[j], qpos[qposAdr[j]]);
364
+ // Camera target eases toward the trunk so the duck stays framed while
365
+ // it walks away.
366
+ _target.set(qpos[0], qpos[2], -qpos[1]);
367
+ controls.target.lerp(_target, 0.06);
368
+ }
369
+
370
+ // Quack: a quick jaw flap on every mode/colour change. The jaw isn't a
371
+ // MuJoCo joint (duck.js re-creates the hinge in JS), so this is purely
372
+ // cosmetic and can't upset the policy.
373
+ const QUACK_MS = 480;
374
+ let quackAt = -Infinity;
375
+ const quack = () => { quackAt = performance.now(); };
376
+ function syncJaw() {
377
+ const t = (performance.now() - quackAt) / QUACK_MS;
378
+ setJawOpen(rig, t >= 0 && t < 1 ? Math.sin(Math.PI * t) : 0);
379
+ }
380
+
381
+ function renderStats() {
382
+ const [vx, vy, wz] = velCmd;
383
+ const posture = mode === "walk"
384
+ ? `running policy`
385
+ : mode === "roulade"
386
+ ? `roulade policy`
387
+ : `sitstand policy \u00b7 ${sitFlag ? "sit" : "stand"}`;
388
+ statsEl.innerHTML =
389
+ `<b>${posture}</b><br>` +
390
+ `cmd vx ${vx.toFixed(2)} \u00b7 vy ${vy.toFixed(2)} \u00b7 wz ${wz.toFixed(2)}<br>` +
391
+ `ctrl ${ctrlHz.toFixed(0)} Hz \u00b7 sim t ${data.time.toFixed(1)} s`;
392
+ }
393
+
394
+ function loop() {
395
+ requestAnimationFrame(loop);
396
+ syncRig();
397
+ syncJaw();
398
+ controls.update();
399
+ renderStats();
400
+ renderer.render(scene, camera);
401
+ }
402
+ loadingEl.style.display = "none";
403
+ hudEl.hidden = false;
404
+ loop();
405
+
406
+ // ── Input: hold-to-command keys + HUD buttons ───────────────────────────
407
+ function refreshVelCmd() {
408
+ velCmd[0] = held.has("fwd") ? VEL_FWD : held.has("back") ? VEL_BACK : 0;
409
+ velCmd[1] = held.has("left") ? VEL_LAT : held.has("right") ? -VEL_LAT : 0;
410
+ velCmd[2] = held.has("turnl") ? VEL_ANG : held.has("turnr") ? -VEL_ANG : 0;
411
+ }
412
+
413
+ const KEYMAP = {
414
+ ArrowUp: "fwd", KeyW: "fwd", KeyZ: "fwd",
415
+ ArrowDown: "back", KeyS: "back",
416
+ ArrowLeft: "turnl", ArrowRight: "turnr",
417
+ KeyA: "left", KeyQ: "left", KeyE: "right", KeyD: "right",
418
+ };
419
+
420
+ window.addEventListener("keydown", (e) => {
421
+ if (e.repeat) return;
422
+ if (e.code === "KeyR") { resetSim(); return; }
423
+ const act = KEYMAP[e.code];
424
+ if (!act) return;
425
+ e.preventDefault();
426
+ held.add(act);
427
+ refreshVelCmd();
428
+ });
429
+ window.addEventListener("keyup", (e) => {
430
+ const act = KEYMAP[e.code];
431
+ if (!act) return;
432
+ held.delete(act);
433
+ refreshVelCmd();
434
+ });
435
+ window.addEventListener("blur", () => { held.clear(); refreshVelCmd(); });
436
+
437
+ const btnWalk = document.getElementById("btn-walk");
438
+ const btnSit = document.getElementById("btn-sit");
439
+ const btnRoulade = document.getElementById("btn-roulade");
440
+ document.getElementById("btn-reset").addEventListener("click", resetSim);
441
+
442
+ // The headline verb tracks the active policy.
443
+ const VERBS = { walk: "run", sitstand: "sit", roulade: "roll over" };
444
+
445
+ function setMode(next) {
446
+ quack();
447
+ if (next !== "sit") {
448
+ // Leaving a sit: let the sitstand policy stand the duck back up first.
449
+ if (mode === "sitstand" && sitFlag === 1) {
450
+ sitFlag = 0;
451
+ setTimeout(() => { mode = next; lastAction.fill(0); syncButtons(); }, 2000);
452
+ syncButtons();
453
+ return;
454
+ }
455
+ mode = next;
456
+ lastAction.fill(0);
457
+ } else {
458
+ // Hand over gently: hold the stand under the sitstand policy for a
459
+ // moment before commanding the sit, or the abrupt session switch
460
+ // (walking's action history + instant flag) knocks the duck over.
461
+ mode = "sitstand";
462
+ sitFlag = 0;
463
+ lastAction.fill(0);
464
+ setTimeout(() => {
465
+ if (mode === "sitstand") { sitFlag = 1; syncButtons(); }
466
+ }, 800);
467
+ }
468
+ syncButtons();
469
+ }
470
+ function syncButtons() {
471
+ const sitting = mode === "sitstand" && sitFlag === 1;
472
+ btnWalk.classList.toggle("on", mode === "walk" || (mode === "sitstand" && !sitting));
473
+ btnSit.classList.toggle("on", sitting);
474
+ btnRoulade.classList.toggle("on", mode === "roulade");
475
+ verbEl.textContent = VERBS[mode];
476
+ }
477
+ btnWalk.addEventListener("click", () => setMode("walk"));
478
+ btnSit.addEventListener("click", () => setMode("sit"));
479
+ btnRoulade.addEventListener("click", () => setMode("roulade"));
480
+
481
+ // ── Colour swatches: re-skin the rig live, with a quack ─────────────────
482
+ // One representative colour per variant so the dots read at a glance.
483
+ const SWATCH_SLOT = { classic: "feet", charcoal: "headDome", purple: "feet", blue: "facePlate" };
484
+ const swatchesEl = document.getElementById("swatches");
485
+ const swatchBtns = new Map();
486
+ for (const name of VARIANT_NAMES) {
487
+ const v = VARIANTS[name];
488
+ const b = document.createElement("button");
489
+ b.style.background = specToHex(v[SWATCH_SLOT[name] ?? "bodyShell"]);
490
+ b.setAttribute("aria-label", `${name} colours`);
491
+ b.addEventListener("click", () => {
492
+ if (name === currentVariant) return;
493
+ currentVariant = name;
494
+ applyVariant(rig, name);
495
+ quack();
496
+ syncSwatches();
497
+ });
498
+ swatchesEl.appendChild(b);
499
+ swatchBtns.set(name, b);
500
+ }
501
+ function syncSwatches() {
502
+ for (const [name, b] of swatchBtns) b.classList.toggle("on", name === currentVariant);
503
+ }
504
+ syncSwatches();
505
+ syncButtons();
506
+ uiReady = true;
507
+
508
+ // Deterministic hooks for automated verification (rAF pauses in
509
+ // background tabs, and the control loop is async).
510
+ window.rl = {
511
+ model, data, mujoco,
512
+ get mode() { return mode; },
513
+ get sitFlag() { return sitFlag; },
514
+ buildObs, cmd,
515
+ velCmd, lastAction, resetSim,
516
+ step: async (n = 1) => { for (let i = 0; i < n; i++) await controlStep(); },
517
+ render: () => { syncRig(); renderer.render(scene, camera); },
518
+ };
robot/mjlab/kinematics.json ADDED
@@ -0,0 +1,2013 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "bodies": [
3
+ {
4
+ "name": "trunk_base",
5
+ "parent": null,
6
+ "pos": [
7
+ 0.0,
8
+ 0.0,
9
+ 0.12
10
+ ],
11
+ "quat": [
12
+ 1.0,
13
+ 0.0,
14
+ 0.0,
15
+ 0.0
16
+ ],
17
+ "geoms": [
18
+ {
19
+ "type": "mesh",
20
+ "mesh": "seeed_bearing__configuration__22x16x4.stl",
21
+ "pos": [
22
+ 0.006,
23
+ 0.0175,
24
+ -0.005
25
+ ],
26
+ "quat": [
27
+ 1.0,
28
+ -0.0,
29
+ -0.0,
30
+ 0.0
31
+ ],
32
+ "color": [
33
+ 0.713725,
34
+ 0.760784,
35
+ 0.8,
36
+ 1.0
37
+ ]
38
+ },
39
+ {
40
+ "type": "mesh",
41
+ "mesh": "right_shell.stl",
42
+ "pos": [
43
+ 0.004,
44
+ -0.0005,
45
+ -0.0175
46
+ ],
47
+ "quat": [
48
+ 0.707107,
49
+ -0.0,
50
+ -0.0,
51
+ 0.707107
52
+ ],
53
+ "color": [
54
+ 0.8,
55
+ 0.8,
56
+ 0.8,
57
+ 1.0
58
+ ]
59
+ },
60
+ {
61
+ "type": "mesh",
62
+ "mesh": "banana_pcb_locker.stl",
63
+ "pos": [
64
+ 0.0042,
65
+ -0.0,
66
+ -0.0175215
67
+ ],
68
+ "quat": [
69
+ 0.707107,
70
+ -0.0,
71
+ 0.0,
72
+ 0.707107
73
+ ],
74
+ "color": [
75
+ 0.647059,
76
+ 0.647059,
77
+ 0.647059,
78
+ 1.0
79
+ ]
80
+ },
81
+ {
82
+ "type": "mesh",
83
+ "mesh": "trunk_base.stl",
84
+ "pos": [
85
+ 0.004,
86
+ -0.0,
87
+ -0.0175
88
+ ],
89
+ "quat": [
90
+ 0.707107,
91
+ -0.0,
92
+ -0.0,
93
+ 0.707107
94
+ ],
95
+ "color": [
96
+ 0.34902,
97
+ 0.376471,
98
+ 0.4,
99
+ 1.0
100
+ ]
101
+ },
102
+ {
103
+ "type": "mesh",
104
+ "mesh": "xl330.stl",
105
+ "pos": [
106
+ 0.006,
107
+ 0.0175,
108
+ 0.0115
109
+ ],
110
+ "quat": [
111
+ 0.0,
112
+ 0.707107,
113
+ -0.0,
114
+ 0.707107
115
+ ],
116
+ "color": [
117
+ 0.286275,
118
+ 0.286275,
119
+ 0.286275,
120
+ 1.0
121
+ ]
122
+ },
123
+ {
124
+ "type": "mesh",
125
+ "mesh": "power_support.stl",
126
+ "pos": [
127
+ 0.004,
128
+ -0.0,
129
+ -0.0175215
130
+ ],
131
+ "quat": [
132
+ 0.707107,
133
+ -0.0,
134
+ 0.0,
135
+ 0.707107
136
+ ],
137
+ "color": [
138
+ 0.901961,
139
+ 0.901961,
140
+ 0.901961,
141
+ 1.0
142
+ ]
143
+ },
144
+ {
145
+ "type": "mesh",
146
+ "mesh": "np_f970.stl",
147
+ "pos": [
148
+ -0.0359693,
149
+ 0.0,
150
+ -0.00385049
151
+ ],
152
+ "quat": [
153
+ 0.00617059,
154
+ 0.70708,
155
+ -0.70708,
156
+ -0.00617059
157
+ ],
158
+ "color": [
159
+ 0.901961,
160
+ 0.901961,
161
+ 0.901961,
162
+ 1.0
163
+ ]
164
+ },
165
+ {
166
+ "type": "mesh",
167
+ "mesh": "xl330.stl",
168
+ "pos": [
169
+ 0.006,
170
+ -0.0175,
171
+ 0.0115
172
+ ],
173
+ "quat": [
174
+ 0.0,
175
+ 0.707107,
176
+ -0.0,
177
+ 0.707107
178
+ ],
179
+ "color": [
180
+ 0.286275,
181
+ 0.286275,
182
+ 0.286275,
183
+ 1.0
184
+ ]
185
+ },
186
+ {
187
+ "type": "mesh",
188
+ "mesh": "left_shell.stl",
189
+ "pos": [
190
+ 0.004,
191
+ -0.0,
192
+ -0.0175785
193
+ ],
194
+ "quat": [
195
+ 0.707107,
196
+ 0.0,
197
+ -0.0,
198
+ 0.707107
199
+ ],
200
+ "color": [
201
+ 0.8,
202
+ 0.8,
203
+ 0.8,
204
+ 1.0
205
+ ]
206
+ },
207
+ {
208
+ "type": "mesh",
209
+ "mesh": "seeed_bearing__configuration__22x16x4.stl",
210
+ "pos": [
211
+ 0.006,
212
+ -0.0175,
213
+ -0.005
214
+ ],
215
+ "quat": [
216
+ 1.0,
217
+ -0.0,
218
+ -0.0,
219
+ 0.0
220
+ ],
221
+ "color": [
222
+ 0.713725,
223
+ 0.760784,
224
+ 0.8,
225
+ 1.0
226
+ ]
227
+ }
228
+ ],
229
+ "joint": null
230
+ },
231
+ {
232
+ "name": "yaw2roll",
233
+ "parent": "trunk_base",
234
+ "pos": [
235
+ 0.006,
236
+ 0.0175,
237
+ -0.005
238
+ ],
239
+ "quat": [
240
+ 0.0,
241
+ -0.707107,
242
+ -0.707107,
243
+ -0.0
244
+ ],
245
+ "geoms": [
246
+ {
247
+ "type": "mesh",
248
+ "mesh": "yaw2roll.stl",
249
+ "pos": [
250
+ -0.0175,
251
+ -0.002,
252
+ 0.0125
253
+ ],
254
+ "quat": [
255
+ 0.0,
256
+ 1.0,
257
+ 0.0,
258
+ 0.0
259
+ ],
260
+ "color": [
261
+ 0.647059,
262
+ 0.647059,
263
+ 0.647059,
264
+ 1.0
265
+ ]
266
+ },
267
+ {
268
+ "type": "mesh",
269
+ "mesh": "xl330.stl",
270
+ "pos": [
271
+ -0.0,
272
+ 0.0,
273
+ 0.0125
274
+ ],
275
+ "quat": [
276
+ 0.0,
277
+ -0.707107,
278
+ 0.707107,
279
+ -0.0
280
+ ],
281
+ "color": [
282
+ 0.286275,
283
+ 0.286275,
284
+ 0.286275,
285
+ 1.0
286
+ ]
287
+ },
288
+ {
289
+ "type": "mesh",
290
+ "mesh": "seeed_bearing__configuration__22x16x4.stl",
291
+ "pos": [
292
+ -0.0,
293
+ 0.0165,
294
+ 0.0125
295
+ ],
296
+ "quat": [
297
+ 0.5,
298
+ 0.5,
299
+ 0.5,
300
+ -0.5
301
+ ],
302
+ "color": [
303
+ 0.713725,
304
+ 0.760784,
305
+ 0.8,
306
+ 1.0
307
+ ]
308
+ },
309
+ {
310
+ "type": "mesh",
311
+ "mesh": "bearing_roll.stl",
312
+ "pos": [
313
+ -0.0175,
314
+ -0.002,
315
+ 0.0125
316
+ ],
317
+ "quat": [
318
+ 0.0,
319
+ -1.0,
320
+ -0.0,
321
+ -0.0
322
+ ],
323
+ "color": [
324
+ 0.262745,
325
+ 0.282353,
326
+ 0.301961,
327
+ 1.0
328
+ ]
329
+ }
330
+ ],
331
+ "joint": {
332
+ "name": "left_hip_yaw",
333
+ "axis": [
334
+ 0.0,
335
+ 0.0,
336
+ 1.0
337
+ ],
338
+ "type": "hinge",
339
+ "pos": [
340
+ 0.0,
341
+ 0.0,
342
+ 0.0
343
+ ],
344
+ "range": [
345
+ -0.4363323129985824,
346
+ 0.5235987755982988
347
+ ],
348
+ "actuator_index": 0
349
+ }
350
+ },
351
+ {
352
+ "name": "hip_l",
353
+ "parent": "yaw2roll",
354
+ "pos": [
355
+ 0.0,
356
+ 0.0165,
357
+ 0.0125
358
+ ],
359
+ "quat": [
360
+ 0.707107,
361
+ -0.707107,
362
+ -0.0,
363
+ -0.0
364
+ ],
365
+ "geoms": [
366
+ {
367
+ "type": "mesh",
368
+ "mesh": "seeed_bearing__configuration__22x16x4.stl",
369
+ "pos": [
370
+ 0.021,
371
+ 0.0,
372
+ -0.0185
373
+ ],
374
+ "quat": [
375
+ 0.5,
376
+ 0.5,
377
+ 0.5,
378
+ 0.5
379
+ ],
380
+ "color": [
381
+ 0.713725,
382
+ 0.760784,
383
+ 0.8,
384
+ 1.0
385
+ ]
386
+ },
387
+ {
388
+ "type": "mesh",
389
+ "mesh": "hip_l.stl",
390
+ "pos": [
391
+ -0.0175,
392
+ 0.0,
393
+ -0.0185
394
+ ],
395
+ "quat": [
396
+ 0.707107,
397
+ -0.707107,
398
+ 0.0,
399
+ -0.0
400
+ ],
401
+ "color": [
402
+ 0.901961,
403
+ 0.901961,
404
+ 0.901961,
405
+ 1.0
406
+ ]
407
+ }
408
+ ],
409
+ "joint": {
410
+ "name": "left_hip_roll",
411
+ "axis": [
412
+ 0.0,
413
+ 0.0,
414
+ 1.0
415
+ ],
416
+ "type": "hinge",
417
+ "pos": [
418
+ 0.0,
419
+ 0.0,
420
+ 0.0
421
+ ],
422
+ "range": [
423
+ -0.3839724354387525,
424
+ 0.3839724354387525
425
+ ],
426
+ "actuator_index": 1
427
+ }
428
+ },
429
+ {
430
+ "name": "upper_leg_left",
431
+ "parent": "hip_l",
432
+ "pos": [
433
+ 0.025,
434
+ 0.0,
435
+ -0.0185
436
+ ],
437
+ "quat": [
438
+ 0.5,
439
+ -0.5,
440
+ 0.5,
441
+ -0.5
442
+ ],
443
+ "geoms": [
444
+ {
445
+ "type": "mesh",
446
+ "mesh": "upper_leg_left.stl",
447
+ "pos": [
448
+ -0.0,
449
+ -0.0,
450
+ -0.0425
451
+ ],
452
+ "quat": [
453
+ 0.5,
454
+ -0.5,
455
+ -0.5,
456
+ -0.5
457
+ ],
458
+ "color": [
459
+ 0.866667,
460
+ 0.866667,
461
+ 0.866667,
462
+ 1.0
463
+ ]
464
+ },
465
+ {
466
+ "type": "mesh",
467
+ "mesh": "xl330.stl",
468
+ "pos": [
469
+ 0.022,
470
+ 0.0357771,
471
+ 0.0125
472
+ ],
473
+ "quat": [
474
+ 0.0,
475
+ -0.707107,
476
+ -0.0,
477
+ -0.707107
478
+ ],
479
+ "color": [
480
+ 0.286275,
481
+ 0.286275,
482
+ 0.286275,
483
+ 1.0
484
+ ]
485
+ },
486
+ {
487
+ "type": "mesh",
488
+ "mesh": "upper_leg_rigidity_plate.stl",
489
+ "pos": [
490
+ 0.0,
491
+ -0.0,
492
+ -0.0425
493
+ ],
494
+ "quat": [
495
+ 0.5,
496
+ -0.5,
497
+ -0.5,
498
+ -0.5
499
+ ],
500
+ "color": [
501
+ 0.537255,
502
+ 0.854902,
503
+ 0.827451,
504
+ 1.0
505
+ ]
506
+ },
507
+ {
508
+ "type": "mesh",
509
+ "mesh": "seeed_bearing__configuration__22x16x4.stl",
510
+ "pos": [
511
+ 0.022,
512
+ 0.0357771,
513
+ -0.004
514
+ ],
515
+ "quat": [
516
+ 0.0,
517
+ 0.0,
518
+ 0.0,
519
+ 1.0
520
+ ],
521
+ "color": [
522
+ 0.713725,
523
+ 0.760784,
524
+ 0.8,
525
+ 1.0
526
+ ]
527
+ },
528
+ {
529
+ "type": "mesh",
530
+ "mesh": "xl330.stl",
531
+ "pos": [
532
+ -0.0,
533
+ -0.0,
534
+ 0.0125
535
+ ],
536
+ "quat": [
537
+ 0.5,
538
+ 0.5,
539
+ -0.5,
540
+ 0.5
541
+ ],
542
+ "color": [
543
+ 0.286275,
544
+ 0.286275,
545
+ 0.286275,
546
+ 1.0
547
+ ]
548
+ }
549
+ ],
550
+ "joint": {
551
+ "name": "left_hip_pitch",
552
+ "axis": [
553
+ 0.0,
554
+ 0.0,
555
+ 1.0
556
+ ],
557
+ "type": "hinge",
558
+ "pos": [
559
+ 0.0,
560
+ 0.0,
561
+ 0.0
562
+ ],
563
+ "range": [
564
+ -1.570796326795005,
565
+ 1.5707963267947882
566
+ ],
567
+ "actuator_index": 2
568
+ }
569
+ },
570
+ {
571
+ "name": "leg",
572
+ "parent": "upper_leg_left",
573
+ "pos": [
574
+ 0.022,
575
+ 0.0357771,
576
+ -0.004
577
+ ],
578
+ "quat": [
579
+ 0.0,
580
+ 0.707107,
581
+ 0.707107,
582
+ 0.0
583
+ ],
584
+ "geoms": [
585
+ {
586
+ "type": "mesh",
587
+ "mesh": "leg.stl",
588
+ "pos": [
589
+ -0.022,
590
+ 0.0357771,
591
+ 0.0385
592
+ ],
593
+ "quat": [
594
+ 0.5,
595
+ 0.5,
596
+ 0.5,
597
+ -0.5
598
+ ],
599
+ "color": [
600
+ 0.811765,
601
+ 0.858824,
602
+ 0.898039,
603
+ 1.0
604
+ ]
605
+ },
606
+ {
607
+ "type": "mesh",
608
+ "mesh": "xl330.stl",
609
+ "pos": [
610
+ 0.0,
611
+ 0.042,
612
+ -0.0115
613
+ ],
614
+ "quat": [
615
+ 0.5,
616
+ -0.5,
617
+ -0.5,
618
+ -0.5
619
+ ],
620
+ "color": [
621
+ 0.286275,
622
+ 0.286275,
623
+ 0.286275,
624
+ 1.0
625
+ ]
626
+ }
627
+ ],
628
+ "joint": {
629
+ "name": "left_knee",
630
+ "axis": [
631
+ 0.0,
632
+ 0.0,
633
+ 1.0
634
+ ],
635
+ "type": "hinge",
636
+ "pos": [
637
+ 0.0,
638
+ 0.0,
639
+ 0.0
640
+ ],
641
+ "range": [
642
+ -1.570796326795012,
643
+ 1.570796326794781
644
+ ],
645
+ "actuator_index": 3
646
+ }
647
+ },
648
+ {
649
+ "name": "ankle_left",
650
+ "parent": "leg",
651
+ "pos": [
652
+ 0.0,
653
+ 0.042,
654
+ -0.026
655
+ ],
656
+ "quat": [
657
+ 0.0,
658
+ 1.0,
659
+ -0.0,
660
+ 0.0
661
+ ],
662
+ "geoms": [
663
+ {
664
+ "type": "mesh",
665
+ "mesh": "sole_left.stl",
666
+ "pos": [
667
+ -0.022,
668
+ 0.00622291,
669
+ -0.0647
670
+ ],
671
+ "quat": [
672
+ 0.5,
673
+ -0.5,
674
+ -0.5,
675
+ -0.5
676
+ ],
677
+ "color": [
678
+ 0.537255,
679
+ 0.854902,
680
+ 0.827451,
681
+ 1.0
682
+ ]
683
+ },
684
+ {
685
+ "type": "mesh",
686
+ "mesh": "foot_left.stl",
687
+ "pos": [
688
+ -0.022,
689
+ 0.00622291,
690
+ -0.0647
691
+ ],
692
+ "quat": [
693
+ 0.5,
694
+ -0.5,
695
+ -0.5,
696
+ -0.5
697
+ ],
698
+ "color": [
699
+ 0.980392,
700
+ 0.713725,
701
+ 0.00392157,
702
+ 1.0
703
+ ]
704
+ },
705
+ {
706
+ "type": "mesh",
707
+ "mesh": "seeed_bearing__configuration_default.stl",
708
+ "pos": [
709
+ 0.0,
710
+ 0.0,
711
+ -0.0322
712
+ ],
713
+ "quat": [
714
+ 0.0,
715
+ 0.0,
716
+ -0.0,
717
+ 1.0
718
+ ],
719
+ "color": [
720
+ 0.713725,
721
+ 0.760784,
722
+ 0.8,
723
+ 1.0
724
+ ]
725
+ },
726
+ {
727
+ "type": "mesh",
728
+ "mesh": "ankle_left.stl",
729
+ "pos": [
730
+ -0.022,
731
+ 0.00622291,
732
+ -0.0647
733
+ ],
734
+ "quat": [
735
+ 0.5,
736
+ -0.5,
737
+ -0.5,
738
+ -0.5
739
+ ],
740
+ "color": [
741
+ 0.980392,
742
+ 0.713725,
743
+ 0.00392157,
744
+ 1.0
745
+ ]
746
+ }
747
+ ],
748
+ "joint": {
749
+ "name": "left_ankle",
750
+ "axis": [
751
+ 0.0,
752
+ 0.0,
753
+ 1.0
754
+ ],
755
+ "type": "hinge",
756
+ "pos": [
757
+ 0.0,
758
+ 0.0,
759
+ 0.0
760
+ ],
761
+ "range": [
762
+ -1.5707963267949019,
763
+ 1.5707963267948912
764
+ ],
765
+ "actuator_index": 4
766
+ }
767
+ },
768
+ {
769
+ "name": "neck",
770
+ "parent": "trunk_base",
771
+ "pos": [
772
+ 0.026,
773
+ 0.0145,
774
+ 0.0324215
775
+ ],
776
+ "quat": [
777
+ 0.0,
778
+ 0.0,
779
+ 0.707107,
780
+ -0.707107
781
+ ],
782
+ "geoms": [
783
+ {
784
+ "type": "mesh",
785
+ "mesh": "neck.stl",
786
+ "pos": [
787
+ 0.022,
788
+ 0.05,
789
+ 0.028
790
+ ],
791
+ "quat": [
792
+ 0.5,
793
+ 0.5,
794
+ 0.5,
795
+ -0.5
796
+ ],
797
+ "color": [
798
+ 0.8,
799
+ 0.8,
800
+ 0.8,
801
+ 1.0
802
+ ]
803
+ },
804
+ {
805
+ "type": "mesh",
806
+ "mesh": "xl330.stl",
807
+ "pos": [
808
+ 0.0,
809
+ 0.0,
810
+ 0.0145
811
+ ],
812
+ "quat": [
813
+ 0.5,
814
+ -0.5,
815
+ -0.5,
816
+ -0.5
817
+ ],
818
+ "color": [
819
+ 0.286275,
820
+ 0.286275,
821
+ 0.286275,
822
+ 1.0
823
+ ]
824
+ },
825
+ {
826
+ "type": "mesh",
827
+ "mesh": "xl330.stl",
828
+ "pos": [
829
+ 0.0,
830
+ -0.05,
831
+ 0.0145
832
+ ],
833
+ "quat": [
834
+ 0.5,
835
+ 0.5,
836
+ -0.5,
837
+ 0.5
838
+ ],
839
+ "color": [
840
+ 0.286275,
841
+ 0.286275,
842
+ 0.286275,
843
+ 1.0
844
+ ]
845
+ },
846
+ {
847
+ "type": "mesh",
848
+ "mesh": "neck.stl",
849
+ "pos": [
850
+ 0.022,
851
+ 0.05,
852
+ 0.003
853
+ ],
854
+ "quat": [
855
+ 0.5,
856
+ 0.5,
857
+ 0.5,
858
+ -0.5
859
+ ],
860
+ "color": [
861
+ 0.8,
862
+ 0.8,
863
+ 0.8,
864
+ 1.0
865
+ ]
866
+ }
867
+ ],
868
+ "joint": {
869
+ "name": "neck_pitch",
870
+ "axis": [
871
+ 0.0,
872
+ 0.0,
873
+ 1.0
874
+ ],
875
+ "type": "hinge",
876
+ "pos": [
877
+ 0.0,
878
+ 0.0,
879
+ 0.0
880
+ ],
881
+ "range": [
882
+ -1.5707963267948966,
883
+ 1.0471975511965976
884
+ ],
885
+ "actuator_index": 5
886
+ }
887
+ },
888
+ {
889
+ "name": "neck_pitch",
890
+ "parent": "neck",
891
+ "pos": [
892
+ 0.0,
893
+ -0.05,
894
+ -0.0
895
+ ],
896
+ "quat": [
897
+ 0.0,
898
+ 1.0,
899
+ 0.0,
900
+ 0.0
901
+ ],
902
+ "geoms": [
903
+ {
904
+ "type": "mesh",
905
+ "mesh": "neck_pitch.stl",
906
+ "pos": [
907
+ -0.0,
908
+ 0.0287931,
909
+ -0.0145
910
+ ],
911
+ "quat": [
912
+ 0.5,
913
+ -0.5,
914
+ -0.5,
915
+ -0.5
916
+ ],
917
+ "color": [
918
+ 0.34902,
919
+ 0.376471,
920
+ 0.4,
921
+ 1.0
922
+ ]
923
+ },
924
+ {
925
+ "type": "mesh",
926
+ "mesh": "seeed_bearing__configuration__22x16x4.stl",
927
+ "pos": [
928
+ -0.0,
929
+ 0.0207931,
930
+ -0.0145
931
+ ],
932
+ "quat": [
933
+ 0.707107,
934
+ 0.707107,
935
+ 0.0,
936
+ 0.0
937
+ ],
938
+ "color": [
939
+ 0.713725,
940
+ 0.760784,
941
+ 0.8,
942
+ 1.0
943
+ ]
944
+ }
945
+ ],
946
+ "joint": {
947
+ "name": "head_pitch",
948
+ "axis": [
949
+ 0.0,
950
+ 0.0,
951
+ 1.0
952
+ ],
953
+ "type": "hinge",
954
+ "pos": [
955
+ 0.0,
956
+ 0.0,
957
+ 0.0
958
+ ],
959
+ "range": [
960
+ -1.5707963267948974,
961
+ 1.5707963267948957
962
+ ],
963
+ "actuator_index": 6
964
+ }
965
+ },
966
+ {
967
+ "name": "yaw_roll_motion",
968
+ "parent": "neck_pitch",
969
+ "pos": [
970
+ -0.0,
971
+ 0.0186931,
972
+ -0.0145
973
+ ],
974
+ "quat": [
975
+ 0.0,
976
+ 0.0,
977
+ -0.707107,
978
+ -0.707107
979
+ ],
980
+ "geoms": [
981
+ {
982
+ "type": "mesh",
983
+ "mesh": "yaw_roll_motion.stl",
984
+ "pos": [
985
+ 0.0,
986
+ 0.0,
987
+ 0.01
988
+ ],
989
+ "quat": [
990
+ 0.707107,
991
+ 0.0,
992
+ -0.0,
993
+ 0.707107
994
+ ],
995
+ "color": [
996
+ 0.768627,
997
+ 0.886275,
998
+ 0.952941,
999
+ 1.0
1000
+ ]
1001
+ },
1002
+ {
1003
+ "type": "mesh",
1004
+ "mesh": "xl330.stl",
1005
+ "pos": [
1006
+ 0.0,
1007
+ 0.0,
1008
+ 0.0145
1009
+ ],
1010
+ "quat": [
1011
+ 0.5,
1012
+ -0.5,
1013
+ -0.5,
1014
+ -0.5
1015
+ ],
1016
+ "color": [
1017
+ 0.286275,
1018
+ 0.286275,
1019
+ 0.286275,
1020
+ 1.0
1021
+ ]
1022
+ },
1023
+ {
1024
+ "type": "mesh",
1025
+ "mesh": "seeed_bearing__configuration__22x16x4.stl",
1026
+ "pos": [
1027
+ -0.016,
1028
+ 0.0,
1029
+ 0.0145
1030
+ ],
1031
+ "quat": [
1032
+ 0.707107,
1033
+ -0.0,
1034
+ -0.707107,
1035
+ -0.0
1036
+ ],
1037
+ "color": [
1038
+ 0.713725,
1039
+ 0.760784,
1040
+ 0.8,
1041
+ 1.0
1042
+ ]
1043
+ },
1044
+ {
1045
+ "type": "mesh",
1046
+ "mesh": "seeed_bearing__configuration__22x16x4.stl",
1047
+ "pos": [
1048
+ 0.018,
1049
+ 0.0,
1050
+ 0.0145
1051
+ ],
1052
+ "quat": [
1053
+ 0.707107,
1054
+ -0.0,
1055
+ -0.707107,
1056
+ -0.0
1057
+ ],
1058
+ "color": [
1059
+ 0.713725,
1060
+ 0.760784,
1061
+ 0.8,
1062
+ 1.0
1063
+ ]
1064
+ }
1065
+ ],
1066
+ "joint": {
1067
+ "name": "head_yaw",
1068
+ "axis": [
1069
+ 0.0,
1070
+ 0.0,
1071
+ 1.0
1072
+ ],
1073
+ "type": "hinge",
1074
+ "pos": [
1075
+ 0.0,
1076
+ 0.0,
1077
+ 0.0
1078
+ ],
1079
+ "range": [
1080
+ -2.967059728390373,
1081
+ 2.967059728390348
1082
+ ],
1083
+ "actuator_index": 7
1084
+ }
1085
+ },
1086
+ {
1087
+ "name": "jaw_soft",
1088
+ "parent": "yaw_roll_motion",
1089
+ "pos": [
1090
+ -0.0179,
1091
+ 0.0,
1092
+ 0.0145
1093
+ ],
1094
+ "quat": [
1095
+ 0.707107,
1096
+ -0.0,
1097
+ -0.707107,
1098
+ -0.0
1099
+ ],
1100
+ "geoms": [
1101
+ {
1102
+ "type": "mesh",
1103
+ "mesh": "lens.stl",
1104
+ "pos": [
1105
+ 0.0155,
1106
+ -9.13778e-05,
1107
+ -0.05548
1108
+ ],
1109
+ "quat": [
1110
+ 0.5,
1111
+ -0.5,
1112
+ 0.5,
1113
+ -0.5
1114
+ ],
1115
+ "color": [
1116
+ 0.262745,
1117
+ 0.282353,
1118
+ 0.301961,
1119
+ 1.0
1120
+ ]
1121
+ },
1122
+ {
1123
+ "type": "mesh",
1124
+ "mesh": "face_part.stl",
1125
+ "pos": [
1126
+ -0.0045,
1127
+ -9.13778e-05,
1128
+ -0.0178
1129
+ ],
1130
+ "quat": [
1131
+ 0.5,
1132
+ 0.5,
1133
+ 0.5,
1134
+ 0.5
1135
+ ],
1136
+ "color": [
1137
+ 0.34902,
1138
+ 0.376471,
1139
+ 0.4,
1140
+ 1.0
1141
+ ]
1142
+ },
1143
+ {
1144
+ "type": "mesh",
1145
+ "mesh": "xl330.stl",
1146
+ "pos": [
1147
+ 0.003,
1148
+ 0.0255,
1149
+ -0.018
1150
+ ],
1151
+ "quat": [
1152
+ 0.707107,
1153
+ 0.0,
1154
+ -0.0,
1155
+ -0.707107
1156
+ ],
1157
+ "color": [
1158
+ 0.286275,
1159
+ 0.286275,
1160
+ 0.286275,
1161
+ 1.0
1162
+ ]
1163
+ },
1164
+ {
1165
+ "type": "mesh",
1166
+ "mesh": "jaw_soft.stl",
1167
+ "pos": [
1168
+ -0.0046,
1169
+ 0.0,
1170
+ -0.0179996
1171
+ ],
1172
+ "quat": [
1173
+ 0.5,
1174
+ 0.5,
1175
+ 0.5,
1176
+ 0.5
1177
+ ],
1178
+ "color": [
1179
+ 0.85098,
1180
+ 0.756863,
1181
+ 0.866667,
1182
+ 1.0
1183
+ ]
1184
+ },
1185
+ {
1186
+ "type": "mesh",
1187
+ "mesh": "motor_support.stl",
1188
+ "pos": [
1189
+ -0.0045,
1190
+ 0.0,
1191
+ -0.018
1192
+ ],
1193
+ "quat": [
1194
+ 0.5,
1195
+ 0.5,
1196
+ 0.5,
1197
+ 0.5
1198
+ ],
1199
+ "color": [
1200
+ 0.262745,
1201
+ 0.282353,
1202
+ 0.301961,
1203
+ 1.0
1204
+ ]
1205
+ },
1206
+ {
1207
+ "type": "mesh",
1208
+ "mesh": "top_head_shell.stl",
1209
+ "pos": [
1210
+ -0.0045,
1211
+ -0.0,
1212
+ -0.018
1213
+ ],
1214
+ "quat": [
1215
+ 0.5,
1216
+ 0.5,
1217
+ 0.5,
1218
+ 0.5
1219
+ ],
1220
+ "color": [
1221
+ 0.901961,
1222
+ 0.901961,
1223
+ 0.901961,
1224
+ 1.0
1225
+ ]
1226
+ },
1227
+ {
1228
+ "type": "mesh",
1229
+ "mesh": "xl330.stl",
1230
+ "pos": [
1231
+ 0.0,
1232
+ 0.0,
1233
+ 0.0145
1234
+ ],
1235
+ "quat": [
1236
+ 0.5,
1237
+ -0.5,
1238
+ -0.5,
1239
+ -0.5
1240
+ ],
1241
+ "color": [
1242
+ 0.286275,
1243
+ 0.286275,
1244
+ 0.286275,
1245
+ 1.0
1246
+ ]
1247
+ },
1248
+ {
1249
+ "type": "mesh",
1250
+ "mesh": "seeed_bearing__configuration_default.stl",
1251
+ "pos": [
1252
+ 0.0032,
1253
+ -0.04,
1254
+ -0.018
1255
+ ],
1256
+ "quat": [
1257
+ 0.5,
1258
+ -0.5,
1259
+ 0.5,
1260
+ 0.5
1261
+ ],
1262
+ "color": [
1263
+ 0.713725,
1264
+ 0.760784,
1265
+ 0.8,
1266
+ 1.0
1267
+ ]
1268
+ },
1269
+ {
1270
+ "type": "mesh",
1271
+ "mesh": "elec_rpi_robot_hat_pcb.stl",
1272
+ "pos": [
1273
+ 0.02694,
1274
+ 0.0289936,
1275
+ -0.0519
1276
+ ],
1277
+ "quat": [
1278
+ 0.707107,
1279
+ 0.0,
1280
+ 0.0,
1281
+ -0.707107
1282
+ ],
1283
+ "color": [
1284
+ 0.313725,
1285
+ 0.486275,
1286
+ 0.411765,
1287
+ 1.0
1288
+ ]
1289
+ },
1290
+ {
1291
+ "type": "mesh",
1292
+ "mesh": "soft_mouth_top.stl",
1293
+ "pos": [
1294
+ -0.00420071,
1295
+ 1.7776e-08,
1296
+ -0.0180814
1297
+ ],
1298
+ "quat": [
1299
+ 0.5,
1300
+ 0.5,
1301
+ 0.5,
1302
+ 0.5
1303
+ ],
1304
+ "color": [
1305
+ 0.85098,
1306
+ 0.756863,
1307
+ 0.866667,
1308
+ 1.0
1309
+ ]
1310
+ },
1311
+ {
1312
+ "type": "mesh",
1313
+ "mesh": "noenoeil.stl",
1314
+ "pos": [
1315
+ -0.0045,
1316
+ -9.13778e-05,
1317
+ -0.0178
1318
+ ],
1319
+ "quat": [
1320
+ 0.5,
1321
+ 0.5,
1322
+ 0.5,
1323
+ 0.5
1324
+ ],
1325
+ "color": [
1326
+ 0.262745,
1327
+ 0.282353,
1328
+ 0.301961,
1329
+ 1.0
1330
+ ]
1331
+ },
1332
+ {
1333
+ "type": "mesh",
1334
+ "mesh": "jaw.stl",
1335
+ "pos": [
1336
+ -0.0045,
1337
+ 0.0,
1338
+ -0.018
1339
+ ],
1340
+ "quat": [
1341
+ 0.5,
1342
+ 0.5,
1343
+ 0.5,
1344
+ 0.5
1345
+ ],
1346
+ "color": [
1347
+ 0.980392,
1348
+ 0.713725,
1349
+ 0.00392157,
1350
+ 1.0
1351
+ ]
1352
+ },
1353
+ {
1354
+ "type": "mesh",
1355
+ "mesh": "bottom_head_shell.stl",
1356
+ "pos": [
1357
+ -0.0043,
1358
+ -0.0,
1359
+ -0.018
1360
+ ],
1361
+ "quat": [
1362
+ 0.5,
1363
+ 0.5,
1364
+ 0.5,
1365
+ 0.5
1366
+ ],
1367
+ "color": [
1368
+ 0.980392,
1369
+ 0.713725,
1370
+ 0.00392157,
1371
+ 1.0
1372
+ ]
1373
+ },
1374
+ {
1375
+ "type": "mesh",
1376
+ "mesh": "m12_lens_holder.stl",
1377
+ "pos": [
1378
+ 0.0155,
1379
+ -9.13778e-05,
1380
+ -0.05558
1381
+ ],
1382
+ "quat": [
1383
+ 0.5,
1384
+ -0.5,
1385
+ 0.5,
1386
+ -0.5
1387
+ ],
1388
+ "color": [
1389
+ 0.262745,
1390
+ 0.282353,
1391
+ 0.301961,
1392
+ 1.0
1393
+ ]
1394
+ },
1395
+ {
1396
+ "type": "mesh",
1397
+ "mesh": "pcb__raspberry_pi_zero_2_w.stl",
1398
+ "pos": [
1399
+ 0.015435,
1400
+ -1.13778e-05,
1401
+ -0.06001
1402
+ ],
1403
+ "quat": [
1404
+ 0.5,
1405
+ -0.5,
1406
+ -0.5,
1407
+ 0.5
1408
+ ],
1409
+ "color": [
1410
+ 0.498039,
1411
+ 0.498039,
1412
+ 0.498039,
1413
+ 1.0
1414
+ ]
1415
+ },
1416
+ {
1417
+ "type": "mesh",
1418
+ "mesh": "speaker.stl",
1419
+ "pos": [
1420
+ 0.01,
1421
+ -0.016786,
1422
+ 0.0260226
1423
+ ],
1424
+ "quat": [
1425
+ 0.5,
1426
+ 0.5,
1427
+ 0.5,
1428
+ 0.5
1429
+ ],
1430
+ "color": [
1431
+ 0.294118,
1432
+ 0.290196,
1433
+ 0.290196,
1434
+ 1.0
1435
+ ]
1436
+ }
1437
+ ],
1438
+ "joint": {
1439
+ "name": "head_roll",
1440
+ "axis": [
1441
+ 0.0,
1442
+ 0.0,
1443
+ 1.0
1444
+ ],
1445
+ "type": "hinge",
1446
+ "pos": [
1447
+ 0.0,
1448
+ 0.0,
1449
+ 0.0
1450
+ ],
1451
+ "range": [
1452
+ -0.43633231299859127,
1453
+ 0.4363323129985735
1454
+ ],
1455
+ "actuator_index": 8
1456
+ }
1457
+ },
1458
+ {
1459
+ "name": "bearing_roll",
1460
+ "parent": "trunk_base",
1461
+ "pos": [
1462
+ 0.006,
1463
+ -0.0175,
1464
+ -0.005
1465
+ ],
1466
+ "quat": [
1467
+ 0.0,
1468
+ 0.707107,
1469
+ 0.707107,
1470
+ 0.0
1471
+ ],
1472
+ "geoms": [
1473
+ {
1474
+ "type": "mesh",
1475
+ "mesh": "yaw2roll.stl",
1476
+ "pos": [
1477
+ -0.0175,
1478
+ -0.002,
1479
+ 0.0125
1480
+ ],
1481
+ "quat": [
1482
+ 0.0,
1483
+ 1.0,
1484
+ 0.0,
1485
+ 0.0
1486
+ ],
1487
+ "color": [
1488
+ 0.647059,
1489
+ 0.647059,
1490
+ 0.647059,
1491
+ 1.0
1492
+ ]
1493
+ },
1494
+ {
1495
+ "type": "mesh",
1496
+ "mesh": "xl330.stl",
1497
+ "pos": [
1498
+ -0.0,
1499
+ 0.0,
1500
+ 0.0125
1501
+ ],
1502
+ "quat": [
1503
+ 0.0,
1504
+ -0.707107,
1505
+ 0.707107,
1506
+ -0.0
1507
+ ],
1508
+ "color": [
1509
+ 0.286275,
1510
+ 0.286275,
1511
+ 0.286275,
1512
+ 1.0
1513
+ ]
1514
+ },
1515
+ {
1516
+ "type": "mesh",
1517
+ "mesh": "bearing_roll.stl",
1518
+ "pos": [
1519
+ -0.0175,
1520
+ -0.002,
1521
+ 0.0125
1522
+ ],
1523
+ "quat": [
1524
+ 0.0,
1525
+ -1.0,
1526
+ -0.0,
1527
+ -0.0
1528
+ ],
1529
+ "color": [
1530
+ 0.262745,
1531
+ 0.282353,
1532
+ 0.301961,
1533
+ 1.0
1534
+ ]
1535
+ },
1536
+ {
1537
+ "type": "mesh",
1538
+ "mesh": "seeed_bearing__configuration__22x16x4.stl",
1539
+ "pos": [
1540
+ -0.0,
1541
+ 0.0165,
1542
+ 0.0125
1543
+ ],
1544
+ "quat": [
1545
+ 0.5,
1546
+ 0.5,
1547
+ 0.5,
1548
+ -0.5
1549
+ ],
1550
+ "color": [
1551
+ 0.713725,
1552
+ 0.760784,
1553
+ 0.8,
1554
+ 1.0
1555
+ ]
1556
+ }
1557
+ ],
1558
+ "joint": {
1559
+ "name": "right_hip_yaw",
1560
+ "axis": [
1561
+ 0.0,
1562
+ 0.0,
1563
+ 1.0
1564
+ ],
1565
+ "type": "hinge",
1566
+ "pos": [
1567
+ 0.0,
1568
+ 0.0,
1569
+ 0.0
1570
+ ],
1571
+ "range": [
1572
+ -0.5235987755982988,
1573
+ 0.4363323129985824
1574
+ ],
1575
+ "actuator_index": 9
1576
+ }
1577
+ },
1578
+ {
1579
+ "name": "hip_l_2",
1580
+ "parent": "bearing_roll",
1581
+ "pos": [
1582
+ -0.0,
1583
+ 0.0165,
1584
+ 0.0125
1585
+ ],
1586
+ "quat": [
1587
+ 0.0,
1588
+ -0.0,
1589
+ 0.707107,
1590
+ 0.707107
1591
+ ],
1592
+ "geoms": [
1593
+ {
1594
+ "type": "mesh",
1595
+ "mesh": "hip_l.stl",
1596
+ "pos": [
1597
+ -0.0175,
1598
+ 0.0,
1599
+ -0.0185
1600
+ ],
1601
+ "quat": [
1602
+ 0.707107,
1603
+ -0.707107,
1604
+ -0.0,
1605
+ -0.0
1606
+ ],
1607
+ "color": [
1608
+ 0.901961,
1609
+ 0.901961,
1610
+ 0.901961,
1611
+ 1.0
1612
+ ]
1613
+ },
1614
+ {
1615
+ "type": "mesh",
1616
+ "mesh": "seeed_bearing__configuration__22x16x4.stl",
1617
+ "pos": [
1618
+ 0.025,
1619
+ 0.0,
1620
+ -0.0185
1621
+ ],
1622
+ "quat": [
1623
+ 0.5,
1624
+ 0.5,
1625
+ -0.5,
1626
+ -0.5
1627
+ ],
1628
+ "color": [
1629
+ 0.713725,
1630
+ 0.760784,
1631
+ 0.8,
1632
+ 1.0
1633
+ ]
1634
+ }
1635
+ ],
1636
+ "joint": {
1637
+ "name": "right_hip_roll",
1638
+ "axis": [
1639
+ 0.0,
1640
+ 0.0,
1641
+ 1.0
1642
+ ],
1643
+ "type": "hinge",
1644
+ "pos": [
1645
+ 0.0,
1646
+ 0.0,
1647
+ 0.0
1648
+ ],
1649
+ "range": [
1650
+ -0.3839724354387525,
1651
+ 0.3839724354387525
1652
+ ],
1653
+ "actuator_index": 10
1654
+ }
1655
+ },
1656
+ {
1657
+ "name": "upper_leg_right",
1658
+ "parent": "hip_l_2",
1659
+ "pos": [
1660
+ 0.025,
1661
+ 0.0,
1662
+ -0.0185
1663
+ ],
1664
+ "quat": [
1665
+ 0.5,
1666
+ -0.5,
1667
+ 0.5,
1668
+ -0.5
1669
+ ],
1670
+ "geoms": [
1671
+ {
1672
+ "type": "mesh",
1673
+ "mesh": "xl330.stl",
1674
+ "pos": [
1675
+ -0.022,
1676
+ 0.0357771,
1677
+ 0.0125
1678
+ ],
1679
+ "quat": [
1680
+ 0.707107,
1681
+ -0.0,
1682
+ -0.707107,
1683
+ -0.0
1684
+ ],
1685
+ "color": [
1686
+ 0.286275,
1687
+ 0.286275,
1688
+ 0.286275,
1689
+ 1.0
1690
+ ]
1691
+ },
1692
+ {
1693
+ "type": "mesh",
1694
+ "mesh": "seeed_bearing__configuration__22x16x4.stl",
1695
+ "pos": [
1696
+ -0.022,
1697
+ 0.0357771,
1698
+ 0.0
1699
+ ],
1700
+ "quat": [
1701
+ 0.0,
1702
+ 1.0,
1703
+ -0.0,
1704
+ -0.0
1705
+ ],
1706
+ "color": [
1707
+ 0.713725,
1708
+ 0.760784,
1709
+ 0.8,
1710
+ 1.0
1711
+ ]
1712
+ },
1713
+ {
1714
+ "type": "mesh",
1715
+ "mesh": "upper_leg_rigidity_plate.stl",
1716
+ "pos": [
1717
+ 0.0,
1718
+ 0.0,
1719
+ 0.0435
1720
+ ],
1721
+ "quat": [
1722
+ 0.5,
1723
+ -0.5,
1724
+ 0.5,
1725
+ 0.5
1726
+ ],
1727
+ "color": [
1728
+ 0.537255,
1729
+ 0.854902,
1730
+ 0.827451,
1731
+ 1.0
1732
+ ]
1733
+ },
1734
+ {
1735
+ "type": "mesh",
1736
+ "mesh": "upper_leg_right.stl",
1737
+ "pos": [
1738
+ -0.0,
1739
+ 0.0,
1740
+ -0.0425
1741
+ ],
1742
+ "quat": [
1743
+ 0.5,
1744
+ -0.5,
1745
+ 0.5,
1746
+ 0.5
1747
+ ],
1748
+ "color": [
1749
+ 0.866667,
1750
+ 0.866667,
1751
+ 0.866667,
1752
+ 1.0
1753
+ ]
1754
+ },
1755
+ {
1756
+ "type": "mesh",
1757
+ "mesh": "xl330.stl",
1758
+ "pos": [
1759
+ 0.0,
1760
+ 0.0,
1761
+ 0.0125
1762
+ ],
1763
+ "quat": [
1764
+ 0.5,
1765
+ 0.5,
1766
+ -0.5,
1767
+ 0.5
1768
+ ],
1769
+ "color": [
1770
+ 0.286275,
1771
+ 0.286275,
1772
+ 0.286275,
1773
+ 1.0
1774
+ ]
1775
+ }
1776
+ ],
1777
+ "joint": {
1778
+ "name": "right_hip_pitch",
1779
+ "axis": [
1780
+ 0.0,
1781
+ 0.0,
1782
+ 1.0
1783
+ ],
1784
+ "type": "hinge",
1785
+ "pos": [
1786
+ 0.0,
1787
+ 0.0,
1788
+ 0.0
1789
+ ],
1790
+ "range": [
1791
+ -1.5707963267949268,
1792
+ 1.5707963267948664
1793
+ ],
1794
+ "actuator_index": 11
1795
+ }
1796
+ },
1797
+ {
1798
+ "name": "leg_2",
1799
+ "parent": "upper_leg_right",
1800
+ "pos": [
1801
+ -0.022,
1802
+ 0.0357771,
1803
+ -0.004
1804
+ ],
1805
+ "quat": [
1806
+ 0.0,
1807
+ 1.0,
1808
+ -0.0,
1809
+ -0.0
1810
+ ],
1811
+ "geoms": [
1812
+ {
1813
+ "type": "mesh",
1814
+ "mesh": "xl330.stl",
1815
+ "pos": [
1816
+ -0.042,
1817
+ 0.0,
1818
+ -0.0115
1819
+ ],
1820
+ "quat": [
1821
+ 0.707107,
1822
+ -0.0,
1823
+ -0.707107,
1824
+ -0.0
1825
+ ],
1826
+ "color": [
1827
+ 0.286275,
1828
+ 0.286275,
1829
+ 0.286275,
1830
+ 1.0
1831
+ ]
1832
+ },
1833
+ {
1834
+ "type": "mesh",
1835
+ "mesh": "leg.stl",
1836
+ "pos": [
1837
+ -0.0357771,
1838
+ -0.022,
1839
+ 0.0385
1840
+ ],
1841
+ "quat": [
1842
+ 0.707107,
1843
+ 0.0,
1844
+ 0.707107,
1845
+ -0.0
1846
+ ],
1847
+ "color": [
1848
+ 0.811765,
1849
+ 0.858824,
1850
+ 0.898039,
1851
+ 1.0
1852
+ ]
1853
+ }
1854
+ ],
1855
+ "joint": {
1856
+ "name": "right_knee",
1857
+ "axis": [
1858
+ 0.0,
1859
+ 0.0,
1860
+ 1.0
1861
+ ],
1862
+ "type": "hinge",
1863
+ "pos": [
1864
+ 0.0,
1865
+ 0.0,
1866
+ 0.0
1867
+ ],
1868
+ "range": [
1869
+ -1.570796326794932,
1870
+ 1.570796326794861
1871
+ ],
1872
+ "actuator_index": 12
1873
+ }
1874
+ },
1875
+ {
1876
+ "name": "ankle_right",
1877
+ "parent": "leg_2",
1878
+ "pos": [
1879
+ -0.042,
1880
+ 0.0,
1881
+ -0.026
1882
+ ],
1883
+ "quat": [
1884
+ 0.0,
1885
+ -0.707107,
1886
+ -0.707107,
1887
+ 0.0
1888
+ ],
1889
+ "geoms": [
1890
+ {
1891
+ "type": "mesh",
1892
+ "mesh": "ankle_right.stl",
1893
+ "pos": [
1894
+ 0.022,
1895
+ 0.00622291,
1896
+ -0.0647
1897
+ ],
1898
+ "quat": [
1899
+ 0.5,
1900
+ -0.5,
1901
+ 0.5,
1902
+ 0.5
1903
+ ],
1904
+ "color": [
1905
+ 0.980392,
1906
+ 0.713725,
1907
+ 0.00392157,
1908
+ 1.0
1909
+ ]
1910
+ },
1911
+ {
1912
+ "type": "mesh",
1913
+ "mesh": "sole_right.stl",
1914
+ "pos": [
1915
+ 0.022,
1916
+ 0.00622291,
1917
+ -0.0647
1918
+ ],
1919
+ "quat": [
1920
+ 0.5,
1921
+ -0.5,
1922
+ 0.5,
1923
+ 0.5
1924
+ ],
1925
+ "color": [
1926
+ 0.537255,
1927
+ 0.854902,
1928
+ 0.827451,
1929
+ 1.0
1930
+ ]
1931
+ },
1932
+ {
1933
+ "type": "mesh",
1934
+ "mesh": "seeed_bearing__configuration_default.stl",
1935
+ "pos": [
1936
+ -0.0,
1937
+ 0.0,
1938
+ -0.0292
1939
+ ],
1940
+ "quat": [
1941
+ 0.0,
1942
+ 1.0,
1943
+ -0.0,
1944
+ -0.0
1945
+ ],
1946
+ "color": [
1947
+ 0.713725,
1948
+ 0.760784,
1949
+ 0.8,
1950
+ 1.0
1951
+ ]
1952
+ },
1953
+ {
1954
+ "type": "mesh",
1955
+ "mesh": "foot_right.stl",
1956
+ "pos": [
1957
+ 0.022,
1958
+ 0.00622291,
1959
+ -0.0647
1960
+ ],
1961
+ "quat": [
1962
+ 0.5,
1963
+ -0.5,
1964
+ 0.5,
1965
+ 0.5
1966
+ ],
1967
+ "color": [
1968
+ 0.980392,
1969
+ 0.713725,
1970
+ 0.00392157,
1971
+ 1.0
1972
+ ]
1973
+ }
1974
+ ],
1975
+ "joint": {
1976
+ "name": "right_ankle",
1977
+ "axis": [
1978
+ 0.0,
1979
+ 0.0,
1980
+ 1.0
1981
+ ],
1982
+ "type": "hinge",
1983
+ "pos": [
1984
+ 0.0,
1985
+ 0.0,
1986
+ 0.0
1987
+ ],
1988
+ "range": [
1989
+ -1.5707963267949054,
1990
+ 1.5707963267948877
1991
+ ],
1992
+ "actuator_index": 13
1993
+ }
1994
+ }
1995
+ ],
1996
+ "actuated_joints": [
1997
+ "left_hip_yaw",
1998
+ "left_hip_roll",
1999
+ "left_hip_pitch",
2000
+ "left_knee",
2001
+ "left_ankle",
2002
+ "neck_pitch",
2003
+ "head_pitch",
2004
+ "head_yaw",
2005
+ "head_roll",
2006
+ "right_hip_yaw",
2007
+ "right_hip_roll",
2008
+ "right_hip_pitch",
2009
+ "right_knee",
2010
+ "right_ankle"
2011
+ ],
2012
+ "mesh_dir": "/robot/mjlab/meshes"
2013
+ }
robot/mjlab/meshes/ankle_left.stl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:d8ad18f2d8c5c44542f1183c37f634e4fc526b13155bcc3aa7f64dcf6fe1014b
3
+ size 62284
robot/mjlab/meshes/ankle_right.stl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:0710cfd347cedfc3895a32cd0b8f75179e511ffec838748289f396941a6344d8
3
+ size 62284
robot/mjlab/meshes/banana_pcb_locker.stl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:6ac5914456188fa1c181d8a06ac5b21274fdd3ed2409194552b2c87cd8afb165
3
+ size 17384
robot/mjlab/meshes/bearing_roll.stl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:fa0da0a5eef428a41da6e9639f75120781594e600c5db7518621750221669849
3
+ size 30284
robot/mjlab/meshes/bottom_head_shell.stl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:3de5a0c729a3a8efaefda01f92c946f7954081269192f160c5f36543f30da29e
3
+ size 262084
robot/mjlab/meshes/elec_rpi_robot_hat_pcb.stl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:6cdf66cc2fbc6e4f3956c551ad1d460ad3cdb463521d05b98942972788f5e12f
3
+ size 235784
robot/mjlab/meshes/face_part.stl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:c75714620738fae64dc664efeb64563053b8b28cef88a823f180b96600790c84
3
+ size 142684
robot/mjlab/meshes/foot_left.stl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:ebfb1d26924658de3c18bb61b48be39799d4aebb9f82ee19c68c4d4ae690f398
3
+ size 249034
robot/mjlab/meshes/foot_right.stl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:486f3b692e8987df7a18fcbcac7101b27d250ab4de87b86474823787be28c1c8
3
+ size 248984
robot/mjlab/meshes/hip_l.stl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:9258f4de8a58898a717c69db7a928acd41458531c15bb2ea02bfa3cc3cc5c6a7
3
+ size 261784
robot/mjlab/meshes/jaw.stl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:19f150f6b040be2fa6e7532028c0d64d30c9fe014ecd5c0c3e306d9d274c0bae
3
+ size 123984
robot/mjlab/meshes/jaw_soft.stl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:6c3ebb578ea5e6d7b995baca9b42ef7c9f9858b56dedf6f0d949f5e6ea9d3ef2
3
+ size 84084
robot/mjlab/meshes/left_shell.stl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:bbce72c8fbbb1afb08600a000b102a5885a1fa3a7406056dbd6ead5fecef31a5
3
+ size 259034
robot/mjlab/meshes/leg.stl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:8e746228a2b0b664fd011677bc1bb1a87446f7b66b7f003dbf6a55a8f2c79de9
3
+ size 184784
robot/mjlab/meshes/lens.stl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:b9b86fce70198040781a739e2c15d2996b549b25d73b7544005ae08a415b486e
3
+ size 23484
robot/mjlab/meshes/m12_lens_holder.stl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:8760c127946f6be365f107053eaba5763aeb4e13d7278836f2e99dfcd8191f0e
3
+ size 137484
robot/mjlab/meshes/motor_support.stl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:d6725004f0a85a5cf0161214ea0d39fb6f14ee3563c9a96cd5aad725b5e2a716
3
+ size 45284
robot/mjlab/meshes/neck.stl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:1e6b27ab0a20b9765465d6d20f8549718a552e1fb3fc867d43320e2190aec44d
3
+ size 18384
robot/mjlab/meshes/neck_pitch.stl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:eb9086636a26d9c5a7ebc8b0e44bd3b458c9c89142e8acffcc431b46b70e9a5d
3
+ size 89084
robot/mjlab/meshes/noenoeil.stl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:55f3167627f6fbd1a19966f92396adf2ccd55795b13ded3b9f52705ba8a7ab77
3
+ size 12384
robot/mjlab/meshes/np_f970.stl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:78db51461cff3f125a905042b6117b6ee15c5648c77ecfde2f061aaab4dd89c6
3
+ size 260134
robot/mjlab/meshes/pcb__raspberry_pi_zero_2_w.stl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:50a365f079c423e5e35cb7b6edbe1bfa6abdea89dd3e17146d579005f8e5a511
3
+ size 262184
robot/mjlab/meshes/power_support.stl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:39d23ce00b9182ec01ad0a96c629225e501eb090f3cdc7074209b18f80359b0f
3
+ size 160484
robot/mjlab/meshes/right_shell.stl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:c2131ef20ffaf39bf0e33558e66f792265c1f26d40c203110f1c8dadbacb31e2
3
+ size 259984
robot/mjlab/meshes/seeed_bearing__configuration__22x16x4.stl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:8551dc99cbdc012bd261c2a175f2cc766dd6aed0786781b35f8381cd3aa95b34
3
+ size 260134
robot/mjlab/meshes/seeed_bearing__configuration_default.stl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:e4338e0e6c7adeb52a35e02d6fa5babe761e8752ffa0fc2e31b0ba672c18630d
3
+ size 260084
robot/mjlab/meshes/soft_mouth_top.stl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:451f53d30350552cb6e26f777698272f7c3fa557f389adb24f94c75a201e67cd
3
+ size 98384
robot/mjlab/meshes/sole_left.stl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:357f80bb46855e3ce50f817705acd80471dae90b4ee6b4a0a948614a7daeddb1
3
+ size 197384
robot/mjlab/meshes/sole_right.stl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:07801107a36dbc1c2b83b3b64ad98be39ba662a8a7fa0b62a29119b201f41fe6
3
+ size 198684
robot/mjlab/meshes/speaker.stl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:0ef54df86c6093c166d73e33dc0b8364ef5643a7a1b1dacaba577be5a9c06c32
3
+ size 684
robot/mjlab/meshes/top_head_shell.stl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:32c7499f12f9871e5471dfc8dd117a277b1efd9450c882913d44a0462af50003
3
+ size 201584
robot/mjlab/meshes/trunk_base.stl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:bca03970d8251c7a489e7f52fa48c5dca3e59185533f269aa1bc786875b479a8
3
+ size 46284
robot/mjlab/meshes/upper_leg_left.stl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:0cad4994905a15fa6af9e890542d146c67533ab8baf767ec7f171a980e512c75
3
+ size 153184
robot/mjlab/meshes/upper_leg_right.stl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:5906134bcde398008361f80897bc1b037bca8a08cf73840720aeab60fa185b9f
3
+ size 153184
robot/mjlab/meshes/upper_leg_rigidity_plate.stl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:cc926157ae915beb0d87d06000f42ffd5680f656c941d5fc586324fcd8284b52
3
+ size 45084
robot/mjlab/meshes/xl330.stl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:49255c2f0e2e009b08c4f56792da35ff9caab0b3c00e6349d63a0fe59ca240d8
3
+ size 51584
robot/mjlab/meshes/yaw2roll.stl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:ec47cd77c456631a5fddabfaec1c7f634806a9343ca1fa3a70cdcac5ecfa3baf
3
+ size 155084
robot/mjlab/meshes/yaw_roll_motion.stl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:41570530b2590e24ba38015cee853d3b17dd3059d782183299a23dea56c2c31c
3
+ size 72684
robot/mjlab/robot_allcollisions.xml ADDED
@@ -0,0 +1,434 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" ?>
2
+ <!-- Generated using onshape-to-robot -->
3
+ <!-- Onshape https://cad.onshape.com/documents/804927696f06d877f3f1803e/w/5b75db19292e71970de02dee/e/ef6e972847fec8d82570b35e -->
4
+ <mujoco model="microduck">
5
+ <compiler angle="radian" meshdir="assets" autolimits="true"/>
6
+ <default>
7
+ <default class="microduck">
8
+ <joint frictionloss="0.1" armature="0.005"/>
9
+ <position kp="50" dampratio="1"/>
10
+ <default class="visual">
11
+ <geom type="mesh" contype="0" conaffinity="0" group="2"/>
12
+ </default>
13
+ <default class="collision">
14
+ <geom group="3"/>
15
+ </default>
16
+ </default>
17
+ </default>
18
+ <!-- Additional joints_properties.xml -->
19
+ <default>
20
+ <default class="perfect_actuator">
21
+ <geom contype="0" conaffinity="0"/>
22
+ <joint damping="0.15" frictionloss="0.1" armature="0.001"/>
23
+ <position kp="10.0" kv="0.1" forcerange="-20.0 20.0"/>
24
+ </default>
25
+ <default class="chosen_actuator_old">
26
+ <geom contype="0" conaffinity="0"/>
27
+ <joint damping="0.048" frictionloss="0.006" armature="0.002"/>
28
+ <position kp="0.52" kv="0.0" forcerange="-0.91 0.91" ctrlrange="-10.0 10.0"/>
29
+ </default>
30
+ <default class="chosen_actuator_new">
31
+ <!-- removing contaminated data -->
32
+ <geom contype="0" conaffinity="0"/>
33
+ <joint damping="0.041" frictionloss="0.032" armature="0.002"/>
34
+ <position kp="0.386" kv="0.0" forcerange="-0.67 0.67" ctrlrange="-10.0 10.0"/>
35
+ </default>
36
+ <default class="chosen_actuator_antoine">
37
+ <geom contype="0" conaffinity="0"/>
38
+ <joint damping="0.044" frictionloss="0.013" armature="0.0017"/>
39
+ <position kp="0.43" kv="0.0" forcerange="-0.75 0.75" ctrlrange="-10.0 10.0"/>
40
+ </default>
41
+ <default class="chosen_actuator">
42
+ <!-- marc -->
43
+ <geom contype="0" conaffinity="0"/>
44
+ <joint damping="0.053" frictionloss="0.0048" armature="0.0018"/>
45
+ <!-- 200 kp -->
46
+ <position kp="0.55" kv="0.0" forcerange="-0.96 0.96" ctrlrange="-10.0 10.0"/>
47
+ <!-- 125 kp -->
48
+ <!-- <position kp="0.35" kv="0.0" forcerange="-0.96 0.96" ctrlrange="-10.0 10.0"/> -->
49
+ </default>
50
+ <default class="passive_wheel">
51
+ <geom contype="0" conaffinity="0"/>
52
+ <joint damping="0.0" frictionloss="0.0" armature="0.0001"/>
53
+ </default>
54
+ <default class="passive_joint">
55
+ <geom contype="0" conaffinity="0"/>
56
+ <joint damping="0.0" frictionloss="0.0" armature="0.0001"/>
57
+ </default>
58
+ </default>
59
+ <!-- Additional sensors.xml -->
60
+ <sensor>
61
+ <framequat name="orientation" objtype="site" noise="0.001" objname="imu"/>
62
+ <gyro name="angular-velocity" site="imu" noise="0.005"/>
63
+ <gyro name="imu_ang_vel" site="imu"/>
64
+ <velocimeter name="imu_lin_vel" site="imu"/>
65
+ <accelerometer name="imu_accel" site="imu"/>
66
+ <subtreeangmom name="root_angmom" body="trunk_base"/>
67
+ </sensor>
68
+ <!-- Additional additional.xml -->
69
+ <default>
70
+ <default class="self_collision_only">
71
+ <geom group="3" contype="2" conaffinity="2"/>
72
+ </default>
73
+ <equality solref="0.002 1" solimp="0.99 0.999 0.0005 0.5 2"/>
74
+ </default>
75
+ <!-- <contact> -->
76
+ <!-- Jaw collision mesh overlaps the bottom head shell by ~2cm because the closed -->
77
+ <!-- loop linkage positions it inside the head. Without these excludes the -->
78
+ <!-- self-collision sensor reports a constant 2 contacts, giving a -->
79
+ <!-- permanent ~-2.0 reward penalty in the standup env. -->
80
+ <!-- <exclude body1="jaw" body2="bottom_head_shell"/> -->
81
+ <!-- </contact> -->
82
+ <worldbody>
83
+ <!-- Link trunk_base -->
84
+ <body name="trunk_base" pos="0 0 0.12" quat="1 0 0 0" childclass="microduck">
85
+ <freejoint name="trunk_base_freejoint"/>
86
+ <inertial pos="-0.0226332 -2.70828e-05 0.00280064" mass="0.199224" fullinertia="0.000123975 0.000145931 0.000115351 7.1651e-08 -2.06915e-05 1.32429e-07"/>
87
+ <!-- Part seeed_bearing__configuration__22x16x4 -->
88
+ <geom type="mesh" class="visual" pos="0.006 0.0175 -0.005" quat="1 -0 -0 0" mesh="seeed_bearing__configuration__22x16x4" material="seeed_bearing__configuration__22x16x4_material"/>
89
+ <!-- Part right_shell -->
90
+ <geom type="mesh" class="visual" pos="0.004 -0.0005 -0.0175" quat="0.707107 -0 -0 0.707107" mesh="right_shell" material="right_shell_material"/>
91
+ <!-- Part banana_pcb_locker -->
92
+ <geom type="mesh" class="visual" pos="0.0042 -0 -0.0175215" quat="0.707107 -0 0 0.707107" mesh="banana_pcb_locker" material="banana_pcb_locker_material"/>
93
+ <!-- Part trunk_base -->
94
+ <geom type="mesh" class="visual" pos="0.004 -0 -0.0175" quat="0.707107 -0 -0 0.707107" mesh="trunk_base" material="trunk_base_material"/>
95
+ <!-- Part xl330 -->
96
+ <geom type="mesh" class="visual" pos="0.006 0.0175 0.0115" quat="0 0.707107 -0 0.707107" mesh="xl330" material="xl330_material"/>
97
+ <!-- Part power_support -->
98
+ <geom type="mesh" class="visual" pos="0.004 -0 -0.0175215" quat="0.707107 -0 0 0.707107" mesh="power_support" material="power_support_material"/>
99
+ <geom type="mesh" class="self_collision_only" pos="0.004 -0 -0.0175215" quat="0.707107 -0 0 0.707107" mesh="power_support" material="power_support_material"/>
100
+ <!-- Part np_f970 -->
101
+ <geom type="mesh" class="visual" pos="-0.0359693 0 -0.00385049" quat="0.00617059 0.70708 -0.70708 -0.00617059" mesh="np_f970" material="np_f970_material"/>
102
+ <geom type="mesh" class="collision" pos="-0.0359693 0 -0.00385049" quat="0.00617059 0.70708 -0.70708 -0.00617059" mesh="np_f970" material="np_f970_material"/>
103
+ <!-- Part xl330_2 -->
104
+ <geom type="mesh" class="visual" pos="0.006 -0.0175 0.0115" quat="0 0.707107 -0 0.707107" mesh="xl330" material="xl330_material"/>
105
+ <!-- Part left_shell -->
106
+ <geom type="mesh" class="visual" pos="0.004 -0 -0.0175785" quat="0.707107 0 -0 0.707107" mesh="left_shell" material="left_shell_material"/>
107
+ <!-- Part seeed_bearing__configuration__22x16x4_2 -->
108
+ <geom type="mesh" class="visual" pos="0.006 -0.0175 -0.005" quat="1 -0 -0 0" mesh="seeed_bearing__configuration__22x16x4" material="seeed_bearing__configuration__22x16x4_material"/>
109
+ <!-- Frame imu -->
110
+ <site group="3" name="imu" pos="-0.021 6.64098e-05 -0.0146984" quat="1 -0 -0 0"/>
111
+ <!-- Link yaw2roll -->
112
+ <body name="yaw2roll" pos="0.006 0.0175 -0.005" quat="0 -0.707107 -0.707107 -0">
113
+ <!-- Joint from trunk_base to yaw2roll -->
114
+ <joint axis="0 0 1" name="left_hip_yaw" type="hinge" range="-0.4363323129985824 0.5235987755982988" class="chosen_actuator"/>
115
+ <inertial pos="-8.8e-11 0.00099414 0.0168917" mass="0.0230406" fullinertia="4.1964e-06 3.41752e-06 2.3265e-06 -0 -0 -5.0881e-08"/>
116
+ <!-- Part yaw2roll -->
117
+ <geom type="mesh" class="visual" pos="-0.0175 -0.002 0.0125" quat="0 1 0 0" mesh="yaw2roll" material="yaw2roll_material"/>
118
+ <!-- Part xl330_3 -->
119
+ <geom type="mesh" class="visual" pos="-0 0 0.0125" quat="0 -0.707107 0.707107 -0" mesh="xl330" material="xl330_material"/>
120
+ <!-- Part seeed_bearing__configuration__22x16x4_3 -->
121
+ <geom type="mesh" class="visual" pos="-0 0.0165 0.0125" quat="0.5 0.5 0.5 -0.5" mesh="seeed_bearing__configuration__22x16x4" material="seeed_bearing__configuration__22x16x4_material"/>
122
+ <!-- Part bearing_roll -->
123
+ <geom type="mesh" class="visual" pos="-0.0175 -0.002 0.0125" quat="0 -1 -0 -0" mesh="bearing_roll" material="bearing_roll_material"/>
124
+ <!-- Link hip_l -->
125
+ <body name="hip_l" pos="0 0.0165 0.0125" quat="0.707107 -0.707107 -0 -0">
126
+ <!-- Joint from yaw2roll to hip_l -->
127
+ <joint axis="0 0 1" name="left_hip_roll" type="hinge" range="-0.3839724354387525 0.3839724354387525" class="chosen_actuator"/>
128
+ <inertial pos="0.014815 1.6179e-08 -0.00829992" mass="0.00618934" fullinertia="8.60626e-07 1.15753e-06 6.51588e-07 1e-12 3.89882e-07 -1e-12"/>
129
+ <!-- Part seeed_bearing__configuration__22x16x4_4 -->
130
+ <geom type="mesh" class="visual" pos="0.021 0 -0.0185" quat="0.5 0.5 0.5 0.5" mesh="seeed_bearing__configuration__22x16x4" material="seeed_bearing__configuration__22x16x4_material"/>
131
+ <!-- Part hip_l -->
132
+ <geom type="mesh" class="visual" pos="-0.0175 0 -0.0185" quat="0.707107 -0.707107 0 -0" mesh="hip_l" material="hip_l_material"/>
133
+ <geom type="mesh" class="collision" pos="-0.0175 0 -0.0185" quat="0.707107 -0.707107 0 -0" mesh="hip_l" material="hip_l_material"/>
134
+ <!-- Link upper_leg_left -->
135
+ <body name="upper_leg_left" pos="0.025 0 -0.0185" quat="0.5 -0.5 0.5 -0.5">
136
+ <!-- Joint from hip_l to upper_leg_left -->
137
+ <joint axis="0 0 1" name="left_hip_pitch" type="hinge" range="-1.570796326795005 1.5707963267947882" class="chosen_actuator"/>
138
+ <inertial pos="0.006766 0.0228838 0.0134531" mass="0.0482067" fullinertia="1.68753e-05 1.03691e-05 1.98269e-05 -4.41852e-06 2.27982e-07 3.89298e-07"/>
139
+ <!-- Part upper_leg_left -->
140
+ <geom type="mesh" class="visual" pos="-0 -0 -0.0425" quat="0.5 -0.5 -0.5 -0.5" mesh="upper_leg_left" material="upper_leg_left_material"/>
141
+ <!-- Part xl330_4 -->
142
+ <geom type="mesh" class="visual" pos="0.022 0.0357771 0.0125" quat="0 -0.707107 -0 -0.707107" mesh="xl330" material="xl330_material"/>
143
+ <!-- Part upper_leg_rigidity_plate -->
144
+ <geom type="mesh" class="visual" pos="0 -0 -0.0425" quat="0.5 -0.5 -0.5 -0.5" mesh="upper_leg_rigidity_plate" material="upper_leg_rigidity_plate_material"/>
145
+ <!-- Part seeed_bearing__configuration__22x16x4_5 -->
146
+ <geom type="mesh" class="visual" pos="0.022 0.0357771 -0.004" quat="0 0 0 1" mesh="seeed_bearing__configuration__22x16x4" material="seeed_bearing__configuration__22x16x4_material"/>
147
+ <!-- Part xl330_5 -->
148
+ <geom type="mesh" class="visual" pos="-0 -0 0.0125" quat="0.5 0.5 -0.5 0.5" mesh="xl330" material="xl330_material"/>
149
+ <!-- Link leg -->
150
+ <body name="leg" pos="0.022 0.0357771 -0.004" quat="0 0.707107 0.707107 0">
151
+ <!-- Joint from upper_leg_left to leg -->
152
+ <joint axis="0 0 1" name="left_knee" type="hinge" range="-1.570796326795012 1.570796326794781" class="chosen_actuator"/>
153
+ <inertial pos="-1.52e-10 0.0318937 -0.00953505" mass="0.0215844" fullinertia="5.25056e-06 2.15507e-06 4.33925e-06 -1e-12 0 7.65005e-07"/>
154
+ <!-- Part leg -->
155
+ <geom type="mesh" class="visual" pos="-0.022 0.0357771 0.0385" quat="0.5 0.5 0.5 -0.5" mesh="leg" material="leg_material"/>
156
+ <geom type="mesh" class="collision" pos="-0.022 0.0357771 0.0385" quat="0.5 0.5 0.5 -0.5" mesh="leg" material="leg_material"/>
157
+ <!-- Part xl330_6 -->
158
+ <geom type="mesh" class="visual" pos="0 0.042 -0.0115" quat="0.5 -0.5 -0.5 -0.5" mesh="xl330" material="xl330_material"/>
159
+ <!-- Link ankle_left -->
160
+ <body name="ankle_left" pos="0 0.042 -0.026" quat="0 1 -0 0">
161
+ <!-- Joint from leg to ankle_left -->
162
+ <joint axis="0 0 1" name="left_ankle" type="hinge" range="-1.5707963267949019 1.5707963267948912" class="chosen_actuator"/>
163
+ <inertial pos="-0.00615738 -0.0137483 -0.0156586" mass="0.0300246" fullinertia="5.91525e-06 1.11534e-05 7.74015e-06 -2.0535e-07 8.2152e-08 5.6714e-08"/>
164
+ <!-- Part sole_left -->
165
+ <geom type="mesh" class="visual" pos="-0.022 0.00622291 -0.0647" quat="0.5 -0.5 -0.5 -0.5" mesh="sole_left" material="sole_left_material"/>
166
+ <geom type="mesh" name="left_foot_collision" class="collision" pos="-0.022 0.00622291 -0.0647" quat="0.5 -0.5 -0.5 -0.5" mesh="sole_left" material="sole_left_material"/>
167
+ <!-- Part foot_left -->
168
+ <geom type="mesh" class="visual" pos="-0.022 0.00622291 -0.0647" quat="0.5 -0.5 -0.5 -0.5" mesh="foot_left" material="foot_left_material"/>
169
+ <!-- Part seeed_bearing__configuration_default -->
170
+ <geom type="mesh" class="visual" pos="0 0 -0.0322" quat="0 0 -0 1" mesh="seeed_bearing__configuration_default" material="seeed_bearing__configuration_default_material"/>
171
+ <!-- Part ankle_left -->
172
+ <geom type="mesh" class="visual" pos="-0.022 0.00622291 -0.0647" quat="0.5 -0.5 -0.5 -0.5" mesh="ankle_left" material="ankle_left_material"/>
173
+ <!-- Frame left_foot -->
174
+ <site group="3" name="left_foot" pos="-0 -0.0238146 -0.0140852" quat="0 0 0.707107 0.707107"/>
175
+ </body>
176
+ </body>
177
+ </body>
178
+ </body>
179
+ </body>
180
+ <!-- Link neck -->
181
+ <body name="neck" pos="0.026 0.0145 0.0324215" quat="0 0 0.707107 -0.707107">
182
+ <!-- Joint from trunk_base to neck -->
183
+ <joint axis="0 0 1" name="neck_pitch" type="hinge" range="-1.5707963267948966 1.0471975511965976" class="chosen_actuator"/>
184
+ <inertial pos="0 -0.025 0.0141665" mass="0.0368414" fullinertia="1.72925e-05 3.12694e-06 1.63707e-05 0 0 0"/>
185
+ <!-- Part neck -->
186
+ <geom type="mesh" class="visual" pos="0.022 0.05 0.028" quat="0.5 0.5 0.5 -0.5" mesh="neck" material="neck_material"/>
187
+ <!-- Part xl330_7 -->
188
+ <geom type="mesh" class="visual" pos="0 0 0.0145" quat="0.5 -0.5 -0.5 -0.5" mesh="xl330" material="xl330_material"/>
189
+ <!-- Part xl330_8 -->
190
+ <geom type="mesh" class="visual" pos="0 -0.05 0.0145" quat="0.5 0.5 -0.5 0.5" mesh="xl330" material="xl330_material"/>
191
+ <!-- Part neck_2 -->
192
+ <geom type="mesh" class="visual" pos="0.022 0.05 0.003" quat="0.5 0.5 0.5 -0.5" mesh="neck" material="neck_material"/>
193
+ <!-- Link neck_pitch -->
194
+ <body name="neck_pitch" pos="0 -0.05 -0" quat="0 1 0 0">
195
+ <!-- Joint from neck to neck_pitch -->
196
+ <joint axis="0 0 1" name="head_pitch" type="hinge" range="-1.5707963267948974 1.5707963267948957" class="chosen_actuator"/>
197
+ <inertial pos="-6.533e-09 0.0116641 -0.0145" mass="0.00572" fullinertia="1.09186e-06 9.66855e-07 5.12111e-07 0 0 0"/>
198
+ <!-- Part neck_pitch -->
199
+ <geom type="mesh" class="visual" pos="-0 0.0287931 -0.0145" quat="0.5 -0.5 -0.5 -0.5" mesh="neck_pitch" material="neck_pitch_material"/>
200
+ <!-- Part seeed_bearing__configuration__22x16x4_6 -->
201
+ <geom type="mesh" class="visual" pos="-0 0.0207931 -0.0145" quat="0.707107 0.707107 0 0" mesh="seeed_bearing__configuration__22x16x4" material="seeed_bearing__configuration__22x16x4_material"/>
202
+ <!-- Link yaw_roll_motion -->
203
+ <body name="yaw_roll_motion" pos="-0 0.0186931 -0.0145" quat="0 0 -0.707107 -0.707107">
204
+ <!-- Joint from neck_pitch to yaw_roll_motion -->
205
+ <joint axis="0 0 1" name="head_yaw" type="hinge" range="-2.967059728390373 2.967059728390348" class="chosen_actuator"/>
206
+ <inertial pos="-0.000166657 -0.00500964 0.0178065" mass="0.0486" fullinertia="6.97903e-06 8.10293e-06 9.57884e-06 3.21657e-07 -3.4797e-08 3.4295e-07"/>
207
+ <!-- Part yaw_roll_motion -->
208
+ <geom type="mesh" class="visual" pos="0 0 0.01" quat="0.707107 0 -0 0.707107" mesh="yaw_roll_motion" material="yaw_roll_motion_material"/>
209
+ <!-- Part xl330_9 -->
210
+ <geom type="mesh" class="visual" pos="0 0 0.0145" quat="0.5 -0.5 -0.5 -0.5" mesh="xl330" material="xl330_material"/>
211
+ <!-- Part seeed_bearing__configuration__22x16x4_7 -->
212
+ <geom type="mesh" class="visual" pos="-0.016 0 0.0145" quat="0.707107 -0 -0.707107 -0" mesh="seeed_bearing__configuration__22x16x4" material="seeed_bearing__configuration__22x16x4_material"/>
213
+ <!-- Part seeed_bearing__configuration__22x16x4_8 -->
214
+ <geom type="mesh" class="visual" pos="0.018 0 0.0145" quat="0.707107 -0 -0.707107 -0" mesh="seeed_bearing__configuration__22x16x4" material="seeed_bearing__configuration__22x16x4_material"/>
215
+ <!-- Link jaw_soft -->
216
+ <body name="jaw_soft" pos="-0.0179 0 0.0145" quat="0.707107 -0 -0.707107 -0">
217
+ <!-- Joint from yaw_roll_motion to jaw_soft -->
218
+ <joint axis="0 0 1" name="head_roll" type="hinge" range="-0.43633231299859127 0.4363323129985735" class="chosen_actuator"/>
219
+ <inertial pos="0.00460523 0.00120047 -0.0324761" mass="0.188766" fullinertia="0.000320812 0.0002541 0.000149882 -6.32292e-07 6.57154e-06 4.18476e-06"/>
220
+ <!-- Part lens -->
221
+ <geom type="mesh" class="visual" pos="0.0155 -9.13778e-05 -0.05548" quat="0.5 -0.5 0.5 -0.5" mesh="lens" material="lens_material"/>
222
+ <!-- Part face_part -->
223
+ <geom type="mesh" class="visual" pos="-0.0045 -9.13778e-05 -0.0178" quat="0.5 0.5 0.5 0.5" mesh="face_part" material="face_part_material"/>
224
+ <!-- Part xl330_10 -->
225
+ <geom type="mesh" class="visual" pos="0.003 0.0255 -0.018" quat="0.707107 0 -0 -0.707107" mesh="xl330" material="xl330_material"/>
226
+ <!-- Part jaw_soft -->
227
+ <geom type="mesh" class="visual" pos="-0.0046 0 -0.0179996" quat="0.5 0.5 0.5 0.5" mesh="jaw_soft" material="jaw_soft_material"/>
228
+ <!-- Part motor_support -->
229
+ <geom type="mesh" class="visual" pos="-0.0045 0 -0.018" quat="0.5 0.5 0.5 0.5" mesh="motor_support" material="motor_support_material"/>
230
+ <!-- Part top_head_shell -->
231
+ <geom type="mesh" class="visual" pos="-0.0045 -0 -0.018" quat="0.5 0.5 0.5 0.5" mesh="top_head_shell" material="top_head_shell_material"/>
232
+ <geom type="mesh" class="collision" pos="-0.0045 -0 -0.018" quat="0.5 0.5 0.5 0.5" mesh="top_head_shell" material="top_head_shell_material"/>
233
+ <!-- Part xl330_11 -->
234
+ <geom type="mesh" class="visual" pos="0 0 0.0145" quat="0.5 -0.5 -0.5 -0.5" mesh="xl330" material="xl330_material"/>
235
+ <!-- Part seeed_bearing__configuration_default_2 -->
236
+ <geom type="mesh" class="visual" pos="0.0032 -0.04 -0.018" quat="0.5 -0.5 0.5 0.5" mesh="seeed_bearing__configuration_default" material="seeed_bearing__configuration_default_material"/>
237
+ <!-- Part elec_rpi_robot_hat_pcb -->
238
+ <geom type="mesh" class="visual" pos="0.02694 0.0289936 -0.0519" quat="0.707107 0 0 -0.707107" mesh="elec_rpi_robot_hat_pcb" material="elec_rpi_robot_hat_pcb_material"/>
239
+ <!-- Part soft_mouth_top -->
240
+ <geom type="mesh" class="visual" pos="-0.00420071 1.7776e-08 -0.0180814" quat="0.5 0.5 0.5 0.5" mesh="soft_mouth_top" material="soft_mouth_top_material"/>
241
+ <!-- Part noenoeil -->
242
+ <geom type="mesh" class="visual" pos="-0.0045 -9.13778e-05 -0.0178" quat="0.5 0.5 0.5 0.5" mesh="noenoeil" material="noenoeil_material"/>
243
+ <!-- Part jaw -->
244
+ <geom type="mesh" class="visual" pos="-0.0045 0 -0.018" quat="0.5 0.5 0.5 0.5" mesh="jaw" material="jaw_material"/>
245
+ <geom type="mesh" class="collision" pos="-0.0045 0 -0.018" quat="0.5 0.5 0.5 0.5" mesh="jaw" material="jaw_material"/>
246
+ <!-- Part bottom_head_shell -->
247
+ <geom type="mesh" class="visual" pos="-0.0043 -0 -0.018" quat="0.5 0.5 0.5 0.5" mesh="bottom_head_shell" material="bottom_head_shell_material"/>
248
+ <geom type="mesh" class="collision" pos="-0.0043 -0 -0.018" quat="0.5 0.5 0.5 0.5" mesh="bottom_head_shell" material="bottom_head_shell_material"/>
249
+ <!-- Part m12_lens_holder -->
250
+ <geom type="mesh" class="visual" pos="0.0155 -9.13778e-05 -0.05558" quat="0.5 -0.5 0.5 -0.5" mesh="m12_lens_holder" material="m12_lens_holder_material"/>
251
+ <!-- Part pcb__raspberry_pi_zero_2_w -->
252
+ <geom type="mesh" class="visual" pos="0.015435 -1.13778e-05 -0.06001" quat="0.5 -0.5 -0.5 0.5" mesh="pcb__raspberry_pi_zero_2_w" material="pcb__raspberry_pi_zero_2_w_material"/>
253
+ <!-- Part speaker -->
254
+ <geom type="mesh" class="visual" pos="0.01 -0.016786 0.0260226" quat="0.5 0.5 0.5 0.5" mesh="speaker" material="speaker_material"/>
255
+ <!-- Frame head_camera -->
256
+ <site group="3" name="head_camera" pos="0.0155 -9.13778e-05 -0.0733" quat="0.707107 0 0.707107 -0"/>
257
+ <camera name="head_camera" pos="0.0155 -9.13778e-05 -0.0733" quat="0 0 -1 0"/>
258
+ <!-- Frame tof -->
259
+ <site group="3" name="tof" pos="0.0135 0.0224086 -0.0733" quat="0.707107 0 0.707107 -0"/>
260
+ <!-- Frame head_imu -->
261
+ <site group="3" name="head_imu" pos="0.0152323 0.000111069 -0.05106" quat="1 0 0 -0"/>
262
+ <!-- Frame mouth_tip -->
263
+ <site group="3" name="mouth_tip" pos="-0.00809334 -0 -0.0777383" quat="0.704015 0 0.710185 -0"/>
264
+ </body>
265
+ </body>
266
+ </body>
267
+ </body>
268
+ <!-- Link bearing_roll -->
269
+ <body name="bearing_roll" pos="0.006 -0.0175 -0.005" quat="0 0.707107 0.707107 0">
270
+ <!-- Joint from trunk_base to bearing_roll -->
271
+ <joint axis="0 0 1" name="right_hip_yaw" type="hinge" range="-0.5235987755982988 0.4363323129985824" class="chosen_actuator"/>
272
+ <inertial pos="-8.8e-11 0.00099414 0.0168917" mass="0.0230406" fullinertia="4.1964e-06 3.41752e-06 2.3265e-06 -0 -0 -5.0881e-08"/>
273
+ <!-- Part yaw2roll_2 -->
274
+ <geom type="mesh" class="visual" pos="-0.0175 -0.002 0.0125" quat="0 1 0 0" mesh="yaw2roll" material="yaw2roll_material"/>
275
+ <!-- Part xl330_12 -->
276
+ <geom type="mesh" class="visual" pos="-0 0 0.0125" quat="0 -0.707107 0.707107 -0" mesh="xl330" material="xl330_material"/>
277
+ <!-- Part bearing_roll_2 -->
278
+ <geom type="mesh" class="visual" pos="-0.0175 -0.002 0.0125" quat="0 -1 -0 -0" mesh="bearing_roll" material="bearing_roll_material"/>
279
+ <!-- Part seeed_bearing__configuration__22x16x4_9 -->
280
+ <geom type="mesh" class="visual" pos="-0 0.0165 0.0125" quat="0.5 0.5 0.5 -0.5" mesh="seeed_bearing__configuration__22x16x4" material="seeed_bearing__configuration__22x16x4_material"/>
281
+ <!-- Link hip_l_2 -->
282
+ <body name="hip_l_2" pos="-0 0.0165 0.0125" quat="0 -0 0.707107 0.707107">
283
+ <!-- Joint from bearing_roll to hip_l_2 -->
284
+ <joint axis="0 0 1" name="right_hip_roll" type="hinge" range="-0.3839724354387525 0.3839724354387525" class="chosen_actuator"/>
285
+ <inertial pos="0.014815 1.6212e-08 -0.00829992" mass="0.00618934" fullinertia="8.60626e-07 1.15753e-06 6.51588e-07 1e-12 3.89882e-07 -1e-12"/>
286
+ <!-- Part hip_l_2 -->
287
+ <geom type="mesh" class="visual" pos="-0.0175 0 -0.0185" quat="0.707107 -0.707107 -0 -0" mesh="hip_l" material="hip_l_material"/>
288
+ <geom type="mesh" class="collision" pos="-0.0175 0 -0.0185" quat="0.707107 -0.707107 -0 -0" mesh="hip_l" material="hip_l_material"/>
289
+ <!-- Part seeed_bearing__configuration__22x16x4_10 -->
290
+ <geom type="mesh" class="visual" pos="0.025 0 -0.0185" quat="0.5 0.5 -0.5 -0.5" mesh="seeed_bearing__configuration__22x16x4" material="seeed_bearing__configuration__22x16x4_material"/>
291
+ <!-- Link upper_leg_right -->
292
+ <body name="upper_leg_right" pos="0.025 0 -0.0185" quat="0.5 -0.5 0.5 -0.5">
293
+ <!-- Joint from hip_l_2 to upper_leg_right -->
294
+ <joint axis="0 0 1" name="right_hip_pitch" type="hinge" range="-1.5707963267949268 1.5707963267948664" class="chosen_actuator"/>
295
+ <inertial pos="-0.006766 0.0228838 0.0134531" mass="0.0482067" fullinertia="1.68753e-05 1.03691e-05 1.98269e-05 4.41852e-06 -2.27982e-07 3.89298e-07"/>
296
+ <!-- Part xl330_13 -->
297
+ <geom type="mesh" class="visual" pos="-0.022 0.0357771 0.0125" quat="0.707107 -0 -0.707107 -0" mesh="xl330" material="xl330_material"/>
298
+ <!-- Part seeed_bearing__configuration__22x16x4_11 -->
299
+ <geom type="mesh" class="visual" pos="-0.022 0.0357771 0" quat="0 1 -0 -0" mesh="seeed_bearing__configuration__22x16x4" material="seeed_bearing__configuration__22x16x4_material"/>
300
+ <!-- Part upper_leg_rigidity_plate_2 -->
301
+ <geom type="mesh" class="visual" pos="0 0 0.0435" quat="0.5 -0.5 0.5 0.5" mesh="upper_leg_rigidity_plate" material="upper_leg_rigidity_plate_material"/>
302
+ <!-- Part upper_leg_right -->
303
+ <geom type="mesh" class="visual" pos="-0 0 -0.0425" quat="0.5 -0.5 0.5 0.5" mesh="upper_leg_right" material="upper_leg_right_material"/>
304
+ <!-- Part xl330_14 -->
305
+ <geom type="mesh" class="visual" pos="0 0 0.0125" quat="0.5 0.5 -0.5 0.5" mesh="xl330" material="xl330_material"/>
306
+ <!-- Link leg_2 -->
307
+ <body name="leg_2" pos="-0.022 0.0357771 -0.004" quat="0 1 -0 -0">
308
+ <!-- Joint from upper_leg_right to leg_2 -->
309
+ <joint axis="0 0 1" name="right_knee" type="hinge" range="-1.570796326794932 1.570796326794861" class="chosen_actuator"/>
310
+ <inertial pos="-0.0318937 -1.52e-10 -0.00953505" mass="0.0215844" fullinertia="2.15507e-06 5.25056e-06 4.33925e-06 1e-12 -7.65005e-07 0"/>
311
+ <!-- Part xl330_15 -->
312
+ <geom type="mesh" class="visual" pos="-0.042 0 -0.0115" quat="0.707107 -0 -0.707107 -0" mesh="xl330" material="xl330_material"/>
313
+ <!-- Part leg_2 -->
314
+ <geom type="mesh" class="visual" pos="-0.0357771 -0.022 0.0385" quat="0.707107 0 0.707107 -0" mesh="leg" material="leg_material"/>
315
+ <geom type="mesh" class="collision" pos="-0.0357771 -0.022 0.0385" quat="0.707107 0 0.707107 -0" mesh="leg" material="leg_material"/>
316
+ <!-- Link ankle_right -->
317
+ <body name="ankle_right" pos="-0.042 0 -0.026" quat="0 -0.707107 -0.707107 0">
318
+ <!-- Joint from leg_2 to ankle_right -->
319
+ <joint axis="0 0 1" name="right_ankle" type="hinge" range="-1.5707963267949054 1.5707963267948877" class="chosen_actuator"/>
320
+ <inertial pos="0.00615539 -0.0137482 -0.0156566" mass="0.0300251" fullinertia="5.91534e-06 1.11534e-05 7.74016e-06 2.04877e-07 -8.247e-08 5.7017e-08"/>
321
+ <!-- Part ankle_right -->
322
+ <geom type="mesh" class="visual" pos="0.022 0.00622291 -0.0647" quat="0.5 -0.5 0.5 0.5" mesh="ankle_right" material="ankle_right_material"/>
323
+ <!-- Part sole_right -->
324
+ <geom type="mesh" class="visual" pos="0.022 0.00622291 -0.0647" quat="0.5 -0.5 0.5 0.5" mesh="sole_right" material="sole_right_material"/>
325
+ <geom type="mesh" name="right_foot_collision" class="collision" pos="0.022 0.00622291 -0.0647" quat="0.5 -0.5 0.5 0.5" mesh="sole_right" material="sole_right_material"/>
326
+ <!-- Part seeed_bearing__configuration_default_3 -->
327
+ <geom type="mesh" class="visual" pos="-0 0 -0.0292" quat="0 1 -0 -0" mesh="seeed_bearing__configuration_default" material="seeed_bearing__configuration_default_material"/>
328
+ <!-- Part foot_right -->
329
+ <geom type="mesh" class="visual" pos="0.022 0.00622291 -0.0647" quat="0.5 -0.5 0.5 0.5" mesh="foot_right" material="foot_right_material"/>
330
+ <!-- Frame right_foot -->
331
+ <site group="3" name="right_foot" pos="0 -0.0238146 -0.0140852" quat="0.707107 -0.707107 0 0"/>
332
+ </body>
333
+ </body>
334
+ </body>
335
+ </body>
336
+ </body>
337
+ </body>
338
+ </worldbody>
339
+ <asset>
340
+ <mesh file="seeed_bearing__configuration__22x16x4.stl"/>
341
+ <mesh file="sole_left.stl"/>
342
+ <mesh file="yaw2roll.stl"/>
343
+ <mesh file="sole_right.stl"/>
344
+ <mesh file="leg.stl"/>
345
+ <mesh file="xl330.stl"/>
346
+ <mesh file="noenoeil.stl"/>
347
+ <mesh file="pcb__raspberry_pi_zero_2_w.stl"/>
348
+ <mesh file="neck_pitch.stl"/>
349
+ <mesh file="foot_left.stl"/>
350
+ <mesh file="ankle_left.stl"/>
351
+ <mesh file="yaw_roll_motion.stl"/>
352
+ <mesh file="left_shell.stl"/>
353
+ <mesh file="m12_lens_holder.stl"/>
354
+ <mesh file="jaw_soft.stl"/>
355
+ <mesh file="elec_rpi_robot_hat_pcb.stl"/>
356
+ <mesh file="top_head_shell.stl"/>
357
+ <mesh file="lens.stl"/>
358
+ <mesh file="bottom_head_shell.stl"/>
359
+ <mesh file="motor_support.stl"/>
360
+ <mesh file="neck.stl"/>
361
+ <mesh file="np_f970.stl"/>
362
+ <mesh file="upper_leg_rigidity_plate.stl"/>
363
+ <mesh file="seeed_bearing__configuration_default.stl"/>
364
+ <mesh file="soft_mouth_top.stl"/>
365
+ <mesh file="trunk_base.stl"/>
366
+ <mesh file="hip_l.stl"/>
367
+ <mesh file="bearing_roll.stl"/>
368
+ <mesh file="banana_pcb_locker.stl"/>
369
+ <mesh file="speaker.stl"/>
370
+ <mesh file="ankle_right.stl"/>
371
+ <mesh file="upper_leg_right.stl"/>
372
+ <mesh file="right_shell.stl"/>
373
+ <mesh file="face_part.stl"/>
374
+ <mesh file="foot_right.stl"/>
375
+ <mesh file="power_support.stl"/>
376
+ <mesh file="upper_leg_left.stl"/>
377
+ <mesh file="jaw.stl"/>
378
+ <material name="seeed_bearing__configuration__22x16x4_material" rgba="0.713725 0.760784 0.8 1"/>
379
+ <material name="right_shell_material" rgba="0.8 0.8 0.8 1"/>
380
+ <material name="banana_pcb_locker_material" rgba="0.647059 0.647059 0.647059 1"/>
381
+ <material name="trunk_base_material" rgba="0.34902 0.376471 0.4 1"/>
382
+ <material name="xl330_material" rgba="0.286275 0.286275 0.286275 1"/>
383
+ <material name="power_support_material" rgba="0.901961 0.901961 0.901961 1"/>
384
+ <material name="np_f970_material" rgba="0.901961 0.901961 0.901961 1"/>
385
+ <material name="left_shell_material" rgba="0.8 0.8 0.8 1"/>
386
+ <material name="yaw2roll_material" rgba="0.647059 0.647059 0.647059 1"/>
387
+ <material name="bearing_roll_material" rgba="0.262745 0.282353 0.301961 1"/>
388
+ <material name="hip_l_material" rgba="0.901961 0.901961 0.901961 1"/>
389
+ <material name="upper_leg_left_material" rgba="0.866667 0.866667 0.866667 1"/>
390
+ <material name="upper_leg_rigidity_plate_material" rgba="0.537255 0.854902 0.827451 1"/>
391
+ <material name="leg_material" rgba="0.811765 0.858824 0.898039 1"/>
392
+ <material name="sole_left_material" rgba="0.537255 0.854902 0.827451 1"/>
393
+ <material name="foot_left_material" rgba="0.980392 0.713725 0.00392157 1"/>
394
+ <material name="seeed_bearing__configuration_default_material" rgba="0.713725 0.760784 0.8 1"/>
395
+ <material name="ankle_left_material" rgba="0.980392 0.713725 0.00392157 1"/>
396
+ <material name="neck_material" rgba="0.8 0.8 0.8 1"/>
397
+ <material name="neck_pitch_material" rgba="0.34902 0.376471 0.4 1"/>
398
+ <material name="yaw_roll_motion_material" rgba="0.768627 0.886275 0.952941 1"/>
399
+ <material name="lens_material" rgba="0.262745 0.282353 0.301961 1"/>
400
+ <material name="face_part_material" rgba="0.34902 0.376471 0.4 1"/>
401
+ <material name="jaw_soft_material" rgba="0.85098 0.756863 0.866667 1"/>
402
+ <material name="motor_support_material" rgba="0.262745 0.282353 0.301961 1"/>
403
+ <material name="top_head_shell_material" rgba="0.901961 0.901961 0.901961 1"/>
404
+ <material name="elec_rpi_robot_hat_pcb_material" rgba="0.313725 0.486275 0.411765 1"/>
405
+ <material name="soft_mouth_top_material" rgba="0.85098 0.756863 0.866667 1"/>
406
+ <material name="noenoeil_material" rgba="0.262745 0.282353 0.301961 1"/>
407
+ <material name="jaw_material" rgba="0.980392 0.713725 0.00392157 1"/>
408
+ <material name="bottom_head_shell_material" rgba="0.980392 0.713725 0.00392157 1"/>
409
+ <material name="m12_lens_holder_material" rgba="0.262745 0.282353 0.301961 1"/>
410
+ <material name="pcb__raspberry_pi_zero_2_w_material" rgba="0.498039 0.498039 0.498039 1"/>
411
+ <material name="speaker_material" rgba="0.294118 0.290196 0.290196 1"/>
412
+ <material name="upper_leg_right_material" rgba="0.866667 0.866667 0.866667 1"/>
413
+ <material name="ankle_right_material" rgba="0.980392 0.713725 0.00392157 1"/>
414
+ <material name="sole_right_material" rgba="0.537255 0.854902 0.827451 1"/>
415
+ <material name="foot_right_material" rgba="0.980392 0.713725 0.00392157 1"/>
416
+ </asset>
417
+ <actuator>
418
+ <position class="chosen_actuator" name="left_hip_yaw" joint="left_hip_yaw"/>
419
+ <position class="chosen_actuator" name="left_hip_roll" joint="left_hip_roll"/>
420
+ <position class="chosen_actuator" name="left_hip_pitch" joint="left_hip_pitch"/>
421
+ <position class="chosen_actuator" name="left_knee" joint="left_knee"/>
422
+ <position class="chosen_actuator" name="left_ankle" joint="left_ankle"/>
423
+ <position class="chosen_actuator" name="neck_pitch" joint="neck_pitch"/>
424
+ <position class="chosen_actuator" name="head_pitch" joint="head_pitch"/>
425
+ <position class="chosen_actuator" name="head_yaw" joint="head_yaw"/>
426
+ <position class="chosen_actuator" name="head_roll" joint="head_roll"/>
427
+ <position class="chosen_actuator" name="right_hip_yaw" joint="right_hip_yaw"/>
428
+ <position class="chosen_actuator" name="right_hip_roll" joint="right_hip_roll"/>
429
+ <position class="chosen_actuator" name="right_hip_pitch" joint="right_hip_pitch"/>
430
+ <position class="chosen_actuator" name="right_knee" joint="right_knee"/>
431
+ <position class="chosen_actuator" name="right_ankle" joint="right_ankle"/>
432
+ </actuator>
433
+ <equality/>
434
+ </mujoco>
style.css DELETED
@@ -1,28 +0,0 @@
1
- body {
2
- padding: 2rem;
3
- font-family: -apple-system, BlinkMacSystemFont, "Arial", sans-serif;
4
- }
5
-
6
- h1 {
7
- font-size: 16px;
8
- margin-top: 0;
9
- }
10
-
11
- p {
12
- color: rgb(107, 114, 128);
13
- font-size: 15px;
14
- margin-bottom: 10px;
15
- margin-top: 5px;
16
- }
17
-
18
- .card {
19
- max-width: 620px;
20
- margin: 0 auto;
21
- padding: 16px;
22
- border: 1px solid lightgray;
23
- border-radius: 16px;
24
- }
25
-
26
- .card p:last-child {
27
- margin-bottom: 0;
28
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
variants.js ADDED
@@ -0,0 +1,212 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Colour variants of the real Microduck robots (see the reference photo of
2
+ // the four printed units). Each variant fills the same semantic slots; the
3
+ // mesh-filename -> slot mapping lives in meshMaterialsFor(). All colours are
4
+ // linear-space RGB and slightly oversaturated: the renderer tone-maps with
5
+ // ACESFilmicToneMapping under warm key/rim lights, which desaturates and
6
+ // shifts hues toward gold, so the base colours lean past the target to land
7
+ // right on screen.
8
+
9
+ import * as THREE from "three";
10
+
11
+ // ── Shared slot specs (identical on all four robots) ───────────────────
12
+ // sRGB targets from the reference photo, converted to linear with a
13
+ // slight saturation boost (x1.12) so ACES lands on the clean tint.
14
+ const AMBER_YELLOW = { color: [1.0, 0.413, 0.007], roughness: 0.4, metalness: 0.0 }; // #ffb52e
15
+ const BRIGHT_ORANGE = { color: [1.0, 0.144, 0.008], roughness: 0.45, metalness: 0.0 }; // #ff7a2f
16
+ const AMBER = { color: [0.847, 0.339, 0.022], roughness: 0.45, metalness: 0.0 }; // #eda63e
17
+ const CLEAN_YELLOW = { color: [1.0, 0.608, 0.021], roughness: 0.4, metalness: 0.0 }; // #ffd23f
18
+ const CREAM = { color: [0.888, 0.86, 0.798], roughness: 0.35, metalness: 0.0 }; // #f2efe8
19
+ const DARK = { color: [0.012, 0.012, 0.014], roughness: 0.55, metalness: 0.3 }; // #1d1d1f
20
+ const GRAY = { color: [0.256, 0.256, 0.279], roughness: 0.5, metalness: 0.35 }; // #8b8b90
21
+ // Camera-lens eye: very dark blue-black, glossy like coated glass.
22
+ const LENS = { color: [0.01, 0.012, 0.02], roughness: 0.05, metalness: 0.0 };
23
+
24
+ // Per-variant colours.
25
+ const WARM_GRAY = { color: [0.328, 0.312, 0.283], roughness: 0.35, metalness: 0.0 }; // #9b9892
26
+ const AMBER_ORANGE = { color: [0.815, 0.321, 0.019], roughness: 0.5, metalness: 0.0 }; // #e9a23b
27
+ const CHARCOAL = { color: [0.028, 0.028, 0.033], roughness: 0.5, metalness: 0.0 }; // #2f2f33
28
+ const CHARCOAL_FACE = { color: [0.044, 0.044, 0.051], roughness: 0.5, metalness: 0.0 }; // #3c3c40
29
+ const CHARCOAL_BODY = { color: [0.017, 0.017, 0.019], roughness: 0.5, metalness: 0.0 }; // #232326
30
+ const LIGHT_WARM_GRAY = { color: [0.485, 0.459, 0.416], roughness: 0.35, metalness: 0.0 };// #b9b5ae
31
+ const PURPLE_RING = { color: [0.114, 0.058, 0.578], roughness: 0.4, metalness: 0.0 }; // #6a52c8
32
+ const PURPLE_FEET = { color: [0.164, 0.085, 0.584], roughness: 0.45, metalness: 0.0 }; // #7a5fc9
33
+ const PALE_BLUE = { color: [0.441, 0.613, 0.723], roughness: 0.35, metalness: 0.0 }; // #b6cfdd
34
+ const MEDIUM_BLUE = { color: [0.13, 0.321, 0.552], roughness: 0.35, metalness: 0.0 }; // #6f9ec4
35
+ const BLUE_GRAY = { color: [0.183, 0.379, 0.565], roughness: 0.35, metalness: 0.0 }; // #7fa9c6
36
+ const FEET_YELLOW = { color: [0.784, 0.455, 0.023], roughness: 0.5, metalness: 0.0 }; // #e5b93e
37
+
38
+ // Slots (validated against the four-robot reference photo): headDome
39
+ // (top shell), facePlate (around the eye), trim (band under the shell +
40
+ // upper beak base), beakUpper (mouth-roof plate), beakLower (jaw plates),
41
+ // eyeRing, lens, bodyShell (trunk shells), legShells (upper-leg shells),
42
+ // feet (foot blocks + ankle brackets), soles, mechDark, mechGray.
43
+ export const VARIANTS = {
44
+ // Front-center robot: warm gray head, cream body, amber eye, orange
45
+ // trim/upper beak over a lighter amber lower beak, amber-orange feet.
46
+ classic: {
47
+ headDome: WARM_GRAY,
48
+ facePlate: WARM_GRAY,
49
+ trim: BRIGHT_ORANGE,
50
+ beakUpper: BRIGHT_ORANGE,
51
+ beakLower: AMBER,
52
+ eyeRing: AMBER_YELLOW,
53
+ lens: LENS,
54
+ bodyShell: CREAM,
55
+ legShells: CREAM,
56
+ feet: AMBER_ORANGE,
57
+ soles: AMBER_ORANGE,
58
+ mechDark: DARK,
59
+ mechGray: GRAY,
60
+ },
61
+ // Front-left robot: charcoal shells, slightly lighter face plate,
62
+ // amber eye, orange/amber beak, yellow soles peeking under black feet.
63
+ charcoal: {
64
+ headDome: CHARCOAL,
65
+ facePlate: CHARCOAL_FACE,
66
+ trim: BRIGHT_ORANGE,
67
+ beakUpper: BRIGHT_ORANGE,
68
+ beakLower: AMBER,
69
+ eyeRing: AMBER_YELLOW,
70
+ lens: LENS,
71
+ bodyShell: CHARCOAL_BODY,
72
+ legShells: CHARCOAL_BODY,
73
+ feet: CHARCOAL_BODY,
74
+ soles: CLEAN_YELLOW,
75
+ mechDark: DARK,
76
+ mechGray: GRAY,
77
+ },
78
+ // Back-center robot: light warm gray head, purple eye, YELLOW trim and
79
+ // beak (both plates), cream body, purple feet.
80
+ purple: {
81
+ headDome: LIGHT_WARM_GRAY,
82
+ facePlate: LIGHT_WARM_GRAY,
83
+ trim: CLEAN_YELLOW,
84
+ beakUpper: CLEAN_YELLOW,
85
+ beakLower: CLEAN_YELLOW,
86
+ eyeRing: PURPLE_RING,
87
+ lens: LENS,
88
+ bodyShell: CREAM,
89
+ legShells: CREAM,
90
+ feet: PURPLE_FEET,
91
+ soles: PURPLE_FEET,
92
+ mechDark: DARK,
93
+ mechGray: GRAY,
94
+ },
95
+ // Right robot: pale blue dome over a medium blue face plate (two
96
+ // different blues), amber eye, orange/amber beak, blue-gray body,
97
+ // clean yellow feet.
98
+ blue: {
99
+ headDome: PALE_BLUE,
100
+ facePlate: MEDIUM_BLUE,
101
+ trim: BRIGHT_ORANGE,
102
+ beakUpper: BRIGHT_ORANGE,
103
+ beakLower: AMBER,
104
+ eyeRing: AMBER_YELLOW,
105
+ lens: LENS,
106
+ bodyShell: BLUE_GRAY,
107
+ legShells: BLUE_GRAY,
108
+ feet: FEET_YELLOW,
109
+ soles: FEET_YELLOW,
110
+ mechDark: DARK,
111
+ mechGray: GRAY,
112
+ },
113
+ };
114
+
115
+ export const VARIANT_NAMES = Object.keys(VARIANTS);
116
+ export const DEFAULT_VARIANT = "classic";
117
+
118
+ export const randomVariantName = () =>
119
+ VARIANT_NAMES[Math.floor(Math.random() * VARIANT_NAMES.length)];
120
+
121
+ // Mesh filename -> material spec for one variant (mjlab model meshes).
122
+ // Anything not listed is small mechanical hardware and falls back to
123
+ // mechGray. Verified visually against the four-robot reference photo.
124
+ export function meshMaterialsFor(v) {
125
+ return {
126
+ // Head: dome, band under it, face plate around the eye.
127
+ "top_head_shell.stl": v.headDome,
128
+ "bottom_head_shell.stl": v.trim,
129
+ "face_part.stl": v.facePlate,
130
+ // The eye: printed ring + camera lens behind it.
131
+ "noenoeil.stl": v.eyeRing,
132
+ "lens.stl": v.lens,
133
+ "m12_lens_holder.stl": v.mechDark,
134
+ // Beak: mouth-roof plate on top, rigid jaw + soft pad below.
135
+ "soft_mouth_top.stl": v.beakUpper,
136
+ "jaw.stl": v.beakLower,
137
+ "jaw_soft.stl": v.beakLower,
138
+ // Body shells
139
+ "trunk_base.stl": v.bodyShell,
140
+ "left_shell.stl": v.bodyShell,
141
+ "right_shell.stl": v.bodyShell,
142
+ "upper_leg_left.stl": v.legShells,
143
+ "upper_leg_right.stl": v.legShells,
144
+ "hip_l.stl": v.trim,
145
+ // Feet: foot block + ankle bracket, soft sole pad below.
146
+ "foot_left.stl": v.feet,
147
+ "foot_right.stl": v.feet,
148
+ "ankle_left.stl": v.feet,
149
+ "ankle_right.stl": v.feet,
150
+ "sole_left.stl": v.soles,
151
+ "sole_right.stl": v.soles,
152
+ // Dark mechanics
153
+ "xl330.stl": v.mechDark,
154
+ "neck.stl": v.mechDark,
155
+ "np_f970.stl": v.mechDark,
156
+ "pcb__raspberry_pi_zero_2_w.stl": v.mechDark,
157
+ "elec_rpi_robot_hat_pcb.stl": v.mechDark,
158
+ "banana_pcb_locker.stl": v.mechDark,
159
+ "speaker.stl": v.mechDark,
160
+ // Gray mechanics
161
+ "upper_leg_rigidity_plate.stl": v.mechGray,
162
+ "leg.stl": v.mechGray,
163
+ "yaw2roll.stl": v.mechGray,
164
+ "yaw_roll_motion.stl": v.mechGray,
165
+ "neck_pitch.stl": v.mechGray,
166
+ "bearing_roll.stl": v.mechGray,
167
+ "motor_support.stl": v.mechGray,
168
+ "power_support.stl": v.mechGray,
169
+ "seeed_bearing__configuration__22x16x4.stl": v.mechGray,
170
+ "seeed_bearing__configuration_default.stl": v.mechGray,
171
+ };
172
+ }
173
+
174
+ // buildRig materialForMesh hook for one variant.
175
+ export const materialHookFor = (v) => {
176
+ const map = meshMaterialsFor(v);
177
+ return (mesh) => map[mesh] ?? v.mechGray;
178
+ };
179
+
180
+ // ── Live re-skin ────────────────────────────────────────────────────────
181
+ // Swap materials on an already-built rig without reloading any STL.
182
+ // Meshes are identified via userData.meshName (set by duck.js buildRig and
183
+ // preserved by cloneRig). Materials are cached per resolved spec so clones
184
+ // and repeated switches share GPU material instances.
185
+ const matCache = new Map();
186
+ function matFor(spec) {
187
+ const key = `${spec.color.join(",")}|${spec.roughness ?? 0.5}|${spec.metalness ?? 0}`;
188
+ let m = matCache.get(key);
189
+ if (!m) {
190
+ m = new THREE.MeshStandardMaterial({
191
+ color: new THREE.Color(...spec.color),
192
+ roughness: spec.roughness ?? 0.5,
193
+ metalness: spec.metalness ?? 0.0,
194
+ });
195
+ matCache.set(key, m);
196
+ }
197
+ return m;
198
+ }
199
+
200
+ export function applyVariant(rig, variant) {
201
+ const v = typeof variant === "string" ? VARIANTS[variant] : variant;
202
+ const map = meshMaterialsFor(v);
203
+ rig.root.traverse((o) => {
204
+ if (!o.isMesh || !o.userData.meshName) return;
205
+ o.material = matFor(map[o.userData.meshName] ?? v.mechGray);
206
+ });
207
+ }
208
+
209
+ // Linear-space spec colour -> sRGB CSS hex, for swatch UI elements.
210
+ export function specToHex(spec) {
211
+ return `#${new THREE.Color(...spec.color).getHexString()}`;
212
+ }