# Skeletons & characters

The heart of the SDK: [`USkeletonConsumer`](#uskeletonconsumer) connects to the skeleton stream, spawns/destroys a character per tracked person, drives each [`UAR51Character`](#uar51character) every frame, and fires person-lifecycle events.

:::caution[Units]
UE works in **centimeters / degrees**; AR 51 mocap data is natively **meters**. Conversion happens inside the SDK before bone driving — by the time positions reach `UAR51Character::Solve(...)` they appear to be **UE world-space cm**. ⚠️ The exact layer that scales meters→cm, and whether `SkeletonData.Positions` handed to callers is meters or cm, is unconfirmed.
:::

## USkeletonConsumer

Moved to [`USkeletonConsumer`](/docs/sdk-api/unreal-api/classes/skeletonconsumer).

## UAR51Character

Moved to [`UAR51Character`](/docs/sdk-api/unreal-api/classes/uar51character).

## AAR51CharacterActor

Moved to [`AAR51CharacterActor`](/docs/sdk-api/unreal-api/classes/aar51characteractor).

## UAR51SkeletonMapping

Moved to [`UAR51SkeletonMapping`](/docs/sdk-api/unreal-api/classes/uar51skeletonmapping).

### FBoneWeight

<Badge tone="type">USTRUCT</Badge> <Badge tone="accent">BlueprintType</Badge>

A single bone + blend weight, used for twist chains: `FName Bone`; `float Weight = 1.0f` (clamp 0–1). Both `EditAnywhere, BlueprintReadWrite`.

### UAR51FootBoundsComponent

Moved to [`UAR51FootBoundsComponent`](/docs/sdk-api/unreal-api/classes/uar51footboundscomponent).

## SkeletonData

Moved to [`SkeletonData`](/docs/sdk-api/unreal-api/classes/skeletondata).

## Keypoints

Moved to [`Keypoints`](/docs/sdk-api/unreal-api/classes/skeletondata#keypoints).

## IPersonBase

Moved to [`IPersonBase`](/docs/sdk-api/unreal-api/classes/ipersonbase).

## BoneTransform

Moved to [`BoneTransform`](/docs/sdk-api/unreal-api/classes/bonetransform).

## Prediction

Smoothing / extrapolation knobs, set via `USkeletonConsumer::SkeletonPredictionSettings`. The predictor classes themselves are C++-only strategy types — you configure them entirely through these structs.

### EPredictionModel

<Badge tone="type">UENUM</Badge> <Badge tone="accent">BlueprintType</Badge>

`None` ("No Prediction"), `Linear`, `Kalman` ("Kalman Filter").

### FPredictionSettings

<Badge tone="type">USTRUCT</Badge> <Badge tone="accent">BlueprintType</Badge>

| Field | Default | Meaning |
|---|---|---|
| `Model` | `None` | which model filters/predicts incoming skeleton data |
| `PredictionDeltaTime` | `0.016f` | look-ahead Δt in **seconds** (~60 FPS) |
| `MaxPredictionDisplacement` | `5.0f` | clamp look-ahead displacement (**cm**) |
| `OutlierThreshold` | `50.f` | ignore per-frame spikes above this (**cm**) |
| `Linear` | — | `FLinearPredictionTuning` (shown when `Model==Linear`) |
| `Kalman` | — | `FKalmanPredictionTuning` (shown when `Model==Kalman`) |

**`FLinearPredictionTuning`** — `VelocitySmoothingAlpha{0.92}` (0–1; higher = smoother), `MaxVelocity{800.f}` (**cm/s**), `ConfidenceMinForVelocity{0.2}` (0–1).

**`FKalmanPredictionTuning`** — `AccelStd{200.f}` (**cm/s²**; higher = more responsive), `MeasurementNoisePos{2.0}` (**cm²**; higher = smoother), `bConfidenceWeighting{true}` (scale trust by `1 - confidence`).

## IK solver & auto-scale settings

### FSolverParams

<Badge tone="type">USTRUCT</Badge> <Badge tone="accent">BlueprintType</Badge>

Set via `USkeletonConsumer::SolverParameters`. All `EditAnywhere, BlueprintReadWrite`:

- `Enabled{true}`.
- `CalfCorrectionScale{0.95}` — ankle-position correction (1.0 = none); per-character override via [`UAR51Character`](#uar51character).
- `HandCorrectionScale{1.0}` — hand auto-scale correction (1.0 = none).
- `MinimumHandsConfidence{0.1}` — min headset-hand confidence still solved.
- `EnableLimbPositioning{true}`, `EnableClavicleRotation{true}`.
- `AutoScaleType{Default}`, `AutoHandScaleType{NoScaling}`, `FilterType{SimpleSmoothing}`.
- `SmoothingFactor{0.995}`, `MinValidScale{0.2}`, `MaxValidScale{1.8}`.
- `EnableBodySmoothing{true}`, `TemporalRotationalSmooth{0.75}`, `TemporalRotationalNeckSmooth{0.75}`, `EnableFingerSmoothing{true}`, `TemporalFingersRotationalSmooth{0.75}`.
- Helpers: `bool IsValidScale(float)`, `bool IsEnableLimbPositioning()`.

### Related enums

<Badge tone="type">UENUM</Badge> <Badge tone="accent">BlueprintType</Badge>

- `EAutoCharacterAssignmentMode` — `ACAM_Default` (first valid character), `ACAM_Cycle`, `ACAM_Random`.
- `EAutoScaleFilterType` — `FT_Default`, `FT_Kalman`, `FT_MovingAverage`, `FT_MovingMedian`, `FT_SimpleSmoothing`.
- `EActivePersonCameraSpace` — `APCS_ComponentSpace` (default, world location), `APCS_RelativeSpace` (relative within parent).
- `EAutoScaleType` — `FT_NoScaling`, `FT_Default`.

## Walkthrough

**Connect → receive skeleton → drive a character** (Blueprint-first; the SDK does the heavy lifting):

1. **Place the consumer.** Add a [`USkeletonConsumer`](#uskeletonconsumer) component to an actor (e.g. a "GameManager" actor). Only one is needed — it's a singleton.
2. **Author a mapping asset.** Create a [`UAR51SkeletonMapping`](#uar51skeletonmapping), set `TargetSkeleton`, run `AutoMap()` (or map in the AR 51 humanoid mapping editor). Check `GetValidationSummary` shows 0 missing/invalid. ⚠️ exact editor menu/action name unconfirmed.
3. **Make a character actor.** Create a Blueprint child of [`AAR51CharacterActor`](#aar51characteractor) for your mesh; set its `Character`'s `AR51Mapping` to the asset from step 2. (Fit `Left/RightFootBounds` via the character's "Create Foot Bounds" action.) ⚠️ whether `AR51Mapping` is set on the BP default or wired at spawn is unconfirmed.
4. **Register the character.** Add your character BP to `USkeletonConsumer::CharacterBlueprints`. Optionally map specific people via `PersonIdToCharacterBPMap` / `SetCharacterByPersonId(...)`, or rely on `AutoCharacterAssignmentMode`.
5. **Tune (optional).** Set `SolverParameters` and `SkeletonPredictionSettings` (e.g. `Model=Linear`). Set the main camera via `SetMainCameraComponent` if head fusion / active-person selection needs a specific one.
6. **Connect.** On BeginPlay, call `Connect("IP:Port")` (the AR 51 skeleton service endpoint). ⚠️ default/expected endpoint string unconfirmed.
7. **React to people.** Bind `OnPersonDetected` / `OnPersonUpdated` / `OnPersonDestroyed` / `OnActivePersonChanged`. The consumer **automatically spawns an `AAR51CharacterActor` per detected person and drives it every frame** (calls `Solve(...)` internally) — you do **not** call `Solve` yourself. Use `GetPersonByPersonId(...)` / `GetActivePerson()` → `GetCharacter()` to read state (head/wrist positions, etc.).
8. **Cleanup.** `Disconnect()` on EndPlay; `ResetCharacters()` to clear all spawned models.

:::note[Manual/advanced path]
If you drive a [`UAR51Character`](#uar51character) yourself: call `Initialize()` once, then per frame call `Solve(positions, confidence)` with positions indexed by [`Keypoints`](#keypoints) in UE world-space cm, followed by `ApplyOnSkeletalMeshes()`. ⚠️ whether `positions` are world- or component-space, and the meters→cm scaling responsibility at that boundary, is unconfirmed.
:::

---

Next: [Object detection](/docs/sdk-api/unreal-api/object-detection).
