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

# Navigation Interfaces

export const NavigationUseCasesTable = () => {
  const rows = [{
    scenario: "Go to a saved location",
    recommendation: "Use built-in behavior (agent handles it)."
  }, {
    scenario: "Survey surroundings",
    recommendation: "Create a custom skill with rotate()."
  }, {
    scenario: "Follow a person",
    recommendation: "Create a custom skill with send_cmd_vel()."
  }, {
    scenario: "Fine positioning for manipulation",
    recommendation: "Create a custom skill."
  }, {
    scenario: "Patrol a route",
    recommendation: "Create a custom skill."
  }];
  return <div className="interface-methods-table-wrap">
      <table className="interface-methods-table">
        <thead>
          <tr>
            <th>Scenario</th>
            <th>Recommendation</th>
          </tr>
        </thead>
        <tbody>
          {rows.map(row => <tr key={row.scenario}>
              <td>{row.scenario}</td>
              <td>{row.recommendation}</td>
            </tr>)}
        </tbody>
      </table>
    </div>;
};

export const NavigationInterfaceMethods = () => {
  const rows = [{
    method: "rotate()",
    params: [{
      name: "angle_radians",
      type: "float"
    }],
    desc: "Rotate in place by an angle (positive = counter-clockwise). Blocking, runs through Nav2, returns True on success."
  }, {
    method: "send_cmd_vel()",
    params: [{
      name: "linear_x",
      type: "float (m/s)"
    }, {
      name: "angular_z",
      type: "float (rad/s)"
    }, {
      name: "duration",
      type: "float | None (seconds)"
    }],
    desc: "Publish one velocity command (non-blocking). With duration set, a deadman stop fires after that many seconds — always pass it in a loop so the base halts if the loop dies."
  }, {
    method: "rotate_in_place()",
    params: [{
      name: "angular_speed",
      type: "float (rad/s)"
    }, {
      name: "duration",
      type: "float (seconds)"
    }],
    desc: "Spin at a fixed angular speed for a duration (non-blocking; sign sets direction)."
  }, {
    method: "stop()",
    params: [],
    desc: "Halt the base immediately. The framework already calls this at cancel and at run end, so you rarely need it."
  }];
  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>;
};

`Mobility` gives you direct control over the robot base — rotation and velocity commands. Use it for custom navigation behaviors that complement the built-in navigation.

## Core navigation (built-in)

The robot comes with built-in navigation that the agent calls *innately*. You don't need to implement this — it works out of the box.

**Do not modify** the shipped `navigate_to_position` skill. It's a system-level skill the agent uses automatically, and changing it can break navigation.

When you tell the robot "go to the kitchen," the agent automatically:

1. Translates "kitchen" to map coordinates (if the location is saved)

2. Calls the built-in navigation skill

3. Monitors progress and handles obstacles

To send the robot somewhere from inside your own skill, don't reimplement any of that — [declare `navigate_to_position` as a sub-skill](/software/skills/code-defined-skills/composing-skills) and call it.

## Declaring the base

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


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

    mobility: Mobility
```

That's the whole wiring. `self.mobility` is guaranteed inside `execute()`.

### Methods

<NavigationInterfaceMethods />

### Examples

```python theme={null}
import math

# Rotate 90 degrees (blocking, planned through Nav2)
self.mobility.rotate(math.pi / 2)

# Drive forward at 0.1 m/s, stopping itself after 2 seconds
self.mobility.send_cmd_vel(linear_x=0.1, angular_z=0.0, duration=2.0)

# Spin in place for 3 seconds
self.mobility.rotate_in_place(angular_speed=0.5, duration=3.0)
```

<Warning>
  **Always pass `duration` to `send_cmd_vel`.** It arms a deadman stop, so if your loop dies or the process is killed mid-command the base halts on its own instead of driving away. Send a fresh command every iteration with a duration slightly longer than the loop period.
</Warning>

`self.mobility.rotate()` is blocking, so it is also a cancel point — it raises `SkillCancelled` and stops the base if a Stop lands mid-rotation. `send_cmd_vel` returns immediately, so a loop built on it needs `self.sleep()` to be interruptible.

## When to use

<NavigationUseCasesTable />

## Example: LookAround

```python theme={null}
import math

from innate import Mobility, Skill


class LookAround(Skill):
    """Rotate in place through several headings to survey the room. Use when
    the robot needs to see what's around it before deciding where to go."""

    mobility: Mobility

    def execute(self, num_directions: int = 4):
        num_directions = max(1, num_directions)
        step = (2 * math.pi) / num_directions

        for i in range(num_directions):
            self.feedback(f"Looking direction {i + 1}/{num_directions}")
            self.mobility.rotate(step)

        return "Survey complete"
```

No `_cancelled` flag, no `cancel()` method, no result tuple: `rotate()` raises `SkillCancelled` on a Stop, the framework brakes the base and reports `CANCELLED`, and returning the message reports `SUCCESS`.

## Example: creep forward until something is close

Pairs the base with the [lidar feed](/software/skills/code-defined-skills/robot-state) — a shape that only works because the sleep is cancellable.

```python theme={null}
import time

from innate import Lidar, Mobility, Skill


class CreepForward(Skill):
    """Drive slowly forward until something is within a stop distance ahead.
    Use to approach an object the robot can already see."""

    mobility: Mobility
    lidar: Lidar

    def execute(self, stop_distance: float = 0.5, speed: float = 0.08, timeout: float = 20.0):
        deadline = time.monotonic() + timeout

        while time.monotonic() < deadline:
            ahead = self.lidar.min_range(-20, 20)
            if ahead is not None and ahead <= stop_distance:
                return f"Stopped {ahead:.2f}m from the nearest obstacle"
            self.mobility.send_cmd_vel(linear_x=speed, duration=0.5)
            self.sleep(0.1)

        self.fail(f"Nothing came within {stop_distance:.2f}m in {timeout:.0f}s")
```

`time.monotonic()` for the deadline, `self.sleep()` for the pause — measuring with `time` is fine, blocking with it is not. The base is braked automatically on both exits, so there is no trailing `stop()`.
