Quick Start
Robotic Hand R2 Python Start Guide
NOTE: For more guidance about other environments, please ask our agent to provide it.
0 Receive Package from Us
To receive your complete package, please contact our service team.
1 Preparation
Recommended use :
- Ubuntu 20.04/22.04 LTS, x86_64/aarch64, glibc v2.3.1+
- Mac 10.15 or later
- Windows 10/11
- Conda Python 3.8 ~ 3.12
- Notice: macOS doesn’t support CANFD, Windows doesn’t support EtherCAT
# Install Python dependencies, it is recommended to use conda to manage the Python environment.
➜ stark-serialport-example git:(main) cd python
➜ python git:(main) ls
__init__.py README.md requirements.txt revo2
➜ python git:(main) conda activate py310
(py310) ➜ python git:(main) which python
/path/to/miniconda/base/envs/py310/bin/python
(py310) ➜ python git:(main) python -V
Python 3.10.16
(py310) ➜ python git:(main) pip install -r requirements.txt --index-url https://pypi.org/simple/ # PyPI source
Looking in indexes: https://pypi.org/simple/
Collecting bc-stark-sdk==0.8.6 (from -r requirements.txt (line 1))
Downloading bc_stark_sdk-0.8.6-cp38-abi3-manylinux_2_31_x86_64.whl.metadata (283 bytes)
Requirement already satisfied: other-xxx1
Requirement already satisfied: other-xxx2
Downloading bc_stark_sdk-0.8.6-cp38-abi3-manylinux_2_31_x86_64.whl (1.7 MB)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 1.7/1.7 MB 1.7 MB/s eta 0:00:00
Installing collected packages: bc-stark-sdk
Successfully installed bc-stark-sdk-0.8.6
# If you cannot install using the above method, you can try manually downloading and then installing the .whl file using pip.
# https://pypi.org/project/bc-stark-sdk/0.8.6/#files
(py310) ➜ python git:(main) pip install --force-reinstall '/path/to/bc_stark_sdk-0.8.6-cp38-abi3-manylinux_2_31_x86_64.whl'
Processing /path/to/bc_stark_sdk-0.8.6-cp38-abi3-manylinux_2_31_x86_64.whl
Installing collected packages: bc-stark-sdk
Successfully installed bc-stark-sdk-0.8.6
# Device Permissions and User Groups
# In a Linux system, device files (such as serial devices) are located in the /dev directory. These device files typically belong to specific user groups (for example, dialout or tty), and only members of these groups can read or write to the device.
# For example, the serial device /dev/ttyUSB0 may have the following permissions:
crw-rw---- 1 root dialout 188, 0 Aug 14 12:00 /dev/ttyUSB0
# This means that only the root user and members of the dialout group can access this device.
# Adding users to the relevant user group
# In order to allow regular users to access serial devices, you can add users to the appropriate user group (usually the dialout group). This way, users can access and operate these devices without needing to elevate to root privileges.
# The command to add a user to the dialout group is as follows:
sudo usermod -aG dialout $USER
# run example
(py310) ➜ python git:(main) ✗ cd revo2
# Control/Read information - Single hand
(py310) ➜ revo2 git:(main) ✗ python revo2_ctrl.py
# Control/Read Information - Single Hand (Tactile Version)
(py310) ➜ revo2 git:(main) ✗ python revo2_touch.py
# Control/Read information - multiple hands
(py310) ➜ revo2 git:(main) ✗ python revo2_ctrl_multi.py
# Action sequence
(py310) ➜ revo2 git:(main) ✗ python revo2_action_seq.py
# Update the configuration, modify the device ID, baud rate, Turbo mode, etc.
(py310) ➜ revo2 git:(main) ✗ python revo2_cfg.py
# Firmware OTA
(py310) ➜ revo2 git:(main) ✗ python revo2_dfu.py
## CANFD Communication protocol
(py310) ➜ python git:(main) ✗ cd revo2_canfd
# ZQWL CANFD equipment currently only provides Windows dependencies.
# For CAN FD devices from other manufacturers, please implement the read and write callbacks by yourself. The SDK itself supports Windows and Linux.
(py310) ➜ revo2_canfd git:(main) ✗ python zqwl_canfd.py # Read device information and control the device.
(py310) ➜ revo2_canfd git:(main) ✗ python zqwl_canfd_dfu.py # Firmware OTA
## EtherCAT Communication protocol
(py310) ➜ python git:(main) ✗ cd revo2_ethercat
(py310) ➜ revo2_ethercat git:(main) ✗ python ec_sdo.py # Sdo read/configure
(py310) ➜ revo2_ethercat git:(main) ✗ python ec_pdo.py # Pdo reads joint status and controls the device.
(py310) ➜ revo2_ethercat git:(main) ✗ python ec_dfu.py # Firmware OTA 2.1 Hand Control
from typing import Sequence
from enum import Enum
# Joint ID
class FingerId(Enum):
Thumb = ...
ThumbAux = ...
Index = ...
Middle = ...
Ring = ...
Pinky = ...
# ================================
# Joint Control Interface
# ================================
def set_finger_position(self, slave_id: int, finger_id: FingerId, position: int):
"""
Set the position of a single joint
Args:
slave_id: Device slave ID
finger_id: FingerId enum value
- Thumb: Thumb
- ThumbAux: Thumb Auxiliary
- Index: Index Finger
- Middle: Middle Finger
- Ring: Ring Finger
- Pinky: Pinky Finger
position: Target position value
Example:
# Set thumb position
device.set_finger_position(slave_id=1, finger_id=FingerId.Thumb, position=500)
# Set index finger position
device.set_finger_position(slave_id=1, finger_id=FingerId.Index, position=1000)
"""
pass
def set_finger_position_with_millis(self, slave_id: int, finger_id: FingerId,
position: int, milliseconds: int):
"""
Set the position of a single joint (with specified running time)
Args:
slave_id: Device slave ID
finger_id: FingerId enum value
position: Target position value
milliseconds: Running time (in milliseconds)
Example:
# Move thumb to position 500 in 2 seconds
device.set_finger_position_with_millis(
slave_id=1,
finger_id=FingerId.Thumb,
position=500,
milliseconds=2000
)
"""
pass
def set_finger_position_with_speed(self, slave_id: int, finger_id: FingerId,
position: int, speed: int):
"""
Set the position of a single joint (with specified running speed)
Args:
slave_id: Device slave ID
finger_id: FingerId enum value
position: Target position value
speed: Running speed value
Example:
# Move thumb to position 500 at speed 300
device.set_finger_position_with_speed(
slave_id=1,
finger_id=FingerId.Thumb,
position=500,
speed=300
)
"""
pass
def set_finger_positions(self, slave_id: int, positions: Sequence[int]):
"""
Set the positions of all joints
Args:
slave_id: Device slave ID
positions: List of position values in the order [Thumb, ThumbAux, Index, Middle, Ring, Pinky]
Example:
# Set all joints to specified positions
positions = [500, 500, 1000, 1000, 1000, 1000] # Thumb half open, other joints closed
device.set_finger_positions(slave_id=1, positions=positions)
"""
pass
def set_finger_positions_and_durations(self, slave_id: int, positions: Sequence[int],
durations: Sequence[int]):
"""
Set the positions and running times for all joints
Args:
slave_id: Device slave ID
positions: List of position values
durations: List of running times (in milliseconds)
Example:
positions = [1000, 1000, 1000, 1000, 1000, 1000]
durations = [2000, 2000, 1500, 1500, 1500, 1500] # Different running times for different joints
device.set_finger_positions_and_durations(slave_id=1, positions=positions, durations=durations)
"""
pass
def set_finger_positions_and_speeds(self, slave_id: int, positions: Sequence[int],
speeds: Sequence[int]):
"""
Set the positions and running speeds for all joints
Args:
slave_id: Device slave ID
positions: List of position values
speeds: List of speed values
Example:
positions = [1000, 1000, 1000, 1000, 1000, 1000]
speeds = [300, 300, 400, 400, 400, 400] # Different speeds for different joints
device.set_finger_positions_and_speeds(slave_id=1, positions=positions, speeds=speeds)
"""
pass
def set_finger_speed(self, slave_id: int, finger_id: FingerId, speed: int):
"""
Set the running speed for a single joint
Args:
slave_id: Device slave ID
finger_id: FingerId enum value
speed: Target speed value
Example:
device.set_finger_speed(slave_id=1, finger_id=FingerId.Thumb, speed=400)
"""
pass
def set_finger_speeds(self, slave_id: int, speeds: Sequence[int]):
"""
Set the running speeds for all joints
Args:
slave_id: Device slave ID
speeds: List of speed values
Example:
speeds = [300, 300, 400, 400, 400, 400]
device.set_finger_speeds(slave_id=1, speeds=speeds)
"""
pass
def set_finger_current(self, slave_id: int, finger_id: FingerId, current: int):
"""
Set the driving current for a single joint
Args:
slave_id: Device slave ID
finger_id: FingerId enum value
current: Target current value
Example:
device.set_finger_current(slave_id=1, finger_id=FingerId.Thumb, current=200)
"""
pass
def set_finger_currents(self, slave_id: int, currents: Sequence[int]):
"""
Set the driving currents for all joints
Args:
slave_id: Device slave ID
currents: List of current values
Example:
currents = [200, 200, 250, 250, 250, 250]
device.set_finger_currents(slave_id=1, currents=currents)
"""
pass
def set_finger_pwm(self, slave_id: int, finger_id: FingerId, pwm: int):
"""
Set the PWM control value for a single joint
Args:
slave_id: Device slave ID
finger_id: FingerId enum value
pwm: PWM control value
Example:
device.set_finger_pwm(slave_id=1, finger_id=FingerId.Thumb, pwm=50)
"""
pass
def set_finger_pwms(self, slave_id: int, pwms: Sequence[int]):
"""
Set the PWM control values for all joints
Args:
slave_id: Device slave ID
pwms: List of PWM control values
Example:
pwms = [50, 50, 60, 60, 60, 60]
device.set_finger_pwms(slave_id=1, pwms=pwms)
"""
pass
2.2 Get Device Information
from typing import List
from enum import Enum
# Joint ID
class FingerId(Enum):
Thumb = ... # Thumb
ThumbAux = ... # Thumb Auxiliary
Index = ... # Index Finger
Middle = ... # Middle Finger
Ring = ... # Ring Finger
Pinky = ... # Pinky Finger
# Joint (Motor) State
class MotorState(Enum):
Unknown = ...
Idle = ... # Idle
Running = ... # Running
Stall = ... # Stalled
Turbo = ... # Turbo Mode
# Full Joint Status Information
class MotorStatusData:
positions: list[int] # Joint positions
speeds: list[int] # Motion speeds
currents: list[int] # Drive currents
states: list[MotorState] # Joint states
description: str # Status description
def get_motor_status(self, slave_id: int) -> MotorStatusData:
"""
Get comprehensive joint status data
Args:
slave_id: Device slave ID
Returns:
MotorStatusData: A data object containing the following attributes:
- positions: List[int] - Current positions of all joints
- speeds: List[int] - Current motion speeds of all joints
- currents: List[int] - Current drive currents of all joints
- states: List[MotorState] - Current running states of all joints
- description: str - Status description
Methods provided by MotorStatusData:
- is_idle() -> bool: Returns True if all joints are idle
- is_opened() -> bool: Returns True if the hand is in the open position
- is_closed() -> bool: Returns True if the hand is in the closed position
- desc() -> str: Returns the status description string
Example:
motor_status = device.get_motor_status(slave_id=1)
print(f"Joint positions: {motor_status.positions}")
print(f"Motion speeds: {motor_status.speeds}")
print(f"Drive currents: {motor_status.currents}")
if motor_status.is_idle():
print("All joints are idle")
"""
pass
def get_finger_positions(self, slave_id: int) -> List[int]:
"""
Get current positions of all joints
Args:
slave_id: Device slave ID
Returns:
List[int]: A list of joint position values in the order:
[Thumb, ThumbAux, Index, Middle, Ring, Pinky]
Example:
positions = device.get_finger_positions(slave_id=1)
print(f"Thumb position: {positions[0]}")
print(f"Index finger position: {positions[2]}")
"""
pass
def get_finger_speeds(self, slave_id: int) -> List[int]:
"""
Get current motion speeds of all joints
Args:
slave_id: Device slave ID
Returns:
List[int]: A list of joint speed values in the order:
[Thumb, ThumbAux, Index, Middle, Ring, Pinky]
Example:
speeds = device.get_finger_speeds(slave_id=1)
print(f"All joint speeds: {speeds}")
"""
pass
def get_finger_currents(self, slave_id: int) -> List[int]:
"""
Get current drive currents of all joints
Args:
slave_id: Device slave ID
Returns:
List[int]: A list of joint current values in the order:
[Thumb, ThumbAux, Index, Middle, Ring, Pinky]
Example:
currents = device.get_finger_currents(slave_id=1)
print(f"All joint currents: {currents}")
"""
pass
def get_motor_state(self, slave_id: int) -> MotorState:
"""
Get the current joint state
Args:
slave_id: Device slave ID
Returns:
MotorState: Motor state enum
- Unknown: Unknown state
- Idle: Joint is idle
- Running: Joint is running
- Stall: Joint is stalled
- Turbo: Turbo high-speed mode
Example:
motor_state = device.get_motor_state(slave_id=1)
if motor_state == MotorState.Running:
print("The joint is running")
elif motor_state == MotorState.Stall:
print("The joint is stalled")
"""
pass
2.3 Touch Sensor
# ================================
# Tactile Sensor Interface
# ================================
# Tactile sensor data
# Normal force, tangential force, tangential force direction, proximity value, state
class TouchFingerItem:
normal_force1: int # Normal force in 0.01 N. For example, 1000 means 1000 × 0.01 = 10 N. Range: 0 ~ 25 N
tangential_force1: int # Tangential force in 0.01 N. For example, 1000 means 1000 × 0.01 = 10 N. Range: 0 ~ 25 N
tangential_direction1: int # Tangential force direction, 0–359°. 0° is toward the fingertip; clockwise up to 359°. 65535 (0xFFFF) means invalid.
self_proximity1: int # Self-capacitance 1
class TouchFingerData:
items: list[TouchFingerItem]
# Raw tactile data
class TouchRawData:
thumb: list[int]
index: list[int]
middle: list[int]
ring: list[int]
pinky: list[int]
description: str
def desc(self) -> str: ...
"""
This document demonstrates dedicated tactile sensor interfaces for Revo1 Touch and Revo2 Touch robotic hands.
Tactile sensor features:
- Multi-dimensional force sensing: normal force, tangential force, tangential direction
- Proximity detection: self-capacitance and mutual capacitance
- Contact state recognition: no contact, static contact, sliding contact
- Real-time data stream: supports high-frequency acquisition and callbacks
Before use, make sure your device supports tactile sensors:
device_info = device.get_device_info(slave_id=1)
if device_info.is_touch():
print("Device supports tactile sensors")
if device_info.is_revo1_touch():
print("Revo1 Touch - 1st Gen Tactile Version")
elif device_info.is_revo2_touch():
print("Revo2 Touch - 2nd Gen Tactile Version")
Usage flow:
1. Check if the device supports tactile sensors
2. Enable and initialize the tactile sensor system
3. Retrieve tactile data
4. Process tactile data and implement control logic
5. Calibrate zero drift and reset tactile sensors
"""
from typing import List, Optional, Callable, Any
# ================================
# Basic Tactile Sensor Interface
# ================================
def get_touch_sensor_enabled(self, slave_id: int) -> bool:
"""
Check if the tactile sensor is enabled
Args:
slave_id: Device slave ID
Returns:
bool: True if enabled, False if disabled
Notes:
- Tactile sensors are disabled by default to save power
- You must enable them to retrieve data
- Applicable to all tactile-supported devices
Example:
if device.get_device_info(slave_id=1).is_touch():
enabled = device.get_touch_sensor_enabled(slave_id=1)
if not enabled:
print("Tactile sensor is not enabled. Please enable it first.")
else:
print("Tactile sensor is enabled")
"""
pass
def get_touch_sensor_fw_versions(self, slave_id: int) -> List[str]:
"""
Get the firmware versions of tactile sensors
Args:
slave_id: Device slave ID
Returns:
List[str]: List of firmware versions for each finger
Notes:
- The tactile sensor must be enabled first
- Each finger sensor may have a different firmware version
- Version info is useful for diagnostics and compatibility checks
Example:
if device.get_touch_sensor_enabled(slave_id=1):
versions = device.get_touch_sensor_fw_versions(slave_id=1)
finger_names = ["Thumb", "Index", "Middle", "Ring", "Pinky"]
for i, version in enumerate(versions):
if i < len(finger_names):
print(f"{finger_names[i]} tactile sensor firmware version: {version}")
else:
print("Please enable the tactile sensor first")
"""
pass
def touch_sensor_setup(self, slave_id: int, bits: int):
"""
Enable and initialize the tactile sensor system
Args:
slave_id: Device slave ID
bits: Bitmask specifying which sensors to enable
- Use bitwise operations to select fingers
- e.g., 0x1F (0b11111) enables all five fingers
- e.g., 0x03 (0b00011) enables only thumb and index
Bitmask:
- Bit 0: Thumb
- Bit 1: Index
- Bit 2: Middle
- Bit 3: Ring
- Bit 4: Pinky
Notes:
- This is the first step before using tactile sensors
- Sensors perform initialization and self-check when enabled
- Initialization may take a few seconds
Example:
# Enable all tactile sensors
device.touch_sensor_setup(slave_id=1, bits=0x1F)
print("All tactile sensors enabled")
# Enable thumb and index only
device.touch_sensor_setup(slave_id=1, bits=0x03)
print("Thumb and index tactile sensors enabled")
# Enable index only
device.touch_sensor_setup(slave_id=1, bits=0x02)
print("Index tactile sensor enabled")
"""
pass
def touch_sensor_reset(self, slave_id: int, bits: int):
"""
Reset specified tactile sensors
Args:
slave_id: Device slave ID
bits: Bitmask specifying which sensors to reset
Notes:
- Sensors return abnormal states during reset
- Normal operation resumes after reset
- Useful for resolving sensor errors or reinitialization
- Reset does not affect the enabled state
Example:
# Reset all tactile sensors
device.touch_sensor_reset(slave_id=1, bits=0x1F)
print("All tactile sensors reset")
# Reset thumb sensor only
device.touch_sensor_reset(slave_id=1, bits=0x01)
print("Thumb tactile sensor reset")
"""
pass
def touch_sensor_calibrate(self, slave_id: int, bits: int):
"""
Perform zero-drift calibration for tactile sensors
Args:
slave_id: Device slave ID
bits: Bitmask specifying which sensors to calibrate
Calibration notes:
- Make sure the fingers are not touching anything
- This removes zero-drift error
- Recommended after temperature stabilization
- Improves accuracy significantly
When to calibrate:
- First-time use
- Large ambient temperature changes
- Sensor readings show clear offset
- Periodic maintenance
Example:
# Calibrate all tactile sensors
print("Calibrating tactile sensors. Ensure fingers are not touching anything...")
device.touch_sensor_calibrate(slave_id=1, bits=0x1F)
print("Calibration completed")
# Calibrate thumb only
device.touch_sensor_calibrate(slave_id=1, bits=0x01)
print("Thumb tactile sensor calibrated")
"""
pass
def get_touch_raw_data(self, slave_id: int) -> TouchRawData:
"""
Get raw tactile data (mainly for Revo1 Touch)
Args:
slave_id: Device slave ID
Returns:
TouchRawData: Object containing:
- thumb: List[int] - raw thumb data
- index: List[int] - raw index data
- middle: List[int] - raw middle data
- ring: List[int] - raw ring data
- pinky: List[int] - raw pinky data
- description: str - data description
TouchRawData provides:
- desc() -> str: Get description
Notes:
- Only for tactile-supported devices, mainly Revo1 Touch
- Must enable tactile sensors first
- Raw data requires manual parsing
- Format may differ by sensor version
Example:
if device.get_device_info(slave_id=1).is_revo1_touch():
if device.get_touch_sensor_enabled(slave_id=1):
raw_data = device.get_touch_raw_data(slave_id=1)
print(f"Thumb raw: {raw_data.thumb}")
print(f"Index raw: {raw_data.index}")
print(f"Description: {raw_data.desc()}")
if len(raw_data.thumb) > 0:
avg_thumb = sum(raw_data.thumb) / len(raw_data.thumb)
print(f"Thumb average: {avg_thumb}")
"""
pass
def get_single_touch_sensor_status(self, slave_id: int, index: int) -> TouchFingerItem:
"""
Get tactile sensor data for a single finger
Args:
slave_id: Device slave ID
index: Finger index (0–4, Thumb to Pinky)
Notes:
- The corresponding sensor must be enabled first
Example:
if device.get_device_info(slave_id=1).is_revo2_touch():
thumb_status = device.get_single_touch_sensor_status(slave_id=1, index=0)
"""
pass
def get_touch_sensor_status(self, slave_id: int) -> TouchFingerData:
"""
Get tactile sensor data for all fingers
Args:
slave_id: Device slave ID
Returns:
TouchFingerData
Notes:
- More efficient than querying each finger individually
- Suitable for multi-finger tactile processing
Example:
if device.get_device_info(slave_id=1).is_revo2_touch():
touch_data = device.get_touch_sensor_status(slave_id=1)
finger_names = ["Thumb", "Index", "Middle", "Ring", "Pinky"]
"""
pass