Previous
Fragments
Add a robotic arm to your machine’s configuration so you can control it from the Viam app and from code.
An arm component controls a multi-jointed robotic arm. The API provides:
Arm models almost always come from modules in the registry because each arm manufacturer has its own communication protocol. Common models include:
| Module | Models |
|---|---|
| UFactory | xArm6, xArm7, xArm850, lite6 |
| Universal Robots | ur3e, ur5e, ur7e, ur20 |
The fake built-in model is useful for testing code without physical hardware.
It supports kinematics for several arm models (ur5e, ur20, xarm6, xarm7, lite6).
my-arm) and click Create.Attributes vary by arm module. Most network-connected arms need a host address:
UFactory xArm 6:
{
"host": "192.168.1.100",
"speed_degs_per_sec": 30,
"acceleration_degs_per_sec_per_sec": 100
}
| Attribute | Type | Required | Description |
|---|---|---|---|
host | string | Yes | IP address of the arm’s control box. You can find this on a sticker on the control box or in the manufacturer’s network settings. |
speed_degs_per_sec | float | No | How fast joints rotate, in degrees per second. Range: 3-180. Default: 60. Start low (15-30) when testing a new setup. Increase once you’re confident in your configuration and collision boundaries. |
acceleration_degs_per_sec_per_sec | float | No | How quickly joints ramp up to speed, in degrees per second squared. Range: 0-1145. Default: ~382. Lower values produce smoother, more predictable motion, which matters when the arm carries a payload or works near obstacles. Most users don’t need to change this unless motion is too jerky or too sluggish. |
These are common attributes for the UFactory xArm module. Check your module’s documentation in the registry for the full list of attributes. For example, see the Universal Robots module.
If you plan to use motion planning or coordinate with other components (like a camera or gripper), add a frame to define the arm’s position in your workspace.
This frame places the arm’s base at the world origin, which is common for single-arm setups:
{
"frame": {
"parent": "world",
"translation": { "x": 0, "y": 0, "z": 0 },
"orientation": {
"type": "ov_degrees",
"value": { "x": 0, "y": 0, "z": 1, "th": 0 }
}
}
}
For two-arm setups, one arm is typically at the world origin and the other is offset by its distance from the first arm in the x and y directions using the translation field.
See Frame System for details on configuring frames.
Click Save, then expand the TEST section.
To develop and test code without physical hardware, use the fake built-in
model:
{
"arm-model": "xarm6"
}
The fake arm simulates kinematics for the specified model. Set arm-model to ur5e, ur20, xarm6, xarm7, or lite6. It responds to all API calls and reports joint positions, but doesn’t move anything physical.
Read the arm’s current joint positions, move two joints, and confirm the positions changed.
To get the credentials for the code below, go to your machine’s page in the Viam app, click the CONNECT tab, and select SDK code sample. Toggle Include API key on. Copy the machine address, API key, and API key ID from the code sample.
If you’re using a real arm, you’ll see it physically move when you run the code below. With a fake arm, the positions update in memory without physical motion, but you can watch the joint values update in real time by expanding the test section on the arm’s component card in the CONFIGURE tab.
pip install viam-sdk
Save this as arm_test.py:
import asyncio
from viam.robot.client import RobotClient
from viam.components.arm import Arm
from viam.proto.component.arm import JointPositions
async def main():
opts = RobotClient.Options.with_api_key(
api_key="YOUR-API-KEY",
api_key_id="YOUR-API-KEY-ID"
)
robot = await RobotClient.at_address("YOUR-MACHINE-ADDRESS", opts)
arm = Arm.from_robot(robot, "my-arm")
# Read current joint positions
joints = await arm.get_joint_positions()
print(f"Joint positions before move: {joints.values}")
# Move joints 1 and 2
new_joints = JointPositions(values=[10, -10, 0, 0, 0, 0])
await arm.move_to_joint_positions(new_joints)
# Read joint positions again to confirm the move
joints = await arm.get_joint_positions()
print(f"Joint positions after move: {joints.values}")
await robot.close()
if __name__ == "__main__":
asyncio.run(main())
Run it:
python arm_test.py
You should see all zeros before the move, then joints 1 and 2 at 10 and -10 degrees:
Joint positions before move: [0, 0, 0, 0, 0, 0]
Joint positions after move: [10, -10, 0, 0, 0, 0]
mkdir arm-test && cd arm-test
go mod init arm-test
go get go.viam.com/rdk
Save this as main.go:
package main
import (
"context"
"fmt"
"go.viam.com/rdk/components/arm"
"go.viam.com/rdk/logging"
"go.viam.com/rdk/referenceframe"
"go.viam.com/rdk/robot/client"
"go.viam.com/rdk/utils"
)
func main() {
ctx := context.Background()
logger := logging.NewLogger("arm-test")
robot, err := client.New(ctx, "YOUR-MACHINE-ADDRESS", logger,
client.WithCredentials(utils.Credentials{
Type: utils.CredentialsTypeAPIKey,
Payload: "YOUR-API-KEY",
}),
client.WithAPIKeyID("YOUR-API-KEY-ID"),
)
if err != nil {
logger.Fatal(err)
}
defer robot.Close(ctx)
a, err := arm.FromProvider(robot, "my-arm")
if err != nil {
logger.Fatal(err)
}
// Read current joint positions
joints, err := a.JointPositions(ctx, nil)
if err != nil {
logger.Fatal(err)
}
fmt.Printf("Joint positions before move: %v\n", joints)
// Move joints 1 and 2
newJoints := []referenceframe.Input{
{Value: 10}, {Value: -10}, {Value: 0},
{Value: 0}, {Value: 0}, {Value: 0},
}
err = a.MoveToJointPositions(ctx, newJoints, nil)
if err != nil {
logger.Fatal(err)
}
// Read joint positions again to confirm the move
joints, err = a.JointPositions(ctx, nil)
if err != nil {
logger.Fatal(err)
}
fmt.Printf("Joint positions after move: %v\n", joints)
}
Run it:
go run main.go
You should see all zeros before the move, then joints 1 and 2 at 10 and -10 degrees.
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!