Skip to main content
Manipulation and Head control the robot’s arm and head. Use them for manipulation tasks, gestures, and camera positioning.

Manipulation

The arm SDK blocks and raises. Every motion returns only once the arm has settled, and a motion that fails raises rather than returning False — so the happy path reads as a straight line and a failure can’t be silently ignored: Both are importable from innate.exceptions. If you don’t catch them, the run reports FAILURE with the message — usually exactly what you want.

Methods

Cartesian motion

Positions are metres in base_link, orientations are radians:
move_to is FK-verified: after the motion it reads the real pose back, and if the arm is off target by more than the tolerance it recovers (reboot + torque on) and retries once before raising ArmUnhealthy. Pass tolerance_xy=None, tolerance_z=None to skip verification when contact is expected to stop the arm early. move_by nudges from the arm’s measured pose rather than its last commanded one, which is the shape a visual-servoing loop wants:

Trajectories

follow() sweeps through waypoints as one smooth motion, starting from wherever the arm currently is:
A trajectory is deliberately not pose-verified: touching something along the path is legitimate. One smooth follow() also beats a chain of move_to calls for anything gesture-like — the stop-and-go between separate moves is visible.

Joint motion

The gripper and the standing grip

strength is radians of preload past the closed stop, clamped to GRIPPER_MAX_STRENGTH (0.6) — beyond that the servo overcurrent-trips on a real object.
The claw runs under current-based position control, so the standing position error is the grip force. The interface remembers the last commanded claw position and carries it through every later motion, so an object stays held while the arm travels. You never thread a gripper= argument through a trajectory, and re-reading the measured claw position — which would drop the object — is not something you can accidentally do.
A blocking gripper_open() verifies the claw actually moved, since a tripped servo can stay shut, and reboots and retries once before raising ArmUnhealthy.

Doing something else while the arm moves

Pass block=False to return as soon as the command is accepted, then join with wait():
A non-blocking motion is unverified until joinedwait() is what surfaces a failure. Issuing a new command supersedes an unjoined motion.

Reading the arm

self.manipulation.pose returns an Arm — the same type as the ambient arm: Arm state feed, so a control loop reads one shape everywhere:

Servo power and recovery

These return bool rather than raising. move_to and gripper_open already call recover() for you on their retry path.
Committed physical actions must not be cancellable. Once the gripper has closed on an object, unwinding mid-grip drops it on the floor. In a section like that — and only there — use time.sleep deliberately and say so in a comment, or the next reader will “fix” it back to self.sleep and reintroduce the bug. Everywhere else, self.sleep.

Methods

Tilt angles

set_position returns immediately; the head takes a moment to arrive. If the next step depends on the new view, sleep first:
Read the current tilt back with the head_position: HeadState feed.

Example: a two-stroke wave

One follow() per stroke rather than two move_to calls, so the arm sweeps instead of stopping at each end. No cancel() method and no cancellation checks: a Stop raises out of the blocking motion, and the framework halts the arm.

Example: look, then pick

The lift after the close carries the object because the standing grip travels with the motion — nothing re-commands the claw.