Configuration Parameter Reference¶
Configuration System explains the two config formats
(unified vs. legacy) and how they're parsed. This page is the field-by-field
glossary for what each parameter in the calibration: and identification:
sections actually means — units, which pipeline step consumes it, and its
unified-format equivalent where one exists.
Field names below match the legacy flat format, since that's what you
see verbatim in an existing config file (the legacy format was removed in
1.0; these are the keys config_migration reads). The Unified path column
gives the equivalent key under tasks.calibration.* /
tasks.identification.* in the unified format — unified_to_legacy_config
and unified_to_legacy_identif_config (both in figaroh/src/figaroh/*/config.py)
convert one to the other, so the two formats agree on meaning even though
the layout differs.
calibration: section¶
| Field | Meaning | Unified path |
|---|---|---|
calib_level |
Kinematic-error model: joint_offset (one offset per joint, about or along its own axis) or full_params (six placement errors per joint, translation and rotation vector in the joint frame) |
parameters.calibration_level |
estimation |
Which parameters to estimate and how: method (structural default, excitation, map, map_cv, cv_subset), priors, noise_std, cross-validation settings. See Calibration estimation methods |
parameters.estimation |
non_geom |
Also identify joint elasticity (gravity-torque-driven compliance, one term per active joint) on top of the geometric model | parameters.include_non_geometric |
base_frame |
URDF frame name at the start of the kinematic chain | kinematics.base_frame |
tool_frame |
URDF frame name at the end of the chain (usually the gripper/tool mount) | kinematics.tool_frame |
base_to_ref_frame |
(optional, eye-hand/camera mode only) A frame reachable from base_frame via a known transform (e.g. a camera housing anchor) |
eye_hand.camera_frame (requires eye_hand.enabled: true) |
ref_frame |
(optional, eye-hand/camera mode only) The frame the chain restarts from after the known base_to_ref_frame segment; the unknown camera/anchor pose being estimated sits between ref_frame and the true world frame |
eye_hand.reference_frame (requires eye_hand.enabled: true) |
markers[].ref_joint |
Joint the external marker is rigidly mounted near; its offset from tool_frame is what gets estimated (pEEx/y/z, phiEEx/y/z; pEEx_k ... for marker k). Several markers are points on the same tool_frame, see Several points on the tool |
measurements.markers[].reference_joint — written by convention, but not currently read back by _extract_marker_info (only measurable_dof, from the first marker, is consumed) |
markers[].measure |
6-DOF boolean mask [x, y, z, roll, pitch, yaw] — which axes the external sensor (mocap/vision/contact) actually reports for that marker. Every marker uses the first marker's mask |
measurements.markers[].measurable_dof |
free_flyer |
True if the robot's base itself is unconstrained/floating in the model (mobile-base robots with an unknown per-sample base pose) |
kinematics.free_flying_base |
base_pose |
Initial guess [x, y, z, roll, pitch, yaw] (m, rad) for the base_frame → world (or anchor → camera, in eye-hand mode) transform being estimated |
measurements.poses.base_pose |
tip_pose |
Initial guess [x, y, z, roll, pitch, yaw] (m, rad) for the tool_frame → marker transform being estimated |
measurements.poses.tool_pose |
coeff_regularize |
Removed in 1.0 (#120, #185), default 0. One L2 weight that robot cost functions applied to every joint parameter, penalising metres and radians alike. Core never applied it; a non-zero value is now a ConfigurationError (the migration tool writes 0 and warns). Use estimation.method: map instead (priors of physical size per parameter group, estimation methods) |
parameters.regularization_coefficient |
outlier_eps |
Position error (meters, norm of the measured x/y/z residuals of a sample, worst marker) above which a sample is excluded and the rest refitted, for up to max_iterations fits. Rotational components do not take part; with no measured position component nothing is excluded. Excluded samples and their errors are in the evaluation (excluded_samples, excluded_sample_errors) and the report |
parameters.outlier_threshold |
random_seed |
Seed for the random configurations that select the identifiable parameter set (default 0). The selection, hence the result, does not depend on any global random state; the seed is recorded in the run provenance | parameters.random_seed |
data_file |
Path to the CSV of recorded (joint configuration, measured marker pose) samples |
data.source_file |
sample_configs_file |
Optional: path cross-referencing which planned/optimal configuration each data_file row corresponds to. Traceability only — not required for the fit itself |
data.sample_configurations_file |
nb_sample |
Number of samples expected from data_file |
data.number_of_samples |
Why the camera pose is a free parameter, not a small offset
Everything else in full_params/joint_offset calibration treats the
URDF as approximately right — each joint gets a small correction
around its known nominal placement. An add-on eye-in-hand/eye-to-hand
camera is different: its housing-to-robot mounting pose usually isn't
known precisely from CAD/assembly at all, so it can't be treated as a
small perturbation. base_to_ref_frame/ref_frame handle this by
inserting the camera as a genuine 6-DOF unknown in the middle of the
chain (base_pose is a real initial guess, not a near-zero one)
rather than folding it into the same small-offset parameters as every
other joint.
That's also what makes eye-hand calibration a specialized, harder case:
this unknown camera-pose block is estimated simultaneously, from the
same measurements, as the per-joint offsets of every joint the camera
observes through — "camera is off by X" and "joint N is off by Y" can
produce very similar marker-pose residuals, which can leave the problem
under-determined or poorly conditioned. Priors (estimation.method: map) and
deliberately exciting poses that vary joint loading/configuration
widely (sample_configs_file, D-optimal design a la
tiago_pro/generate_optimal_configs.py) are the two practical levers
against this. A plain single-anchor unknown base frame — base_frame
set to universe/base_footprint with no base_to_ref_frame, as in
TiagoPro's tiago_pro_calibration_config.yaml (known_baseframe=False
in run_calibration.py) — doesn't have this coupling, because nothing
downstream of that single anchor is itself another unknown sensor
mount; it's one 6-DOF unknown at the root, not one inserted mid-chain
and re-estimated jointly with everything past it.
Several points on the tool¶
Each entry of measurements.markers is one measured point (or pose) on the
same tool_frame, for example the points of one mocap rigid body. The CSV
has columns x1, y1, z1, x2, ... per marker, and each marker k gets its
own offset pEEx_k, pEEy_k, pEEz_k (and phiEE*_k when orientation is
measured); the base frame and the joint parameters are shared. Residual rows
are ordered marker, then component, then sample.
What several points do and do not give (figaroh-plus#119):
- The same identifiable joint parameters as one point. Free point offsets absorb any rotation of the tool frame itself, so a joint offset about the tool axis stays unidentifiable. On a TIAGo arm, four points keep exactly the joint parameters one point keeps.
- Smaller standard errors, about
1/sqrt(n_points)when the points' noise is independent. Points of one rigid body are not fully independent, so expect less. - Not necessarily better prediction. On the TIAGo mocap reference, a
four-point fit did not predict held-out postures better than marker 1
alone (
full_params: 3.39 against 2.80 mm on marker 1); the extra points' rows bring their own errors (reflections, rigid-body tracking, unmodelled wrist effects) into every parameter. - One bad point affects the whole sample. Outlier exclusion
(
outlier_eps) uses each sample's worst point, so a misreflected point removes that sample. A missing (occluded) point, i.e. an empty cell, is refused by the loader with its CSV rows; exclude them withdel_list.
Decide per robot and dataset: compare held-out errors per point (the validation report lists them) with a one-point fit before adopting more points.
Eye-hand / camera calibration now has a unified-format equivalent
base_to_ref_frame/ref_frame map to
tasks.calibration.eye_hand.{camera_frame,reference_frame} (with
eye_hand.enabled: true) — the unified schema template
(templates/base_robot_config.yaml) already reserved this section;
unified_to_legacy_config just didn't read it. Fixed, so eye-hand
configs like examples/tiago/config/tiago_config_hey5.yaml are no
longer stuck in the legacy format — see
Migrating from legacy to unified format
below. (free_flyer/kinematics.free_flying_base had a similar
wrong-key bug — the converter read parameters.free_flyer, a key the
template never defines — fixed the same way.)
identification: section¶
robot_params¶
| Field | Meaning | Unified path |
|---|---|---|
q_lim_def, dq_lim_def, ddq_lim_def, tau_lim_def |
Joint position/velocity/acceleration/torque limit margins | joints.joint_limits.{position,velocity,acceleration,torque} — parsed but not currently consumed anywhere in the identification pipeline (kept for schema/future use) |
fv |
Viscous friction coefficient per joint (N·m·s/rad) | mechanics.friction_coefficients.viscous — used only if has_friction |
fs |
Coulomb/static friction coefficient per joint (N·m) | mechanics.friction_coefficients.static — used only if has_friction |
Ia |
Reflected actuator (motor + gearbox) inertia per joint (kg·m²) | mechanics.actuator_inertias — used only if has_actuator_inertia |
offset |
Constant joint torque offset per joint (N·m) — electrical/sensor bias term | mechanics.joint_offsets — used only if has_joint_offset |
Iam6, fvm6, fsm6 |
Same three quantities as Ia/fv/fs, but for the shared "6th motor" of a differential-coupled wrist (two motors jointly drive the last two wrist axes, e.g. Staubli TX40) |
coupling.{Iam6,fvm6,fsm6} — used only if has_coupled_wrist |
reduction_ratio |
Per-joint gear reduction ratio (motor turns per joint turn); sign encodes actuation direction | mechanics.reduction_ratios |
ratio_essential |
Threshold (%) for the "essential parameters" selection technique (Pham et al., 1995) | mechanics.ratio_essential — documented intent only; not currently wired into any solver in this codebase |
problem_params¶
| Field | Meaning | Unified path |
|---|---|---|
is_external_wrench |
Include a measured force/torque sensor wrench as a regressor input | problem.include_external_forces |
is_joint_torques |
Regress against joint torque signals (standard inverse-dynamics identification; almost always True) |
problem.use_joint_torques |
force_torque |
Which F/T channels to use (e.g. ['Fx','Fy','Fz'] or ['All']) — only applied if is_external_wrench is True |
problem.force_torque_sensors |
external_wrench_offsets |
Also identify a constant bias offset on the F/T sensor reading | problem.external_wrench_offsets |
has_friction |
Include the fv/fs columns in the regressor |
problem.model_components.friction |
has_actuator_inertia |
Include the Ia column in the regressor |
problem.model_components.actuator_inertia |
has_joint_offset |
Include the offset column in the regressor |
problem.model_components.joint_offset |
is_static_regressor |
Include the friction + offset ("static") regressor block | (no unified equivalent found — always derived) |
is_inertia_regressor |
Include the rigid-body inertial-parameter regressor block | (no unified equivalent found — always derived) |
has_coupled_wrist |
Last two wrist joints share differential actuation → adds the Iam6/fvm6/fsm6 terms and a coupling transform to the regressor |
coupling.has_coupled_wrist |
embedded_forces |
Express the external wrench in a body-embedded (tool) frame rather than a fixed/world frame | (no unified equivalent found) |
qr_relative_tolerance |
(optional) QR rank threshold as a fraction of the largest pivot. Columns whose pivot falls below it are treated as unidentifiable instead of fitting noise. Unset: only the absolute 1e-6 threshold applies. 1e-4 is a reasonable start when the log warns about an ill-conditioned base regressor |
problem.qr_relative_tolerance |
active_joints |
(optional) Restrict identification to this explicit subset of joint names, instead of every active joint in the chain | joints.active_joints |
torque_fit_joints |
(optional) Active joints whose measured effort enters the fit and the fit/validation statistics. The other active joints keep their kinematics in the regressor (their motion still loads the fitted joints), but their effort is neither fitted nor scored; validation lists them under not_fitted_joints. Use it for joints whose effort signal is not trusted (unknown force constant, heavy quantisation). Unset: every active joint |
problem.torque_fit_joints |
processing_params¶
| Field | Meaning | Unified path |
|---|---|---|
cut_off_frequency_butterworth |
Low-pass Butterworth filter cutoff frequency (Hz) applied to position/velocity/acceleration/torque signals before regression | signal_processing.cutoff_frequency |
ts |
Sample period (seconds) of the recorded data | signal_processing.sampling_frequency (unified format specifies the rate directly, in Hz, rather than the period) |
filter_config.filter_params |
Arguments of the median + Butterworth filter (f_sample, f_butter, nbutter, med_fil). In the unified format, f_sample, f_butter and nbutter default to sampling_frequency, cutoff_frequency and filter_order (4); an explicit signal_processing.filter_params entry overrides them |
signal_processing.filter_params |
ts → nb_samples is a rate, not a count
Despite the name, the legacy parser computes
identif_config["nb_samples"] = int(1 / ts) — i.e. an implied
samples-per-second rate, not the number of rows in your data file. Don't
be misled by the field name; it isn't used as a row count anywhere
downstream.
tls_params (optional — known-payload identification)¶
| Field | Meaning |
|---|---|
mass_load |
Known mass (kg) of a test payload attached during a supplementary run, used to refine the identified inertial parameters via total-least-squares |
which_body_loaded |
Index of the link/body in the regressor that carries that payload |
sync_joint_motion |
Parsed by nothing — not even read into identif_config by the legacy parser. Effectively decorative today. |
trajectory_params (optional — used by optimal_trajectory.py, not identification.py)¶
| Field | Meaning |
|---|---|
n_wps |
Number of waypoints in the generated exciting trajectory |
freq |
Trajectory sample frequency (Hz) |
t_s |
Time window between waypoints (s) |
soft_lim |
Joint-limit safety margin/discount applied during trajectory search |
max_attempts |
Maximum attempts to find a feasible trajectory before giving up |
max_iterations |
IPOPT iteration cap per trajectory segment (default 200; unified config: problem.max_iterations) |
segment_attempts |
Solves per trajectory segment: a failed segment is retried from a new random initial guess (default 1 = no retry; unified config: problem.segment_attempts) |
collision_margin |
Minimum clearance (m) between collision pairs (default 0.01). Pairs: the geometry model's, or, when it has none, every two geometries on non-adjacent bodies (the world and the first moving body count as adjacent); srdf removes pairs (#143). Initial guesses and every solved segment must keep this clearance along the whole spline, checked at collision_check_frequency (default 200 Hz) |
collision_checks_per_interval |
Check points per waypoint interval in the collision constraint, ending at the waypoint (default 1 = waypoints only) |
collision_screen |
Optional (m): exact distances only for pairs within it, the others count as the screen; faster with many mesh pairs (UR10 example: 0.02). Default none = all distances exact |
Migrating from legacy to unified format¶
The legacy flat format was removed in 1.0 (it warned since 0.4.6). Loading a
legacy config, whether via BaseCalibration/BaseIdentification.load_param
or figaroh.utils.config_parser.get_param_from_yaml, raises a
ConfigurationError that names the command below. The migration tool itself
still reads legacy files.
figaroh.utils.config_migration converts a legacy config to unified format
automatically, using the exact field mapping in the tables above:
python -m figaroh.utils.config_migration \
--input examples/tiago/config/tiago_config_hey5.yaml \
--output examples/tiago/config/tiago_config_hey5_unified.yaml \
--urdf urdf/tiago_48_hey5.urdf # optional: runs a round-trip self-check
--template {base,manipulator,humanoid}(defaultbase) picks which template the outputextends:—base(extendingbase_robot_config.yamldirectly) is the safest default since it doesn't risk silently inheriting a specialized template's default the legacy file didn't actually have.- Passing
--urdfruns the output back through the realunified_to_legacy_config/unified_to_legacy_identif_configand diffs it against the original legacy values — the same self-check mechanism that caught thefree_flyer/eye_handbugs above. A migration that reportsMISMATCHESshould not be trusted without investigating why. - Fields with no unified-format consumer today (
is_static_regressor,is_inertia_regressor,sync_joint_motion, the unused joint-limit fields — see the tables above) are preserved under the output'scustom:section rather than silently dropped, even though nothing reads them back yet. - The tool only converts the file; it doesn't rewrite the example script
that loads it, or verify the migrated config still produces the same
calibration/identification results on real data — treat that as a
required follow-up before switching an example over, the same way the
calc_updated_fkmmerge was validated against real TIAGo/TIAGo Pro/TALOS/UR10 data before landing.
Next steps¶
- Configuration System — format structure and template inheritance.
- Calibration Walkthrough and Identification Walkthrough — these fields in context, end to end.