Configuration
This page lists every setting TANDEM reads. For setup, see Installation; for commands, the Command reference.
Profiles
A profile is one task in one YAML file:
# ~/tandem-data/profiles/my-task.yml
version: 3
description: 'Cup onto plate'
task:
prompt: put the cup on the plate # the episode's language label
target_episodes: 30
hitl: # phase planning (see below)
enabled: true
on_verification_failure: label
planner:
backend: tiptop
options:
tamp: # TiPToP's task settings
num_particles: 512
recording:
enabled: true
export:
hf_repo: my-org/cup-on-plate
Keys you leave out take their defaults. Usually you start from the paper's settings or a copy:
tandem profile create my-task --prompt "put the cup on the plate" # the paper's settings, your task
tandem profile create my-box --from store-bread-in-closed-box # a copy of any profile
tandem profile edit my-task # opens $EDITOR; validated on save
Robot, camera and calibration settings aren't in a profile; they're in the rig. Every key is documented
in src/tandem/resources/profile_template.yml. An unknown key fails at load, and a misspelled tamp key gets
a suggestion. Profiles and their trajectories live under ~/tandem-data/:
~/tandem-data/
├── profiles/
│ ├── my-task.yml one file per profile
│ └── .planner-options/ a previous planner's options (tandem planners use)
└── trajectories/
└── my-task/
├── eval/ not yet labeled
├── success/
└── failure/ failures, and settled trials (no label)
Profile keys
| key | default | what it does |
|---|---|---|
version |
3 |
Layout version. See older profiles. |
description |
Free text. The profile's name is its file name: lowercase letters, digits, - and _, not starting with - or _. |
|
task.prompt |
The episode's language label, and what phase planning splits into steps. | |
task.goal |
null |
The planner's goal, when it must differ from task.prompt. |
task.target_episodes |
20 |
Target shown by profile show, the UI and the session. > 0. |
hitl |
Phase planning. | |
planner |
The planner and its task settings. | |
recording.enabled |
true |
Record camera video. tandem collect --no-record overrides it. |
export.hf_repo |
'' |
Repository for tandem export lerobot without --repo. |
export.private |
false |
Upload as private. --private or --public overrides it. |
The paper's five
tandem init adds the paper's five tasks, each with the settings the paper collected it with, plus
the three switches.
| profile | paper task (Fig. 3) | its own settings |
|---|---|---|
cover-bread-rolls |
Cover Bread Rolls | A grasp-centre cost and threshold, and finer voxels. |
solve-constrained-puzzle |
Solve Constrained Puzzle | Surface-fitted placement. |
sort-and-cover-snacks |
Sort & Cover Snacks | |
open-obstructed-book |
Open Obstructed Book | |
store-bread-in-closed-box |
Store Bread in Closed Box | Surface-fitted placement into the box, and finer voxels. |
- They're yours to edit.
tandem initnever overwrites one, and restores one you deleted. profile create --promptgives a new profile what the five share: phase planning, the TAMP and DATAFARM settings, and the three switches.- Safety: they set
tamp.time_dilation_factor_literal: 1.0, so planned motions ignore the rig'stime_dilation_factor(homing and capture moves don't). Keep a hand on the E-stop.
Older profiles
Before version 3, a profile was a directory (profiles/<name>/profile.yml) with its own robot, cameras and
calibration.json. They don't show up until you move them:
tandem profile migrate # tandem init also does this
Each profile's task goes to profiles/<name>.yml and its trajectories to trajectories/<name>/. The robot
and cameras seed the rig (from the active profile) if rig.yml doesn't exist yet. Extrinsics are
added to the rig's calibration.json only for cameras without an entry.
- Old directories aren't deleted. Each is archived in
profiles/.migrated/<name>/with amigration.json. - If the rig can't be set up from them, no profile is moved. A profile that fails to move is left as it was, and running the command again finishes a partial move.
tandem rig setandrig editwait until old profiles are moved. Older TANDEM can't read version 3.- Version-1 profiles keep
hitl.on_robot_phase_failure: teleop, the old default. Setabortunless you chose teleop.
Phase planning (hitl)
Phase planning is on in the paper's profiles and in every new profile, and off in a profile with no hitl:
block. The defaults match the paper. Some common changes:
# Label failed human steps yourself, instead of excluding the trial from the dataset
hitl:
enabled: true
on_verification_failure: label
# When the planner can't plan a robot step, let a person do it (checked like a human step)
hitl:
enabled: true
on_robot_phase_failure: teleop # or replan: ask the model for a new plan
# Turn phase planning off: the planner does the whole task, with no human steps or checks
hitl:
enabled: false
Each check costs one model call per checkable atom, with the arm parked. Checkable atoms are invented
predicates plus the planner's checkable_predicates (TiPToP: On; capabilities).
Planning
| key | default | what it does |
|---|---|---|
enabled |
false |
When off, each attempt is one leg to the planner's goal, with no phases or checks. |
proposal_model |
gemini-2.5-pro |
Splits the task into phases and invents predicates and operators. |
vlm_model |
gemini-2.5-flash |
Answers checks, and names objects for tandem plan. |
max_attempts |
3 |
Tries per proposal and per check, rejections fed back. Also the re-plan limit under replan. ≥ 1. |
check_plan_effects |
true |
Run the contract check when repairing a proposal (no model call). |
conjoin_robot_phases |
true |
Plan consecutive robot phases as one goal where sound (conditions). When off, each robot phase gets its own leg and perception pass. |
cache_path |
null |
SQLite cache of proposals (keyed by model, prompt, image), relative to profiles/. Re-plans skip it. |
save_vlm_io |
true |
Save every model image and reply, rejected ones too, in vlm/. |
Checks
| key | default | what it does |
|---|---|---|
verification_camera |
external |
Which camera's image the checks use: external, hand or perception (whichever camera perception uses). |
check_human_effects |
true |
After a human phase, check its add effects hold and delete effects don't. |
check_human_preconditions |
false |
Check a human phase's preconditions once, before the hand-off. |
check_tamp_preconditions |
false |
Before a robot leg, check what earlier phases should have made true. |
check_tamp_effects |
false |
After a robot leg, check its add effects. Recorded only. |
classify_initial |
false |
Classify each invented atom on the first image (a call each); with check_plan_effects, re-run the contract check. Recorded only. |
verify_enforced |
true |
When off, failed checks are only recorded. |
precondition_enforced |
false |
When on, an unmet precondition ends the trial at verification. When off, it's only recorded. |
verify_final_phase |
true |
When off, a final human phase isn't checked and your label decides. |
Failures
| key | default | what it does |
|---|---|---|
verify_retries |
1 |
Extra tries for a human phase that fails its check; the operator is told what's missing. ≥ 0. |
on_verification_failure |
exclude |
What happens when a check still fails after retries. exclude files the trial in failure/ as excluded, with no label. label lets your label decide. |
on_robot_phase_failure |
abort |
What happens when a robot phase can't be planned. abort fails the trial at tamp_planning. teleop has a person do it, checked like a human phase. replan asks the model for a new plan that accounts for the failure. Execution failures always end at tamp_execution. |
Human executor
| key | default | what it does |
|---|---|---|
human_executor |
teleop |
Who does human phases (choosing one). |
human_executor_options |
{} |
Each executor's settings, keyed by its name. Checked at load. |
allow_unrecorded_human_phase |
false |
While recording, accept a human phase with no recording (d, or a leg with no frames). --no-record always accepts. |
The rig
The rig holds this workstation's robot, camera and calibration settings. All profiles use it. tandem init
writes it to ~/.config/tandem/rig.yml:
version: 1
robot:
type: fr3_robotiq
host: 172.16.0.2 # the NUC
cameras:
perception: external
hand: {serial: '14846828'}
external: {serial: '32439448'}
calibration: calibration.json
planners: {} # a planner's machine settings, where they differ from its defaults
To view or change it:
tandem rig show # also --json; the web UI's Settings page shows it too
tandem rig set robot.host NUC_ADDRESS # `null` removes a setting
tandem rig set planners.tiptop.perception.m2t2.url http://HOST:8123
tandem rig edit # opens $EDITOR; validated on save
tandem rig path --calibration # where the extrinsics are
| key | default | what it does |
|---|---|---|
robot.type |
fr3_robotiq |
The arm. TiPToP: fr3_robotiq (FR3) or panda_robotiq (Panda), with a Robotiq 2F-85 via the shim. panda (Franka Hand): the shim refuses its gripper commands. ur5: needs tiptop's ur5 extra (ur_rtde), not in the runtime. |
robot.host |
172.16.0.2 |
The NUC's hostname or IP address. The planner sets the ports. |
cameras.perception |
external |
The camera perception uses. With external the arm stays at q_home; with hand it moves to q_capture first. |
cameras.hand, cameras.external |
Wrist and third-person cameras. Every leg is recorded from them; both must open at warm-up. | |
cameras.external_2 |
Optional second third-person camera (DROID exterior_2). If listed, it must open. |
|
<camera>.serial |
ZED serial, quoted ('14846828'). One role per serial. |
|
<camera>.type, .resolution |
zed, HD720 |
|
<camera>.fps |
15 |
Keep 15: three ZEDs at HD720@30 exceed USB bandwidth, and export resamples to 15 Hz. |
calibration |
calibration.json |
The extrinsics file, relative to rig.yml, or absolute. |
planners.<name> |
Each planner's machine settings (TiPToP's). |
Calibration file
calibration.json holds one pose per camera, keyed by serial:
{
"14846828": {"pose": [0.0266, 0.0705, -0.1392, -0.4509, -0.0045, -1.5620]},
"32439448": {"pose": [0.1532, -0.5827, 0.4410, -2.0843, 0.0126, 0.1964]}
}
poseis[x, y, z, roll, pitch, yaw], in meters and radians (xyzEuler angles).- The wrist camera's pose is relative to the end effector (
ee_from_cam). - An external camera's pose is relative to the robot's base (
world_from_cam). - Other keys in an entry, such as
timestamp, are ignored.
A session won't start if a camera has no entry. To get the poses, see Cameras and calibration.
Related settings outside the rig
- TiPToP's scripts (
calibrate-wrist-cam,viz-calibration,cutamp-demo, …) run throughtandem runtime runorshelluse your rig settings, through$TIPTOP_CONFIGand$TIPTOP_CALIBRATION.--rawuses tiptop's stock config instead. - Teleop uses
robot.hosttoo; TANDEM passes it to the teleop driver. - Gripper mask (
perception: handonly): make yours withtandem runtime run compute-gripper-mask(orpaint-gripper-mask). It writes the runtime'stiptop/tiptop/config/assets/gripper_mask.png.
Planner settings
A profile names its planner and that planner's settings for the task:
planner:
backend: tiptop # which planner
options: # its task settings, checked by the planner at load
tamp:
num_particles: 512
Task settings go in the profile's planner.options. Machine settings (a server's address, a robot's ports) go
in the rig under planners.<name>. If you put a key in the wrong file, TANDEM refuses it and says
where it goes. tandem planners info NAME lists both (how planners declare them).
Did my setting apply? tandem profile show NAME --planner prints exactly what the planner receives.
Switching planners
tandem planners use myplanner # the active profile now plans with it
tandem planners use myplanner -o KEY=VALUE # also set an option it requires (repeatable)
tandem planners default myplanner # the planner new profiles get
use sets planner.backend and stashes the old planner's options in
profiles/.planner-options/<profile>.<planner>.yml, restoring them when you switch back. It warns if the
runtime isn't installed, and it repairs a profile naming a planner this machine lacks.
TiPToP options
Machine settings live in rig.yml under planners.tiptop:
tandem rig set planners.tiptop.robot.time_dilation_factor 0.3 # arm at 30% speed
tandem rig set planners.tiptop.perception.m2t2.url http://HOST:8123
tandem rig set planners.tiptop.perception.depth_smoothing_frames 1
The robot's address and type come from robot.host and robot.type.
robot:
| key | default | what it does |
|---|---|---|
dof |
7 |
Joint count. It must match q_home and q_capture. |
port, gripper_port, state_port |
5555, 5559, 5557 |
Shim control, gripper and state ports. |
time_dilation_factor |
0.2 |
Arm speed, in (0, 1]. 0.2 is 20%. |
q_home, q_capture |
TiPToP's | Home pose; capture pose for cameras.perception: hand. |
perception:
| key | default | what it does |
|---|---|---|
m2t2.url, m2t2.apply_bounds |
http://localhost:8123, true |
M2T2 grasp server, and a flag sent with each request. If it runs on this workstation, a session starts it when it's down (servers). tandem doctor probes it. |
foundation_stereo.url |
http://localhost:1234 |
FoundationStereo depth server, used every rollout. If it runs on this workstation, a session starts it when it's down. tandem doctor probes it. |
sam_mode |
local |
local: SAM-2 in the runtime. remote: the server at sam_url (then required). |
depth_smoothing_frames |
5 |
Depth frames median-fused at capture. 1 disables it. |
robot_mask_margin_m |
0.02 |
Padding when cutting the arm out of a third-person cloud. Raise it if arm points remain. |
depth_trunc_m, voxel_downsample_size, contact_threshold_m, mask_erosion_pixels |
5.0, 0.0075, 0.01, 3 |
Passed to tiptop's settings of the same names. |
gemini.model, gemini.temperature |
gemini-robotics-er-2-preview, null |
Only recorded; the pinned tiptop always uses this model at its own temperature. tandem doctor warns on a mismatch. |
Task settings live in the profile's planner.options.tamp:
planner:
backend: tiptop
options:
tamp:
num_particles: 512
voxel_downsample_size: 0.005 # finer than the rig's perception setting
placement_support: true # see Surface-fitted placement
- These are cuTAMP and cuRobo overrides with tiptop's key names, so a
cfg/tamp/*.yml'stamp_overridespaste in as is. Accepted keys:src/tandem/planners/tiptop/tamp_keys.py. - For
contact_threshold_mandvoxel_downsample_size, thetampvalue overrides the rig's. tandem doctorwarns about a key that needs another (retime_opswithoutretime_trajectory).- Relative checkpoint paths (
encoder_path,posture_ref) resolve beside the profile, then in the runtime, where the install puts the DATAFARM checkpoints (vae/checkpoints/vae_full_v2.pt,rnd/checkpoints/rnd_droid.pt). - Stroke re-timing is the trajectory encoder's:
retime_trajectorywithencoder_pathand the otherretime_*keys. A profile written with the older names (vae_path,vae_manifold_weight,blend_*) still loads, read under the new names;vae_retiming: falseandblend_mode: vaeare dropped, and a setting of a removed mode (the VAE-owned clock, spline or flow re-timing) is refused with the reason.
Surface-fitted placement
By default, cuTAMP can place an object anywhere in a surface's bounding box, at the top of it, such as on an
open box's rim or a plate's edge. placement_support: true places only on observed level patches that fit the footprint:
tamp:
placement_support: true
placement_fill_occluded: true # e.g. a box whose floor the camera can't see
| key | default | what it does |
|---|---|---|
placement_support |
false |
Turns it on. The other keys are read only then. |
placement_support_margin |
0.01 |
Surface kept around the footprint, in metres. ≥ 0. |
placement_flatness_tol |
0.008 |
Height variation (and slope) that still counts as level, in metres. > 0. Raise it for noisy reconstructions. |
placement_support_required |
true |
If no patch fits, the plan fails. With false, it falls back to the bounding box. |
placement_into_surface |
true |
Let the object overlap its surface in collision checks. Containers need it (they reconstruct as filled hulls). |
placement_fill_occluded |
false |
Count unseen cells inside a surface's outline as floor, as in a box the camera can't see into. |
placement_min_seen_frac |
0.25 |
Observed fraction each footprint needs; guards placement_fill_occluded. In [0, 1]. |
In the paper's profiles, solve-constrained-puzzle sets placement_support, placement_support_required and
placement_into_surface. store-bread-in-closed-box sets all seven, with margin 0.005, flatness 0.012 and
placement_fill_occluded: true.
The three switches
These tamp: keys are off unless set. The paper's five and every new profile turn them on,
because every run in the paper used them.
tamp:
table_plane_support_vote: true
disjoint_object_masks: true
retime_stretch_to_caps: true
| key | what it switches on |
|---|---|
table_plane_support_vote |
Pick the table among RANSAC's planes by the objects resting on each, instead of by every object within 3 cm above or below. |
disjoint_object_masks |
Disjoint object masks (a shared pixel goes to the smaller object), so a container's hull stops at what rests on it. Surface-fitted placement always uses them. |
retime_stretch_to_caps |
With retime_trajectory, slow a stroke that can't be re-timed within the velocity and acceleration caps until it fits. It can get many times slower. |
TANDEM settings and credentials
TANDEM's own settings live in ~/.config/tandem/config.toml:
tandem config list
tandem config set ui.port 8800
tandem config set data_root /mnt/data/tandem
tandem config edit # also: get KEY, path
| key | default | what it does |
|---|---|---|
active_profile |
none | The profile commands act on (tandem profile use). tandem init sets cover-bread-rolls, and tandem profile create makes its profile active when none is. |
data_root |
~/tandem-data |
Profiles and trajectories. $TANDEM_DATA_ROOT overrides it. |
runtime_dir |
~/.local/share/tandem/runtime |
TiPToP's runtime (about 25 GB). $TANDEM_RUNTIME_DIR overrides it. |
default_planner |
tiptop |
The planner new profiles get (tandem planners default NAME). |
hf_org |
Owner for an export repo named without one. | |
teleop.enabled |
false |
Human phases can use teleop. tandem executors install teleop turns it on (teleop). |
teleop.droid_dir, teleop.python |
blank | Blank: the driver runs in the teleop runtime. Set both to run a DROID checkout and environment of your own. |
teleop.controller |
right |
Which VR controller drives the arm: right or left. |
ui.host, ui.port, ui.open_browser |
127.0.0.1, 8787, true |
tandem ui. A busy port steps to the next free one. |
Credentials
Credentials are stored in credentials.toml next to it, readable only by you:
tandem config set-gemini-key # for phase planning and TiPToP's perception
tandem config set-hf-token # for tandem export lerobot --push
GEMINI_API_KEYorGOOGLE_API_KEYoverrides the stored Gemini key.- The Hugging Face token is looked up in order: stored,
HF_TOKENorHUGGING_FACE_HUB_TOKEN, then~/.cache/huggingface/token(huggingface-cli login).
Directories
These are the Linux defaults; other systems differ.
| directory | default | env var |
|---|---|---|
config: config.toml, credentials.toml, rig.yml, calibration.json |
~/.config/tandem |
$TANDEM_CONFIG_DIR |
| state: logs, session scratch (Logs and session files) | ~/.local/state/tandem |
$TANDEM_STATE_DIR |
| share: runtimes | ~/.local/share/tandem |
$TANDEM_SHARE_DIR |
| data: profiles, trajectories | ~/tandem-data |
$TANDEM_DATA_ROOT |
| TiPToP's runtime | <share>/runtime |
$TANDEM_RUNTIME_DIR |
| other planners' runtimes, teleop's and the servers' | <share>/runtimes/NAME (teleop, m2t2, foundation_stereo) |
$TANDEM_RUNTIMES_DIR |
| offline install sources | none | $TANDEM_PLANNER_SOURCES |
The planner runtime
tandem planners install tiptop # build or update TiPToP's runtime (tandem init runs this)
tandem planners info tiptop # pinned vs installed commits
tandem planners install tiptop --force # refetch and rebuild everything
TiPToP's runtime is a pixi environment (torch, cuRobo's CUDA kernels, cuTAMP, tiptop).
The first build takes about 25 GB and 5–20 minutes, and asks before installing pixi (--yes accepts). Its
pinned sources are below; the TANDEM branches add surface-fitted placement and
the three switches.
| source | branch | pinned commit |
|---|---|---|
| SamratSahoo/tiptop | TANDEM |
6cabf0f |
| SamratSahoo/cuTAMP | TANDEM |
fc8f233 |
| SamratSahoo/curobo | main |
3a90ff4 |
The install applies one patch (so $TIPTOP_CONFIG and $TIPTOP_CALIBRATION can point TiPToP at the rig)
and places the DATAFARM checkpoints. How sources are fetched is in Adding a planner, and where the build
log goes is in Logs and session files.
- Updating. A moved pin shows
outdatedintandem planners list. Reinstalling replaces only the moved sources. - ZED. With the ZED SDK installed (
/usr/local/zed/get_python_api.py), the install addspyzed. Without it the install succeeds but ZED cameras won't open, andtandem doctorsays so. Install the SDK, then reinstall.
Offline install
If the workstation can't reach GitHub, bundle the sources on a machine that can:
tandem planners bundle tiptop --out /media/usb/planner-sources # on a machine with network
tandem planners install tiptop --sources /media/usb/planner-sources # on the workstation
- Use the same TANDEM version on both: commits are checked against their pins and files against the digest.
$TANDEM_PLANNER_SOURCEScan replace--sources(one directory per source:tiptop/,cuTAMP/,curobo/). While set, nothing is fetched.- Only sources are bundled.
pixi installstill needs conda-forge, PyPI and GitHub (to build SAM-2), and the first warm-up downloads the SAM-2 checkpoint (about 0.9 GB).