cs686's picture
Deploy Kimodo ZeroGPU motion API
a550c4e verified
|
Raw
History Blame Contribute Delete
13.5 kB
# Metrics
The benchmark evaluates generated motion along three axes:
- **Motion quality** -- foot-skate and contact-consistency metrics,
- **Constraint following** -- position error for root, end-effector, and full-body constraints,
- **Text alignment** -- TMR retrieval and distributional metrics.
Metrics are implemented in `kimodo/metrics/` and orchestrated by `benchmark/evaluate_folder.py`.
The protocol is aligned with the [tech report](https://research.nvidia.com/labs/sil/projects/kimodo/assets/kimodo_tech_report.pdf) (Sec. 6.1, "Evaluation Metrics").
## Evaluation Protocol
The evaluation pipeline runs two passes over each group of test cases:
1. **Generated pass** -- evaluates `motion.npz` with all metrics (foot skate, contact consistency, constraint following) and, when TMR embeddings are available, computes retrieval and FID scores.
2. **Ground-truth pass** -- evaluates `gt_motion.npz` with the same motion-quality and constraint metrics. TMR retrieval metrics are not recomputed in this pass.
Running both passes enables side-by-side comparison: the GT row serves as an empirical upper bound for motion quality, and deviations between GT and generated metrics highlight where the model can improve. See [Evaluation pipeline](pipeline.md) for the full workflow.
## Metrics Reference
The table below lists every key written to `metrics.json`. Detailed descriptions follow in subsequent sections.
| Key | Category | Unit | Direction |
| --- | --- | --- | --- |
| `foot_skate_from_height` | Motion quality | m/s | Lower is better |
| `foot_skate_from_pred_contacts` | Motion quality | m/s | Lower is better |
| `foot_skate_max_vel` | Motion quality | m/s | Lower is better |
| `foot_skate_ratio` | Motion quality | ratio (0--1) | Lower is better |
| `foot_contact_consistency` | Motion quality | ratio (0--1) | Higher is better |
| `constraint_root2d_err` | Constraint follow | m | Lower is better |
| `constraint_root2d_err_p95` | Constraint follow | m | Lower is better |
| `constraint_root2d_acc` | Constraint follow | ratio (0--1) | Higher is better |
| `constraint_fullbody_keyframe` | Constraint follow | m | Lower is better |
| `constraint_end_effector` | Constraint follow | m | Lower is better |
| `TMR/t2m_sim` | Text alignment | score (0--1) | Higher is better |
| `TMR/t2m_R/R01` ... `R10` | Text alignment | % | Higher is better |
| `TMR/t2m_R/MedR` | Text alignment | rank | Lower is better |
| `TMR/FID/gen_text` | Text alignment | distance | Lower is better |
| `TMR/FID/gen_gt` | Text alignment | distance | Lower is better |
| `TMR/FID/gt_text` | Text alignment | distance | Lower is better |
| `TMR/m2m_sim` | Text alignment | score (0--1) | Higher is better |
| `TMR/t2m_gt_sim` | Text alignment | score (0--1) | Higher is better |
| `TMR/m2m_R/R01` ... `R10` | Text alignment | % | Higher is better |
| `TMR/t2m_gt_R/R01` ... `R10` | Text alignment | % | Higher is better |
:::{note}
Raw metric values are stored in SI units (meters for positions, m/s for velocities).
The summary tables printed by `benchmark/parse_folder.py` convert constraint position errors to **cm** and foot-skate velocities to **cm/s** for readability.
:::
### Foot Skating Metrics
Foot skating measures how much a foot slides along the ground when it should be in static contact with the ground. Four complementary metrics capture different aspects of this artifact.
- **`foot_skate_from_height`** (m/s, lower is better):
Mean velocity of the **toe joints** (left toe, right toe) on frames where the toe height is below a floor threshold (`height_thresh = 0.05 m`).
This metric does not rely on predicted contact labels -- it uses a geometric criterion (Y-coordinate < threshold) to identify ground-contact frames.
- **`foot_skate_from_pred_contacts`** (m/s, lower is better):
Mean velocity of all **four foot joints** (left/right heel and toe) on frames where the model predicts contact via the `foot_contacts` output.
Unlike `foot_skate_from_height`, this metric trusts the model's own contact predictions and measures all four foot joints rather than toes only.
- **`foot_skate_max_vel`** (m/s, lower is better):
Maximum velocity across all four foot joints and all time steps where predicted contact is active.
This captures worst-case slip spikes that mean-based metrics can hide.
- **`foot_skate_ratio`** (ratio 0--1, lower is better):
Fraction of ground-contact frames where toe velocity exceeds a threshold (`vel_thresh = 0.2 m/s`). A frame counts as ground contact when the toe is below `height_thresh = 0.05 m` on both the current and the next frame. Inspired by the [GMD](https://github.com/korrawe/guided-motion-diffusion) skating metric.
### Contact Consistency Metric
- **`foot_contact_consistency`** (ratio 0--1, higher is better):
Agreement between the model's predicted foot contacts and a heuristic contact detector based on joint height and velocity (`vel_thresh = 0.15 m/s`, `height_thresh = 0.10 m`).
Computed as accuracy (`1 - incorrect_ratio`) over all time steps and four contact channels.
A score of 1.0 means perfect agreement between predicted and heuristic contacts.
As noted in the [tech report](https://research.nvidia.com/labs/sil/projects/kimodo/assets/kimodo_tech_report.pdf), this metric provides important context for interpreting the contact-based foot-skate metrics above: if contact consistency is low, `foot_skate_from_pred_contacts` may be unreliable.
### Constraint-Following Metrics
Constraint metrics are computed only when the test case includes a `constraints.json` file. The `ContraintFollow` metric class dispatches by [constraint type](../key_concepts/constraints.md):
- **`constraint_end_effector`** (m, lower is better):
Mean Euclidean distance between target end-effector positions and generated joint positions at the constrained frames.
Only position-constrained joints are evaluated (rotation targets are not measured by this metric).
- **`constraint_fullbody_keyframe`** (m, lower is better):
Mean per-joint Euclidean distance between target and generated full-body joint positions at keyframes.
The error is averaged over all joints and all keyframe frames.
- **`constraint_root2d_err`** (m, lower is better):
Mean 2D Euclidean distance (in the XZ ground plane) between target and generated root positions at constrained frames.
- **`constraint_root2d_err_p95`** (m, lower is better):
95th percentile of the per-frame root 2D error across all samples in a group.
Computed during aggregation by `evaluate_folder.py` to capture tail-end failures that the mean can mask.
- **`constraint_root2d_acc`** (ratio 0--1, higher is better):
Fraction of constrained root frames where the 2D position error is within a distance threshold (`root_threshold = 0.10 m`).
### TMR-Based Metrics
Text alignment is evaluated using [TMR](https://mathis.petrovich.fr/tmr/) (Text-to-Motion Retrieval), a separate encoder model that maps both text and motion into a shared embedding space. TMR is not used for generation -- it is loaded only for evaluation (see `kimodo/model/tmr.py`).
We release a version of TMR retrained on the full Rigplay dataset as [`TMR-SOMA-RP-v1`](https://huggingface.co/nvidia/TMR-SOMA-RP-v1). The original TMR was trained on HumanML3D; our retrained variant uses the same architecture but is trained on the Rigplay motion dataset, SOMA skeleton, and with [LLM2Vec](https://github.com/McGill-NLP/llm2vec) text embeddings.
#### Similarity Scores
TMR encodes each text prompt and each motion clip into a unit-length embedding vector. Cosine similarity between text and motion embeddings is rescaled to a [0, 1] range:
```
score = cosine_similarity / 2 + 0.5
```
Three per-test-case similarity scores are recorded:
- **`TMR/t2m_sim`** (0--1, higher is better): similarity between the text prompt and the generated motion.
- **`TMR/m2m_sim`** (0--1, higher is better): similarity between the generated and ground-truth motions (only when GT is available).
- **`TMR/t2m_gt_sim`** (0--1, higher is better): similarity between the text prompt and the GT motion (only when GT is available).
#### R-precision (Retrieval Accuracy)
R-precision measures whether the correct motion can be retrieved from a pool given its corresponding text query.
For each text query in the evaluation group, all motions are ranked by TMR similarity.
R@k is the percentage of queries where the correct motion appears in the top k results.
Reported keys: `TMR/t2m_R/R01`, `R02`, `R03`, `R05`, `R10` (%), and `TMR/t2m_R/MedR` (median rank, lower is better) correspond to retrieval accuracy when using generated motions.
When ground-truth motions are available, analogous retrieval metrics are computed for motion-to-GT-motion (`TMR/m2m_R/...`) and text-to-GT-motion (`TMR/t2m_gt_R/...`).
:::{note}
Near-duplicate text prompts can artificially penalize retrieval ranking. The evaluation handles this by grouping prompts whose text-text similarity exceeds a threshold of 0.99 and treating any motion in that group as a valid match.
:::
#### FID (Frechet Inception Distance)
FID measures distributional distance between two sets of TMR embeddings by fitting a multivariate Gaussian to each set and computing the Frechet distance. Three FID variants are reported:
- **`TMR/FID/gen_gt`**: distance between generated-motion and GT-motion embeddings (only when GT is available). This is the FID metric that is typically reported in the motion generation literature.
- **`TMR/FID/gen_text`**: distance between generated-motion embeddings and text embeddings.
- **`TMR/FID/gt_text`**: distance between GT-motion and text embeddings (only when GT is available).
Lower values indicate that the two distributions are more similar. FID requires at least 2 samples; groups with fewer samples report `NaN`.
#### Per-Test-Case Retrieval
In addition to the aggregate metrics above, each test case's `metrics.json` includes a `tmr` block with single motion retrieval results:
- `t2m_rank`: the rank of the correct motion when retrieving with this test case's text query.
- `top5_retrieved`: the top-5 retrieved motions (sample IDs and text prompts) for inspection.
## JSON Output Format
Below is a representative `metrics.json` written by `evaluate_folder.py` for a single test case with mixed constraints (root + end-effector + full-body) and TMR embeddings:
```json
{
"num_motions": 1,
"folder": "...",
"per_motion_mean_gen": {
"foot_skate_from_height": 0.3144,
"foot_skate_from_pred_contacts": 0.0672,
"foot_skate_max_vel": 0.2109,
"foot_contact_consistency": 0.9522,
"foot_skate_ratio": 0.2182,
"constraint_end_effector": 0.0286,
"constraint_root2d_err": 0.0534,
"constraint_root2d_acc": 1.0,
"constraint_fullbody_keyframe": 0.0324,
"TMR/t2m_sim": 0.8209
},
"per_motion_mean_gt": {
"foot_skate_from_height": 0.2361,
"foot_skate_from_pred_contacts": 0.0269,
"foot_skate_max_vel": 0.1459,
"foot_contact_consistency": 1.0,
"foot_skate_ratio": 0.1402,
"constraint_end_effector": 9.82e-07,
"constraint_root2d_err": 0.0407,
"constraint_root2d_acc": 1.0,
"constraint_fullbody_keyframe": 8.73e-07
},
"tmr": {
"t2m_rank": 2,
"text": "A person is swiftly performing a dance move by moving their hands and legs.",
"top5_retrieved": [
{
"id": "0231",
"text": "A person is performing dance steps while stepping back and forward..."
},
{
"id": "0029",
"text": "A person is swiftly performing a dance move by moving their hands and legs."
}
]
}
}
```
Group-level aggregate JSONs (`<group_name>.json`) have the same structure but with `num_motions > 1`, averaged per-motion metrics, additional keys like `constraint_root2d_err_p95`, and a `tmr` block containing the aggregate retrieval and FID scores:
```json
{
"num_motions": 256,
"folder": "...",
"per_motion_mean_gen": {
"foot_skate_from_height": 0.1742,
"foot_skate_from_pred_contacts": 0.0611,
"foot_skate_max_vel": 0.3747,
"foot_contact_consistency": 0.9483,
"foot_skate_ratio": 0.1499,
"constraint_end_effector": 0.0367,
"constraint_root2d_err": 0.0495,
"constraint_root2d_acc": 0.9212,
"constraint_fullbody_keyframe": 0.0324,
"constraint_root2d_err_p95": 0.1115
},
"per_motion_mean_gt": {
"foot_skate_from_height": 0.1617,
"foot_skate_from_pred_contacts": 0.0235,
"foot_skate_max_vel": 0.1185,
"foot_contact_consistency": 1.0,
"foot_skate_ratio": 0.1214,
"constraint_end_effector": 1.48e-06,
"constraint_root2d_err": 0.0376,
"constraint_root2d_acc": 1.0,
"constraint_fullbody_keyframe": 1.16e-06,
"constraint_root2d_err_p95": 0.0602
},
"tmr": {
"TMR/t2m_sim": 0.8742,
"TMR/t2m_R/R01": 75.39,
"TMR/t2m_R/R02": 85.55,
"TMR/t2m_R/R03": 88.28,
"TMR/t2m_R/R05": 90.23,
"TMR/t2m_R/R10": 93.36,
"TMR/t2m_R/MedR": 1.0,
"TMR/t2m_R/len": 256.0,
"TMR/FID/gen_text": 0.1442,
"TMR/m2m_R/R01": 94.53,
"TMR/m2m_R/R02": 97.66,
"TMR/m2m_R/R03": 98.05,
"TMR/m2m_R/R05": 98.83,
"TMR/m2m_R/R10": 99.22,
"TMR/m2m_R/MedR": 1.0,
"TMR/m2m_R/len": 256.0,
"TMR/t2m_gt_R/R01": 80.47,
"TMR/t2m_gt_R/R02": 88.28,
"TMR/t2m_gt_R/R03": 91.02,
"TMR/t2m_gt_R/R05": 92.58,
"TMR/t2m_gt_R/R10": 94.53,
"TMR/t2m_gt_R/MedR": 1.0,
"TMR/t2m_gt_R/len": 256.0,
"TMR/FID/gen_gt": 0.0387,
"TMR/FID/gt_text": 0.1349
}
}
```