MetriCal Command: Calibrate
Usage
metrical calibrate [OPTIONS] \
<DATASET> \
<INPUT_PLEX> \
<INPUT_OBJECT_SPACE>
command = "calibrate"
dataset = "{{variables.dataset}}"
input-plex = "{{init-stage.initialized-plex}}"
input-object-space = "{{variables.object-space}}"
camera-motion-threshold = "disabled"
lidar-motion-threshold = "disabled"
optimization-profile = "standard"
preserve-input-constraints = false
disable-ore-inference = false
overwrite-detections = false
override-diagnostics = false
render = false
detections = "{{auto}}"
results = "{{auto}}"
Purpose
The Calibrate command runs a full bundle adjustment over the input calibration data. It requires three main arguments:
- The input data.
- An initial plex (usually derived from Init command). This represents a naive guess at the state of your system.
- An object space file. This tells MetriCal what targets to look for during calibration.
We mean it when we say this runs a full bundle adjustment. MetriCal will optimize both the plex values (the calibration of your system, effectively) and the object space values. This means that MetriCal is robust to bent boards, misplaced targets, etc. All of these values will be solved for during the adjustment, and your calibration results will be all the better for it.
We use ANSI terminal codes for colorizing output according to an internal assessment of metric quality. Generally speaking:
- Cyan: spectacular
- Green: good
- Orange: okay, but generally poor
- Red: bad
Note that these judgments are applied internally to the software and have limits that may change release-to-release. We advise that you determine your own internal limits for each independent metric.
Cached Detections
Extracting detections from a dataset usually takes the lion's share of time during a run. To make this less onerous on subsequent runs, MetriCal will automatically create a cache of detections in MCAP format. It will then save them to the same directory as the input dataset, as well as recall that data on subsequent runs of the same calibration.
Detections rely on matching UUIDs between its data and the components in the plex to correctly attribute observations. This means that generating new UUIDs for an initialized plex will require new detections, since the UUIDs will have changed.
Extracting Optimized Values
There is a lot of data in the MetriCal results. Luckily, it's all MCAP, and it's fairly easy to pick apart. Use the MCAP CLI tool to extract this data into its own file for easy analysis and comparison to the original inputs. You can also read more about the results MCAP format in its documentation page.
# Retrieve the optimized plex
mcap get attachment --name optimized-plex results.mcap > optimized_plex.json
# Retrieve the optimized object space
mcap get attachment --name optimized-object-space results.mcap > optimized_object.json
The Motion Filter
MetriCal runs a motion filter over any and all images and point clouds in the input dataset. This filter removes any features that might be affected by motion blur, rolling shutter, false detections, or other artifacts. This is a critical step in the calibration process, as it ensures that the data used for calibration is the best we can get.
Both cameras and lidar have motion filters that get run automatically for every calibration. The thresholds for motion must be set before every calibration run. MetriCal requires this for two reasons:
- Smart motion filtering can dramatically cut down on calibration time and make processes more efficient
- We want you to know a motion filter even exists! It can be jarring to run a calibration and see half of your data was skipped over.
See the breakdown of camera and lidar motion thresholds below for more on those values.
There are three states that the motion filter can assign to a component.
| State | Description |
|---|---|
| In Motion | The movement between subsequent detections is above the motion threshold. |
| Still | The movement between subsequent detections is below the motion threshold. |
| Still (Redundant) | The component is Still and the movement between detections is small enough that they can be considered the same detection. |

The motion filter works at the sync group level, not component-by-component. You can see how this decision affects the filter's behavior over time. If one component is moving, the motion filter labels all other components as moving as well, regardless of their actual detected motion status. This decision makes intuitive sense: in a calibration dataset, all components (or all object spaces) should be moving together.
The motion filter inherently assumes a stream is producing high-frequency observations with consistent timestamps, e.g. a 30fps camera. For observation streams with inconsistent or infrequent observations, even a low motion threshold can filter everything! It can also be a sign of malformed timestamps.
If either of these things sound like your data, you should disable the corresponding motion filter entirely.
Camera Motion Threshold
The image motion filter sets its threshold based on the average flow of detected features between
subsequent frames. Background motion is not considered, so you can still use this filter when taking
data in a busy place. Any motion that results in an average pixel shift higher than the camera
motion threshold (--camera-motion-threshold) is considered In Motion.
In addition to assigning a custom non-negative number, there are a number of predefined profiles that can be used for convenience:
| Value | Alias | Meaning |
|---|---|---|
strict | rolling-shutter | 1 pixels/frame. A low threshold, necessary for rolling shutter cameras. |
lenient | motion-cal | 10 pixels/frame. A higher threshold, suitable for calibrations that require some motion (IMU, odometry). |
disabled | N/A | No motion filtering, disabled for camera streams entirely |

Lidar Motion Threshold
The lidar motion filter sets its threshold based on the geometric movement of the detected circle
center between subsequent observations. Any motion that results in an average distance shift higher
than the lidar motion threshold (--lidar-motion-threshold) is considered In Motion.
In addition to assigning a custom non-negative number, there are a number of predefined profiles that can be used for convenience:
| Value | Alias | Meaning |
|---|---|---|
strict | poor-sync | 0.02 meters/obs. A low threshold, necessary when there is poor synchronization between lidars or camera-lidar pairs. |
lenient | motion-cal | 0.1 meters/obs. A higher threshold, suitable for calibrations that require some motion (IMU, odometry). |
disabled | N/A | No motion filtering, disabled for lidar streams entirely |

Examples
Run a calibration. Save the results to results.mcap
metrical calibrate \
--results results.mcap \
$DATA $PLEX $OBJSPC
Visualize detections
Make sure you have a Rerun server running!
metrical calibrate \
--render \
$DATA $PLEX $OBJSPC
Save calibration log output
There's so much data in a calibration log. This is where the global
--report-pathflag really comes in handy; you can save all of that log output to an HTML file for easy viewing. Note: Using a manifest will write a report for the entire run automatically into the workspace directory, no--report-pathargument needed.metrical calibrate --report-path metrical.html $DATA $PLEX $OBJSPC
Arguments
[DATASET]
The dataset with which to calibrate, or the detections from an earlier run. Users can pass MCAP files (usually with an
.mcapextension) or a top-level directory containing a set of nested directories for each topic
[INPUT_PLEX]
The input plex path (initialized plex, optimized plex, or calibrate results MCAP)
[INPUT_OBJECT_SPACE]
Path to object space description (JSON file or calibrate results MCAP)
Options
Global Arguments
As with every command, all global arguments are supported (though not all may be used).
--camera-motion-threshold [PROFILE|VALUE]
This threshold is used for filtering camera data based on detected feature motion in the image. An image is considered still if the average delta in features between subsequent frames is below this threshold. The units for this threshold are in pixels/frame.
Predefined profiles:
strictorrolling-shutter: A low threshold, necessary for rolling shutter cameras. (1 pixels/frame)lenientormotion-cal: A higher threshold, suitable for calibrations that require some motion (IMU, odometry). (10 pixels/frame)disabled: No motion filtering, disabled for camera streams entirely. (inf pixels/frame)- Or a custom non-negative numeric value in pixels/frame.
--lidar-motion-threshold [PROFILE|VALUE]
This threshold is used for filtering lidar data based on detected feature motion in the point cloud's detected circle center. A point cloud is considered still if the average delta in metric space between subsequent detected circle centers is below this threshold. The units for this threshold are in meters/observation.
Predefined profiles:
strictorpoor-sync: A low threshold, necessary when there is poor synchronization between lidar and camera. (0.02 meters/obs)lenientormotion-cal: A higher threshold, suitable for calibrations that require some motion (IMU, odometry). (0.1 meters/obs)disabled: No motion filtering, disabled for lidar streams entirely. (inf meters/obs)- Or a custom non-negative numeric value in meters/obs.
--optimization-profile [OPTIMIZATION_PROFILE]
Default: standard
The optimization profile of the bundle adjustment. This is a high-level setting that controls the optimization's maximum iterations, relative cost threshold, and absolute cost threshold.
Possible values:
- performance: Use this argument when speed is necessary
- standard: This is a balanced profile between speed and accuracy
- minimize-error: This profile maximizes accuracy. For some small datasets, this profile may be just as fast as the standard profile. However, for larger datasets, this profile may take significantly longer to converge
--opt-max-iterations [OPT_MAX_ITERATIONS]
The maximum iteration count on the bundle adjustment. This is a hard limit on the number of iterations for the bundle adjustment. If the optimization does not converge within this number of iterations, the optimization will stop and return the current state of the optimization.
--opt-relative-threshold [OPT_RELATIVE_THRESHOLD]
The relative cost termination threshold for the bundle adjustment. "Relative cost" is the difference in cost between iterations. If the relative cost between iterations is below the threshold, the calibration will exit
--opt-absolute-threshold [OPT_ABSOLUTE_THRESHOLD]
The absolute cost termination threshold for the bundle adjustment. "Absolute cost" is the value of the cost at a given iteration of the optimization. In practice, this value is hardly ever reached; the optimization will hit a local minima well above this value and exit
--preserve-input-constraints
Preserve the spatial constraints that were input for this dataset.
The default behavior is to discard any constraints that were not derived from the data (e.g. spatial constraints from sync groups) or optimized. Pass this flag if the output plex should include any constraints that were input but not found or optimized.
--disable-ore-inference
Disable inference of object relative extrinsic constraints in the bundle adjustment.
The default behavior is to infer object relative extrinsic constraints in the bundle adjustment. Pass this flag if object relative extrinsic constraints should not be inferred; this may save a substantial amount of time in datasets using many object spaces.
It's generally expected that disabling ORE inference will not affect calibration accuracy. The main downside to setting this flag is that MetriCal will not output the object space intrinsics as part of the calibration results, which may disable visualization of the target poses when using
metrical display.
-y, --overwrite-detections
Overwrite the detections at this location, if they exist.
--override-diagnostics
When this flag is set to true, a calibration dataset that presents data diagnostics errors will still output a calibration result.
-r, --render
Whether to visualize the detections using Rerun. This requires a working installation of Rerun on your machine. You may also need to start a rerun server manually if you are running this in Docker.
Read more about configuring Rerun here.
--render-socket [RENDER_SOCKET]
The web socket address on which Rerun is listening. This should be an IP address and port number separated by a colon, e.g.
--render-socket="127.0.0.1:3030". By default, Rerun will listen on sockethost.docker.internal:9876. If running locally (not via Docker), Rerun's default port is127.0.0.1:9876When running Rerun from its CLI, the IP would correspond to its
--bindoption and the port would correspond to its--portoption.
--detections [DETECTIONS]
This argument acts both as input and output to MetriCal.
If there are no detections at this path, it is assumed to be an output path, and detections will be written here after being computed.
If there are detections at this path, they will be read in and used as input to the calibration. In this case, no new detections will be computed, and the existing detections will be used as-is.
The
--overwrite-detectionsflag will force overwriting the detections at this path if they exist.
-o, --results [RESULTS_PATH]
Default: path/to/dataset/[name_of_dataset].results.mcap
The output path to save the final results of the program, in MCAP format.