> ## Documentation Index
> Fetch the complete documentation index at: https://docs.almond.bot/llms.txt
> Use this file to discover all available pages before exploring further.

# Run Policy Config Parameters

> Every setting for axol run-policy — what it does and its default.

export const prefix_2 = "robot_config.cameras."

export const prefix_1 = "robot_config."

export const F = ({children}) => <code style={{
  whiteSpace: "nowrap"
}}>{children}</code>;

export const prefix_0 = "robot_config.axol_config."

Every setting for `axol run-policy`. Add any flag below to the command, or set it once in the control panel — see [Command configuration](/cli/configuration) for config files and syntax details.

In the [shared settings file](/guides/control-panel-settings) (`~/.almond/settings.json`, read by default) `robot_config.axol_config.*` is the file's `axol` section, the other `robot_config.*` fields are `lerobot`, `--left_channel` / `--right_channel` live under `robot`, the recording fields under `recording`, and the inference and action-chunking fields (plus `policy_torque_threshold`) under `inference`.

```bash theme={null}
axol run-policy --policy_path myorg/pick-act --policy_type act --task "pick up the cup" \
  --robot_config.axol_config.left_stiffness 0.8
```

<Warning>
  Stiffness, gripper torque, `observe_torques`, `observe_cartesian`, `fps`, and the camera set must be the **same as in [Data Collection](/operations/data-collection-config)**. A policy only works with the dynamics it was trained on.
</Warning>

## Policy

| Flag | Default | Description |
| - | - | - |
| `--policy_type` | required | `act`, `smolvla`, `diffusion`, `tdmpc`, `vqbet`, `pi0`, `pi05`, `groot`, or `custom` ([your own model](/operations/custom-policy)). |
| `--policy_path` | required for LeRobot policies | Policy checkpoint — a HuggingFace Hub repo id or local path (reachable from the inference server when remote). Unused for `custom`; model selection stays at the endpoint. |
| `--task` | required | Task description stored with recorded frames. Sent to LeRobot policies; custom policies choose instructions at the endpoint. |
| `--device` | `cuda` | `cuda`, `cpu`, or `mps` (LeRobot policies only). |
| `--episode_time_s` | `120` | Maximum episode length (s). |
| `--fps` | `30` | Control rate. Must match the training fps unless `--allow_fps_mismatch true` (a checkpoint trained on 60 fps data needs `--fps 60`). |
| `--allow_fps_mismatch` | `false` | Start even if `fps` differs from the training fps. Experiments only. |
| `--server_host` | `null` (local) | Address of a remote [`inference-server`](/cli/inference-server). `null` starts one locally for LeRobot policies. With `custom`, connects to an existing endpoint (`null` = this machine); no server is spawned. |
| `--server_port` | `8765` | Inference server port (or your custom policy server's). |

## Action chunking

Custom policies use the [custom policy interface](/api/policy-plan): the endpoint
publishes the final plan, and the robot adopts its remaining rows without
altering that plan. LeRobot policies retain robot-side chunk aggregation.
Execution filtering applies downstream of either path.

| Flag | Default | Description |
| - | - | - |
| `--actions_per_chunk` | `50` | Maximum actions requested from the policy per call. |
| `--plan_config.request_interval` | `5` | Custom policies: dispatch this many rows between requests. |
| `--plan_config.max_adoption_offset_steps` | `6` | Custom policies: maximum reply age in control steps from the saved request origin. Must be inside the action horizon. |
| `--plan_config.late_policy` | `blocking_refresh` | Custom policies: hold and request reset plans on expiry, or select `abort` to fail the session. |
| `--chunk_size_threshold` | `0.9` | LeRobot policies: request the next chunk when the queue drops below this fraction. |
| `--aggregate_fn` | `temporal_ensemble` | LeRobot policies: combine overlapping chunks using `temporal_ensemble`, `weighted_average`, `latest_only`, `average`, or `conservative`. |
| `--temporal_ensemble_coeff` | `0.01` | LeRobot policies: weight decay for `temporal_ensemble`. |
| `--ensemble_blend_s` | `0.2` | LeRobot policies: fade (s) at the end of each chunk so coverage expiry does not step the ensemble. `0` disables. |
| `--align_fade_s` | `1.0` | LeRobot policies: fade (s) over which each new chunk's offset from current motion is blended in. `0` disables. |
| `--exec_max_vel` | `6.283` | Joint speed limit (rad/s) on commands sent to the arms. `0` disables. |
| `--exec_max_accel` | `21.99` | Joint acceleration limit (rad/s²). `0` disables. |

## Recording

Optional — the rollout is recorded to a dataset when `--repo_id` is set.

| Flag | Default | Description |
| - | - | - |
| `--repo_id` | `null` | Dataset id, `<user>/<dataset>`. Set it to record the rollout. |
| `--vcodec` | per platform | Dataset video codec: `h264` on Jetson, `auto` elsewhere. |
| `--root` | `null` | Local dataset directory. Default is the LeRobot cache, `~/.cache/huggingface/lerobot`. |
| `--push_to_hub` | `false` | Upload the dataset to the HuggingFace Hub when done. |
| `--rerun_ip` | `null` (off) | Stream live visualisation to a [Rerun](https://rerun.io) viewer at this address. |
| `--rerun_port` | `9876` | Rerun viewer port. |
| `--log_level` | `INFO` | `DEBUG`, `INFO`, `WARNING`, or `ERROR`. |
| `--config_path` | — | Load a JSON/YAML config file. Flags override it. |
| `--settings_path` / `--no_settings` | `~/.almond/settings.json` | Read another [shared settings file](/cli/configuration#the-shared-settings-file) / ignore it. Applies beneath `--config_path` and the flags. |

## Contact safety

A contact stop trips when a joint's torque is higher than the gravity model expects — the arm hit something. The arms then go limp instead of pushing through.

| Flag | Default | Description |
| - | - | - |
| `--reset_torque_threshold` | `6.0` | Contact stop during return-to-rest (Nm). `0` disables. |
| `--policy_torque_threshold` | `0` (off) | Contact stop while the policy drives (Nm). A trip aborts the episode. The control panel arms it at `16`. |
| `--reset_gravity_comp_kd` | `0.25` | Damping (Nm·s/rad) of the limp hold. |

<h2 id="robot-config">
  Robot
</h2>

CAN buses, cameras, and what goes into observations.

| Flag | Default | Description |
| - | - | - |
| <F>--{prefix_1}left\_channel</F> | `can_alm_axol_l` | CAN interface for the left arm. |
| <F>--{prefix_1}right\_channel</F> | `can_alm_axol_r` | CAN interface for the right arm. |
| <F>--{prefix_1}cameras</F> | 3 unassigned slots | Camera slots `overhead`, `left_arm`, `right_arm` — fields below. Pass inline: `--robot_config.cameras "{overhead: {serial: 41234567}}"`. Slots left at serial `0` are dropped. |
| <F>--{prefix_1}video\_backend</F> | `auto` | Camera backend: `gst` (GPU pipeline, low latency), `sdk` (ZED SDK), or `auto` (prefers `gst`). Must match between collection and inference. |
| <F>--{prefix_1}observe\_torques</F> | `false` | Include joint torques in observations. Must match between collection and inference. |
| <F>--{prefix_1}observe\_cartesian</F> | `false` | Use end-effector poses instead of joint angles for observations and actions. Must match between collection and inference. |
| <F>--{prefix_1}id</F> | `null` | LeRobot bookkeeping, unused by Axol. |
| <F>--{prefix_1}calibration\_dir</F> | `null` | LeRobot bookkeeping, unused by Axol. |

<h3 id="cameras">
  Cameras
</h3>

One ZED camera per slot: `overhead`, `left_arm`, `right_arm`. Flags are shown for `overhead`; swap in the other slot names. Stereo ZED X cameras are detected from their serial. The [control panel's Cameras tab](/guides/control-panel#cameras) edits all of this visually.

| Flag | Default | Description |
| - | - | - |
| <F>--{prefix_2}overhead.serial</F> | `0` (unassigned) | Serial number of the ZED camera. `0` leaves the slot unused. |
| <F>--{prefix_2}overhead.fps</F> | `60` | Capture rate. Raised automatically if the recording `fps` is higher. |
| <F>--{prefix_2}overhead.width</F> | `960` | Capture width. With `height`, must be a ZED resolution: `SVGA` 960×600, `HD1080` 1920×1080, `HD1200` 1920×1200. |
| <F>--{prefix_2}overhead.height</F> | `600` | Capture height. |
| <F>--{prefix_2}overhead.color\_mode</F> | `RGB` | `RGB` or `BGR`. |
| <F>--{prefix_2}overhead.warmup\_s</F> | `1` | Seconds of frames discarded on connect. |
| <F>--{prefix_2}overhead.stereo</F> | `false` (auto) | Force stereo on or off instead of detecting it from the serial. |
| <F>--{prefix_2}overhead.eyes</F> | `both` | Which eye(s) to **record**: `both` (`overhead_left` + `overhead_right`), `left`, or `right`. |
| <F>--{prefix_2}overhead.stream\_eyes</F> | `null` | Which eye(s) to **stream** to the headset. `null` follows `eyes`. Not used by run-policy. |
| <F>--{prefix_2}overhead.record</F> | `true` | Write this camera to the dataset. |
| <F>--{prefix_2}overhead.stream</F> | `true` | Show this camera in the headset. Not used by run-policy. |

<h3 id="robot">
  Arm gains
</h3>

Arm stiffness, gripper limits, and per-joint gains.

| Flag | Default | Description | | |
| - | - | - | - | - |
| <F>--{prefix_0}left\_stiffness</F> | `1.0` | How stiff the left arm is, from `0` (soft, hand-guidable) to `1` (the tuned gains). Lower values only add compliance — the arm tracks a little less tightly but yields more to obstacles. One number applies to every joint; a 7-element list sets each joint in the order `shoulder_1, shoulder_2, shoulder_3, elbow, wrist_1, wrist_2, wrist_3`. | | |
| <F>--{prefix_0}right\_stiffness</F> | `1.0` | Same, for the right arm. | | |
| <F>--{prefix_0}has\_gripper</F> | `true` | Set `false` on the gripperless model. The gripper motors are never enabled and datasets record 7 channels per arm instead of 8. | | |
| <F>--{prefix_0}gripper</F> | `parallel` | Which gripper is fitted: `parallel` (the stock two-finger gripper) or `parcel` (the hinged-plate parcel gripper). Sets which way each jaw closes for the bring-up calibration, which refuses a gripper whose measured stroke is the other type's. Box mode exists only with `parcel`. | | |
| <F>--{prefix_0}max\_step\_rad</F> | `0.5` | Largest joint move (rad) allowed between two consecutive commands. Bigger moves are dropped with a warning. `inf` disables. | | |
| <F>--{prefix_0}controller</F> | `impedance` | Which control law the realtime core runs the arms on. `impedance` is the production MIT frame at **240 Hz**: host gravity / friction / inertia feed-forward and host damping around the firmware PD, compliant. `position` hands every joint to its motor's own position loop at **400 Hz** — 0xA4 on the MyActuator joints, position-velocity on the Damiao wrists, the `firmware.*` gains below (written to ROM at enable) — where the position loop's target staircase (audible at 200 Hz) is gone. It is stiff: no compliance, no host feed-forward, and the contact watchdog is blind on the MyActuator joints (their torque telemetry reads NaN). At 400 Hz the bus cannot carry every motor every tick, so the core thins its schedule: every MyActuator command still goes out every tick, the two wrists are commanded on alternate ticks (200 Hz each), and one 0.01° a4 position read per tick rotates through the a4 joints and the gripper (\~67 Hz each; between reads a joint's position is carried on the speed its command echo reports every tick). The choice is baked into each joint's `wire_mode` when the robot is constructed; under `impedance` a per-joint `wire_mode` set explicitly is kept. Impedance joints are commanded at **240 Hz only**: an arm that mixes them with firmware-loop joints (a per-joint `wire_mode a4` under `impedance`, or `tune.motion --a4`) runs its core at **480 Hz**, the firmware-loop joints every tick and each impedance joint on alternate ticks — exactly 240 Hz, with its host feed-forward and damping stepped at that rate — and any other rate with an impedance joint on the arm is refused. Recorded-motion replays take `--controller` ([`tune.motion`](/cli/tune-motion)); teleop takes `--axol.controller position`. | | |
| <F>--{prefix_0}impedance\_hz</F> | `240.0` | Command rate of the MyActuator impedance joints: `240.0` (the verified rate) or `480.0` — every tick of a **480 Hz** core loop, their host feed-forward, damping and tracker stepped at 480, while the Damiao wrists stay at 240 Hz on alternate ticks (the gripper and any a4 position reads take one tick in eight). An experiment, never the default: the impedance gains, host damping and feed-forward were tuned at 240. With it set, any arm with an impedance joint runs its core at 480 Hz only. `tune.motion --impedance-hz 480` sets it for a run. | | |
| <F>--{prefix_0}left.gripper.torque\_limit</F> | `0.5` | Grip force — peak gripper torque (Nm). | | |
| <F>--{prefix_0}left.gripper.max\_speed</F> | `10.0` | Maximum gripper speed (rad/s). | | |
| <F>--{prefix_0}left.elbow\.kp</F> | per joint | Position stiffness of one joint, in `[0, 500]`. This is the stiffness-`1` value. | | |
| <F>--{prefix_0}left.elbow\.kd</F> | per joint | Velocity damping in the motor firmware, in `[0, 5]`. | | |
| <F>--{prefix_0}left.elbow\.kd\_host</F> | per joint | Extra damping (Nm·s/rad) applied by the host, for the slow shoulder resonance the firmware can't damp. Scaled down automatically in poses where the joint carries less inertia. `0` on joints whose firmware damping is enough. | | |
| <F>--{prefix_0}left.elbow\.kd\_host\_hz</F> | `null` | Centre frequency (Hz) of the band the host damping acts in. `null` tracks the arm's pose automatically. | | |
| <F>--{prefix_0}left.elbow\.kd\_host\_q</F> | `null` | Width of that band (bandwidth = centre ÷ q). `null` uses `0.8`. | | |
| <F>--{prefix_0}left.elbow\.j\_eff</F> | per joint | Effective inertia (kg·m²) used for acceleration feed-forward. | | |
| <F>--{prefix_0}left.elbow\.stiction\_gain</F> | `0.0` | Error-sign Coulomb compensation as a fraction of `friction.fc`: pushes up to `gain × fc` toward the target while the joint is stuck and the velocity feed-forward is off, fading out once the joint slides. The lever for slow-motion stick-slip stairs on the high-ratio X8 shoulders; keep it below the breakaway/`fc` ratio [`tune.breakaway`](/cli/tune-breakaway) measures. `0` is the production law. | | |
| <F>--{prefix_0}left.elbow\.stiction\_load\_gain</F> | `0.0` | Load-proportional part of that push, in Nm per Nm of the joint's gravity feed-forward (peak push = \`stiction\_gain × fc + stiction\_load\_gain × | gravity | \`). Gear friction follows the transmitted torque — right shoulder\_1 breaks away at 0.66 Nm at rest but at 2–3 Nm with the arm extended — so a constant push either hunts at rest or does nothing under load. |
| <F>--{prefix_0}left.elbow\.stiction\_err\_deg</F> | `0.1` | Position error (degrees) at which that term saturates. The push fades on *measured* velocity (gone above \~0.05 rad/s), so it never drives the slip. | | |
| <F>--{prefix_0}left.elbow\.dither\_nm</F> | `0.0` | Peak amplitude (Nm) of a sinusoidal torque dither on the feed-forward, `0` off. Keeps a geared joint's meshes sliding so its velocity-weakening friction (the X8-P20 shoulders: 2.1 Nm sliding at 0.05 rad/s, 0.7 Nm at 0.2 rad/s) cannot re-stick between cycles. Start at 1–2 Nm on a shoulder; it is audible. | | |
| <F>--{prefix_0}left.elbow\.dither\_hz</F> | `60.0` | Dither frequency: above the arm's structural modes (\~35 Hz), below the core's 120 Hz Nyquist. | | |
| <F>--{prefix_0}left.elbow\.stribeck\_gain</F> | `0.0` | Friction cancellation on **measured** velocity, as a fraction of the measured static-minus-sliding excess: \`gain × (stribeck\_dfs + stribeck\_load\_gain × | gravity | ) × exp(−(v/stribeck\_vs)²) × tanh(v/0.02)\`. The one feed-forward that acts on the velocity-weakening slope behind the X8 shoulders' 2 Hz stick-slip (a command-driven term cannot). Zero at rest. Sweep 0.5 → 0.9 and stop when the arm's 3 Hz mode grows. |
| <F>--{prefix_0}left.elbow\.stribeck\_dfs</F> | `0.3` | Static-minus-sliding friction excess (Nm) at zero gravity load. | | |
| <F>--{prefix_0}left.elbow\.stribeck\_load\_gain</F> | `0.1` | Growth of that excess per Nm of gravity feed-forward (right shoulder\_1: \~1.5 Nm under 12 Nm). | | |
| <F>--{prefix_0}left.elbow\.stribeck\_vs</F> | `0.1` | Speed (rad/s) at which the excess has fallen to 1/e. | | |
| <F>--{prefix_0}left.elbow\.stribeck\_pole</F> | `20.0` | Low-pass pole (rad/s) of the measured velocity the term follows. 20 is smooth but \~40° behind a 2.6 Hz surge; 40–80 follows it at the cost of encoder-step noise in the torque. | | |
| <F>--{prefix_0}left.elbow\.wire\_mode</F> | `mit` | Frame the realtime core commands the joint with while tracking. `mit` is the impedance frame (production). `a4` hands a **MyActuator** joint to the firmware's own position loop (0xA4, speed-capped at the tracker limit) with a paired 0x92 read for 0.01° position — the candidate for creeping through X8-P20 stick-slip. The joint is on 0xA4 from its first frame, holds included: the X6-P20's 2025-07 firmware ignores 0xA4 after an MIT frame until the motor is reset (the 2026 X8-P20 firmware switches freely). `pv` is the same for a **Damiao** wrist: its position-velocity mode (0x100+ID; the core sets control-mode register 2 at bring-up and switches it back to MIT for limp / gravity comp), gains in the KP\_APR/KP\_ASR registers, ramps in ACC/DEC. Costs of either: no compliance, no host feed-forward, and on a4 NaN torque telemetry (the contact watchdog is blind on that joint; a pv wrist keeps its torque channel). The gripper, gravity comp and the limp fallback always use MIT — so on the older firmware an a4 joint that has been hand-guided needs a re-enable before it tracks again. An a4 joint's stored planner acceleration must be 0 or 60000 (see [`tune.a4`](/cli/tune-a4)); loop gains come from `firmware.*` below. `controller` `position` (above) sets every joint's position wire mode at once; this field is the per-joint override. | | |
| <F>--{prefix_0}left.elbow\.mass</F> | per joint | Mass (kg) of the link, used for gravity compensation. Tune if an arm sags or pushes back in [gravity comp](/operations/gravity-comp). | | |
| <F>--{prefix_0}left.elbow\.com</F> | per joint | Centre of mass `[x, y, z]` (m) of the link. | | |
| <F>--{prefix_0}left.elbow\.friction.fc</F> | per motor | Coulomb friction (Nm). Measured per motor with [`tune.friction`](/cli/tune-friction). | | |
| <F>--{prefix_0}left.elbow\.friction.k</F> | per motor | Friction curve sharpness. | | |
| <F>--{prefix_0}left.elbow\.friction.fv</F> | per motor | Viscous friction (Nm·s/rad). | | |
| <F>--{prefix_0}left.elbow\.friction.fo</F> | per motor | Constant torque offset (Nm). | | |
| <F>--{prefix_0}left.elbow\.friction.fl</F> | `0.0` | Load-proportional Coulomb friction, Nm per Nm of gravity feed-forward: the effective `fc` is \`fc + fl × | gravity | `. Planetary gear friction grows with the torque it carries (right shoulder_1: ~0.6 Nm sliding at rest, ~1.5 Nm under 12 Nm), so a constant `fc`over-compensates at rest and under-compensates at reach.`0\` keeps the constant model. |
| <F>--{prefix_0}left.elbow\.firmware.position\_kp</F> | `null` | Firmware position-loop proportional gain (MyActuator 0x30 index 0x07, Damiao register KP\_APR), written to the motor's ROM at enable when set (`null` leaves the motor's stored value). Every joint defaults to `null` on every `firmware.*` field: the arms run impedance, where these loops are inert, so bring-up leaves the motors' own loops as shipped. These five gains are the motor's own position PI → speed PI controller and only act under `wire_mode` `a4`; identify them with [`tune.a4`](/cli/tune-a4). Stock 0.008 on the X8-P20 shoulders stick-slips at creep speed; the stairs are gone by 0.2 and, with the 400 Hz stream the core runs, tracking keeps improving to 1.0 (0.17° / 12 ms at 12 deg/s) with 1.4 still clean. | | |
| <F>--{prefix_0}left.elbow\.firmware.position\_ki</F> | `null` | Firmware position-loop integral gain. Leave at the stock `0`: on top of the speed integrator it hunts around the target. | | |
| <F>--{prefix_0}left.elbow\.firmware.position\_kd</F> | `null` | Firmware position-loop derivative gain (protocol V4.2+). The motor stores it, but the 0xA4 loop on the X8-P20 measured inert to it. | | |
| <F>--{prefix_0}left.elbow\.firmware.speed\_kp</F> | `null` | Firmware speed-loop proportional gain. Not a damper in practice — it did nothing for the loop's 5 Hz reversal mode — but the knob on the speed loop's own resonance (\~100 Hz on the X8-P20, \~135 Hz on the X6-P20): the current tone grows with it until the loop goes unstable (0.2 on the shoulder, 0.1 at position\_kp 1.5 on the elbow). Lower is quieter at no tracking cost. | | |
| <F>--{prefix_0}left.elbow\.firmware.profile\_acc</F> | `null` | **Damiao wrists only:** the position-velocity profiler's ramp (rad/s²), written to ACC and, negated, DEC at enable. Every streamed target is approached along a trapezoid under this acceleration, so it caps how fast the wrist follows: the stock `2` (115 deg/s²) cannot keep up with the 200 Hz stream — the loop hunts at \~5 Hz — and would take half a second to reach teleop speed. `50` sits above the core tracker's 33 rad/s² limit while still rounding each 5 ms step; `50` and `200` scored alike in `tune.a4`. | | |
| <F>--{prefix_0}left.elbow\.firmware.speed\_ki</F> | `null` | Firmware speed-loop integral gain. Lower than the stock `1e-4` is smoother on a geared joint: the integrator winds up while the joint is stuck and dumps it at release. | | |
| <F>--{prefix_0}left.elbow\.firmware.planner\_accel</F> | `null` | **MyActuator only:** the 0xA4 position planner's acceleration and deceleration (dps/s), written at enable to a joint on `wire_mode` `a4`. Only `0` (direct PI tracking of each target) and `60000` (the planner finishing each step within the tick) follow a stream; anything between re-plans every target and the joint barely moves, and is refused. On the X6-P20 elbow `tune.a4` tracked a 3 deg/s triangle to 0.02° RMS with 4 ms lag at `60000`, against 0.23° / 74 ms direct. `60000` wants `cap_track`. | | |
| <F>--{prefix_0}left.elbow\.firmware.cap\_track</F> | `null` | **0xA4 joints with the planner on:** each tick's speed cap as this multiple of the commanded speed (floor 1 dps, ceiling the tracker limit) instead of the fixed tracker limit — at a fixed cap the planner bursts through each step and idles the rest of the tick (4× the current spread on the elbow); `1.1`–`1.2` moved it continuously. Leave unset under direct tracking, where the cap is a hard limit on the PI output. Carried to the realtime core, not written to the motor. | | |
| <F>--{prefix_0}left.elbow\.firmware.tf\_rated\_current\_a</F> | `null` | **MyActuator a4 joints:** the motor's rated current (A — datasheet, or [`tune.a4 --tf-probe`](/cli/tune-a4)). Set, the realtime core sends the joint's position command as **0x73** (protocol V4.4 position control with torque feed-forward) carrying gravity and inertia, scaled by the joint's torque constant into the firmware's int8 1%-of-rated-current unit and faded in over a second. Only on firmware that implements 0x73 (VersionDate 2026042402 or later — the X8-P20 shoulders, not the X6-P20 elbow's 2025070202; elsewhere the joint stays on plain 0xA4 and the core logs why), and only in direct tracking (`planner_accel` `0`). Not written to the motor. | | |
| <F>--{prefix_0}left.elbow\.impedance\_hz</F> | `null` | This joint's impedance command rate: `480.0` commands it every tick of a 480 Hz core loop (its host feed-forward, damping and tracker stepped at 480), `240.0` keeps it on the verified 240 Hz lane, `null` follows `impedance_hz` above. Any joint at 480 runs the core at 480 Hz, the other impedance joints on alternate ticks. `tune.motion --fast-impedance right.shoulder_1` sets it for a run. | | |

Per-arm and per-joint flags are shown for `left` / `elbow`. Swap in `right`, or any joint: `shoulder_1`, `shoulder_2`, `shoulder_3`, `elbow`, `wrist_1`, `wrist_2`, `wrist_3`.

<Note>
  A robot calibrated with [`tune.pid --save`](/cli/tune-pid), [`tune.friction --save`](/cli/tune-friction), or [`tune.gravity --save`](/cli/tune-gravity) uses its own measured values (from `~/.almond/calibration.json`) instead of the defaults below. Flags you pass still override both.
</Note>

<Accordion title="Per-joint defaults">
  Both arms share these. `kp`/`kd` are the stiffness-`1` values (the default); the **soft** columns are what stiffness `0` blends toward. `shoulder_1` pins its host damper at `kd_host_hz` 2.6 / `kd_host_q` 1.0 and the `elbow` at 1.6 / 1.5 (every other joint leaves both `null`: a pose-tracked centre, the shared Q); `shoulder_1`, `shoulder_2` and the `elbow` run the Stribeck cancellation (`stribeck_gain` 0.8, `stribeck_pole` 40). Those values come from slow-motion tuning on the jelly robot's right arm (2026-09, and shoulder\_1's damper and shoulder\_2 on 2026-10-06 — shoulder\_2's creep ripple −57% at 3°/s; with shoulder\_1's damper change slow\_osc's 1–3 Hz wrist sway about −10% over three interleaved rounds).

  | Joint | `kp` | `kd` | `kd_host` | `j_eff` | `mass` (kg) | soft `kp` | soft `kd` |
  | - | - | - | - | - | - | - | - |
  | `shoulder_1` | 450 | 5.0 | 110 | 1.27 | 1.80 | 40 | 1.4 |
  | `shoulder_2` | 500 | 5.0 | 70 | 1.1 | 1.00 | 50 | 1.57 |
  | `shoulder_3` | 180 | 5.0 | 0 | 0.25 | 3.75 | 45 | 1.0 |
  | `elbow` | 200 | 5.0 | 10 | 0.6 | 0.25 | 40 | 2.22 |
  | `wrist_1` | 180 | 1.7 | 0 | 0 | 0.25 | 30 | 0.69 |
  | `wrist_2` | 130 | 2.25 | 0 | 0 | 0.65 | 25 | 1.5 |
  | `wrist_3` | 130 | 2.0 | 0 | 0 | 0.75 | 25 | 0.9 |
</Accordion>

<Accordion title="Per-motor friction defaults">
  Fallback friction for each motor, `fc + fl·|g|` sliding (Nm, `k` 100 — the realtime core's cap) with offset `fo`, and the low-speed Stribeck excess `dfs + ls·|g|` over `vs` (rad/s). Measured on the jelly robot with [`tune.friction --profile slow`](/cli/tune-friction) (2026-09); only `shoulder_1` and `elbow` cancel the excess by default (`stribeck_gain` 0.8). Friction is per motor — calibrate each robot.

  | Joint | Left: `fc`, `fl`, `fo` / `dfs`, `ls`, `vs` | Right: `fc`, `fl`, `fo` / `dfs`, `ls`, `vs` |
  | - | - | - |
  | `shoulder_1` | `0.5903, 0.0491, 0.0733` / `0.9814, 0.0485, 0.1` | `0.4022, 0.0544, −0.3074` / `0.7326, 0.0335, 0.1` |
  | `shoulder_2` | `0.4702, 0.0401, −0.6108` / `0.6142, 0.0389, 0.0989` | `1.3210, 0.0768, 1.0389` / `1.1065, 0.066, 0.0937` |
  | `shoulder_3` | `0.3664, 0.0786, −0.0018` / `0.2214, 0.1685, 0.1` | `0.3674, 0.0550, 0.1217` / `0.4078, 0.1479, 0.1` |
  | `elbow` | `0.0382, 0.0858, −0.0113` / `0.2347, 0.1301, 0.1` | `0.2704, 0.0448, 0.0197` / `0.1558, 0.1405, 0.0754` |
  | `wrist_1` | `0.4791, 0, 0.0686` / `0.5188, 0, 0.1` | `0.4998, 0, 0.1562` / `0.3968, 0, 0.0852` |
  | `wrist_2` | `0.1228, 0, −0.0286` / `0.0828, 0, 0.1` | `0.1923, 0, −0.0657` / `0.1498, 0, 0.1` |
  | `wrist_3` | `0.2765, 0, 0.0267` / `0.2378, 0, 0.1` | `0.2790, 0, −0.0392` / `0.2439, 0, 0.0916` |
</Accordion>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.