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

# AirGenCar

> AirGen simulation car implementation

```python theme={null}
from grid_sim_client.airgen.robot import AirGenCar
```

AirGen simulation car implementation.

This class provides an interface to control simulated wheeled cars in AirGen
using the CarClient from the grid\_sim\_client.airgen client.

## Constructor

```python Signature theme={null}
AirGenCar(
    ip: str = '127.0.0.1',
    port: int = 41471,
    timeout_value: int = 3600,
    vehicle_name: str = '',
)
```

Initialize a simulated AirGen car client.

<ParamField body="ip" type="str" default="'127.0.0.1'">
  IP address of the AirGen server. (default: "127.0.0.1")
</ParamField>

<ParamField body="port" type="int" default="41471">
  Port number for the AirGen server. (default: 41471)
</ParamField>

<ParamField body="timeout_value" type="int" default="3600">
  Timeout value for the connection in seconds. (default: 3600)
</ParamField>

<ParamField body="vehicle_name" type="str" default="''">
  Name of the vehicle in the simulation. (default: "")
</ParamField>

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

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

### `cleanup()`

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

Release AirGen transport resources and unregister base cleanup.

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

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

### `getBarometerData()`

```python Signature theme={null}
getBarometerData(barometer_name: str = 'barometer')
```

### `getDistanceSensorData()`

```python Signature theme={null}
getDistanceSensorData(distance_sensor_name: str = 'front_range')
```

### `getGpsData()`

```python Signature theme={null}
getGpsData(gps_name: str = 'gps')
```

### `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 = '', image_type: str = 'rgb', **kwargs) -> Image
```

Get an image from the specified camera.

<ParamField body="camera_name" type="str" default="''">
  Name of the camera in the AirGen scene. (default: "front\_center")
</ParamField>

<ParamField body="image_type" type="str" default="'rgb'">
  Image type, one of ("rgb"|"depth"). (default: "rgb")
</ParamField>

**Returns:**

Image: Captured image wrapped in the project's Image type.

### `getImuData()`

```python Signature theme={null}
getImuData(imu_name: str = 'imu')
```

### `getLidarData()`

```python Signature theme={null}
getLidarData(lidar_name: str = 'front_lidar')
```

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

### `getMagnetometerData()`

```python Signature theme={null}
getMagnetometerData(magnetometer_name: str = 'magnetometer')
```

### `getOrientation()`

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

Get the orientation of the car in the world frame.

**Returns:**

Optional\[Orientation]: Current orientation as a quaternion (x, y, z, w).

### `getPosition()`

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

Get the position of the car in the world frame.

**Returns:**

Optional\[Position]: Current position of the car in world coordinates.

### `getState()`

```python Signature theme={null}
getState(proprioception_items: Optional[list[str]] = None) -> dict
```

Get the current state of the car.

**Returns:**

dict: Current state including position, orientation, and velocity.

### `moveByVelocity()`

```python Signature theme={null}
moveByVelocity(
    linear_velocity: Velocity,
    angular_velocity: Velocity,
    frame: str = 'body',
) -> None
```

Move the car with a velocity command.

For cars, velocity control is limited. The linear\_velocity.x\_vel controls
forward/backward speed, and angular\_velocity.z\_vel controls turning rate.

<ParamField body="linear_velocity" type="Velocity" required>
  Linear velocity in m/s (x\_vel used for forward speed).
</ParamField>

<ParamField body="angular_velocity" type="Velocity" required>
  Angular velocity in rad/s (z\_vel/yaw rate is used).
</ParamField>

<ParamField body="frame" type="str" default="'body'">
  Reference frame for velocity ("body" only). (default: "body")
</ParamField>

**Returns:**

None

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

Stop the car by zeroing target speed and controls.

**Returns:**

None


## Related topics

- [owlv2](/models/cortex/owlv2.md)
- [MDP Config](/simulation/isaac/session_configuration/mdp.md)
- [graspgen](/models/cortex/graspgen.md)
- [graspgenx](/models/cortex/graspgenx.md)
- [Camera Calibration](/deployment/camera-calibration.md)
