Rigs_ImportCalibration
const url = 'https://junction-api.qualiastudios.dev/v1/accounts/example/rigs/example:importCalibration';const options = { method: 'POST', headers: {'X-API-Key': '<X-API-Key>', 'Content-Type': 'application/json'}, body: '{"name":"example","filename":"example","content":"example","etag":"example"}'};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request POST \ --url https://junction-api.qualiastudios.dev/v1/accounts/example/rigs/example:importCalibration \ --header 'Content-Type: application/json' \ --header 'X-API-Key: <X-API-Key>' \ --data '{ "name": "example", "filename": "example", "content": "example", "etag": "example" }'Imports a handheld rig’s TCP calibration from a tcp_calibration.json
and stores it on the rig, replacing any earlier one. The server reads the
file: each hand’s <side>.controller_to_tcp needs position_m (three
finite numbers) and R (a 3x3 rotation: finite, determinant within 0.01
of 1, and orthonormal to 1e-3, a stricter check than the determinant
alone); both hands are required, and other keys are ignored but the
end-effector ones. When the file’s ee_frame text names a link with
<side> in it, and each hand has controller_to_ee and ee_to_tcp poses
(passing the same checks) whose composition is its controller_to_tcp
(within 1 mm and 1e-3 per rotation entry), the calibration also gets
ee_link and Hand.ee_to_tcp. Otherwise it gets neither, with no error:
display-only numbers never refuse a file, and everything ingest reads
stands. A file that fails is INVALID_ARGUMENT, and the message names the
field, e.g. “left.controller_to_tcp.position_m must be three finite
numbers”. FAILED_PRECONDITION on an arm rig. ABORTED when etag is stale.
The calibration lives on a gripper (Robots.ImportGripperCalibration): one
imported here is carried to the rig’s gripper when that gripper stands
for the rig’s key or for none. A file that would reach no gripper (the rig
has none, its gripper stands for another rig’s key, or it is the built-in
UMI rig) is FAILED_PRECONDITION and stores nothing: import it on the
gripper, or set the rig’s gripper first.
Authorizations
Section titled “Authorizations ”Parameters
Section titled “ Parameters ”Path Parameters
Section titled “Path Parameters ”The account id.
The rig id.
Request Body required
Section titled “Request Body required ”Request for Rigs.ImportCalibration.
object
Accounts/{account}/rigs/{rig}
The file’s name, recorded with the calibration: 1 to 256 characters, not only spaces, no control characters. E.g. “tcp_calibration.json”.
The file’s text: a JSON object of at most 64 KiB.
The rig’s etag as read. When set and the rig has changed since, ABORTED.
Example generated
{ "name": "example", "filename": "example", "content": "example", "etag": "example"}Responses
Section titled “ Responses ”OK
A capture rig: what episodes are recorded with. An arm rig drives a fleet
robot; a handheld (UMI) rig carries trackers whose fixed mount to each
gripper root belongs here, not to any robot. Episodes name their
rig by key (Episode.rig).
The UMI handheld rig (key umi-handheld) is built in: every account lists
and reads it under its own name, and nobody edits or deletes it
(FAILED_PRECONDITION). An account rig with the same key takes its place, so
an account records its own calibration of its UMI pair by creating one, or
by setting a gripper on the built-in (UpdateRig), which makes that copy
itself. An account may delete its copy; it then lists the built-in again,
uncalibrated.
A handheld rig may carry the TCP calibration of its trackers (calibration)
and the design of its gripper (gripper, a Robot of kind GRIPPER): the
gripper’s URDF, with the calibration saying where on it the TCP is. Both are
a record on the rig: importing or replacing either changes no dataset.
Ingest reads only the calibration’s tracker_to_tcp, filename and
derive_date; the gripper design and the calibration’s end-effector fields
(ee_link, ee_to_tcp) are for display.
Served by fleet.
object
Accounts/{account}/rigs/{rig}
Human name.
What recordings carry as their rig id (meta/ingest.toml, LeRobot robot_type). Unique per account; 1 to 128 letters, digits, ’.’, ’_’, ’:’ or ’-’, starting with a letter or digit. Fixed at creation.
Arm or handheld.
Arm rig: accounts/{account}/robots/{robot} it drives, so its episodes get forward kinematics on that robot’s model. A robot (not a gripper design) of the rig’s own account; deleting the robot clears it. A handheld rig has none.
Handheld rig: one mount per tracked hand, at least one, at most one per side. An arm rig has none.
One tracker’s fixed mount.
object
Which hand.
The fixed mount: the tracker’s pose in its gripper root’s frame (x along
the fingers, y the way the jaw opens, z along the hinge): xyz in metres
and rpy in radians like every Transform. meta/ingest.toml’s
[rig.mount.
object
X, y, z in metres.
Roll, pitch, yaw in radians.
The gripper’s closure calibration: the raw angle fully open.
The raw angle shut.
When it was created.
When it last changed.
Sent back on UpdateRig.
Handheld rig: where each tracker’s TCP is, from the last ImportCalibration. Unset until one is imported, and never set on the built-in rig. Not editable through UpdateRig.
object
The uploaded file’s name, e.g. “tcp_calibration.json”.
When the calibration was made, “YYYY-MM-DD”: the leading date of the
file’s calibrated or else its derived text. Empty when the file gives
none.
Both hands, left then right.
One hand’s transform.
object
Which hand.
The TCP’s pose in the tracker’s frame: xyz in metres and rpy in radians (URDF convention, extrinsic XYZ), the file’s rotation matrix converted. A rotation whose pitch is within a hair of +-90 degrees has no unique roll and yaw; it is stored with roll 0.
object
X, y, z in metres.
Roll, pitch, yaw in radians.
The same TCP in the frame of the gripper design’s ee_link for this
hand, in the form of tracker_to_tcp (so the same pitch limit). Display
only. Set with ee_link or not at all, and only when it composes with
the file’s controller_to_ee to the file’s controller_to_tcp, which is
what makes the TCP drawn the TCP used.
object
X, y, z in metres.
Roll, pitch, yaw in radians.
The link of the rig’s gripper design that Hand.ee_to_tcp is a pose in,
with {side} for “left” or “right” (the gripper joint’s convention in
RobotModel.Gripper), e.g. “openarm_{side}_ee_base_link”. Read from the
file’s own description of its end-effector frame. Empty when the file
names none, lacks the end-effector poses, or they disagree with its
controller_to_tcp (then no ee_to_tcp is set either). Display only.
ee_to_tcp is drawn in this link’s own frame, which must be the plain
end-effector frame the file means. An export whose link sits elsewhere
draws the TCP off by the difference: an OpenArm exported with the xacro
default collapse_internal_empty_links puts openarm_
Whether this is a built-in rig (the UMI handheld rig): read only, the same in every account. Setting a gripper on one makes the account’s own copy.
Handheld rig: accounts/{account}/robots/{robot} of kind GRIPPER, the design of its gripper (a URDF, imported as a robot model). A design of the rig’s own account; deleting the design clears it. A design lives in one workspace, like any robot, and a rig in none, so this may name a design of any workspace of the account, and Robots.GetRobot finds it only from that workspace. Set with UpdateRig (CreateRig refuses it); on the built-in rig that makes the account’s own copy, uncalibrated. An arm rig has none; it names a robot.
Example
{ "kind": "KIND_UNSPECIFIED", "mounts": [ { "side": "SIDE_UNSPECIFIED" } ], "calibration": { "hands": [ { "side": "SIDE_UNSPECIFIED" } ] }}default
Section titled “default ”Default error response
A refusal from the API itself.
object
The gRPC code name in upper case, e.g. NOT_FOUND.
Whether the same call may succeed if retried.
Empty, except when a dataset copy or move is refused: then one google.rpc.ErrorInfo with the refusal’s reason code, its domain and, in metadata, the datasets it names.
object
A refusal from the gateway before the call reaches the API, for example 401 bad or missing API key, 403 key lacks the scope or environment closed, 413 body too large.
object
What went wrong.
Example generated
{ "code": "example", "message": "example", "retryable": true, "details": [ {} ]}