GuidesMove joints
Move joints safely
Set limits, move one joint or many, reach named poses with a smooth ramp, then hold or release the robot.
This guide takes you from a fresh session to smooth, repeatable joint moves. It works the same for an arm on your PC and for the full robot; the examples use the right arm's joint names, J01-R to J06-R.
Set the limits first
The robot does not move until your first motion command. Use that moment to set the two limits it enforces on its own side:
robot.safety.set_torque_fraction(0.4) # 40 % of each joint's current limit
robot.safety.set_max_joint_velocity(1.0) # rad/s at the jointEvery later command is clamped by these limits, however large a step you ask for. Start low and raise them once your code behaves.
Move one joint
A relative move adds to the current target:
robot.control.set_joints_relative({"J01-R": 0.1}) # base turns 0.1 radAn absolute move sets the target directly:
robot.control.set_joints_absolute({"J01-R": 0.0}) # base back to 0 radJoints you do not name keep their target. On the very first command both kinds start from the measured pose, so the arm never jumps to a stale target. The call returns at once; the joint runs to its target at no more than the velocity limit.
A joint name the robot does not have raises an error that lists the valid names. Either spelling works: the wire names (J01-R, J01-L) and the part names (right_J1, left_J1).
Move several joints at once
robot.control.set_joints_absolute({"J02-R": 1.1, "J03-R": -1.1, "J04-R": -0.9})All named joints start together, and each runs at up to the velocity limit, so short moves finish before long ones.
Reach a named pose
Every robot model declares two poses: zero, with the arms stretched straight out, and neutral, a folded ready pose.
poses = robot.model.named_poses() # {"zero": {...}, "neutral": {...}}
robot.control.set_joints_absolute(poses["neutral"])Move smoothly over a set time
A single absolute command makes every joint run at the velocity limit and stop abruptly. For a move that starts and ends gently and takes a known time, stream intermediate targets. This is what Go neutral does in the console:
import time
def move_to(target, seconds=2.0, hz=50):
start = robot.sensors.get_joints()
steps = int(seconds * hz)
for k in range(1, steps + 1):
a = k / steps
a = a * a * (3 - 2 * a) # smoothstep: gentle start and stop
robot.control.set_joints_absolute(
{j: start[j] + a * (target[j] - start[j]) for j in target})
time.sleep(1 / hz)
move_to(robot.model.named_poses()["neutral"], seconds=2.0)The ramp starts from the measured joints, not from an old target, so it is safe to call after the arm was moved by hand. Ctrl+C in the middle of it leaves the arm holding where it got to.
Know what you commanded
robot.control.get_commanded_joints() # the targets being sent right now
robot.sensors.get_joints() # where the joints actually areThe difference between the two is the tracking error. It is small at rest and grows while a joint is moving or pushing against something.
Hold, release, and let go
robot.control.stop() # disarm: the robot holds its pose, motion commands pause
robot.safety.torque_off() # release the motors: the arm sags under its weight
robot.safety.torque_on() # energise again: the arm holds where it is nowAfter stop() nothing moves until your next motion command, which re-arms the session and continues from the targets you had. torque_off() is the way to move an arm by hand; support it before you call it.
What happens if your script dies
Your session sends commands continuously in the background, at 90 Hz by default. If they stop arriving for 30 seconds, because your process crashed or the network went away, the robot server shuts down and the session ends. The drives keep their last target, so the arm stays where it was.
robot.actuation.get_frequency() # 90.0, the rate commands are resent at
robot.actuation.set_frequency(60)