rigidax 1.0.0__tar.gz

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.
rigidax-1.0.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Nishanth Sundaran
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
rigidax-1.0.0/PKG-INFO ADDED
@@ -0,0 +1,257 @@
1
+ Metadata-Version: 2.4
2
+ Name: rigidax
3
+ Version: 1.0.0
4
+ Summary: Differentiable rigid-body kinematics, dynamics and batched IK / motion retargeting in JAX (MJCF + URDF)
5
+ Author-email: Nishanth Sundaran <sundharnishanth@gmail.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/NishanthSundaran/Rigidax
8
+ Project-URL: Repository, https://github.com/NishanthSundaran/Rigidax
9
+ Project-URL: Issues, https://github.com/NishanthSundaran/Rigidax/issues
10
+ Project-URL: Changelog, https://github.com/NishanthSundaran/Rigidax/blob/master/CHANGELOG.md
11
+ Keywords: robotics,kinematics,dynamics,inverse-kinematics,retargeting,jax,mujoco
12
+ Classifier: Development Status :: 5 - Production/Stable
13
+ Classifier: Intended Audience :: Science/Research
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Topic :: Scientific/Engineering :: Physics
20
+ Requires-Python: >=3.10
21
+ Description-Content-Type: text/markdown
22
+ License-File: LICENSE
23
+ Requires-Dist: numpy
24
+ Requires-Dist: jax>=0.4.30
25
+ Requires-Dist: mujoco>=3.1
26
+ Provides-Extra: test
27
+ Requires-Dist: pytest; extra == "test"
28
+ Requires-Dist: pin; extra == "test"
29
+ Provides-Extra: dev
30
+ Requires-Dist: pytest; extra == "dev"
31
+ Requires-Dist: pin; extra == "dev"
32
+ Requires-Dist: ruff; extra == "dev"
33
+ Dynamic: license-file
34
+
35
+ # rigidax
36
+
37
+ Differentiable rigid-body kinematics and dynamics in JAX. Its design starts from
38
+ [frax](https://github.com/StanfordASL/frax): the dense, loop-free formulation of the
39
+ dynamics over a DOF ancestor mask. It fills in frax's gaps:
40
+
41
+ | | frax 0.2 | rigidax |
42
+ |---|---|---|
43
+ | Model formats | URDF | **MJCF and URDF** (compiled by MuJoCo) |
44
+ | Joint types | revolute, prismatic, free | hinge, slide, **ball**, free, **several joints per body** |
45
+ | Gravity | hardcoded 9.81 in `forward_dynamics` | **model parameter**, any vector |
46
+ | Physical parameters | frozen into the compiled function | **pytree leaves**: `jax.grad` / `vmap` over masses, inertias, offsets, gravity; swap them without recompiling |
47
+ | Armature | no | yes (in M and inverse dynamics) |
48
+ | External wrenches | per-link | per-body, MuJoCo `xfrc_applied` layout |
49
+ | Ground truth tests | Pinocchio / MuJoCo | every quantity vs MuJoCo on random trees + G1/A2/Panda, plus Pinocchio |
50
+
51
+ ![Operational space control demo](https://raw.githubusercontent.com/NishanthSundaran/Rigidax/master/examples/osc_demo.gif)
52
+
53
+ The Panda tracks a figure-eight with operational space control (`examples/osc_demo.py`).
54
+ rigidax computes the kinematics, Jacobian, mass matrix and bias forces in about 54 µs
55
+ per step, Python dispatch included. Mean tracking error is 0.9 mm. MuJoCo only
56
+ simulates and renders.
57
+
58
+ ```
59
+ env -u PYTHONPATH MUJOCO_GL=egl python examples/osc_demo.py --out osc_demo.mp4
60
+ ```
61
+
62
+ ![G1 + Dex3 hands, retargeted LAFAN1 motions](https://raw.githubusercontent.com/NishanthSundaran/Rigidax/master/examples/g1_lafan1.gif)
63
+
64
+ Two output variants are produced from the same solve settings. Speed on the same
65
+ laptop with self-collision: 22.8k frames/s for the plain G1 and 18.8k for G1 + Dex3. The
66
+ Dex3 version is slower because the finger envelope adds collision spheres. frax, with
67
+ self-collision, runs at 18.3k.
68
+
69
+ | Variant | Command | Output | GIF |
70
+ |---|---|---|---|
71
+ | G1 + Dex3 hands (default) | `retarget_lafan1.py <clips>` | `data/g1_lafan1_hands/` (nq 50) | `examples/g1_lafan1.gif` |
72
+ | Plain G1, rigid hands | `retarget_lafan1.py <clips> --no-hands` | `data/g1_lafan1_nohands/` (nq 36) | `examples/g1_lafan1_nohands.gif` |
73
+
74
+ Both variants track the forearm twist. Palm floor contact and fingers are Dex3-only.
75
+ Render a variant with `render_motions.py --data data/g1_lafan1_nohands ...`.
76
+
77
+ ## `rigidax.ik`: batched IK and retargeting
78
+
79
+ A dedicated differential IK solver for retargeting whole datasets on CPU:
80
+
81
+ - **Each Gauss-Newton step is a small QP**, solved by a purpose-built fixed-iteration
82
+ ADMM (`rigidax.ik.solve_qp`). It has no branches, so it vectorizes over thousands of
83
+ frames. It uses:
84
+ - row equilibration,
85
+ - one explicit inverse per solve, then matrix-vector products (batched triangular
86
+ solves are slow on XLA:CPU),
87
+ - warm starts from the previous frame,
88
+ - optional polishing.
89
+ - **Constraints:**
90
+ - hard joint limits,
91
+ - a per-iteration trust region, so a joint can never flip across its range in one
92
+ frame,
93
+ - a hard floor (`FloorPoints`),
94
+ - elastic self-collision, an exact L1 penalty that keeps the problem always feasible,
95
+ on an automatic sphere model of any MuJoCo model (`SphereModel.fit`).
96
+ - **Tasks:** position, orientation, limb direction (retargets between skeletons with
97
+ different proportions) and posture.
98
+ - **Batching:** `solve_trajectory` splits clips into segments that run in parallel (vmap
99
+ within a device, pmap across devices). Each segment is seeded by a multi-start solve,
100
+ and consecutive segments are crossfaded so seams are smooth.
101
+
102
+ ```python
103
+ from rigidax import ik
104
+ tasks = [ik.position(model, bodies, w), ik.direction(model, links, bases, w),
105
+ ik.orientation(model, frames, w), ik.PostureTask(posture_w)]
106
+ solver = ik.IKSolver(model, tasks, floor=ik.FloorPoints.on(model, feet, sole_points),
107
+ collision=ik.SphereModel.fit(mj_model, model, pair_filter=...))
108
+ qs, costs = solver.solve_trajectory(seed_fn, [clip1_targets, clip2_targets, ...])
109
+ ```
110
+
111
+ ### vs frax on LAFAN1 → Unitree G1
112
+
113
+ Both libraries ran frax's own LAFAN1 settings on the same i9-11900H laptop (8 cores, one
114
+ XLA device per core), on 8 clips (53,621 frames, 30 min of motion). Both outputs are scored
115
+ with the same metrics:
116
+ - **Motion kept:** robot vs human limb angular speed (1.0 = all of it).
117
+ - **Lag:** in frames.
118
+ - **Joint acceleration:** worst 5% of frames.
119
+ - **Skating:** the robot's toe contact sliding while the human toe is planted.
120
+ - **Penetration:** MuJoCo's own mesh collision check.
121
+
122
+ `bench/bench_frax_ik.py` runs frax.
123
+
124
+ | | frames/s | arm / leg dir. error p95 | hand / forearm rot. p95 | arms / legs kept | lag (frames) | >20° jumps | skate median / p95 | touchdown p95 | penetration | through floor |
125
+ |---|---|---|---|---|---|---|---|---|---|---|
126
+ | frax, self-collision | 18,327 | 17.4° / 11.6° | 13° / 20° | 0.88 / 0.95 | 0.16 | 1.9% | 4.6 / 32 cm/s | 40 | 0.0% | 0.6 cm max |
127
+ | **rigidax, self-collision** (plain G1) | **22,836** | **14.0°** / 11.7° | **12°** / 24° | **0.92** / 0.94 | **-0.01** | **0.1%** | **0.9 / 21** | **35** | 0.0% | 3.8 cm max (lying) |
128
+ | frax, no self-collision | 37,490 | 17.1° / 11.3° | 11° / 19° | 0.88 / 0.95 | 0.15 | 1.8% | 4.4 / 31 | 38 | 3.3% | |
129
+ | **rigidax, no self-collision** | **46,808** | **3.9°** / 11.7° | 10° / 22° | **0.96** / 0.94 | -0.10 | **0.1%** | **0.9 / 21** | **35** | 2.2% | |
130
+
131
+ The rigidax task set covers everything frax tracks: pelvis, the thigh / shin / foot links,
132
+ torso, and the upper arm / forearm / hand orientations. It adds limb directions and
133
+ toe contact points on top.
134
+
135
+ Where frax is still slightly ahead:
136
+ - forearm twist (24° vs 20°),
137
+ - leg motion kept (0.94 vs 0.95).
138
+
139
+ frax's lower joint-acceleration number (8.4 vs 9.3°/f²) is mostly smoothed-away motion.
140
+ On the knees, rigidax matches the human's own knee acceleration and frax sits below it.
141
+
142
+ Hands: the output uses the G1 with Dex3-1 hands (`g1_with_hands.xml`, 7 finger joints
143
+ per hand). The body is solved on the 35-DOF model and the finger angles are written
144
+ afterwards, since nothing depends on them, which keeps the solve as fast as before.
145
+ - **Synthesized fingers:** LAFAN1 records no fingers, so the grip is inferred from context
146
+ and is not tracked motion:
147
+ - fist in a fighting guard (hand in front of the chest, in the chest frame) or during
148
+ a strike, in `fight` clips only;
149
+ - flat open hand while the human hand rests on the floor;
150
+ - relaxed otherwise.
151
+ - **Palm contact:** a height task puts the palm on the floor while the human hand rests
152
+ there. The previous hover was 6.2 cm; now it is about 0.
153
+ - **Forearm twist:** a twist-only task about the forearm axis.
154
+
155
+ Diagnostics:
156
+ - `examples/render_overlay.py`: draws the solver's targets over the robot (visible red
157
+ means a real tracking error).
158
+ - `examples/floor_report.py`: whole-body floor penetration.
159
+ - `examples/collision_report.py`: self-collision.
160
+
161
+ ```
162
+ # CPU, one XLA device per core
163
+ XLA_FLAGS="--xla_force_host_platform_device_count=8 --xla_cpu_multi_thread_eigen=false" \
164
+ python examples/retarget_lafan1.py dance1_subject1 walk1_subject1 ... [--no-collision]
165
+ python examples/collision_report.py dance1_subject1 ... # MuJoCo mesh ground truth
166
+ ```
167
+
168
+ LAFAN1 is CC BY-NC-ND 4.0 (non-commercial). Get it with:
169
+
170
+ ```
171
+ curl -L -o data/lafan1.zip https://github.com/ubisoft/ubisoft-laforge-animation-dataset/raw/master/lafan1/lafan1.zip
172
+ unzip -j data/lafan1.zip '*.bvh' -d data/lafan1
173
+ MUJOCO_GL=egl python examples/render_motions.py --out g1_lafan1.gif dance1_subject1:DANCE \
174
+ dance2_subject3:"DANCE 2" fightAndSports1_subject1@13.8:"FIGHT + KICK" jumps1_subject1:JUMPS \
175
+ run1_subject2:RUN sprint1_subject2@177:SPRINT walk1_subject1:WALK fallAndGetUp1_subject1@63:"FALL + GET UP"
176
+ # clip@seconds picks the window; without it the most active 10 s are used
177
+ ```
178
+
179
+ ![G1 motions side by side](https://raw.githubusercontent.com/NishanthSundaran/Rigidax/master/examples/g1_all.gif)
180
+
181
+ ![G1 whole-body control demo](https://raw.githubusercontent.com/NishanthSundaran/Rigidax/master/examples/g1_demo.gif)
182
+
183
+ A free-floating Unitree G1 (35 DOF) squats, twists and waves while balancing on its
184
+ feet (`examples/g1_demo.py`). Per step, rigidax computes:
185
+ - M and h, the foot contact Jacobians, and Jc_dot·v (via autodiff)
186
+ - reference accelerations that keep the feet planted (KKT solve)
187
+ - the contact wrenches that balance the floating base
188
+ - the feedforward joint torques
189
+
190
+ Joint PD supplies the feedback. A step takes about 190 µs. MuJoCo's position servos
191
+ are replaced by pure torque motors.
192
+
193
+ ```
194
+ env -u PYTHONPATH MUJOCO_GL=egl python examples/g1_demo.py --out g1_demo.mp4
195
+ # one motion set, frax-style grey studio: --motion squat|twist|wave|squat,wave|all --style light
196
+ ```
197
+
198
+ ## Use
199
+
200
+ ```python
201
+ import jax, rigidax as rx
202
+ jax.config.update("jax_enable_x64", True)
203
+
204
+ model = rx.load("robot.xml") # or .urdf; strip_meshes=True if meshes are missing
205
+ q, v = model.q0, jax.numpy.zeros(model.nv)
206
+
207
+ M, bias = jax.jit(rx.dynamics_terms)(model, q, v) # one kinematics pass
208
+ qacc = jax.jit(rx.forward_dynamics)(model, q, v, tau)
209
+ moon = model.replace(gravity=jax.numpy.array([0, 0, -1.62])) # no recompile
210
+
211
+ # gradients w.r.t. physical parameters (e.g. system identification)
212
+ dL_dmass = jax.grad(lambda m: loss(model.replace(body_mass=m)))(model.params.body_mass)
213
+ ```
214
+
215
+ Available functions: `forward_kinematics`, `site_pose`, `body_pose`, `point_jacobian`,
216
+ `site_jacobian`, `body_jacobian`, `center_of_mass`, `center_of_mass_jacobian`, `rnea`,
217
+ `mass_matrix`, `bias_forces`, `gravity_forces`, `coriolis_forces`, `dynamics_terms`,
218
+ `forward_dynamics`, `mass_matrix_inverse`, `integrate`, `difference`.
219
+ Every function takes an optional `kin=` argument to reuse a forward-kinematics pass.
220
+
221
+ Conventions follow MuJoCo: `q` uses `[pos, wxyz quat]` for free joints and `wxyz` for
222
+ ball joints. `v` uses world-frame linear and body-frame angular velocity.
223
+
224
+ ## Tests
225
+
226
+ ```
227
+ env -u PYTHONPATH python -m pytest # unset ROS's PYTHONPATH if it is sourced
228
+ ```
229
+
230
+ ## Speed vs frax (CPU, float64, single thread, jax 0.4.30)
231
+
232
+ Median µs per call, from `bench/bench_vs_frax.py`, two runs. A ratio above 1 means rigidax is faster.
233
+
234
+ | | Panda single | G1 (35 dof) single | Panda batch 1024 | G1 batch 1024 |
235
+ |---|---|---|---|---|
236
+ | mass matrix | 0.96–0.98x | 0.97x | 1.09x | 1.02–1.15x |
237
+ | bias forces | 0.85–0.91x | 0.78–0.80x | 0.95–1.02x | 0.91–0.92x |
238
+ | forward dynamics | 0.82–0.87x | 0.95–0.96x | 0.97–1.11x | 0.94–0.97x |
239
+
240
+ The two are about equal overall. The single-call bias computation is still 10–20% slower.
241
+ GPU has not been benchmarked.
242
+
243
+ ## Known limitations (1.0)
244
+
245
+ - **Synthesized fingers:** Dex3 finger motion is inferred from context (LAFAN1 has no
246
+ finger data). Finger collisions are handled with rigid envelopes, so fists held
247
+ against the torso in the fight guard still touch it in about 1% of frames.
248
+ - **GPU:** the GPU path works but is not tuned (~6k frames/s on an RTX 3060 laptop).
249
+ - **G1 forearm pronation:** on the G1 this lives on `wrist_roll_link`. The twist task
250
+ currently targets `elbow_link`.
251
+
252
+ ## Roadmap
253
+
254
+ - Foot skating p95 (contact-aware per-frame weights), GPU throughput
255
+ - Capsule/box collision primitives
256
+ - Close the remaining bias-forces gap; GPU benchmarks
257
+ - Analytic derivatives of RNEA and ABA
@@ -0,0 +1,223 @@
1
+ # rigidax
2
+
3
+ Differentiable rigid-body kinematics and dynamics in JAX. Its design starts from
4
+ [frax](https://github.com/StanfordASL/frax): the dense, loop-free formulation of the
5
+ dynamics over a DOF ancestor mask. It fills in frax's gaps:
6
+
7
+ | | frax 0.2 | rigidax |
8
+ |---|---|---|
9
+ | Model formats | URDF | **MJCF and URDF** (compiled by MuJoCo) |
10
+ | Joint types | revolute, prismatic, free | hinge, slide, **ball**, free, **several joints per body** |
11
+ | Gravity | hardcoded 9.81 in `forward_dynamics` | **model parameter**, any vector |
12
+ | Physical parameters | frozen into the compiled function | **pytree leaves**: `jax.grad` / `vmap` over masses, inertias, offsets, gravity; swap them without recompiling |
13
+ | Armature | no | yes (in M and inverse dynamics) |
14
+ | External wrenches | per-link | per-body, MuJoCo `xfrc_applied` layout |
15
+ | Ground truth tests | Pinocchio / MuJoCo | every quantity vs MuJoCo on random trees + G1/A2/Panda, plus Pinocchio |
16
+
17
+ ![Operational space control demo](https://raw.githubusercontent.com/NishanthSundaran/Rigidax/master/examples/osc_demo.gif)
18
+
19
+ The Panda tracks a figure-eight with operational space control (`examples/osc_demo.py`).
20
+ rigidax computes the kinematics, Jacobian, mass matrix and bias forces in about 54 µs
21
+ per step, Python dispatch included. Mean tracking error is 0.9 mm. MuJoCo only
22
+ simulates and renders.
23
+
24
+ ```
25
+ env -u PYTHONPATH MUJOCO_GL=egl python examples/osc_demo.py --out osc_demo.mp4
26
+ ```
27
+
28
+ ![G1 + Dex3 hands, retargeted LAFAN1 motions](https://raw.githubusercontent.com/NishanthSundaran/Rigidax/master/examples/g1_lafan1.gif)
29
+
30
+ Two output variants are produced from the same solve settings. Speed on the same
31
+ laptop with self-collision: 22.8k frames/s for the plain G1 and 18.8k for G1 + Dex3. The
32
+ Dex3 version is slower because the finger envelope adds collision spheres. frax, with
33
+ self-collision, runs at 18.3k.
34
+
35
+ | Variant | Command | Output | GIF |
36
+ |---|---|---|---|
37
+ | G1 + Dex3 hands (default) | `retarget_lafan1.py <clips>` | `data/g1_lafan1_hands/` (nq 50) | `examples/g1_lafan1.gif` |
38
+ | Plain G1, rigid hands | `retarget_lafan1.py <clips> --no-hands` | `data/g1_lafan1_nohands/` (nq 36) | `examples/g1_lafan1_nohands.gif` |
39
+
40
+ Both variants track the forearm twist. Palm floor contact and fingers are Dex3-only.
41
+ Render a variant with `render_motions.py --data data/g1_lafan1_nohands ...`.
42
+
43
+ ## `rigidax.ik`: batched IK and retargeting
44
+
45
+ A dedicated differential IK solver for retargeting whole datasets on CPU:
46
+
47
+ - **Each Gauss-Newton step is a small QP**, solved by a purpose-built fixed-iteration
48
+ ADMM (`rigidax.ik.solve_qp`). It has no branches, so it vectorizes over thousands of
49
+ frames. It uses:
50
+ - row equilibration,
51
+ - one explicit inverse per solve, then matrix-vector products (batched triangular
52
+ solves are slow on XLA:CPU),
53
+ - warm starts from the previous frame,
54
+ - optional polishing.
55
+ - **Constraints:**
56
+ - hard joint limits,
57
+ - a per-iteration trust region, so a joint can never flip across its range in one
58
+ frame,
59
+ - a hard floor (`FloorPoints`),
60
+ - elastic self-collision, an exact L1 penalty that keeps the problem always feasible,
61
+ on an automatic sphere model of any MuJoCo model (`SphereModel.fit`).
62
+ - **Tasks:** position, orientation, limb direction (retargets between skeletons with
63
+ different proportions) and posture.
64
+ - **Batching:** `solve_trajectory` splits clips into segments that run in parallel (vmap
65
+ within a device, pmap across devices). Each segment is seeded by a multi-start solve,
66
+ and consecutive segments are crossfaded so seams are smooth.
67
+
68
+ ```python
69
+ from rigidax import ik
70
+ tasks = [ik.position(model, bodies, w), ik.direction(model, links, bases, w),
71
+ ik.orientation(model, frames, w), ik.PostureTask(posture_w)]
72
+ solver = ik.IKSolver(model, tasks, floor=ik.FloorPoints.on(model, feet, sole_points),
73
+ collision=ik.SphereModel.fit(mj_model, model, pair_filter=...))
74
+ qs, costs = solver.solve_trajectory(seed_fn, [clip1_targets, clip2_targets, ...])
75
+ ```
76
+
77
+ ### vs frax on LAFAN1 → Unitree G1
78
+
79
+ Both libraries ran frax's own LAFAN1 settings on the same i9-11900H laptop (8 cores, one
80
+ XLA device per core), on 8 clips (53,621 frames, 30 min of motion). Both outputs are scored
81
+ with the same metrics:
82
+ - **Motion kept:** robot vs human limb angular speed (1.0 = all of it).
83
+ - **Lag:** in frames.
84
+ - **Joint acceleration:** worst 5% of frames.
85
+ - **Skating:** the robot's toe contact sliding while the human toe is planted.
86
+ - **Penetration:** MuJoCo's own mesh collision check.
87
+
88
+ `bench/bench_frax_ik.py` runs frax.
89
+
90
+ | | frames/s | arm / leg dir. error p95 | hand / forearm rot. p95 | arms / legs kept | lag (frames) | >20° jumps | skate median / p95 | touchdown p95 | penetration | through floor |
91
+ |---|---|---|---|---|---|---|---|---|---|---|
92
+ | frax, self-collision | 18,327 | 17.4° / 11.6° | 13° / 20° | 0.88 / 0.95 | 0.16 | 1.9% | 4.6 / 32 cm/s | 40 | 0.0% | 0.6 cm max |
93
+ | **rigidax, self-collision** (plain G1) | **22,836** | **14.0°** / 11.7° | **12°** / 24° | **0.92** / 0.94 | **-0.01** | **0.1%** | **0.9 / 21** | **35** | 0.0% | 3.8 cm max (lying) |
94
+ | frax, no self-collision | 37,490 | 17.1° / 11.3° | 11° / 19° | 0.88 / 0.95 | 0.15 | 1.8% | 4.4 / 31 | 38 | 3.3% | |
95
+ | **rigidax, no self-collision** | **46,808** | **3.9°** / 11.7° | 10° / 22° | **0.96** / 0.94 | -0.10 | **0.1%** | **0.9 / 21** | **35** | 2.2% | |
96
+
97
+ The rigidax task set covers everything frax tracks: pelvis, the thigh / shin / foot links,
98
+ torso, and the upper arm / forearm / hand orientations. It adds limb directions and
99
+ toe contact points on top.
100
+
101
+ Where frax is still slightly ahead:
102
+ - forearm twist (24° vs 20°),
103
+ - leg motion kept (0.94 vs 0.95).
104
+
105
+ frax's lower joint-acceleration number (8.4 vs 9.3°/f²) is mostly smoothed-away motion.
106
+ On the knees, rigidax matches the human's own knee acceleration and frax sits below it.
107
+
108
+ Hands: the output uses the G1 with Dex3-1 hands (`g1_with_hands.xml`, 7 finger joints
109
+ per hand). The body is solved on the 35-DOF model and the finger angles are written
110
+ afterwards, since nothing depends on them, which keeps the solve as fast as before.
111
+ - **Synthesized fingers:** LAFAN1 records no fingers, so the grip is inferred from context
112
+ and is not tracked motion:
113
+ - fist in a fighting guard (hand in front of the chest, in the chest frame) or during
114
+ a strike, in `fight` clips only;
115
+ - flat open hand while the human hand rests on the floor;
116
+ - relaxed otherwise.
117
+ - **Palm contact:** a height task puts the palm on the floor while the human hand rests
118
+ there. The previous hover was 6.2 cm; now it is about 0.
119
+ - **Forearm twist:** a twist-only task about the forearm axis.
120
+
121
+ Diagnostics:
122
+ - `examples/render_overlay.py`: draws the solver's targets over the robot (visible red
123
+ means a real tracking error).
124
+ - `examples/floor_report.py`: whole-body floor penetration.
125
+ - `examples/collision_report.py`: self-collision.
126
+
127
+ ```
128
+ # CPU, one XLA device per core
129
+ XLA_FLAGS="--xla_force_host_platform_device_count=8 --xla_cpu_multi_thread_eigen=false" \
130
+ python examples/retarget_lafan1.py dance1_subject1 walk1_subject1 ... [--no-collision]
131
+ python examples/collision_report.py dance1_subject1 ... # MuJoCo mesh ground truth
132
+ ```
133
+
134
+ LAFAN1 is CC BY-NC-ND 4.0 (non-commercial). Get it with:
135
+
136
+ ```
137
+ curl -L -o data/lafan1.zip https://github.com/ubisoft/ubisoft-laforge-animation-dataset/raw/master/lafan1/lafan1.zip
138
+ unzip -j data/lafan1.zip '*.bvh' -d data/lafan1
139
+ MUJOCO_GL=egl python examples/render_motions.py --out g1_lafan1.gif dance1_subject1:DANCE \
140
+ dance2_subject3:"DANCE 2" fightAndSports1_subject1@13.8:"FIGHT + KICK" jumps1_subject1:JUMPS \
141
+ run1_subject2:RUN sprint1_subject2@177:SPRINT walk1_subject1:WALK fallAndGetUp1_subject1@63:"FALL + GET UP"
142
+ # clip@seconds picks the window; without it the most active 10 s are used
143
+ ```
144
+
145
+ ![G1 motions side by side](https://raw.githubusercontent.com/NishanthSundaran/Rigidax/master/examples/g1_all.gif)
146
+
147
+ ![G1 whole-body control demo](https://raw.githubusercontent.com/NishanthSundaran/Rigidax/master/examples/g1_demo.gif)
148
+
149
+ A free-floating Unitree G1 (35 DOF) squats, twists and waves while balancing on its
150
+ feet (`examples/g1_demo.py`). Per step, rigidax computes:
151
+ - M and h, the foot contact Jacobians, and Jc_dot·v (via autodiff)
152
+ - reference accelerations that keep the feet planted (KKT solve)
153
+ - the contact wrenches that balance the floating base
154
+ - the feedforward joint torques
155
+
156
+ Joint PD supplies the feedback. A step takes about 190 µs. MuJoCo's position servos
157
+ are replaced by pure torque motors.
158
+
159
+ ```
160
+ env -u PYTHONPATH MUJOCO_GL=egl python examples/g1_demo.py --out g1_demo.mp4
161
+ # one motion set, frax-style grey studio: --motion squat|twist|wave|squat,wave|all --style light
162
+ ```
163
+
164
+ ## Use
165
+
166
+ ```python
167
+ import jax, rigidax as rx
168
+ jax.config.update("jax_enable_x64", True)
169
+
170
+ model = rx.load("robot.xml") # or .urdf; strip_meshes=True if meshes are missing
171
+ q, v = model.q0, jax.numpy.zeros(model.nv)
172
+
173
+ M, bias = jax.jit(rx.dynamics_terms)(model, q, v) # one kinematics pass
174
+ qacc = jax.jit(rx.forward_dynamics)(model, q, v, tau)
175
+ moon = model.replace(gravity=jax.numpy.array([0, 0, -1.62])) # no recompile
176
+
177
+ # gradients w.r.t. physical parameters (e.g. system identification)
178
+ dL_dmass = jax.grad(lambda m: loss(model.replace(body_mass=m)))(model.params.body_mass)
179
+ ```
180
+
181
+ Available functions: `forward_kinematics`, `site_pose`, `body_pose`, `point_jacobian`,
182
+ `site_jacobian`, `body_jacobian`, `center_of_mass`, `center_of_mass_jacobian`, `rnea`,
183
+ `mass_matrix`, `bias_forces`, `gravity_forces`, `coriolis_forces`, `dynamics_terms`,
184
+ `forward_dynamics`, `mass_matrix_inverse`, `integrate`, `difference`.
185
+ Every function takes an optional `kin=` argument to reuse a forward-kinematics pass.
186
+
187
+ Conventions follow MuJoCo: `q` uses `[pos, wxyz quat]` for free joints and `wxyz` for
188
+ ball joints. `v` uses world-frame linear and body-frame angular velocity.
189
+
190
+ ## Tests
191
+
192
+ ```
193
+ env -u PYTHONPATH python -m pytest # unset ROS's PYTHONPATH if it is sourced
194
+ ```
195
+
196
+ ## Speed vs frax (CPU, float64, single thread, jax 0.4.30)
197
+
198
+ Median µs per call, from `bench/bench_vs_frax.py`, two runs. A ratio above 1 means rigidax is faster.
199
+
200
+ | | Panda single | G1 (35 dof) single | Panda batch 1024 | G1 batch 1024 |
201
+ |---|---|---|---|---|
202
+ | mass matrix | 0.96–0.98x | 0.97x | 1.09x | 1.02–1.15x |
203
+ | bias forces | 0.85–0.91x | 0.78–0.80x | 0.95–1.02x | 0.91–0.92x |
204
+ | forward dynamics | 0.82–0.87x | 0.95–0.96x | 0.97–1.11x | 0.94–0.97x |
205
+
206
+ The two are about equal overall. The single-call bias computation is still 10–20% slower.
207
+ GPU has not been benchmarked.
208
+
209
+ ## Known limitations (1.0)
210
+
211
+ - **Synthesized fingers:** Dex3 finger motion is inferred from context (LAFAN1 has no
212
+ finger data). Finger collisions are handled with rigid envelopes, so fists held
213
+ against the torso in the fight guard still touch it in about 1% of frames.
214
+ - **GPU:** the GPU path works but is not tuned (~6k frames/s on an RTX 3060 laptop).
215
+ - **G1 forearm pronation:** on the G1 this lives on `wrist_roll_link`. The twist task
216
+ currently targets `elbow_link`.
217
+
218
+ ## Roadmap
219
+
220
+ - Foot skating p95 (contact-aware per-frame weights), GPU throughput
221
+ - Capsule/box collision primitives
222
+ - Close the remaining bias-forces gap; GPU benchmarks
223
+ - Analytic derivatives of RNEA and ABA
@@ -0,0 +1,48 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "rigidax"
7
+ version = "1.0.0"
8
+ description = "Differentiable rigid-body kinematics, dynamics and batched IK / motion retargeting in JAX (MJCF + URDF)"
9
+ readme = "README.md"
10
+ license = { text = "MIT" }
11
+ authors = [{ name = "Nishanth Sundaran", email = "sundharnishanth@gmail.com" }]
12
+ requires-python = ">=3.10"
13
+ keywords = ["robotics", "kinematics", "dynamics", "inverse-kinematics", "retargeting", "jax", "mujoco"]
14
+ classifiers = [
15
+ "Development Status :: 5 - Production/Stable",
16
+ "Intended Audience :: Science/Research",
17
+ "License :: OSI Approved :: MIT License",
18
+ "Programming Language :: Python :: 3",
19
+ "Programming Language :: Python :: 3.10",
20
+ "Programming Language :: Python :: 3.11",
21
+ "Programming Language :: Python :: 3.12",
22
+ "Topic :: Scientific/Engineering :: Physics",
23
+ ]
24
+ dependencies = ["numpy", "jax>=0.4.30", "mujoco>=3.1"]
25
+
26
+ [project.urls]
27
+ Homepage = "https://github.com/NishanthSundaran/Rigidax"
28
+ Repository = "https://github.com/NishanthSundaran/Rigidax"
29
+ Issues = "https://github.com/NishanthSundaran/Rigidax/issues"
30
+ Changelog = "https://github.com/NishanthSundaran/Rigidax/blob/master/CHANGELOG.md"
31
+
32
+ [project.optional-dependencies]
33
+ test = ["pytest", "pin"]
34
+ dev = ["pytest", "pin", "ruff"]
35
+
36
+ [tool.setuptools.packages.find]
37
+ include = ["rigidax*"]
38
+
39
+ [tool.pytest.ini_options]
40
+ testpaths = ["tests"]
41
+
42
+ [tool.ruff]
43
+ line-length = 100
44
+ target-version = "py310"
45
+
46
+ [tool.ruff.lint]
47
+ select = ["E", "F", "W", "I"]
48
+ ignore = ["E731", "E741"]
@@ -0,0 +1,64 @@
1
+ """rigidax: differentiable rigid-body kinematics and dynamics in JAX"""
2
+
3
+ from rigidax.dynamics import (
4
+ bias_forces,
5
+ body_spatial_inertias,
6
+ coriolis_forces,
7
+ difference,
8
+ dof_inertias,
9
+ dynamics_terms,
10
+ forward_dynamics,
11
+ gravity_forces,
12
+ integrate,
13
+ mass_matrix,
14
+ mass_matrix_inverse,
15
+ rnea,
16
+ )
17
+ from rigidax.kinematics import (
18
+ Kinematics,
19
+ body_coms,
20
+ body_jacobian,
21
+ body_pose,
22
+ center_of_mass,
23
+ center_of_mass_jacobian,
24
+ forward_kinematics,
25
+ point_jacobian,
26
+ points_jacobians,
27
+ site_jacobian,
28
+ site_pose,
29
+ )
30
+ from rigidax.model import Model, Params, Topology, from_mujoco, from_xml_string, load
31
+
32
+ __version__ = "1.0.0"
33
+
34
+ __all__ = [
35
+ "Kinematics",
36
+ "Model",
37
+ "Params",
38
+ "Topology",
39
+ "bias_forces",
40
+ "body_coms",
41
+ "body_jacobian",
42
+ "body_pose",
43
+ "body_spatial_inertias",
44
+ "center_of_mass",
45
+ "center_of_mass_jacobian",
46
+ "coriolis_forces",
47
+ "difference",
48
+ "dof_inertias",
49
+ "dynamics_terms",
50
+ "forward_dynamics",
51
+ "forward_kinematics",
52
+ "from_mujoco",
53
+ "from_xml_string",
54
+ "gravity_forces",
55
+ "integrate",
56
+ "load",
57
+ "mass_matrix",
58
+ "mass_matrix_inverse",
59
+ "point_jacobian",
60
+ "points_jacobians",
61
+ "rnea",
62
+ "site_jacobian",
63
+ "site_pose",
64
+ ]