Skip to content

4. SDK接口调用说明

📋 目录

  1. 硬件安装方法说明
  2. SDK安装和配置方法
  3. 例程编译和调用方法
  4. SDK接口调用说明
  5. SDK打印内容说明
  6. 连接状态查询与故障排查

完整手册


4.1 核心类和方法

4.1.1 SerialInterface 类

串口通信接口类,处理底层 Modbus RTU 硬件通信。

python
from psi_glove_sdk import SerialInterface

# 创建串口接口
serial = SerialInterface(
    port="/dev/ttyACM0",   # 串口路径(Linux: /dev/ttyACM0,Windows: COM3)
    baudrate=115200,       # 波特率,常用 115200 / 500000 / 921600
    timeout=0.01,         # 读超时(秒)
    auto_connect=False,    # 是否在构造时自动连接
    mock=False,            # 模拟模式(无真实硬件,用于测试)
    write_timeout=None,    # 写超时;默认 None 表示 max(1.0, timeout×10)
)
参数类型默认说明
portstr必填串口设备路径
baudrateint115200通信波特率
timeoutfloat0.01读超时(秒)
auto_connectboolFalse构造时是否自动connect()
mockboolFalse模拟模式
write_timeoutfloat| NoneNone写超时;LRA 连续下发时建议保持默认

4.1.2 PSIGloveController 类

主控制器类,管理连接、Modbus 读关节、移动平均平滑、LRA 触觉下发。

python
from psi_glove_sdk import PSIGloveController, ADCCalibrationParams

# 创建控制器
controller = PSIGloveController(
    communication_interface=serial,       # 通信接口实例
    smoothing_window_size=10,           # 平滑窗口(样本数)
    adc_calibration=ADCCalibrationParams()  # 可选 ADC 校准,默认内置参数
)

核心方法:

方法名称返回类型功能说明
connect()bool连接设备,成功返回True
disconnect()None断开设备连接
is_connected()bool检查是否已连接
loop()Optional[StatusMessage]读取关节数据并更新缓存,失败返回None
read_joint_positions()Optional[StatusMessage]仅读取一帧,不经过loop() 的缓存语义
get_last_status()Optional[StatusMessage]获取最后一次成功读取的数据
play_lra(modes, amplitudes, slave_id=1)boolLRA 线性马达控制(Modbus 写0xB0

4.2 读取主手数据的完整流程

python
#!/usr/bin/env python3
from psi_glove_sdk import PSIGloveController, SerialInterface, StatusMessage
import time

# 步骤 1: 创建串口接口
serial = SerialInterface(
    port="/dev/ttyACM0",
    baudrate=115200,
    timeout=0.006,
)

# 步骤 2: 创建控制器
controller = PSIGloveController(
    communication_interface=serial,
    smoothing_window_size=10,
)

# 步骤 3: 连接设备
if not controller.connect():
    print("错误: 无法连接到设备")
    exit(1)

print("连接成功!")

# 步骤 4: 循环读取数据
try:
    while True:
        # 读取数据(自动 Modbus 解析 + 平滑)
        status: StatusMessage = controller.loop()

        if status:
            print(f"拇指关节 (6): {status.thumb}")
            print(f"食指关节 (4): {status.index}")
            print(f"中指关节 (4): {status.middle}")
            print(f"无名指关节 (4): {status.ring}")
            print(f"小指关节 (4): {status.pinky}")

            # 22 路关节扁平列表
            all_joints = status.to_list()
            print(f"所有关节 (22个): {all_joints}")
        else:
            last_status = controller.get_last_status()
            if last_status:
                print("读取失败,使用缓存数据")

        time.sleep(0.01)  # 约 100 Hz

except KeyboardInterrupt:
    print("\n用户中断")

finally:
    controller.disconnect()
    print("已断开连接")

4.3 数据格式和单位

StatusMessage 数据结构

Air Hand V2 提供 22 路关节 ADC。StatusMessagejoints 为主字段,并通过属性访问各指数据:

python
from dataclasses import dataclass
from typing import List

@dataclass
class StatusMessage:
    joints: List[int]  # 22 个关节 ADC,顺序见下表

    @property
    def thumb(self) -> List[int]: ...   # joints[0:6],6 路
    @property
    def index(self) -> List[int]: ...   # joints[6:10]
    @property
    def middle(self) -> List[int]: ...  # joints[10:14]
    @property
    def ring(self) -> List[int]: ...    # joints[14:18]
    @property
    def pinky(self) -> List[int]: ...   # joints[18:22]

    def to_list(self) -> List[int]:
        """返回 22 个关节值的列表"""
        return list(self.joints)

    def to_dict(self) -> dict:
        """返回含 joints 与各指属性的字典"""
        ...

关节索引映射

手指关节数量joints 索引访问方式顺序说明
拇指60–5status.thumbtip, mid, back, back2, side, rotate
食指46–9status.indextip, mid, back, side
中指410–13status.middletip, mid, back, side
无名指414–17status.ringtip, mid, back, side
小指418–21status.pinkytip, mid, back, side

数据单位和范围

项目说明
数据类型int
数值范围0 – 4095(12 位 ADC)
物理含义关节传感器经 ADC 转换后的原始码值(经 SDK 可选校准与平滑)

数值对应(示意,实际以手套校准为准):

  • 0:传感器低端(通常接近伸直)
  • 2048:中间量程
  • 4095:传感器高端(通常接近弯曲)

数据处理示例:

python
def normalize_joint(value: int) -> float:
    """将 ADC 值归一化到 [0.0, 1.0]"""
    return value / 4095.0

def adc_to_angle(
    value: int,
    min_adc: int,
    max_adc: int,
    min_angle: float,
    max_angle: float,
) -> float:
    """将 ADC 值线性映射到角度(需根据手套标定 min/max)"""
    normalized = (value - min_adc) / (max_adc - min_adc)
    return min_angle + normalized * (max_angle - min_angle)

status = controller.loop()
if status:
    normalized = normalize_joint(status.thumb[0])
    print(f"拇指指尖归一化: {normalized:.3f}")

    angle = adc_to_angle(status.thumb[0], 500, 3500, 0.0, 90.0)
    print(f"拇指指尖角度: {angle:.2f}°")

4.4 高级用法示例

4.4.1 批量数据采集

python
import time
import csv
from psi_glove_sdk import PSIGloveController, SerialInterface

serial = SerialInterface("/dev/ttyACM0", 115200)
controller = PSIGloveController(serial, smoothing_window_size=10)
controller.connect()

data_buffer = []
for _ in range(1000):
    status = controller.loop()
    if status:
        data_buffer.append(status.to_list())  # 每行 22 个关节
    time.sleep(0.01)

with open("glove_data.csv", "w", newline="", encoding="utf-8") as f:
    writer = csv.writer(f)
    writer.writerow([f"joint_{i}" for i in range(22)])
    writer.writerows(data_buffer)

print(f"已保存 {len(data_buffer)} 帧到 glove_data.csv")
controller.disconnect()

也可使用官方 advanced_usage.py,第三个参数传入 --log 自动生成带时间戳的 CSV。

4.4.2 多手套同时使用

每只手套使用独立 SerialInterfacePSIGloveController

python
from psi_glove_sdk import PSIGloveController, SerialInterface
import time

left_serial = SerialInterface("/dev/ttyACM0", 115200)
left_controller = PSIGloveController(left_serial)
left_controller.connect()

right_serial = SerialInterface("/dev/ttyACM1", 115200)
right_controller = PSIGloveController(right_serial)
right_controller.connect()

try:
    while True:
        left_status = left_controller.loop()
        right_status = right_controller.loop()

        if left_status and right_status:
            print(
                f"左手拇指尖: {left_status.thumb[0]}, "
                f"右手拇指尖: {right_status.thumb[0]}"
            )
        time.sleep(0.01)
finally:
    left_controller.disconnect()
    right_controller.disconnect()

4.4.3 获取指尖位姿

通过 FingertipPoseCalculator22 路 ADC 映射为 URDF 关节角,再用 MuJoCo 正运动学得到五指指尖 4×4 齐次位姿(形状 (5, 4, 4),顺序:thumb → index → middle → ring → little)。

依赖:

bash
pip install mujoco numpy ruamel.yaml
# 或:pip install -e ".[fingertip]"
方法说明
connect() / disconnect()连接/断开内部PSIGloveController
get_fingertip_poses(joint_data_22)由 22 路 ADC 仅做 FK,返回(5, 4, 4)
update_and_get_poses()读一帧 ADC 并 FK,返回(poses, urdf_angles, adc) 或全 None

单手:实时读取

python
import time
from psi_glove_sdk import FingertipPoseCalculator

calc = FingertipPoseCalculator(port="/dev/ttyACM0", baudrate=115200, hand="right")
if not calc.connect():
    raise RuntimeError("连接失败")

try:
    while True:
        poses, urdf_angles, adc = calc.update_and_get_poses()
        if poses is not None:
            print(f"拇指尖 xyz (m): {poses[0][:3, 3]}")
        time.sleep(0.01)
finally:
    calc.disconnect()

双手:同步获取指尖位姿

每只手一个串口、一个 FingertipPoseCalculatorhand="left" / "right"):

python
import time
from psi_glove_sdk import FingertipPoseCalculator

left_calc = FingertipPoseCalculator(
    port="/dev/ttyACM0", baudrate=115200, hand="left",
)
right_calc = FingertipPoseCalculator(
    port="/dev/ttyACM1", baudrate=115200, hand="right",
)

if not left_calc.connect() or not right_calc.connect():
    raise RuntimeError("双手连接失败")

try:
    while True:
        left_poses, _, _ = left_calc.update_and_get_poses()
        right_poses, _, _ = right_calc.update_and_get_poses()

        if left_poses is not None and right_poses is not None:
            print("左拇指尖:", left_poses[0][:3, 3])
            print("右拇指尖:", right_poses[0][:3, 3])
        time.sleep(0.01)
except KeyboardInterrupt:
    pass
finally:
    left_calc.disconnect()
    right_calc.disconnect()

说明: 构造时会尝试从设备读校准并更新 master_slave_config URDF.yaml。可视化见 test_fingertip_visualizer.py例程 3.2.4

4.5 LRA 线性马达 play_lra

通过 Modbus 功能码 16 写寄存器 0xB0,载荷 10 字节:[m0,a0, m1,a1, …, m4,a4](拇指→小指,均为 uint8)。

python
from psi_glove_sdk import REG_LRA_CTRL, LRA_AMPLITUDE_MAX

modes = [5, 0, 0, 0, 0]   # 拇指波形 5,其余指关闭
amps  = [32, 0, 0, 0, 0]  # 振幅 0–255

ok = controller.play_lra(modes, amps, slave_id=0x01)
参数范围说明
finger_modes长度 5,每元素 0–100 关闭该指;1–10 波形编号
finger_amplitudes长度 5,每元素 0–255播放增益;0 无驱动
slave_id1–247Modbus 从机地址,默认1

停止全部 LRA:

python
controller.play_lra([0, 0, 0, 0, 0], [0, 0, 0, 0, 0])

TIP

mode=1 表示「波形 1」,不是「振幅 1」。低强度请减小 amplitude(如 1–64)。

4.6 配置与 URDF 路径

python
from psi_glove_sdk import (
    load_config,
    save_config,
    get_default_config_path,
    get_configs_dir,
    get_synglove_urdf_root,
    get_resources_root,
)

cfg = load_config()
urdf_root = get_synglove_urdf_root()
  • 开发安装:配置在 python_sdk/configs/,URDF 在仓库 SynGlove_Air_URDF/
  • 离线 wheel:资源在 psi_glove_sdk/resources/ 内。

4.7 FingertipPoseCalculator

将手套 ADC 映射为 URDF/MuJoCo 指尖位姿,需额外安装 mujoconumpyruamel.yaml

python
from psi_glove_sdk import FingertipPoseCalculator

calc = FingertipPoseCalculator(
    port="/dev/ttyACM0",
    baudrate=115200,
    hand="right",
)

详见 test_fingertip_visualizer.py例程 3.2.4

4.8 Modbus 协议摘要

功能功能码地址/说明
读关节0x03读 22 寄存器 →StatusMessageRequestType.READ_JOINT_POSITION
LRA 播放0x100xB0,10 字节 [m0,a0,…,m4,a4]

通信格式:Modbus RTU,帧尾 CRC16。