Inspect Robots adapters for WidowX 250S arms and OpenVLA or openpi policies.
Project description
inspect-robots-widowx
Run Inspect Robots evals on existing WidowX 250S arms with OpenVLA or openpi policy servers.
Note: Trossen discontinued the WidowX 250S in July 2025. This package supports existing 250S rigs. The successor WidowX AI uses a different stack and is outside this package's scope.
Inspect Robots has two swappable inputs, a Policy and an Embodiment. This
package provides three registered components:
openvlapolicy: a REST client for OpenVLA's first-party/actserver.openpipolicy: a websocket client for community Bridge fine-tunes.widowxembodiment: a client for the BridgeData WidowX robot server.
Every pairing uses the same 7-D eef_delta_pose contract. Slots 0 through 5
are xyz and roll/pitch/yaw displacements. Slot 6 is an absolute normalized
gripper target, where 0 is closed and 1 is open. Both policy pairings pass
compatibility with zero errors and zero warnings.
Install
Client machine
Create an environment and install the package:
uv venv && source .venv/bin/activate
uv pip install inspect-robots-widowx
uv pip install "edgeml @ git+https://github.com/youliangtan/edgeml@b4b8495b489e7c973187742d2f2fe9aa016d9aca"
uv pip install "widowx_envs @ git+https://github.com/rail-berkeley/bridge_data_robot@b841131ecd512bafb303075bd8f8b677e0bf9f1f#subdirectory=widowx_envs"
Caution: The PyPI project named
edgemlis an unrelated Microsoft package. Do not install it for this adapter. Use the pinned git command above.
Both BridgeData repositories are frozen and have seen no upstream development since 2024. The exact commits above make the old robot stack reproducible and limit dependency substitution risk. Review and vendor them for long-lived rigs.
Install openpi-client only when using an openpi policy server:
uv pip install "openpi-client @ git+https://github.com/Physical-Intelligence/openpi.git#subdirectory=packages/openpi-client"
Robot host
The robot host runs the upstream ROS1 Noetic BridgeData server inside its Docker environment. Pin the same server source used by the client install:
git clone https://github.com/rail-berkeley/bridge_data_robot.git
cd bridge_data_robot
git checkout b841131ecd512bafb303075bd8f8b677e0bf9f1f
Build and enter the ROS1 Noetic image using the scripts under the repository's
widowx_envs/docker directory, then start the service inside the container:
widowx_env_service --server --port 5556
The ZMQ request and broadcast ports are 5556 and 5557 by default. Expose both ports to the client network. Install the upstream Interbotix udev rules on the host, pass the robot USB devices into the container, and verify stable device permissions after every replug. Run the upstream manual motion checks inside the container before connecting a learned policy.
GPU machine
Install OpenVLA from source and serve a Bridge checkpoint:
git clone https://github.com/openvla/openvla.git
cd openvla
pip install -e .
python vla-scripts/deploy.py \
--openvla_path openvla/openvla-7b \
--port 8000
The client sends unnorm_key="bridge_orig". Use a checkpoint that carries the
Bridge normalization statistics or override unnorm_key for the fine-tune.
OpenVLA's server does not resize requests. The default client transport resizes
the raw 640x480 frame to 256x256 with Pillow Lanczos and performs an in-memory
JPEG encode/decode round trip to approximate the Bridge evaluation pipeline.
Openpi has no official Bridge checkpoint. The openpi entry point is for a
bring-your-own community or local Bridge fine-tune served by openpi.
Note: inspect-robots-franka also registers a policy named
openpi(the DROID client). With both packages installed,--policy openpiresolves to whichever distribution the registry enumerates first; a mismatch fails loudly at preflight with an action dimension error. Uninstall one package or use the Python API to disambiguate.
Preflight
Preflight constructs metadata only. It does not connect to the robot or a policy server.
inspect-robots-widowx-preflight
inspect-robots-widowx-preflight --task cubepick-reach
inspect-robots-widowx-preflight --dry-run
inspect-robots-widowx-preflight --json --dry-run
A green report verifies the action dimension, control mode, rotation
representation, gripper kind, frame, camera, state requirements, and optional
scene realizability. JSON output includes a dry_run field.
Run on hardware
Add defaults to ~/.config/inspect-robots/config.ini:
[defaults]
policy = openvla
embodiment = widowx
scorer = success_at_end
max_steps = 200
store_frames = true
[policy.args]
server_url = http://192.168.1.20:8000
endpoint = /act
unnorm_key = bridge_orig
[embodiment.args]
host = 192.168.1.10
port = 5556
control_hz = 5.0
move_duration = 0.2
move_to_start = true
Run a task or direct instruction:
inspect-robots run --task cubepick-reach --policy openvla --embodiment widowx
inspect-robots "pick up the red block" --policy openvla --embodiment widowx
For openpi, change policy = openpi and set host, port,
action_horizon = 10, and replan_interval = 5 under [policy.args].
Reset first calls the server's neutral reset. By default it then performs a
blocking move to [0.3, -0.09, 0.26] using the reference OpenVLA 4x4 rotation.
The attended flow asks for scene readiness after reset. Press Enter during the
episode and answer y/N to score it. WidowXConfig(unattended=True) skips all
prompts and operator polling.
Safety
Warning: Keep an operator at the e-stop for first runs, after container or udev changes, and whenever a checkpoint or camera arrangement changes.
- Hard clamp: every
step()rejects non-finite actions and clips all seven values todelta_low/highinside the embodiment, independent of anApprover. - Mixed delta and absolute semantics: xyz and rpy are per-step displacements, but the gripper slot is an absolute target. It is never a gripper rate.
- Control rate: the server uses
move_duration=0.2and the embodiment self-paces at 5 Hz. These fields must remain reciprocal. - Workspace: the BridgeData workspace boundaries are enforced server-side. Client action bounds do not replace collision checking, physical stops, or workspace validation.
- Discontinued hardware: use this adapter only for maintained existing 250S rigs. Confirm spare parts, e-stop behavior, and container recovery before unattended operation.
- First-run verification: with the arm clear, use small asymmetric actions, confirm all xyz and rpy signs, then command gripper values 0.25 and 0.75 while the pose slots remain zero. Verify that the larger value opens the gripper.
In displacement mode, DeltaLimitApprover derives no additional default limit
beyond the configured action box. There is no reference tracking. Clamping is
applied to the action value itself. Do not use a scalar
--max-action-delta 0.05 with this contract. A scalar intersects every slot
with [-0.05, 0.05], so the absolute gripper's [0, 1] range becomes
[0, 0.05]. That pins the gripper nearly shut forever. It does not rate-limit
the gripper.
Python callers can provide a per-dimension vector and leave the gripper limit at 1.0:
import numpy as np
from inspect_robots import eval
from inspect_robots.approver import ChainApprover, ClampApprover, DeltaLimitApprover
from inspect_robots_widowx import OpenVLAPolicy, WidowXEmbodiment
embodiment = WidowXEmbodiment(host="192.168.1.10")
space = embodiment.info.action_space
approver = ChainApprover(
ClampApprover(space),
DeltaLimitApprover(
space,
max_delta=np.asarray([0.05, 0.05, 0.05, 0.25, 0.25, 0.25, 1.0]),
),
)
logs = eval(
"cubepick-reach",
OpenVLAPolicy(server_url="http://192.168.1.20:8000"),
embodiment,
approver=approver,
)
Configuration
Action and state units
| Slot | Label | Action meaning | Observed state meaning |
|---|---|---|---|
| 0 | dx |
x displacement in metres | x position in metres |
| 1 | dy |
y displacement in metres | y position in metres |
| 2 | dz |
z displacement in metres | z position in metres |
| 3 | droll |
roll displacement in radians | controller-relative roll in radians |
| 4 | dpitch |
pitch displacement in radians | controller-relative pitch in radians |
| 5 | dyaw |
yaw displacement in radians | controller-relative yaw in radians |
| 6 | gripper |
absolute normalized target | normalized opening, 0 closed and 1 open |
The action declares rotation_repr="none" deliberately. The dimension labels
carry the rpy meaning while
inspect-robots issue #143
tracks support for Euler pose displacement guardrails. Declaring
euler_xyz today makes the default delta approver reject the space.
WidowXConfig fields
| Field | Default | Meaning |
|---|---|---|
host |
localhost |
BridgeData robot server host |
port |
5556 |
ZMQ request port, with broadcast on port + 1 |
control_hz |
5.0 |
Self-paced command rate |
start_move_duration_s |
0.8 |
Duration of the blocking move to the start pose at reset |
move_duration |
0.2 |
Server step duration, required to equal 1/control_hz |
delta_low |
translation -0.05, rotation -0.25, gripper 0 | Per-step hard lower bounds |
delta_high |
translation 0.05, rotation 0.25, gripper 1 | Per-step hard upper bounds |
move_to_start |
True |
Apply the OpenVLA evaluation start pose after neutral reset |
start_eef_pos |
(0.3, -0.09, 0.26) |
Translation inserted into the reference 4x4 transform |
start_transform |
None |
Advanced 16-value row-major 4x4 override; replaces position and rotation |
obs_timeout_s |
10.0 |
Maximum wait for a non-None server observation |
unattended |
False |
Skip readiness and scoring prompts |
docs_extra |
empty | Rig-specific notes appended to embodiment docs |
There is no start_eef_quat field. The reference controller's start rotation
cannot be reproduced from the commonly documented quaternion because the
controller composes an internal default rotation.
OpenVLAConfig fields
| Field | Default | Meaning |
|---|---|---|
server_url |
http://127.0.0.1:8000 |
OpenVLA server base URL |
endpoint |
/act |
REST endpoint |
unnorm_key |
bridge_orig |
Action normalization statistics key |
timeout_s |
30.0 |
HTTP request timeout |
name |
openvla |
Policy label in eval logs |
resize_px |
256 |
Default transport square resize |
jpeg_roundtrip |
True |
Match Bridge evaluation image compression |
OpenVLA always advertises action horizon 1 and no replanning interval. A custom
injected post_fn receives the raw camera frame and owns all preprocessing.
OpenpiConfig fields
| Field | Default | Meaning |
|---|---|---|
host, port |
127.0.0.1, 8000 |
openpi websocket server |
api_key |
None |
Optional authentication, excluded from eval policy config |
action_horizon |
10 |
Maximum returned action chunk length |
replan_interval |
5 |
Actions consumed before reinference |
name |
openpi |
Policy label in eval logs |
resize_px |
224 |
Default resize-with-pad size |
Development
Use the local cache path required by this workspace:
export UV_CACHE_DIR="$PWD/.uv-cache"
uv sync --extra dev
uv run ruff check .
uv run ruff format --check .
uv run mypy
uv run pytest --cov
The tests inject clients, transports, clocks, sleeps, operator I/O, and end polling. They require no robot, server, camera, network, or stdin. Coverage is enforced at 100 percent with branch coverage enabled.
Dependency changes require uv lock. CI uses uv sync --locked; the weekly
canary resolves current allowed versions without the lockfile.
Citation
@software{inspect-robots-widowx,
author = {Robocurve},
title = {Inspect Robots WidowX: OpenVLA and openpi adapters for WidowX 250S arms},
year = {2026},
url = {https://github.com/robocurve/inspect-robots-widowx},
license = {MIT}
}
See CITATION.cff for citation metadata.
License
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file inspect_robots_widowx-0.1.0.tar.gz.
File metadata
- Download URL: inspect_robots_widowx-0.1.0.tar.gz
- Upload date:
- Size: 122.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4ff5a4ea53cdbf839fee3f061659aa68c0b77b777160662900bff4f59e0c48ef
|
|
| MD5 |
0593d21f36c57769a765aee0f489556b
|
|
| BLAKE2b-256 |
9e739079e51adb7732f017e4aa70cf44234beb60ce98de80fd54a0d0e1c26188
|
Provenance
The following attestation bundles were made for inspect_robots_widowx-0.1.0.tar.gz:
Publisher:
release.yml on robocurve/inspect-robots-widowx
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
inspect_robots_widowx-0.1.0.tar.gz -
Subject digest:
4ff5a4ea53cdbf839fee3f061659aa68c0b77b777160662900bff4f59e0c48ef - Sigstore transparency entry: 2195587966
- Sigstore integration time:
-
Permalink:
robocurve/inspect-robots-widowx@c0b2758946e2abb39cd83b06f1bdc732878cd30c -
Branch / Tag:
refs/heads/main - Owner: https://github.com/robocurve
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@c0b2758946e2abb39cd83b06f1bdc732878cd30c -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file inspect_robots_widowx-0.1.0-py3-none-any.whl.
File metadata
- Download URL: inspect_robots_widowx-0.1.0-py3-none-any.whl
- Upload date:
- Size: 24.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9a8670907abe98e8aadf42a96e41fa4173d5e0b595f0333747973638fca04177
|
|
| MD5 |
9844543bb12da2151ffa044acb99e15d
|
|
| BLAKE2b-256 |
6ea65f8519ae224fd74cfb0b37b558c4565d1a93e73a93dab82fa52d8b6e0005
|
Provenance
The following attestation bundles were made for inspect_robots_widowx-0.1.0-py3-none-any.whl:
Publisher:
release.yml on robocurve/inspect-robots-widowx
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
inspect_robots_widowx-0.1.0-py3-none-any.whl -
Subject digest:
9a8670907abe98e8aadf42a96e41fa4173d5e0b595f0333747973638fca04177 - Sigstore transparency entry: 2195587976
- Sigstore integration time:
-
Permalink:
robocurve/inspect-robots-widowx@c0b2758946e2abb39cd83b06f1bdc732878cd30c -
Branch / Tag:
refs/heads/main - Owner: https://github.com/robocurve
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@c0b2758946e2abb39cd83b06f1bdc732878cd30c -
Trigger Event:
workflow_dispatch
-
Statement type: