Robots_SetRobotGripper
const url = 'https://dev-api.qualiastudios.dev/v1/accounts/example/robots/example:setGripper';const options = { method: 'POST', headers: {'X-API-Key': '<X-API-Key>', 'Content-Type': 'application/json'}, body: '{"name":"example","gripper":"example","mounts":[{"side":"SIDE_UNSPECIFIED","parentLink":"example","matrix":{"m":[1]},"baked":true,"guessed":true}],"etag":"example","copyFrom":"example","exactMounts":true,"replaceExisting":true,"sourceTcpEtag":"example","sourceModelEtag":"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/robots/example:setGripper \ --header 'Content-Type: application/json' \ --header 'X-API-Key: <X-API-Key>' \ --data '{ "name": "example", "gripper": "example", "mounts": [ { "side": "SIDE_UNSPECIFIED", "parentLink": "example", "matrix": { "m": [ 1 ] }, "baked": true, "guessed": true } ], "etag": "example", "copyFrom": "example", "exactMounts": true, "replaceExisting": true, "sourceTcpEtag": "example", "sourceModelEtag": "example" }'Gives a robot a gripper, sets where its own gripper sits on each arm, or takes the gripper off, and returns the robot.
A robot owns its gripper. copy_from attaches a copy of a gripper (its
URDF, TCP and jaw as they are now: a snapshot, never re-synced) to a robot
that carries none; the copy is the robot’s own and editing it changes this
robot only. gripper names the robot’s own gripper, to set its mounts. A
robot that carries a gripper never swaps it: copy the robot to try another
gripper (FAILED_PRECONDITION). gripper naming a workspace-level
gripper on a robot that carries none attaches a copy of it, like copy_from
(until clients send copy_from). Neither field set takes the
gripper off, which deletes the robot’s own gripper and its files.
NOT_FOUND for a gripper of another workspace, like any robot of one.
FAILED_PRECONDITION when the named robot is not a gripper design, the robot
is not a robot, it carries a gripper and another is asked for, gripper
names another robot’s own gripper (use copy_from), the source has no URDF
or is being imported, a mount’s hand has no TCP in a link yet (that link is
what the mount places), or a baked mount’s URDFs disagree; INVALID_ARGUMENT
for a mount whose parent link the robot’s URDF lacks or is not joined to the
arm’s hand link by fixed joints, or for both gripper and copy_from.
(— api-linter: core::0134::synonyms=disabled
aip.dev/not-precedent: it is not an Update: UpdateRobot never touches the
gripper a robot carries, which has its own checks and etag. —)
Authorizations
Section titled “Authorizations ”Parameters
Section titled “ Parameters ”Path Parameters
Section titled “Path Parameters ”The account id.
The robot id.
Request Body required
Section titled “Request Body required ”Request for Robots.SetRobotGripper.
object
Accounts/{account}/robots/{robot}, of kind ROBOT.
The robot’s own gripper (Robot.CarriedGripper.gripper), to set its mounts.
Until a workspace is backfilled a robot may still carry a workspace-level
gripper it shared; naming that one updates its mounts too. Naming a
workspace-level gripper on a robot that carries none attaches a copy of it
(as copy_from does, with these mounts); any other is
FAILED_PRECONDITION: attach with copy_from. Empty with no copy_from
takes the robot’s gripper off and clears its mounts (an owned gripper is
deleted).
Where the gripper sits on each arm, one per side at most. With copy_from
and none given, the gripper is mounted at the hand link of every arm whose
hand it has a TCP in a link for, with no offset and marked guessed, unless
exact_mounts is true.
Where a gripper’s hand link sits on one arm.
object
Which arm.
The robot’s link the gripper hangs off: it must be joined to the arm’s
hand_link by fixed joints only.
The pose of the gripper’s hand link (its TCP link) in parent_link’s
frame. Ignored when baked. The mount is for the link the hand’s TCP is
in when it is set, which must exist then; a TCP set in another link
later composes nothing until the mount is set again.
object
M[0..3] is the first row, m[12..15] the last.
The robot’s own URDF already has the gripper built in, as the gripper
design has it: the mount is the identity on the robot’s link of the
gripper’s link name. The server checks that the two URDFs agree on where
that link is (FAILED_PRECONDITION when they do not) and sets
parent_link and matrix itself.
The mount was filled in by a guess and has not been confirmed.
Robot.CarriedGripper.etag as read; empty when the robot had none.
Accounts/{account}/robots/{robot}: a gripper design, or a robot whose own
gripper to copy, in the robot’s workspace. Only for a robot that carries no
gripper unless replace_existing is true. The copy takes the source’s URDF and meshes, TCP (hands, file,
date), jaw and name; it has no rig_key, but a source_rig_key when the
source had one. The source is not changed.
(— api-linter: core::0140::prepositions=disabled
aip.dev/not-precedent: the field is the source of a copy, and “copy_from”
is the name every client reads it by. —)
Use exactly the mounts supplied, including an empty list. Without this, copy_from retains its legacy default of guessed mounts when none are sent.
Replace the currently carried gripper atomically with a copy. Requires copy_from and the carried gripper etag; old clients retain attach-only behavior.
Source versions read before staging an exact copy, including an empty TCP etag when the source has no TCP. Checked before files or rows change.
Source model etag read before staging, checked before copying its files.
Responses
Section titled “ Responses ”OK
A robot the account keeps (fleet.robots). Episodes can be recorded on it (Dataset.source_robot) and played on it (Dataset.target_robot).
A gripper design (kind GRIPPER) is the same record: a URDF of one gripper
with the TCP of each hand on it (gripper_tcp, QUA-3188), which is also what
a handheld rig’s trackers are calibrated to and what Rig.gripper points at
(ML-316). Everything that takes a robot name works on it (upload, model,
delete), but it is no robot: ListRobots leaves it out unless asked for
grippers, so it is never the default target, and Robot.config_digest is
always empty (its RobotModel keeps one), so no reach result is keyed by it.
It lives in one workspace. ListRobots with kind = GRIPPER makes the
workspace’s “UMI handheld” gripper (rig_key “umi-handheld”) on first ask: an
ordinary row, editable and deletable like any other.
A robot carries a gripper on its flanges (carried_gripper), set with
SetRobotGripper, and owns it: attaching a gripper copies its URDF, TCP and jaw
into a gripper design of the robot’s own (owner names the robot), so editing
it changes that robot only. An owned gripper is carried by its owner alone, is
included when listing grippers as a source to copy, is deleted with its robot,
by detaching it, or by DeleteRobot on the gripper, and
needs robots:write to change (a workspace-level gripper, such as the
handheld’s, needs rigs:write). Everything that takes a gripper name works on
it (GetRobot, the model, SetGripperTcp, ImportGripperCalibration).
Served by fleet.
object
Accounts/{account}/robots/{robot}
Unique per account.
The robot type key (fleet.robot_type.key), e.g. “openarm”, “franka_fr3”. Empty for a robot defined only by an imported URDF.
Serial number, free text.
Whether the robot has a model (URDF) configured.
RobotModel.config_digest, empty without a model: what reach results are keyed by. Always empty for a gripper design.
When it was registered.
When it last changed.
Sent back on UpdateRobot.
Robot or gripper design. Set by CreateRobot (unset is ROBOT) and never changed.
Kind GRIPPER only: its TCPs.
object
The hands that have a TCP: none, one or both sides.
One hand’s TCP.
object
Which hand.
The link of the gripper’s URDF that tcp is expressed in, with the
side resolved (“openarm_left_ee_base_link”). Empty when the hand has
only a tracker_to_tcp.
The TCP’s pose in link’s frame. Empty when the hand has none.
object
M[0..3] is the first row, m[12..15] the last.
The TCP’s pose in the tracker’s frame: what uploads are resolved with. Unset when the hand has none, so it serves robots only. (— api-linter: core::0140::prepositions=disabled aip.dev/not-precedent: a transform is named by its direction, as TcpCalibration.Hand.tracker_to_tcp is. —)
object
X, y, z in metres.
Roll, pitch, yaw in radians.
The hands’ tracker_to_tcp as a calibration, the form
Datasets.ResolvePoses takes. Set only when resolves_uploads.
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_
The ingest rig kind this gripper stands for, e.g. “umi-handheld”: what
Dataset.poses_rig names. Unique among workspace-level grippers. Empty for
a gripper that stands for none, and for the gripper a robot owns (see
source_rig_key); a TCP on it serves robots only.
The calibration file’s name, or “tcp-matrix:
“YYYY-MM-DD”: the file’s, or the day the matrix was set (UTC). Empty when a file gave none.
Whether the gripper’s URDF changed since a TCP in a link was set: a re-import changed its digest or took the link away. A stale TCP is shown with a warning and is never composed into a robot’s arm; set it again, every hand that has one in the same call.
Robots can use it: every hand has its TCP in a link and it is not stale.
Uploads can be resolved with it: both hands have a tracker_to_tcp, and
the calibration has a file name and a date.
Sent back on SetGripperTcp and ImportGripperCalibration.
The rig_key of the gripper this one was copied from, e.g.
“umi-handheld”, for the gripper a robot owns; empty otherwise. It says
the gripper is a copy of that rig’s gripper (and so has its controller to
TCP transform), which a copy cannot claim as its own rig_key: uploads are
resolved with the gripper that holds the key.
Kind ROBOT only: the gripper it carries, if any.
object
Accounts/{account}/robots/{robot} of kind GRIPPER, in the robot’s
workspace: the robot’s own gripper (its owner is the robot).
One per arm side the gripper is mounted on.
Where a gripper’s hand link sits on one arm.
object
Which arm.
The robot’s link the gripper hangs off: it must be joined to the arm’s
hand_link by fixed joints only.
The pose of the gripper’s hand link (its TCP link) in parent_link’s
frame. Ignored when baked. The mount is for the link the hand’s TCP is
in when it is set, which must exist then; a TCP set in another link
later composes nothing until the mount is set again.
object
M[0..3] is the first row, m[12..15] the last.
The robot’s own URDF already has the gripper built in, as the gripper
design has it: the mount is the identity on the robot’s link of the
gripper’s link name. The server checks that the two URDFs agree on where
that link is (FAILED_PRECONDITION when they do not) and sets
parent_link and matrix itself.
The mount was filled in by a guess and has not been confirmed.
Sent back on SetRobotGripper.
The gripper’s display name (the one it was copied from), so a robot can say “gripper: KIWI gripper pair” without reading the gripper.
Kind GRIPPER only: accounts/{account}/robots/{robot} of the robot that
owns this gripper. Empty for a workspace-level gripper (the handheld’s, or
a library gripper). Set on CreateRobot (with kind GRIPPER) to make the
named robot’s own gripper, which that robot carries from then on (with no
mounts); never changed after.
(— api-linter: core::0121::no-mutable-cycles=disabled
aip.dev/not-precedent: owner is set once, on CreateRobot, and
never changed (it is IMMUTABLE), so the Robot to Robot reference
cannot form a cycle: a gripper names the robot that owns it, and a
robot never has an owner. —)
The server supports exact staged gripper copy and fenced replacement. Absent on older servers; clients must not assume those guarantees.
The sha256 (lowercase hex) of the robot’s thumbnail, a picture of its model at the default pose that fleet draws when the model is imported (the bytes are GetRobotThumbnail’s). Empty when it has none yet, or when the model was replaced since it was drawn, so a replaced model never shows its old picture.
Example
{ "kind": "KIND_UNSPECIFIED", "gripperTcp": { "hands": [ { "side": "SIDE_UNSPECIFIED" } ], "calibration": { "hands": [ { "side": "SIDE_UNSPECIFIED" } ] } }, "carriedGripper": { "mounts": [ { "side": "SIDE_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" } ]}