> ## Documentation Index
> Fetch the complete documentation index at: https://docs.generalrobotics.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Clearpath Jackal

> The Clearpath Jackal as GRID drives it

The Clearpath Jackal as GRID drives it — what this robot actually implements, with the signatures it actually accepts.

`make_robot("<name>")` returns a `RemoteRobot`. Its attributes are the methods and subcomponents below, and every call runs on the robot; the driver behind it is `Jackal`. Connecting neither starts nor moves the robot — it attaches to one that is already up. A method that fails on the robot arrives as `RuntimeError` naming the original error in its message. The client packages this page uses are preinstalled in every GRID session workspace and in the Python environment the GRID CLI prepares when you run a program with `skill run`; there is nothing to install.

```python theme={null}
from grid_nexus_client import make_robot

robot = make_robot("<jackal-name>")
```

## Methods on the robot

Called as `robot.<method>(...)`.

### `getBatteryState()`

```python Signature theme={null}
getBatteryState() -> Optional[dict]
```

Return the latest battery-management-system reading.

**Returns:**

A dict with `voltage` (volts), `current` (amps, negative when
discharging), `charge_percent` (0–100, or `None` if the BMS
does not report it) — or `None` if no battery message has
arrived yet.

### `getIMU()`

```python Signature theme={null}
getIMU() -> Optional[dict]
```

Return the latest IMU reading.

Same keys, units and semantics as `Vision60.getIMU`.

**Returns:**

A dict with `orientation` (quaternion), `angular_velocity`
(rad/s) and `linear_acceleration` (m/s²) — or `None` if no IMU
message has arrived yet.

**Raises:**

RuntimeError: If IMU data is arriving but does not decode as
`sensor_msgs/Imu`.

### `getImage()`

```python Signature theme={null}
getImage(camera_name: str = '', **kwargs) -> Image
```

Return the image of camera.

Which camera a no-argument call reads is decided by cardinality
alone — no robot, config, or class declares a default. A robot with
exactly one camera needs no name; a robot with several requires one,
so adding a second camera never silently changes which one a bare
`getImage()` returns.

**camera\_name** `str` (default: '')

Name of the camera to get image from — either a directly attached camera's name or a `/`-separated path through subcomponents to a nested one (`left_arm/wrist`). May be omitted when the robot has exactly one camera.

\*\***kwargs**

Forwarded to the underlying sensor's `getImage`. Lets callers pass camera-specific options (e.g. `image_type="depth"`, `compressed=False`) without the base class having to enumerate them.

**Returns:**

Image: The captured image.

**Raises:**

RuntimeError: If no cameras are configured, or the name is
omitted on a robot with more than one camera; the message
lists every configured camera path.
KeyError: If camera\_name is given but names no camera on this robot.

### `getLidarPointCloud()`

```python Signature theme={null}
getLidarPointCloud(lidar_name: str = '') -> Optional[PointCloud]
```

Get a point cloud from the named LiDAR sensor.

**lidar\_name** `str` (default: '')

Name of the lidar. If empty, uses the first available lidar.

**Returns:**

Optional\[PointCloud]: The point cloud, or None if lidar not found

### `getMagneticField()`

```python Signature theme={null}
getMagneticField() -> Optional[Velocity]
```

Return the latest magnetometer reading in tesla.

**Returns:**

Field vector in tesla as a `Velocity` triple (the shared
three-component type; these are not velocities), or `None` if no
magnetometer message has arrived yet.

### `getOrientation()`

```python Signature theme={null}
getOrientation() -> Optional[Orientation]
```

Return the base orientation as a quaternion, from fused odometry.

Yaw is dead-reckoned and drifts; roll and pitch are gravity-referenced
via the IMU and do not.

**Returns:**

Orientation quaternion, or `None` if no odometry has arrived yet.

**Raises:**

RuntimeError: If odometry is arriving but does not decode as
`nav_msgs/Odometry`.

### `getPosition()`

```python Signature theme={null}
getPosition() -> Optional[Position]
```

Return the base position in metres, from fused odometry.

Dead-reckoned from wheel encoders and IMU by the platform's EKF, in
the odometry frame the stack started in. It drifts without bound and
is not an absolute world position.

**Returns:**

Position in metres, or `None` if no odometry has arrived yet.

**Raises:**

RuntimeError: If odometry is arriving but does not decode as
`nav_msgs/Odometry` — `None` means "nothing yet" only.

### `getState()`

```python Signature theme={null}
getState(keys: Optional[Union[str, List[str]]] = None) -> Dict[str, Any]
```

Get a live state snapshot of this component's tree.

Recursive walk owned by the base class, following the lifecycle
template: each class contributes only the protected local hook
`_local_state`, and the walk composes the per-component
results into one nested mapping. Subclasses must not replace
this method — per-component state belongs in `_local_state`.

With no `keys`, returns this component's local state merged
with one nested node per subcomponent name — the tree structure
is the nesting itself, and the names mirror `subcomponents`
(and therefore `serialize()` and edge introspection): a
two-arm rig returns `&#123;"left_arm": &#123;"joint_positions": ...&#125;,
"right_arm": &#123;...&#125;&#125;`, and a leaf component returns its local
state alone. A component whose state could not be read (dead
telemetry, not yet started, already shut down) carries an
`"error"` entry — `&#123;"error": "&lt;ExceptionType>: &lt;message>"&#125;`,
with its children still nested alongside — instead of its state
keys. Errors are contained per node: one failing component never
loses the rest of the snapshot, and the walk still descends into
the failing component's children. `"error"` is reserved for
that purpose, and local state keys must not collide with
subcomponent names; a hook violating either is reported as that
node's error.

With `keys`, returns a flat mapping with one entry per match.
Each key is a `/`-separated path through the component tree:
every segment names a subcomponent, and the final segment may
instead name an entry in that component's local state. Because
structure is the nesting, a literal path is the same as plain
indexing — `getState(["left_arm/joint_positions"])` returns
the value of `getState()["left_arm"]["joint_positions"]` — and
a path ending on a subcomponent yields that component's full
nested node. Segments may use shell-style wildcards over
subcomponent names — `*` matches within one chunk and `**`
matches any chain of chunks — so `"*_arm/joint_positions"`
fans out to one entry per arm, keyed by the concrete matched
path. Wildcards never match state entries; only a literal final
segment does. A key that matches nothing raises, so a typo is
loud rather than silently absent. A matched component whose
state read failed yields `&#123;"error": "..."&#125;` at its path.

Keyed reads are lazy: a hook runs only where a key lands — a
literal path executes the final component's hook alone (not the
nodes traversed on the way), a glob executes only the matched
nodes, and each component's hook runs at most once per call
however many keys reach it. Components with a selective-read
hook (`_local_state_entries` — every `Robot` with
registered state getters) go further and execute only the getters a key names, so
a `**` glob probes their registries without touching
hardware. The caller pays only for the state actually
requested; `**` and the no-`keys` form visit the whole tree
because that is the request.

State is cheap by convention: local state carries kinematics,
status, and health — never bulk sensor payloads (camera frames,
point clouds, scans), which stay in their dedicated methods
(`getImage`, `getPointCloud`, ...). Callable on a stopped
tree — reading state after a soft e-stop is when it matters most
— and on a partially started one, where unstarted components
report a per-node error instead of failing the call.

**keys**

State paths to read, or None for the full nested snapshot. A single string is shorthand for a one-element list.

**Returns:**

The nested state snapshot when `keys` is None, otherwise a
flat mapping of matched path to state value or nested node.

**Raises:**

TypeError: If `keys` is neither None, a string, nor a
list/tuple of strings.
ValueError: If a key is empty, has an empty path segment, or
matches no subcomponent path or state entry.

### `getVelocity()`

```python Signature theme={null}
getVelocity() -> Optional[Velocity]
```

Return the measured body-frame linear velocity in m/s.

Measured, not commanded — use it to close the loop on
`moveByVelocity`. Angular velocity is available from `getIMU()` or
`getState()`.

**Returns:**

Body-frame linear velocity in m/s, or `None` if no odometry has
arrived yet.

**Raises:**

RuntimeError: If odometry is arriving but does not decode as
`nav_msgs/Odometry`.

### `isEmergencyStopped()`

```python Signature theme={null}
isEmergencyStopped() -> bool
```

Report whether the platform emergency stop is currently engaged.

Raises rather than returning `None` when the state is unknown: a
caller writing `if robot.isEmergencyStopped()` would read an unknown
state as "clear to move", which is exactly backwards for a safety
interlock.

**Returns:**

True when the stop is engaged and motion is inhibited, False when
it is clear.

**Raises:**

RuntimeError: If the emergency-stop state is unknown — either
nothing has arrived on the topic, or what arrives does not
decode as `std_msgs/Bool`. The second case warns once on
first read, not at bring-up.

### `moveByVelocity()`

```python Signature theme={null}
moveByVelocity(
    linear_velocity: Velocity,
    angular_velocity: Velocity,
    frame: str = 'body',
    *,
    duration: Optional[float] = 1.0,
) -> None
```

Command a body-frame twist.

Non-blocking: returns immediately, and the command is republished at
`_CMD_RATE_HZ` Hz until superseded by another call, until `stop()`,
or until *duration* seconds elapse — the platform MCU halts the wheels
when commands stop arriving, so continuous republish is what holds a
velocity.

The Jackal is a differential-drive base: it cannot translate
sideways. `linear_velocity.y` and `.z` are ignored, as are
`angular_velocity.x` and `.y`.

**linear\_velocity** *required*

Body-frame linear velocity in m/s; only `x` (forward-positive) is actuated.

**angular\_velocity** *required*

Body-frame angular velocity in rad/s; only `z` (counter-clockwise-positive yaw) is actuated.

**frame** (default: 'body')

Reference frame. Only `"body"` is supported.

**duration** (default: 1.0)

Seconds to hold the command. `None` republishes indefinitely until `stop()` or the next command.

**Raises:**

NotImplementedError: If `frame == "world"`. The platform accepts
only body-frame twists; compose a world-frame move from
`getOrientation()` and repeated body-frame commands.
ValueError: If *frame* is neither `"body"` nor `"world"`, if
*duration* is negative, or if a commanded speed exceeds
`max_linear_speed` / `max_angular_speed`.

### `stop()`

```python Signature theme={null}
stop() -> None
```

Halt all motion across this component's tree (soft e-stop).

Recursive walk owned by the base class: every subcomponent is
stopped first (children before their parent, leaf-to-root;
siblings in `subcomponents` insertion order), then this
component's own `_stop_self` hook runs. The walk is
best-effort — a failing component never prevents the rest of the
tree from being stopped; failures are collected and raised
together after the walk completes. Components (and their
subtrees) that have already been shut down are skipped. Safe to
call repeatedly: stopping an already-stopped tree re-runs the
hooks, which must tolerate that. Blocks until every hook has
returned.

Stops motion only; resources stay live and the component remains
usable — telemetry and every other public method keep working on a
stopped tree. Use `shutdown` to release resources. Stopping
re-arms each visited component, so a later `start` re-runs
the bring-up hooks — that is the stop-then-start restart path —
but it does not revoke callability: only `shutdown` does
that.

Subclasses must not replace this method — per-component stop
behavior belongs in `_stop_self`. An override may only
*extend* the walk (adding a driver-specific mode, as `UR5e` does
with its `immediate` escape hatch) and must delegate to
`super().stop()` for the normal path; it must never
re-implement the recursion.

**Raises:**

ComponentStopError: If one or more stop hooks raised. The
walk still visited every component; the exception carries
every `(component_path, exception)` pair.

## Properties on the robot

Read as `robot.<property>`; each read runs the getter on the robot.

**cmd\_vel\_topic** `str`

Velocity-command topic: the platform `twist_mux` input.

**latest\_battery** `Optional[BatteryState]`

The most recent battery-management-system message, or `None`.

**latest\_estop** `Optional[bool]`

Whether the emergency stop is engaged, or `None` if unknown.

**latest\_imu** `Optional[Imu]`

The most recent IMU message, or `None` if none has arrived.

**latest\_joint\_state** `Optional[JointState]`

The most recent wheel joint-state message, or `None`.

**latest\_magnetometer** `Optional[MagneticField]`

The most recent magnetometer message, or `None`.

**latest\_odometry** `Optional[Odometry]`

The most recent fused-odometry message, or `None` if none has arrived.

**odometry\_topic** `str`

Fused (EKF) odometry topic.

## Lifecycle and configuration

The driver's own bring-up and configuration hooks. The edge runs them when the robot comes up; do not call them from a program. Note that `robot.shutdown()` on the proxy is not the method below — it is `RemoteRobot.shutdown()`, which closes your connection and leaves the robot as it was.

* `addSensor()` — Add an external sensor to the robot.
* `addSubcomponent()` — Attach a child component under a name.
* `builtin_subcomponents()` — Declare the children this class always ships with (customization point).
* `configHash()` — Hash this component's serialized config subtree.
* `config_schema()`
* `from_config()` — Construct this component and its config-declared subtree.
* `getIdentity()` — Get this component's own hardware identity (serial, model, version, MAC).
* `getRobotId()` — Stable, readable identifier for a robot or rig, for a database key.
* `serialize()` — Serialize this component tree back to its config envelope.
* `setup_shutdown_handlers()` — Register the process-wide atexit and signal handlers for safe teardown.
* `shutdown()` — Shut down this component's tree, halting motion and releasing resources.
* `start()` — Bring this component's tree online (connect, enable, arm).

The contract these methods implement is **Robot interface**; values returned are **grid-types** (camera reads return `Image` — call [`decode()`](https://docs.generalrobotics.dev/v2.3/python-api/grid-types/image) for an ndarray). You reach the robot through [`make_robot`](https://docs.generalrobotics.dev/v2.3/python-api/grid-nexus-client/make_robot).


## Related topics

- [Python APIs](https://docs.generalrobotics.dev/v2.3/python-api/overview.md)
- [Robots](https://docs.generalrobotics.dev/v2.3/simulation/airgen/robots.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.