API Reference
1. Overview & Scope
Section titled “1. Overview & Scope”1.1 Hardware Support & Capabilities
Section titled “1.1 Hardware Support & Capabilities”SDK 2.x identifies products by their three-character product_code. The 21-DOF products are UB1, UD1, UF1–UF3, UT1, UT2, and UV1–UV6; the 16-DOF products are PB1 and PT1; the 13-DOF products are DB1 and DT1. JointLayout reports the logical joint count and layout. The SDK enables the feature domains supported by the hardware on the listed 21-DOF products. The 16-DOF and 13-DOF products currently expose only identity and JointLayout. Unverified capabilities report NotVerified, while capabilities without corresponding hardware report HardwareMissing. See Product Codes and Compatibility Model Classification.
The SDK provides hand motion, state, and maintenance for UV1–UV6. A dedicated SDK supplies independent vision-tactile fingertip data over USB or serial; that data does not traverse this SDK’s Modbus/CANFD transport and is not exposed by hand.touch. For UV1–UV4, read-only discovery identifies the primary-link piezoresistive tactile array or high-density tactile matrix finger-pad and palm modules, and hand.touch exposes only those five finger-pad modules and the palm module. The two channels have independent lifecycles and no atomic timestamp guarantee.
Read by role:
- Application integrator: start with Chapter 2 for connection flow, Chapter 3 for
product_code, and Chapter 8 for errors, timeouts, and retries.- Motion-control developer: focus on 4.1 Motion, 4.2 State, and Chapter 7 for waits and motion conflicts.
- Touch and data developer: focus on 4.3 Touch, the touch methods in 5.4/5.6, and the data structures in 6.3.
- Diagnostics and maintenance developer: focus on 4.4 Health, 4.6 Config, 4.7 Calibration, and 4.8 Maintenance.
- Python/C++/C integrator: start with Chapter 5 public method mappings, then read Chapter 9 for units and timestamps and Chapter 10 for language differences.
1.2 Object Model Architecture
Section titled “1.2 Object Model Architecture”Manager serves as the device manager handling device discovery and connection lifecycles; Hand represents the device handle for a single dexterous hand, providing device metadata and feature domain objects:
Manager (Device Discovery & Connection Management)└── Hand (Single Hand Device Handle) ├── Device Information & Metadata │ ├── DeviceInfo Physical identity for the hand, motors & touch modules │ ├── FirmwareInfo Controller, motor & touch firmware versions │ └── JointLayout Joint mapping & topology └── Feature Domain Objects ├── Motion Trajectory motion, real-time streaming, zero force & Software Stop ├── State Motor feedback snapshots & subscriptions ├── Touch Tactile array & force-torque sensor sampling ├── Health System diagnostics, motor health & runtime health state ├── ExperimentalCollision Experimental SDK-side collision detection ├── Config Device settings & runtime options ├── Calibration Joint zero calibration └── Maintenance Device reboot & firmware updatesThe diagram above illustrates feature ownership; exact signatures appear in Sections 2 through 5.
2. Core Entry Objects & Basic Usage
Section titled “2. Core Entry Objects & Basic Usage”Manager and Hand form the core entry objects of SDK 2.x, responsible for device discovery, session establishment, and handle lifecycle management.
2.1 Manager
Section titled “2.1 Manager”Applications create a Manager instance to perform discovery, connection management, and handle lifecycles:
- Discovery & Connection:
list_ports(): Enumerates visible communication ports or adapters for UI, CLI, or manual port selection. It does not probe devices or returnHandinstances.discover(scan_all=False): Scans for available bus devices (returning port name, transport protocol, andslave_id). Stops at the first device by default; setscan_all=Trueto scan all devices.connect_auto(): Discovers and connects to a matching device, ideal for quickstarts and single-hand defaults. Accepts optionalport,slave_id,protocol, ormodelfilters.connect(detected, model=None): Connects to a knownDetectedDevice, useful when the app callsdiscover()first to present a device picker to the user.connect_all(devices): Connects to multiple known devices in batch and returnslist[Hand]. This supports multiple hands on one bus and multiple Modbus ports.
- Bus Sharing & Lifecycle Rules:
- Port Isolation & Sharing: Opens only one Transport connection per physical port (such as RS485 or CANFD). Multiple
Handhandles (differentslave_id) on the same bus share this connection. - CANFD Session Limit: A process may have only one active CANFD Transport session. Multiple
slave_idvalues on that CANFD bus share the session. Close it before connecting another CANFD adapter or starting CANFD discovery; a session also cannot be created while CANFD discovery is running. This limit does not apply to Modbus. - Independent Close: Closing a single
Handreleases only that handle and its references. The SDK releases the underlying bus connection only after the lastHandon that port is closed. - Global Cleanup: Closing
Manageratomically closes all managedHandhandles and underlying physical connections. - Disconnection & Invalidations: After bus disconnects and reconnection recoveries, previous
Handhandles, subscriptions, and caches automatically invalidate, requiring the application to re-obtain a handle.
- Port Isolation & Sharing: Opens only one Transport connection per physical port (such as RS485 or CANFD). Multiple
2.2 Hand
Section titled “2.2 Hand”Hand is the active handle for a connected robotic hand, aggregating read-only metadata snapshots and functional domain sub-modules:
Hand / revo3::Hand├── device_info / device_info() --> Basic device info (model/SN/hand_side/hw_ver)├── firmware_info / firmware_info() --> Firmware versions (controller/drivers/touch)├── joint_layout / joint_layout() --> Joint topology & mapping (21 DoF logical order)├── slave_id / slave_id() --> Modbus slave ID├── motion / motion() --> Motion control API (move_to, move_joint, teach)├── state / state() --> Status & telemetry API (snapshot state)├── touch / touch() --> Tactile sensing API (layout, stream & maintenance)├── health / health() --> Health & safety diagnostics API├── experimental_collision / experimental_collision() --> Experimental collision API├── config / config() --> Device parameter configuration API├── calibration / calibration() --> Joint zeroing & calibration API├── maintenance / maintenance() --> Firmware OTA & DFU maintenance API└── close() --> Close handle & release connection- Resource Ownership: Supports calling
close()to release the handle; multiple devices on the same bus share a single connection, so closing oneHanddoes not affect other hands on the same port.
2.3 Basic Usage
Section titled “2.3 Basic Usage”Revo3 SDK provides consistent object-oriented basic usage patterns across Python and C++:
Python Basic Usage
Section titled “Python Basic Usage”import asynciofrom bc_revo3_sdk import main_mod as sdk
async def main(): manager = sdk.Manager() hand = None try: hand = await manager.connect_auto()
# 1. Read device metadata and layout info = hand.device_info layout = hand.joint_layout if layout is None: raise RuntimeError("Joint layout is unavailable") print(f"Hand Model: {info.model}, SN: {info.serial_number}")
# 2. Build a target from the latest state snapshot state = await hand.state.snapshot() target = list(state.positions_deg) target[0] = 45.0 # Target angle for J0 in degrees
# 3. Dispatch one motion command and wait for completion handle = await hand.motion.move_to(target, duration=0.8) result = await handle.wait(timeout=2.0) print(f"Motion result: {result}") finally: if hand is not None: await hand.close() await manager.close()
asyncio.run(main())C++ Basic Usage
Section titled “C++ Basic Usage”#include <iostream>#include <revo3/revo3.hpp>#include <stdexcept>#include <vector>
using namespace std::chrono_literals;
int main() { revo3::Manager manager; auto hand = manager.connect_auto();
// 1. Read device metadata and layout const auto info = hand.device_info(); std::cout << "Hand Model: " << static_cast<int>(info.model) << ", SN: " << info.serial_number << "\n";
// 2. Build a safe motion target from state snapshot auto state = hand.state().snapshot(); const auto layout = hand.joint_layout(); if (!layout) { throw std::runtime_error("Joint layout is unavailable"); } std::vector<float> target( state.motors.positions_deg, state.motors.positions_deg + layout->joint_count); target[0] = 45.0f; // Target angle for J0 in degrees
// 3. Dispatch motion command and wait for completion auto handle = hand.motion().move_to(target, 800ms); const auto result = handle.wait(2s); std::cout << "Motion result status: " << static_cast<int>(result) << "\n";
// 4. Release handle & resources hand.close(); return 0;}3. Device View & Metadata
Section titled “3. Device View & Metadata”Device info and metadata are provided across two lifecycle stages: Device Discovery and Device Connection:
- Device Discovery Stage: Returned by
Manager.discover()asDetectedDevice, capturing endpoint parameters and baseline descriptors for connection input; - Device Connection Stage: After establishing a connection via
connect()orconnect_auto(), accessed via theHandhandle.
3.1 Discovered Device (DetectedDevice)
Section titled “3.1 Discovered Device (DetectedDevice)”DetectedDevice / revo3::DetectedDevice├── protocol_type --> Transport protocol type (ModbusRTU / CANFD)├── port_name --> Serial port or CAN interface name (e.g. /dev/ttyUSB0, can0)├── slave_id --> Device Modbus slave ID├── nominal_baudrate_bps --> RS485 baudrate or CAN nominal baudrate (e.g. 115200, 1000000)├── data_baudrate_bps --> CANFD data-phase baudrate (e.g. 5000000)├── model --> Detected device model (e.g. UltraTouch)├── hand_side --> Detected hand side (Left / Right)├── serial_number --> Device unique serial number (e.g. BCUTL40124000001)├── firmware_version --> Controller firmware version└── hardware_revision --> Hardware revision identifier3.2 Device Info (DeviceInfo)
Section titled “3.2 Device Info (DeviceInfo)”DeviceInfo├── product_code --> Three-character product code (e.g. UT1)├── model --> SDK 2.x compatibility model classification (e.g. UltraTouch)├── serial_number --> Device unique serial number (e.g. BCUTL40124000001)├── hand_side --> Hand side (Left / Right)├── hardware_revision --> Hardware revision identifier├── motor_serial_numbers --> Motor serial number list└── touch_serial_numbers --> Touch module serial number listDeviceInfo is a snapshot of basic info and hardware metadata for the connected device, used for device identification, logging traceability, and compatibility diagnostics. The snapshot is automatically fetched and cached upon connection; calling await hand.refresh_device_info() is required only during factory testing, servicing, or forced sync.
If the device serial number or hardware version is missing, hand.device_info returns None (without faking info via empty strings). Unread or unsupported component serial numbers appear as empty lists [] without blocking DeviceInfo creation.
Field Specifications
Section titled “Field Specifications”product_code: Three-character product code (for example,UT1) derived from the four-character model prefix in the serial number by removing theL/Rhand-side marker;Nonefor legacy or unknown SNs that cannot be normalized reliably.model: SDK 2.x compatibility model classification (Revo3Model), used for connection overrides and joint-layout resolution rather than exact product identity.serial_number: Device unique serial number (e.g."BCUTL40124000001"), used for device identification, multi-hand logging, and asset management.hand_side: Hand side (Left/Right), used for mirror kinematics, 3D pose transformations, and control mappings.hardware_revision: Hardware revision identifier for production traceability. Applications should use concrete domain APIs and structured errors for runtime availability checks.motor_serial_numbers: Known physical motor serial numbers ordered by logical joints.touch_serial_numbers: Known touch-module serial numbers (empty for non-Touch SKUs or unreadable SNs).
Model Resolution & Explicit Overrides
Section titled “Model Resolution & Explicit Overrides”Current device firmware does not store an independent product model field. The SDK automatically resolves model from serial number prefixes:
- Normal Connection:
DetectedDevice.modelcarries the auto-detected model; simply callconnect(detected)orconnect_auto(). - Explicit Override: For older firmware with missing, incorrect, or incomplete serial numbers, pass an explicit
modelupon connection. Overrides take precedence over SN detection for the current connection context without altering device firmware.
# Example: Connect with explicit model override in Pythonhand = await manager.connect_auto(model=sdk.Revo3Model.UltraTouch)// Example: Connect with explicit model override in C++auto devices = manager.discover();auto detected = devices.front();detected.model = REVO3_MODEL_ULTRA_TOUCH;auto hand = manager.connect(detected);The low-level touch protocol resolution state is SDK-internal and omitted from DeviceInfo. Applications should query hand.touch.layout for touch layout and touch data shape.
# Example: Read basic device info and modelinfo = hand.device_infoif info is not None: print(f"SN: {info.serial_number}, Model: {info.model}, Hand side: {info.hand_side}") print(f"Motor SN count: {len(info.motor_serial_numbers)}, Touch SN count: {len(info.touch_serial_numbers)}")3.3 Firmware Info (FirmwareInfo)
Section titled “3.3 Firmware Info (FirmwareInfo)”FirmwareInfo├── controller_firmware_version├── motor_firmware_versions└── touch_firmware_versionsFirmware information is separate from basic device info and hardware metadata and must be re-read after an upgrade or reconnect. The controller firmware belongs to the main device, but it remains in FirmwareInfo because it is software rather than hardware metadata. Field definitions:
controller_firmware_version: Main controller firmware version.motor_firmware_versions: Currently known motor controller firmware versions in logical joint order.touch_firmware_versions: Currently known touch module firmware versions. The list is empty for non-Touch SKUs.
An empty list means that the current snapshot has no known versions. It may indicate that the device has no corresponding modules or that their versions have not been read. The current API does not expose a firmware-inventory completeness field. Call await hand.refresh_firmware_info() when component versions need to be refreshed.
# Example: Read firmware versionsfw = hand.firmware_infoprint(f"Controller FW: {fw.controller_firmware_version}, Motor FW count: {len(fw.motor_firmware_versions)}")3.4 Joint Layout Model (JointLayout)
Section titled “3.4 Joint Layout Model (JointLayout)”Python and C++ use hand.joint_layout to identify the active layout and validate array lengths. The property contains layout_id, version, and joint_count:
JointLayout├── layout_id --> Joint topology identifier (e.g. Revo3Ultra21 / Revo3Pro16 / Revo3Basic13)├── version --> Version of the layout specification (currently 1)└── joint_count --> Total number of logical joints (21 / 16 / 13)The 21-DOF Ultra layout uses the fixed logical order below. Tools that inspect Pro or Basic metadata must use JointLayout.joint_count for their 16/13-DOF layouts and must not treat all 21 storage slots as active joints. This metadata support does not enable Motion, State, Touch, Health, Config, Calibration, or Maintenance for those models. Ultra position and velocity limits come from DeviceConfig; the SDK maps controller channels at the protocol boundary instead of exposing them through the Python or C++ API.
The current 21-DOF logical grouping is:
| Group | Logical indices | Joint count |
|---|---|---|
| Pinky | 0..3 | 4 |
| Ring | 4..7 | 4 |
| Middle | 8..11 | 4 |
| Index | 12..15 | 4 |
| Thumb | 16..20 | 5 |
The public Thumb order is J16, J17, J18, J19, and J20: CMC Flex (base flexion), MCP, IP, CMC Abd, and CMC Rotation. The protocol adapter handles the underlying order. Pass targets by joint index.
3.5 Product Codes and Compatibility Model Classification
Section titled “3.5 Product Codes and Compatibility Model Classification”The three-character product_code identifies the product independently of hand side. It is derived from the four-character model prefix in the serial number by removing the L / R marker. The standard product number is BC-Revo-3. The table lists product identity and hardware configuration; product availability does not imply support for every SDK feature.
| Product Code | Official Product Line Name | DOF | Functional / Touch Configuration | SN Prefix (Left / Right) |
|---|---|---|---|---|
UB1 |
BrainCo Revo3(Basic) UBL1/UBR1 | 21 | Standard, no touch (V0W0) | UBL1 / UBR1 |
UD1 |
BrainCo Revo3(Basic) UDL1/UDR1 | 21 | Demo hand set, no touch | UDL1 / UDR1 |
UF1 |
BrainCo Revo3(3DForce) UFL1/UFR1 | 21 | Fingertip 3D force / force-torque modules, five tips (V0W3) | UFL1 / UFR1 |
UF2 |
BrainCo Revo3(3DForce) UFL2/UFR2 | 21 | Fingertip 3D force / force-torque modules plus piezoresistive pads and palm (V1W3) | UFL2 / UFR2 |
UF3 |
BrainCo Revo3(3DForce) UFL3/UFR3 | 21 | Fingertip 3D force / array modules plus piezoresistive pads and palm (V1W4) | UFL3 / UFR3 |
UT1 |
BrainCo Revo3(Touch) UTL1/UTR1 | 21 | Full-hand piezoresistive array (V1W1) | UTL1 / UTR1 |
UT2 |
BrainCo Revo3(Touch) UTL2/UTR2 | 21 | Full-hand high-density matrix (V2W2) | UTL2 / UTR2 |
UV1 |
BrainCo Revo3(Vision) UVL1/UVR1 | 21 | Piezoresistive pads and palm plus independent vision fingertips, profile 1 (V1W5) | UVL1 / UVR1 |
UV2 |
BrainCo Revo3(Vision) UVL2/UVR2 | 21 | Piezoresistive pads and palm plus independent vision fingertips, profile 2 (V1W6) | UVL2 / UVR2 |
UV3 |
BrainCo Revo3(Vision) UVL3/UVR3 | 21 | High-density matrix pads and palm plus independent vision fingertips, profile 1 (V2W5) | UVL3 / UVR3 |
UV4 |
BrainCo Revo3(Vision) UVL4/UVR4 | 21 | High-density matrix pads and palm plus independent vision fingertips, profile 2 (V2W6) | UVL4 / UVR4 |
UV5 |
BrainCo Revo3(Vision) UVL5/UVR5 | 21 | No primary-link array; independent vision fingertips, profile 2 (V0W6) | UVL5 / UVR5 |
UV6 |
BrainCo Revo3(Vision) UVL6/UVR6 | 21 | No primary-link array; independent vision fingertips, profile 1 (V0W5) | UVL6 / UVR6 |
PB1 |
BrainCo Revo3(Basic) PBL1/PBR1 | 16 | Standard, no touch | PBL1 / PBR1 |
PT1 |
BrainCo Revo3(Touch) PTL1/PTR1 | 16 | Piezoresistive array | PTL1 / PTR1 |
DB1 |
BrainCo Revo3(Basic) DBL1/DBR1 | 13 | Standard, no touch | DBL1 / DBR1 |
DT1 |
BrainCo Revo3(Touch) DTL1/DTR1 | 13 | Piezoresistive array | DTL1 / DTR1 |
Current SDK support: UB1 and UD1 expose 21-DOF hand motion, state, configuration, and maintenance, but have no touch hardware. UF1–UF3, UT1, and UT2 also expose hand.touch for recognized, supported touch layouts. UV1–UV4 expose hand functionality and primary-link finger-pad/palm touch; independent vision-tactile fingertips are not part of this SDK’s hand.touch. UV5 and UV6 have no primary-link touch array; their independent fingertips use a dedicated SDK. PB1, PT1, DB1, and DT1 currently expose only device identity and JointLayout. Other capabilities report NotVerified, or HardwareMissing when the hardware is absent. Touch availability depends on the layout resolved at connection time; a product code alone does not guarantee that every module can be read.
Revo3Model is a public enum for legacy-device connection overrides, joint layouts, and ABI compatibility. It is not a product name and does not replace product_code. Compatibility mapping: UB1, UD1 → Ultra; UF1–UF3, UT1, UT2 → UltraTouch; UV1–UV6 → UltraVisionTouch; PB1 → Pro; PT1 → ProTouch; DB1 → Basic; DT1 → BasicTouch.
The SDK treats a recognized three-character product code from the SN as authoritative for hardware topology and touch configuration. It does not replace that identity with potentially stale registers 135 or 136. Legacy SNs and unknown variants that do not resolve to a recognized product code continue to use register and read-only metadata detection.
3.6 Connection, Logging, And Firmware Target Enums
Section titled “3.6 Connection, Logging, And Firmware Target Enums”Public Python integer enums expose a read-only int_value property containing the integer representation shared with the C ABI and protocol. Application logic should still compare enum members instead of hard-coding integers.
ProtocolType (Enum)
Section titled “ProtocolType (Enum)”ProtocolType is used by discovery and connection options. Auto means that the SDK selects a supported transport; it is not a separate device protocol.
| Option | Value | Description |
|---|---|---|
Auto |
0 |
Auto-detect Modbus RTU or CANFD |
Modbus |
1 |
Use Modbus RTU over RS485 |
CanFd |
3 |
Use CANFD |
Rs485Baudrate (Enum)
Section titled “Rs485Baudrate (Enum)”Python connection APIs use this strongly typed enum. C++ currently represents DiscoveryOptions.modbus_baudrate as an integer in bps.
| Option | Value | Line Rate |
|---|---|---|
Baud1Mbps |
1 |
1,000,000 bps |
Baud2Mbps |
2 |
2,000,000 bps |
Baud3Mbps |
3 |
3,000,000 bps |
Baud5Mbps |
5 |
5,000,000 bps |
CanFdBaudrate (Enum)
Section titled “CanFdBaudrate (Enum)”Python connection APIs use this strongly typed enum. C++ currently represents DiscoveryOptions.canfd_data_baudrate as an integer in bps. This enum configures the CANFD data-phase rate. The adapter and transport implementation determine the arbitration-phase rate; this enum does not configure it.
| Option | Value | Data-Phase Rate |
|---|---|---|
Baud1Mbps |
1 |
1,000,000 bps |
Baud2Mbps |
2 |
2,000,000 bps |
Baud4Mbps |
4 |
4,000,000 bps |
Baud5Mbps |
5 |
5,000,000 bps |
LogLevel (Enum)
Section titled “LogLevel (Enum)”Python uses this enum with init_logging(), the C ABI uses it with revo3_init_logging(), and C++ uses it with revo3::init_logging().
| Option | Value | Description |
|---|---|---|
Error |
0 |
Error messages only |
Warn |
1 |
Warning and error messages |
Info |
2 |
Normal operational information; the default level |
Debug |
3 |
Debug information |
Trace |
4 |
Most detailed tracing information |
FirmwareTarget (Enum)
Section titled “FirmwareTarget (Enum)”Python names this type FirmwareTarget; C++ names it FirmwareTarget.
| Python Option | C++ Option | Value | Description |
|---|---|---|---|
MainFirmware |
MainFirmware |
0 |
Main controller firmware |
Image |
Image |
1 |
Device image target; available only when explicitly supported by the corresponding firmware |
MotorFirmware |
MotorFirmware |
2 |
Motor-module firmware |
An enum member identifies an update target; it does not establish that the current device, firmware, or transport supports that target. Non-main-controller targets also require firmware support for writing and reading back the target register. The update fails when that confirmation fails and must not silently switch to another target.
4. Hand Domain APIs
Section titled “4. Hand Domain APIs”4.1 Motion Control API
Section titled “4.1 Motion Control API”Target motion, trajectory generation, and real-time streaming control all start from hand.motion. API calling patterns are categorized as follows:
| Interface Class | Primary Use | Caller Responsibility |
|---|---|---|
| Standard Device API | Discovery, normal motion, state, touch, config, and maintenance | Check returned states and errors |
| Motion Streaming-Control Mode | Data gloves, teleoperation, or custom controllers that continuously send new targets | Maintain an interval compatible with the configured send timeout and handle disconnect and exit behavior; do not describe non-deterministic transports and general-purpose operating systems as hard real time |
| Low-level Protocol API | Registers, raw frames, no-retry writes, and protocol diagnostics | Own protocol, safety, and compatibility risk |
Hand└── Motion ├── move_to() -> OperationHandle --> Whole-hand timed target motion ├── move_joint() -> OperationHandle --> Single-joint timed target motion ├── move_finger() -> OperationHandle --> Full finger posture motion ├── flex_finger() -> OperationHandle --> Simple finger flexion motion ├── move_thumb() -> OperationHandle --> Independent thumb posture motion ├── open_servo() -> ServoSession --> Open real-time streaming session ├── start_servo_drag() --> Start SDK-managed drag worker ├── update_servo_drag() --> Update drag target position ├── stop_servo_drag() --> Stop drag & send hold frame ├── cancel_servo_drag() --> Cancel drag & stop transmission ├── teach_joint() --> Record single-joint trajectory ├── teach_hand() --> Record whole-hand trajectory ├── replay_joint() --> Replay single-joint trajectory ├── replay_hand() --> Replay whole-hand trajectory ├── set_zero_force_enabled(enabled) --> Switch zero-torque / teach mode ├── software_stop() -> None --> Software-level motion pause └── recover_software_stop() -> None --> Recover from software pause- Provides timed target motion for the whole hand, one joint, one finger, and the Thumb.
- Typed position, velocity, current, and impedance commands.
- Long-running motion returns a
OperationHandlefor status, waiting, and cancellation requests. - A cancellation request does not mean mechanical motion has stopped when the call returns.
teach_joint(),teach_hand(),replay_joint(), andreplay_hand()live underhand.motionbecause they capture or replay motion trajectories and share control ownership with other Motion modes.hand.motion.set_zero_force_enabled(enabled)maps the existing Teaching Mode command to enter or leave whole-hand zero-torque/backdrive mode. It is a Motion operating mode, not a Stop or functional-safety capability, and it conflicts with other active Motion operations when sent.hand.motion.software_stop()andhand.motion.recover_software_stop()call the existing firmware stop and recovery commands. The SDK does not define how motors stop; firmware documentation defines their behavior and states.
4.1.1 Trajectory Motion API: move_to, move_joint, move_finger & move_thumb
Section titled “4.1.1 Trajectory Motion API: move_to, move_joint, move_finger & move_thumb”Note: Underlying Trajectory & Transmission Mechanism: Timed motion APIs (
move_to,move_joint,move_finger,move_thumb) use Quintic Polynomial Trajectory Interpolation internally to guarantee continuous velocity and acceleration transitions. During motion execution, the SDK streams interpolated position and velocity targets as Five-parameter MIT Hybrid Control Commands (Kp, Kd, Pos, Vel, Feedforward Current) at high frequency to motor drivers. The feedback and feedforward fields remain namedcurrent/current_ma: the device reports electrical current in mA, not calibrated joint torque in Nm.
Use move_to() for timed whole-hand motion:
handle = await hand.motion.move_to( target_positions, duration=0.8, dt=0.01,)await handle.wait(timeout=2.0)| Parameter | Meaning |
|---|---|
target_positions |
Target position of every joint in degrees; ordering follows hand.joint_layout, and the array length must equal joint_count |
duration |
Total time to move from the current state to the target, in seconds; must be greater than zero and is mutually exclusive with speed |
speed |
Target motion speed in rpm; must be greater than zero, and the SDK derives duration from current and target positions |
kp, kd |
Optional uniform gains; SDK defaults are used when omitted |
dt |
Interval between trajectory points generated and sent by the SDK, in seconds; defaults to 0.01 and must be greater than zero |
The SDK generates a quintic trajectory and sends whole-hand targets. In Python, await move_to() validates the arguments, creates the motion task, and returns a OperationHandle; it does not wait for trajectory transmission or the complete motion. Errors after task creation are reported through the handle. The C++ equivalent is:
auto handle = hand.motion().move_to(target_positions, 800ms, 10ms);handle.wait(2000ms);C++ uses std::chrono::milliseconds for duration and period. Motion can continue after move_to() returns; wait() is the call that blocks the current thread for the result.
OperationHandle provides:
| Member | Purpose |
|---|---|
state |
Read Pending, Running, Succeeded, Cancelled, Preempted, Failed, or Indeterminate |
wait(timeout) |
Wait for completion; a timeout ends only this wait and does not stop motion automatically |
cancel() |
Stop further trajectory sends by the SDK; the current firmware cannot confirm that mechanical motion has stopped, so the final result may be Indeterminate |
error |
Read the error when motion fails; empty after success |
Calling move_to() during an active target motion replaces the old target. The SDK regenerates the trajectory from current position and velocity feedback, and the old handle becomes Preempted. This is intended for occasional target changes, not continuous updates from a data glove, teleoperation system, or controller; use open_servo() for those cases.
These convenience methods use the same trajectory, control ownership, and OperationHandle behavior as move_to():
joint_motion = await hand.motion.move_joint( joint_index=0, target_position=20.0, duration=0.8,)
finger_motion = await hand.motion.move_finger( finger_index=1, target_positions=[0.0, 30.0, 30.0, 0.0], duration=0.8,)
flex_motion = await hand.motion.flex_finger( finger_index=1, flexion_position=30.0, duration=0.8,)
thumb_motion = await hand.motion.move_thumb( target_positions=[0.0, 20.0, 20.0, 0.0, 20.0], duration=0.8,)| Method | Parameters |
|---|---|
move_joint() |
joint_index is a zero-based logical joint index; target_position is in degrees |
move_finger() |
finger_index is 1=Index, 2=Middle, 3=Ring, or 4=Pinky; the target array contains exactly 4 degree values on a 21-DOF hand, ordered as Abd, MCP, PIP, and DIP |
flex_finger() |
Uses the same finger_index as move_finger(); flexion_position is applied to MCP, PIP, and DIP while Abd stays at the current feedback position |
move_thumb() |
The target array contains exactly 5 degree values on a 21-DOF hand, ordered as J16, J17, J18, J19, and J20 |
move_joint() uses the same duration/speed, uniform kp/kd, and dt arguments as move_to(). move_finger(), flex_finger(), and move_thumb() are duration-based and accept optional uniform or per-joint kp/kd. move_finger() is the full finger-posture API and controls Abd; flex_finger() is the semantic bending API for quick starts, grasp actions, or GUI controls. All four return a OperationHandle. They can replace one another or move_to() for low-rate target changes; the old handle becomes Preempted. They conflict with open_servo().
4.1.2 Real-time Servo Control (open_servo)
Section titled “4.1.2 Real-time Servo Control (open_servo)”Calling hand.motion.open_servo() creates a ServoSession. Callers supply targets via send_position(), send_velocity(), send_current(), send_impedance(), or send_mit().
4.1.3 Managed Drag Control (start_servo_drag)
Section titled “4.1.3 Managed Drag Control (start_servo_drag)”For event-driven input sources (e.g. GUI sliders), call Motion.start_servo_drag(joint_index, initial_position) to initialize, and update_servo_drag(joint_index, target_position) on target changes.
4.1.4 Real-time Control Entry Points: open_servo vs. start_servo_drag
Section titled “4.1.4 Real-time Control Entry Points: open_servo vs. start_servo_drag”For real-time streaming and continuous motion, the SDK provides two distinct entry points at different abstraction levels:
open_servo()(Caller-managed loop): Opens aServoSession, delegating high-frequency streaming control to a caller-owned loop. Ideal for VR gloves, teleoperation, or RL policies issuing targets every 5–20ms.start_servo_drag(...)(SDK-managed worker): Starts an SDK-managed background worker for a single joint. Ideal for GUI sliders and joystick controls, where the caller invokesupdate_servo_drag()on event changes, while the SDK automatically maintains continuous transmission with filtering, velocity limits, and collision protection.
Comparison Table:
| Feature / Dimension | open_servo() (Streaming Session) |
start_servo_drag() (Managed Drag Stream) |
|---|---|---|
| Core Concept | “Delegate real-time control to caller loop” | “SDK manages background loop for single joint” |
| Primary Use Cases | Teleoperation, VR gloves, RL policies updating multiple joints every 5–20ms | GUI Slider dragging, interactive UI, joystick single-joint control |
| Loop Ownership | Caller Loop (caller maintains loop & send frequency) | SDK Background Worker (SDK transmits periodically) |
| Control Scope | Joint, Finger, Thumb, Full Hand | Single Joint |
| Method List | send_position(), send_velocity(), send_mit(), etc. |
start_servo_drag(), update_servo_drag(), stop_servo_drag() |
| Lifecycle & Timeout | Explicit session.close(), auto-expire via command_timeout_ms |
stop_servo_drag() for normal release; cancel_servo_drag() for emergency stop |
| Safety Features | Relies on algorithm layer for smoothing | Built-in filtering, velocity limits, collision checks & idle hold |
4.1.5 Teach And Replay API
Section titled “4.1.5 Teach And Replay API”teach_joint() and teach_hand() sample joint feedback positions for a specified duration and return trajectory arrays that can be replayed later. replay_joint() and replay_hand() replay those trajectories using the provided dt, kp, and kd. These are Motion-domain APIs: while running, they own motion control and conflict with move_to(), local trajectory motion, open_servo(), and managed drag control.
joint_positions = await hand.motion.teach_joint( joint_index=0, duration=3.0, dt=0.01,)await hand.motion.replay_joint( joint_index=0, positions=joint_positions, dt=0.01, kp=1.0, kd=0.1,)
hand_trajectory = await hand.motion.teach_hand(duration=3.0, dt=0.01)await hand.motion.replay_hand(hand_trajectory, dt=0.01, kp=1.0, kd=0.1)4.2 State API
Section titled “4.2 State API”HandState contains status, position, velocity, current, and error for each motor. System state and the global error code are available from HealthSnapshot. A failed read returns SdkError.
State also contains one receive timestamp. Linux SocketCAN uses the kernel software timestamp from SO_TIMESTAMPNS; other CANFD and Modbus paths record when the SDK finishes reading. Only timestamps with the same clock value may be compared. This is not firmware sample time and cannot be used for cross-device synchronization.
# Example: Read feedback snapshot or subscribe at 50Hzsnapshot = await hand.state.snapshot()print(f"Current positions (degree): {snapshot.positions_deg}")
# Async subscriptionsub = hand.state.subscribe(period=0.02)try: frame = await sub.next()finally: sub.close()4.3 Touch API
Section titled “4.3 Touch API”This section contains both SDK usage guidance and compatibility implementation notes. For SDK integration, start with the “Application usage” subsection. Its fields, units, capability matrix, errors, and call constraints describe the public behavior applications can rely on. The “Implementation reference” subsection is intended for firmware integration and troubleshooting; applications should not depend directly on register addresses or the SDK’s internal probe order.
4.3.1 Application usage: layouts, data, and capabilities
Section titled “4.3.1 Application usage: layouts, data, and capabilities”Recommended integration flow:
- Connect a
Handand readhand.touch.layout. - Use the device
product_codefor product-level capabilities, then use each module’ssignalsandpoint_countfor the data returned by this connection. - Use
snapshot()for a single frame orsubscribe()for continuous reads. - Find modules by their public
module_id; do not substitute array positions for module IDs. - Call
set_layout()only when the layout cannot be identified and the physical configuration has been confirmed.
Typical main-link touch capabilities are:
| Product | Main-link array | Fingertip force/torque | Point data | Independent vision touch |
|---|---|---|---|---|
UF1 |
— | Yes | 48 points or none | — |
UF2, UF3 |
Yes | Yes | Layout dependent | — |
UT1, UT2 |
Yes | — | Yes | — |
UV1–UV4 |
Yes | — | Layout dependent | Yes |
UV5, UV6 |
— | — | — | Yes |
The SDK uses TouchLayout to describe the touch capabilities of the connected device and TouchFrame to return samples. Applications should use the modules, signals, and point counts in the layout without depending on internal register mappings.
Touch capabilities:
- Point data: touch modules return per-point values; use
point_countfor the actual number of points. - Regional-force data: some devices provide regional force values in a compatibility read mode through
regional_forces_mn; new applications should not assume every device supports it. - Fingertip 3D force/torque data: some modules provide
force3d,torque2d, andresultant_force_mn; some layouts also provide 48 points. - Hybrid touch data: one device may return point data, regional force, and fingertip force/torque together; use
signalsandsample_stateto determine what is available. - Independent vision-tactile data: read through a dedicated SDK and channel; it is not part of
hand.touch.
TouchLayout contains regions and modules. Each module provides module_id, region, region_index, signals, and point_count. layout_id is an optional schema key for heatmaps, simulation, and compatibility tools; applications should not use it as the sole product capability check. Applications should find modules by module_id; in hybrid layouts, the array position does not necessarily equal the module ID.
TouchFrame contains a receive timestamp, sequence number, and TouchModuleData modules. A module may provide points, force3d, torque2d, resultant_force_mn, and sample_state; the available fields depend on the module signals and active read mode. Public force values use mN and torque values use Nm.
module_id is the stable public identifier within the active product layout; it is not an array index. Pure array layouts usually use dense IDs. Hybrid layouts use sparse IDs: 0 for the palm, 1/3/5/7/9 for thumb-to-pinky fingertips, and 2/4/6/8/10 for thumb-to-pinky finger pads. Applications should find a module by module_id, then use its region, signals, and data fields.
When deciding whether data is usable, check sample_state == Valid first and then check individual fields for None. Disabled means the module is disabled, Unavailable means the capability or data is unavailable, WarmingUp means a fingertip module is not ready, and SensorFault means the sensor reported an abnormal state. NotSampled and ReadFailed are reserved states and normally do not appear in a successful complete snapshot.
Touch operations route through the active layout. Unsupported operations return UnsupportedCapability without sending a device command. Common handling is:
| Situation | Application handling |
|---|---|
| The product does not provide the touch capability | Skip the related UI or feature based on product_code. |
| The active layout is unresolved | Re-read device information and hand.touch.layout; do not guess. |
| The selected module does not support an operation | Handle UnsupportedCapability; do not substitute another touch command. |
| A single read fails | Use retryable and operation_effect from Chapter 8 before retrying; do not fill failed data with zeros. |
4.3.2 Firmware protocol reference
Section titled “4.3.2 Firmware protocol reference”This API reference does not enumerate register addresses, Modbus function codes, or the SDK’s internal probe order. Applications should rely on the layouts, data structures, capability checks, and error behavior defined above. Use the applicable Revo3 firmware protocol documentation and internal compatibility troubleshooting records for firmware integration, register diagnosis, or compatibility analysis.
The public SDK behavior is: unresolved layouts fail closed; unsupported operations return UnsupportedCapability; explicit set_layout() changes only the current connection session and does not write the device; the layout must be confirmed again after reconnecting.
4.4 Health & Safety State API
Section titled “4.4 Health & Safety State API”HealthSnapshot is read-only. It contains system state, the global error code, current, voltage, power, system temperature, per-motor raw status codes, faulted motor count, and safety_state. Per-motor status codes come from input registers 2120..2140, contain both fault and non-fault status bits, and are collected separately from the high-rate HandState. Bit 11 means Running, so 0x0800 does not count as a motor fault; 0x0900 means Running and Stalled. Bit 5 means CalibrationFailed and bit 9 means Calibrating on motor firmware 0.4 and later; Calibrating does not count as a fault. Motor-module temperatures and the online bitmask are health diagnostic queries exposed as hand.health.motor_module_temperatures_c() and hand.health.motor_online_mask(). These values are not duplicated in HealthSnapshot. A complete protection-state model and its SafetyState mapping still require confirmed firmware semantics and on-device fault-path tests.
HealthSnapshot and SafetyState are software-level diagnostics collected and aggregated over ordinary Modbus RTU or CAN FD links. They are not functional-safety states and must not be used as evidence for an ISO 13849 PL or IEC 61508 SIL claim, a safety PLC decision, an Emergency Stop circuit, or STO. Confirmed errors produce SafetyState::Faulted; insufficient information produces SafetyState::Unknown. Software Stop and Servo timeout provide software-level control degradation only. Independent safety measures must be selected through the system risk assessment.
# Example: Read health diagnostic snapshothealth = await hand.health.snapshot()print(f"Safety State: {health.safety_state}, Faulted Motors: {health.faulted_motor_count}")print(f"Motor Fault Codes: {health.motor_fault_codes}")
temperatures = await hand.health.motor_module_temperatures_c()online_mask = await hand.health.motor_online_mask()motor_0_online = bool(online_mask & (1 << 0))C++ can read motor-module temperatures and the online bitmask together through hand.health().motor_module_diagnostics().
4.5 ExperimentalCollision API
Section titled “4.5 ExperimentalCollision API”Collision detection is an explicit experimental domain and is not part of
Health. Python uses hand.experimental_collision, C++ uses
hand.experimental_collision(), and C uses
revo3_experimental_collision_*.
The feature is disabled by default. It currently relies mainly on SDK-side position error, motor current, and the age of cached state, then applies a software stop, zero-force mode, or actual-position hold strategy. It is not a functional-safety mechanism, does not guarantee fixed detection latency, and can produce false positives or missed collisions. Transport timing, status update timing, thresholds, and firmware feedback timing all affect its behavior. It must not replace an Emergency Stop, hardware limits, or a safety-rated controller interlock. Experimental configuration fields and semantics may be adjusted in later 2.x minor releases as hardware validation progresses.
config = sdk.ExperimentalCollisionConfig( enable=True, source=sdk.CollisionDetectionSource.HardwareOnly, strategy=sdk.CollisionProtectionStrategy.SoftStop,)await hand.experimental_collision.configure(config)active_joints = await hand.experimental_collision.active_joints()await hand.experimental_collision.reset()revo3::ExperimentalCollisionConfig config;config.enabled = true;hand.experimental_collision().configure(config);const auto active_joints = hand.experimental_collision().active_joints();hand.experimental_collision().reset();CollisionDetectionSource (Enum)
Section titled “CollisionDetectionSource (Enum)”| Option | Value | Description |
|---|---|---|
HardwareOnly |
0 |
Use only collision state reported by the device |
SoftwareOnly |
1 |
Use only SDK-side position-error and motor-current thresholds |
Hybrid |
2 |
Use both device state and SDK-side thresholds |
CollisionProtectionStrategy (Enum)
Section titled “CollisionProtectionStrategy (Enum)”| Option | Value | Description |
|---|---|---|
SoftStop |
0 |
Trigger an SDK software stop |
ZeroForce |
1 |
Send the zero-force control command |
HoldActualPosition |
2 |
Hold the actual feedback position observed at detection time |
Python and C++ callers must pass a defined enum member. An unknown integer is an InvalidArgument; the SDK must not fall back to a default detection source or protection strategy. This input contract does not change the experimental and non-functional-safety boundaries stated above.
4.6 Config API
Section titled “4.6 Config API”Config manages device configuration snapshots and SDK runtime options. Device configuration is persisted by firmware; runtime options affect only the current SDK process.
-
hand.config.snapshot()returnsDeviceConfig, includingslave_id, RS485 baud rate, device switches, protection current, position and speed limits, andpersistence_scope.hand.configalso provides explicitly named per-setting setters, but no bulk update that overwrites unrelated fields. Firmware is the sole source of truth for persistence. -
hand.config.runtime_optionsreturnsRuntimeOptions, andhand.config.set_runtime_options(...)updates process-local defaults for pull interval and streaming-control send timeout. They are not written to the device. A pull interval is not a device sample period or a fixed-rate guarantee. Thumb J16 is CMC Flex (base flexion, 0 to 75 degrees), J17 is middle joint flexion (-10 to 90 degrees), J18 is terminal joint flexion (-20 to 90 degrees), and J20 is rotation (0 to 105 degrees). SDK labels MCP/IP correspond to J17/J18. Runtime limits come from device readback throughhand.config.snapshot(). -
Communication settings use the
Rs485Baudrate/CanFdBaudrateenums. The corresponding C ABI symbols arerevo3_device_set_rs485_baudrate()/revo3_device_set_canfd_baudrate().
config = await hand.config.snapshot()print(f"Slave ID: {config.slave_id}, Baudrate: {config.rs485_baudrate}")runtime = hand.config.runtime_options4.7 Calibration API
Section titled “4.7 Calibration API”Calibration provides joint calibration, calibration-current, and zero-position operations. Calibration validates that Motion is idle and does not invent firmware progress or cancellation state.
await hand.calibration.calibrate_joints() # Joint calibrationawait hand.calibration.set_current(120.0)await hand.calibration.set_current_position_as_zero()4.8 Maintenance API
Section titled “4.8 Maintenance API”Maintenance provides factory reset, reboot, and firmware update. update_firmware(file_path, target=None, wait_secs=10) is the only object-level update entry point; the current implementation uses the device DFU/OTA flow internally. Reboot and firmware update return queryable OperationHandle objects.
reboot_handle = hand.maintenance.reboot() # Reboot deviceota_handle = hand.maintenance.update_firmware("revo3_controller.bin")5. Public API Reference
Section titled “5. Public API Reference”This section lists the Python and C++ object-layer public APIs. Python methods that perform I/O usually return awaitables; await ... in the table shows the recommended call style. C++ APIs live in the revo3 namespace.
5.1 Manager / Manager
Section titled “5.1 Manager / Manager”| Python | C++ | Returns | Behavior |
|---|---|---|---|
sdk.Manager() |
revo3::Manager manager; |
manager | Create a device manager |
manager.list_ports() |
- | list[SerialPortInfo] |
List local ports/adapters without probing devices |
await manager.discover(...) |
manager.discover(options) |
list[DetectedDevice] |
Discover devices (supports on_found streaming callback and cancellation) |
await manager.connect_auto(...) |
manager.connect_auto(options) |
Hand |
Discover and connect the first matching hand |
await manager.connect(detected, model=None) |
manager.connect(detected) |
Hand |
Connect a selected discovered device |
await manager.connect_all(devices) |
manager.connect_all(devices) |
list[Hand] / std::vector<Hand> |
Connect multiple devices |
await manager.close() |
manager.close() |
None |
Close managed hands and transports |
Python returns a standard list[Hand] with iteration, slicing, and zero-based indexed access. Applications can select a device by filtering on hand.device_info.serial_number. C++ returns std::vector<Hand> for multi-device connections.
Python SerialPortInfo exposes these read-only fields:
| Field | Type | Meaning |
|---|---|---|
port_name |
str |
Operating-system port name |
manufacturer |
str | None |
USB manufacturer string, or None when unavailable |
product_name |
str | None |
USB product string, or None when unavailable |
serial_number |
str | None |
Adapter serial number, or None when unavailable |
vid / pid |
int | None |
USB VID/PID, or None for non-USB or unavailable metadata |
discover(…) Streaming Callback
Section titled “discover(…) Streaming Callback”- Python Signature:
await manager.discover(scan_all=False, port=None, protocol=None, slave_id=None, modbus_baudrate=None, canfd_data_baudrate=None, broadcast=True, on_found=None)on_found: Optional callbackCallable[[DetectedDevice], bool | None]. Invoked for each device discovered. ReturningFalsestops the scan early.
- C++ Signature:
std::vector<DetectedDevice> manager.discover(const DiscoveryOptions &options)DiscoveryOptions.on_found: Optional callbackstd::function<bool(const DetectedDevice &device)>. Returningfalsestops probing.
5.2 Hand And Metadata
Section titled “5.2 Hand And Metadata”| Python | C++ | Returns | Behavior |
|---|---|---|---|
hand.device_info |
hand.device_info() |
DeviceInfo / None |
Basic device information |
hand.firmware_info |
hand.firmware_info() |
FirmwareInfo |
Controller, motor, and touch firmware versions |
hand.joint_layout |
hand.joint_layout() |
JointLayout / None |
Joint count and topology |
await hand.refresh_device_info() |
hand.refresh_device_info() |
DeviceInfo |
Refresh basic device information |
await hand.refresh_firmware_info() |
hand.refresh_firmware_info() |
FirmwareInfo |
Refresh firmware versions |
hand.motion |
hand.motion() |
Motion |
Motion domain |
hand.state |
hand.state() |
State |
Motor feedback domain |
hand.touch |
hand.touch() |
Touch |
Touch domain |
hand.health |
hand.health() |
Health |
Health diagnostics domain |
hand.experimental_collision |
hand.experimental_collision() |
ExperimentalCollision |
Experimental software collision detection and response |
hand.config |
hand.config() |
Config |
Configuration domain |
hand.calibration |
hand.calibration() |
Calibration |
Calibration domain |
hand.maintenance |
hand.maintenance() |
Maintenance |
Maintenance domain |
hand.statistics |
hand.statistics() |
RuntimeStatistics |
Runtime read/write, failure, and timeout statistics |
await hand.close() |
hand.close() |
None |
Close this hand handle |
5.3 Motion And ServoSession
Section titled “5.3 Motion And ServoSession”| Python | C++ | Returns | Behavior |
|---|---|---|---|
await hand.motion.move_to(...) |
hand.motion().move_to(...) |
OperationHandle |
Whole-hand joint angle control (all 21 joints) |
await hand.motion.move_joint(...) |
hand.motion().move_joint(...) |
OperationHandle |
Single-joint target angle control |
await hand.motion.move_thumb(...) |
hand.motion().move_thumb(...) |
OperationHandle |
Thumb motion control (all 5 joints, including opposition & abduction) |
await hand.motion.move_finger(...) |
hand.motion().move_finger(...) |
OperationHandle |
Single-finger all-joint control (all 4 joints, including abduction & flexion) |
await hand.motion.flex_finger(...) |
hand.motion().flex_finger(...) |
OperationHandle |
Semantic finger flexion (flexion only, holding abduction) |
hand.motion.open_servo(...) |
hand.motion().open_servo(...) |
ServoSession |
Create a streaming session synchronously; send_*() performs asynchronous I/O |
await session.send_position(...) |
session.send_position(...) |
None |
Send position stream frame |
await session.send_velocity(...) |
session.send_velocity(...) |
None |
Send velocity stream frame |
await session.send_current(...) |
session.send_current(...) |
None |
Send current stream frame |
await session.send_impedance(...) |
session.send_impedance(...) |
None |
Send impedance stream frame |
await session.send_mit(...) |
session.send_mit(...) |
None |
Send MIT stream frame |
session.state |
session.state() |
ServoSessionState |
Read session state |
session.close() |
session.close() |
None |
Close streaming session |
await hand.motion.start_servo_drag(...) |
hand.motion().start_servo_drag(...) |
None |
Start managed drag |
hand.motion.update_servo_drag(...) |
hand.motion().update_servo_drag(...) |
None |
Update managed drag target |
await hand.motion.stop_servo_drag(...) |
hand.motion().stop_servo_drag(...) |
None |
Stop drag normally |
await hand.motion.cancel_servo_drag(...) |
hand.motion().cancel_servo_drag(...) |
None |
Cancel drag transmission |
await hand.motion.teach_joint(...) |
hand.motion().teach_joint(...) |
list[float] |
Record one joint trajectory |
await hand.motion.teach_hand(...) |
hand.motion().teach_hand(...) |
list[list[float]] |
Record whole-hand trajectory |
await hand.motion.replay_joint(...) |
hand.motion().replay_joint(...) |
None |
Replay one joint trajectory |
await hand.motion.replay_hand(...) |
hand.motion().replay_hand(...) |
None |
Replay whole-hand trajectory |
await hand.motion.set_zero_force_enabled(enabled) |
hand.motion().set_zero_force_enabled(enabled) |
None |
Zero-force / teach-mode switch |
await hand.motion.software_stop() |
hand.motion().software_stop() |
None |
Send software stop and wait for this device I/O to finish |
await hand.motion.recover_software_stop() |
hand.motion().recover_software_stop() |
None |
Send software stop recovery and wait for this device I/O to finish |
5.4 State, Touch, And Health
Section titled “5.4 State, Touch, And Health”| Python | C++ | Returns | Behavior |
|---|---|---|---|
await hand.state.snapshot() |
hand.state().snapshot() |
HandState |
Read motor feedback snapshot |
hand.state.subscribe(period) |
hand.state().subscribe(period) |
StateSubscription |
Create state polling subscription |
await sub.next() |
sub.next() |
HandState |
Read next state frame |
hand.touch.layout |
hand.touch().layout() |
TouchLayout | None / TouchLayout |
Read touch region groups and module layout; C++ throws when unavailable |
await hand.touch.snapshot() |
hand.touch().snapshot() |
TouchFrame |
Read touch snapshot |
await hand.touch.snapshot(module_indices=[...]) |
hand.touch().snapshot({...}) |
TouchFrame |
Read selected modules in request order; C ABI: revo3_device_touch_get_snapshot_modules(...) |
await hand.touch.module_snapshot(i) |
hand.touch().module_snapshot(i) |
TouchModuleData |
Read one module |
hand.touch.subscribe(period) |
hand.touch().subscribe(period) |
TouchSubscription |
Create touch subscription |
await hand.touch.enabled_mask() |
hand.touch().enabled_mask() |
int |
Read touch enable bitmask |
await hand.touch.set_enabled_mask(mask) |
hand.touch().set_enabled_mask(mask) |
None |
Set touch enable bitmask |
await hand.touch.module_enabled(i) |
hand.touch().module_enabled(i) |
bool |
Read one module enable state |
await hand.touch.set_module_enabled(i, enabled) |
hand.touch().set_module_enabled(i, enabled) |
None |
Set one module enable state |
await hand.touch.tare(module_index=None) |
hand.touch().tare() / hand.touch().tare(module_index) |
None |
Universal tactile zero-offset calibration entry point; leaving out module_index clears all modules, while passing one clears a single module (auto-routes to the current code family’s piezoresistive tactile array / high-density tactile matrix / fingertip 3D force/torque tactile module modules) |
await hand.health.snapshot() |
hand.health().snapshot() |
HealthSnapshot |
Read system health snapshot |
await hand.health.motor_module_temperatures_c() |
hand.health().motor_module_diagnostics() |
list[float] / MotorModuleDiagnostics |
Motor-module temperatures |
await hand.health.motor_online_mask() |
hand.health().motor_module_diagnostics() |
int / MotorModuleDiagnostics |
Motor online bitmask |
await hand.health.clear_motor_faults() |
hand.health().clear_motor_faults() |
None |
Clear motor faults |
5.5 ExperimentalCollision
Section titled “5.5 ExperimentalCollision”| Python | C++ | C | Returns | Behavior |
|---|---|---|---|---|
await hand.experimental_collision.configure(config) |
hand.experimental_collision().configure(config) |
revo3_experimental_collision_configure(...) |
None |
Configure or disable experimental collision detection |
await hand.experimental_collision.active_joints() |
hand.experimental_collision().active_joints() |
revo3_experimental_collision_get_active(...) |
21 booleans | Query latched experimental collision state |
await hand.experimental_collision.reset() |
hand.experimental_collision().reset() |
revo3_experimental_collision_reset(...) |
None |
Reset experimental collision state |
ExperimentalCollisionConfig, revo3::ExperimentalCollisionConfig, and
CRevo3ExperimentalCollisionConfig carry enablement, detection source,
position-error and current thresholds, debounce duration, maximum cached-state
age, response strategy, and auto-clear duration. See 4.5
for the safety and stability limits.
Python configuration fields and constructor defaults are:
| Field | Default | Unit or meaning |
|---|---|---|
enable |
False |
Master enable |
source |
HardwareOnly |
Detection source |
position_error_threshold_deg |
15.0 |
degree |
current_threshold_ma |
800.0 |
mA |
debounce_time_ms |
100 |
ms |
max_cached_status_age_ms |
50 |
ms |
strategy |
SoftStop |
Protection strategy |
auto_clear_time_ms |
1000 |
ms |
5.6 Touch, Config, Calibration, And Maintenance
Section titled “5.6 Touch, Config, Calibration, And Maintenance”Touch provides the unified read, configuration, and maintenance surface. Availability is determined by the current TouchLayout and protocol capabilities.
Read and Subscribe
Section titled “Read and Subscribe”hand.touch.layout: read the currentTouchLayout.await hand.touch.snapshot(): read a singleTouchFrame.await hand.touch.snapshot(module_indices=[...]): read only the selected modules and return them in module-ID request order.await hand.touch.module_snapshot(module_index): read and return oneTouchModuleDatadirectly.hand.touch.subscribe(period): create aTouchSubscription; pull the next frame vianext()and release it viaclose().
Layout Configuration
Section titled “Layout Configuration”await hand.touch.set_layout(layout): set a confirmed complete layout for the current connection session; it does not write device registers and only updates the SDK’s parsing routing.- Layout override supports the complete integrated layouts of
UF1–UF3,UT1, andUT2, plus the primary-link finger-pad and palm layouts ofUV1–UV4; unknown or incomplete layouts fail before any device request is sent.
Module Enable State
Section titled “Module Enable State”set_module_enabled(module_index, enabled)/module_enabled(module_index): operate on one logical module.set_enabled_mask(enabled_mask)/enabled_mask(): operate on or read the logical module bitmask.module_indextakes the publicmodule_id: in pure piezoresistive tactile array / high-density tactile matrix layouts it equals theTouchLayout.modulesarray position (dense 0~10); in hybrid layouts it uses sparse numbering and no longer matches the array position.
Read Mode
Section titled “Read Mode”set_read_mode(mode)/read_mode(): switch or read the piezoresistive tactile arrayPointArray/LegacyForceSummarymode.LegacyForceSummaryis a compatibility read mode that may be removed in a future release. In this mode,pointsisNoneand regional force values are written toregional_forces_mn. New applications should not depend on it.
Value Mode
Section titled “Value Mode”set_value_mode(mode, module_index=None)/value_mode(module_index=None): read or set the piezoresistive tactile array / high-density tactile matrix ADC or force mode.- The public enum contains only
Adc(0) andForce(2); unused piezoresistive tactile array register value1is rejected.
Zero Calibration
Section titled “Zero Calibration”tare(module_index=None): unified zero-offset calibration entry for piezoresistive tactile array, high-density tactile matrix, and fingertip 3D force/torque tactile module.cancel_tare(module_index=None): high-density tactile matrix only; writes a cancel command to restore the default/factory zero baseline. It does not imply an in-progress asynchronous procedure exists.tare_status(module_index=None): high-density tactile matrix only; reads the protocol-defined tare status. fingertip 3D force/torque tactile module has no corresponding status register.
Module Information and Maintenance
Section titled “Module Information and Maintenance”point_counts(): read piezoresistive tactile array or high-density tactile matrix runtime point counts.restart(module_index=None): restart high-density tactile matrix modules.hand.device_info.touch_serial_numbers: read the discovered touch module serial numbers; the C ABI exposes them throughCRevo3DeviceInfo.touch_serial_numbers.
| Python | C++ | Returns | Behavior |
|---|---|---|---|
await hand.touch.read_mode() |
hand.touch().read_mode() |
TouchReadMode |
Read touch layout mode |
await hand.touch.set_read_mode(mode) |
hand.touch().set_read_mode(mode) |
None |
Set touch layout mode |
await hand.touch.value_mode(module_index=None) |
hand.touch().value_mode(module_index) |
TouchValueMode |
Read touch value mode |
await hand.touch.set_value_mode(mode, module_index=None) |
hand.touch().set_value_mode(mode, module_index) |
None |
Set touch value mode |
await hand.touch.tare(module_index=None) |
hand.touch().tare(module_index) |
None |
Run zero-offset calibration |
await hand.touch.cancel_tare(module_index=None) |
hand.touch().cancel_tare(module_index) |
None |
Cancel a protocol-supported tare operation |
await hand.touch.tare_status(module_index=None) |
hand.touch().tare_status(module_index) |
TouchTareStatus |
Query tare status |
await hand.touch.point_counts() |
hand.touch().point_counts() |
list[int] / std::vector<uint16_t> |
Read module point counts |
await hand.touch.restart(module_index=None) |
hand.touch().restart(module_index) |
None |
Restart touch modules |
Config, Calibration, And Maintenance
Section titled “Config, Calibration, And Maintenance”| Python | C++ | Returns | Behavior |
|---|---|---|---|
await hand.config.snapshot() |
hand.config().snapshot() |
DeviceConfig |
Read device config |
hand.config.runtime_options |
hand.config().runtime_options() |
RuntimeOptions |
Read SDK runtime options |
hand.config.set_runtime_options(options) |
hand.config().set_runtime_options(options) |
None |
Set SDK runtime options |
await hand.config.set_buzzer(enabled) |
hand.config().set_buzzer(enabled) |
None |
Set buzzer |
await hand.config.set_vibration(enabled) |
hand.config().set_vibration(enabled) |
None |
Set vibration |
await hand.config.set_touch_screen(enabled) |
hand.config().set_touch_screen(enabled) |
None |
Set touch screen |
await hand.config.set_use_broadcast_id(enabled) |
hand.config().set_use_broadcast_id(enabled) |
None |
Set broadcast-ID usage |
await hand.config.set_power_on_auto_calibration(enabled) |
hand.config().set_power_on_auto_calibration(enabled) |
None |
Enable or disable automatic calibration on power-up |
await hand.config.set_auto_clear_motor_faults(enabled) |
hand.config().set_auto_clear_motor_faults(enabled) |
None |
Enable or disable automatic motor fault clearing |
await hand.config.set_max_continuous_current(ma) |
hand.config().set_max_continuous_current(ma) |
None |
Set max continuous current |
await hand.config.set_global_protect_current(ma) |
hand.config().set_global_protect_current(ma) |
None |
Set global protection current |
await hand.config.set_joint_protect_current(i, ma) |
hand.config().set_joint_protect_current(i, ma) |
None |
Set joint protection current |
await hand.config.set_joint_position_limits(i, min, max) |
hand.config().set_joint_position_limits(i, min, max) |
None |
Set joint position limits |
await hand.config.set_all_joint_position_limits(minimums, maximums) |
hand.config().set_all_joint_position_limits(minimums, maximums) |
None |
Validate and synchronize all 21 joint position limits |
await hand.config.set_joint_speed_limits(i, min, max) |
hand.config().set_joint_speed_limits(i, min, max) |
None |
Set joint speed limits |
await hand.config.set_rs485_baudrate(baudrate) |
hand.config().set_rs485_baudrate(baudrate) |
None / void |
Set RS485 baudrate |
await hand.config.set_canfd_baudrate(baudrate) |
hand.config().set_canfd_baudrate(baudrate) |
None / void |
Set CANFD baudrate |
await hand.calibration.calibrate_joints() |
hand.calibration().calibrate_joints() |
None |
Joint calibration |
await hand.calibration.set_current(ma) |
hand.calibration().set_current(ma) |
None |
Set calibration current |
await hand.calibration.zero_positions() |
hand.calibration().zero_positions() |
list[float] |
Read zero positions |
await hand.calibration.set_zero_positions(values) |
hand.calibration().set_zero_positions(values) |
None |
Set zero positions |
await hand.calibration.set_current_position_as_zero() |
hand.calibration().set_current_position_as_zero() |
None |
Set current posture as zero |
await hand.calibration.reset_finger_defaults() |
hand.calibration().reset_finger_defaults() |
None |
Reset finger defaults |
hand.maintenance.reboot() |
hand.maintenance().reboot() |
OperationHandle |
Reboot device |
hand.maintenance.update_firmware(path, target=None, wait_secs=10) |
hand.maintenance().update_firmware(path, target) |
OperationHandle |
Firmware update |
await hand.maintenance.factory_reset() |
hand.maintenance().factory_reset() |
None |
Factory reset |
await hand.maintenance.abort_firmware_update() |
hand.maintenance().abort_firmware_update() |
None |
Abort firmware update |
await hand.maintenance.reset_firmware_update_state() |
hand.maintenance().reset_firmware_update_state() |
None |
Reset firmware update state |
6. Data Structures & Types Reference
Section titled “6. Data Structures & Types Reference”This section details public data structures, status enums, and attribute fields returned by the Revo3 2.1 SDK.
6.1 Health & Diagnostics Structures
Section titled “6.1 Health & Diagnostics Structures”HealthSnapshot
Section titled “HealthSnapshot”Read-only system health and safety status snapshot:
| Field | Type | Description |
|---|---|---|
system_state |
int |
Global system status (0=Normal, 1=Fault) |
error_code |
int |
Global error code (0=Normal, 1=CommError, 2=NoCalibration, 3=TempAbnormal) |
current_ma |
int |
Total system current (mA) |
voltage_v |
float |
Bus voltage (V), converted from the raw register value with 0.01 V resolution |
power_w |
float |
Total system power (W), converted from the raw register value with 0.01 W resolution |
temperature_c |
int |
Controller chip/board temperature (°C) |
motor_fault_codes |
list[int] / std::array<int, 21> |
Per-joint raw status codes from input registers 2120..2140; the compatibility field name includes both fault and non-fault status bits |
faulted_motor_count |
int |
Number of motors with a non-zero defined fault code |
safety_state |
SafetyState |
System safety diagnostic state (Normal / RecoveryRequired / Faulted / Unknown) |
observed_at |
Timestamp |
Observation timestamp |
RuntimeStatistics
Section titled “RuntimeStatistics”SDK transport and communication quality statistics:
| Field | Type | Description |
|---|---|---|
state_reads |
int |
Successful motor status read count |
touch_reads |
int |
Successful touch frame read count |
commands_sent |
int |
Total write commands sent |
failed_operations |
int |
Total failed operation count |
servo_command_timeouts |
int |
Servo session heartbeat timeout count |
MotorModuleDiagnostics
Section titled “MotorModuleDiagnostics”Per-motor driver layer diagnostics:
| Field | Type | Description |
|---|---|---|
temperatures_c |
list[float] / std::array<float, 21> |
Per-motor temperatures (°C) |
online_mask |
int / uint32_t |
21-bit motor online bitmask (Bits 0 |
serial_numbers |
list[str] / std::vector<std::string> |
Per-motor serial numbers |
SafetyState (Enum)
Section titled “SafetyState (Enum)”Normal (0): System operating normally.RecoveryRequired (1): Recoverable error present, reboot/recovery required.Faulted (2): Severe fault state, motion commands halted.Unknown (3): State unknown.
6.2 Streaming Control & Subscriptions (ServoSession & Subscriptions)
Section titled “6.2 Streaming Control & Subscriptions (ServoSession & Subscriptions)”HandState
Section titled “HandState”Single frame snapshot of motor feedback states across all 21 joints:
| Field | Type | Description |
|---|---|---|
operating_states |
list[int] / std::array<int, 21> |
Per-joint raw operating-state bitmasks from input registers 2000..2020 |
positions_deg |
list[float] / std::array<float, 21> |
Per-motor current positions (deg) |
velocities_rpm |
list[float] / std::array<float, 21> |
Per-motor current velocities (rpm) |
currents_ma |
list[float] / std::array<float, 21> |
Per-motor current values (mA) |
timestamp |
Timestamp |
Frame arrival timestamp |
ServoSessionState (Enum)
Section titled “ServoSessionState (Enum)”Active (0): Servo session active, accepting high-frequency control frames.Expired (1): Heartbeat timeout (>100ms), session automatically expired.Closed (2): Session explicitly closed viaclose().
ServoSession.state is an observational interface for diagnostics and for distinguishing timeout expiration from explicit closure. The state may change immediately after it is read, so applications must not treat an Active check followed by a send as a concurrency guarantee. The result or structured error of each send remains authoritative. Both Expired and Closed are terminal; open a new Servo session before sending again.
ServoFilterMode (Enum)
Section titled “ServoFilterMode (Enum)”| Option | Value | Description |
|---|---|---|
Disabled |
0 |
Disable smoothing; targets enter the drag control loop directly |
FirstOrderLpf |
1 |
Smooth target positions with a first-order low-pass filter |
SecondOrderCriticallyDamped |
2 |
Smooth target positions with a second-order critically damped filter |
StateSubscription / TouchSubscription / HealthSubscription
Section titled “StateSubscription / TouchSubscription / HealthSubscription”Asynchronous periodic data subscription objects:
| Method | Returns | Description |
|---|---|---|
await sub.next() / sub.next() |
HandState / TouchFrame / HealthSnapshot |
Wait and receive next periodic sample frame |
sub.close() |
None |
Explicitly close subscription handle |
6.3 Touch Sensor Structures (Touch)
Section titled “6.3 Touch Sensor Structures (Touch)”TouchLayout
Section titled “TouchLayout”Touch sensor module topology and layout, including pure piezoresistive tactile array, high-density tactile matrix, and fingertip 3D force/torque tactile module layouts and the declared fingertip force/torque + piezoresistive-array, fingertip force/torque + high-density-matrix, and fingertip force/torque + high-density-matrix + piezoresistive-array hybrid layouts:
Applications dynamically inspect the hand’s touch distribution via TouchLayout, obtaining anatomical region groupings (Palm / Fingertips / Fingerpads) via regions, and layout ID, point count, and signal modalities via modules. Secondary-calibrated regional forces from LegacyForceSummary are read directly from each TouchModuleData.regional_forces_mn.
Note: The independent vision-tactile fingertips on
UV1–UV6use a dedicated data channel and are not merged into the primary-linkTouchLayoutorTouchFrame. Automatically detected piezoresistive tactile array/high-density tactile matrix finger-pad and palm arrays onUV1–UV4belong to the primary link and can appear in those structures.
| Field | Type | Description |
|---|---|---|
regions |
list[TouchRegionLayout] |
Region-grouped touch module layouts |
modules |
list[TouchModuleLayout] |
Touch module layout list |
TouchRegionLayout
Section titled “TouchRegionLayout”Anatomical region groupings of touch modules:
| Field | Type | Description |
|---|---|---|
region |
TouchRegion |
Touch region enum |
module_ids |
list[int] |
List of stable module IDs belonging to this region |
TouchModuleLayout
Section titled “TouchModuleLayout”Topology and channel configuration of a single touch module:
| Field | Type | Description |
|---|---|---|
module_id |
int |
Stable module ID (0~10) |
region |
TouchRegion |
Touch region enum |
region_index |
int |
Index within the region |
layout_id |
str |
Tactile layout ID (e.g. pressure_array_palm_36, fingertip_force_torque_48, fingertip_force_torque) |
point_count |
int |
Total tactile array point count |
signals |
list[TouchSignal] |
Supported tactile signal modalities |
TouchFrame
Section titled “TouchFrame”Single frame touch sensor snapshot:
| Field | Type | Description |
|---|---|---|
sequence |
int |
Frame sequence number |
timestamp |
Timestamp |
Packet reception timestamp |
modules |
list[TouchModuleData] |
Per-module touch data list |
TouchFrame does not use one mode to summarize the whole frame because a hybrid topology can carry tactile points, regional force values, and force/torque data together. Applications inspect regional_forces_mn and each module’s sample_state, points, force3d, torque2d, and resultant_force_mn. Device read configuration is represented separately by TouchReadMode.
TouchModuleData
Section titled “TouchModuleData”Multi-channel sensor data for a single touch module:
| Field | Type | Description |
|---|---|---|
region |
TouchRegion |
Touch region |
region_index |
int |
Region-local index |
module_id |
int |
Stable module ID |
layout_id |
str |
Module layout ID |
sample_state |
TouchSampleState |
Sampling state of this module in the current frame |
points |
list[int] | None |
Tactile points, or None when disabled, not sampled, or unavailable |
regional_forces_mn |
list[int] | None |
One or more secondary-calibrated regional resultant-force values for this module in the piezoresistive tactile array LegacyForceSummary compatibility mode, in mN |
force3d |
TouchForce3D | None |
fingertip 3D force/torque tactile module module Fx/Fy/Fz in the module-local coordinate system, in mN |
torque2d |
TouchTorque2D | None |
fingertip 3D force/torque tactile module module Mx/My around the module-local x/y axes, in Nm |
resultant_force_mn |
float | None |
Scalar resultant Fn over the entire fingertip 3D force/torque tactile module module tactile area, in mN; not Fz |
diagnostics |
TouchModuleDiagnostics | None |
Optional raw protocol diagnostics for troubleshooting; do not use it as the application state |
TouchModuleDiagnostics
Section titled “TouchModuleDiagnostics”TouchModuleDiagnostics preserves raw device status for logging and protocol troubleshooting. Applications should use TouchModuleData.sample_state to determine whether module data is valid.
| Field | Type | Description |
|---|---|---|
module_status_raw |
int |
Raw module status: 0 means warming up, 1 means ready, and 2 or an unknown value means unavailable |
sensor_fault_code_raw |
int |
Raw sensor fault code: 0 means normal; a nonzero value indicates a fault |
TouchForce3D / TouchTorque2D
Section titled “TouchForce3D / TouchTorque2D”3D Force and 2D Torque Vectors:
| Type | Field | Type | Description |
|---|---|---|---|
TouchForce3D |
x, y, z |
float |
3D force vector Fx, Fy, Fz (mN) |
TouchTorque2D |
x, y |
float |
2D torque vector Mx, My (Nm) |
TouchSampleState (Enum)
Section titled “TouchSampleState (Enum)”| Option | Value | Description |
|---|---|---|
Valid |
1 |
Module data is valid in this frame |
Disabled |
2 |
Module is disabled |
NotSampled |
3 |
The module was not polled and contributed no data to this frame |
ReadFailed |
4 |
Module read failed |
Unavailable |
5 |
Module data is unavailable |
WarmingUp |
6 |
fingertip 3D force/torque tactile module module warm-up is not complete |
SensorFault |
7 |
fingertip 3D force/torque tactile module module is ready but reports a sensor fault |
Snapshot consistency applies to the requested scope. A full snapshot() fails if any enabled module read fails; a selective snapshot(module_indices=[...]) fails if any selected enabled module read fails. Failed modules are not replaced with zero-filled data, and unselected modules are omitted from the returned frame. NotSampled and ReadFailed therefore remain reserved and are not produced during normal snapshot reads.
Touch Module layout_id Examples
Section titled “Touch Module layout_id Examples”The base layout_id format is <prefix>_<region>_<actual_point_count>:
- piezoresistive tactile array: e.g.,
pressure_array_palm_36,pressure_array_thumb_tip_31,pressure_array_fingertip_21,pressure_array_thumb_pad_57,pressure_array_finger_pad_52 - high-density tactile matrix: generated from runtime-reported point counts. Recent hardware observations include
high_density_matrix_palm_53,high_density_matrix_fingertip_56,high_density_matrix_finger_pad_22,high_density_matrix_fingertip_21, andhigh_density_matrix_finger_pad_27. Protocol capacities200/80/120are not actual counts and must not be used to construct layout IDs - fingertip 3D force/torque tactile module:
fingertip_force_torque_48identifies a fingertip module with a 48-point array;fingertip_force_torqueidentifies a force/torque fingertip module without a point array
The base ID distinguishes only region and actual point count. If modules with the same count use different point order or geometry, a controlled hardware-revision or module-identity mapping must provide a suffix such as _v2 or _v3. The SDK must not infer a layout revision from point count. Automatic detection currently emits only base IDs; a revision suffix must be added to the SDK’s controlled layout mapping before it is published as a public ID. Applications encountering an unknown ID must not apply an existing coordinate map, although they may still consume the one-dimensional data using point_count.
TouchRegion (Enum)
Section titled “TouchRegion (Enum)”| Option | Python value | C/C++ value | Description |
|---|---|---|---|
Fingertip |
0 |
1 |
Fingertip region; region_index identifies the digit |
FingerPad |
1 |
2 |
Fingerpad region; region_index identifies the digit |
Palm |
2 |
3 |
Palm region; region_index is 0 |
Within Fingertip and FingerPad, region_index maps Thumb/Index/Middle/Ring/Pinky to 0/1/2/3/4. Applications must not reference nonexistent enum options such as ThumbTip or IndexPad.
The C ABI additionally defines C_REVO3_TOUCH_REGION_UNKNOWN = 0 so unused region slots in a zero-initialized CRevo3TouchLayout have a valid representation. CRevo3TouchLayout has no explicit module count: valid modules must occupy contiguous slots starting at modules[0], and the first slot whose layout_id[0] == '\0' ends the list. All later slots must remain unused. revo3_device_touch_set_layout() rejects an Unknown region in a valid module or a non-empty layout_id after the terminating slot. Python and C++ object APIs do not expose this sentinel member.
TouchSignal (Enum)
Section titled “TouchSignal (Enum)”| Option | Description |
|---|---|
TouchPoint |
Tactile array pressure points |
Force3D |
3D contact force (Fx, Fy, Fz) |
Torque2D |
2D contact torque (Mx, My) |
ResultantForce |
Normal resultant contact force (Fn) |
TouchReadMode (Enum)
Section titled “TouchReadMode (Enum)”Touch data mode:
| Option | Value | Description |
|---|---|---|
PointArray |
0 |
Point-array mode: Outputs point-array data; point values are selected by TouchValueMode. |
LegacyForceSummary |
1 |
Secondary-calibrated force-summary compatibility mode: Supports a small number of shipped devices and is scheduled for removal; new applications should not depend on it. |
Applicability: Applies to layouts containing piezoresistive tactile array modules.
TouchValueMode (Enum)
Section titled “TouchValueMode (Enum)”Touch value mode:
| Option | Value | Description |
|---|---|---|
Adc |
0 |
ADC Value: Raw circuit sample value (for debugging). |
Force |
2 |
Force value: Force value output by the device. |
Supported Modes:
- piezoresistive tactile array modules: Supports
Adc(0) andForce(2). OnlyAdc(0) andForce(2) are public enum values; other values are rejected.- high-density tactile matrix modules: Supports
Adc(0) andForce(2). The SDK maps public value2to the high-density tactile matrix register value1.
TouchTareStatus (Enum)
Section titled “TouchTareStatus (Enum)”| Option | Value | Description |
|---|---|---|
NotTared |
0 |
Zero-offset calibration has not completed |
Tared |
1 |
Zero-offset calibration completed |
BusyOrFailed |
2 |
The operation is in progress or failed; the protocol does not provide a more specific state |
6.4 Device Metadata Structures
Section titled “6.4 Device Metadata Structures”DeviceInfo
Section titled “DeviceInfo”Basic device identity and hardware identifiers:
| Field | Type | Description |
|---|---|---|
product_code |
Python: str | None; C++: std::string |
Three-character product code (for example, UB1 or UT2); Python returns None and C++ returns an empty string for legacy or unknown SNs |
model |
Revo3Model |
Coarse SDK 2.x compatibility classification for legacy or unknown SN fallback; use product_code for exact product identity and capability decisions |
serial_number |
str |
Hand serial number (e.g. BCUBR40124000001) |
hand_side |
HandSide |
Hand side (Left / Right) |
hardware_revision |
str |
Hardware revision string |
motor_serial_numbers |
list[str] |
Per-motor serial numbers |
touch_serial_numbers |
list[str] |
Per-touch-module serial numbers |
FirmwareInfo
Section titled “FirmwareInfo”Firmware versions across sub-modules:
| Field | Type | Description |
|---|---|---|
main_controller |
str |
Main controller firmware version string |
motor_driver |
str |
Motor driver board firmware version string |
touch_module |
str |
Touch module firmware version string |
JointLayout
Section titled “JointLayout”Logical joint topology and degree-of-freedom specifications:
| Field | Type | Description |
|---|---|---|
layout_id |
str |
Logical joint topology identifier (e.g. Revo3Ultra21, Revo3Pro16, Revo3Basic13) |
version |
int |
Layout specification version (e.g. 1) |
joint_count |
int |
Total number of logical joints (e.g. 21, 16, 13) |
DeviceConfig
Section titled “DeviceConfig”Device hardware and control configuration parameter snapshot:
| Field | Python / C++ type | Description |
|---|---|---|
slave_id |
int / std::uint8_t |
Current device slave ID |
rs485_baudrate |
int / std::uint32_t |
Current RS485 baudrate (bps) |
canfd_baudrate |
int / std::uint32_t |
Current CANFD data baudrate (bps) |
buzzer_enabled |
bool |
Buzzer state |
vibration_enabled |
bool |
Vibration motor state |
touch_screen_enabled |
bool |
Touch screen state |
teaching_mode_enabled |
bool |
Zero-force/teaching mode state |
software_stop_enabled |
bool |
Software stop state |
use_broadcast_id |
bool |
Broadcast ID usage state |
power_on_auto_calibration_enabled |
bool |
Automatic calibration on power-up is enabled |
auto_clear_motor_faults_enabled |
bool |
Automatic motor fault clearing enabled |
max_continuous_current_ma |
float |
Maximum continuous current (mA) |
global_protect_current_ma |
float |
Global protection current (mA) |
joint_protect_current_ma |
list[float] / std::array<float, 21> |
Per-joint protection currents (mA); valid length is defined by JointLayout.joint_count |
joint_min_position_deg |
list[float] / std::array<float, 21> |
Per-joint minimum position limits (deg); valid length is defined by JointLayout.joint_count |
joint_max_position_deg |
list[float] / std::array<float, 21> |
Per-joint maximum position limits (deg); valid length is defined by JointLayout.joint_count |
joint_min_speed_rpm |
list[float] / std::array<float, 21> |
Per-joint minimum speed limits (rpm); valid length is defined by JointLayout.joint_count |
joint_max_speed_rpm |
list[float] / std::array<float, 21> |
Per-joint maximum speed limits (rpm); valid length is defined by JointLayout.joint_count |
persistence_scope |
str / - |
Python-only persistence scope description; currently firmware-defined |
RuntimeOptions
Section titled “RuntimeOptions”SDK runtime client configuration (process-local defaults, not written to the device):
| Field | Type | Description |
|---|---|---|
state_subscription_period_ms |
int |
Default State subscription pull interval (ms); default 20 |
touch_subscription_period_ms |
int |
Default Touch subscription pull interval (ms); default 20 |
health_subscription_period_ms |
int |
Default Health subscription pull interval (ms); default 1000 |
servo_command_timeout_ms |
int |
Default timeout (ms) between consecutive streaming Servo commands; default 100 |
Timestamp
Section titled “Timestamp”Data frame arrival and system timestamps:
| Field | Type | Description |
|---|---|---|
sec |
int |
Seconds |
nsec |
int |
Nanoseconds (0~999,999,999) |
clock |
TimestampClock |
Clock source (ProcessMonotonic, UnixRealtime) |
TimestampClock (Enum)
Section titled “TimestampClock (Enum)”| Enum Variant | Value | Description |
|---|---|---|
ProcessMonotonic |
0 |
Process-local monotonic clock (immune to wall clock adjustments) |
UnixRealtime |
1 |
UTC epoch realtime clock |
HandSide (Enum)
Section titled “HandSide (Enum)”| Option | Value | Description |
|---|---|---|
Left |
0 |
Left hand |
Right |
1 |
Right hand |
7. Waiting, Cancellation, And Motion Conflicts
Section titled “7. Waiting, Cancellation, And Motion Conflicts”Target motion, device restart, and firmware update return a Handle. Applications use the Handle to inspect state, wait for completion, or request cancellation. OperationHandle is the caller-facing name for motion and maintenance operation handles; all such handles use OperationState and the SDK does not define a duplicate MotionState. Joint calibration, software stop, and software-stop recovery wait directly for one device I/O operation and do not return a Handle. A device restart cannot be withdrawn after device I/O begins, so calling cancel() leaves its Handle in the current state.
OperationHandle├── id├── state└── errorHandle states are Pending, Running, Succeeded, Cancelled, Preempted, Failed, and Indeterminate. With the current hardware communication model, cooperative cancellation requested through cancel() ends in Indeterminate: read actual state before deciding whether to retry. Cancelled is reserved for a future protocol that can confirm deterministic device-side cancellation. Current firmware does not provide common progress or device-side start and finish times, so the Handle does not include those fields. Indeterminate means that the SDK does not know how far the device executed the operation; callers must not retry immediately.
7.1 OperationState (Enum)
Section titled “7.1 OperationState (Enum)”| Option | Value | Terminal | Description |
|---|---|---|---|
Pending |
0 |
No | Created but not yet executing |
Running |
1 |
No | Executing |
Succeeded |
2 |
Yes | Completed successfully |
Cancelled |
3 |
Yes | Device-side cancellation was confirmed; the current protocol generally cannot provide this confirmation |
Preempted |
4 |
Yes | Replaced by a newer operation of the same kind |
Failed |
5 |
Yes | Failed with an available error object |
Indeterminate |
6 |
Yes | Final device effect cannot be confirmed; read actual state first |
Target motion uses cooperative cancellation. The SDK lets the current register request finish, stops sending trajectory points at the next control-cycle boundary, and then releases software control ownership; it does not discard an in-flight serial request. Firmware update cancellation sends the device abort command at the next DFU polling or packet boundary. The Handle becomes Indeterminate when the cancellation request was issued but the final device position or write result cannot be confirmed.
Python handle.error and C++ handle.error() return the SdkError bound to that Handle; when no error exists, they return None and std::nullopt, respectively. Terminal state and error are published as one result. Therefore, when Failed or an error-bearing Indeterminate is observed, the corresponding error is already readable and does not depend on a thread-local last API error.
A Hand cannot run move_to() and a ServoSession at the same time; conflicts return ControlConflict. move_to() generates and sends a trajectory, while ServoSession accepts targets continuously from the caller.
Calling move_to() while another move_to() is active replaces the previous target. The SDK replans from the current feedback position and velocity, and the previous OperationHandle becomes Preempted. This behavior is intended only for low-rate replanning. For frequent target updates, use open_servo().
Joint calibration uses a single command. The SDK checks that no motion is active before sending it. Touch reads and Touch calibration do not affect motion. Firmware does not provide calibration progress or completion status, so a write response confirms only that the command was sent.
8. Error Handling, Timeouts, And Retries
Section titled “8. Error Handling, Timeouts, And Retries”SdkError├── code├── message├── retryable├── operation_effect├── recovery_requirement└── low_level_causeSdkError is the structured error returned by all API failures. Applications inspect code and message for logging and UI display. After a failed write, callers must inspect operation_effect: if it is Indeterminate, the command may have taken effect on the device while the response was lost; read device state before deciding whether to retry. The remaining fields provide retry eligibility (retryable), recommended recovery action (recovery_requirement), and underlying driver cause (low_level_cause).
Python represents code, operation_effect, and recovery_requirement with the SdkErrorCode, OperationEffect, and RecoveryRequirement enums. C++ uses strongly typed enums with the same names and does not expose untyped integer error fields.
8.1 Error Enums
Section titled “8.1 Error Enums”SdkErrorCode is the sole error identifier for programmatic branching. C++ additionally reserves Unknown = 0 when converting an unrecognized C ABI value; Python does not export that member.
| Value | SdkErrorCode |
Typical Meaning |
|---|---|---|
1 |
ConnectionFailed |
Connection establishment or transport failure |
2 |
InvalidArgument |
An argument violates the public contract |
3 |
InvalidState |
The current lifecycle or device state does not permit the operation |
4 |
Timeout |
Bounded wait or communication timeout |
5 |
UnsupportedCapability |
The current model, firmware, layout, or transport does not support the capability |
6 |
DeviceFault |
The device explicitly reported a fault |
7 |
Internal |
Internal SDK error |
8 |
ControlConflict |
Conflict with current motion or streaming-control ownership |
OperationEffect (Enum)
Section titled “OperationEffect (Enum)”| Option | Value | Description |
|---|---|---|
NotApplied |
1 |
The operation is confirmed not to have been applied to the device |
PartiallyApplied |
2 |
The operation was only partially applied; read state and follow API-specific recovery |
Indeterminate |
3 |
Whether the operation took effect cannot be confirmed; do not retry a write immediately |
RecoveryRequirement (Enum)
Section titled “RecoveryRequirement (Enum)”| Python Option | C++ Option | Value | Description |
|---|---|---|---|
None_ |
None |
0 |
No additional recovery action is required; Python uses None_ to avoid the keyword |
Retry |
Retry |
1 |
Retry only when allowed by retryable and the operation effect |
Reconnect |
Reconnect |
2 |
Reconnect and reacquire session state |
OperatorAction |
OperatorAction |
3 |
The device explicitly reported a fault that requires operator inspection or intervention |
The SDK returns only recovery actions for which it currently has an explicit decision rule. Safety-protection recovery and device power cycling do not yet have uniform firmware semantics or an automatic classification path, so they are not exposed as RecoveryRequirement values.
Retry rules:
- These rules apply to device requests made through the Hand API. Manager discovery, connection, and reconnection follow their own flows and are not command retries.
- A read-only request may be retried automatically only when the underlying connection has not been rebuilt and policy permits.
- If the response to a state-changing request is lost, the result is unknown: the command may or may not have run. The SDK must not send it again automatically.
- Disconnect, reconnect, safety recovery, and resending a command are distinct actions.
- A
wait(timeout)timeout ends only that wait; it does not automatically cancel the device operation. - Python and C++ use the same error codes and handling rules.
# Example: Catch structured SdkError and handle Indeterminate resultstry: await hand.motion.move_to(targets, duration=1.0)except sdk.SdkError as error: print(f"Code: {error.code}") if error.operation_effect == sdk.OperationEffect.Indeterminate: # Lost response: read state to check if operation took effect before retrying state = await hand.state.snapshot()9. Data, Timestamp, And Physical Unit Conventions
Section titled “9. Data, Timestamp, And Physical Unit Conventions”9.1 Time and Clock Model
Section titled “9.1 Time and Clock Model”- Control & Feedback Units: SDK public APIs uniformly use degrees (°) for positions, rpm for rotational velocities, and mA for currents. Lower-level drivers handle any necessary binary or unit conversions.
- Current Is Not Calibrated Joint Torque: Motor feedback and MIT feedforward fields are electrical current in mA. The device does not provide a calibrated joint-torque signal in Nm, so these fields remain
current/current_marather thantorque.
9.2 Timestamp Semantics
Section titled “9.2 Timestamp Semantics”- Receive Timestamp: The
timestampfield inStateandTouchsnapshots represents the SDK packet receive time. On Linux SocketCAN, kernel software timestamps are preferred; other transports use process monotonic clocks. - Scoping & Limits: The
timestampis not the internal firmware sample instant and cannot be used for cross-device hardware clock sync. For multi-frame snapshots,timestampmarks when full assembly completes and does not guarantee simultaneous sampling for all fields.
9.3 Unit Conversion Tools
Section titled “9.3 Unit Conversion Tools”Physical unit conversion utilities and conversions are provided across languages for ROS / ROS 2 and SI compatibility:
Physical Unit Conversion Constants
Section titled “Physical Unit Conversion Constants”- Angle:
deg_to_rad,rad_to_deg(1 deg = π / 180 rad) - Velocity:
rpm_to_rad_s,rad_s_to_rpm(1 rpm = π / 30 rad/s) - Current:
ma_to_a,a_to_ma(1 mA = 0.001 A)
C ABI and C++ Conversion Tools
Section titled “C ABI and C++ Conversion Tools”- C ABI (
revo3-sdk.h):revo3_deg_to_rad(float),revo3_rad_to_deg(float),revo3_rpm_to_rad_s(float),revo3_rad_s_to_rpm(float),revo3_ma_to_a(float),revo3_a_to_ma(float)and batch array counterparts (revo3_deg_to_rad_array(...), etc.). - C++ (
revo3::units):revo3::units::deg_to_rad(...)overloads supportingfloat,std::vector<float>, andstd::array<float, N>.StateSnapshot.positions_rad,velocities_rad_s, andcurrents_aprovide converted feedback views.
Python Conversion Functions
Section titled “Python Conversion Functions”sdk.deg_to_rad(value): accepts a float or a sequence of floats and returns radians.sdk.rad_to_deg(value): converts radians to degrees.sdk.rpm_to_rad_s(value): converts rotational speed rpm to angular velocity rad/s.sdk.rad_s_to_rpm(value): converts angular velocity rad/s to rotational speed rpm.sdk.ma_to_a(value): converts milliampere (mA) to ampere (A).sdk.a_to_ma(value): converts ampere (A) to milliampere (mA).HandState.positions_rad,velocities_rad_s, andcurrents_aprovide converted feedback views.
9.4 Module Utilities and Logging
Section titled “9.4 Module Utilities and Logging”The SDK exposes environment setup, logging initialization, version inspection, and port discovery tools across languages:
Logging System Initialization
Section titled “Logging System Initialization”- C ABI (
revo3-sdk.h):revo3_init_logging(level, enable_file_logging)initializes logging; when file output is enabled it writes tologs/revo3_<timestamp>.log. - C++ (
revo3::init_logging):revo3::init_logging(level=LOG_LEVEL_INFO, enable_file_logging=true)initializes logging. Applications should initialize logging once during process startup; subsequent calls can update the log level, while the initial call sets the target sinks (console/file). - Python (
main_mod.init_logging):sdk.init_logging(level=LogLevel.Info, enable_file_logging=True)configures SDK logging. When file logging is enabled, it installs an SDK-owned Pythonlogging.FileHandlerthat writes tologs/revo3_<timestamp>.log. Repeated calls replace that SDK file handler without removing handlers configured by the application.
Version and Hardware Utilities
Section titled “Version and Hardware Utilities”- Version Query:
- Python:
sdk.get_sdk_version()returns the exact SDK version string, including pre-release suffixes. - C++:
revo3::api_version()returns encoded version numbers and semantic strings.
- Python:
- Port Enumeration and VID/PID Allowlist:
- Python:
sdk.list_available_ports()returnslist[SerialPortInfo]without probing devices. - Python:
sdk.configure_usb_vid_pid_allowlist(custom_ids=[], include_defaults=True)configures the USB adapter VID/PID allowlist; setinclude_defaults=Falseto use only caller-provided entries.
- Python:
10. Language Binding Conventions
Section titled “10. Language Binding Conventions”10.1 C/C++ API
Section titled “10.1 C/C++ API”- Namespaces & Types: The minimum compiler standard is C++17. Public types reside in the
revo3namespace (e.g.revo3::Manager,revo3::Hand,revo3::OperationHandle), omitting redundantrevo3_prefixes from class and method names. - Version Query:
revo3::api_version()returns encoded version numbers, major/minor/patch, and an exact string including pre-release suffixes (such as2.0.0-rc.3). - C ABI:
revo3-sdk.hcan be directly included by C11 and C++17 compilers; C symbols uniformly use therevo3_prefix. SDK 2.0 does not export 1.xDeviceHandler, manual transport initialization, global callback setters, or unprefixedstark_*compatibility entries. - Object Layer: The C++17 object API is implemented on top of the public C ABI, providing RAII, strongly typed parameters, and exception translation without forming a redundant second protocol stack.
- Resource Management & Lifecycles:
ManagerandHandobjects can be moved but not copied. They release resources when leaving scope, or when callingclose()directly; repeatedclose()calls are safe and idempotent. - Async Handles & Waiting: Long operations (such as target motion, reboot, and firmware update) return a Handle object immediately; call
wait(std::chrono::milliseconds)to perform a blocking wait. - Exceptions & Status: Runtime errors throw
revo3::SdkError, while operation completion status is represented byOperationState.
10.2 Python API
Section titled “10.2 Python API”- Module Design & Types: Exports only the classes, enums, and data structs defined by this specification; provides no 1.x module-level or
DeviceContextcompatibility aliases. Supports Python 3.10+ with preciseT | None,Sequence[T], andAwaitable[T]stub typing. - Resource Management & Lifecycles: Supports calling
close()explicitly or usingasync withcontext managers to close ports and connections automatically on exit. - Async Handles & Waiting: Long operations (such as target motion, reboot, and firmware update) return a Handle object; call
await handle.wait(timeout)to wait for completion (the Handle itself is not directly awaitable). - Exceptions & Status: Shares identical
SdkErrorexception structures andOperationStatestatus representations with C++.