mujoco-react 8.4.0 → 8.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +54 -82
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -18,113 +18,89 @@ Composable [React Three Fiber](https://docs.pmnd.rs/react-three-fiber) wrapper a
|
|
|
18
18
|
npm install mujoco-react three @react-three/fiber @react-three/drei
|
|
19
19
|
```
|
|
20
20
|
|
|
21
|
-
##
|
|
21
|
+
## Load a Model
|
|
22
22
|
|
|
23
23
|
```tsx
|
|
24
|
-
import {
|
|
25
|
-
MujocoProvider,
|
|
26
|
-
MujocoCanvas,
|
|
27
|
-
useIkController,
|
|
28
|
-
IkGizmo,
|
|
29
|
-
} from "mujoco-react";
|
|
24
|
+
import { MujocoProvider, MujocoCanvas } from "mujoco-react";
|
|
30
25
|
import type { SceneConfig } from "mujoco-react";
|
|
31
|
-
import { OrbitControls } from "@react-three/drei";
|
|
32
26
|
|
|
33
|
-
const
|
|
27
|
+
const panda: SceneConfig = {
|
|
34
28
|
src: "https://raw.githubusercontent.com/google-deepmind/mujoco_menagerie/main/franka_emika_panda/",
|
|
35
29
|
sceneFile: "scene.xml",
|
|
36
30
|
homeJoints: [1.707, -1.754, 0.003, -2.702, 0.003, 0.951, 2.490],
|
|
37
31
|
};
|
|
38
32
|
|
|
39
|
-
function
|
|
40
|
-
const ik = useIkController({ siteName: "tcp" });
|
|
41
|
-
return (
|
|
42
|
-
<>
|
|
43
|
-
<OrbitControls enableDamping makeDefault />
|
|
44
|
-
{ik && <IkGizmo controller={ik} />}
|
|
45
|
-
<ambientLight intensity={0.7} />
|
|
46
|
-
<directionalLight position={[1, 2, 5]} intensity={1.2} castShadow />
|
|
47
|
-
</>
|
|
48
|
-
);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
function App() {
|
|
33
|
+
export function App() {
|
|
52
34
|
return (
|
|
53
35
|
<MujocoProvider>
|
|
54
36
|
<MujocoCanvas
|
|
55
|
-
config={
|
|
37
|
+
config={panda}
|
|
56
38
|
camera={{ position: [2, -1.5, 2.5], up: [0, 0, 1], fov: 45 }}
|
|
57
|
-
shadows
|
|
58
39
|
style={{ width: "100%", height: "100vh" }}
|
|
59
|
-
|
|
60
|
-
<Scene />
|
|
61
|
-
</MujocoCanvas>
|
|
40
|
+
/>
|
|
62
41
|
</MujocoProvider>
|
|
63
42
|
);
|
|
64
43
|
}
|
|
65
44
|
```
|
|
66
45
|
|
|
67
|
-
##
|
|
68
|
-
|
|
69
|
-
Inside `<MujocoCanvas>` or `<MujocoPhysics>`, `useMujoco()` gives you the simulation API, refs to the live model/data, and status:
|
|
46
|
+
## Add Scene Tools
|
|
70
47
|
|
|
71
48
|
```tsx
|
|
72
|
-
import {
|
|
73
|
-
|
|
74
|
-
function MyComponent() {
|
|
75
|
-
const { isPending, isError, error, api, mjModelRef } = useMujoco();
|
|
49
|
+
import { OrbitControls } from "@react-three/drei";
|
|
50
|
+
import { IkGizmo, useIkController } from "mujoco-react";
|
|
76
51
|
|
|
77
|
-
|
|
78
|
-
|
|
52
|
+
function PandaTools() {
|
|
53
|
+
const ik = useIkController({ siteName: "tcp" });
|
|
79
54
|
|
|
80
55
|
return (
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
56
|
+
<>
|
|
57
|
+
<OrbitControls makeDefault />
|
|
58
|
+
{ik && <IkGizmo controller={ik} />}
|
|
59
|
+
<ambientLight intensity={0.7} />
|
|
60
|
+
<directionalLight position={[1, 2, 5]} intensity={1.2} />
|
|
61
|
+
</>
|
|
84
62
|
);
|
|
85
63
|
}
|
|
86
64
|
```
|
|
87
65
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
A controller is a hook that reads sensors and writes actuators each physics step:
|
|
66
|
+
Use it as a child of `<MujocoCanvas>`:
|
|
91
67
|
|
|
92
68
|
```tsx
|
|
93
|
-
|
|
69
|
+
<MujocoCanvas config={panda}>
|
|
70
|
+
<PandaTools />
|
|
71
|
+
</MujocoCanvas>
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Write Controllers
|
|
94
75
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
const elbow = useCtrl("elbow");
|
|
98
|
-
const force = useSensor("force_sensor");
|
|
76
|
+
```tsx
|
|
77
|
+
import { useBeforePhysicsStep } from "mujoco-react";
|
|
99
78
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
79
|
+
function MyController() {
|
|
80
|
+
useBeforePhysicsStep((_model, data) => {
|
|
81
|
+
data.ctrl[0] = Math.sin(data.time);
|
|
103
82
|
});
|
|
83
|
+
|
|
84
|
+
return null;
|
|
104
85
|
}
|
|
105
86
|
```
|
|
106
87
|
|
|
107
|
-
|
|
88
|
+
Controllers are just React children that read sensors, write `data.ctrl`, apply forces, or call the `MujocoSimAPI` at physics-step time.
|
|
108
89
|
|
|
109
|
-
|
|
90
|
+
## Use the Sim API
|
|
110
91
|
|
|
111
92
|
```tsx
|
|
112
|
-
import {
|
|
93
|
+
import { useMujoco } from "mujoco-react";
|
|
113
94
|
|
|
114
|
-
function
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
data.xfrc_applied[i6 + 4] = (target[1] - data.xpos[i3 + 1]) * stiffness;
|
|
120
|
-
data.xfrc_applied[i6 + 5] = (target[2] - data.xpos[i3 + 2]) * stiffness;
|
|
121
|
-
});
|
|
95
|
+
function ResetButton() {
|
|
96
|
+
const sim = useMujoco();
|
|
97
|
+
if (!sim.isReady) return null;
|
|
98
|
+
|
|
99
|
+
return <button onClick={() => sim.api.reset()}>Reset</button>;
|
|
122
100
|
}
|
|
123
101
|
```
|
|
124
102
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
### WebSocket Control
|
|
103
|
+
## WebSocket Control
|
|
128
104
|
|
|
129
105
|
Stream actuator commands over a WebSocket and send simulation state back. Use a schema validator such as Zod at this boundary because socket messages are untrusted app input (`npm install zod` for this example):
|
|
130
106
|
|
|
@@ -133,32 +109,28 @@ import { useEffect, useRef } from "react";
|
|
|
133
109
|
import { z } from "zod";
|
|
134
110
|
import { useMujoco, useBeforePhysicsStep, useAfterPhysicsStep } from "mujoco-react";
|
|
135
111
|
|
|
136
|
-
const CtrlCommand = z.
|
|
137
|
-
type: z.literal("ctrl_command"),
|
|
138
|
-
ctrl: z.array(z.number()),
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
type CtrlCommand = z.infer<typeof CtrlCommand>;
|
|
142
|
-
|
|
143
|
-
function parseSocketMessage(data: string): CtrlCommand | null {
|
|
112
|
+
const CtrlCommand = z.preprocess((data) => {
|
|
144
113
|
try {
|
|
145
|
-
|
|
146
|
-
return parsed.success ? parsed.data : null;
|
|
114
|
+
return typeof data === "string" ? JSON.parse(data) : data;
|
|
147
115
|
} catch {
|
|
148
|
-
return
|
|
116
|
+
return undefined;
|
|
149
117
|
}
|
|
150
|
-
}
|
|
118
|
+
}, z.object({
|
|
119
|
+
type: z.literal("ctrl_command"),
|
|
120
|
+
ctrl: z.array(z.number()),
|
|
121
|
+
}));
|
|
151
122
|
|
|
152
123
|
function useWebSocketControls(url: string) {
|
|
153
124
|
const wsRef = useRef<WebSocket | null>(null);
|
|
154
|
-
const
|
|
125
|
+
const latestCtrlRef = useRef<number[] | null>(null);
|
|
155
126
|
|
|
156
127
|
useEffect(() => {
|
|
157
128
|
const ws = new WebSocket(url);
|
|
158
129
|
wsRef.current = ws;
|
|
159
130
|
|
|
160
131
|
ws.onmessage = (evt) => {
|
|
161
|
-
|
|
132
|
+
const command = CtrlCommand.safeParse(evt.data);
|
|
133
|
+
if (command.success) latestCtrlRef.current = command.data.ctrl;
|
|
162
134
|
};
|
|
163
135
|
|
|
164
136
|
return () => ws.close();
|
|
@@ -166,10 +138,10 @@ function useWebSocketControls(url: string) {
|
|
|
166
138
|
|
|
167
139
|
// Apply incoming actuator controls each physics step.
|
|
168
140
|
useBeforePhysicsStep((model, data) => {
|
|
169
|
-
const
|
|
170
|
-
if (!
|
|
171
|
-
for (let i = 0; i < Math.min(
|
|
172
|
-
data.ctrl[i] =
|
|
141
|
+
const ctrl = latestCtrlRef.current;
|
|
142
|
+
if (!ctrl) return;
|
|
143
|
+
for (let i = 0; i < Math.min(ctrl.length, model.nu); i++) {
|
|
144
|
+
data.ctrl[i] = ctrl[i];
|
|
173
145
|
}
|
|
174
146
|
});
|
|
175
147
|
|