Skip to content

Commit 546d5cd

Browse files
author
Yifeng Jiang
committed
update kimodo soma support to match upstream
Made-with: Cursor
1 parent 33c2d3d commit 546d5cd

File tree

5 files changed

+31
-59
lines changed

5 files changed

+31
-59
lines changed

data/scripts/convert_soma23_npz_to_proto.py

Lines changed: 20 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -14,17 +14,16 @@
1414
# limitations under the License.
1515
#
1616
"""
17-
Convert SOMASkeleton30 NPZ motion files to ProtoMotions format for soma23.
17+
Convert SOMA NPZ motion files to ProtoMotions format for soma23.
1818
19-
NPZ files contain data from the SOMA pipeline with 30-body SOMASkeleton30:
20-
local_rot_mats — (T, 30, 3, 3) local rotation matrices
21-
root_positions — (T, 3) root translation
19+
NPZ files contain the full 77-joint SOMA skeleton:
20+
local_rot_mats — (T, 77, 3, 3) local rotation matrices
21+
posed_joints — (T, 77, 3) global joint positions (root pos = index 0)
22+
global_rot_mats — (T, 77, 3, 3) global rotation matrices (unused by converter)
23+
foot_contacts — (T, 4) foot contact labels (unused by converter)
2224
23-
A legacy batch dimension (B=1) is also accepted and squeezed automatically.
24-
25-
The 30 bodies are subselected to the 23 MJCF bodies by dropping 7 leaf
26-
end-effectors: Jaw, LeftEye, RightEye, LeftHandThumbEnd, LeftHandMiddleEnd,
27-
RightHandThumbEnd, RightHandMiddleEnd.
25+
The 77 joints are subselected to the 23 MJCF bodies using SOMASKEL77_TO_MJCF_INDICES
26+
(same mapping as the npy and BVH pipelines).
2827
2928
Usage:
3029
python data/scripts/convert_soma23_npz_to_proto.py \
@@ -42,30 +41,13 @@
4241
from protomotions.components.pose_lib import extract_kinematic_info
4342

4443
from convert_soma23_to_proto import (
45-
MJCF_BODY_NAMES,
44+
SOMASKEL77_TO_MJCF_INDICES,
4645
create_motion_from_soma23_data,
4746
)
48-
from contact_detection import compute_contact_labels_from_pos_and_vel
4947
from motion_filter import passes_exclude_motion_filter
5048

5149
app = typer.Typer(pretty_exceptions_enable=False)
5250

53-
# SOMASkeleton30 bone order (from SOMASkeleton30.bone_order_names_with_parents)
54-
SOMASKEL30_BONE_NAMES = [
55-
"Hips", "Spine1", "Spine2", "Chest",
56-
"Neck1", "Neck2", "Head", "Jaw", "LeftEye", "RightEye",
57-
"LeftShoulder", "LeftArm", "LeftForeArm", "LeftHand",
58-
"LeftHandThumbEnd", "LeftHandMiddleEnd",
59-
"RightShoulder", "RightArm", "RightForeArm", "RightHand",
60-
"RightHandThumbEnd", "RightHandMiddleEnd",
61-
"LeftLeg", "LeftShin", "LeftFoot", "LeftToeBase",
62-
"RightLeg", "RightShin", "RightFoot", "RightToeBase",
63-
]
64-
65-
SOMASKEL30_TO_MJCF_INDICES = [
66-
SOMASKEL30_BONE_NAMES.index(name) for name in MJCF_BODY_NAMES
67-
]
68-
6951

7052
@app.command()
7153
def main(
@@ -82,7 +64,7 @@ def main(
8264
duration_height_seconds: float = typer.Option(0.6),
8365
yaml_output_name: Optional[str] = None,
8466
):
85-
"""Convert SOMASkeleton30 NPZ motion files to ProtoMotions format."""
67+
"""Convert SOMA NPZ motion files (77 joints) to ProtoMotions format."""
8668
device = torch.device("cpu")
8769
dtype = torch.float32
8870

@@ -119,33 +101,26 @@ def main(
119101
try:
120102
data = np.load(npz_file, allow_pickle=True)
121103

122-
if "local_rot_mats" not in data or "root_positions" not in data:
123-
print(
124-
f"Skipping {npz_file}: missing 'local_rot_mats' or 'root_positions'"
125-
)
126-
continue
127-
128-
local_rot_mats = data["local_rot_mats"]
129-
root_pos = data["root_positions"]
104+
for required in ("local_rot_mats", "posed_joints"):
105+
if required not in data:
106+
print(f"Skipping {npz_file}: missing '{required}'")
107+
continue
130108

131-
# Squeeze leading batch dimension if present
132-
if local_rot_mats.ndim == 5:
133-
local_rot_mats = local_rot_mats[0]
134-
if root_pos.ndim == 3:
135-
root_pos = root_pos[0]
109+
local_rot_mats = data["local_rot_mats"] # (T, 77, 3, 3)
110+
root_pos = data["posed_joints"][:, 0, :] # (T, 3)
136111

137-
if local_rot_mats.shape[1] != 30:
112+
if local_rot_mats.shape[1] != 77:
138113
print(
139-
f"Skipping {npz_file}: expected 30 bodies, "
114+
f"Skipping {npz_file}: expected 77 joints, "
140115
f"got {local_rot_mats.shape[1]}"
141116
)
142117
continue
143118

144119
print(f" local_rot_mats: {local_rot_mats.shape}")
145120
print(f" root_pos: {root_pos.shape}")
146121

147-
# Subselect 30→23 MJCF bodies
148-
local_rot_mats = local_rot_mats[:, SOMASKEL30_TO_MJCF_INDICES, :, :]
122+
# Subselect to 23 MJCF bodies
123+
local_rot_mats = local_rot_mats[:, SOMASKEL77_TO_MJCF_INDICES, :, :]
149124

150125
# Downsample
151126
local_rot_mats = local_rot_mats[::downsample_factor]

data/scripts/convert_soma23_to_proto.py

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,9 @@
2121
posed_joints/<session>/<motion>.npy — (T, 77, 3) joint positions, y-up
2222
joint_rot_mats/<session>/<motion>.npy — (T, 77, 3, 3) LOCAL rotation matrices
2323
24-
The 77-body SOMASkeleton77 is subselected down to the 23 bodies in
25-
soma23_humanoid.xml. The 7 dropped bodies (from SOMASkeleton30's 30) are
26-
leaf end-effectors without actuators:
27-
Jaw, LeftEye, RightEye,
28-
LeftHandThumbEnd, LeftHandMiddleEnd,
29-
RightHandThumbEnd, RightHandMiddleEnd
24+
The 77-body SOMA skeleton is subselected down to the 23 bodies in
25+
soma23_humanoid.xml, dropping hands, finger details, face joints,
26+
and toe ends (leaf end-effectors without actuators).
3027
3128
Coordinate system is converted from y-up to z-up.
3229
"""
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
version https://git-lfs.github.com/spec/v1
2-
oid sha256:09a5f17090175150c926dec5500bcd0e2ee0ee1621f4147f82936475f19d8824
3-
size 385250
2+
oid sha256:6624bec7c961dde541fb6dacec48f8a6672f393f4baa09bda788237efd55b792
3+
size 971858
0 Bytes
Binary file not shown.

docs/source/getting_started/kimodo_preparation.rst

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ Overview
1010

1111
Kimodo can generate motions from text prompts in two skeleton formats:
1212

13-
* **SOMA skeleton** (30 joints): output as ``.npz`` files at 30 fps
13+
* **SOMA skeleton** (77 joints): output as ``.npz`` files at 30 fps
1414
* **G1 skeleton**: Unitree G1 robot, output as ``.csv`` files at 30 fps
1515

1616
Both formats can be converted to ProtoMotions ``.motion`` files and packaged into
@@ -35,12 +35,12 @@ SOMA Skeleton Motions (.npz)
3535

3636
Kimodo generates SOMA-format motions as ``.npz`` files with the following fields:
3737

38-
* ``local_rot_mats``: ``(T, 30, 3, 3)`` — local rotation matrices for 30 joints
39-
* ``root_positions``: ``(T, 3)`` — root translations in meters
38+
* ``local_rot_mats``: ``(T, 77, 3, 3)`` — local rotation matrices for the full SOMA skeleton
39+
* ``posed_joints``: ``(T, 77, 3)`` — global joint positions (root position at index 0)
4040
* ``foot_contacts``: ``(T, 4)`` — foot contact labels (optional)
4141

42-
The converter subselects from 30 joints to the 23 actuated joints in
43-
``soma23_humanoid.xml``, dropping 7 leaf end-effectors (face and fingertip joints).
42+
The converter subselects from 77 joints to the 23 actuated joints in the MJCF,
43+
dropping hands, finger details, face joints, and toe ends.
4444

4545
.. note::
4646

@@ -193,7 +193,7 @@ Supported Skeleton Formats
193193
- ProtoMotions Robot
194194
* - Kimodo SOMA
195195
- ``.npz``
196-
- 30
196+
- 77
197197
- ``convert_soma23_npz_to_proto.py``
198198
- ``soma23``
199199
* - Kimodo G1

0 commit comments

Comments
 (0)