Install
openclaw skills install @huaweiclouddev/huawei-cloud-cloudrobo-r2cRun the R2C (Robot-to-Cloud) data-plane client — start the robot-side edge client (Zenoh pub/sub with mTLS credential bundle, hardware adapter, translator, control loop). The credential bundle is produced by the robot skill's export-certificate command. This skill does NOT cover robot registration or certificate export (use the robot skill). Triggers include: r2c client, robot edge client, Zenoh, mTLS, credential bundle, hardware adapter, robot config, dry_run, observation recording, custom adapter, robot-to-cloud, R2C客户端, 硬件适配器, 机器人配置, 凭证包.
openclaw skills install @huaweiclouddev/huawei-cloud-cloudrobo-r2cWindows / PowerShell: Examples use bash syntax. To run on Windows PowerShell:
- Flatten
\line continuations to a single line, or end lines with a backtick.- Set env vars with
$env:NAME="value"instead ofexport NAME="value".- Single-quoted JSON
'{"a":"b"}'works as-is.
The cloudrobo-r2c skill operates the R2C (Robot-to-Cloud) data-plane SDK. Unlike other
cloudrobo skills that call REST APIs via cloudrobo-service, this skill runs long-lived
data-plane processes that use Zenoh pub/sub with Protobuf serialization and mTLS
authentication. It covers one CLI command: r2c client (robot edge client).
Applicable scenarios:
IRobotHardwareAdapter classes and ConfigurableDeviceTranslator mappings for robots
not in the built-in adapter list.Architecture:
Zenoh pub/sub (mTLS)
┌─────────────┐ observations ┌──────────────────┐
│ Robot Edge │ ─────────────► │ │
│ Client │ │ Zenoh Router │
│ (r2c client)│ ◄───────────── │ │
└──────┬──────┘ actions └──────────────────┘
│
┌──────▼──────┐
│ Hardware │
│ Adapter │
│ (robot arm, │
│ gripper...) │
└─────────────┘
Credential Bundle (from robot skill export-certificate)
├── device_info.json (account_id, robot_id, permission_role)
├── zenoh.json (mode, endpoints, mTLS config)
├── ca.pem (CA certificate)
├── server_cert.pem (client certificate — note: named server_cert.pem in the exported bundle)
└── server_key.pem (private key, optionally encrypted)
--bundleaccepts the access-config zip directly (or an extracted directory); the SDK resolves the certificate paths insidezenoh.json(which references acerts/-style layout) automatically. Do not manually rename the cert files.
The R2C data plane is decoupled from the CloudRobo REST API. The platform's cloudrobo-service
handles robot registration and certificate export (covered by the robot skill); the R2C
SDK consumes the exported credential bundle to establish mTLS-authenticated Zenoh sessions.
references/cli-installation-guide.md for CLI installation, R2C package extras, and
credential bundle preparation.cloudrobo robot export-certificate (robot skill).
The bundle contains device_info.json, zenoh.json, ca.pem, server_cert.pem, and
server_key.pem.robot skill: create →
export-certificate). The robot_id in the credential bundle identifies the robot.Scenario: "Start the R2C client on the robot to stream observations and execute cloud actions."
export-certificate writes cert_config_<robot-name>_<timestamp>.zip into the output
directory — pass that zip path to --bundle:
cloudrobo r2c client --bundle <path/to/cert_config_*.zip> --robot-config config/robot_dummy_config.yaml
--private-key-password / --private-key-password-env).Scenario: "Test the R2C client without moving the real robot."
runtime.dry_run: true. This publishes
real observations but does not execute received actions (logs only).cloudrobo r2c client --bundle <bundle> --robot-config config/robot_dummy_config.yaml
runtime.dry_run: false and restart for real execution.Scenario: "My robot is not in the built-in adapter list. How do I add support?"
The R2C SDK provides two approaches for custom adapters: entry_point registration (recommended for reusable, shareable adapters) and CLI override (for quick prototyping).
Approach A: Entry-Point Registration (Recommended) 入口点注册(推荐)
references/custom-adapter-guide.md for the full development guide.IRobotHardwareAdapter
(connect(), disconnect(), get_observation(), send_action()).def create_my_adapter(config, **kwargs) -> IRobotHardwareAdapter.pyproject.toml, register under
[project.entry-points."r2c_sdk.adapters"]: my_robot = "my_adapter_module:create_my_adapter".pip install . registers the adapter under the
r2c_sdk.adapters entry-point group, making it discoverable by the SDK's
AdapterRegistry (used by RobotFactory and robot-config schema validation).hardware.type: "my_robot" (matching the entry_point name) in the
robot config YAML. No --hardware-class CLI override needed.register_command_class() in __post_init__ for
custom commands (e.g., go_home), then reference them in hardware.config.commands in
the robot config.ConfigurableDeviceTranslator with device_to_r2c and
r2c_to_device mapping sections in the robot config YAML.dry_run: true → verify
observation publishing and action logging.Approach B: CLI Override (Quick Prototyping) CLI 覆盖(快速原型)
hardware.type: "custom" and hardware.class_path to the dotted
import path of your adapter class in the robot config YAML. Alternatively, use
--hardware-class my_pkg.my_module.MyAdapter on the CLI.dry_run: true → verify
observation publishing and action logging.When to use which: Use entry_point registration when the adapter will be reused across projects or shared with other teams. Use CLI override for one-off testing or when you cannot create a separate package.
Scenario: "Record observations for later playback analysis."
cloudrobo r2c client --bundle <bundle> --robot-config <config> --record ./observations.pkl.pkl file.playback adapter type with the recorded file for replay.cloudrobo r2c client [OPTIONS]
| Feature | Description | Example |
|---|---|---|
| Command group | r2c (registered via entry point) | cloudrobo r2c |
| Subcommand | client | cloudrobo r2c client |
| Credential bundle | --bundle <path> (zip or directory, recommended) | --bundle ./cert.zip |
| Client config | --client-config <path> (default config/client_config.yaml) | alternative to --bundle |
| Robot config | --robot-config <path> (default config/robot_dummy_config.yaml) | --robot-config config/robot_jaka_sdk_config.yaml |
| Endpoints | --endpoints tls/127.0.0.1:7447 (comma-separated) | Zenoh router address |
| Output format | Log output to stdout/stderr | structured logging |
| Long-running | Runs until Ctrl+C or --duration | --duration 60 |
cloudrobo r2c client --bundle <path/to/cert_config_*.zip> --robot-config config/robot_dummy_config.yaml [--client-config config/client_config.yaml] [--duration 0] [--log-level INFO] [--log-file ./r2c.log] [--record ./observations.pkl] [--hardware-class my_pkg.my_module.MyAdapter] [--translator-class my_pkg.my_module.MyTranslator] [--private-key-password <password>] [--private-key-password-env <ENV_VAR>] [--no-prompt-password]
Alternative (without bundle, using client config + explicit parameters):
cloudrobo r2c client --client-config config/client_config.yaml --project-id <project-id> --device-id <device-id> --endpoints tls/127.0.0.1:7447 --mode peer --robot-config config/robot_dummy_config.yaml
Connection priority: --bundle (recommended) > --client-config > explicit CLI params
(requires --project-id and --device-id).
Lifecycle: load_yaml(robot_config) → build_session(args) →
_maybe_start_heartbeats(session, robot_config) → build_sync_robot_client(...) →
hardware_adapter.connect() → robot_client.start() → wait (duration or Ctrl+C) →
robot_client.stop() → hardware_adapter.disconnect() → session.close().
| Parameter | Default | Description |
|---|---|---|
--bundle | None | Path to credential bundle zip/dir (recommended; supersedes client-config) |
--client-config | config/client_config.yaml | Path to R2C client config YAML |
--project-id | None | Project ID (required if no bundle and no client-config) |
--device-id | None | Device ID (required if no bundle and no client-config) |
--client-id | None | Client ID (defaults to sync-robot-client) |
--endpoints | "" | Comma-separated Zenoh endpoints, e.g. tls/127.0.0.1:7447 |
--mode | peer | Zenoh mode: peer or client |
--endpoint-role | None | connect (active, default) or listen (passive) |
--robot-config | config/robot_dummy_config.yaml | Path to robot config YAML |
--hardware-class | None | Dotted class path for custom IRobotHardwareAdapter |
--translator-class | None | Dotted class path for custom IDeviceTranslator |
--duration | 0.0 | Run duration in seconds; 0 = run forever |
--log-level | INFO | Logging level: CRITICAL/ERROR/WARNING/INFO/DEBUG/NOTSET |
--log-file | None | Rotating log file path (100 MB max, 5 backups) |
--record | None | Path to save recorded observations (.pkl) |
--private-key-password | None | Password for encrypted server_key.pem |
--private-key-password-env | None | Environment variable name holding the password |
--prompt-password | True (default) | Prompt for password only when encrypted key detected |
--no-prompt-password | False | Never prompt; fail if password required |
The following hardware adapter types are registered via r2c_sdk.adapters entry points
and can be used by setting hardware.type in the robot config YAML:
| Adapter | Description |
|---|---|
dummy | Simulated robot for testing and development |
a1z | A1Z + G1Z gripper (6-DOF arm, GALAXEA-A1Z SDK via SocketCAN) |
lerobot | LeRobot-compatible robots (SO-101, etc.) |
ros2 | ROS 2 based robots |
raw_sdk | Vendor SDK via VendorSDKHardwareAdapter |
jaka | Jaka robotic arms |
ur5e_rtde | Universal Robots UR5e via RTDE |
zenoh_ros1 | ROS 1 robots via Zenoh bridge |
flexiv | Flexiv robotic arms |
playback | Replay recorded observations |
moz1 | MOZ1 robot |
q25 | Q25 robot (direct SDK) |
q25_ros2 | Q25 robot via ROS 2 |
tsd | TSD robot |
The a1z adapter is registered in pyproject.toml but requires the [a1z] extra
(python-can, pin) and the GALAXEA-A1Z SDK. Installing cloudrobo-r2c[all] includes it.
Custom adapters can also be loaded via --hardware-class (dotted import path) or
hardware.type: "custom" + hardware.class_path in the robot config.
See references/custom-adapter-guide.md for details.
| Parameter | Source | Required | Confirmation Needed |
|---|---|---|---|
--bundle | User (from robot skill export-certificate) | Yes (recommended) | Verify file exists and is readable |
--robot-config | User | Yes | Ensure YAML is valid before starting |
--client-config | User | No (fallback to bundle) | — |
--project-id / --device-id | User | Yes (if no bundle) | Verify against platform registration |
--endpoints | User or bundle | No (from bundle) | Verify Zenoh router is reachable |
--hardware-class | User | No | Verify class implements IRobotHardwareAdapter |
--duration | User | No | 0 = run forever; set a value for timed tests |
--record | User | No | Ensure output path is writable |
--private-key-password | User | No (only if key encrypted) | Masked; never echo back |
Starting the client is a long-running, externally visible operation. Confirm the credential bundle, robot config, and endpoint reachability before launching.
| Scenario | Handling |
|---|---|
| Missing credential bundle | Client fails with error; use cloudrobo robot export-certificate to produce one |
| Encrypted private key | Prompt for password (default), or use --private-key-password / --private-key-password-env; --no-prompt-password fails if password required |
| Invalid robot config | Client startup fails with ValidationError details listing all schema errors |
| Unknown hardware adapter type | Use custom type with --hardware-class for unlisted robots; see built-in adapters list above |
| Third-party adapter not found after install | Verify entry_point is registered in pyproject.toml under [project.entry-points."r2c_sdk.adapters"]; reinstall with pip install -e . |
| Zenoh connection failure | Client fails with R2CConnectionError; check endpoints, TLS certificates, and network |
| dry_run mode | Observations are published normally; received actions are logged but not executed on hardware |
| Duration 0 | Client runs indefinitely until Ctrl+C or process termination |
| Robot config missing required fields | Client startup fails with ValidationError for missing hardware.type, translator.type, or mapping sections |
| Heartbeat disabled | Set runtime.heartbeat.enabled: false in robot config; no heartbeat messages sent |
| Observation recording | --record path must be writable; observations saved as .pkl (pickle format) |
| Config directory resolution | _config_dir is injected into robot_config for relative path resolution (e.g., custom adapter modules) |
| Keyboard control | Enabled via runtime.keyboard_control.enabled: true; space=pause/resume, h=go_home (when paused), e=graceful exit |
| Action timeout | runtime.action_response_timeout_s controls wait time; backoff configured via _initial_s and _backoff |
| Action chunk alignment | runtime.enable_action_chunk_alignment prevents trajectory jumps; default false |
| Async request fusion | runtime.async_request configures observation fusion strategy (replace/weighted_average/nearest_neighbor) |
| Cross-skill dependency | This skill depends on the robot skill for credential bundle production; it does not call the robot skill directly |
| API paths | This skill does not call REST APIs; it uses Zenoh pub/sub with Protobuf serialization |
| Mutating operations | Starting r2c client is a long-running process; confirm parameters before launch |
obs:// paths | Not applicable to this skill; R2C uses Zenoh topics, not object storage |
bash scripts/test-cli-commands.sh
# CLI (optionally scope workspace tests: bash scripts/test-cli-commands.sh -w <workspace-id>)
bash scripts/test-cli-commands.sh
See templates/test-vars.json for the full test case list covering client startup, dry_run scenarios, and observation recording.
r2c client with --bundle and a valid robot config starts and logs heartbeat/connection messagesr2c client with dry_run: true publishes observations but does not execute actions--no-prompt-password)--duration N stops the client after N seconds--record produces a non-empty .pkl file--log-file produces a rotating log file--bundle (recommended) instead of explicit --project-id/--device-id for simpler setupdry_run: true to verify observation publishing without risking robot movementdummy adapter for development and testing when real hardware is unavailable--duration for timed tests instead of running indefinitely during development--log-level DEBUG and --log-file for detailed troubleshooting--private-key-password-env instead of --private-key-password to avoid secrets in shell historyIRobotHardwareAdapter methods and test with dry_run firstConfigurableDeviceTranslator (config-driven mapping) instead of writing a custom translator class when possible--record for offline analysis and playback testingrobot skill — do not attempt to create one manually