File size: 7,297 Bytes
56972c4 792cc2f 56972c4 792cc2f 56972c4 792cc2f 56972c4 792cc2f 56972c4 792cc2f 56972c4 792cc2f 56972c4 792cc2f 56972c4 792cc2f | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 | ---
library_name: onnxruntime
tags:
- animation
- character-animation
- inverse-kinematics
- pose-estimation
- onnx
license: other
---
# Animatica Autoposer (SOMA-30)
A neural autoposer for full-body character rigs. Give it a handful of handles β where a hand
should be, where the feet are planted, which way the head looks β and it returns a complete,
natural pose for a 30-joint skeleton. It is what makes posing in the
[Animatica](https://animatica.ai) Blender add-on feel like moving a character rather than
rotating thirty bones.
It is a **poser, not an animator**: one pose per call, a few milliseconds each, no motion model
and no temporal state. Ask it once per frame and you have an editable animation; ask it once and
you have a pose.
## What you give it
Any number of effectors, in any combination. Each carries a **tolerance** β metres of slack β so
a handle can be an exact pin or a loose suggestion the model may overrule to keep the body
plausible.
| Effector | Type id | Value | Meaning |
|---|---|---|---|
| Position | `0` | xyz | put this joint here |
| Rotation | `1` | 6D rotation | orient this joint like this |
| Look-at | `2` | xyz + direction | aim this joint at this point |
Plus a **body descriptor**: 29 bone lengths, z-scored. The same effectors solve correctly on a
child, a heavyset adult, or a giant, because proportion is an input rather than an assumption.
Three position effectors is the sparsest input it was trained to answer well; one or two will
return something, but the body below them is mostly prior.
## What you get back
- **draft joint positions** β `[B, 90]`, the 30 joints as xyz, root first
- **6D rotations** β `[B, 180]`, per joint, for FK on your own skeleton
Run the rotations through SOMA-30 forward kinematics with your body's neutral joints to get world
positions. The bundled IK pass (below) then pins the effectors exactly and keeps the feet out of
the floor.
## Use it in Blender
The Animatica add-on downloads this repository on first use and needs no token or manual setup β
handles appear on your rig and you drag them. That is the shortest path to using this model, and
the one it is tuned for.
## Use it directly (ONNX)
`onnx/` is the runtime bundle: two graphs and the metadata that describes them.
```python
import json, numpy as np, onnxruntime as ort
from huggingface_hub import snapshot_download
d = snapshot_download("Animatica-ai/autoposer", allow_patterns=["onnx/*"])
meta = json.load(open(f"{d}/onnx/meta.json"))
poser = ort.InferenceSession(f"{d}/onnx/poser.onnx")
E = 3 # three handles: hips and both hands
val = np.zeros((1, E, 7), np.float32) # 6 value channels + 1 tolerance
val[0, 0, :3], val[0, 0, 6] = (0.0, 0.95, 0.0), 0.005
val[0, 1, :3], val[0, 1, 6] = (0.35, 1.20, 0.20), 0.005
val[0, 2, :3], val[0, 2, 6] = (-0.35, 1.20, 0.20), 0.005
draft, rot6d = poser.run(None, {
"val": val,
"weights": np.ones((1, E), np.float32), # encoder blending weights
"joint_id": np.array([[0, 13, 19]], np.int64), # Hips, LeftHand, RightHand
"eff_type": np.zeros((1, E), np.int64), # all positions
"cond": np.zeros((1, 29), np.float32), # z-scored bone lengths
})
```
Two details the graph will not tell you, and which decide whether the output is a pose or a heap:
- **Reference frame.** Position and look-at values are expressed relative to the centroid of the
position targets. This checkpoint is floor-referenced (`floor_ref: true`), so **y stays
absolute** β the ground is a real height, not something relative β and the centroid is zeroed
on y before subtracting.
- **Conditioning.** `cond` is the 29 bone lengths, z-scored with `blen_mean` / `blen_std` from
`meta.json` and clamped to Β±4.5. Zeros mean "the average body the statistics describe", which
is why the snippet above works at all.
### The IK pass
`onnx/ik.onnx` is a damped least-squares refinement that runs after the network: it pins
effectors to the tolerance you asked for, holds a floor plane, and rolls the toes rather than
sinking the feet. Twelve iterations over a damping ladder, accepting per step. The network gets
the pose right; this makes the handles exact.
`meta.json` carries a **selftest** β recorded inputs and expected outputs β so a miscompiled
graph is detectable. It matters more than it sounds: a bad kernel fusion does not raise, it
quietly returns a wrong pose. Run the IK graph on `selftest.inputs` and compare against
`selftest.expect`; agreement should be well under half a centimetre.
## Use it directly (PyTorch)
`model.safetensors` + `config.json` are the training weights, for research use and for anyone who
wants to fine-tune. `config.json` records the skeleton (`njoints`, `ux`/`ux_names`), the
conditioning width (`cond_dim: 29`), floor referencing, and the training step.
## The skeleton
30 joints, root first:
```
Hips Spine1 Spine2 Chest Neck1 Neck2 Head Jaw LeftEye RightEye
LeftShoulder LeftArm LeftForeArm LeftHand LeftHandThumbEnd LeftHandMiddleEnd
RightShoulder RightArm RightForeArm RightHand RightHandThumbEnd RightHandMiddleEnd
LeftLeg LeftShin LeftFoot LeftToeBase
RightLeg RightShin RightFoot RightToeBase
```
`meta.json` has the parent list, the neutral (rest) joint positions, the four foot joints used by
the floor term, and the effector joint ids.
## How it works
Each effector is embedded from its value, tolerance, joint id and type, and the set is blended by
weight into a single pose code β so the model takes any number of handles in any order, rather
than a fixed rig of them. Two-stage decoding follows: draft joint positions first, then per-joint
6D rotations, which is what keeps limbs attached to a body of the right proportions instead of
merely near the handles. The body descriptor is injected twice, at the encoder alongside each
effector and again at the decoder embedding (zero-initialised, so it starts as a no-op and is
learned into).
## Files
| File | Size | What |
|---|---|---|
| `onnx/poser.onnx` | 142 MB | the network |
| `onnx/ik.onnx` | 1.9 MB | the IK refinement pass |
| `onnx/meta.json` | 19 KB | skeleton, normalisation stats, checksums, selftest |
| `model.safetensors` | β | PyTorch weights |
| `config.json` | β | training configuration |
## Limitations
- **One pose at a time.** There is no temporal model: consecutive frames are consistent because
consecutive effectors are, not because the model remembers anything.
- **SOMA-30 only.** Other skeletons need retargeting onto these 30 joints first.
- **Hands and face are along for the ride.** Fingers are a thumb and middle tip per hand; jaw and
eyes are in the skeleton but are not meaningfully posed.
- **Fewer than three position effectors** leaves most of the body to the prior. It will answer;
it just has little to answer from.
- **Floor-referenced.** Feed it absolute heights. A pose built in a different vertical frame will
come back crouching or floating.
---
Made by [Animatica](https://animatica.ai). Found something odd? Tell us β the model and the
add-on move together, and a pose that comes out wrong is usually interesting.
|