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

# Fanuc CRX-30iA Arm

> The Fanuc CRX-30iA Arm as GRID drives it

The Fanuc CRX-30iA Arm as GRID drives it — what this robot actually implements, with the signatures it actually accepts.

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

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

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

## Methods on the robot

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

### `activate_end_effector()`

```python Signature theme={null}
activate_end_effector(*, auto_suspend_stream_motion: bool = True) -> None
```

Run the end effector's one-time activation sequence, if it has one.

Robotiq's adaptive and EPick grippers need a rising edge on their
activation bit before any grasp or release call works. A gripper left
at `activate_on_start=True` runs this from its own bring-up hook;
call it here when activation was deferred, and again after a
controller power-cycle.

**Commands physical motion** on an adaptive gripper: activation drives
a reference sweep in which the jaws stroke to relearn their travel, so
anything between the fingers is dropped or crushed. Run it with the
cell clear.

No-op for an arm with no end effector, for a gripper on the Stream
Motion channel, and for one whose TP programs need no activation — so
it is safe to call unconditionally.

<ParamField body="auto_suspend_stream_motion" default="True">
  Pause a running Stream Motion session around the activation. A live `STREAM_MOTN` holds controller execution and `FRC_Call` only replies once its program has completed, so without this the call blocks until the RMI socket times out. Defaults to True.
</ParamField>

**Raises:**

NotImplementedError: If the gripper needs activation but the arm
was constructed without an RMI backend.
RuntimeError: If the arm was constructed with `read_only=True`.
FanucFault: If the controller reports an alarm while activating;
the pendant codes are on `FanucFault.codes`.

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

### `followJointTrajectory()`

```python Signature theme={null}
followJointTrajectory(
    angles: list,
    duration: 'Optional[float | list[float]]' = None,
    *,
    speed_override: Optional[int] = None,
    term_value: Optional[int] = None,
    blocking: bool = True,
    prefer: str = 'auto',
    auto_suspend_stream_motion: bool = True,
    payload_kg: Optional[float] = None,
) -> None
```

Stream a joint-space trajectory through Stream Motion or RMI.

Routes by `prefer`, except that a backend-specific argument forces its
own backend: `speed_override`/`term_value` force RMI,
`blocking=False` forces Stream Motion, and mixing the two raises. On
`prefer="auto"` Stream Motion is tried first; if the trajectory asks
for more speed, acceleration or jerk than the arm allows, the call falls
back to RMI and logs a warning. When the call lands on RMI
while Stream Motion is running, Stream Motion is paused for the move and
resumed after. Blocks until the trajectory finishes unless
`blocking=False`.

See the Fanuc guide (`examples/arm/pick_and_place/fanuc.md`) for the
full routing table.

<ParamField body="angles" required>
  Ordered joint-angle targets in radians; each entry has one value per joint, as long as `getJointAngles()`.
</ParamField>

<ParamField body="duration">
  Trajectory duration in seconds — a single float for the total (split evenly across segments), or one duration per segment, in which case its length must equal `len(angles)` (the leading segment from the current pose to `angles[0]` plus each inter-waypoint segment). Every entry must be positive. Defaults to None (backend default).
</ParamField>

<ParamField body="speed_override">
  RMI speed override in percent (1-100). RMI only, and passing it forces RMI. Defaults to None.
</ParamField>

<ParamField body="term_value">
  RMI blend radius, 0 (FINE) to 100 (full blend). RMI only, and passing it forces RMI. Defaults to None (`CNT_SMOOTH`).
</ParamField>

<ParamField body="blocking" default="True">
  Wait for the trajectory to finish. Stream Motion only; passing False forces Stream Motion. Defaults to True.
</ParamField>

<ParamField body="prefer" default="'auto'">
  Routing preference — `"auto"`, `"rmi"` or `"stream_motion"`. Defaults to `"auto"`.
</ParamField>

<ParamField body="auto_suspend_stream_motion" default="True">
  Pause an active Stream Motion session for the duration of a move that lands on RMI, and resume after. Ignored when the call routes to SM. Defaults to True.
</ParamField>

<ParamField body="payload_kg">
  Mass at the flange in kilograms (>= 0). A heavier load lowers the speed and acceleration the arm is allowed, so this sets what the safety check measures against. Defaults to None, which assumes the robot's rated maximum payload — the cautious reading when the caller has not said what is mounted. Pass 0.0 for an empty flange, the gripper's mass for an empty gripper, or gripper plus workpiece after grasping; nothing tracks what is on the wrist for you.
</ParamField>

**Raises:**

ValueError: If RMI-specific and Stream Motion-specific arguments are
mixed, if `prefer` is not one of the accepted values, if
`duration` is the wrong length or not positive, if
`payload_kg` is negative, or if the trajectory would exceed the
arm's speed, acceleration or jerk limits for the given payload.
NotImplementedError: If the arguments force a backend that is not
configured.

### `getCommandedJointAngles()`

```python Signature theme={null}
getCommandedJointAngles() -> list[float]
```

Return the joint angles the controller is currently commanding, in radians.

Asks the Stream Motion backend for the controller's commanded
position (the value the servo loop is targeting and the position SM
holds at between trajectories), which is where a streamed trajectory
must depart from — starting from the feedback position instead
injects the servo follow-error as a one-cycle step that the
controller's per-cycle jerk check reads as a spike (MOTN-722).

Resolves through the same
`FanucStreamMotion.command_anchor` the streaming thread uses,
so a trajectory planned from this value is streamed from that same
value. Resolving the two separately is what put an 80 ms buffer
lag on the wire as a one-cycle step at every stream-to-stream
boundary — see `command_anchor` for the full account.

Falls back to `getJointAngles` (feedback) when the Stream
Motion backend is not configured or the controller does not answer
the command-position request in time.

**Returns:**

Commanded joint angles in radians, ordered by joint index,
length `num_joints`.

### `getEndEffectorPose()`

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

Return the full end-effector pose from the RMI backend.

**Returns:**

Pose: End-effector pose (position + orientation).

**Raises:**

NotImplementedError: If no RMI backend is configured.

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

Return current joint angles in radians.

Routed to the preferred joint-read backend (Stream Motion if
configured, else RMI).

**Returns:**

list\[float]: 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() -> Orientation
```

Return the end-effector orientation from the RMI backend.

**Returns:**

Orientation: End-effector orientation as a quaternion.

**Raises:**

NotImplementedError: If no RMI backend is configured.

### `getPose()`

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

Return the current end-effector pose from the RMI backend.

**Returns:**

Pose: End-effector pose (position + orientation).

**Raises:**

NotImplementedError: If no RMI backend is configured.

### `getPosition()`

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

Return the end-effector position from the RMI backend.

**Returns:**

Position: End-effector position in meters.

**Raises:**

NotImplementedError: If no RMI backend is configured.

### `getState()`

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

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

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

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

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

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

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

<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(
    *,
    position: Optional[float] = None,
    force: Optional[float] = None,
    expect_grasp: Optional[bool] = None,
    auto_suspend_stream_motion: bool = True,
) -> None
```

Close the gripper.

Which transport carries it is fixed when the gripper is built, not
chosen here: a gripper given an `io=` mapping is driven as a binary
digital-I/O device over Stream Motion, and any other gripper runs its
TP programs over RMI. On the Stream Motion channel the session is
managed for you — brought up if it is not running and put back
afterwards — so this works between planned moves, which leave no
session open.

Commands no arm travel. On the Stream Motion channel it may command a
zero-displacement hold to get the streaming thread going: the arm holds
its pose, it does not travel.

This never confirms the grip. Check with `validateGrasp`.

<ParamField body="position">
  Target opening in the gripper's own units. RMI only, and only for a gripper whose TP programs take it. Defaults to None.
</ParamField>

<ParamField body="force">
  Target grip force in the gripper's own units. RMI only, on the same terms as `position`. Defaults to None.
</ParamField>

<ParamField body="expect_grasp">
  Use the program that fails when nothing ends up between the fingers. RMI only. Defaults to None, meaning True on the RMI channel.
</ParamField>

<ParamField body="auto_suspend_stream_motion" default="True">
  Pause a running Stream Motion session around the grasp. RMI path only. Some gripper programs move the arm, and those would wait forever while Stream Motion holds it, so pausing is the safe default. Pass False if you know your gripper program only toggles I/O. Defaults to True.
</ParamField>

**Raises:**

NotImplementedError: If no end effector is attached, or the chosen
transport is not configured.
ValueError: If an RMI-only argument is passed to a gripper on the
Stream Motion channel, or if the gripper's TP programs do not
accept `position` / `force`.
RuntimeError: If a staged Stream Motion write was not sent before
the stream was released, or the controller reports a latched
alarm while bringing Stream Motion up.
TypeError: If the attached end effector is neither a Stream Motion
I/O gripper nor an RMIGripper.

### `moveByVelocity()`

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

Command a Cartesian velocity via the RMI backend.

<ParamField body="linear_velocity" type="Velocity" required>
  Linear velocity command in m/s along each axis.
</ParamField>

<ParamField body="angular_velocity" type="Velocity" required>
  Angular velocity command in rad/s about each axis.
</ParamField>

<ParamField body="frame" type="str" default="'body'">
  Reference frame for the velocity command ("body" or "world"). Defaults to "body".
</ParamField>

<ParamField body="auto_suspend_stream_motion" type="bool" default="True">
  If True (default) and Stream Motion is currently active, transparently pause it around this RMI move. See `moveToPose` for why this is required.
</ParamField>

**Raises:**

NotImplementedError: If no RMI backend is configured.

### `moveToDeltaPose()`

```python Signature theme={null}
moveToDeltaPose(
    delta_pose: Pose,
    blocking: bool = True,
    *,
    prefer: str = 'auto',
    payload_kg: float = 0.0,
    pointcloud: Optional[Sequence] = None,
    speed_scale: float = _DEFAULT_SPEED_SCALE,
    moving_time: Optional[float] = None,
    accel_time: Optional[float] = None,
    speed: Optional[float] = None,
    continuous: Optional[bool] = None,
    term_value: Optional[int] = None,
    auto_suspend_stream_motion: bool = True,
) -> None
```

Move the end-effector by a relative Cartesian delta.

Routes exactly as `moveToPose` does — an arm in Stream Motion mode
plans a collision-aware route to the composed pose, one in RMI mode
drives a straight line — so the two Cartesian verbs honor the arm's mode
alike. The composition itself is the base
`Arm.moveToDeltaPose` one: position added, orientation composed.

<ParamField body="delta_pose" type="Pose" required>
  Pose offset composed with the current end-effector pose (position added, orientation composed).
</ParamField>

<ParamField body="blocking" type="bool" default="True">
  Wait for movement to complete. RMI only; a planned move always blocks. Defaults to True.
</ParamField>

<ParamField body="prefer" type="str" default="'auto'">
  Which path to take — `"auto"`, `"rmi"` or `"stream_motion"`. Defaults to `"auto"`.
</ParamField>

<ParamField body="payload_kg" type="float" default="0.0">
  Mass held at the flange in kilograms (>= 0). Planned path only. Defaults to 0.0.
</ParamField>

<ParamField body="pointcloud" type="Optional[Sequence]">
  Scene point cloud in the robot base frame (meters) to plan around. Planned path only. Defaults to None, meaning the robot avoids only itself.
</ParamField>

<ParamField body="speed_scale" type="float" default="_DEFAULT_SPEED_SCALE">
  How fast to move, as a fraction of the arm's rated limits. Planned path only.
</ParamField>

<ParamField body="moving_time" type="Optional[float]">
  Total move duration in seconds. RMI only; rejected on the planned path. Defaults to None, meaning 0.5 on the RMI path.
</ParamField>

<ParamField body="accel_time" type="Optional[float]">
  Acceleration ramp duration in seconds. RMI only; rejected on the planned path. Defaults to None, meaning 0.2 on the RMI path.
</ParamField>

<ParamField body="speed" type="Optional[float]">
  Cartesian linear speed in m/s. RMI only; rejected on the planned path. Defaults to None, meaning 0.1 on the RMI path.
</ParamField>

<ParamField body="continuous" type="Optional[bool]">
  Use CNT blending instead of FINE. RMI only; rejected on the planned path. Defaults to None, meaning False on the RMI path.
</ParamField>

<ParamField body="term_value" type="Optional[int]">
  CNT blend radius (0-100). RMI only; rejected on the planned path. Defaults to None, meaning `CNT_MAX` on the RMI path.
</ParamField>

<ParamField body="auto_suspend_stream_motion" type="bool" default="True">
  If True (default) and Stream Motion is currently active, transparently pause it around this RMI move. See `moveToPose` for why this is required.
</ParamField>

**Raises:**

ValueError: If `prefer` is not one of the accepted values, or if an
RMI-only argument is passed explicitly and the planned path is
taken.
NotImplementedError: If the chosen path's backend is not configured.
RuntimeError: On the planned path, if GRACE is not installed, if this
robot model has no `ROBOT_MODEL_NAME`, or if no collision-free
route to the composed pose exists.

### `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,
    moving_time: float = 2.0,
    accel_time: float = 0.75,
    *,
    speed_override: Optional[int] = None,
    prefer: str = 'rmi',
    auto_suspend_stream_motion: bool = True,
) -> None
```

Move to a previously registered named pose.

Named poses live on the composite (`self.named_poses`) and the move
executes via `setJointAngles`. Routes to RMI by default: named
poses are usually setup or recovery moves that swing the joints a long
way (from anywhere back to home), which the controller's own planner
handles smoothly, whereas Stream Motion's stricter smoothness check would
turn the same move down at the default duration.

<ParamField body="pose_name" required>
  Name of a pose previously registered via `setNamedPose`.
</ParamField>

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

<ParamField body="moving_time" default="2.0">
  Total move duration in seconds. Defaults to 2.0.
</ParamField>

<ParamField body="accel_time" default="0.75">
  Acceleration ramp duration in seconds. Defaults to 0.75.
</ParamField>

<ParamField body="speed_override">
  RMI speed override in percent (1-100). RMI only, and passing it forces RMI. Defaults to None.
</ParamField>

<ParamField body="prefer" default="'rmi'">
  Routing preference forwarded to `setJointAngles` — `"auto"`, `"rmi"` or `"stream_motion"`. Defaults to `"rmi"`.
</ParamField>

<ParamField body="auto_suspend_stream_motion" default="True">
  Pause an active Stream Motion session for the duration of a move that lands on RMI. Defaults to True.
</ParamField>

**Raises:**

ValueError: If `pose_name` is not a registered named pose.

### `moveToPose()`

```python Signature theme={null}
moveToPose(
    pose: Pose,
    blocking: bool = True,
    *,
    prefer: str = 'auto',
    payload_kg: float = 0.0,
    pointcloud: Optional[Sequence] = None,
    speed_scale: float = _DEFAULT_SPEED_SCALE,
    avoid_force: bool = True,
    moving_time: Optional[float] = None,
    accel_time: Optional[float] = None,
    speed: Optional[float] = None,
    continuous: Optional[bool] = None,
    term_value: Optional[int] = None,
    auto_suspend_stream_motion: bool = True,
) -> None
```

Move the end-effector to a Cartesian pose.

Two ways to get there, and by default the arm picks the one matching the
mode it is in — **RMI** drives a straight line and hits whatever is in the
way; **Stream Motion** plans a collision-aware route around the robot (and
around a `pointcloud` if you pass one) and streams it, which needs GRACE
installed and does not travel in a straight line. Pass `prefer` to force
one for a single call without changing the arm's mode.

Args below are marked for the path they apply to; the other path ignores
them. See `examples/arm/pick_and_place/fanuc.md` for the routing table.

<ParamField body="pose" required>
  Target end-effector pose in the base frame (position in meters, orientation as a unit quaternion).
</ParamField>

<ParamField body="blocking" default="True">
  Wait for the movement to complete. RMI only; a planned move always blocks. Defaults to True.
</ParamField>

<ParamField body="prefer" default="'auto'">
  Which path to take — `"auto"`, `"rmi"` or `"stream_motion"`. Defaults to `"auto"`.
</ParamField>

<ParamField body="payload_kg" default="0.0">
  Mass held at the flange in kilograms (>= 0), which lowers the speed and acceleration a planned move is allowed. Planned path only. Defaults to 0.0.
</ParamField>

<ParamField body="pointcloud">
  Scene point cloud in the robot base frame (meters) to plan around. Planned path only; the grasped object is not included. Defaults to None, meaning the robot avoids only itself.
</ParamField>

<ParamField body="speed_scale" default="_DEFAULT_SPEED_SCALE">
  How fast to move, as a fraction of the arm's rated limits, from just above 0 up to 1.0. Scales speed, acceleration and jerk together. Planned path only; lower is slower with more margin.
</ParamField>

<ParamField body="avoid_force" default="True">
  Accepted for signature parity with the other arms and with `FanucRMI`. It is a no-op on this arm on both paths — no Cartesian force limit is applied. Defaults to True.
</ParamField>

<ParamField body="moving_time">
  Total move duration in seconds. RMI only; rejected on the planned path. Defaults to None, meaning 2.0 on the RMI path.
</ParamField>

<ParamField body="accel_time">
  Acceleration ramp duration in seconds. RMI only; rejected on the planned path. Defaults to None, meaning 0.5 on the RMI path.
</ParamField>

<ParamField body="speed">
  Cartesian linear speed in m/s. RMI only; rejected on the planned path. Defaults to None, meaning 0.1 on the RMI path.
</ParamField>

<ParamField body="continuous">
  Blend through the target instead of stopping on it. RMI only; rejected on the planned path. Defaults to None, meaning False on the RMI path.
</ParamField>

<ParamField body="term_value">
  Blend radius, 0 (stop exactly) to 100 (full blend). RMI only; rejected on the planned path. Defaults to None, meaning `CNT_MAX` on the RMI path.
</ParamField>

<ParamField body="auto_suspend_stream_motion" default="True">
  Pause a running Stream Motion session for the duration of an RMI move and resume it after, since Stream Motion holds the arm and an RMI move issued underneath it would wait forever. RMI path only. Defaults to True.
</ParamField>

**Raises:**

ValueError: If `prefer` is not one of the accepted values, or if an
RMI-only argument is passed explicitly and the planned path is
taken.
NotImplementedError: If the chosen path's backend is not configured.
RuntimeError: On the planned path, if GRACE is not installed, if this
robot model has no `ROBOT_MODEL_NAME`, or if no collision-free
route to `pose` exists.

### `nativeToUrdfJoints()`

```python Signature theme={null}
nativeToUrdfJoints(angles: list[float]) -> list[float]
```

Convert FANUC wire-convention joints to the serial (URDF) convention.

FANUC controllers report and accept J3 world-referenced — the
controller folds J2 motion out of J3 (the J2/J3 interaction) — while
a serial-chain URDF expresses J3 relative to the upper arm. The
mapping is `J3_serial = J3_wire + J2`, verified on a real CRX-30iA
(FK of `J3_wire + J2` reproduces the controller's own Cartesian
TCP; the raw wire values do not). This is a reporting-convention
difference, not geometry: both the RMI and Stream Motion backends
deliberately speak the raw wire convention, so the conversion lives
here at the arm boundary, never in the URDF.

`urdfToNativeJoints` is the exact inverse.

<ParamField body="angles" required>
  Joint vector in FANUC wire convention (radians), ordered J1..Jn with n >= 3.
</ParamField>

**Returns:**

The same physical configuration in serial (URDF) convention
(radians), as a new list (`result[2] = angles[2] + angles[1]`).

**Raises:**

ValueError: If `angles` has fewer than 3 entries — the J2/J3
conversion needs at least joints J1..J3.

### `network_addresses()`

```python Signature theme={null}
network_addresses(args: dict) -> list[str]
```

Controller addresses a routing check should verify for these args.

<ParamField body="args" required>
  Config `args` for this robot, as the wizard collected them.
</ParamField>

**Returns:**

The controller address, or none when no address is configured.

### `readFaultCodes()`

```python Signature theme={null}
readFaultCodes(fault: RobotFault) -> list[str]
```

Return the controller alarm codes for a caught fault, best-effort.

Uses the codes already attached to `fault` when it has them. RMI faults
usually do not, so for those this asks the controller for its current
alarm codes instead. Only ask while the arm is idle — call this where you
catch the fault, not mid-motion. It never raises: if the read fails you
get the fault's own codes back (possibly none), so this can never block
your recovery path.

<ParamField body="fault" required>
  The caught controller fault to resolve codes for.
</ParamField>

**Returns:**

list\[str]: Alarm codes in `"XXXX-NNN"` form (e.g.
`["MOTN-722"]`), most-recent first. Empty when none were
attached to the fault and none could be read back (no RMI
backend configured, or the read-back failed).

### `recoverFromFault()`

```python Signature theme={null}
recoverFromFault(fault: RobotFault) -> bool
```

Try to clear a controller fault and get the arm ready to move again.

Call this where you catch a fault, before retrying whatever failed. It
clears the alarm and puts Stream Motion back the way it was, so a retry
starts from a known state rather than a half-torn-down one.

A fault the driver marked unrecoverable is not worth resetting, so this
returns False without touching the arm. Otherwise a False means the alarm
is still latched — an emergency stop or a servo alarm — and somebody has
to clear it on the teach pendant before the robot will move.

Never raises: a failed reset is an answer (False), not an error, so your
recovery path is never itself the thing that crashes.

<ParamField body="fault" required>
  The controller fault you caught.
</ParamField>

**Returns:**

True when the arm is cleared and ready to retry, False when it is not
and a human needs to intervene.

### `release()`

```python Signature theme={null}
release(
    *,
    force: Optional[float] = None,
    auto_suspend_stream_motion: bool = True,
) -> None
```

Open the gripper.

Mirrors `grasp`, including the transport choice and the Stream
Motion session handling. On the Stream Motion channel it drives the
open pattern, holds it for the mapping's `release_hold_s`, then
returns the gripper to idle — FANUC outputs latch, so otherwise the
release solenoid would stay energized indefinitely.

<ParamField body="force">
  Target release force in the gripper's own units. RMI only, and only for a gripper whose TP programs take it. Defaults to None.
</ParamField>

<ParamField body="auto_suspend_stream_motion" default="True">
  Pause a running Stream Motion session around the release. RMI path only; see `grasp` for why this is the safe default. Defaults to True.
</ParamField>

**Raises:**

NotImplementedError: If no end effector is attached, or the chosen
transport is not configured.
ValueError: If `force` is passed to a gripper on the Stream
Motion channel, or if the gripper's TP programs do not accept
it.
RuntimeError: If a staged Stream Motion write was not sent before
the stream was released, or the controller reports a latched
alarm while bringing Stream Motion up.
TypeError: If the attached end effector is neither a Stream Motion
I/O gripper nor an RMIGripper.

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

### `reset()`

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

Reset / reconnect every configured backend.

Errors from one backend do not prevent the others from being
reset. The first encountered error is re-raised after every
backend has been reset.

**Raises:**

Exception: The first error raised by any backend, after all
backends have been asked to reset.

### `setJointAngles()`

```python Signature theme={null}
setJointAngles(
    angles: list,
    blocking: bool = True,
    moving_time: Optional[float] = None,
    accel_time: Optional[float] = None,
    *,
    speed_override: Optional[int] = None,
    continuous: bool = False,
    term_value: Optional[int] = None,
    max_joint_speed_rad_s: Optional[float] = None,
    prefer: str = 'auto',
    auto_suspend_stream_motion: bool = True,
) -> None
```

Move the arm to the given joint angles.

Routes by `prefer`, except that a backend-specific argument forces its
own backend: `speed_override`, `continuous=True` or `term_value`
force RMI, `max_joint_speed_rad_s` forces Stream Motion, and mixing the
two sets raises. When the call lands on RMI while Stream Motion is
active, SM is paused for the move and resumed after.

<ParamField body="angles" required>
  Target joint angles in radians, one per joint.
</ParamField>

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

<ParamField body="moving_time">
  Movement duration in seconds. Defaults to None (backend default).
</ParamField>

<ParamField body="accel_time">
  Acceleration ramp time in seconds. Defaults to None.
</ParamField>

<ParamField body="speed_override">
  RMI speed override in percent (1-100). RMI only, and passing it forces RMI. Defaults to None.
</ParamField>

<ParamField body="continuous" default="False">
  Use RMI CNT blending instead of FINE. RMI only, and passing True forces RMI. Defaults to False.
</ParamField>

<ParamField body="term_value">
  RMI blend radius, 0 (FINE) to 100 (full blend). RMI only, and passing it forces RMI. Defaults to None (`CNT_MAX`).
</ParamField>

<ParamField body="max_joint_speed_rad_s">
  Stream Motion peak joint speed in rad/s, used to derive the duration when `moving_time` is None. Stream Motion only, and passing it forces SM. Defaults to None.
</ParamField>

<ParamField body="prefer" default="'auto'">
  Routing preference — `"auto"`, `"rmi"` or `"stream_motion"`. Defaults to `"auto"`.
</ParamField>

<ParamField body="auto_suspend_stream_motion" default="True">
  Pause an active Stream Motion session for the duration of a move that lands on RMI. Ignored when the call routes to SM. Defaults to True.
</ParamField>

**Raises:**

ValueError: If RMI-specific and Stream Motion-specific arguments are
mixed, or if `prefer` is not one of the accepted values.
NotImplementedError: If the arguments force a backend that is not
configured.

### `setPayload()`

```python Signature theme={null}
setPayload(schedule_number: int) -> None
```

Select a payload schedule on the controller.

<ParamField body="schedule_number" type="int" required>
  Payload schedule number on the controller (1-based).
</ParamField>

**Raises:**

NotImplementedError: If no RMI backend is configured.

### `setSpeedOverride()`

```python Signature theme={null}
setSpeedOverride(speed_override: int) -> None
```

Set the controller's global speed override percentage.

<ParamField body="speed_override" type="int" required>
  Speed override as percent (1-100).
</ParamField>

**Raises:**

NotImplementedError: If no RMI backend is configured.

### `start_stream_motion()`

```python Signature theme={null}
start_stream_motion(timeout_s: float = _RESUME_CMD_READY_TIMEOUT_S) -> None
```

Start Stream Motion so high-rate joint streaming can run.

This is both the first-time start for an arm built with
`use_stream_motion=False`, and the counterpart to
`stop_stream_motion` after an RMI move paused the session. It
clears the position the controller was last told to hold, hands motion
control over, starts the controller-side program and waits until the
controller is accepting commands.

If an alarm is still latched this refuses to start rather than quietly
clearing it — acknowledge the alarm first with `reset` or on the
teach pendant.

Idempotent: a no-op when no Stream Motion backend is configured, or when
a session is already running.

<ParamField body="timeout_s" default="_RESUME_CMD_READY_TIMEOUT_S">
  Seconds to wait for the controller to report it is accepting commands. Defaults to 5.0.
</ParamField>

**Raises:**

RuntimeError: If `stream_motion` is configured but `rmi` is not
(RMI is what relaunches the program), or if the controller
reports a latched alarm.
TimeoutError: If the controller does not report itself ready within
`timeout_s`.

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

### `stop_stream_motion()`

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

Pause Stream Motion so RMI moves can run.

While a Stream Motion session is running it holds the arm, and any RMI
move issued underneath it would simply wait forever. This stops the
session so RMI can take over. Joint feedback keeps flowing throughout —
only command control is handed back — so reads still work while paused,
and `start_stream_motion` resumes.

Idempotent: a no-op when no Stream Motion backend is configured, or when
it is already paused.

**Raises:**

RuntimeError: If `stream_motion` is configured but `rmi` is not —
RMI is what stops the controller-side program.

### `urdfToNativeJoints()`

```python Signature theme={null}
urdfToNativeJoints(angles: list[float]) -> list[float]
```

Convert serial-convention (URDF) joints to the FANUC wire convention.

Exact inverse of `nativeToUrdfJoints`
(`result[2] = angles[2] - angles[1]`). Apply to every joint vector
produced by a motion planner before handing it to this arm's
execution methods — the RMI and Stream Motion backends speak the raw
FANUC wire convention, in which J3 is world-referenced (the J2/J3
interaction). Executing serial-convention joints raw moves the elbow
wrong by exactly J2.

<ParamField body="angles" required>
  Joint vector in serial (URDF) convention (radians), ordered J1..Jn with n >= 3.
</ParamField>

**Returns:**

The same physical configuration in FANUC wire convention
(radians), as a new list.

**Raises:**

ValueError: If `angles` has fewer than 3 entries — the J2/J3
conversion needs at least joints J1..J3.

### `validateGrasp()`

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

Report whether the gripper is currently holding a part.

On the **Stream Motion** channel this takes a fresh reading rather than
trusting a cached one: it brings the session up if needed, discards the
previous value, waits for the sensor to settle from live status
packets, and puts the session back. A part can be lost while nothing is
streaming, so a cached value could report a part that is gone.

Commands physical motion when the streaming thread is not already
running: a zero-displacement hold at the current joint angles. The arm
holds its pose; it does not travel.

On the **RMI** channel this asks the gripper itself, and the base
gripper has no sensor to ask — grip state lives on the controller.

**Returns:**

bool: True when a gripped part is detected. False when the sensor
reads open, and also when no stable reading arrives within the
mapping's `confirm_timeout_s` — an unknown state never reads as
"holding".

**Raises:**

NotImplementedError: On the RMI channel, where the gripper has no
grip sensor; or on the Stream Motion channel when the mapping
configures no feedback point.
RuntimeError: If no end effector is attached, or the controller
reports a latched alarm while bringing Stream Motion up.
TimeoutError: If Stream Motion does not become ready to accept
commands.

## Properties on the robot

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

<ResponseField name="home_pose" type="list[float]">
  Joint angles (radians) defining the CRX-30iA home pose.
</ResponseField>

<ResponseField name="named_poses" type="Dict[str, List[float]]">
  Registered poses as `&#123;name: joint angles in radians&#125;` (a copy).

  Mutating the returned mapping does not change what the arm knows:
  register and remove entries with `addNamedPose` and
  `removeNamedPose`, or assign a whole mapping to replace them all.
</ResponseField>

<ResponseField name="total_payload_kg" type="float">
  Worst-case total mass at the flange.

  Always returns the robot's rated `max_payload_kg` (or 0.0
  if the subclass doesn't define one). The end-effector spec's
  `mass_kg` and `held_mass_kg` fields are NOT auto-applied
  here, deliberately: an end-effector picks up and drops
  workpieces, and any host-side count of "what's currently on
  the wrist" is fragile (one missed update and the pre-flight
  runs against a too-permissive limit, controller alarms).

  To opt into a lower payload for a specific motion, pass
  `payload_kg=...` per call. That makes the assumption
  explicit at the call site instead of buried in cached state.
  For a fully-empty arm: `payload_kg=0.0`. For a known
  gripper-only weight: `payload_kg=ee.mass_kg`. For a
  gripper carrying a known workpiece:
  `payload_kg=ee.mass_kg + workpiece_kg`.
</ResponseField>

## Configured children

* `robot.end_effector` — the gripper, chosen in the robot's configuration; when one is fitted, its methods are the [`EndEffector`](/v2.2/python-api/robot-interface/endeffector) interface's.

## Not implemented on this robot

Declared by the interface, raises `NotImplementedError` here: `endFreeDrive`, `startFreeDrive`.

## Lifecycle and configuration

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

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

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


## Related topics

- [Python APIs](/v2.2/python-api/overview.md)
- [Fanuc LR Mate 200iD Arm](/v2.2/python-api/fanuclrmate200id/fanuclrmate200id.md)
- [RobotFault](/v2.2/python-api/grid-types/robotfault.md)
- [Arm](/v2.2/python-api/robot-interface/arm.md)
- [Flexiv Rizon Arm](/v2.2/python-api/flexiv-rizon/flexivrizon.md)


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