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

# teleop

> Launch a VR teleoperation session on the robot or simulator.

Launches a VR teleoperation session. When started, the hostname (`.local`) and local IP address are printed — enter either of these in the VR app at [axol.almond.bot](https://axol.almond.bot) to connect.

Runs on the **real robot by default**; pass `--sim` for the browser visualizer. The full robot config is exposed via draccus, so every gain, stiffness, and gripper limit is reachable from the CLI or a config file — see [Command configuration](/cli/configuration).

<Tip>
  For a guided, step-by-step walkthrough including the VR controller layout, see the [Teleoperation guide](/operations/teleop).
</Tip>

<Warning>
  **Before opening the VR app**, accept the self-signed HTTPS certificate in the VR browser by navigating to `https://<hostname>.local:8000` or `https://<local-ip>:8000` and proceeding past the security warning.
</Warning>

<Note>
  **Network tip:** If VR tracking feels jittery or packets arrive in bursts, configure the following on your router/access point:

  * **DTIM interval** → `1`
  * **Beacon interval** → `100` ms
  * **WMM APSD (U-APSD)** → disabled

  These settings prevent the AP from buffering packets between beacon intervals, which causes intermittent delivery delays that are especially noticeable for latency-sensitive VR traffic.
</Note>

| Flag                                     | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
| ---------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--sim`                                  | Use the software visualizer instead of the real robot. Bare `--sim` works (or `--sim true`). Omit to run on real hardware (the default).                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
| `--left_channel <ch\|null>`              | CAN channel for the left arm; `null` disables it. Default: `CAN_LEFT`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
| `--right_channel <ch\|null>`             | CAN channel for the right arm; `null` disables it. Default: `CAN_RIGHT`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
| `--axol.left_stiffness S`                | Compliance↔stiffness blend for the left arm in `[0, 1]`. Scalar (`0.8`) or a 7-element list (`[0.8,0.8,0.5,0.5,0.2,0.2,0.0]`, one per arm joint in `Joint` enum order). `0` = fully compliant; `1` = pre-tuning industrial gains; `0.5` (default) is the geometric mean. See [`AxolConfig.left_stiffness`](/api/robot).                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |
| `--axol.right_stiffness S`               | Same, for the right arm.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
| `--axol.left.gripper.torque_limit FLOAT` | Max torque (Nm) for the left gripper in POSITION\_FORCE mode (default: 0.5). Use `right` for the right gripper.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              |
| `--axol.<side>.<joint>.<gain> VALUE`     | Override any per-joint gain, e.g. `--axol.left.elbow.kp 60`. `<side>` is `left`/`right`; `<joint>` is `shoulder_1…wrist_3`; `<gain>` is `kp`/`kd`/etc.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
| `--teleop.position_multiplier FLOAT`     | Scale factor on hand **position** (not orientation): the end-effector target moves this many times as far as your hand. `1.0` (default) is 1:1; `2.0` moves the arm twice as far, helping cover the robot's full reach when the arm is longer than the operator's.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |
| `--teleop.rotation_multiplier FLOAT`     | Scale factor on hand **orientation** (not position): the end-effector rotates this many times as far as your wrist. `1.0` (default) is 1:1; `2.0` rotates the arm twice as far.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              |
| `--cameras DICT`                         | Map camera slots to the serial numbers of locally attached ZED cameras, e.g. `--cameras "{overhead: 41234567, left_arm: 41234568}"` (any of `overhead` / `left_arm` / `right_arm`). When set, teleop opens those cameras and relays them to the headset over WebRTC (overhead + both wrist views shown at once, each freely moved and resized). Stereo ZED X cameras are detected automatically from their serial: a stereo overhead is relayed as a single side-by-side track, `overhead_sbs` (both eyes packed into one stream), rendered per-lens on the headset, while a stereo wrist streams a single eye under its slot name (one feed, like a mono camera). Requires `pyzed` ([`zed.install`](/cli/zed-install)) and the GStreamer NVENC stack ([`gst.install`](/cli/gst-install)). Default: `{}` (no headset video). |
| `--camera_eyes DICT`                     | Override which eye(s) of a **stereo** slot are streamed to the headset, keyed by slot: `--camera_eyes "{overhead: both, left_arm: left}"` (`both` / `left` / `right`). Unset slots fall back to the default policy — overhead streams both eyes (packed side-by-side, rendered per-lens), wrists stream their left eye. Ignored for mono slots. Default: `{}`.                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
| `--resolution {SVGA,HD1080,HD1200}`      | Capture resolution for all cameras. Default `null`: each camera's SDK default (HD1200 on GMSL).                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              |
| `--kinematics.<field> VALUE`             | Override an IK solver cost weight or solver parameter, e.g. `--kinematics.pos_weight 100` or `--kinematics.max_joint_delta 0.02`. See [`almond_axol.kinematics`](/api/kinematics) for every field and its default.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |
| `--vr_server.port INT`                   | Port for the VR teleop WebSocket server (default: 8000). The TLS cert/key paths live here too (`--vr_server.certfile`, `--vr_server.keyfile`); `null` (the default) uses the auto-generated cert.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |
| `--log_level {DEBUG,INFO,WARNING,ERROR}` | Default: `INFO`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              |
| `--config_path PATH`                     | Load a whole-config JSON/YAML file; CLI overrides layer on top. See [Command configuration](/cli/configuration).                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |

```bash theme={null}
axol teleop                                       # real robot
axol teleop --sim                                 # browser visualizer
axol teleop --axol.left_stiffness 0.8
axol teleop --axol.left.elbow.kp 60 --axol.right.gripper.torque_limit 0.7
axol teleop --teleop.position_multiplier 2.0      # scale hand motion 2x
axol teleop --teleop.rotation_multiplier 2.0      # scale hand rotation 2x
axol teleop --left_channel null                   # disable the left arm
axol teleop --cameras "{overhead: 41234567, left_arm: 41234568, right_arm: 41234569}"
axol teleop --cameras "{overhead: 41234567}"      # stereo overhead auto-detected, per-lens
axol teleop --config_path my_teleop.json          # whole-config file
```

## Camera streaming to the headset

With `--cameras` set, teleop opens the locally attached ZED cameras by serial number and relays them to the headset over WebRTC, so the operator sees the robot's cameras instead of (or alongside) passthrough. All streamed feeds show at once and can be moved and resized with the controllers — see the [VR Interface guide](/guides/vr-interface#camera-views) for the layout and controls. Requires `pyzed` ([`zed.install`](/cli/zed-install)) and the GStreamer NVENC stack ([`gst.install`](/cli/gst-install) — the hosted installer runs it).

Teleop and [`collect-data`](/cli/collect-data) share one **GPU-resident** video path (`almond_axol.video.gst_zed`): the zed-gstreamer source elements (`zedxonesrc` mono / `zedsrc` stereo) grab and **NVENC**-encode (`nvvidconv` → `nvv4l2h264enc`) entirely on the GPU, and Python only ever sees the encoded H.264 access units — which **aiortc** forwards as pre-encoded packets (RTP/SRTP/ICE + SDP signaling), with no Python encode step. Teleop asks the cameras for 60 fps capture (falling back to the camera default when unsupported). Stereo ZED X cameras are auto-detected from their serial: a **stereo overhead** grabs both eyes on one pass and is relayed as a single side-by-side source, `overhead_sbs` (both eyes packed into one track — one decoder session on the headset), rendered per-lens, while a **stereo wrist** is relayed as a single source under its slot name (one feed, like a mono camera). When the gst stack is unavailable the path falls back to the ZED SDK grab (`ZedCamera`) plus in-Python NVENC (`almond_axol.video.hw_video`), which streams the overhead as the two per-eye sources `overhead_left` / `overhead_right` instead.

The whole relay — camera grab/encode plus WebRTC sending — runs in a **dedicated subprocess** (`almond_axol.video.video_proc`), the same isolation pattern used for the IK solver: it keeps the camera pump off the teleop process so the control loops stay at full rate. Only SDP signaling crosses the process boundary.

For low-latency encode the NVENC/VIC engine clocks must be pinned to their maximum (the default Jetson devfreq governor scales them for throughput, not latency, which roughly triples per-frame encode time). This is handled by [`axol jetson.setup`](/cli/jetson-setup), run at boot by the installer's systemd unit — not at teleop startup. If no GStreamer hardware encoder is present at all, aiortc falls back to software H.264 — `SVGA` is then the recommended resolution.

<Note>
  This command takes its config via draccus, not fixed `argparse` flags. Field names use underscores, nested fields are dot-separated, `None` is `null`, and lists use `[a,b,c]` — see [Command configuration](/cli/configuration) for the full model.
</Note>
