> ## Documentation Index
> Fetch the complete documentation index at: https://innateinc-docs-skills-0-7-interface.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Body Control Interfaces

export const HeadTiltAnglesTable = () => {
  const rows = [{
    angle: "-25deg",
    view: "Floor and objects below."
  }, {
    angle: "0deg",
    view: "Straight ahead."
  }, {
    angle: "+15deg",
    view: "Faces and shelves above."
  }];
  return <div className="interface-methods-table-wrap">
      <table className="interface-methods-table">
        <thead>
          <tr>
            <th>Angle</th>
            <th>View</th>
          </tr>
        </thead>
        <tbody>
          {rows.map(row => <tr key={row.angle}>
              <td>
                <span className="interface-param-badge">{row.angle}</span>
              </td>
              <td>{row.view}</td>
            </tr>)}
        </tbody>
      </table>
    </div>;
};

export const HeadInterfaceMethods = () => {
  const rows = [{
    method: "set_position()",
    params: [{
      name: "angle_degrees",
      type: "int"
    }],
    desc: "Set head tilt angle (from -25deg to +15deg). Non-blocking — the head takes a moment to arrive, so self.sleep() after it if the next step needs the new view."
  }];
  return <div className="interface-methods-table-wrap">
      <table className="interface-methods-table">
        <thead>
          <tr>
            <th>Method</th>
            <th>Parameters</th>
            <th>Description</th>
          </tr>
        </thead>
        <tbody>
          {rows.map(row => <tr key={row.method}>
              <td>
                <span className="interface-method-pill">{row.method}</span>
              </td>
              <td>
                {row.params?.length ? <div className="interface-method-params">
                    {row.params.map(param => <span key={`${row.method}-${param.name}`} className="interface-param-badge">
                        {param.name}
                        {param.type ? <span className="interface-param-type">: {param.type}</span> : null}
                      </span>)}
                  </div> : <span className="interface-no-params">None</span>}
              </td>
              <td>{row.desc}</td>
            </tr>)}
        </tbody>
      </table>
    </div>;
};

export const ManipulationInterfaceMethods = () => {
  const rows = [{
    method: "move_to()",
    params: [{
      name: "x, y, z",
      type: "float (metres)"
    }, {
      name: "roll, pitch, yaw",
      type: "float (radians)"
    }, {
      name: "duration",
      type: "float (seconds)"
    }, {
      name: "grip",
      type: "float | None"
    }, {
      name: "block",
      type: "bool"
    }],
    desc: "Move the end-effector to a pose in base_link and return the settled Arm. FK-verified: an off-target result recovers and retries once, then raises ArmUnhealthy. Unreachable raises ArmFailed."
  }, {
    method: "move_by()",
    params: [{
      name: "dx, dy, dz",
      type: "float (metres)"
    }, {
      name: "droll, dpitch, dyaw",
      type: "float (radians)"
    }, {
      name: "duration",
      type: "float (seconds)"
    }, {
      name: "block",
      type: "bool"
    }],
    desc: "Nudge the end-effector by offsets from its measured pose — the shape a visual-servoing loop wants."
  }, {
    method: "follow()",
    params: [{
      name: "waypoints",
      type: "Sequence[Waypoint]"
    }, {
      name: "grip",
      type: "float | None"
    }, {
      name: "block",
      type: "bool"
    }],
    desc: "Sweep through waypoints as one smooth trajectory from wherever the arm is. Unverified — contact along the path is legitimate."
  }, {
    method: "move_joints()",
    params: [{
      name: "joints",
      type: "Sequence[float] (5 or 6)"
    }, {
      name: "duration",
      type: "float (seconds)"
    }, {
      name: "block",
      type: "bool"
    }],
    desc: "Move to joint positions in radians. 5 values keep the standing grip; 6 set it."
  }, {
    method: "rest()",
    params: [{
      name: "duration",
      type: "float (seconds)"
    }],
    desc: "Fold to the rest pose, keeping the grip. Safe while holding an object."
  }, {
    method: "gripper_open()",
    params: [{
      name: "percent",
      type: "float (0-100)"
    }, {
      name: "duration",
      type: "float (seconds)"
    }, {
      name: "block",
      type: "bool"
    }],
    desc: "Open the claw. A blocking open verifies the claw moved, rebooting and retrying once before raising ArmUnhealthy."
  }, {
    method: "gripper_close()",
    params: [{
      name: "strength",
      type: "float (radians of preload)"
    }, {
      name: "duration",
      type: "float (seconds)"
    }, {
      name: "block",
      type: "bool"
    }],
    desc: "Close the claw. strength is grip preload past the closed stop, clamped to GRIPPER_MAX_STRENGTH, and becomes the standing grip target."
  }, {
    method: "wait()",
    params: [{
      name: "timeout",
      type: "float | None"
    }],
    desc: "Join the in-flight block=False motion and return the settled Arm. Raises ArmFailed on failure or timeout."
  }, {
    method: "moving",
    params: [],
    desc: "Property: True while a non-blocking motion is still in flight."
  }, {
    method: "pose",
    params: [],
    desc: "Property: the current end-effector pose as an Arm — the same type as the ambient arm: Arm feed."
  }, {
    method: "torque_on() / torque_off()",
    params: [],
    desc: "Power the servos on or off. Return bool rather than raising."
  }, {
    method: "reboot_servos()",
    params: [],
    desc: "Reboot the servos to clear hardware errors; leaves torque off. Returns bool."
  }, {
    method: "recover()",
    params: [],
    desc: "Reboot, re-enable torque and settle (~2.5 s), preserving the standing grip so a mid-pick retry keeps its preload."
  }];
  return <div className="interface-methods-table-wrap">
      <table className="interface-methods-table">
        <thead>
          <tr>
            <th>Method</th>
            <th>Parameters</th>
            <th>Description</th>
          </tr>
        </thead>
        <tbody>
          {rows.map(row => <tr key={row.method}>
              <td>
                <span className="interface-method-pill">{row.method}</span>
              </td>
              <td>
                {row.params?.length ? <div className="interface-method-params">
                    {row.params.map(param => <span key={`${row.method}-${param.name}`} className="interface-param-badge">
                        {param.name}
                        {param.type ? <span className="interface-param-type">: {param.type}</span> : null}
                      </span>)}
                  </div> : <span className="interface-no-params">None</span>}
              </td>
              <td>{row.desc}</td>
            </tr>)}
        </tbody>
      </table>
    </div>;
};

`Manipulation` and `Head` control the robot's arm and head. Use them for manipulation tasks, gestures, and camera positioning.

## Manipulation

```python theme={null}
from innate import Manipulation, Skill


class MySkill(Skill):
    """..."""

    manipulation: 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:

| Exception      | Means                                                                                                                  |
| -------------- | ---------------------------------------------------------------------------------------------------------------------- |
| `ArmFailed`    | The command was rejected or impossible — an unreachable pose, a failed IK solve, a trajectory the driver refused.      |
| `ArmUnhealthy` | The arm accepted the command but didn't get there, and a reboot-and-retry didn't fix it. The hardware needs attention. |

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

<ManipulationInterfaceMethods />

### Cartesian motion

Positions are metres in `base_link`, orientations are radians:

```python theme={null}
settled = self.manipulation.move_to(x=0.30, y=0.0, z=0.12, pitch=1.2, duration=1.5)
self.logger.info(f"arm settled at z={settled.z:.3f}")
```

`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:

```python theme={null}
self.manipulation.move_by(dx=0.01, dy=-0.005, duration=0.3)
```

### Trajectories

`follow()` sweeps through waypoints as one smooth motion, starting from wherever the arm currently is:

```python theme={null}
from innate import Waypoint

self.manipulation.follow([
    Waypoint(x=0.30, y=0.00, z=0.20, duration=0.8),
    Waypoint(x=0.34, y=0.06, z=0.14, duration=0.6),
    Waypoint(x=0.34, y=0.00, z=0.10, duration=0.6),
])
```

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

```python theme={null}
# 6 values set the claw, 5 keep whatever grip the arm is already holding
self.manipulation.move_joints([0.0, -0.5, 1.5, -1.0, 0.0], duration=2.0)

# Fold away, keeping the grip — safe while carrying something
self.manipulation.rest()

self.manipulation.JOINT_NAMES   # ("joint1", ..., "joint6")
self.manipulation.REST          # the folded pose
```

### The gripper and the standing grip

```python theme={null}
self.manipulation.gripper_open()               # fully open
self.manipulation.gripper_open(percent=50)     # half
self.manipulation.gripper_close(strength=0.4)  # close with grip preload
```

`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.

<Note>
  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.
</Note>

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()`:

```python theme={null}
self.manipulation.move_to(x=0.30, y=0.0, z=0.25, duration=2.0, block=False)

while self.manipulation.moving:
    self.mobility.send_cmd_vel(linear_x=0.05, duration=0.3)
    self.sleep(0.1)

settled = self.manipulation.wait()      # raises if the motion failed
```

A non-blocking motion is **unverified until joined** — `wait()` 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](/software/skills/code-defined-skills/robot-state), so a control loop reads one shape everywhere:

```python theme={null}
cur = self.manipulation.pose
cur.x, cur.y, cur.z      # metres
cur.rpy                  # (roll, pitch, yaw), radians
cur.gripper              # claw joint, radians
```

### Servo power and recovery

```python theme={null}
self.manipulation.torque_on()
self.manipulation.torque_off()
self.manipulation.reboot_servos()   # clears hardware errors, leaves torque off
self.manipulation.recover()         # reboot + torque on + settle, keeping the grip
```

These return `bool` rather than raising. `move_to` and `gripper_open` already call `recover()` for you on their retry path.

<Warning>
  **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`.
</Warning>

## Head

```python theme={null}
from innate import Head, Skill


class MySkill(Skill):
    """..."""

    head: Head
```

### Methods

<HeadInterfaceMethods />

### Tilt angles

<HeadTiltAnglesTable />

```python theme={null}
self.head.set_position(-15)   # look down at objects
self.head.set_position(0)     # look straight ahead
self.head.set_position(10)    # look up at faces
```

`set_position` returns immediately; the head takes a moment to arrive. If the next step depends on the new view, sleep first:

```python theme={null}
self.head.set_position(-20)
self.sleep(1.0)               # let the head settle before reading the camera
```

Read the current tilt back with the [`head_position: HeadState`](/software/skills/code-defined-skills/robot-state) feed.

## Example: a two-stroke wave

```python theme={null}
from innate import Manipulation, Skill, Waypoint


class Wave(Skill):
    """Wave the arm at someone. Use when greeting a person the robot can see."""

    manipulation: Manipulation

    UP = (0.22, 0.00, 0.30)

    def execute(self, times: int = 3):
        self.manipulation.move_to(*self.UP, duration=1.0)

        for _ in range(max(1, times)):
            self.manipulation.follow([
                Waypoint(x=0.22, y=0.10, z=0.30, duration=0.35),
                Waypoint(x=0.22, y=-0.10, z=0.30, duration=0.35),
            ])

        self.manipulation.rest()
        return f"Waved {times} times"
```

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

```python theme={null}
from innate import Manipulation, Head, Skill
from innate.exceptions import ArmFailed


class GrabInFront(Skill):
    """Reach down and grab whatever is directly in front of the robot on the
    floor. Use only when the object is already within reach."""

    manipulation: Manipulation
    head: Head

    def execute(self, height: float = 0.03):
        self.head.set_position(-20)
        self.sleep(1.0)

        self.manipulation.gripper_open()
        try:
            self.manipulation.move_to(x=0.34, y=0.0, z=height, pitch=1.4, duration=1.5)
        except ArmFailed:
            self.fail("That spot is outside the arm's reach")

        self.manipulation.gripper_close(strength=0.4)
        self.manipulation.move_to(x=0.30, y=0.0, z=0.20, duration=1.0)
        return "Grabbed it"
```

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