> ## 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.

# DAgger Collection Config Parameters

> Every setting for axol collect-dagger — what it does and its default.

export const prefix_6 = "teleop_config.jelly."

export const prefix_5 = "teleop_config.vr_server_config."

export const prefix_4 = "teleop_config.kinematics_config."

export const prefix_3 = "teleop_config.vr_teleop_config."

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 collect-dagger`. 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) the same sections appear without this command's wrapper prefixes — `robot_config.axol_config.*` is the file's `axol`, `teleop_config.vr_teleop_config.*` is `teleop`, `teleop_config.kinematics_config.*` is `kinematics`, the other `robot_config.*` fields are `lerobot`, and the recording fields live under `recording` — exactly as for [data collection](/operations/data-collection-config).

```bash theme={null}
axol collect-dagger --policy_path myorg/pick-act --policy_type act \
  --repo_id myorg/pick-dagger --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 when the policy's data was collected**. A policy only works with the dynamics it was trained on.
</Warning>

## Policy

| Flag | Default | Description |
| - | - | - |
| `--policy_path` | required for local policies | Policy checkpoint — a HuggingFace Hub repo id or local path. Unused for `custom`; model selection stays at the endpoint. |
| `--policy_type` | required | `act`, `smolvla`, `diffusion`, `tdmpc`, `vqbet`, `pi0`, `pi05`, `groot`, or `custom` for the remote custom policy interface. |
| `--device` | `cuda` | `cuda`, `cpu`, or `mps`; local policies only. |
| `--episode_time_s` | `600` | Maximum episode length (s), including interventions. `0` removes the cap. |
| `--policy_max_vel` | `6.283` | Local policies: speed limit (rad/s) on joint actions. `0` disables. |
| `--policy_max_accel` | `21.99` | Local policies: acceleration limit (rad/s²) on joint actions. |
| `--subtasks` | `null` | Local policies: ordered sub-instructions, e.g. `["reach","grasp","lift"]`. Typing `1..N` switches instructions mid-episode. Rejected for custom policies, which manage instructions at the endpoint. |

## Custom policy interface

`--policy_type custom` uses the [custom policy interface](/api/policy-plan).
Set these fields on the DAgger command itself; its shared settings inherit
from `collect-data`, not the run-policy inference category.

| Flag | Default | Description |
| - | - | - |
| `--server_host` | `127.0.0.1` | Address of the existing policy endpoint. |
| `--server_port` | `8765` | Policy endpoint port. |
| `--actions_per_chunk` | `30` | Maximum returned plan horizon. |
| `--plan_config.request_interval` | `5` | Dispatch this many rows between policy requests. |
| `--plan_config.max_adoption_offset_steps` | `6` | Maximum reply age in control steps; must be inside the action horizon. |
| `--plan_config.late_policy` | `blocking_refresh` | Hold and request reset plans on expiry, or choose `abort`. |
| `--hold_to_intervene` | `false` | Must be `true` for custom policies: held grips take control; releasing both requests policy handback. |
| `--shadow_inference` | `true` | Continue prediction during operator control, discarding replies. |
| `--handover_duration_s` | `0.2667` | Joint-space handback ramp after IK, followed by limited convergence. |
| `--record_joint_actions` | `false` | Record resolved joint commands independently of the wire action layout. |
| `--start_from_current_pose` | `false` | Skip startup home and the home after idle teleop. |
| `--home_on_start` | `false` | Restore startup home when current-pose episode starts are enabled. |

## Recording

| Flag | Default | Description |
| - | - | - |
| `--repo_id` | required | Dataset id, `<user>/<dataset>`. |
| `--task` | required | Task description stored with every frame. Sent to local policies; custom policies choose instructions at the endpoint. |
| `--fps` | `30` | Recording rate and the policy state's control rate; must match the policy's training fps. Cameras capture at 60 and are decimated to this before encoding. |
| `--teleop_hz` | `120` | Motor command rate (Hz) while you intervene, independent of `fps`. |
| `--policy_fps` | `20` | Rate the relay feeds the policy's observation frames at, capped at the capture rate. Independent of `fps`: it only thins the RGB ring the control process reads inference images from, so the relay fits beside the headset and panel streams. Between ring frames a tick re-uses the last frame set; the loop, the recorded state rows and the action stream stay at `fps`. |
| `--dataset_resolution` | `SVGA` | Resolution the recorded video is downscaled to, and the resolution the policy sees: `SVGA`, `HD1080`, or `HD1200`. |
| `--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. |

<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` confines its pose-tracked host damper with `kd_host_q` 3 (every other joint leaves `kd_host_hz` / `kd_host_q` `null`: a pose-tracked centre, the shared Q), and no joint runs the Stribeck cancellation.

  | Joint | `kp` | `kd` | `kd_host` | `j_eff` | `mass` (kg) | soft `kp` | soft `kd` |
  | - | - | - | - | - | - | - | - |
  | `shoulder_1` | 250 | 3.5 | 40 | 1.27 | 1.80 | 40 | 1.4 |
  | `shoulder_2` | 250 | 3.5 | 35 | 1.1 | 1.00 | 50 | 1.57 |
  | `shoulder_3` | 180 | 5.0 | 0 | 0.25 | 3.75 | 45 | 1.0 |
  | `elbow` | 130 | 5.0 | 0 | 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 |

  **Slow-motion gains.** A joint whose own slow-profile friction fit is in the calibration — written by [`tune.factory`](/cli/tune-factory) (its default profile) or [`tune.friction --profile slow --save`](/cli/tune-friction) — runs the stiffer set instead. They were tuned against calibrated friction and oscillate on a robot still running the fallback friction below, so an uncalibrated joint keeps the table above.

  | Joint | `kp` | `kd` | `kd_host` | `kd_host_hz` / `kd_host_q` | `stribeck_gain` / `stribeck_pole` |
  | - | - | - | - | - | - |
  | `shoulder_1` | 450 | 5.0 | 110 | 2.6 / 1.0 | 0.8 / 40 |
  | `shoulder_2` | 500 | 5.0 | 70 | `null` / `null` | 0.8 / 40 |
  | `elbow` | 200 | 5.0 | 10 | 1.6 / 1.5 | 0.8 / 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). Gains saved in the calibration entry itself (`tune.pid --save`, the fit's `stribeck_gain`) still win over them.
</Accordion>

<Accordion title="Per-motor friction defaults">
  Fallback values of `fc`, `k`, `fv`, `fo` for each motor, measured on the reference robot with [`tune.friction`](/cli/tune-friction). A joint's slow-profile friction (`fl` and the Stribeck excess) comes only from its own calibration: there is no fallback for it, so calibrate each robot.

  | Joint | Left arm | Right arm |
  | - | - | - |
  | `shoulder_1` | `0.9091, 799.59, 0, 0.37` | `1.2972, 742.11, 0, −0.1557` |
  | `shoulder_2` | `1.1378, 756.76, 0, −0.3969` | `1.3950, 768.06, 0, 0.2082` |
  | `shoulder_3` | `0.3599, 835.51, 0, −0.0133` | `0.4377, 107.94, 0.0853, −0.0147` |
  | `elbow` | `0.6023, 855.95, 0, −0.072` | `0.6066, 784.78, 0, 0.049` |
  | `wrist_1` | `0.3765, 88.91, 0.0298, −0.0115` | `0.5245, 98.58, 0.3062, −0.0097` |
  | `wrist_2` | `0.1521, 780.31, 0, −0.0152` | `0.1092, 899.67, 0, 0.0021` |
  | `wrist_3` | `0.0714, 927.26, 0, 0.0042` | `0.1172, 204.46, 0, 0.0042` |
</Accordion>

<h2 id="teleop">
  Teleop session
</h2>

How VR controller motion becomes arm motion while you intervene.

| Flag | Default | Description |
| - | - | - |
| <F>--{prefix_3}rest\_pose\_left</F> | `[−0.157, 0, 0, 0.314, 0, 0, −0.157]` | Left arm rest pose (rad, 7 joints). The arms start here and return here on reset. Edit it visually in the control panel's **Rest pose** tab. |
| <F>--{prefix_3}rest\_pose\_right</F> | `[0.157, 0, 0, −0.314, 0, 0, 0.157]` | Right arm rest pose. |
| <F>--{prefix_3}frequency</F> | `120` | Motor command rate (Hz). Data collection sets this from `teleop_hz`. |
| <F>--{prefix_3}ik\_frequency</F> | `120` | How often VR poses are sent to the IK solver (Hz). The control loop interpolates between solutions. |
| <F>--{prefix_3}position\_multiplier</F> | `1.0` | Scales hand movement. `2` moves the arm twice as far as your hand. Ignored in `absolute_mode`. |
| <F>--{prefix_3}rotation\_multiplier</F> | `1.0` | Scales hand rotation the same way. Ignored in `absolute_mode`. |
| <F>--{prefix_3}hold\_to\_engage</F> | `false` | `false`: click both grips to engage, then click a grip to freeze or unfreeze that arm. `true`: each arm tracks only while its grip is held. |
| <F>--{prefix_3}disengage\_timeout</F> | `0.5` | Seconds without a VR frame before tracking disengages and the arms hold position. `0` disables. |
| <F>--{prefix_3}record</F> | `null` | Record every stage of the teleop pipeline to `~/.almond/recordings/<name>_*.npz` for [`motion.build`](/cli/motion-build) and [`diag.offline`](/cli/diag-offline). A bare name like `rec1` goes in that folder; a path is used as-is. |
| <F>--{prefix_3}reset\_speed</F> | `0.628` | Speed (rad/s) of the return-to-rest move. |
| <F>--{prefix_3}reset\_min\_duration</F> | `1.5` | Minimum duration (s) of a return-to-rest move. |
| <F>--{prefix_3}reset\_rest\_weight</F> | `50.0` | IK weight toward the rest pose when planning a return. |
| <F>--{prefix_3}reset\_limit\_weight</F> | `100.0` | IK weight against joint limits when planning a return. |
| <F>--{prefix_3}reset\_collision\_margin</F> | `0.025` | Minimum self-collision clearance (m) when planning a return. |
| <F>--{prefix_3}reset\_collision\_weight</F> | `100.0` | IK weight on self-collision when planning a return. |
| <F>--{prefix_3}reset\_max\_iterations</F> | `10` | IK iterations per return waypoint. |
| <F>--{prefix_3}reset\_torque\_threshold</F> | `6.0` | **Contact stop during return-to-rest** (Nm of unexpected torque). If the arm hits something, the move stops and the arms go limp; press reset to retry. `0` disables. Hardware only. |
| <F>--{prefix_3}teleop\_torque\_threshold</F> | `0` (off) | **Contact stop while tracking.** Same behaviour; a running episode is discarded. The control panel arms it at `16`. Hardware only. |
| <F>--{prefix_3}reset\_gravity\_comp\_kd</F> | `0.25` | Damping (Nm·s/rad) of the limp hold after a contact stop. |
| <F>--{prefix_3}engage\_max\_vel</F> | `0.628` | Joint speed cap (rad/s) right after engaging, so the first move is gentle. |
| <F>--{prefix_3}engage\_duration</F> | `1.0` | Seconds to ramp from `engage_max_vel` up to `teleop_max_vel`. |
| <F>--{prefix_3}teleop\_max\_vel</F> | `6.283` | Maximum joint speed (rad/s) while tracking. |
| <F>--{prefix_3}teleop\_max\_accel</F> | `21.99` | Maximum joint acceleration (rad/s²) while tracking. |
| <F>--{prefix_3}ik\_alpha</F> | `0.3` | Smoothing on the IK output, `(0, 1]`. `1` is no smoothing; lower is smoother but adds lag. See [Latency tuning](/operations/teleop#latency-tuning). |
| <F>--{prefix_3}pose\_cutoff</F> | `2.5` | Low-pass cutoff (Hz) on raw controller poses before IK. Lower rejects more hand tremor but adds lag. |
| <F>--{prefix_3}absolute\_mode</F> | `false` | Mantis mapping: track controller poses 1:1 in a fixed world frame instead of relative to where you engaged. Forced `true` by `--mantis`. |
| <F>--{prefix_3}base\_height</F> | `null` | In `absolute_mode`, fix the robot base this many metres above the VR floor instead of inferring it. Keeps datasets consistent across operators. |
| <F>--{prefix_3}tcp\_transform\_left</F> | `null` | *Mantis data collection.* Tracker → gripper transform `[x, y, z, qx, qy, qz, qw]` for the left rig. Normally read from the saved calibration; set to override. See [`tracker`](/cli/tracker#transform-file). |
| <F>--{prefix_3}tcp\_transform\_right</F> | `null` | *Mantis data collection.* Same for the right rig. |
| <F>--{prefix_3}tracker\_key</F> | `null` | *Mantis data collection, Quest.* Which saved calibration to use, e.g. `quest:oculus-touch-v3:grip`. Set from **Mantis settings** in the panel. |
| <F>--{prefix_3}quest\_controller\_profile</F> | `null` | *Mantis data collection, Quest.* Expected controller profile. Filled from `tracker_key`. |
| <F>--{prefix_3}quest\_pose\_space</F> | `null` | *Mantis data collection, Quest.* Expected pose datum, `grip` or `target-ray`. Filled from `tracker_key`. |
| <F>--{prefix_3}urdf\_viewer\_world\_aligned</F> | `null` | *Mantis data collection.* Whether the pose source shares the viewer headset's floor. `null` means `true` for Quest, `false` for Lighthouse/Ultimate. |

<Note>
  **Mantis mode** (`--mantis`) forces `absolute_mode: true`, `hold_to_engage: false`, `ik_alpha: 1.0`, `pose_cutoff: 5.0`, and removes the speed and acceleration limits. Your values for these keys are ignored in Mantis mode.
</Note>

<h2 id="kinematics">
  Kinematics
</h2>

The IK solver that turns hand targets into joint angles. A higher weight means the solver cares more about that term.

| Flag | Default | Description |
| - | - | - |
| <F>--{prefix_4}pos\_weight</F> | `50.0` | Weight on end-effector position error. |
| <F>--{prefix_4}ori\_weight</F> | `10.0` | Weight on end-effector orientation error. |
| <F>--{prefix_4}elbow\_weight</F> | `0` (off) | Weight on the headset's elbow-position hint. Off by default because the hint is unreliable on hardware. |
| <F>--{prefix_4}rest\_weight</F> | `15.0` | Damping toward the current pose. Higher is steadier, lower tracks faster. |
| <F>--{prefix_4}posture\_weight</F> | `5.0` | Pull toward the home posture, preventing slow drift. |
| <F>--{prefix_4}manipulability\_weight</F> | `0.05` | Keeps the arm away from singular poses. Forced `0` in Mantis mode. |
| <F>--{prefix_4}limit\_weight</F> | `75.0` | Penalty on joint-limit violations. |
| <F>--{prefix_4}self\_collision\_margin</F> | `0.025` | Distance (m) at which self-collision avoidance starts. Keep below the arm–torso clearance of normal poses. |
| <F>--{prefix_4}self\_collision\_weight</F> | `150.0` | Weight on self-collision avoidance. |
| <F>--{prefix_4}max\_iterations</F> | `8` | Solver iterations per IK call. |
| <F>--{prefix_4}cost\_tolerance</F> | `1e-4` | Convergence tolerance. Keep small. |
| <F>--{prefix_4}lambda\_initial</F> | `1e-2` | Initial Levenberg–Marquardt damping. |
| <F>--{prefix_4}lambda\_factor</F> | `10.0` | Damping multiplier after a rejected solver step. |
| <F>--{prefix_4}max\_joint\_delta</F> | `0.0346` | Largest joint change (rad) per IK call. Raise for faster tracking, lower for gentler motion. |
| <F>--{prefix_4}max\_reach</F> | `0.82` | Maximum shoulder-to-target distance (m). Targets beyond it are pulled in. |
| <F>--{prefix_4}reach\_soft\_start</F> | `0.78` | Distance (m) at which the reach limit starts to act. |
| <F>--{prefix_4}manip\_damping\_threshold</F> | `0.015` | Below this manipulability, extra damping kicks in (near full extension). |
| <F>--{prefix_4}manip\_damping\_boost</F> | `60.0` | Extra damping at zero manipulability. `0` disables. |
| <F>--{prefix_4}limit\_damping\_margin</F> | `0.12` | Distance (rad) from a joint limit inside which the joint is damped while moving toward it. |
| <F>--{prefix_4}elbow\_fade\_band</F> | `0.15` | Height (m) above the shoulder over which the elbow hint fades out. |

<Note>
  **Mantis mode** replaces any of these still at their default with `pos_weight 200`, `ori_weight 120`, `manipulability_weight 0`, `self_collision_margin 0.02`, `max_joint_delta 0.05`, `max_iterations 12`. A value you set explicitly still wins.
</Note>

<h2 id="vr-server">
  VR server
</h2>

The connection the headset streams poses over.

| Flag | Default | Description |
| - | - | - |
| <F>--{prefix_5}port</F> | `8000` | Port the headset connects to. [Quest-over-USB](/guides/quest-over-usb) uses the same port. |
| <F>--{prefix_5}certfile</F> | `null` | TLS certificate. `null` uses the auto-generated one in `~/.almond/vr/certs/`. |
| <F>--{prefix_5}keyfile</F> | `null` | TLS private key. `null` as above. |
| <F>--{prefix_5}interp\_enabled</F> | `true` | Smooth out jittery or batched pose arrivals. `false` uses the latest raw frame. |
| <F>--{prefix_5}interp\_min\_delay\_s</F> | `0` | Minimum playout delay (s). |
| <F>--{prefix_5}interp\_max\_delay\_s</F> | `0.15` | Maximum playout delay (s) spent absorbing network jitter. |
| <F>--{prefix_5}interp\_smooth\_window\_s</F> | `0.12` | Smoothing window (s). Adds about half of it as latency. `0` disables. See [Latency tuning](/operations/teleop#latency-tuning). |
| <F>--{prefix_5}interp\_outlier\_k</F> | `4.0` | Glitch-rejection threshold. Lower is more aggressive; `≤ 0` disables. |
| <F>--{prefix_5}interp\_max\_lag\_s</F> | `0.5` | Pose frames arriving more than this (s) later than normal are skipped, so the arms hold through a network stall instead of replaying it late. `≤ 0` disables. |
| <F>--{prefix_5}pose\_source\_kind</F> | `null` | Which pose source may drive: `webxr` (Quest), `tracker` (Lighthouse/Ultimate), or `null` for whichever connects first. Set automatically for Mantis. |
| <F>--{prefix_5}expected\_pose\_source\_id</F> | `null` | *Mantis data collection.* Exact pose-source ID accepted. Set automatically. |

<h2 id="jelly">
  Jelly
</h2>

The optional powered cart, driven from the headset thumbsticks between takes. Jelly motion is never recorded.

| Flag | Default | Description |
| - | - | - |
| <F>--{prefix_6}wheels</F> | `true` | Drive the wheels when their CAN interface is attached (thumbsticks drive the base). `false` leaves attached wheels cold. Jelly itself is never enabled by hand — it is inferred from the CAN devices present. |
| <F>--{prefix_6}channel</F> | `can_alm_axol_b` | CAN interface of the wheel motors. `null` means lift only. |
| <F>--{prefix_6}max\_speed</F> | `20.0` | Peak wheel speed (rad/s) at full stick. |
| <F>--{prefix_6}turn\_scale</F> | `1.0` | Rotation speed relative to driving speed, `[0, 1]`. |
| <F>--{prefix_6}slew</F> | `0.5` | Acceleration ramp. The default takes 2 s from rest to full speed. |
| <F>--{prefix_6}axis\_snap\_deg</F> | `15.0` | Stick directions within this many degrees of straight or sideways snap to exact. `0` disables. |
| <F>--{prefix_6}deadzone</F> | `0.15` | Thumbstick deadzone, as a fraction of full deflection. |
| <F>--{prefix_6}imu</F> | `false` | Use the board IMU to hold a straight heading. |
| <F>--{prefix_6}yaw\_hold\_gain</F> | `2.0` | Heading-hold strength. `0` disables. |
| <F>--{prefix_6}yaw\_hold\_max</F> | `0.3` | Maximum heading-hold correction. |
| <F>--{prefix_6}yaw\_log</F> | `false` | Log heading-hold data at 10 Hz, for diagnosing drift. |
| <F>--{prefix_6}hold\_kp</F> | `60.0` | Stiffness (Nm/rad) holding the wheels when parked. `0` leaves them idle. |
| <F>--{prefix_6}hold\_kd</F> | `1.5` | Damping (Nm·s/rad) of the parked hold. |
| <F>--{prefix_6}frequency</F> | `50.0` | Wheel command rate (Hz). |
| <F>--{prefix_6}command\_timeout</F> | `0.2` | Seconds without a stick command before Jelly stops. |
| <F>--{prefix_6}can\_timeout\_ms</F> | `200.0` | Motor-side loss-of-comms timeout (ms). Stops the wheels even if the host hangs. |
| <F>--{prefix_6}lift</F> | `true` | Run the lift when its CAN bus is attached (stick clicks). `false` leaves an attached lift cold. |
| <F>--{prefix_6}lift\_channel</F> | `null` (auto) | CAN interface of the lift controller. `null` picks the chest bus `can_alm_axol_c` when that interface exists, otherwise the wheel bus `can_alm_axol_b` the lift shares with the motors. |
| <F>--{prefix_6}lift\_speed</F> | `650` | Lift speed (encoder counts/s). |

<h2 id="teleop-config">
  Teleoperator
</h2>

| Flag | Default | Description |
| - | - | - |
| `--teleop_config.has_gripper` | `true` | Set automatically from `--robot_config.axol_config.has_gripper`. |
| `--teleop_config.vr_teleop_config.gripper` | `parallel` | Set automatically from `--robot_config.axol_config.gripper`; box mode exists only with `parcel`. |
| `--teleop_config.id` | `null` | LeRobot bookkeeping, unused by Axol. |
| `--teleop_config.calibration_dir` | `null` | LeRobot bookkeeping, unused by Axol. |


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