Arm with gripper and wrist camera
A table-mounted arm, a gripper bolted to the end effector, and a camera clamped near the wrist is the most common manipulation setup. The camera moves with the arm, so its view is always centered on wherever the arm is reaching. For the motion service and vision pipelines to know that, that the camera is on the arm, not separate from it, you have to tell the frame system three relationships: arm-to-world, gripper-to-arm, and camera-to-arm. This guide walks through all three.
Frame hierarchy
world
├── my-arm
│ ├── my-gripper (attached to arm)
│ └── my-camera (mounted on arm)
└── table-surface
The arm is a direct child of the world frame. The gripper and camera are both children of the arm, so they move with the arm automatically as it changes position.
Steps
1. Choose your world frame origin
Every frame on the machine will be defined relative to the world frame, so the
origin is the point you measure from. Two good choices for a table-mounted
arm: the arm’s base, or a corner of the table. Using the arm base as the
origin means the arm’s translation is (0, 0, 0) and every other measurement
starts from the arm. Using a table corner gives you a physical landmark (you
can literally see the corner) at the cost of measuring arm-base-to-corner
first. Either way, mark the origin physically; you will refer to it every
time you add or adjust a frame.
2. Add a frame to the arm
In the Viam app, navigate to your machine and click the CONFIGURE tab. In the sidebar, click your arm component to open its card. On the card, click Frame.
The Frame section opens a JSON editor (there is no form, no parent dropdown, or geometry-type picker). Edit the JSON directly.
If the arm base is your world frame origin:
{
"parent": "world",
"translation": { "x": 0, "y": 0, "z": 0 },
"orientation": {
"type": "ov_degrees",
"value": { "x": 0, "y": 0, "z": 1, "th": 0 }
}
}
If the world frame origin is at a table corner and the arm base is 300 mm to the right and 250 mm forward from that corner:
{
"parent": "world",
"translation": { "x": 300, "y": 250, "z": 0 },
"orientation": {
"type": "ov_degrees",
"value": { "x": 0, "y": 0, "z": 1, "th": 0 }
}
}
Click Save in the top-right of the page (or press ⌘/Ctrl+S).
3. Verify the axes point the way you expect
Before adding more frames, confirm the arm’s coordinate axes line up with your
world frame. In the CONTROL tab, jog the end effector a small amount in
+z. If the arm moves up, z matches the Viam convention. Repeat for +x and +y.
An axis that points the wrong way will make every subsequent frame offset
wrong in the same direction, so fix it now by rotating the arm’s orientation
to flip the bad axis.
For example, if the arm’s +x points opposite to your intended +x, rotate 180 degrees around the z axis:
{
"orientation": {
"type": "ov_degrees",
"value": { "x": 0, "y": 0, "z": 1, "th": 180 }
}
}
4. Add a frame to the gripper
In the sidebar, click your gripper component to open its card. On the card, click Frame.
Pick where the gripper frame origin sits
Choose a point on the gripper as the frame origin. It is up to you, but a point near the center of the gripper jaws is usually the most convenient choice: when you later call the motion service to move the gripper to a target pose, the point you pick here is what gets moved to that pose.
Configure the frame
In the JSON, set parent to your arm’s component name, translation to
the gripper frame origin’s offset in mm from the arm’s end effector, and
orientation to the gripper’s rotation relative to the arm.
If the gripper attaches directly to the arm’s end effector with no adapter plate and no rotation, use a zero offset:
{
"parent": "my-arm",
"translation": { "x": 0, "y": 0, "z": 0 },
"orientation": {
"type": "ov_degrees",
"value": { "x": 0, "y": 0, "z": 1, "th": 0 }
}
}
If the gripper is mounted through an adapter plate or flange that adds height, set the z translation to the adapter height in millimeters. For example, with a 50 mm adapter plate:
{
"parent": "my-arm",
"translation": { "x": 0, "y": 0, "z": 50 },
"orientation": {
"type": "ov_degrees",
"value": { "x": 0, "y": 0, "z": 1, "th": 0 }
}
}
Click Save.
Check whether the gripper has a kinematics file
Some grippers ship with a kinematics file that describes the position of the jaws as they open and close, along with collision geometry for the jaw linkages. If the gripper has one, the motion planner already knows the gripper’s volume and you do not need to add collision geometry to the gripper frame.
Call GetKinematics on the gripper (or check the module source) to see
whether kinematics are provided. If they are, verify that the gripper
renders as expected in the 3D SCENE tab. If it does, you are done.
If the gripper does not have a kinematics file and you want the planner
to avoid collisions with the gripper body, add a geometry field to the
gripper’s frame describing its physical volume. See
Define obstacles
for the pattern.
5. Add a frame to the wrist camera
In the sidebar, click your camera component to open its card. On the card, click Frame.
The camera’s parent is the arm, not the world frame. Measure the offset from the arm’s end effector to the camera’s optical center. For a camera mounted 30 mm to the side and 60 mm above the end effector:
{
"parent": "my-arm",
"translation": { "x": 0, "y": 30, "z": 60 },
"orientation": {
"type": "ov_degrees",
"value": { "x": 0, "y": 0, "z": 1, "th": 0 }
}
}
If the camera is tilted downward (for example, angled 30 degrees toward the gripper), add a rotation around the x axis:
{
"parent": "my-arm",
"translation": { "x": 0, "y": 30, "z": 60 },
"orientation": {
"type": "ov_degrees",
"value": { "x": 1, "y": 0, "z": 0, "th": -30 }
}
}
Click Save.
6. Visualize the frame system
- Navigate to the 3D SCENE tab in the Viam app.
- The viewer renders all configured frames in 3D space. Each frame appears as a set of colored axes (red = x, green = y, blue = z).
- Verify that the gripper and camera frames are attached to the arm and move with it.
- Check that the positions and orientations match your physical setup.
If a frame appears in the wrong position, return to the CONFIGURE tab and adjust the translation or orientation values.
7. Verify with TransformPose
Use the TransformPose API to confirm that the frame system computes correct transforms between frames.
Express the camera’s origin (0, 0, 0) in the camera frame and transform it to the world frame.
The result should match the physical position of the camera.
For details on the TransformPose API, see Frame system: TransformPose.
Troubleshooting
What’s next
Was this page helpful?
Glad to hear it! If you have any other feedback please let us know:
We're sorry about that. To help us improve, please tell us what we can do better:
Thank you!