User Manuel -Hand R1
Robotic Hand R1 Python Start Guide
- OTA
Communication
- Details
- Sample Code
- Preparation
C++ SDK
- Details
- Sample Code Modbus
Python SDK
Start Preparation
Recommended use :
- Ubuntu 20.04/22.04 LTS
- Mac 10.15 or later
- Windows 10/11
- Conda Python 3.8 ~ 3.12
# 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 Example Codes
Modbus-RTU
Single hand control
"""
Revo1 Dexterous Hand Automatic Control Example
This example demonstrates how to implement automatic control for the Revo1 dexterous hand, including:
- Initialization and connection of the hand
- Periodic motor state monitoring
- Automatic grip/open actions based on current states
- Precise control of individual fingers
- Creation and management of asynchronous tasks
"""
import asyncio
import sys
import time
from utils import setup_shutdown_event
from revo1_utils import *
async def get_motor_status_periodically(client, slave_id):
"""
Periodically retrieve motor status and perform automatic control.
This function continuously monitors the motor status and automatically triggers
grip/open actions based on finger positions:
- When the fingers are open, perform a grip action.
- When the fingers are closed, perform an open action.
- Record the time cost and status information for each operation.
Args:
client: Modbus client instance
slave_id: Device ID
"""
logger.info("Motor status monitoring started")
index = 0
while True:
try:
# Retrieve motor status
logger.debug("get_motor_status")
start = time.perf_counter()
status: libstark.MotorStatusData = await client.get_motor_status(slave_id)
cost_ms = (time.perf_counter() - start) * 1000
# Log status information
logger.info(
f"[{index}] Motor status - cost: {cost_ms:.2f}ms, "
f"is_idle: {status.is_idle()}, "
f"is_closed: {status.is_closed()}, "
f"is_opened: {status.is_opened()}"
)
logger.info(f"[{index}] Motor status: {status.description}")
index += 1
# Perform automatic control based on current state
if status.is_idle():
if status.is_opened():
# When fingers are open, perform a grip action
# Position values: [Thumb, ThumbAux, Index, Middle, Ring, Pinky]
await client.set_finger_positions(
slave_id, [60, 60, 100, 100, 100, 100]
)
elif status.is_closed():
# When fingers are closed, perform an open action
# Set all fingers to the minimum position value (0)
await client.set_finger_positions(slave_id, [0] * 6)
except Exception as e:
logger.error(f"Error in motor status monitoring: {e}")
# Wait 1 second before retrying after an error
await asyncio.sleep(1)
async def main():
"""
Main function: Initialize the connection to the dexterous hand and start the automatic control task.
"""
# Set up shutdown event listener
shutdown_event = setup_shutdown_event(logger)
# Detect the baud rate and device ID of the dexterous hand, and initialize the client
(client, slave_id) = await open_modbus_revo1(port_name=None, quick=True) # Replace with the actual serial port name; passing None will try automatic detection
# Perform initialization actions
# Control a single finger: close the pinky to 100% position
await client.set_finger_position(slave_id, libstark.FingerId.Pinky, 100)
await asyncio.sleep(1) # Wait 1 second
# Perform a grip action: set all fingers to the preset positions
# Position parameters: [Thumb 60%, ThumbAux 60%, Index 100%, Middle 100%, Ring 100%, Pinky 100%]
await client.set_finger_positions(slave_id, [60, 60, 100, 100, 100, 100])
await asyncio.sleep(1) # Wait 1 second
# Perform an OPEN action: set all fingers to the minimum position (0%)
await client.set_finger_positions(slave_id, [0] * 6)
await asyncio.sleep(1) # Wait 1 second
# Create and start the motor status monitoring task
asyncio.create_task(get_motor_status_periodically(client, slave_id))
logger.info("Motor status monitoring task started")
# Wait for the shutdown event (Ctrl+C or another termination signal)
await shutdown_event.wait()
# Close resources
libstark.modbus_close(client)
logger.info("Modbus client closed")
sys.exit(0)
if __name__ == "__main__":
asyncio.run(main())
Double hand control - Use two serial ports
"""
Revo1 Dual-Hand Control Example
Connection Instructions:
- Use two serial ports to connect the left and right hands separately.
- Both devices can share the same device ID (default ID is 1).
"""
import asyncio
import sys
import time
from revo1_utils import *
from utils import setup_shutdown_event
LEFT_PORT = "/dev/ttyUSB0" # Replace with the actual left-hand device port
RIGHT_PORT = "/dev/ttyUSB1" # Replace with the actual right-hand device port
LEFT_ID = 1 # Replace with the actual left-hand device ID
RIGHT_ID = 1 # Replace with the actual right-hand device ID
BAUDRATE = libstark.Baudrate.Baud115200
async def main():
"""
Main function: initialize device connections and control tasks.
"""
libstark.init_config(libstark.StarkProtocolType.Modbus)
shutdown_event = setup_shutdown_event(logger)
# Open both devices
client_left = await libstark.modbus_open(LEFT_PORT, BAUDRATE)
client_right = await libstark.modbus_open(RIGHT_PORT, BAUDRATE)
if not client_left or not client_right:
logger.critical("Failed to open one or both serial ports.")
sys.exit(1)
# Retrieve device information
left_info = await client_left.get_device_info(LEFT_ID)
right_info = await client_right.get_device_info(RIGHT_ID)
if not left_info or not right_info:
logger.critical("Failed to get device info for one or both devices.")
sys.exit(1)
logger.info(f"Left: {left_info.description}")
logger.info(f"Right: {right_info.description}")
# Start motor status monitoring task
task = asyncio.create_task(
monitor_and_control(client_left, client_right, LEFT_ID, RIGHT_ID)
)
# Wait for shutdown event
await shutdown_event.wait()
# Clean up resources
task.cancel()
await asyncio.gather(task, return_exceptions=True)
await client_left.modbus_close()
await client_right.modbus_close()
logger.info("Modbus clients closed")
sys.exit(0)
async def monitor_and_control(client_left, client_right, left_id, right_id):
"""
Periodically retrieve motor status and perform automatic control.
"""
logger.info(f"Motor status monitoring started for devices {left_id:02x} and {right_id:02x}")
index = 0
positions = [0] * 6
period = 2.0
while True:
try:
index += 1
now_ms = int(time.time() * 1000)
t = (now_ms / 1000.0) % period
if t < period / 2:
positions = [0, 0, 0, 0, 0, 0]
else:
positions = [0, 0, 100, 100, 100, 100]
# Retrieve motor status
for client, dev_id, hand in [
(client_left, left_id, "Left"),
(client_right, right_id, "Right"),
]:
start = time.perf_counter()
try:
status = await client.get_motor_status(dev_id)
cost_ms = (time.perf_counter() - start) * 1000
logger.debug(
f"{hand} [{dev_id:02x}] [{index}] Motor status - cost: {cost_ms:.2f}ms, "
f"is_idle: {status.is_idle()}, is_closed: {status.is_closed()}, is_opened: {status.is_opened()}"
)
logger.debug(f"{hand} [{dev_id:02x}] [{index}] Motor status: {status.description}")
except Exception as e:
logger.error(f"Error getting motor status for {hand} [{dev_id:02x}]: {e}")
logger.info(f"Publishing positions: {positions}")
await asyncio.gather(
client_left.set_finger_positions(left_id, positions),
client_right.set_finger_positions(right_id, positions),
)
await asyncio.sleep(0.01)
except Exception as e:
logger.error(f"Error in monitor_and_control loop: {e}")
await asyncio.sleep(1)
if __name__ == "__main__":
asyncio.run(main())
Dual-hand Control - Connect the left hand and the right hand via a single serial port. The left hand and the right hand need to be configured with different IDs, such as 1 and 2.
"""
Revo1 Smart Hand Multi-device Control example
This example demonstrates how to control multiple Revo1 Dextrous Hand devices simultaneously, including:
- Multi-device connection methods (single serial port for multiple devices vs multiple serial ports for multiple devices)
- Tactile sensor configuration and data acquisition (tactile version)
- Motor status monitoring and automatic control
- Finger position and current control
- Concurrent task management and exception handling
Connection Method Description:
- Method 1: Connect multiple devices via a single serial port (requires configuring different device IDs)
- Method 2: Connect different devices via multiple serial ports (can use the same device ID)
Applicable Scenarios:
- Two-handed control systems
- Multi-device collaborative operations
- Device performance comparison testing
"""
import asyncio
import sys
import time
from revo1_utils import *
from utils import setup_shutdown_event
# Multi-device Control Configuration
# Method 1: Connecting multiple devices to a single serial port (requires configuring different device IDs)
# Method 2: Multiple serial ports are connected to different devices (the same device ID can be used)
# For example:
# client_left_hand = await libstark.modbus_open(port_name_left, baudrate)
# client_right_hand = await libstark.modbus_open(port_name_right, baudrate)
# Device ID List Configuration
slave_ids = [1] # A single serial port connects to one device
# slave_ids = [1, 2] # A single serial port connects two devices, with device IDs 1 and 2 respectively.
# Device Tactile Version Marking Dictionary
# Used to record whether each device is a tactile version, so that they can be distinguished during subsequent processing
slave_is_touch = {1: False, 2: False}
async def main():
"""
Main function: Initialize device connection and control tasks
"""
# Set up a close event listener
shutdown_event = setup_shutdown_event(logger)
# Open ModbusConnection
(client, detected_slave_id) = await open_modbus_revo1()
# Configure a tactile sensor for each device and start the status monitoring task
for slave_id in slave_ids:
await setup_touch_sensor(client, slave_id)
asyncio.create_task(get_motor_status_periodically(client, slave_id))
logger.info("Status monitoring tasks started")
# An example of multiple serial ports connected to different devices (optional)
# Suitable for scenarios where complete independent control of two devices is required
# client_left_hand = await libstark.modbus_open("/dev/ttyUSB0", libstark.Baudrate.Baud115200)
# client_right_hand = await libstark.modbus_open("/dev/ttyUSB1", libstark.Baudrate.Baud115200)
# slave_id = 1 # Both devices use the default ID=1
# await setup_touch_sensor(client_left_hand, slave_id) # If it is the tactile version
# await setup_touch_sensor(client_right_hand, slave_id) # If it is the tactile version
# asyncio.create_task(get_motor_status_periodically(client_left_hand, slave_id))
# asyncio.create_task(get_motor_status_periodically(client_right_hand, slave_id))
# Wait for the close event
await shutdown_event.wait()
# Clean up resources
libstark.modbus_close(client)
logger.info("Modbus client closed")
sys.exit(0)
async def setup_touch_sensor(client, slave_id):
"""
Configure the tactile sensor
Configure accordingly based on the device type (Tactile Edition/Basic Edition).
The tactile version of the device will enable the tactile sensor function, while the basic version will skip this step.
Args:
client: Modbus Client instance
slave_id: Device ID
"""
# Verify device type
device_info = await client.get_device_info(slave_id)
logger.info(f"Device {slave_id} info: {device_info.description}")
is_revo1_touch = device_info.is_revo1_touch()
slave_is_touch[slave_id] = is_revo1_touch
if not is_revo1_touch:
logger.warning(f"Device {slave_id} is not Revo1 Touch hardware, skipping touch sensor setup")
return
# Enable the tactile sensor function (default disabled on startup)
bits = 0x1F # 0x1f: Enable all tactile sensors on 5 fingers
await client.touch_sensor_setup(slave_id, bits)
await asyncio.sleep(1) # Wait for the tactile sensor to be ready
# Verify sensor enable status
bits = await client.get_touch_sensor_enabled(slave_id)
logger.info(f"Device {slave_id} Touch Sensor Enabled: {(bits & 0x1F):05b}")
# Get the firmware version of the tactile sensor (can only be obtained after setup)
touch_fw_versions = await client.get_touch_sensor_fw_versions(slave_id)
logger.info(f"Device {slave_id} Touch Fw Versions: {touch_fw_versions}")
async def get_touch_status(client, slave_id):
"""
Get tactile sensor status and data
Tactile Sensor Data Description:
- Normal force: The force (pressure) perpendicular to the contact surface
- Tangential force: A force acting in the tangential direction of the contact surface (such as frictional force, inertial force, etc.)
- Self-approach: Self-capacitance change value, reflecting the degree of proximity of an object
- Mutual Proximity: Mutual Capacitance Variation, Reflecting the Degree of Object Proximity
- Sensor status: 0=Normal, 1=Data anomaly, 2=Communication anomaly
Args:
client: Modbus Client instance
slave_id: Device ID
"""
# Get the tactile sensor status of all fingers
touch_status: list[libstark.TouchFingerItem] = await client.get_touch_sensor_status(slave_id)
thumb = touch_status[0] # Thumb
index = touch_status[1] # Index finger
middle = touch_status[2] # Middle finger
ring = touch_status[3] # Ring finger
pinky = touch_status[4] # Little finger
# Record detailed data of the index finger sensor
logger.debug(f"Device {slave_id} Index Sensor Desc: {index.description}")
logger.info(f"Device {slave_id} Index Sensor status: {index.status}")
logger.info(f"Device {slave_id} Index normal_force1: {index.normal_force1}") # Normal force 1 logger.info(f"Device {slave_id} Index normal_force2: {index.normal_force2}") # Normal Force 2 logger.info(f"Device {slave_id} Index normal_force3: {index.normal_force3}") # Normal force 3
logger.info(f"Device {slave_id} Index tangential_force1: {index.tangential_force1}") # Tangential Force 1
logger.info(f"Device {slave_id} Index tangential_force2: {index.tangential_force2}") # Tangential Force 2
logger.info(f"Device {slave_id} Index tangential_force3: {index.tangential_force3}") # Tangential force 3
logger.info(f"Device {slave_id} Index tangential_direction1: {index.tangential_direction1}") # Tangential Force Direction 1
logger.info(f"Device {slave_id} Index tangential_direction2: {index.tangential_direction2}") # Tangential Force Direction 2
logger.info(f"Device {slave_id} Index tangential_direction3: {index.tangential_direction3}") # Tangential force direction 3
logger.info(f"Device {slave_id} Index self_proximity1: {index.self_proximity1}") # Since approaching 1
logger.info(f"Device {slave_id} Index self_proximity2: {index.self_proximity2}") # Since approaching 2
logger.info(f"Device {slave_id} Index mutual_proximity: {index.mutual_proximity}") # Mutual approach
# Get the tactile sensor status of a single finger (another way to obtain it)
thumb = await client.get_single_touch_sensor_status(slave_id, 0)
index = await client.get_single_touch_sensor_status(slave_id, 1)
middle = await client.get_single_touch_sensor_status(slave_id, 2)
ring = await client.get_single_touch_sensor_status(slave_id, 3)
pinky = await client.get_single_touch_sensor_status(slave_id, 4)
# Record thumb sensor data
logger.debug(f"Device {slave_id} Thumb Sensor Desc: {thumb.description}")
logger.info(f"Device {slave_id} Thumb Sensor status: {thumb.status}")
logger.info(f"Device {slave_id} Thumb normal_force1: {thumb.normal_force1}") # Normal force 1
logger.info(f"Device {slave_id} Thumb normal_force2: {thumb.normal_force2}") # Normal force 2
logger.info(f"Device {slave_id} Thumb tangential_force1: {thumb.tangential_force1}") # Tangential force 1
logger.info(f"Device {slave_id} Thumb tangential_force2: {thumb.tangential_force2}") # Tangential Force 2
logger.info(f"Device {slave_id} Thumb tangential_direction1: {thumb.tangential_direction1}") # Tangential Force Direction 1
logger.info(f"Device {slave_id} Thumb tangential_direction2: {thumb.tangential_direction2}") # Tangential Force Direction 2
logger.info(f"Device {slave_id} Thumb self_proximity1: {thumb.self_proximity1}") # Since approaching 1
# Get sensor raw channel values (optional, for debugging and advanced analysis)
# touch_raw_data = await client.get_touch_sensor_raw_data(slave_id)
# logger.info(f"Device {slave_id} Touch Sensor Raw Data: {touch_raw_data.description}")
# logger.debug(f"Device {slave_id} Touch Sensor Raw Data Thumb: {touch_raw_data.thumb}")
# logger.debug(f"Device {slave_id} Touch Sensor Raw Data Index: {touch_raw_data.index}")
# logger.debug(f"Device {slave_id} Touch Sensor Raw Data Middle: {touch_raw_data.middle}")
# logger.debug(f"Device {slave_id} Touch Sensor Raw Data Ring: {touch_raw_data.ring}")
# logger.debug(f"Device {slave_id} Touch Sensor Raw Data Pinky: {touch_raw_data.pinky}")
async def get_motor_status_periodically(client, slave_id):
"""
Regularly obtain the motor status and perform automatic control
This function will continuously monitor the motor status and perform simple opening and closing actions based on the finger position:
- When the fingers are spread, perform a handshake action
- When the fingers close, perform the opening action
- For tactile version devices, acquire tactile sensor data simultaneously
Args:
client: Modbus Client instance
slave_id: Device ID
"""
logger.info(f"Motor status monitoring started for device {slave_id}")
index = 0
while True:
try:
# Check if it is the tactile version, and if so, get the status of the tactile sensor
if slave_is_touch.get(slave_id, False):
logger.debug(f"Getting touch sensor status for device {slave_id}...")
await get_touch_status(client, slave_id)
# Get motor status
logger.debug(f"Getting motor status for device {slave_id}...")
start = time.perf_counter()
status: libstark.MotorStatusData = await client.get_motor_status(slave_id)
cost_ms = (time.perf_counter() - start) * 1000
# Record status information
logger.info(
f"Device {slave_id} [{index}] Motor status - cost: {cost_ms:.2f}ms, "
f"is_idle: {status.is_idle()}, "
f"is_closed: {status.is_closed()}, "
f"is_opened: {status.is_opened()}"
)
logger.info(f"Device {slave_id} [{index}] Motor status: {status.description}")
index += 1
# Execute automatic control based on the current status
if status.is_idle():
if status.is_opened():
# When fingers are spread, perform a handshake action
# Position values: [Thumb, Thumb assist, Index finger, Middle finger, Ring finger, Little finger]
await client.set_finger_positions(
slave_id, [60, 60, 100, 100, 100, 100]
)
logger.debug(f"Device {slave_id} executing grip action")
elif status.is_closed():
# When the fingers close, perform an opening action
await client.set_finger_positions(slave_id, [0] * 6)
logger.debug(f"Device {slave_id} executing open action")
# Add a delay to avoid overly frequent queries
await asyncio.sleep(0.001)
except Exception as e:
logger.error(f"Error in motor status monitoring for device {slave_id}: {e}")
await asyncio.sleep(1) # Wait longer before retrying when an error occurs
if __name__ == "__main__":
asyncio.run(main())
Obtain tactile sensor status and data
Acquiring tactile information
"""
Revo1 Tactile Version Dextrous Hand Control Example
This example demonstrates how to control the Revo1 Tactile Edition Dextrous Hand device, including:
- Configuration and Enablement of Tactile Sensors
- Acquisition and processing of tactile sensor data
- Motor status monitoring and automatic control
- Finger Position Control and Current Control
"""
import asyncio
import sys
import time
from revo1_utils import *
from utils import setup_shutdown_event
async def main():
"""
Main function: Initialize tactile version dexterous hand connection and control tasks
"""
# Set up a close event listener
shutdown_event = setup_shutdown_event(logger)
# Detect the baud rate and device ID of the dexterous hand, and initialize the client
(client, slave_id) = await open_modbus_revo1()
# Verify that the device type is the tactile version
device_info = await client.get_device_info(slave_id)
logger.info(f"Device info: {device_info.description}")
if not device_info.is_revo1_touch():
logger.error("This example is only for Revo1 Touch hardware")
sys.exit(1)
# Configure the tactile sensor
await setup_touch_sensor(client, slave_id)
# Start the motor status monitoring task
asyncio.create_task(get_motor_status_periodically(client, slave_id))
logger.info("Status task started")
# Wait for the close event
await shutdown_event.wait()
# Close the resource
libstark.modbus_close(client)
logger.info("Modbus client closed")
sys.exit(0)
async def setup_touch_sensor(client, slave_id):
"""
Configure and enable the tactile sensor
Args:
client: Modbus Client instance
slave_id: Device ID
"""
# Enable the tactile sensor function (default disabled on startup)
bits = 0x1F # 0x1f: Enable all tactile sensors on 5 fingers
await client.touch_sensor_setup(slave_id, bits)
await asyncio.sleep(1) # Wait for the tactile sensor to be ready
# Verify sensor enable status
bits = await client.get_touch_sensor_enabled(slave_id)
logger.info(f"Touch Sensor Enabled: {(bits & 0x1F):05b}")
# Get the firmware version of the tactile sensor (can only be obtained after setup)
touch_fw_versions = await client.get_touch_sensor_fw_versions(slave_id)
logger.info(f"Touch Fw Versions: {touch_fw_versions}")
# Tactile Sensor Maintenance Command (Optional)
# Reset: Send the sensor acquisition channel reset command. When executing, try to avoid applying force to the finger sensor.
# await client.touch_sensor_reset(slave_id, 0x1f)
# Calibration: Used when the three-dimensional force value in idle state is not zero, and the execution time is long.
# It is recommended to ignore data within ten seconds after calibration. When in operation, the finger sensor should not be subjected to force.
# await client.touch_sensor_calibrate(slave_id, 0x1f)
async def get_touch_status(client, slave_id):
"""
Get the status and data of the tactile sensor
Tactile Sensor Data Description:
- Normal Force: The force perpendicular to the contact surface (pressure)
- Tangential force: A force acting along the tangent direction of the contact surface (such as frictional force, inertial force, etc.)
- Self-approach: Self-capacitance change value
- Mutual Proximity: Mutual Capacitance Change Value
- Sensor status: 0=Normal, 1=Data anomaly, 2=Communication anomaly
Args:
client: Modbus Client instance
slave_id: Device ID
"""
# Get the tactile sensor status of all fingers
touch_status: list[libstark.TouchFingerItem] = await client.get_touch_sensor_status(slave_id)
thumb = touch_status[0] # Thumb
index = touch_status[1] # Index finger
middle = touch_status[2] # Middle finger
ring = touch_status[3] # Ring finger
pinky = touch_status[4] # Little finger
# Record detailed data of the index finger sensor
logger.debug(f"Index Sensor Desc: {index.description}")
logger.info(f"Index Sensor status: {index.status}") # Tactile sensor status
logger.info(f"Index normal_force1: {index.normal_force1}") # Normal force 1
logger.info(f"Index normal_force2: {index.normal_force2}") # Normal Force 2
logger.info(f"Index normal_force3: {index.normal_force3}") # Normal force 3
logger.info(f"Index tangential_force1: {index.tangential_force1}") # Tangential force 1
logger.info(f"Index tangential_force2: {index.tangential_force2}") # Tangential Force 2
logger.info(f"Index tangential_force3: {index.tangential_force3}") # Tangential force 3
logger.info(f"Index tangential_direction1: {index.tangential_direction1}") # Tangential force direction 1 logger.info(f"Index tangential_direction2: {index.tangential_direction2}") # Tangential Force Direction 2
logger.info(f"Index tangential_direction3: {index.tangential_direction3}") # Tangential force direction 3
logger.info(f"Index self_proximity1: {index.self_proximity1}") # Since approaching 1
logger.info(f"Index self_proximity2: {index.self_proximity2}") # Since approaching 2
logger.info(f"Index mutual_proximity: {index.mutual_proximity}") # Mutual proximity
# Get the tactile sensor status of a single finger (another way to obtain it)
thumb = await client.get_single_touch_sensor_status(slave_id, 0)
index = await client.get_single_touch_sensor_status(slave_id, 1)
middle = await client.get_single_touch_sensor_status(slave_id, 2)
ring = await client.get_single_touch_sensor_status(slave_id, 3)
pinky = await client.get_single_touch_sensor_status(slave_id, 4)
# Record thumb sensor data
logger.debug(f"Thumb Sensor Desc: {thumb.description}")
logger.info(f"Thumb Sensor status: {thumb.status}")
logger.info(f"Thumb normal_force1: {thumb.normal_force1}") # Normal Force 1
logger.info(f"Thumb normal_force2: {thumb.normal_force2}") # Normal Force 2
logger.info(f"Thumb tangential_force1: {thumb.tangential_force1}") # Tangential Force 1
logger.info(f"Thumb tangential_force2: {thumb.tangential_force2}") # Tangential force 2
logger.info(f"Thumb tangential_direction1: {thumb.tangential_direction1}") # Tangential Force Direction 1
logger.info(f"Thumb tangential_direction2: {thumb.tangential_direction2}") # Tangential Force Direction 2 logger.info(f"Thumb self_proximity1: {thumb.self_proximity1}") # As it approaches 1
# Get sensor raw channel values (optional, for debugging)
# touch_raw_data = await client.get_touch_sensor_raw_data(slave_id)
# logger.info(f"Touch Sensor Raw Data: {touch_raw_data.description}")
# logger.debug(f"Touch Sensor Raw Data Thumb: {touch_raw_data.thumb}")
# logger.debug(f"Touch Sensor Raw Data Index: {touch_raw_data.index}")
# logger.debug(f"Touch Sensor Raw Data Middle: {touch_raw_data.middle}")
# logger.debug(f"Touch Sensor Raw Data Ring: {touch_raw_data.ring}")
# logger.debug(f"Touch Sensor Raw Data Pinky: {touch_raw_data.pinky}")
async def get_motor_status_periodically(client, slave_id):
"""
Regularly obtain the motor status and perform automatic control
This function will continuously monitor the motor status and perform simple opening and closing actions based on the finger position:
- When the fingers are spread, perform a handshake action
- When the fingers close, perform the opening action
Args:
client: Modbus Client instance
slave_id: Device ID
"""
logger.info("Motor status monitoring started")
index = 0
while True:
try:
# Get the status of the tactile sensor
logger.debug("Getting touch sensor status...")
await get_touch_status(client, slave_id)
# Get motor status
logger.debug("Getting motor status...")
start = time.perf_counter()
status: libstark.MotorStatusData = await client.get_motor_status(slave_id)
cost_ms = (time.perf_counter() - start) * 1000
# Record status information
logger.info(
f"[{index}] Motor status - cost: {cost_ms:.2f}ms, "
f"is_idle: {status.is_idle()}, "
f"is_closed: {status.is_closed()}, "
f"is_opened: {status.is_opened()}"
)
logger.info(f"[{index}] Motor status: {status.description}")
index += 1
# Perform actions based on the current status
if status.is_idle():
if status.is_opened():
# When fingers are spread, perform a handshake action
await client.set_finger_positions(
slave_id, [60, 60, 100, 100, 100, 100]
)
elif status.is_closed():
# When the fingers close, perform an opening action
await client.set_finger_positions(slave_id, [0] * 6)
# Add a delay to avoid overly frequent queries
await asyncio.sleep(0.001)
except Exception as e:
logger.error(f"Error in motor status monitoring: {e}")
await asyncio.sleep(1) # Wait longer when an error occurs
async def set_finger_current(client, slave_id: int):
"""
Set finger current control
Current control parameter range:-100~-20, 20~100
Positive numbers indicate the closing direction, and negative numbers indicate the opening direction.
Args:
client: Modbus Client instance
slave_id: Device ID
"""
# Select the finger to control
# finger_id = libstark.FingerId.Thumb # Thumb
# finger_id = libstark.FingerId.ThumbAux # Thumb assist
# finger_id = libstark.FingerId.Index # Index finger
# finger_id = libstark.FingerId.Middle # Middle finger
finger_id = libstark.FingerId.Ring # Ring finger
# finger_id = libstark.FingerId.Pinky # Little finger
# Set single finger current
await client.set_finger_current(slave_id, finger_id, -20)
# Or set all finger currents
# await client.set_finger_currents(slave_id, [-100] * 6)
if __name__ == "__main__":
asyncio.run(main())
"""
Revo1 Tactile Version Dextrous Hand Control Example
This example demonstrates how to control the Revo1 Tactile Edition Dextrous Hand device, including:
- Configuration and Enablement of Tactile Sensors
- Acquisition and processing of tactile sensor data
- Motor status monitoring and automatic control
- Finger Position Control and Current Control
"""
import asyncio
import sys
import time
from revo1_utils import *
from utils import setup_shutdown_event
async def main():
"""
Main function: Initialize tactile version dexterous hand connection and control tasks
"""
# Set up a close event listener
shutdown_event = setup_shutdown_event(logger)
# Detect the baud rate and device ID of the dexterous hand, and initialize the client
(client, slave_id) = await open_modbus_revo1()
# Verify that the device type is the tactile version
device_info = await client.get_device_info(slave_id)
logger.info(f"Device info: {device_info.description}")
if not device_info.is_revo1_touch():
logger.error("This example is only for Revo1 Touch hardware")
sys.exit(1)
# Configure the tactile sensor
await setup_touch_sensor(client, slave_id)
# Start the motor status monitoring task
asyncio.create_task(get_motor_status_periodically(client, slave_id))
logger.info("Status task started")
# Wait for the close event
await shutdown_event.wait()
# Close the resource
libstark.modbus_close(client)
logger.info("Modbus client closed")
sys.exit(0)
async def setup_touch_sensor(client, slave_id):
"""
Configure and enable the tactile sensor
Args:
client: Modbus Client instance
slave_id: Device ID
"""
# Enable the tactile sensor function (default disabled on startup)
bits = 0x1F # 0x1f: Enable all tactile sensors on 5 fingers
await client.touch_sensor_setup(slave_id, bits)
await asyncio.sleep(1) # Wait for the tactile sensor to be ready
# Verify sensor enable status
bits = await client.get_touch_sensor_enabled(slave_id)
logger.info(f"Touch Sensor Enabled: {(bits & 0x1F):05b}")
# Get the firmware version of the tactile sensor (can only be obtained after setup)
touch_fw_versions = await client.get_touch_sensor_fw_versions(slave_id)
logger.info(f"Touch Fw Versions: {touch_fw_versions}")
# Tactile Sensor Maintenance Command (Optional)
# Reset: Send the sensor acquisition channel reset command. When executing, try to avoid applying force to the finger sensor.
# await client.touch_sensor_reset(slave_id, 0x1f)
# Calibration: Used when the three-dimensional force value in idle state is not zero, and the execution time is long.
# It is recommended to ignore data within ten seconds after calibration. When in operation, the finger sensor should not be subjected to force.
# await client.touch_sensor_calibrate(slave_id, 0x1f)
async def get_touch_status(client, slave_id):
"""
Get the status and data of the tactile sensor
Tactile Sensor Data Description:
- Normal Force: The force perpendicular to the contact surface (pressure)
- Tangential force: A force acting along the tangent direction of the contact surface (such as frictional force, inertial force, etc.)
- Self-approach: Self-capacitance change value
- Mutual Proximity: Mutual Capacitance Change Value
- Sensor status: 0=Normal, 1=Data anomaly, 2=Communication anomaly
Args:
client: Modbus Client instance
slave_id: Device ID
"""
# Get the tactile sensor status of all fingers
touch_status: list[libstark.TouchFingerItem] = await client.get_touch_sensor_status(slave_id)
thumb = touch_status[0] # Thumb
index = touch_status[1] # Index finger
middle = touch_status[2] # Middle finger
ring = touch_status[3] # Ring finger
pinky = touch_status[4] # Little finger
# Record detailed data of the index finger sensor
logger.debug(f"Index Sensor Desc: {index.description}")
logger.info(f"Index Sensor status: {index.status}") # Tactile sensor status
logger.info(f"Index normal_force1: {index.normal_force1}") # Normal force 1
logger.info(f"Index normal_force2: {index.normal_force2}") # Normal Force 2
logger.info(f"Index normal_force3: {index.normal_force3}") # Normal force 3
logger.info(f"Index tangential_force1: {index.tangential_force1}") # Tangential force 1
logger.info(f"Index tangential_force2: {index.tangential_force2}") # Tangential Force 2
logger.info(f"Index tangential_force3: {index.tangential_force3}") # Tangential force 3
logger.info(f"Index tangential_direction1: {index.tangential_direction1}") # Tangential force direction 1 logger.info(f"Index tangential_direction2: {index.tangential_direction2}") # Tangential Force Direction 2
logger.info(f"Index tangential_direction3: {index.tangential_direction3}") # Tangential force direction 3
logger.info(f"Index self_proximity1: {index.self_proximity1}") # Since approaching 1
logger.info(f"Index self_proximity2: {index.self_proximity2}") # Since approaching 2
logger.info(f"Index mutual_proximity: {index.mutual_proximity}") # Mutual proximity
# Get the tactile sensor status of a single finger (another way to obtain it)
thumb = await client.get_single_touch_sensor_status(slave_id, 0)
index = await client.get_single_touch_sensor_status(slave_id, 1)
middle = await client.get_single_touch_sensor_status(slave_id, 2)
ring = await client.get_single_touch_sensor_status(slave_id, 3)
pinky = await client.get_single_touch_sensor_status(slave_id, 4)
# Record thumb sensor data
logger.debug(f"Thumb Sensor Desc: {thumb.description}")
logger.info(f"Thumb Sensor status: {thumb.status}")
logger.info(f"Thumb normal_force1: {thumb.normal_force1}") # Normal Force 1
logger.info(f"Thumb normal_force2: {thumb.normal_force2}") # Normal Force 2
logger.info(f"Thumb tangential_force1: {thumb.tangential_force1}") # Tangential Force 1
logger.info(f"Thumb tangential_force2: {thumb.tangential_force2}") # Tangential force 2
logger.info(f"Thumb tangential_direction1: {thumb.tangential_direction1}") # Tangential Force Direction 1
logger.info(f"Thumb tangential_direction2: {thumb.tangential_direction2}") # Tangential Force Direction 2 logger.info(f"Thumb self_proximity1: {thumb.self_proximity1}") # As it approaches 1
# Get sensor raw channel values (optional, for debugging)
# touch_raw_data = await client.get_touch_sensor_raw_data(slave_id)
# logger.info(f"Touch Sensor Raw Data: {touch_raw_data.description}")
# logger.debug(f"Touch Sensor Raw Data Thumb: {touch_raw_data.thumb}")
# logger.debug(f"Touch Sensor Raw Data Index: {touch_raw_data.index}")
# logger.debug(f"Touch Sensor Raw Data Middle: {touch_raw_data.middle}")
# logger.debug(f"Touch Sensor Raw Data Ring: {touch_raw_data.ring}")
# logger.debug(f"Touch Sensor Raw Data Pinky: {touch_raw_data.pinky}")
async def get_motor_status_periodically(client, slave_id):
"""
Regularly obtain the motor status and perform automatic control
This function will continuously monitor the motor status and perform simple opening and closing actions based on the finger position:
- When the fingers are spread, perform a handshake action
- When the fingers close, perform the opening action
Args:
client: Modbus Client instance
slave_id: Device ID
"""
logger.info("Motor status monitoring started")
index = 0
while True:
try:
# Get the status of the tactile sensor
logger.debug("Getting touch sensor status...")
await get_touch_status(client, slave_id)
# Get motor status
logger.debug("Getting motor status...")
start = time.perf_counter()
status: libstark.MotorStatusData = await client.get_motor_status(slave_id)
cost_ms = (time.perf_counter() - start) * 1000
# Record status information
logger.info(
f"[{index}] Motor status - cost: {cost_ms:.2f}ms, "
f"is_idle: {status.is_idle()}, "
f"is_closed: {status.is_closed()}, "
f"is_opened: {status.is_opened()}"
)
logger.info(f"[{index}] Motor status: {status.description}")
index += 1
# Perform actions based on the current status
if status.is_idle():
if status.is_opened():
# When fingers are spread, perform a handshake action
await client.set_finger_positions(
slave_id, [60, 60, 100, 100, 100, 100]
)
elif status.is_closed():
# When the fingers close, perform an opening action
await client.set_finger_positions(slave_id, [0] * 6)
# Add a delay to avoid overly frequent queries
await asyncio.sleep(0.001)
except Exception as e:
logger.error(f"Error in motor status monitoring: {e}")
await asyncio.sleep(1) # Wait longer when an error occurs
async def set_finger_current(client, slave_id: int):
"""
Set finger current control
Current control parameter range:-100~-20, 20~100
Positive numbers indicate the closing direction, and negative numbers indicate the opening direction.
Args:
client: Modbus Client instance
slave_id: Device ID
"""
# Select the finger to control
# finger_id = libstark.FingerId.Thumb # Thumb
# finger_id = libstark.FingerId.ThumbAux # Thumb assist
# finger_id = libstark.FingerId.Index # Index finger
# finger_id = libstark.FingerId.Middle # Middle finger
finger_id = libstark.FingerId.Ring # Ring finger
# finger_id = libstark.FingerId.Pinky # Little finger
# Set single finger current
await client.set_finger_current(slave_id, finger_id, -20)
# Or set all finger currents
# await client.set_finger_currents(slave_id, [-100] * 6)
if __name__ == "__main__":
asyncio.run(main())
Get tactile sensor status and data
async def get_touch_status(client, slave_id):
"""
Get the status and data of the tactile sensor
Tactile Sensor Data Description:
- Normal Force: The force perpendicular to the contact surface (pressure)
- Tangential force: A force acting in the tangential direction of the contact surface (such as frictional force, inertial force, etc.)
- Self-approach: Self-capacitance change value
- Mutual Proximity: Mutual Capacitance Change Value
- Sensor status: 0=Normal, 1=Data anomaly, 2=Communication anomaly
Args:
client: Modbus Client instance
slave_id: Device ID
"""
# Get the status of tactile sensors on all fingers
touch_status = await client.get_touch_sensor_status(slave_id)
thumb = touch_status[0] # Thumb
index = touch_status[1] # Index finger
middle = touch_status[2] # Middle finger
ring = touch_status[3] # Ring finger
pinky = touch_status[4] # Little finger
# Record detailed data of the index finger sensor
logger.debug(f"Index Sensor Desc: {index.description}")
logger.info(f"Index Sensor status: {index.status}") # Tactile sensor status
logger.info(f"Index normal_force1: {index.normal_force1}") # Normal Force 1
logger.info(f"Index normal_force2: {index.normal_force2}") # Normal Force 2
logger.info(f"Index normal_force3: {index.normal_force3}") # Normal force 3
logger.info(f"Index tangential_force1: {index.tangential_force1}") # Tangential Force 1
logger.info(f"Index tangential_force2: {index.tangential_force2}") # Tangential force 2
logger.info(f"Index tangential_force3: {index.tangential_force3}") # Tangential force 3
logger.info(f"Index tangential_direction1: {index.tangential_direction1}") # Tangential Force Direction 1
logger.info(f"Index tangential_direction2: {index.tangential_direction2}") # Tangential Force Direction 2
logger.info(f"Index tangential_direction3: {index.tangential_direction3}") # Tangential force direction 3
logger.info(f"Index self_proximity1: {index.self_proximity1}") # Since approaching 1
logger.info(f"Index self_proximity2: {index.self_proximity2}") # Since approaching 2
logger.info(f"Index mutual_proximity: {index.mutual_proximity}") # Mutual approach
# Get the tactile sensor status of a single finger (another way to obtain it)
thumb = await client.get_single_touch_sensor_status(slave_id, 0)
index = await client.get_single_touch_sensor_status(slave_id, 1)
middle = await client.get_single_touch_sensor_status(slave_id, 2)
ring = await client.get_single_touch_sensor_status(slave_id, 3)
pinky = await client.get_single_touch_sensor_status(slave_id, 4)
# Record thumb sensor data
logger.debug(f"Thumb Sensor Desc: {thumb.description}")
logger.info(f"Thumb Sensor status: {thumb.status}")
logger.info(f"Thumb normal_force1: {thumb.normal_force1}") # Normal force 1
logger.info(f"Thumb normal_force2: {thumb.normal_force2}") # Normal Force 2
logger.info(f"Thumb tangential_force1: {thumb.tangential_force1}") # Tangential force 1
logger.info(f"Thumb tangential_force2: {thumb.tangential_force2}") # Tangential Force 2
logger.info(f"Thumb tangential_direction1: {thumb.tangential_direction1}") # Tangential Force Direction 1
logger.info(f"Thumb tangential_direction2: {thumb.tangential_direction2}") # Tangential Force Direction 2
logger.info(f"Thumb self_proximity1: {thumb.self_proximity1}") # Since approaching 1
# Get sensor raw channel values (optional, for debugging)
# touch_raw_data = await client.get_touch_sensor_raw_data(slave_id)
# logger.info(f"Touch Sensor Raw Data: {touch_raw_data.description}")
# logger.debug(f"Touch Sensor Raw Data Thumb: {touch_raw_data.thumb}")
# logger.debug(f"Touch Sensor Raw Data Index: {touch_raw_data.index}")
# logger.debug(f"Touch Sensor Raw Data Middle: {touch_raw_data.middle}")
# logger.debug(f"Touch Sensor Raw Data Ring: {touch_raw_data.ring}")
# logger.debug(f"Touch Sensor Raw Data Pinky: {touch_raw_data.pinky}")
Set device ID
# Set up the device ID
# Parameter Description:
# client: client Object,Device Connection handle
# slave_id: Device ID,The scope is 1~247, 0 As a broadcast address,The first generation assumes 1
# Set the device asynchronously ID
await client.set_slave_id(slave_id, new_slave_id) # After setting it up, the device will automatically restart (soft reboot)
Read/Set Serial Port Baud Rate
# Read/Set Serial Port Baud Rate
# Parameter Description:
# client: client Object,Device Connection handle
# baudrate, Baud rate,115200, 57600, 19200, 460800,First-generation handsets are considered 115200
# Read/Configure Serial Port Baud Rate in Asynchronous Mode
baudrate = await client.get_serialport_baudrate(slave_id)
logger.info(f"Baudrate: {baudrate}")
await client.set_serialport_baudrate(slave_id, new_baudrate)
logger.info(f"write success, wait 3s for device reboot")
await asyncio.sleep(3.0) # Wait for the device to restart
Enable/disable Turbo mode
# Set Turbo mode on/off
# Parameter Description:
# client: client Object,Device Connection handle
# slave_id: Device ID
# enabled: Whether to enable Turbo mode,True means enabled,False means disabled
# Return value: None
# Set Turbo mode asynchronously
await client.set_turbo_mode_enabled(slave_id, True) # Enable Turbo mode
await client.set_turbo_mode_enabled(slave_id, False) # Turn off Turbo mode
turbo_mode = await client.get_turbo_mode_enabled(slave_id) # Check if Turbo mode is enabled
# Print Turbo mode status
if turbo_mode:
logger.info("Turbo mode: enabled")
else:
logger.info("Turbo mode: disabled")
Set Turbo mode parameters
# Set Turbo mode parameters
# Parameter Description:
# client: client object,Device connection handle
# slave_id: Device ID
# turbo_interval: Turbo mode interval time, unit in milliseconds
# turbo_duration: Turbo mode duration, in milliseconds
# Return value: None
# Set Turbo mode parameters asynchronously
await client.set_turbo_config(slave_id, libstark.TurboConfig(turbo_interval, turbo_duration))
Sequence of actions
"""
Revo1 Dextrous Hand Motion Sequence Control Example
This example demonstrates how to use the motion sequence function of Revo1 Dextrous Hand, including:
- Creation and configuration of custom action sequences
- Upload and storage of action sequences
- Execution of built-in action sequences
- Execution and management of custom action sequences
- Reading and validation of action sequence data
"""
import asyncio
import sys
from utils import setup_shutdown_event
from revo1_utils import *
# A generation of dexterous hand motion sequence configuration
# Note:Only the positions parameter is actually effective in the current version,The speeds and forces parameters are reserved fields
sample_action_sequences = [
{
"index": 0, # Action Sequence Index
"duration_ms": 2000, # Action Duration (milliseconds)
"positions": [0, 0, 100, 100, 100, 100], # Finger position: [thumb, thumb assist, index finger, middle finger, ring finger, pinky finger]
"speeds": [10, 20, 30, 40, 50, 60], # Finger speed (reserved parameter)
"forces": [5, 10, 15, 20, 25, 30], # Finger Pressure (Reserved Parameter)
},
{
"index": 1,
"duration_ms": 2000,
"positions": [100, 100, 0, 0, 0, 0], # Close the thumb and spread the other fingers
"speeds": [15, 25, 35, 45, 55, 65],
"forces": [6, 11, 16, 21, 26, 31],
},
{
"index": 2,
"duration_ms": 2000,
"positions": [0, 0, 100, 100, 100, 100], # Spread the thumb and close the other fingers
"speeds": [10, 20, 30, 40, 50, 60],
"forces": [5, 10, 15, 20, 25, 30],
},
{
"index": 3,
"duration_ms": 2000,
"positions": [100, 100, 0, 0, 0, 0],
"speeds": [15, 25, 35, 45, 55, 65],
"forces": [6, 11, 16, 21, 26, 31],
},
{
"index": 4,
"duration_ms": 2000,
"positions": [0, 0, 0, 0, 0, 0], # All fingers fully spread
"speeds": [15, 25, 35, 45, 55, 65],
"forces": [6, 11, 16, 21, 26, 31],
},
{
"index": 5,
"duration_ms": 2000,
"positions": [0, 0, 0, 0, 0, 0], # Keep in an open state
"speeds": [15, 25, 35, 45, 55, 65],
"forces": [6, 11, 16, 21, 26, 31],
},
{
"index": 6,
"duration_ms": 2000,
"positions": [0, 0, 0, 0, 0, 0],
"speeds": [15, 25, 35, 45, 55, 65],
"forces": [6, 11, 16, 21, 26, 31],
},
{
"index": 7,
"duration_ms": 2000,
"positions": [0, 0, 0, 0, 0, 0],
"speeds": [15, 25, 35, 45, 55, 65],
"forces": [6, 11, 16, 21, 26, 31],
},
{
"index": 8,
"duration_ms": 2000,
"positions": [0, 0, 0, 0, 0, 0],
"speeds": [15, 25, 35, 45, 55, 65],
"forces": [6, 11, 16, 21, 26, 31],
},
{
"index": 9,
"duration_ms": 2000,
"positions": [0, 0, 0, 0, 0, 0],
"speeds": [15, 25, 35, 45, 55, 65],
"forces": [6, 11, 16, 21, 26, 31],
},
]
def action_sequence_info_to_list(action):
"""
Convert the action sequence dictionary to list format
Convert the configuration information of the action sequence into a list format required by the SDK for uploading to the device.
Args:
action (dict): Action Sequence Configuration Dictionary,contains index、duration_ms、positions、speeds、forces
Returns:
list: Converted list format [index, duration_ms, positions..., speeds..., forces...]
"""
return (
[action["index"]]
+ [action["duration_ms"]]
+ action["positions"]
+ action["speeds"]
+ action["forces"]
)
async def main():
"""
Main function: Initialize the dexterous hand and execute the action sequence control
"""
# Set up a close event listener
shutdown_event = setup_shutdown_event(logger)
# Detect the baud rate and device ID of the dexterous hand, and initialize the client
(client, slave_id) = await open_modbus_revo1(port_name=None) # Replace with the actual serial port name. Passing None will attempt automatic detection.
# Convert action sequence format
# sample_action_sequences = random_action_sequences() # Optional: Use a random action sequence
mapped_sequences = map(lambda action: action_sequence_info_to_list(action), sample_action_sequences)
action_sequences = list(mapped_sequences)
# Upload custom action sequences to the device
# The device supports a maximum of 6 custom action sequences(CustomGesture1 ~ CustomGesture6)
custom_action_id = libstark.ActionSequenceId.CustomGesture1
await client.transfer_action_sequence(slave_id, custom_action_id, action_sequences)
logger.info(f"Custom action sequence uploaded to {custom_action_id}")
# Read and verify the uploaded action sequence
action_result: libstark.ActionSequenceItem = await client.get_action_sequence(slave_id, custom_action_id)
logger.info(f"Action sequence: {action_result.description}")
# Execute the sequence of built-in actions: Fist action
logger.info("Executing built-in fist gesture...")
await client.run_action_sequence(slave_id, libstark.ActionSequenceId.DefaultGestureFist)
await asyncio.sleep(3) # Wait for the built-in action sequence to complete execution
# Execute a custom action sequence
logger.info("Executing custom action sequence...")
await client.run_action_sequence(slave_id, custom_action_id)
# Wait for the close event(Ctrl+C or other closing signals)
await shutdown_event.wait()
# Close the resource
libstark.modbus_close(client)
logger.info("Modbus client closed")
sys.exit(0)
if __name__ == "__main__":
asyncio.run(main())
Action Sequence - Gesture
Action sequence
# Detect the baud rate and device ID of the dexterous hand,Initialize client
(client, slave_id) = await open_modbus_revo1()
# Upload custom action sequence to device
# The device supports a maximum of 6 custom action sequences(CustomGesture1 ~ CustomGesture6)
custom_action_id = libstark.ActionSequenceId.CustomGesture1
await client.transfer_action_sequence(slave_id, custom_action_id, action_sequences)
logger.info(f"Custom action sequence uploaded to {custom_action_id}")
# Read and verify the uploaded action sequence
action_result = await client.get_action_sequence(slave_id, custom_action_id)
logger.info(f"Action sequence: {action_result.description}")
# Execute the built-in action sequence: Fist action
logger.info("Executing built-in fist gesture...")
await client.run_action_sequence(slave_id, libstark.ActionSequenceId.DefaultGestureFist)
await asyncio.sleep(3) # Wait for the built-in action sequence to complete
# Execute custom action sequence
logger.info("Executing custom action sequence...")
await client.run_action_sequence(slave_id, custom_action_id)
Firmware OTA
# Establish Modbus connection
client = await libstark.modbus_open(port_name, baudrate)
await client.start_dfu(
slave_id,
ota_bin_path,
wait_seconds,
on_dfu_state, # State change callback
on_dfu_progress, # Progress Update Callback
)
def on_dfu_state(_slave_id, state):
"""
DFU Status Change Callback Function
Called during the firmware upgrade process when the status changes。
Args:
_slave_id: Device ID (Unused)
state: DFU Status enumeration value
"""
logger.info(f"DFU STATE: {libstark.DfuState(state)}")
dfu_state = libstark.DfuState(state)
def on_dfu_progress(_slave_id, progress):
"""
DFU Progress Update Callback Function
Report the upgrade progress regularly during the firmware upgrade process。
Args:
_slave_id: Device ID (Unused)
progress: Upgrade progress,Scope 0.0-1.0
"""
logger.info(f"progress: {progress * 100.0 :.2f}%")
CAN 2.0
Single-handed/Two-handed Control
One-Handed Control - ZLG USBCAN Device - C++ Example
// This example is a simple control example combining Revo1 with ZLG USB-CAN devices.
// USBCANFD-200U
// USBCANFD-100U
// USBCANFD-100U-mini
// Need to download the .so file provided by the vendor
// https://manual.zlg.cn/web/#/146
#include <stdio.h>
#include <cstring>
#include <unistd.h>
#include "stark-sdk.h"
#include "zlgcan/zcan.h"
// Constant Definition
#define ZCAN_TYPE_USBCANFD 33 // Device Type
#define ZCAN_CARD_INDEX 0 // Card index
#define ZCAN_CHANNEL_INDEX 0 // Channel index
#define MAX_CHANNELS 2 // Maximum number of channels
#define RX_WAIT_TIME 100 // Reception waiting time
#define RX_BUFF_SIZE 1000 // Receive buffer size
// Function declaration
bool init_can_device();
bool start_can_channel();
void cleanup_resources();
void setup_can_callbacks();
void get_device_info(DeviceHandler *handle, uint8_t slave_id);
int main(int argc, char const *argv[])
{
// Initialize CAN device
if (!init_can_device())
{
return -1;
}
// Start the CAN channel
if (!start_can_channel())
{
cleanup_resources();
return -1;
}
setup_can_callbacks(); // Set read and write callbacks
init_cfg(STARK_PROTOCOL_TYPE_CAN, LOG_LEVEL_DEBUG);
auto handle = create_device_handler();
uint8_t slave_id = 1; // The default ID for a first-generation device is 1
get_device_info(handle, slave_id);
uint16_t positions_fist[] = {50, 50, 100, 100, 100, 100}; // Make a fist
uint16_t positions_open[] = {0, 0, 0, 0, 0, 0}; // Open
useconds_t delay = 1000 * 1000; // 1000ms
stark_set_finger_positions(handle, slave_id, positions_fist, 6);
usleep(delay);
stark_set_finger_positions(handle, slave_id, positions_open, 6);
usleep(delay);
auto finger_status = stark_get_motor_status(handle, slave_id);
if (finger_status != NULL)
{
printf("Positions: %hu, %hu, %hu, %hu, %hu, %hu\n", finger_status->positions[0], finger_status->positions[1], finger_status->positions[2], finger_status->positions[3], finger_status->positions[4], finger_status->positions[5]);
printf("Speeds: %hd, %hd, %hd, %hd, %hd, %hd\n", finger_status->speeds[0], finger_status->speeds[1], finger_status->speeds[2], finger_status->speeds[3], finger_status->speeds[4], finger_status->speeds[5]);
printf("Currents: %hd, %hd, %hd, %hd, %hd, %hd\n", finger_status->currents[0], finger_status->currents[1], finger_status->currents[2], finger_status->currents[3], finger_status->currents[4], finger_status->currents[5]);
printf("States: %hhu, %hhu, %hhu, %hhu, %hhu, %hhu\n", finger_status->states[0], finger_status->states[1], finger_status->states[2], finger_status->states[3], finger_status->states[4], finger_status->states[5]);
free_motor_status_data(finger_status);
}
// Clean up resources
cleanup_resources();
return 0;
}
bool init_can_device()
{
// Turn on the device
if (!VCI_OpenDevice(ZCAN_TYPE_USBCANFD, ZCAN_CARD_INDEX, ZCAN_CHANNEL_INDEX))
{
printf("Failed to open device\n");
return false;
}
return true;
}
bool start_can_channel()
{
ZCAN_INIT init; // Baud rate structure, data is derived from the baud rate calculator of zcanpro
init.mode = 0; // 0- Normal
init.clk = 60000000; // clock: 60M(V1.01) 80M(V1.03 That is the above)
// Arbitration Domain 1Mbps
init.aset.sjw = 1;
init.aset.brp = 5;
init.aset.tseg1 = 6;
init.aset.tseg2 = 1;
init.aset.smp = 0;
// Data domain 5Mbps
init.dset.sjw = 1;
init.dset.brp = 0;
init.dset.tseg1 = 7;
init.dset.tseg2 = 2;
init.dset.smp = 0;
// Initialize CAN channel
if (!VCI_InitCAN(ZCAN_TYPE_USBCANFD, ZCAN_CARD_INDEX, ZCAN_CHANNEL_INDEX, &init))
{
printf("Failed to initialize CAN channel\n");
return false;
}
// Start the CAN channel
if (!VCI_StartCAN(ZCAN_TYPE_USBCANFD, ZCAN_CARD_INDEX, ZCAN_CHANNEL_INDEX))
{
printf("Failed to start CAN channel\n");
return false;
}
return true;
}
void setup_can_callbacks()
{
// CAN Send callback
set_can_tx_callback([](uint8_t slave_id,
uint32_t can_id,
const uint8_t *data,
uintptr_t data_len) -> int
{
printf("CAN Send: Slave ID: %d, CAN ID: 0x%X, Data Length: %zu\n", slave_id, can_id, data_len);
printf("Data: ");
for (uintptr_t i = 0; i < data_len; ++i)
{
printf("%02x ", data[i]);
}
printf("\n");
// Construct CAN send data structure
ZCAN_20_MSG can_msg;
memset(&can_msg, 0, sizeof(ZCAN_20_MSG));
can_msg.hdr.inf.txm = 0; // 0- Send normally
can_msg.hdr.inf.fmt = 0; // 0-CAN
can_msg.hdr.inf.sdf = 0; // 0-Data Frame, 1-Remote Frame
can_msg.hdr.inf.sef = 0; // 0 - Standard Frame, 1-Extended Frame
// can_msg.hdr.inf.echo = 1; // Send echo back
can_msg.hdr.id = can_id; // ID
can_msg.hdr.chn = ZCAN_CHANNEL_INDEX; // Channel
can_msg.hdr.len = data_len; // Data length
// Fill in the data
for (uintptr_t i = 0; i < data_len && i < 8; ++i)
{
can_msg.dat[i] = data[i];
}
// Send CAN frame
printf("Transmitting CAN frame...\n");
int result = VCI_Transmit(ZCAN_TYPE_USBCANFD, ZCAN_CARD_INDEX, ZCAN_CHANNEL_INDEX, &can_msg, 1);
printf("Transmit result: %d\n", result);
return result == 1 ? 0 : -1; // 0 Indicates success
});
// CAN Read callback
set_can_rx_callback([](uint8_t slave_id,
uint32_t *can_id_out,
uint8_t *data_out,
uintptr_t *data_len_out) -> int
{
printf("CAN Read: Slave ID: %d\n", slave_id);
// Read data
ZCAN_20_MSG can_data[RX_BUFF_SIZE];
int len = VCI_Receive(ZCAN_TYPE_USBCANFD, ZCAN_CARD_INDEX, ZCAN_CHANNEL_INDEX, can_data, RX_BUFF_SIZE, RX_WAIT_TIME); // CAN
printf("ZCAN Receive, len: %d\n", len);
if (len < 1)
{
return -1;
}
// Concatenate multiple CAN frames, and the CAN IDs of multiple CAN frames should be consistent
*can_id_out = can_data[0].hdr.id & 0x1FFFFFFF;
int idx = 0;
int total_dlc = 0;
for (int i = 0; i < len; i++)
{
ZCAN_20_MSG recv_data = can_data[i];
int can_dlc = recv_data.hdr.len;
for (int j = 0; j < can_dlc; j++)
{
data_out[idx++] = recv_data.dat[j];
}
total_dlc += can_dlc;
}
*data_len_out = total_dlc;
return 0; // Successful return 0
});
}
void get_device_info(DeviceHandler *handle, uint8_t slave_id)
{
DeviceInfo *info = stark_get_device_info(handle, slave_id);
if (info != NULL)
{
printf("Slave[%hhu] Serial Number: %s\n", slave_id, info->serial_number);
free_device_info(info);
}
}
void cleanup_resources()
{
VCI_ResetCAN(ZCAN_TYPE_USBCANFD, ZCAN_CARD_INDEX, ZCAN_CHANNEL_INDEX); // Reset CAN Channel
VCI_CloseDevice(ZCAN_TYPE_USBCANFD, ZCAN_CARD_INDEX);
printf("Resources cleaned up.\n");
}
One-Handed Control - ZQWL USBCAN Device - C++ Example
// This example is a simple control example combining Revo1 with the ZQWL USB-CAN device.
// USBCANFD-200U
// USBCANFD-100U
// USBCANFD-100U-mini
// Need to download the .dll provided by the vendor
// http://39.108.220.80/download/user/ZQWL/UCANFD/
#include <stdio.h>
#include <windows.h>
#include "stark-sdk.h"
#include "zlgcan/zlgcan.h"
// Global variable declaration
static long device_handle_ = INVALID_DEVICE_HANDLE;
static Handle_chl channel_handle_ = {INVALID_CHANNEL_HANDLE};
// Function declaration
bool init_can_device(int device_type, int channel_index);
bool setup_can_baudrate(int channel_index, int baudrate);
bool start_can_channel(int channel_index);
void setup_can_callbacks();
void get_device_info(DeviceHandler *handle, uint8_t slave_id);
void cleanup_resources();
int main(int argc, char const *argv[])
{
const int device_type = ZCAN_USBCANFD_100U;
const int channel_index = 0;
const int baudrate = 1000000; // 1Mbps
// Initialize CAN device
if (!init_can_device(device_type, channel_index))
{
return -1;
}
// Start the CAN channel
if (!start_can_channel(channel_index))
{
cleanup_resources();
return -1;
}
// Set baud rate and timing parameters
if (!setup_can_baudrate(channel_index, baudrate))
{
cleanup_resources();
return -1;
}
setup_can_callbacks(); // Set read and write callbacks
init_cfg(STARK_PROTOCOL_TYPE_CAN, LOG_LEVEL_DEBUG);
auto handle = create_device_handler();
get_device_info(handle, slave_id);
uint16_t positions_fist[] = {50, 50, 100, 100, 100, 100}; // Make a fist
uint16_t positions_open[] = {0, 0, 0, 0, 0, 0}; // Open
int delay = 1000; // 1000ms
// stark_set_finger_position(handle, slave_id, STARK_FINGER_ID_PINKY, 100);
// Sleep(delay);
stark_set_finger_positions(handle, slave_id, positions_fist, 6);
Sleep(delay);
stark_set_finger_positions(handle, slave_id, positions_open, 6);
Sleep(delay);
// Clean up resources
cleanup_resources();
return 0;
}
bool init_can_device(int device_type, int channel_index)
{
device_handle_ = ZCAN_OpenDevice(device_type, channel_index, 0);
if (device_handle_ == INVALID_DEVICE_HANDLE)
{
printf("Failed to open device\n");
return false;
}
return true;
}
bool setup_can_baudrate(int channel_index, int baudrate)
{
// Set baud rate
char path[50] = {0};
char value[10] = {0};
sprintf_s(path, "%d/baud_rate", channel_index);
sprintf_s(value, "%d", baudrate);
ZCAN_SetValue(device_handle_, path, value);
// Set CAN timing parameters(timing0, timing1)
// 1Mbps Recommendation timing0=0x00, timing1=0x14
char timing_path0[50] = {0};
char timing_value0[10] = {0};
char timing_path1[50] = {0};
char timing_value1[10] = {0};
sprintf_s(timing_path0, "%d/abit_timing0", channel_index);
sprintf_s(timing_value0, "0x00");
sprintf_s(timing_path1, "%d/abit_timing1", channel_index);
sprintf_s(timing_value1, "0x14");
ZCAN_SetValue(device_handle_, timing_path0, timing_value0);
ZCAN_SetValue(device_handle_, timing_path1, timing_value1);
return true;
}
bool start_can_channel(int channel_index)
{
ZCAN_CHANNEL_INIT_CONFIG config;
memset(&config, 0, sizeof(config));
config.can_type = TYPE_CAN;
config.can.mode = 0; // 0: Normal mode, 1: Listen-only mode
channel_handle_ = ZCAN_InitCAN(device_handle_, channel_index, config)[0];
if (INVALID_CHANNEL_HANDLE == channel_handle_.handle)
{
printf("Failed to initialize CAN channel\n");
return false;
}
if (ZCAN_StartCAN(channel_handle_) != STATUS_OK)
{
printf("Failed to start CAN channel\n");
return false;
}
return true;
}
void setup_can_callbacks()
{
// CAN Send callback
set_can_tx_callback([](uint8_t slave_id,
uint32_t can_id,
const uint8_t *data,
uintptr_t data_len) -> int
{
printf("CAN Send: Slave ID: %d, CAN ID: 0x%X, Data Length: %zu\n", slave_id, can_id, data_len);
printf("Data: ");
for (uintptr_t i = 0; i < data_len; ++i)
{
printf("%02x ", data[i]);
}
printf("\n");
// Construct CAN send data structure
ZCAN_Transmit_Data can_data;
memset(&can_data, 0, sizeof(can_data));
// Settings CAN ID,Standard frame eff=0,Extended frame eff=1
// rtr=0: Data frame,rtr=1: Remote Frame;err=0: Normal frame,err=1: Error frame
can_data.frame.can_id = MAKE_CAN_ID(can_id, 0, 0, 0); // Standard frame
can_data.frame.can_dlc = data_len;
can_data.transmit_type = 0; // Send normally
// Fill in the data
for (uintptr_t i = 0; i < data_len && i < 8; ++i)
{
can_data.frame.data[i] = data[i];
}
// Send CAN frame
int result = ZCAN_Transmit(channel_handle_, &can_data, 1);
return result == 1 ? 0 : -1; // 0 Indicates success
});
// CAN Read callback
set_can_rx_callback([](uint8_t slave_id,
uint32_t *can_id_out,
uint8_t *data_out,
uintptr_t *data_len_out) -> int
{
printf("CAN Read: Slave ID: %d\n", slave_id);
// Read data
ZCAN_Receive_Data can_data[1000];
int len = ZCAN_GetReceiveNum(channel_handle_, TYPE_CAN);
len = ZCAN_Receive(channel_handle_, can_data, 1000, 50);
if (len < 1)
{
return -1;
}
// Concatenate multiple CAN frames,The CAN IDs of multiple CAN frames should be consistent. *can_id_out = can_data[0].frame.can_id;
int idx = 0;
int total_dlc = 0;
for (int i = 0; i < len; i++)
{
ZCAN_Receive_Data recv_data = can_data[i];
int can_dlc = recv_data.frame.can_dlc;
for (int j = 0; j < can_dlc; j++)
{
data_out[idx++] = recv_data.frame.data[j];
}
total_dlc += can_dlc;
}
*data_len_out = total_dlc;
return 0; // Successful return 0
});
}
void get_device_info(DeviceHandler *handle, uint8_t slave_id)
{
DeviceInfo *info = stark_get_device_info(handle, slave_id);
if (info != NULL)
{
printf("Slave[%hhu] Serial Number: %s\n", slave_id, info->serial_number);
free_device_info(info);
}
}
void cleanup_resources()
{
if (channel_handle_.handle != INVALID_CHANNEL_HANDLE)
{
ZCAN_ResetCAN(channel_handle_);
}
if (device_handle_ != INVALID_DEVICE_HANDLE)
{
ZCAN_CloseDevice(device_handle_);
}
}
One-Handed Control - ZQWL USBCAN Device - Python Example
import asyncio
import sys
from zqwl_win import zcan_open, zcan_close, zcan_send_message, zqwl_can_receive_message
from can_utils import libstark, logger, setup_shutdown_event
class Revo1CanController:
"""Revo1 CAN Communication Controller"""
def __init__(self, master_id: int = 1, slave_id: int = 1):
self.master_id = master_id
self.slave_id = slave_id
self.client = libstark.PyDeviceContext()
async def initialize(
self, device_type: int = 42, channel: int = 0, baudrate: int = 1000000
):
""" Initialize CAN connection """
try:
# Initialize ZCAN device
zcan_open(device_type=device_type, channel=channel, baudrate=baudrate)
# Set up a callback function
libstark.set_can_tx_callback(self._can_send)
libstark.set_can_rx_callback(self._can_read)
logger.info(
f"CAN Connection initialization succeeded - Master ID: {self.master_id}, Slave ID: {self.slave_id}"
)
return True
except Exception as e:
logger.error(f"CAN Connection initialization failed: {e}")
return False
def _can_send(self, slave_id: int, can_id: int, data: list) -> bool:
"""CAN Message sending"""
try:
if not zcan_send_message(slave_id, can_id, bytes(data)):
logger.error("Failed to send CAN message ")
return False
return True
except Exception as e:
logger.error(f"CAN Sending exception: {e}")
return False
def _can_read(self, slave_id: int) -> tuple:
"""CAN Message reception"""
try:
recv_msg = zqwl_can_receive_message()
if recv_msg is None:
return 0, bytes([])
can_id, data = recv_msg
# Optional: Enable detailed debug logs
# logger.debug(f" ReceiveCAN - ID: {can_id:029b}, Data: {bytes(data).hex()}")
return can_id, data
except Exception as e:
logger.error(f"CAN Receiving an exception: {e}")
return 0, bytes([])
async def get_device_info(self):
""" Get device information """
try:
device_info = await self.client.get_device_info(self.slave_id)
logger.info(f" Device Information: {device_info.description}")
return device_info
except Exception as e:
logger.error(f" Failed to get device information: {e}")
return None
async def change_slave_id(self, new_slave_id: int):
"""Modify device slave ID(Use with caution, the device will restart)"""
try:
await self.client.set_slave_id(self.slave_id, new_slave_id)
logger.info(f" The slave station ID has been modified to {new_slave_id}, and the device will restart...")
return True
except Exception as e:
logger.error(f" Failed to modify the substation ID: {e}")
return False
async def configure_device(self):
""" Configure device parameters """
try:
# Options: Modify the slave station ID (if needed, uncomment and set a new ID)
# WARNING: The device will restart, and the program will exit.
# if await self.change_slave_id(new_slave_id=2):
# sys.exit(0)
# Disable auto-calibration and perform manual calibration
# await self.client.set_auto_calibration(self.slave_id, False) # Disable automatic calibration after startup # await self.client.calibrate_position(self.slave_id)
# await self.client.set_auto_calibration(self.slave_id, True) # Enable automatic calibration after startup
# auto_calibration_enabled = await self.client.get_auto_calibration_enabled(self.slave_id)
# logger.info(f" Automatic calibration on startup: {auto_calibration_enabled}")
# Configuration: Turbo mode (optional, uncomment to enable)
# await self.configure_turbo_mode()
return True
except Exception as e:
logger.error(f" Failed to configure the device: {e}")
return False
async def configure_turbo_mode(self):
""" Configure Turbo mode """
try:
# Enable Turbo mode
await self.client.set_turbo_mode_enabled(self.slave_id, True)
# Set the Turbo parameter
turbo_interval = 200 # Grip interval time (milliseconds)
turbo_duration = 300 # Grip duration (milliseconds)
turbo_conf = libstark.TurboConfig(turbo_interval, turbo_duration)
await self.client.set_turbo_config(self.slave_id, turbo_conf)
# Verify configuration
turbo_mode_enabled = await self.client.get_turbo_mode_enabled(self.slave_id)
turbo_config = await self.client.get_turbo_config(self.slave_id)
logger.info(f" Turbo Mode: {turbo_mode_enabled}")
logger.info(
f"Turbo Configuration - Interval: {turbo_config.interval}ms, Continuous: {turbo_config.duration}ms"
)
except Exception as e:
logger.error(f" Failed to configure Turbo mode: {e}")
async def finger_position_examples(self):
""" Finger Position Control Example """
logger.info("===Finger Position Control Example ===")
# Example 1: Make a fist action one by one
logger.info("Make a fist with each finger one by one ")
positions = [
[20, 0, 0, 0, 0, 0], # Thumb
[20, 30, 0, 0, 0, 0], # Thumb + Index finger
[20, 30, 50, 0, 0, 0], # + Middle finger
[20, 30, 50, 70, 0, 0], # + Ring finger
[20, 30, 50, 70, 80, 0], # + Pinky
[20, 30, 50, 70, 80, 90], # + wrist
]
for i, pos in enumerate(positions):
logger.info(f" Steps {i+1}: {pos}")
await self.client.set_finger_positions(self.slave_id, pos)
await asyncio.sleep(0.8)
await asyncio.sleep(1.0)
# Example 2: Preset Gestures
logger.info("Example 2: Preset Gestures ")
gestures = {
" Open your hands ": [0, 0, 0, 0, 0, 0],
" Point to ": [0, 30, 0, 0, 0, 0],
" Victory gesture ": [0, 30, 80, 0, 0, 0],
" OK gesture ": [80, 30, 80, 0, 0, 0],
" Make a fist ": [80, 30, 100, 100, 100, 100],
}
for gesture_name, positions in gestures.items():
logger.info(f" Perform the gesture: {gesture_name} - {positions}")
await self.client.set_finger_positions(self.slave_id, positions)
await asyncio.sleep(1.5)
# Example 3: Simulating the Grabbing Action
logger.info("Example 3: Simulating the Grabbing Action ")
grab_sequence = [
([0, 0, 0, 0, 0, 0], " Initial position "),
([30, 40, 60, 80, 80, 0], " Crawling Preparation "),
([60, 70, 90, 100, 100, 0], " Capture completed "),
]
for positions, description in grab_sequence:
logger.info(f"{description}: {positions}")
await self.client.set_finger_positions(self.slave_id, positions)
await asyncio.sleep(1.0)
async def finger_speed_examples(self):
""" Finger Speed Control Example """
logger.info("===Finger Speed Control Example ===")
# Stop all movements
await self.client.set_finger_speeds(self.slave_id, [0] * 6)
logger.info("All fingers stop moving ")
async def single_finger_control_example(self, finger_id: libstark.FingerId):
""" Single Finger Control Example """
logger.info(f"=== Single Finger Control Example - {finger_id} ===")
# Position Control
logger.info("Position Control Test ")
await self.client.set_finger_position(self.slave_id, finger_id, 100) # Maximum position
await asyncio.sleep(1.0)
await self.client.set_finger_position(self.slave_id, finger_id, 0) # Initial position
await asyncio.sleep(1.0)
# Speed control
logger.info("Speed Control Test ")
await self.client.set_finger_speed(
self.slave_id, finger_id, 100
) # Forward maximum speed
await asyncio.sleep(1.0)
await self.client.set_finger_speed(
self.slave_id, finger_id, -100
) # Reverse maximum speed
await asyncio.sleep(1.0)
await self.client.set_finger_speed(self.slave_id, finger_id, 0) # Stop
async def get_motor_status(self) -> libstark.MotorStatusData:
""" Get motor status """
try:
status = await self.client.get_motor_status(self.slave_id)
# Optional: Enable detailed status logging
# logger.info(f" Motor status: {status.description}")
return status
except Exception as e:
logger.error(f" Failed to get motor status: {e}")
return None # type: ignore
async def monitor_motor_status(self, interval: float = 0.001):
""" Continuously monitor the motor status """
logger.info(f" Start monitoring the device {self.slave_id:02x} the motor status ")
while True:
try:
await self.get_motor_status()
await asyncio.sleep(interval)
except asyncio.CancelledError:
logger.info("Motor status monitoring has been canceled ")
break
except Exception as e:
logger.error(f" Abnormal motor status monitoring: {e}")
await asyncio.sleep(1)
async def demo_task(self):
""" Demonstration task """
# Get device information
await self.get_device_info()
# Configure device parameters and enable as needed
# await self.configure_device()
# Enable the following demo as needed
await self.finger_position_examples()
await asyncio.sleep(1.0)
await self.finger_speed_examples()
await asyncio.sleep(1.0)
await self.single_finger_control_example(libstark.FingerId.Pinky)
# Set the final gesture (boxing gesture)
# await self.client.set_finger_positions(
# self.slave_id, [80, 30, 100, 100, 100, 100]
# )
def cleanup(self):
""" Clean up resources """
try:
zcan_close()
logger.info("CAN connection has been closed ")
except Exception as e:
logger.error(f" Error occurred while cleaning up resources: {e}")
async def main():
""" Main function """
controller = Revo1CanController(master_id=1, slave_id=1)
try:
# Initialize connection
if not await controller.initialize():
logger.error("Initialization failed, program exited ")
return
# Set up a close event listener
shutdown_event = setup_shutdown_event(logger)
# Create a task
tasks = []
# Start the demo task
demo_task = asyncio.create_task(controller.demo_task())
tasks.append(demo_task)
# Optional: Start motor status monitoring
monitor_task = asyncio.create_task(controller.monitor_motor_status())
tasks.append(monitor_task)
# Wait for the close signal
await shutdown_event.wait()
logger.info("Received shutdown signal, stop all tasks...")
# Cancel all tasks
for task in tasks:
task.cancel()
try:
await task
except asyncio.CancelledError:
pass
except Exception as e:
logger.error(f" Program execution exception: {e}")
finally:
controller.cleanup()
sys.exit(0)
if __name__ == "__main__":
asyncio.run(main())
Python SDK API Details
Modbus-RTU Connection
import libstark
from libstark import Baudrate, PyDeviceContext
# ================================
# Modbus Serial Port Connection (for Revo1/Revo2 Basic Edition)
# ================================
async def connect_modbus(port_name: str = "/dev/ttyUSB0", baudrate: Baudrate = Baudrate.Baud115200) -> PyDeviceContext:
"""
Connect the device via Modbus protocol
Args:
port_name: Serial port name
- Linux: "/dev/ttyUSB0", "/dev/ttyACM0"
- Windows: "COM3", "COM4"
- macOS: "/dev/tty.usbserial-xxx"
baudrate: Serial Port Baud Rate
- It is recommended to use Baudrate.Baud115200 (default)
- Other optional options: Baud57600, Baud460800, Baud1Mbps, etc.
Returns:
PyDeviceContext: Device Connection Object
Example:
device = await connect_modbus("/dev/ttyUSB0", Baudrate.Baud115200)
device_info = await device.get_device_info(slave_id=1)
print(f" Connected devices: {device_info.sku_type}")
"""
try:
libstark.init_config(libstark.StarkProtocolType.Modbus)
device = await libstark.modbus_open(port_name, baudrate)
print(f"Modbus Connection successful: {port_name} @ {baudrate.name}")
return device
except Exception as e:
print(f"Modbus Connection failed: {e}")
raise
# ================================
# Modbus Device Connection Utility Function
# ================================
def list_available_ports():
"""
List the available serial ports in the system
Returns:
list: List of available serial ports
Using example:
ports = list_available_ports()
print("Can be used with serial port:")
for port in ports:
print(f" {port}")
"""
try:
ports = libstark.list_available_ports()
print(f" Found {len(ports)} available serial ports ")
return ports
except Exception as e:
print(f" Failed to get serial port list: {e}")
return []
async def auto_detect_device(port_name: str = None, quick: bool = True):
"""
Automatically detect device protocols and connection parameters
Args:
port_name: Specify port name,None For automatic search
quick: Is quick detection (skip some optional detection steps)
Returns:
PyDeviceContext: Device connection object; returns None if the device is not found
Note:
- It will sequentially attempt different ports and baud rates
- Fast detection mode skips some time-consuming detection steps
- Suitable for rapid prototyping and testing
"""
try:
(protocol, port_name, baudrate, slave_id) = await libstark.auto_detect_device(port_name, quick)
logger.info(f"Detected protocol: {protocol}, port: {port_name}, baudrate: {baudrate}, slave_id: {slave_id}")
except Exception as e:
print(f" Device automatic detection failed: {e}")
return None
async def auto_detect_modbus_revo1(port_name: str = None, quick: bool = True):
"""
Automatically detect Modbus connection parameters for Revo1 device
Returns:
tuple: (protocol, port_name, baudrate, slave_id) or None
"""
try:
(protocol, port_name, baudrate, slave_id) = await libstark.auto_detect_modbus_revo1(port_name, quick)
logger.info(f"Detected Revo1 Modbus parameters: {protocol}, {port_name}, {baudrate}, {slave_id}")
return (protocol, port_name, baudrate, slave_id)
except Exception as e:
print(f"Revo1 Device automatic detection failed: {e}")
return None
async def auto_detect_modbus_revo2(port_name: str = None, quick: bool = True):
"""
Automatically detect Modbus connection parameters for Revo2 device
Returns:
tuple: (protocol, port_name, baudrate, slave_id) or None
"""
try:
(protocol, port_name, baudrate, slave_id) = await libstark.auto_detect_modbus_revo2(port_name, quick)
logger.info(f"Detected Revo2 Modbus parameters: {protocol}, {port_name}, {baudrate}, {slave_id}")
return (protocol, port_name, baudrate, slave_id)
except Exception as e:
print(f"Revo2 Device automatic detection failed: {e}")
return None
CAN 2.0
import libstark
from libstark import PyDeviceContext
# ================================
# CAN Bus Connection (for Revo1)
# ================================
def connect_can():
"""
Connect devices via CAN bus
Returns:
PyDeviceContext: Device Connection Object
Note:
- Only applicable to Revo1 devices
- CAN 2.0 adapter hardware support is required
Preparation before use:
1. Connect the CAN 2.0 adapter to the computer
2. Implement CAN device connection
3. Implement the _can_send and _can_read callback functions
"""
try:
libstark.init_config(libstark.StarkProtocolType.Can)
client = libstark.PyDeviceContext()
# CAN Device Connection (needs adjustment according to actual hardware)
libuser.open_can_device()
# Set CAN communication callback function
# Note: The user needs to implement these two callback functions
libstark.set_can_tx_callback(_can_send) # Set up send callback
libstark.set_can_rx_callback(_can_read) # Set up the receive callback
return client
except Exception as e:
print(f"CAN Connection failed: {e}")
raise
# ================================
# CAN Communication Callback Function Example
# ================================
def _can_send(slave_id: int, can_id: int, data: list):
"""
CAN Example of data sending callback function
Args:
slave_id: Substation ID
can_id: CAN ID
data: CAN data to be sent
Note:
- The user needs to implement this function based on the actual CAN adapter.
- The function is responsible for sending data through the CAN adapter to the bus.
- When implementing, hardware-related sending logic needs to be handled.
"""
# Example implementation (needs to be adjusted according to actual hardware)
# can_interface.send(data)
print(f" Send CAN data: {data}")
def _can_read(slave_id: int):
"""
CAN Example of Data Reception Callback Function
Args:
slave_id: Substation ID
Note:
- This function is called when data is received from the CAN bus
- The SDK will automatically parse the received data
- Users usually do not need to perform additional processing in this function
"""
# Example implementation (needs to be adjusted according to actual hardware)
# can_id = data = can_interface.recv()
print(f" Received CAN ID, CAN data,{can_id}, {data}")
return can_id, data
Device Information
from enum import Enum
# Device SKU Type
class SkuType(Enum):
MediumRight = ...
MediumLeft = ...
int_value: int
# Hardware type
class StarkHardwareType(Enum):
Revo1Basic = ... # First Generation Basic Edition
Revo1Touch = ... # First-Generation Tactile Version
Revo2Basic = ... # Second-Generation Basic Version
Revo2Touch = ... # Second-Generation Tactile Edition
int_value: int
# Basic Device Information
class DeviceInfo:
sku_type: SkuType # Device SKU Type
r"""
Hand SKU
"""
serial_number: str # Serial number
r"""
Serial number
"""
firmware_version: str # Firmware version
r"""
Firmware version
"""
description: str
def is_revo1(self) -> bool: ...
def is_revo1_touch(self) -> bool: ...
def is_revo2(self) -> bool: ...
def is_revo2_touch(self) -> bool: ...
def is_touch(self) -> bool: ...
def get_hardware_type(self) -> StarkHardwareType: ...
def desc(self) -> str: ...
# RS-485 Baud rate
class Baudrate(Enum):
Baud115200 = ...
Baud57600 = ...
Baud19200 = ...
Baud460800 = ...
Baud1Mbps = ...
Baud2Mbps = ...
Baud5Mbps = ...
int_value: int
# RS-485 Serial Port Configuration
class SerialPortCfg:
slave_id: int # From Machine ID
r"""
Slave ID for the protocol
- For Modbus protocol: range 0~254, default is 1, 0 is the broadcast address (broadcast is only for control commands)
"""
baudrate: Baudrate # Baud rate
r"""
Baud rate for the serial communication
"""
description: str
def desc(self) -> str: ...
# Get device information
def get_device_info(self, slave_id: int) -> DeviceInfo:
"""
Get device basic information
Args:
slave_id: Device Slave ID
Returns:
DeviceInfo: Device information object, which includes the following properties:
- sku_type: SkuType - Device SKU Type (Left/Right Hand Differentiation)
- serial_number: str - Device Serial Number
- firmware_version: str - Firmware version number
- description: str - Device description information
DeviceInfo The provided method:
- is_revo1() -> bool: Determine if it is a Revo1 device
- is_revo1_touch() -> bool: Determine if it is the Revo1 Tactile version
- is_revo2() -> bool: Determine if it is a Revo2 device
- is_revo2_touch() -> bool: Determine if it is the Revo2 Tactile version
- is_touch() -> bool: Determine if tactile function is supported
- get_hardware_type() -> StarkHardwareType: Get hardware type
- desc() -> str: Get description information
Example:
device_info = device.get_device_info(slave_id=1)
print(f" Device Type: {device_info.sku_type}")
print(f" Serial number: {device_info.serial_number}")
print(f" Firmware version: {device_info.firmware_version}")
if device_info.is_revo2_touch():
print("This is a Revo2 device with tactile support ")
"""
pass
# Get device serial number
def get_device_sn(self, slave_id: int) -> str:
"""
Get device firmware serial number
Args:
slave_id: Device Slave ID
Returns:
str: Device serial number string
Example:
serial_number = device.get_device_sn(slave_id=1)
print(f" Device Serial Number: {serial_number}")
"""
pass
# Get the device firmware version number
def get_device_fw_version(self, slave_id: int) -> str:
"""
Get the device firmware version number
Args:
slave_id: Device Slave ID
Returns:
str: Firmware version number string
Example:
fw_version = device.get_device_fw_version(slave_id=1)
print(f" Firmware version: {fw_version}")
"""
pass
# Get device SKU type
def get_sku_type(self, slave_id: int) -> SkuType:
"""
Get the device SKU type to distinguish between left-handed and right-handed
Args:
slave_id: Device Slave ID
Returns:
SkuType: Device Type Enumeration
- MediumRight: Medium right hand
- MediumLeft: Medium left hand
- SmallRight: Small horn right hand
- SmallLeft: Small horn left hand
Example:
sku_type = device.get_sku_type(slave_id=1)
if sku_type == SkuType.MediumRight:
print("This is a medium-sized right-handed device ")
"""
pass
# Get RS-485 device configuration
def get_serialport_cfg(self, slave_id: int) -> SerialPortCfg:
"""
Get RS-485 device configuration
Args:
slave_id: Device Slave ID
Returns:
SerialPortCfg: Serial port configuration object, which includes the following properties:
- slave_id: int - Protocol Slave ID
* Protocol Buffers protocol: Range 10~254, default is 10, 254 is the broadcast address
* Modbus Protocol: Range 0~254, default is 1, 0 is the broadcast address (only used for control commands)
- baudrate: Baudrate - Serial Communication Baud Rate
- description: str - Configuration description information
SerialPortCfg The provided method:
- desc() -> str: Get configuration description
Example:
config = device.get_serialport_cfg(slave_id=1)
print(f" Substation ID: {config.slave_id}")
print(f" Baud rate: {config.baudrate}")
"""
pass
# Get RS-485 baud rate
def get_serialport_baudrate(self, slave_id: int) -> int:
"""
Get RS-485 Serial Port Baud Rate
Args:
slave_id: Device Slave ID
Returns:
int: Current configured baud rate value
Example:
baudrate = device.get_serialport_baudrate(slave_id=1)
print(f" Current baud rate: {baudrate}")
"""
pass
# Set RS-485 Baud Rate
def set_serialport_baudrate(self, slave_id: int, baudrate: "Baudrate"):
"""
Set serial port baud rate
Args:
slave_id: Device Slave ID
baudrate: Baud rate enumeration values
- Baud115200: 115200 bps
- Baud57600: 57600 bps
- Baud19200: 19200 bps
- Baud460800: 460800 bps
- Baud1Mbps: 1 Mbps
- Baud2Mbps: 2 Mbps
- Baud5Mbps: 5 Mbps
Example:
device.set_serialport_baudrate(slave_id=1, baudrate=Baudrate.Baud460800)
"""
pass
# Set RS-485 Slave ID
def set_slave_id(self, slave_id: int, new_id: int):
"""
Set device slave ID
Args:
slave_id: Current Device Slave ID
new_id: New slave station ID
Example:
device.set_slave_id(slave_id=1, new_id=2)
"""
pass
Joint Control - Position/Velocity/Current
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 a single joint position
Args:
slave_id: Device Slave ID
finger_id: FingerId enumeration value
- Thumb: Thumb
- ThumbAux: Thumb assist
- Index: Index finger
- Middle: Middle finger
- Ring: Ring finger
- Pinky: Little finger
position: Target position value
Example:
# Set thumb position
device.set_finger_position(slave_id=1, finger_id=FingerId.Thumb, position=500)
# Set the position of the index finger
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 single joint position (specify run time)
Args:
slave_id: Device Slave ID
finger_id: FingerId enumeration value
position: Target position value
milliseconds: Running time (milliseconds)
Example:
# Thumb moves to position 500 within 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 single joint position (specify running speed)
Args:
slave_id: Device Slave ID
finger_id: FingerId enumeration value
position: Target position value
speed: Running speed value
Example:
# Thumb moves to position 500 at a speed of 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 all joint positions
Args:
slave_id: Device Slave ID
positions: List of position values, in the order [thumb, thumb auxiliary, index finger, middle finger, ring finger, little finger]
Example:
# Set all joints to the specified position
positions = [500, 500, 1000, 1000, 1000, 1000] # The thumb is half-open, and the other joints are 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 all joint positions and run time
Args:
slave_id: Device Slave ID
positions: List of position values
durations: List of running times (milliseconds)
Example:
positions = [1000, 1000, 1000, 1000, 1000, 1000]
durations = [2000, 2000, 1500, 1500, 1500, 1500] # Different joints have different operating times
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 all joint positions and running speed
Args:
slave_id: Device Slave ID
positions: List of position values
speeds: List of operating speeds
Example:
positions = [1000, 1000, 1000, 1000, 1000, 1000]
speeds = [300, 300, 400, 400, 400, 400] # Different joints at different speeds
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 operating speed of a single joint
Args:
slave_id: Device Slave ID
finger_id: FingerId enumeration 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 operating speed of 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 single joint drive current
Args:
slave_id: Device Slave ID
finger_id: FingerId enumeration 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 all joint drive currents
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 of a single joint
Args:
slave_id: Device Slave ID
finger_id: FingerId Enumeration 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 all joint PWM control values
Args:
slave_id: Device Slave ID
pwms: PWM Control value list
Example:
pwms = [50, 50, 60, 60, 60, 60]
device.set_finger_pwms(slave_id=1, pwms=pwms)
"""
pass
Joint information
from typing import List
from enum import Enum
# Joint ID
class FingerId(Enum):
Thumb = ... # Thumb
ThumbAux = ... # Thumb assist
Index = ... # Index finger
Middle = ... # Middle finger
Ring = ... # Ring finger
Pinky = ... # Little finger
# Joint condition
class MotorState(Enum):
Unknown = ...
Idle = ... # Idle
Running = ... # In operation
Stall = ... # Locked rotor
Turbo = ... # Turbo Pattern
# All joint information
class MotorStatusData:
positions: list[int] # Joint position
speeds: list[int] # Speed of movement
currents: list[int] # Drive current
states: list[MotorState] # Status
description: str
def get_motor_status(self, slave_id: int) -> MotorStatusData:
"""
Get comprehensive joint status data
Args:
slave_id: Device Slave ID
Returns:
MotorStatusData: Joint status data object, which includes the following properties:
- positions: List[int] - Current position values of all joints
- speeds: List[int] - Current movement speed of all joints
- currents: List[int] - Current movement speed of all joints
- states: List[MotorState] - All joint operation status
- description: str - Status description information
MotorStatusData The provided method:
- is_idle() -> bool: Determine if all joints are in an idle state
- is_opened() -> bool: Determine if it is in an open state
- is_closed() -> bool: Determine if it is in a gripping state
- desc() -> str: Get status description
Example:
motor_status = device.get_motor_status(slave_id=1)
print(f" Joint position: {motor_status.positions}")
print(f" Speed of movement: {motor_status.speeds}")
print(f" Drive current: {motor_status.currents}")
if motor_status.is_idle():
print("All joints are in a free state ")
"""
pass
def get_finger_positions(self, slave_id: int) -> List[int]:
"""
Get the current position values of all joints
Args:
slave_id: Device Slave ID
Returns:
List[int]: List of joint position values, arranged in the order [thumb, thumb auxiliary, index finger, middle finger, ring finger, little finger]
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 the current motion speed of all joints
Args:
slave_id: Device Slave ID
Returns:
List[int]: List of joint velocity values, arranged in the order [thumb, thumb auxiliary, index finger, middle finger, ring finger, little finger]
Example:
speeds = device.get_finger_speeds(slave_id=1)
print(f" All joint velocities: {speeds}")
"""
pass
def get_finger_currents(self, slave_id: int) -> List[int]:
"""
Get the current drive current of all joints
Args:
slave_id: Device Slave ID
Returns:
List[int]: List of joint current values, arranged in the order [thumb, thumb auxiliary, index finger, middle finger, ring finger, little finger]
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 joint operation status
Args:
slave_id: Device Slave ID
Returns:
MotorState: Joint Status Enumeration
- Unknown: Unknown status
- Idle: Joint clearance
- Running: Joint movement
- Stall: Joint jamming
- Turbo: Turbo High-speed mode
Example:
motor_state = device.get_motor_state(slave_id=1)
if motor_state == MotorState.Running:
print("The joint is operating ")
elif motor_state == MotorState.Stall:
print("Joint jamming ")
"""
pass
Tactile information
from typing import List
# ================================
# Tactile Sensor Interface
# ================================
# Tactile sensor data
# Three-dimensional force values, self-approach capacitance, mutual-approach capacitance, and sensor status
class TouchFingerItem:
normal_force1: int # Normal force 1,Unit 0.01N,For example, 1000 represents 1000 * 0.01, which is 10 N。The measurement range of the normal force is 0 ~ 25 N
normal_force2: int # Normal force 2,Unit 0.01N
normal_force3: int # Normal force 3,Unit 0.01N
tangential_force1: int # Tangential Force 1,Unit 0.01N,For example, 1000 represents 1000 * 0.01, which is 10 N。The measurement range of the tangential force is 0 ~ 25 N
tangential_force2: int # Tangential Force 2,Unit 0.01N
tangential_force3: int # Tangential Force 3,Unit 0.01N
tangential_direction1: int # Tangential force direction 1, numerical range is 0 ~ 359 degrees。The direction towards the fingertips is 0 degrees. It can be rotated clockwise up to a maximum of 359 degrees. When the value is 65535 (0xFFFF), it indicates that the tangential force direction is invalid.
tangential_direction2: int # Tangential Force Direction 2
tangential_direction3: int # Tangential Force Direction 3
self_proximity1: int # Self-capacitance 1
self_proximity2: int # Self-capacitance 2
mutual_proximity: int # Mutual capacitance
status: int # Status
description: str # Describe
def is_normal(self) -> bool: ...
def is_abnormal(self) -> bool: ...
def desc(self) -> str: ...
class TouchFingerData:
items: list[TouchFingerItem]
# Tactile raw 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 the dedicated interface for tactile sensors, applicable to the Tactile Edition of the first and second generation dexterous hands。
Tactile Sensor Function:
- Multidimensional Force Sensing: Normal Force, Tangential Force, Tangential Force Direction
- Proximity Detection: Self-Capacitance and Mutual-Capacitance Detection
- Contact status recognition: No contact, static contact, sliding contact
- Real-time data stream: supports high-frequency data collection and callback
Please confirm the device type before use:
device_info = device.get_device_info(slave_id=1)
if device_info.is_touch():
print("The device supports haptic feedback ")
if device_info.is_revo1_touch():
print("Revo1 Touch - Generation One Dexterous Hand Tactile Version ")
elif device_info.is_revo2_touch():
print("Revo2 Touch - Second-Generation Dextrous Hand Tactile Version ")
Tactile Sensor Usage Process:
1. Check if the device supports haptic feedback
2. Enable and initialize the tactile sensor
3. Acquire tactile data
4. Process tactile data and implement corresponding control logic
5. Calibrate zero drift and reset the tactile sensor
"""
def get_touch_sensor_enabled(self, slave_id: int) -> bool:
"""
Get whether the tactile sensor is enabled
Args:
slave_id: Device Slave ID
Returns:
bool: True means enabled, False means disabled
Note:
- The tactile sensor is defaultly turned off to save power consumption
- Tactile data can only be obtained after enabling it.
- Suitable for all touch-enabled 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("The tactile sensor is not enabled. Please enable it first. ")
else:
print("Tactile sensor has been enabled ")
"""
pass
def get_touch_sensor_fw_versions(self, slave_id: int) -> List[str]:
"""
Get the list of firmware version numbers for the tactile sensor
Args:
slave_id: Device Slave ID
Returns:
List[str]: List of firmware version numbers for each finger touch sensor
Note:
- The tactile sensor needs to be enabled first
- The sensors on each finger may have different firmware versions
- Version information helps with problem diagnosis and compatibility confirmation
Example:
if device.get_touch_sensor_enabled(slave_id=1):
versions = device.get_touch_sensor_fw_versions(slave_id=1)
finger_names = ["Thumb ", " Index finger ", " Middle finger ", " Ring finger ", " Little finger "]
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: Sensor bit mask, specifying the sensors to be enabled
- Use bitwise operations to specify which fingers to enable
- For example: 0x1F (0b11111) represents enabling all 5 fingers
- For example: 0x03 (0b00011) indicates that only the thumb and index finger are enabled.
Sensor Bit Mask Description:
- Bit 0: Thumb
- Bit 1: Index finger
- Bit 2: Middle finger
- Bit 3: Ring finger
- Bit 4: Little finger
Note:
- This is the first step in using a tactile sensor
- After enabling, the sensor will perform initialization and self-check.
- The initialization process
Example:
# Enable the tactile sensors of all fingers
device.touch_sensor_setup(slave_id=1, bits=0x1F)
print("All tactile sensors have been enabled ")
# Only enable the tactile sensors of the thumb and index finger
device.touch_sensor_setup(slave_id=1, bits=0x03)
print("The tactile sensor on the thumb and index finger has been enabled ")
# Only enable the tactile sensor of the index finger
device.touch_sensor_setup(slave_id=1, bits=0x02)
print("The index finger tactile sensor has been enabled ")
"""
pass
def touch_sensor_reset(self, slave_id: int, bits: int):
"""
Reset the specified tactile sensor
Args:
slave_id: Device Slave ID
bits: Sensor bit mask, specifying the sensors to be reset
Note:
- During reset, the sensor status register will continuously return an abnormal state.
- After reset, the sensor will resume normal operation
- Used to resolve sensor anomalies or reinitialize
- Resetting will not change the enabled status of the sensor
Example:
# Reset all tactile sensors
device.touch_sensor_reset(slave_id=1, bits=0x1F)
print("All tactile sensors have been reset ")
# Reset only the thumb sensor (if an anomaly is detected)
device.touch_sensor_reset(slave_id=1, bits=0x01)
print("The thumb tactile sensor has been reset ")
"""
pass
def touch_sensor_calibrate(self, slave_id: int, bits: int):
"""
Perform tactile sensor zero-drift calibration
Args:
slave_id: Device Slave ID
bits: Sensor bit mask, specifying the sensors to be calibrated
Calibration Notes:
- When calibrating, make sure your fingers do not touch any objects
- The calibration process will eliminate the zero-drift error of the sensor
- It is recommended to perform calibration after the ambient temperature stabilizes
- After calibration is completed, the sensor accuracy will be significantly improved
Recommended calibration timing:
- When using the device for the first time
- When the ambient temperature changes significantly
- When sensor readings show a significant offset
- During regular maintenance
Example:
# Calibrate all tactile sensors
print("Start calibrating the tactile sensor, please make sure your fingers are not in contact with any objects...")
device.touch_sensor_calibrate(slave_id=1, bits=0x1F)
print("Tactile sensor calibration completed ")
# Calibrate the thumb sensor only
device.touch_sensor_calibrate(slave_id=1, bits=0x01)
print("Thumb tactile sensor calibration completed ")
"""
pass
def get_touch_raw_data(self, slave_id: int) -> TouchRawData:
"""
Get raw data from the tactile sensor(Mainly used for Revo1 Touch)
Args:
slave_id: Device Slave ID
Returns:
: Tactile raw data object, which contains the following properties:
- thumb: List[int] - Thumb original sensor data
- index: List[int] - Index finger raw sensor data
- middle: List[int] - Middle finger original sensor data
- ring: List[int] - Raw sensor data of the ring finger
- pinky: List[int] - Little finger original sensor data
- description: str - Data Description
TouchRawData The provided method:
- desc() -> str: Get data description
Note:
- Only applicable to touch-enabled devices, primarily Revo1 Touch
- The tactile sensor needs to be enabled first
- The original data consists of unprocessed sensor readings that need to be parsed manually.
- Data format and meaning may vary depending on the 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 data: {raw_data.thumb}")
print(f" Index finger raw data: {raw_data.index}")
print(f" Data Description: {raw_data.desc()}")
# Example of raw data processing
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 the specified finger
Args:
slave_id: Device Slave ID
index: Finger index (0-4, corresponding to thumb to little finger)
Note:
- The tactile sensor of the corresponding finger needs to be enabled first
Example:
if device.get_device_info(slave_id=1).is_revo2_touch():
# Get thumb tactile status
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
Note:
- A single call retrieves the tactile status of all fingers, resulting in higher efficiency
- Suitable for applications that require the integrated processing of tactile information from multiple fingers
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 finger ", " Middle finger ", " Ring finger ", " Little finger "]
"""
pass
def check_touch_sensor_health(self, slave_id: int) -> dict:
"""
Check the health status of the tactile sensor (utility function)
Args:
slave_id: Device Slave ID
Returns:
dict: Sensor Health Status Report
Example:
health_report = self.check_touch_sensor_health(slave_id=1)
print(f" Health Status Report: {health_report}")
"""
health_report = {
"enabled": False,
"fw_versions": [],
"sensor_status": [],
"errors": []
}
try:
# Check if tactile support is available
device_info = self.get_device_info(slave_id)
if not device_info.is_touch():
health_report["errors"].append("The device does not support haptic feedback ")
return health_report
# Check if enabled
touch_enabled = self.get_touch_sensor_enabled(slave_id)
if not touch_enabled:
health_report["errors"].append("Tactile sensor is not enabled ")
return health_report
# Get firmware version
health_report["fw_versions"] = self.get_touch_sensor_fw_versions(slave_id)
# Check the status of each sensor
touch_data = self.get_modulus_touch_data(slave_id)
finger_names = ["Thumb ", " Index finger ", " Middle finger ", " Ring finger ", " Little finger "]
for i, item in enumerate(touch_data.items):
finger_name = finger_names[i] if i < len(finger_names) else f" Finger {i}"
status = {
"finger": finger_name,
"normal": item.is_normal(),
"description": item.desc()
}
health_report["sensor_status"].append(status)
if not item.is_normal():
health_report["errors"].append(f"{finger_name} Sensor anomaly: {item.desc()}")
except Exception as e:
health_report["errors"].append(f" An error occurred during the check: {str(e)}")
return health_report
# ================================
# Using example
# ================================
def touch_sensor_basic_example():
"""
Basic Usage Example of Tactile Sensor
"""
slave_id = 1
device = modbus_open("/dev/ttyUSB0", Baudrate.Baud115200)
# Check if the device supports haptics
device_info = device.get_device_info(slave_id)
if not device_info.is_touch():
print("The device does not support haptic feedback ")
return
print(f" The device supports haptic features: {device_info.sku_type}")
# Enable the tactile sensor
print("Enable the tactile sensor...")
device.touch_sensor_setup(slave_id, bits=0x1F) # Enable all fingers
# Wait for initialization to complete
import time
time.sleep(2)
# Check if enabled successfully
if device.get_touch_sensor_enabled(slave_id):
print("Tactile sensor enabled successfully ")
# Get firmware version
versions = device.get_touch_sensor_fw_versions(slave_id)
print(f" Sensor Firmware Version: {versions}")
# Perform calibration
print("Perform calibration(Please ensure that your fingers are not touching any objects)...")
device.touch_sensor_calibrate(slave_id, bits=0x1F)
time.sleep(3)
print("Calibration completed ")
else:
print("Tactile sensor activation failed ")
return
# Data Acquisition Loop
print("Start acquiring tactile data...")
try:
while True:
finger_names = ["Thumb ", " Index finger ", " Middle finger ", " Ring finger ", " Little finger "]
# Obtain three-dimensional force data
touch_data = device.get_touch_sensor_status(slave_id)
# Get the original data
raw_data = device.get_touch_raw_data(slave_id)
except KeyboardInterrupt:
print("\\nStop acquiring tactile data")
Action Sequence - Gesture
# ================================
# Gesture Control Interface
# ================================
from typing import List, Sequence
from enum import Enum
# Action Sequence ID
class ActionSequenceId(Enum):
DefaultGestureOpen = ...
DefaultGestureFist = ...
DefaultGesturePinchTwo = ...
DefaultGesturePinchThree = ...
DefaultGesturePinchSide = ...
DefaultGesturePoint = ...
CustomGesture1 = ...
CustomGesture2 = ...
CustomGesture3 = ...
CustomGesture4 = ...
CustomGesture5 = ...
CustomGesture6 = ...
# Action sequence sub-item
class ActionSequenceItem:
index: int
duration: int
mode: int
positions: list[int]
durations: list[int]
speeds: list[int]
currents: list[int]
description: str
def desc(self) -> str: ...
# Action sequence
class ActionSequence:
action_id: ActionSequenceId
data: list[ActionSequenceItem]
description: str
def desc(self) -> str: ...
def get_action_sequence(self, slave_id: int, action_id: ActionSequenceId) -> ActionSequence:
"""
Get the gesture action sequence of the specified ID
Args:
slave_id: Device Slave ID
action_id: ActionSequenceId Enumeration value
- DefaultGestureOpen: Default open hand gesture
- DefaultGestureFist: Default fist gesture
- DefaultGesturePinchTwo: Default pinch-to-zoom
- DefaultGesturePinchThree: Default three-finger pinch
- DefaultGesturePinchSide: Default lateral pinch
- DefaultGesturePoint: Default pointing gesture
- CustomGesture1~6: Custom Gestures 1-6
Returns:
ActionSequence: Action Sequence Object,Contains action data and description
- action_id: ActionSequenceId - Action ID
- data: List[ActionSequenceItem] - Action sequence data
- description: str - Action Description
Example:
action = device.get_action_sequence(slave_id=1, action_id=ActionSequenceId.DefaultGestureFist)
print(f" Gesture description: {action.description}")
print(f" Number of action steps: {len(action.data)}")
"""
pass
def transfer_action_sequence(self, slave_id: int, action_id: ActionSequenceId,
sequences: Sequence[Sequence[int]]):
"""
Transfer and set the gesture action sequence to the device
Args:
slave_id: Device Slave ID
action_id: ActionSequenceId Enumeration value
sequences: Action sequence data, where each sequence includes parameters such as joint position, velocity, and current
Example:
# Define a simple fist-clenching action sequence
{
"index": 0, # Action Sequence Index
"duration_ms": 2000, # Action Duration (milliseconds)
"positions": [0, 0, 100, 100, 100, 100], # Joint position:[Thumb, Thumb assist, Index finger, Middle finger, Ring finger, Little finger]
},
device.transfer_action_sequence(
slave_id=1,
action_id=ActionSequenceId.CustomGesture1,
sequences=sequences
)
"""
pass
def run_action_sequence(self, slave_id: int, action_id: ActionSequenceId):
"""
Execute the specified sequence of gesture actions
Args:
slave_id: Device Slave ID
action_id: ActionSequenceId Enumeration value
Example:
# Execute the default fist gesture
device.run_action_sequence(slave_id=1, action_id=ActionSequenceId.DefaultGestureFist)
# Execute custom gesture
device.run_action_sequence(slave_id=1, action_id=ActionSequenceId.CustomGesture1)
"""
pass
Turbo Mode
# Turbo Pattern Configuration
class TurboConfig:
interval: int
duration: int
description: str
def desc(self) -> str: ...
def get_turbo_mode_enabled(self, slave_id: int) -> bool:
"""
Get whether the Turbo continuous grip mode is enabled
Args:
slave_id: Device Slave ID
Returns:
bool: True means Turbo mode is enabled, and False means Turbo mode is disabled.
Example:
turbo_enabled = device.get_turbo_mode_enabled(slave_id=1)
if turbo_enabled:
print("Turbo mode has been enabled ")
"""
pass
def set_turbo_mode_enabled(self, slave_id: int, enabled: bool):
"""
Set Turbo high-speed mode switch status
Args:
slave_id: Device Slave ID
enabled: Whether to enable Turbo mode
Example:
device.set_turbo_mode_enabled(slave_id=1, enabled=True)
"""
pass
def get_turbo_config(self, slave_id: int) -> TurboConfig:
"""
Get detailed configuration for Turbo mode
Args:
slave_id: Device Slave ID
Returns:
TurboConfig: Turbo configuration object, which includes the following properties:
- interval: int - Interval time (milliseconds)
- duration: int - Continuous grip time (milliseconds)
- description: str - Configuration description information
TurboConfig The provided method:
- desc() -> str: Get configuration description
Example:
turbo_config = device.get_turbo_config(slave_id=1)
print(f" Interval time: {turbo_config.interval}ms")
print(f" Hold on to time tightly: {turbo_config.duration}ms")
"""
pass
def set_turbo_config(self, slave_id: int, turbo_config: TurboConfig):
"""
Set Turbo mode configuration
Args:
slave_id: Device Slave ID
turbo_config: TurboConfig configuration object
Example:
config = TurboConfig(interval=100, duration=50)
device.set_turbo_config(slave_id=1, turbo_config=config)
"""
pass
More
# ================================
# Revo1 Dedicated Interface Document
# ================================
"""
This document demonstrates the unique interfaces of the Revo1 device, which are only applicable to Revo1 series devices。
Please confirm the device type before use:
device_info = device.get_device_info(slave_id=1)
if device_info.is_revo1():
print("This is the Revo1 device, which can use the interfaces in this document ")
elif device_info.is_revo1_touch():
print("This is the Revo1 Touch device, which supports haptic functionality ")
"""
import builtins
from typing import List, Sequence, Any, Optional
from enum import Enum
# ================================
# Get the voltage value
# ================================
def get_voltage(self, slave_id: int) -> float:
"""
Get the current battery voltage of the device
Args:
slave_id: Device Slave ID
Returns:
float: Voltage value (unit: mV)
示例:
voltage = device.get_voltage(slave_id=1)
print(f" Current battery voltage: {voltage:.2f}V")
"""
pass
# ================================
# LED Indicator Light Control Interface
# ================================
class LedColor(Enum):
Unchanged = ...
R = ...
G = ...
RG = ...
B = ...
RB = ...
GB = ...
RGB = ...
class LedMode(Enum):
Unknown = ...
Shutdown = ...
Keep = ...
Blink = ...
OneShot = ...
Blink0_5Hz = ...
Blink2Hz = ...
class LedInfo:
color: LedColor
mode: LedMode
description: builtins.str
def desc(self) -> builtins.str: ...
def get_led_info(self, slave_id: int) -> LedInfo:
"""
Get LED light status
Args:
slave_id: Device Slave ID
Returns:
LedInfo: LED status object, which includes the following properties:
- color: LedColor - LED Color
- mode: LedMode - LED flashing mode
- description: str - Status description
LedColor Enumeration value:
- Unchanged: Keep the original color
- R: Red
- G: Green
- RG: Red and green (yellow)
- B: Blue
- RB: Red and blue (purple)
- GB: Green-blue (cyan)
- RGB: Red, green, blue (white)
LedMode Enumeration value:
- Unknown: Unknown pattern
- Shutdown: Close
- Keep: Constantly lit
- Blink: Flashing
- OneShot: Single flash
- Blink0_5Hz: Flashing at a frequency of 0.5Hz
- Blink2Hz: 2Hz frequency flicker
LedInfo The provided method:
- desc() -> str: Get LED status description
Example:
led_info = device.get_led_info(slave_id=1)
print(f"LED Color: {led_info.color}")
print(f"LED Pattern: {led_info.mode}")
print(f" Status Description: {led_info.desc()}")
"""
pass
def set_led_info(self, slave_id: int, led_info: "LedInfo"):
"""
Set LED light status
Args:
slave_id: Device Slave ID
led_info: LedInfo Object,Contains color and pattern settings
Example:
# Set red constant on
led_info = LedInfo(color=LedColor.R, mode=LedMode.Keep)
device.set_led_info(slave_id=1, led_info=led_info)
# Set green flashing
led_info = LedInfo(color=LedColor.G, mode=LedMode.Blink)
device.set_led_info(slave_id=1, led_info=led_info)
# Set blue 2Hz frequency flashing
led_info = LedInfo(color=LedColor.B, mode=LedMode.Blink2Hz)
device.set_led_info(slave_id=1, led_info=led_info)
# Close LED
led_info = LedInfo(color=LedColor.Unchanged, mode=LedMode.Shutdown)
device.set_led_info(slave_id=1, led_info=led_info)
"""
pass
# ================================
# Button Event Interface
# ================================
class PressState(Enum):
Unknown = ...
Down = ...
Up = ...
class ButtonPressEvent:
timestamp: builtins.int
button_id: builtins.int
press_state: PressState
description: builtins.str
def get_button_event(self, slave_id: int) -> ButtonPressEvent:
"""
Get the status of the back-of-hand button event
Args:
slave_id: Device Slave ID
Returns:
ButtonPressEvent: Button event object, which contains the following properties:
- timestamp: int - Event timestamp (milliseconds)
- button_id: int - Button ID (usually 0)
- press_state: PressState - Press state
- description: str - Event Description
PressState Enumeration value:
- Unknown: Unknown status
- Down: Button pressed
- Up: Button release
ButtonPressEvent The provided method:
- desc() -> str: Get event description
Note:
- This interface needs to be polled to detect button events
- The event status will be cleared after reading
- Timestamps can be used to determine the newness or age of events
Example:
# Detect button event
button_event = device.get_button_event(slave_id=1)
if button_event.press_state == PressState.Down:
print(f" The button was pressed at {button_event.timestamp}ms ")
# Handle button press events here
elif button_event.press_state == PressState.Up:
print(f" The button was released at {button_event.timestamp}ms ")
# Handle the button release event here
# Button Event Handling Example
def handle_button_events():
while True:
event = device.get_button_event(slave_id=1)
if event.press_state == PressState.Down:
# Execute a fist gesture when the button is pressed
device.run_action_sequence(slave_id=1, action_id=ActionSequenceId.DefaultGestureFist)
elif event.press_state == PressState.Up:
# Execute the spread gesture when the button is released
device.run_action_sequence(slave_id=1, action_id=ActionSequenceId.DefaultGestureOpen)
time.sleep(0.01) # 10ms Polling interval
"""
pass
# ================================
# Status Indicator LED Quick Settings
# ================================
def set_status_led_normal(self, slave_id: int):
"""
Set the LED to normal status (green constant on)
Args:
slave_id: Device Slave ID
Example:
device.set_status_led_normal(slave_id=1)
"""
led_info = LedInfo(color=LedColor.G, mode=LedMode.Keep)
self.set_led_info(slave_id, led_info)
def set_status_led_warning(self, slave_id: int):
"""
Set the LED to warning status (flashing yellow)
Args:
slave_id: Device Slave ID
Example:
device.set_status_led_warning(slave_id=1)
"""
led_info = LedInfo(color=LedColor.RG, mode=LedMode.Blink)
self.set_led_info(slave_id, led_info)
def set_status_led_error(self, slave_id: int):
"""
Set the LED to error status (red fast flashing)
Args:
slave_id: Device Slave ID
Example:
device.set_status_led_error(slave_id=1)
"""
led_info = LedInfo(color=LedColor.R, mode=LedMode.Blink2Hz)
self.set_led_info(slave_id, led_info)
def set_status_led_off(self, slave_id: int):
"""
Turn off the LED
Args:
slave_id: Device Slave ID
Example:
device.set_status_led_off(slave_id=1)
"""
led_info = LedInfo(color=LedColor.Unchanged, mode=LedMode.Shutdown)
self.set_led_info(slave_id, led_info)
# ================================
# Using example
# ================================
def revo1_example_usage():
"""
Revo1 Device Usage Example
"""
# Connect the device
device = modbus_open("/dev/ttyUSB0", Baudrate.Baud115200)
slave_id = 1
# Check device type
device_info = device.get_device_info(slave_id)
if not device_info.is_revo1():
print("Error:This is not a Revo1 device ")
return
print(f" Connect to Revo1 device,Serial number: {device_info.serial_number}")
# Check battery voltage
voltage = device.get_voltage(slave_id)
print(f" Battery voltage: {voltage:.2f}V")
if voltage < 6.5:
# Set the red LED to warn when the voltage is low
device.set_status_led_error(slave_id)
print("Warning: Battery voltage is too low ")
else:
# Set the green LED when the voltage is normal
device.set_status_led_normal(slave_id)
# Button event handling loop
print("Start listening for button events...")
try:
while True:
# Check button event
button_event = device.get_button_event(slave_id)
if button_event.press_state == PressState.Down:
print("Button pressed - Make a fist gesture ")
device.run_action_sequence(slave_id, ActionSequenceId.DefaultGestureFist)
# Set the blue LED to indicate action execution
device.set_status_led_info(slave_id, LedInfo(LedColor.B, LedMode.Keep))
elif button_event.press_state == PressState.Up:
print("Button release - Perform the open hand gesture ")
device.run_action_sequence(slave_id, ActionSequenceId.DefaultGestureOpen)
# Restore green LED
device.set_status_led_normal(slave_id)
# If it is the Touch version, it can also process tactile data
if device_info.is_revo1_touch():
if device.get_touch_sensor_enabled(slave_id):
raw_data = device.get_touch_raw_data(slave_id)
# Processing tactile data...
time.sleep(0.01) # 10ms Polling interval
except KeyboardInterrupt:
print("\\nProgram ends")
device.set_status_led_off(slave_id)