Datasets_ResolvePoses
const url = 'https://dev-api.qualiastudios.dev/v1/accounts/example/datasets/example:resolvePoses';const options = { method: 'POST', headers: {'X-API-Key': '<X-API-Key>', 'Content-Type': 'application/json'}, body: '{"name":"example","posesAt":"POSES_AT_UNSPECIFIED","calibration":{"filename":"example","deriveDate":"example","hands":[{"side":"SIDE_UNSPECIFIED","trackerToTcp":{"xyz":[1],"rpy":[1]},"eeToTcp":{"xyz":[1],"rpy":[1]}}],"eeLink":"example"},"mergedSettingsContext":{"mergedDataset":"example","members":["example"],"membershipRevision":"example","etag":"example","sourceCommit":"example"},"requestId":"example","validateOnly":true,"members":["example"],"membershipRevision":"example","expectedOperationId":"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://dev-api.qualiastudios.dev/v1/accounts/example/datasets/example:resolvePoses \ --header 'Content-Type: application/json' \ --header 'X-API-Key: <X-API-Key>' \ --data '{ "name": "example", "posesAt": "POSES_AT_UNSPECIFIED", "calibration": { "filename": "example", "deriveDate": "example", "hands": [ { "side": "SIDE_UNSPECIFIED", "trackerToTcp": { "xyz": [ 1 ], "rpy": [ 1 ] }, "eeToTcp": { "xyz": [ 1 ], "rpy": [ 1 ] } } ], "eeLink": "example" }, "mergedSettingsContext": { "mergedDataset": "example", "members": [ "example" ], "membershipRevision": "example", "etag": "example", "sourceCommit": "example" }, "requestId": "example", "validateOnly": true, "members": [ "example" ], "membershipRevision": "example", "expectedOperationId": "example" }'Resolves a handheld upload’s poses at each hand’s TCP (poses_at TCP), or
undoes that (ROOT), by re-deriving the upload with the calibration. It
adds the <side>.tcp.* channels beside the gripper root’s <side>.ee.*,
which stay exactly as they were.
The work is accepted, not finished: it is a new publication of the whole upload, as long as the upload’s video takes to check again, so the response says only that it started. Poll it through the sync-status route (GET /datasets/sync-status/{operation_id}) until it is finalized or failed; GetDataset then shows the new poses_at. The old version stays readable, and a version tag or pin cut earlier keeps the poses it had. Resolving again with a replaced calibration is the same call; the same calibration again, or ROOT on an upload not at the TCP, changes nothing and answers no_op.
One re-derive of an upload runs at a time (this call, ReingestDataset and the dashboard’s automatic resolve after a robot is picked all admit one). The same resolve asked while it runs answers the running operation, which is the one to poll; a different one (an undo of it, another calibration) is FAILED_PRECONDITION until it lands. There is no way to cancel one.
Only the poses at the TCP are stored. What reads them is not part of this
call: a reach check still walks the gripper root, and an export carries
<side>.ee.* as before.
FAILED_PRECONDITION unless the dataset is a LeRobot upload recorded on a handheld rig whose state ingest converted (or a raw MCAP recording with tracked poses); also on a mixture, a merged dataset (QUA-3511: every upload of a merge sits at one frame, so resolve them before they merge) and an upload merged into one (naming it). The re-derive is built on the version that was current when it started, so it fails (the status route says so) when a review ruling or a trim lands first: resolve again.
Authorizations
Section titled “Authorizations ”Parameters
Section titled “ Parameters ”Path Parameters
Section titled “Path Parameters ”The account id.
The dataset id.
Request Body required
Section titled “Request Body required ”Request for Datasets.ResolvePoses.
object
Accounts/{account}/datasets/{dataset}
TCP to resolve at the TCP (again, with a replaced calibration, when the upload is there already), ROOT to undo that. Unspecified is INVALID_ARGUMENT. (— api-linter: core::0140::prepositions=disabled aip.dev/not-precedent: the same name as Dataset.poses_at. —)
The calibration to resolve with: required for TCP, empty for ROOT. The caller supplies it, normally the calibration of the rig the upload was recognised as recorded on (Dataset.poses_rig; not a dataset label, those are editable, and not Episode.rig, which ingest may not have used), and it is recorded in the upload’s meta/ingest.toml, so the upload does not follow the rig afterwards. Both hands, every number finite. INVALID_ARGUMENT otherwise.
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. (— api-linter: core::0140::prepositions=disabled aip.dev/not-precedent: a transform is named by its direction, as the calibration file’s own controller_to_tcp is. —)
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.
(— api-linter: core::0140::prepositions=disabled
aip.dev/not-precedent: a transform is named by its direction, as
the calibration file’s own ee_to_tcp is. —)
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_
Explicitly confirmed correction of one eligible LeRobot merged member. Its training adapter must consume observation.tcp and its own recognized rig supplies calibration; one rig’s calibration must not be fanned out to heterogeneous uploads. Omitted retains the old merged-away restriction.
object
The merged dataset whose membership was confirmed.
Every upload it held, in merge order, including the selected upload.
Opaque merge/undo epoch from GetDataset; never recapture it on retry.
The dataset’s editable etag; a member edit sends the selected upload’s.
The selected upload’s exact paired head. Empty on the merged dataset’s context, which is used only to fence global label edits.
UUID retry key, required with merged_settings_context.
Merged datasets only: run the same per-upload admission checks without creating a lease or dispatching ingest. All blockers are returned by name; a later move rechecks them. Standalone uploads retain their existing API.
Merged only: retry these held uploads. Empty selects every current member.
Required for a merged move (not validate_only), from GetDataset’s pose_preparation. No caller calibration is accepted for merged datasets: the server reads each upload’s own recognized rig’s current calibration.
Resume only this existing pose operation; never admit a new move.
Responses
Section titled “ Responses ”OK
Response for Datasets.ResolvePoses.
object
The re-derive’s operation id, to poll through GET /datasets/sync-status/{operation_id}. Empty when no_op is set.
True when nothing needed doing (the upload already stands as asked), so there is nothing to wait for.
Merged dataset only: one result per upload. Eligible uploads may proceed despite other blockers. Retry joins the same running work; no batch job or implicit pose move on a robot selection is created.
object
Opaque ordered-membership epoch. Send it back on an explicit move or removal; a changed membership is ABORTED and must be confirmed again.
Number already at their current capture calibration.
Number of uploads in the current membership.
Preparation state for each upload, in membership order.
One upload’s current state. Names and reasons are suitable for the merged dataset’s pre-check; no source data or historical rulings are removed.
object
The source upload resource.
The source upload’s display name.
The upload’s current preparation state.
Human-readable explanation when applicable.
UUID polled at this upload’s reingests/{operation_id} resource.
Recording rig, never an editable collection tag or playback robot.
The capture gripper supplying the calibration.
The capture calibration’s version.
Preparation is complete and its publication is pending; it cannot be cancelled.
False when none of these recordings has handheld poses to prepare.
Example
{ "mergedPreparation": { "members": [ { "state": "STATE_UNSPECIFIED" } ] }}default
Section titled “default ”Default error response
The Status type defines a logical error model that is suitable for different programming environments, including REST APIs and RPC APIs. It is used by gRPC. Each Status message contains three pieces of data: error code, error message, and error details. You can find out more about this error model and how to work with it in the API Design Guide.
object
The status code, which should be an enum value of [google.rpc.Code][google.rpc.Code].
A developer-facing error message, which should be in English. Any user-facing error message should be localized and sent in the [google.rpc.Status.details][google.rpc.Status.details] field, or localized by the client.
A list of messages that carry the error details. There is a common set of message types for APIs to use.
Contains an arbitrary serialized message along with a @type that describes the type of the serialized message.
object
The type of the serialized message.
Example generated
{ "code": 1, "message": "example", "details": [ { "@type": "example" } ]}