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

# Arm

> Abstract base class defining the interface for robotic arms

```python theme={null}
from grid_robot_api.base.robot import Arm
```

Abstract base class defining the interface for robotic arms.

This class extends the base Robot class with additional methods specific to
robotic arm manipulation and control including joint control and gripper operations.

Class attributes:
NUM\_JOINTS: Number of joints (DOF) returned by `getJointAngles`,
or `None` when the joint count is not statically known and
must be resolved from the instance after configuration (e.g.
Isaac Sim arms whose DOF depends on the loaded URDF). Concrete
subclasses with a fixed joint count should override with an
`int`; software mocks with no joint model should set `0`.

## Constructor

```python Signature theme={null}
Arm()
```

## Properties

<ResponseField name="arms" type="Dict[str, 'Component']">
  Child components that are arms (derived, read-only).

  Filtered from `subcomponents` by `Arm` role; the returned dict
  is a fresh snapshot.
</ResponseField>

<ResponseField name="end_effector" type="Optional['Component']">
  The attached end effector, or `None` (derived, read-only).

  Returns the child named `"end_effector"` when it is an
  `EndEffector`, otherwise the first `EndEffector` child in
  insertion order. Attach one via
  `addSubcomponent("end_effector", ...)`.
</ResponseField>

<ResponseField name="expected_identity" type="ComponentIdentity">
  Hardware identity this component's config declared it should have.

  Set from the `identity` block of the config envelope by
  `from_config`, and empty for a component built any other
  way. Recording it acquires no hardware, and what it *means*
  splits by transport. For network-addressed hardware (an arm at a
  configured IP) it is a declaration to verify against: whether a
  mismatch warns or fails belongs to the caller that owns that
  policy, not to the driver. For bus-enumerated hardware (USB
  cameras), whose enumeration order is not a stable address, the
  declared `serial_number` is instead a *selector*: bring-up
  opens exactly that unit and fails loudly when it is absent,
  never falling back to a different device.

  Compare it against `getIdentity`, which is what the
  hardware reports.
</ResponseField>

<ResponseField name="home_pose" type="List[float]">
  Joint angles (radians) defining the home position. Must be implemented by subclasses.

  This property is accessed during `Arm.__init__`, so it must not depend on any
  instance state set by the subclass `__init__` — return a pure literal.
</ResponseField>

<ResponseField name="sensors" type="Dict[str, 'Component']">
  Child components that are sensors (derived, read-only).

  Filtered from `subcomponents` by `Sensor` role; attach sensors
  via `addSensor`/`addSubcomponent` rather than writing to the
  returned dict, which is a fresh snapshot.
</ResponseField>

<ResponseField name="subcomponents" type="Dict[str, 'Component']">
  Mapping of child-component name to child component.

  The returned dict is the live backing store: mutating it mutates
  the component tree. Prefer `addSubcomponent` for inserts.
</ResponseField>

## Methods

### `addNamedPose()`

```python Signature theme={null}
addNamedPose(pose_name: str, joint_angles: List[float]) -> None
```

Define a pose\_name:joint\_angles pair in the dictionary of named poses
(overriding any existing pair).

<ParamField body="pose_name" required>
  Name of the pose to define
</ParamField>

<ParamField body="joint_angles" required>
  List of joint angles in radians
</ParamField>

**Raises:**

ValueError: If the length of joint\_angles list does match the length of other named
poses

### `addSensor()`

```python Signature theme={null}
addSensor(name: str, sensor: Sensor) -> None
```

Add an external sensor to the robot.

<ParamField body="name" type="str" required>
  Unique identifier for the sensor
</ParamField>

<ParamField body="sensor" type="Sensor" required>
  Sensor object to add (e.g. Camera, IMU, Lidar)
</ParamField>

**Returns:**

None

### `addSubcomponent()`

```python Signature theme={null}
addSubcomponent(name: str, component: 'Component') -> None
```

Attach a child component under a name.

An existing child with the same name is replaced, matching dict
assignment semantics (`addSensor` has always overwritten).

<ParamField body="name" required>
  Non-empty name for the child, unique within this component's `subcomponents`. Names are addressable as `getState` path chunks, so the path syntax characters `/ * ? [ ]` are not allowed.
</ParamField>

<ParamField body="component" required>
  The child component to attach.
</ParamField>

**Raises:**

ValueError: If `name` is not a non-empty string, or contains
a path syntax character.

### `builtin_subcomponents()`

```python Signature theme={null}
builtin_subcomponents() -> Dict[str, Dict[str, Any]]
```

Declare the children this class always ships with (customization point).

Hardware that is part of the component itself — a humanoid's head
camera, an arm's wrist camera, a base built into a mobile
manipulator — is declared here rather than constructed in
`__init__`, so one description drives both the runtime tree and
the static config tooling. The declaration is **pure data** in the
same shape an authored `subcomponents` entry uses:
`&#123;"&lt;child name>": &#123;"type": "&lt;registry name>", "args": &#123;...&#125;,
"subcomponents": &#123;...&#125;, "enabled": True&#125;&#125;`, where `type` names a
robot- or sensor-registry key, `args` are that class's config
args, `subcomponents` declares grandchildren by the same rules,
and `enabled` (default `True`) can declare a child off by
default.

An override must be callable on the bare class, without
instantiation and without importing a driver or touching hardware:
the setup-time stream resolver reads it off the registry class to
resolve a robot's camera streams on a machine that has none of the
hardware attached.

The base class materializes the declaration on every construction
path — a direct constructor call attaches the children once the
constructor returns, and `from_config` attaches them merged
with the authored `subcomponents` block (authored keys win, so an
entry with the same name retunes `args` or drops the child with
`enabled: false` — and may omit `type`, inheriting the one
declared here — while an entry naming a different `type` is an
error). Attached builtin children are ordinary subcomponents:
they appear in `serialize`, in `getState`, and in the
lifecycle walks, and a builtin `Camera` child is skipped when the
tree is built with `enable_cameras=False`.

**Returns:**

Mapping of child name to its config declaration. The default is
empty — a class with no builtin children.

### `configHash()`

```python Signature theme={null}
configHash(
    *,
    include_types: Optional[Tuple[type, ...]] = None,
    include_identity: bool = False,
    exclude_fields: AbstractSet[str] = frozenset(),
    type_prefix: bool = False,
) -> str
```

Hash this component's serialized config subtree.

A SHA-256 hex digest over the `serialize` output — the
config topology and args, not object identity and not
`getState` telemetry. Deterministic: the same tree with
the same options always produces the same digest, regardless of
dict insertion order. Touches no hardware; with
`include_identity` it hashes whatever identity is already
recorded (declared or previously read), triggering no new read.

Two hashes are comparable only when computed with the same
options; equality across differently-parameterized calls is
meaningless. Note that with `include_identity` the digest
changes as identity fields become known — a component reports
fields monotonically (some only once started), so hash at a
consistent point in the lifecycle when comparing across runs.

<ParamField body="include_types">
  When given, only tree nodes whose component is an instance of one of these classes are hashed; a non-matching node is pruned together with its entire subtree. `None` keeps every node.
</ParamField>

<ParamField body="include_identity" default="False">
  When True, each kept node's `identity` block (as `serialize` records it — hardware-read overlaid on declared) is included in the hash. When False the hash covers configuration only.
</ParamField>

<ParamField body="exclude_fields" default="frozenset()">
  Config `args` keys dropped from every kept node before hashing (e.g. an IP address that changes across networks).
</ParamField>

<ParamField body="type_prefix" default="False">
  When True, prepend this component's registry type and an underscore to the digest (e.g. `"go2_&lt;digest>"`, `"rig_&lt;digest>"` for a composite root) for a human-readable, greppable label. The type is already part of the hashed payload, so this only labels the same digest — the suffix after `_` is identical to the unprefixed call with the same options.
</ParamField>

**Returns:**

A 64-character lowercase SHA-256 hex digest, or
`"&lt;type>_&lt;digest>"` when `type_prefix` is True.

**Raises:**

RuntimeError: If this component was not constructed from a
config, so it cannot be serialized.
ValueError: If this component itself matches none of
`include_types` — the hash would cover an empty tree
and compare equal for unrelated components.

### `config_schema()`

```python Signature theme={null}
config_schema() -> dict[str, Any]
```

### `endFreeDrive()`

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

Exit free-drive mode and return to position control.

The arm is no longer movable by hand and again responds to
position-control commands such as `moveToPose` and
`setJointAngles`.

**Raises:**

NotImplementedError: If the arm does not support free-drive
mode.

### `followJointTrajectory()`

```python Signature theme={null}
followJointTrajectory(
    angles: List[List[float]],
    duration: Union[float, List[float]],
    blocking: bool = True,
) -> None
```

Move the arm through a sequence of joint configurations.

Each waypoint in `angles` is visited in order. The interpretation of
`duration` is uniform across implementations: a scalar is the *total*
trajectory time, split evenly across segments; a list specifies the
duration of each segment, where segment `i` is the move from the
previous pose (or the current arm pose, for `i=0`) to `angles[i]`,
so `len(duration)` must equal `len(angles)`.

Subclasses with a native trajectory-streaming API may blend between
waypoints; subclasses without one typically loop per-segment moves and
briefly stop at each waypoint. Either way the segment timings above
are honored.

<ParamField body="angles" required>
  Sequence of joint configurations (radians) to visit in order. Each inner list must have the same length as the arm's joint count.
</ParamField>

<ParamField body="duration" required>
  Either a single positive float giving the total trajectory time in seconds, or a list of positive floats with the same length as `angles` giving per-segment durations in seconds.
</ParamField>

<ParamField body="blocking" default="True">
  Wait for the trajectory to complete before returning. Defaults to True.
</ParamField>

**Raises:**

NotImplementedError: If the arm does not implement trajectory
following.
ValueError: If `angles` is empty, `duration` is not positive,
or list-form `duration` length does not match `angles`.

### `from_config()`

```python Signature theme={null}
from_config(config: Dict[str, Any], *, enable_cameras: bool = True) -> 'Component'
```

Construct this component and its config-declared subtree.

Recursive walk owned by the base class, the inverse of
`serialize`: the config envelope
(`&#123;"type": ..., "args": &#123;...&#125;, "subcomponents": &#123;...&#125;&#125;`) is
validated, this component is constructed from `args` via
`_from_config_args`, and each declared child is resolved
through the robot/sensor registries, constructed by its own
class's `from_config`, and attached. `type` may be omitted
when calling on a concrete class — it is then taken from the
class's registry entry.

The declared children are this class's
`builtin_subcomponents` overlaid by the config's
`subcomponents` block: builtin children first in declaration
order, then authored-only children. An authored entry whose name
matches a builtin one overlays it — its `args` merge over the
builtin `args` and `enabled: false` drops the child — rather
than colliding with it, and it may omit `type` entirely
(`&#123;"chassis_rear": &#123;"enabled": false&#125;&#125;` is a complete entry),
inheriting the builtin's; an entry naming a *different* `type`
than the builtin declaration is an error, as is an entry that
omits `type` while overlaying no builtin child. Any entry with
`enabled: false` is skipped, builtin or authored.

A node's optional `identity` block is recorded on the built
component as `expected_identity` — the hardware this entry
is *declared* to be. It is never checked against the device
here: construction acquires no hardware, and deciding what a
mismatch means belongs to the caller.

Component configs nested inside `args` (an arm's
`end_effector`, for example) are the constructor's business and
are built in `_from_config_args`; the recursion here covers
the `subcomponents` block only. The returned tree is
constructed but **not** started — use `make_robot`/
`make_sensor`, a `with` block, or an explicit
`start` call to bring it online.

<ParamField body="config" required>
  Config envelope for this component. Extra keys are rejected; `args` and `subcomponents` may be omitted.
</ParamField>

<ParamField body="enable_cameras" default="True">
  When True, camera components declared in the config are constructed and attached for local `getImage` use. When False, config-declared cameras are skipped at every depth because deploy streaming owns those devices; the flag is also passed to `_from_config_args` so a driver can skip cameras it builds itself.
</ParamField>

**Returns:**

The constructed component with its declared subcomponents
attached and its config recorded — including, per builtin
child, whether the authored config moved it off its
declaration's `enabled` state — so
`type(c).from_config(c.serialize())` rebuilds an equivalent
tree.

**Raises:**

ValueError: If `config` is not a mapping, a declared
subcomponent type is in neither registry, or an authored
entry overlays a builtin child with a different `type`.
ConfigValidationError: If the config tree does not match the
envelope shape; the message carries full config-tree
paths.

### `getEndEffectorPose()`

```python Signature theme={null}
getEndEffectorPose() -> Pose
```

```text theme={null}
[Deprecated]
Get the current end effector pose (position + orientation) w.r.t. the base frame.
```

### `getIdentity()`

```python Signature theme={null}
getIdentity() -> ComponentIdentity
```

Get this component's own hardware identity (serial, model, version, MAC).

Local, not recursive: it answers for this component alone. The
tree-wide view is `serialize`, which carries each node's
identity alongside its topology; identity is deliberately absent
from `getState`, which is live telemetry rather than
constants burned into a device.

**Never raises and never blocks the caller's real work.** A
driver read that fails — controller unreachable, dashboard
refused, SDK error — is logged and degrades to whatever fields
are already known, down to an empty mapping. Absence means
*unknown*, never *different*: see `ComponentIdentity` for what
consumers may conclude from a missing field.

**Callable in every lifecycle state** — before `start()`,
while started, after `stop()`, and after `shutdown()`. Where
the transport allows it (the UR dashboard server needs only the
controller IP, ZED serials are enumerable from sysfs before the
device is opened) a constructed-but-unstarted component already
answers, which is what lets a caller de-duplicate a robot before
bringing it up. Drivers that can only read identity from a live
device handle return nothing until then, so a call after
`start()` may carry *more* fields than one before it.

**Monotonic per component.** Every field ever read successfully
is cached, so repeated calls only ever gain fields — a later
failure never drops one that was already known. A field whose
value is re-read is updated to the fresh value.

**The field set is closed.** Only the fields `ComponentIdentity`
declares are accepted; anything else a driver reports is logged
and dropped, exactly like a non-string value, so what a component
reports always round-trips through the config envelope.

**Returns:**

A fresh `ComponentIdentity` mapping holding only the fields
known for this component; `&#123;&#125;` when nothing is known.
Mutating the result does not affect the component.

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

<ParamField body="camera_name" type="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.
</ParamField>

<ParamField body="**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.
</ParamField>

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

### `getJointAngles()`

```python Signature theme={null}
getJointAngles() -> List[float]
```

Get current joint angles in radians.

**Returns:**

Current joint angles in radians, ordered by joint index

### `getLidarPointCloud()`

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

Get a point cloud from the named LiDAR sensor.

<ParamField body="lidar_name" type="str" default="''">
  Name of the lidar. If empty, uses the first available lidar.
</ParamField>

**Returns:**

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

### `getNamedPose()`

```python Signature theme={null}
getNamedPose(pose_name: str) -> Optional[List[float]]
```

Get the list of joint angles corresponding to a named pose.

<ParamField body="pose_name" required>
  Name of the pose to get (case-insensitive)
</ParamField>

**Returns:**

list: Joint angles (radians) of the named pose, or None if the named pose does not exist

### `getOrientation()`

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

Get the orientation of the robot in the world frame.

**Returns:**

Optional\[Orientation]: Current orientation of the robot in world coordinates,
or None if orientation is unavailable

**Raises:**

NotImplementedError: Must be implemented by subclasses

### `getPose()`

```python Signature theme={null}
getPose() -> Pose
```

Get the current end effector pose (position + orientation) w\.r.t. the base frame.

**Returns:**

Current end effector pose with respect to the base frame.

### `getPosition()`

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

Get the position of the robot in the world frame, in meters.

**Returns:**

Optional\[Position]: Current position of the robot in world coordinates,
or None if position is unavailable

**Raises:**

NotImplementedError: Must be implemented by subclasses

### `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 `state_getters`
robot) 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.

<ParamField body="keys">
  State paths to read, or None for the full nested snapshot. A single string is shorthand for a one-element list.
</ParamField>

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

### `grasp()`

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

Close the gripper to grasp an object.

Closes the gripper to securely hold an object. The gripper will
apply appropriate force based on the object detected.

### `moveToDeltaPose()`

```python Signature theme={null}
moveToDeltaPose(delta_pose: Pose, blocking: bool = True) -> None
```

Offset the robot end effector by the specified delta pose from its current pose
(with respect to the base frame).

<ParamField body="delta_pose" required>
  Pose offset to compose with the current pose. The position is added to the current position and the orientation is composed with the current orientation.
</ParamField>

<ParamField body="blocking" default="True">
  Wait for movement to complete. Defaults to True.
</ParamField>

### `moveToHome()`

```python Signature theme={null}
moveToHome(blocking: bool = True) -> None
```

Move the arm to its predefined home pose.

Equivalent to `moveToNamedPose("home", blocking=blocking)`. The
`"home"` entry is registered in `named_poses` from the abstract
`home_pose` property during `Arm.__init__`, so every concrete
arm has it. Subclasses may add hardware-specific tuning parameters as
keyword-only arguments after `blocking`.

<ParamField body="blocking" default="True">
  Wait for movement to complete. Defaults to True.
</ParamField>

### `moveToNamedPose()`

```python Signature theme={null}
moveToNamedPose(pose_name: str, blocking: bool = True) -> None
```

Move the robot to the joint angles of a named pose.

Subclasses may add hardware-specific tuning parameters (timing,
force thresholds, etc.) as keyword-only arguments after
`blocking`.

<ParamField body="pose_name" required>
  Name of the pose to move to (case-insensitive).
</ParamField>

<ParamField body="blocking" default="True">
  Wait for movement to complete. Defaults to True.
</ParamField>

**Raises:**

ValueError: If pose\_name is not found in the named poses
dictionary.

### `moveToPose()`

```python Signature theme={null}
moveToPose(pose: Pose, blocking: bool = True) -> None
```

Move the robot end effector to a specified pose with respect to the base frame.

Subclasses may add hardware-specific tuning parameters (timing, force
thresholds, etc.) as keyword-only arguments after `blocking`.

<ParamField body="pose" required>
  Target pose (position in meters, orientation as a unit quaternion). The pose is interpreted in the robot's base frame.
</ParamField>

<ParamField body="blocking" default="True">
  Wait for movement to complete. Defaults to True.
</ParamField>

### `release()`

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

Open the gripper to release an object.

Opens the gripper to release any held object. The gripper will
return to its fully open position.

### `removeNamedPose()`

```python Signature theme={null}
removeNamedPose(pose_name: str) -> Optional[List[float]]
```

Remove a named pose from the dictionary of pose\_name:joint\_angles pairs.

<ParamField body="pose_name" required>
  Name of the pose to remove (case-insensitive)
</ParamField>

**Returns:**

list: Joint angles (radians) of the named pose that was removed, or None if the named
pose did not exist

### `serialize()`

```python Signature theme={null}
serialize() -> Dict[str, Any]
```

Serialize this component tree back to its config envelope.

The exact inverse of `from_config`, producing the recursive
config schema it consumes:
`&#123;"type": ..., "args": &#123;...&#125;, "subcomponents": &#123;name: &lt;node>&#125;&#125;`,
minimized via `model_dump(exclude_defaults=True)` so empty
`args`/`subcomponents` are omitted. Only config-declared
children (those constructed through `from_config`) are
emitted; parts a driver constructs internally are implied by the
parent's `args` and reappear on reconstruction.

A child a class ships with (`builtin_subcomponents`) is
normally implied by the parent's `type` and needs no
`enabled` key. It gets an explicit one exactly when its
attached state diverges from what the bare class declaration
would produce, because reconstruction would otherwise flip it:

* declared enabled but switched off by the config
  (`enabled: false`) — emitted as a bare
  `&#123;"enabled": false&#125;` stub under `subcomponents`, the
  type-omitted overlay form that inherits the declaration's
  `type`. The stub exists only in this output; it is never a
  phantom entry in `subcomponents`, `getState`, or
  introspection.
* declared `enabled: false` but switched on by the config
  (`enabled: true`) — its normal serialized entry additionally
  carries `"enabled": true`, which an omitted key would not
  preserve (an authored entry that omits `enabled` keeps the
  declaration's value).

A child whose state matches its declaration is emitted exactly as
before, with no `enabled` key and no stub.

The asymmetry to
know: children skipped because the tree was built with
`enable_cameras=False` get **no** stub. That flag is
factory-only camera-ownership semantics — deploy streaming owns
those devices for this process, the config never said to drop
them — so the serialized config keeps describing the robot's
cameras, and passing the flag again is what skips them again.

A node also carries an `identity` block —
`&#123;"serial_number": ..., "model": ..., "controller_version":
..., "mac_address": ...&#125;`, each key present only when known —
whenever this component has a hardware identity to record, so the serialized
tree says which physical units it was built from and not just
which types. It is what `getIdentity` reports overlaid on
the identity the config declared (`expected_identity`), so
hardware that has identified itself wins over the declaration
while an unread declaration survives the round-trip. Omitted
entirely when nothing is known — never an empty block.

**Returns:**

The minimal config mapping such that
`make_robot(component.serialize())` reconstructs an
equivalent component tree.

**Raises:**

RuntimeError: If this component was not constructed from a
config, so no config type is recorded.
ConfigValidationError: If the recorded config does not fit
the envelope (e.g. a recorded arg key is not a string).

### `setJointAngles()`

```python Signature theme={null}
setJointAngles(angles: List[float], blocking: bool = True) -> None
```

Set joint angles in radians.

Commands the arm to move joints to the specified target angles.

Subclasses may add hardware-specific tuning parameters (timing,
force thresholds, high-frequency streaming, etc.) as keyword-only
arguments after `blocking`.

<ParamField body="angles" required>
  Target joint angles in radians, ordered by joint index. Length must match the number of joints on the arm.
</ParamField>

<ParamField body="blocking" default="True">
  Wait for movement to complete. Defaults to True.
</ParamField>

### `setup_shutdown_handlers()`

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

Register the process-wide atexit and signal handlers for safe teardown.

Installs an `atexit` hook and SIGINT/SIGTERM (and SIGHUP where
available) handlers that shut down every enrolled bring-up entry
point, newest first, so hardware is released on normal
termination and on Ctrl+C. Registration normally happens
automatically the first time a component is enrolled from the
main thread; call this explicitly from the main thread when
components are only ever brought up from background threads,
where Python forbids installing signal handlers — enrollment
there defers the atexit hook together with the signal handlers.

### `shutdown()`

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

Shut down this component's tree, halting motion and releasing resources.

Two phases, both owned by the base class. First a tree-wide
`stop`: motion is halted everywhere before anything is torn
down, so no part of the robot is still commandable while another
part is being released. Then the teardown walk — every
subcomponent is shut down first (children before their parent,
leaf-to-root; siblings in `subcomponents` insertion order),
then this component's own `_shutdown_self` hook runs.
Children-first ordering is a contract for shared resources: a
parent that owns a resource its children borrow (e.g. one ROS
node shared by both arms) releases it in its own hook, after all
children have shut down — a child must never release a resource
it does not own.

Idempotent: the first call latches, and every later call is a
no-op; the tree-wide stop also runs once, so a nested
`shutdown()` reached by the walk does not re-stop its subtree.
Best-effort: the stop phase and every hook failure are logged and
the walk continues, so this method never raises — it can run
safely from `__exit__`, signal handlers, and interpreter-exit
hooks. Blocks until every hook has returned; the component is
unusable afterwards.

Shutting down also drops the component from process-wide exit
cleanup (the enrollment made by the public `start`), so
the atexit/signal handlers only ever touch components that are
still live.

Subclasses must not replace this method — per-component cleanup
belongs in `_shutdown_self`. An override may only *extend*
the walk and must delegate to `super().shutdown()`; it must
never re-implement the recursion.

### `start()`

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

Bring this component's tree online (connect, enable, arm).

Recursive walk owned by the base class and the mirror of
`shutdown`. Per node the walk runs `_start_self`,
then `_provision_children`, then descends into every
subcomponent (parents before their children, root-to-leaf;
siblings in `subcomponents` insertion order). That ordering is
the resource-handoff contract: a parent creates the runtime
resources its children borrow (an RTDE connection, a ROS node) in
its own bring-up hook and hands them to its children in
`_provision_children`, before any child hook runs.

**Warning: bring-up hooks move hardware.** `start()` is not a
passive connect. Driver hooks power on, enable, home, calibrate,
or stand a robot up — a Robotiq gripper strokes its jaws to
auto-calibrate, a WidowX homes, a Flexiv enables and homes its
gripper, a Go2 stands up. Restarting a tree re-runs those hooks,
so `stop()` then `start()` repeats that motion. Clear the
workspace before starting or restarting a physical robot; each
driver's `_start_self` documents the motion it commands.

Idempotent: the walk always descends the whole tree, but a
component whose bring-up hook already ran — and whose
`_still_live` health hook still reports it live — is
skipped, so calling `start()` twice re-runs no bring-up and
calling it after attaching a new subcomponent brings up only the
newcomer. A started component whose health hook reports its
resources dead (a power-cycled controller, a dropped connection)
gets its bring-up re-run, so `start()` alone is the recovery
call after such a fault.
`_provision_children` runs on every descent through a node
regardless of that mark, so a newcomer is provisioned by its
parent before its own hook runs. `stop` re-arms every
component it visits, so `stop()` then `start()` re-runs the
bring-up hooks (reconnect-after-fault) — while leaving the stopped
tree callable throughout. Blocks until every hook has returned.

Fail-fast: the first failing hook aborts the walk and
`ComponentStartError` is raised — a half-started component is
never handed back to the caller. Cleanup is scoped to this call:
the components this walk brought up for the first time are shut
down (best-effort) and the failing subtree is released, while
components that were already live before the call keep running
and the tree is not latched. On an initial bring-up nothing was
live, so that scope is the whole tree. Components this walk was
*restarting* (started before, re-armed by `stop`) are only
stopped, never shut down — a failed reconnect (e.g. a robot still
in an emergency stop) stays retryable with another `start()`.
A subtree that was already shut down is skipped with a warning
rather than resurrected.

Calling this method also enrolls the component in process-wide
exit cleanup: the atexit/signal handlers shut down every
component whose public `start()` was called (a tree root, or a
subtree driven directly) and that has not been shut down yet, so
hardware is released on normal termination and Ctrl+C once the
handlers are installed — automatic on the first main-thread
enrollment; a process that only brings components up from
background threads must call `setup_shutdown_handlers`
from its main thread. `shutdown` unenrolls.

Subclasses must not replace this method — per-component bring-up
belongs in `_start_self`. An override may only *extend* the
walk by delegating to `super().start()`; it must never
re-implement the recursion.

**Raises:**

RuntimeError: If this component has already been shut down;
`shutdown()` is final, so a new object is required.
ComponentStartError: If a bring-up hook raised. Whatever this
call brought up has been released by the time it
propagates, and the original failure is the exception's
`__cause__`.

### `startFreeDrive()`

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

Enable free-drive (hand-guided) mode.

The arm becomes compliant and can be moved by hand while supporting
its own weight. Position-control commands such as
`moveToPose` and `setJointAngles` are not active in
this mode — call `endFreeDrive` to return to position
control.

**Raises:**

NotImplementedError: If the arm does not support free-drive
mode.

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

### `validateGrasp()`

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

Check whether the end effector is currently holding an object.

Thin wrapper that delegates to the configured end effector's
`EndEffector.getGripDetected`. Subclasses generally do not
need to override this.

**Returns:**

True if the end effector reports an object in its grip, False
otherwise.

**Raises:**

RuntimeError: If no end effector is configured on this arm.
NotImplementedError: If the configured end effector does not
support grip detection.


## Related topics

- [Customizing RL Training](/simulation/isaac/reinforcement-learning/customizing-training.md)
- [RemoteRobot](/python-api/grid-nexus-client/remoterobot.md)
- [Camera Calibration](/deployment/camera-calibration.md)
- [Training Workflow](/simulation/isaac/reinforcement-learning/training-workflow.md)
- [VR Teleop Guide](/simulation/isaac/teleoperation/vr-teleop-guide.md)
