Skip to main content
Version: 18.1

Steady-state Production Line

Objectives

  • Configure a reference plex and object space for your calibration line.
  • Initialize steady-state calibrations using the reference files.
  • Set the proper flags in the calibrate command to trust these files.

MetriCal is designed to work with very little information while still delivering incredible calibration results. However, sometimes that kind of sparsity isn't necessary, or even desirable.

This is the case when using MetriCal in a steady-state production environment. You expect one system to calibrate similarly to another system; at this point, MetriCal is just refining the results for production. Why start from scratch every time?

Here, we will learn how to use the values from one calibration as a reference to all future calibrations, cutting down on solving time and giving us metrics comparisons for free.

Creating A Reference Plex

A plex represents the state of a system's calibration at a specific moment in time. Plexes generally don't describe multiple systems accurately; in other words, one system's calibration won't ever match another's, no matter the mechanical tolerances. But they'll be close. This means we can use one system's plex as a reference for the others.

Let's run MetriCal naively once to get those reference values. We're going to use the manifest from our single camera calibration guide for simplicity, but this process applies across any MetriCal workflow. This is just the vanilla manifest, nothing special yet.

single_cam_reference_creation.toml
# Project Settings...

[stages.cam-new]
command = "plex-new"
output-plex = "{{auto}}"

[stages.cam-learn]
command = "plex-learn"
input-plex = "{{cam-new.output-plex}}"
dataset = "{{variables.dataset}}"
topic-to-model = [["09*", "no-distortion"]]
output-plex = "{{auto}}"

[stages.cam-calibrate]
command = "calibrate"
dataset = "{{variables.dataset}}"
input-plex = "{{cam-learn.output-plex}}"
input-object-space = "{{variables.object-space}}"
# ...other options
detections = "{{auto}}"
results = "{{auto}}"

If successful, this process will output our usual results.mcap file. We can then extract the optimized plex later in the manifest. Your reference plex will now show up in the directory {workspace}/plex-mst-stage/output-plex.json.

single_cam_reference_creation.toml
[stages.plex-mst-stage]
command = "plex-mst"
input-plex = "{{cam-calibrate.results}}"
output-plex = "{{auto}}"

There are several different ways to extract a plex from a results.json, depending on your goals. Check those out in the plex command documentation.

Creating A Reference Object Space

In a multi-board scenario where a consolidated object space is beneficial (like narrow field-of-view camera calibration), it can be beneficial to use a reference object space as well.

In this case, you'll just add one more stage to the end of your manifest:

single_cam_reference_creation.toml
[stages.consolidate]
command = "consolidate"
input-object-space = "{{cam-calibrate.results}}"
consolidated-object-space = "{{auto}}"
overwrite = true

That consolidate object space will output to {workspace}/consolidate/consolidated-object-space.json. This file is valid as long as your calibration targets do not move.

We're Serious About This

Consolidated Object Spaces are analogous to a plex's spatial constraints: they are only accurate if the targets don't move. If you need a consolidated object space but don't have a static target field, do not use a reference. Instead, re-run consolidation on every calibration.

Steady-state Process

We now have two files in our possession:

  • {workspace}/plex-mst-stage/output-plex.json: our reference plex based on a previous successful calibration
  • {workspace}/consolidate/consolidated-object-space.json: our reference consolidated object space.

Great! We can create our steady-state manifest now.

single_cam_steady_state.toml
# Project Settings...

[stages.copy-reference]
command = "plex-copy"
# points to reference plex
input-plex = "./plex-mst-stage/output-plex.json"
regenerate-uuids = true
rename-topic = []
output-plex = "{{auto}}"

[stages.cam-calibrate]
command = "calibrate"
dataset = "{{variables.dataset}}"
# points to copied reference plex + new UUIDs
input-plex = "{{copy-reference.output-plex}}"
# points to reference object space
input-object-space = "./consolidate/consolidated-object-space.json"
skip-intrinsics-search = true
# ...other options
detections = "{{auto}}"
results = "{{auto}}"

That's a lot simpler, right? Instead of creating a naive plex with new and learn every time, we can instead start with values that roughly match what we would expect based on our reference calibration. We can use this format forevermore.

New UUIDs, New Components

This process copies the reference plex before passing it to the calibration command. We do this specifically for one flag: regenerate-uuids. This flag assigns all-new UUIDs to every component in the plex.

New UUIDs represent new components, indicating that this plex isn't calibrating the same system as the reference plex did. This makes it trivial to track a specific deployed component's calibration over time: just find and match the UUIDs across plexes.

Of course, if you need to re-run a calibration for a specific system, you can always pass its reference plex directly to the calibrate command or set regenerate-uuids = false.

We also passed an extra flag to the calibrate command: skip-intrinsics-search. In naive circumstances, MetriCal will perform a large intrinsics search on all cameras to compensate for poor seeds and longer-than-usual focal lengths. But we don't need that! We already have rough values for our intrinsics, so we can skip this search entirely.

If you forget to pass this flag into your steady-state manifest, don't panic; you should still get great calibrations. The flag will just save a little time.