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

# Hand

> Abstract base class defining the interface for hands

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

Abstract base class defining the interface for hands.

## 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="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

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

### `from_choices()`

```python Signature theme={null}
from_choices(
    config: Any,
    choices: Mapping[str, EndEffectorFactory],
    context: str = 'end_effector',
) -> Optional[EndEffector]
```

Build the end effector named by a nested `end_effector` config arg.

End effectors are not in the robot/sensor registries: each arm
offers its own whitelist of compatible tools, so an arm's
construction hook resolves its `end_effector` arg through this
helper. Distinct from `Component.from_config`, which
constructs a registered component and its declared subtree.

<ParamField body="config">
  The nested `end_effector` config value: a mapping with a `type` and optional `args`, `None`, or `&#123;"type": "none"&#125;` for no end effector.
</ParamField>

<ParamField body="choices" required>
  Mapping of accepted type name to the factory (normally the class) that builds it.
</ParamField>

<ParamField body="context" default="'end_effector'">
  Label for error messages, naming the owning arm's config field (e.g. `"UR5e end_effector"`).
</ParamField>

**Returns:**

The constructed end effector, or `None` when the config
selects no end effector. It is constructed only — the
attaching arm's `start` walk brings it online.

**Raises:**

TypeError: If the config, its `type`, or its `args` has
the wrong shape.
ValueError: If `type` is not one of `choices`.

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

### `getGripDetected()`

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

Detect whether the end effector is currently holding an object.

**Returns:**

bool: True if an object is detected in the grip.

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

### `getPosition()`

```python Signature theme={null}
getPosition(normalized: bool = True) -> float
```

Get the current end-effector position.

Subclasses with a continuous position sensor (e.g. parallel-jaw
grippers) must override this. Subclasses without a meaningful
continuous position (e.g. binary suction valves) leave the
default, which raises.

<ParamField body="normalized" default="True">
  If True (default), return the position normalized to \[0.0, 1.0] on a uniform scale across all end effectors: 0.0 is fully open/disengaged, 1.0 is fully closed/engaged, with linear interpolation between those endpoints. If False, return the raw hardware position; the units and range are implementation-specific (see the subclass docstring).
</ParamField>

**Returns:**

```text theme={null}
float: Current position. Normalized values are clamped to
[0.0, 1.0]; raw values pass through cast to ``float``.
```

**Raises:**

NotImplementedError: If the end effector does not expose a
continuous position.

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

### `getToolCenterWrtArm()`

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

Get the tool center point offset relative to the arm flange.

**Returns:**

Pose: The TCP pose with respect to the arm flange.

### `grasp()`

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

Close/engage the end effector to grasp an object.

### `release()`

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

Open/disengage the end effector to release an object.

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

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

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


## Related topics

- [Customizing RL Training](/simulation/isaac/reinforcement-learning/customizing-training.md)
- [Camera Calibration](/deployment/camera-calibration.md)
- [VR Teleop Guide](/simulation/isaac/teleoperation/vr-teleop-guide.md)
- [Your first skill](/get-started/first-skill.md)
- [Overview](/simulation/isaac/teleoperation/overview.md)
