Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

安装

从 PyPI 安装:

# pip
pip install linkerbot-py

# uv
uv add linkerbot-py

从 Git 仓库安装:

# pip
pip install git+https://github.com/linker-bot/linkerbot-python-sdk.git

# uv
uv add "linkerbot-py @ git+https://github.com/linker-bot/linkerbot-python-sdk"

可选依赖

Kinetix(运动学,机械臂必需)

机械臂(A7 / A7 Lite)依赖 Pinocchio 进行运动学计算,需要额外安装:

# pip (Linux / macOS)
pip install linkerbot-py[kinetix]

# uv
uv add linkerbot-py --extra kinetix

Windows 用户注意:Pinocchio 不支持在 Windows 上通过 pip 安装。请使用 Conda:

conda install pinocchio -c conda-forge

如果只使用灵巧手(L6 / L20 Lite / L25 等),无需安装此依赖。

快速开始

连接灵巧手

from linkerbot import L6

with L6(side="left", interface_name="can0") as hand:
    # 控制和读取灵巧手
    pass

使用 with 语句确保资源正确释放。

控制关节角度

设置 6 个关节的目标角度(范围 0-100):

from linkerbot import L6

with L6(side="left", interface_name="can0") as hand:
    # 张开手掌
    hand.angle.set_angles([100, 50, 100, 100, 100, 100])

    # 握拳
    hand.angle.set_angles([0, 0, 0, 0, 0, 0])

关节顺序:[拇指弯曲, 拇指侧摆, 食指, 中指, 无名指, 小指]

读取角度

with L6(side="left", interface_name="can0") as hand:
    # 阻塞读取
    data = hand.angle.get_blocking(timeout_ms=100)
    print(f"当前角度:{data.angles.to_list()}")

    # 访问单个关节
    print(f"食指:{data.angles.index}")

读取力传感器

获取 5 个手指的力传感器数据:

with L6(side="left", interface_name="can0") as hand:
    data = hand.force_sensor.get_blocking(timeout_ms=1000)

    # 访问各手指数据
    print(f"拇指:{data.thumb.values.shape}")  # (12, 6)
    print(f"食指:{data.index.values.shape}")

流式读取

通过统一事件流持续接收数据。初始化时会自动以默认间隔启动轮询(角度 60 Hz、力传感器 30 Hz),也可手动调用 start_polling() 覆盖:

from linkerbot.hand.l6 import SensorSource, AngleEvent

with L6(side="left", interface_name="can0") as hand:
    # 初始化已自动启动默认轮询(角度 + 力传感器)
    # 如需自定义,可再次调用 start_polling() 覆盖
    hand.start_polling({SensorSource.ANGLE: 0.1})

    for event in hand.stream():
        match event:
            case AngleEvent(data=data):
                print(f"角度:{data.angles.to_list()}")

        if should_stop():
            break

    hand.stop_polling()
    hand.stop_stream()

完整示例

from linkerbot import L6
import time

with L6(side="left", interface_name="can0") as hand:
    # 设置速度
    hand.speed.set_speeds([50, 50, 50, 50, 50, 50])

    # 张开
    hand.angle.set_angles([100, 50, 100, 100, 100, 100])
    time.sleep(1)

    # 握拳
    hand.angle.set_angles([0, 0, 0, 0, 0, 0])
    time.sleep(1)

    # 读取状态
    angles = hand.angle.get_blocking()
    temps = hand.temperature.get_blocking()

    print(f"角度:{angles.angles.to_list()}")
    print(f"温度:{temps.temperatures.to_list()} °C")

下一步

基础知识

架构概览

SDK 采用模块化设计,通过主类访问各功能模块:

L6
├── hand.angle          # 角度控制
├── hand.speed          # 速度控制
├── hand.torque         # 扭矩控制
├── hand.current        # 电流读取
├── hand.temperature    # 温度读取
├── hand.force_sensor   # 力传感器
├── hand.fault          # 故障管理
└── hand.version        # 版本信息

调用方式

所有功能通过 hand.模块.方法() 调用:

from linkerbot import L6

with L6(side="left", interface_name="can0") as hand:
    hand.angle.set_angles([50, 50, 50, 50, 50, 50])
    hand.speed.set_speeds([100, 100, 100, 100, 100, 100])

    data = hand.temperature.get_blocking()

类型安全

SDK 提供类型化的数据类,支持 IDE 自动补全:

from linkerbot import L6
from linkerbot.hand.l6 import L6Angle

with L6(side="left", interface_name="can0") as hand:
    # 使用数据类(推荐,有类型提示)
    angle = L6Angle(thumb_flex=50, thumb_abd=30, index=60, middle=60, ring=60, pinky=60)
    hand.angle.set_angles(angle)

    # 或使用列表(简洁)
    hand.angle.set_angles([50, 30, 60, 60, 60, 60])

数据类属性:

属性说明
thumb_flex拇指弯曲
thumb_abd拇指侧摆
index食指
middle中指
ring无名指
pinky小指

数据读取模式

阻塞读取

发送请求并等待响应:

data = hand.angle.get_blocking(timeout_ms=100)

缓存读取

获取最近一次接收的数据(非阻塞):

data = hand.angle.get_snapshot()
if data is not None:
    print(data.angles)

流式读取

通过统一事件流持续接收数据:

from linkerbot.hand.l6 import SensorSource, AngleEvent

hand.start_polling({SensorSource.ANGLE: 0.1})

for event in hand.stream():
    match event:
        case AngleEvent(data=data):
            print(data.angles)
    if should_stop():
        break

hand.stop_polling()
hand.stop_stream()

快照

获取所有传感器的最新缓存数据:

snap = hand.get_snapshot()
print(snap.angle)  # AngleData | None
print(snap.temperature)  # TemperatureData | None

资源管理

使用 with 语句自动管理资源:

with L6(side="left", interface_name="can0") as hand:
    # 使用灵巧手
    pass
# 自动释放资源

或手动管理:

hand = L6(side="left", interface_name="can0")
try:
    # 使用灵巧手
    pass
finally:
    hand.close()

异常处理

from linkerbot import L6
from linkerbot.exceptions import TimeoutError, ValidationError

with L6(side="left", interface_name="can0") as hand:
    try:
        data = hand.angle.get_blocking(timeout_ms=100)
    except TimeoutError:
        print("读取超时")
    except ValidationError as e:
        print(f"参数错误:{e}")
异常说明
TimeoutError通信超时
ValidationError参数验证失败
StateError状态错误(如接口已关闭)

关节索引

所有 6 关节模块使用相同的索引顺序:

索引名称标识
0拇指弯曲thumb_flex
1拇指侧摆thumb_abd
2食指index
3中指middle
4无名指ring
5小指pinky

CAN 总线

L6/O6 灵巧手通过 CAN 总线通信。构造函数中的 interface_type 参数指定 CAN 适配器类型。

构造参数

from linkerbot import L6

hand = L6(
    side="left",  # "left" 或 "right"
    interface_name="can0",  # 接口名称
    interface_type="socketcan",  # 适配器类型
)
参数说明
side左手 "left" 或右手 "right"
interface_name接口名称,取决于操作系统和适配器
interface_typeCAN 适配器类型,默认 "socketcan"

Linux (SocketCAN)

Linux 默认使用 SocketCAN,无需指定 interface_type

hand = L6(side="left", interface_name="can0")

配置 CAN 接口:

sudo ip link set can0 type can bitrate 1000000
sudo ip link set can0 up

连接多个 CAN 接口:Linux 系统会根据接口创建时间自动递增来命名接口,即根据 CAN 连接到主控板的顺序,依次命名为 can0can1can2,以此类推。可以串联依次执行以下命令启动多个 CAN 接口:

sudo ip link set can0 type can bitrate 1000000 && sudo ip link set can0 up && \
sudo ip link set can1 type can bitrate 1000000 && sudo ip link set can1 up && \
sudo ip link set can2 type can bitrate 1000000 && sudo ip link set can2 up
# ...

Windows (PCAN)

Windows 使用 PCAN 适配器:

hand = L6(side="left", interface_name="PCAN_USBBUS1", interface_type="pcan")

其他适配器类型参考 python-can 文档

L6 灵巧手

快速开始

from linkerbot import L6

with L6(side="left", interface_name="can0") as hand:
    # 设置角度
    hand.angle.set_angles((10, 20, 30, 40, 50, 60))

    # 读取角度
    data = hand.angle.get_blocking(timeout_ms=500)
    print(data.angles)

构造参数

参数类型说明
side"left" | "right"左手或右手
interface_namestrCAN 接口名,如 "can0"
interface_typestrCAN 接口类型,默认 "socketcan"。Windows 用法参考 CAN 总线 文档

关节说明

索引名称标识
0拇指弯曲thumb_flex
1拇指侧摆thumb_abd
2食指index
3中指middle
4无名指ring
5小指pinky

功能模块

模块说明文档
hand.angle关节角度控制与读取angle
hand.speed运动速度控制speed
hand.torque扭矩控制torque
hand.force_sensor力传感器数据force-sensor
hand.temperature温度监测temperature
hand.current电流监测current
hand.fault故障检测与清除fault
hand.version设备版本信息version

统一流式读取

L6 提供统一的事件流接口,通过 hand.stream()hand.start_polling() 获取所有传感器数据。初始化时会自动以默认间隔启动轮询(角度 60 Hz、力传感器 30 Hz),无需手动调用 start_polling()

from linkerbot import L6
from linkerbot.hand.l6 import SensorSource, AngleEvent, TemperatureEvent

with L6(side="left", interface_name="can0") as hand:
    # 可同时指定多个传感器及各自的轮询间隔(秒)
    # 再次调用 start_polling() 会覆盖之前的设置
    hand.start_polling({SensorSource.ANGLE: 0.05, SensorSource.TEMPERATURE: 1.0})

    for event in hand.stream():
        match event:
            case AngleEvent(data=ad):
                print(f"角度:{ad.angles.to_list()}")
            case TemperatureEvent(data=td):
                print(f"温度:{td.temperatures.to_list()}")

    hand.stop_polling()
    hand.stop_stream()

SensorSource 可选值及默认频率

说明默认频率
SensorSource.ANGLE角度60 Hz
SensorSource.TORQUE扭矩不默认轮询
SensorSource.TEMPERATURE温度不默认轮询
SensorSource.CURRENT电流不默认轮询
SensorSource.FAULT故障不默认轮询
SensorSource.FORCE_SENSOR力传感器30 Hz

快照

获取所有传感器最新缓存数据:

snap = hand.get_snapshot()
print(snap.angle)  # AngleData | None
print(snap.temperature)  # TemperatureData | None

角度控制

通过 hand.angle 控制和读取 L6 灵巧手的 6 个关节电机角度。

  • 角度范围: 0-100
  • 单位: 无量纲(映射到关节电机实际角度)

设置角度

from linkerbot import L6
from linkerbot.hand.l6 import L6Angle

# 使用列表
hand.angle.set_angles([50.0, 30.0, 60.0, 60.0, 60.0, 60.0])

# 使用 L6Angle 对象
angles = L6Angle(
    thumb_flex=50.0,  # 拇指屈曲
    thumb_abd=30.0,  # 拇指侧摆
    index=60.0,  # 食指
    middle=60.0,  # 中指
    ring=60.0,  # 无名指
    pinky=60.0,  # 小指
)
hand.angle.set_angles(angles)

读取角度

阻塞读取

from linkerbot import L6
from linkerbot.exceptions import TimeoutError

try:
    data = hand.angle.get_blocking(timeout_ms=500)
    print(f"拇指屈曲:{data.angles.thumb_flex}")
    print(f"全部角度:{data.angles.to_list()}")
except TimeoutError:
    print("读取超时")

缓存读取

data = hand.angle.get_snapshot()
if data:
    print(f"角度:{data.angles.to_list()}")
    print(f"时间戳:{data.timestamp}")

流式读取

通过顶层 hand.stream() 统一接收所有传感器事件:

from linkerbot.hand.l6 import SensorSource, AngleEvent

hand.start_polling({SensorSource.ANGLE: 0.1})

for event in hand.stream():
    match event:
        case AngleEvent(data=data):
            print(f"角度:{data.angles.to_list()}")

hand.stop_polling()
hand.stop_stream()

完整示例

from linkerbot import L6

with L6(side="left", interface_name="can0") as hand:
    # 设置角度
    hand.angle.set_angles([0.0, 0.0, 0.0, 0.0, 0.0, 0.0])

    # 读取当前角度
    data = hand.angle.get_blocking(timeout_ms=500)
    print(f"当前角度:{data.angles.to_list()}")

    # 渐进移动
    for i in range(0, 101, 10):
        hand.angle.set_angles([float(i)] * 6)

速度控制

控制 L6 灵巧手各关节电机的运动速度。

设置速度

hand.speed.set_speeds(speeds)

设置 6 个关节电机的目标速度。

参数:

  • speeds: L6Speed 实例或 6 元素列表,速度范围 0-100(无单位)

手指顺序: 拇指弯曲、拇指侧摆、食指、中指、无名指、小指

示例

列表方式

from linkerbot import L6

with L6(side="left", interface_name="can0") as hand:
    # 所有手指设为中速
    hand.speed.set_speeds([50.0, 50.0, 50.0, 50.0, 50.0, 50.0])

L6Speed 方式

from linkerbot import L6
from linkerbot.hand.l6 import L6Speed

with L6(side="left", interface_name="can0") as hand:
    # 拇指慢速,其他手指快速
    speed = L6Speed(
        thumb_flex=30.0, thumb_abd=30.0, index=80.0, middle=80.0, ring=80.0, pinky=80.0
    )
    hand.speed.set_speeds(speed)

扭矩控制

概述

通过 hand.torque 访问扭矩控制功能,支持设置目标扭矩和读取当前扭矩。

扭矩值范围为 0-100(无量纲,表示关节电机的相对扭矩百分比),对应 L6 灵巧手的 6 个关节电机:

索引属性关节电机
0thumb_flex拇指弯曲
1thumb_abd拇指侧摆
2index食指
3middle中指
4ring无名指
5pinky小指

设置扭矩

# 使用列表
hand.torque.set_torques([50.0, 30.0, 60.0, 60.0, 60.0, 60.0])

# 使用 L6Torque 对象
from linkerbot.hand.l6 import L6Torque

target = L6Torque(
    thumb_flex=50.0, thumb_abd=30.0, index=60.0, middle=60.0, ring=60.0, pinky=60.0
)
hand.torque.set_torques(target)

读取扭矩

阻塞读取

发送请求并等待响应:

from linkerbot.exceptions import TimeoutError

try:
    data = hand.torque.get_blocking(timeout_ms=500)
    print(f"拇指弯曲:{data.torques.thumb_flex}")
    print(f"食指:{data.torques.index}")
except TimeoutError:
    print("读取超时")

缓存读取

获取最近一次的缓存数据(非阻塞):

data = hand.torque.get_snapshot()
if data:
    print(f"扭矩:{data.torques.to_list()}")

流式读取

通过顶层 hand.stream() 统一接收所有传感器事件:

from linkerbot.hand.l6 import SensorSource, TorqueEvent

hand.start_polling({SensorSource.TORQUE: 0.05})

for event in hand.stream():
    match event:
        case TorqueEvent(data=data):
            print(f"扭矩:{data.torques.to_list()}")

hand.stop_polling()
hand.stop_stream()

示例

完整控制流程

from linkerbot import L6
from linkerbot.hand.l6 import L6Torque

with L6(side="left", interface_name="can0") as hand:
    # 设置扭矩
    hand.torque.set_torques([50.0, 30.0, 60.0, 60.0, 60.0, 60.0])

    # 阻塞读取当前扭矩
    data = hand.torque.get_blocking(timeout_ms=200)
    print(f"当前扭矩:{data.torques.to_list()}")

实时监控

from linkerbot import L6
from linkerbot.hand.l6 import SensorSource, TorqueEvent

with L6(side="left", interface_name="can0") as hand:
    hand.start_polling({SensorSource.TORQUE: 0.1})

    try:
        for event in hand.stream():
            match event:
                case TorqueEvent(data=data):
                    if data.torques.index > 80:
                        print("食指关节电机扭矩过高")
                        break
    finally:
        hand.stop_polling()
        hand.stop_stream()

电流读取

from linkerbot import L6

读取 L6 灵巧手六个关节电机的实时电流数据(单位:mA)。

概述

通过 hand.current 访问电流读取功能,支持三种模式:

模式方法用途
阻塞读取get_blocking()单次查询
流式读取hand.stream()持续监测
缓存读取get_snapshot()读取最近缓存

读取电流

阻塞读取

发送请求并等待响应:

data = hand.current.get_blocking(timeout_ms=500)

# 访问各手指电流 (单位:mA)
print(data.currents.thumb_flex)  # 拇指弯曲
print(data.currents.thumb_abd)  # 拇指侧摆
print(data.currents.index)  # 食指
print(data.currents.middle)  # 中指
print(data.currents.ring)  # 无名指
print(data.currents.pinky)  # 小指

# 索引访问
print(data.currents[0])  # thumb_flex

参数:

  • timeout_ms: 超时时间 (毫秒),默认 100

异常:

  • TimeoutError: 超时未收到响应

缓存读取

获取最近一次缓存的数据,不发送请求:

data = hand.current.get_snapshot()
if data:
    print(f"电流:{data.currents.to_list()}")

流式读取

通过顶层 hand.stream() 统一接收所有传感器事件:

from linkerbot.hand.l6 import SensorSource, CurrentEvent

hand.start_polling({SensorSource.CURRENT: 0.05})

for event in hand.stream():
    match event:
        case CurrentEvent(data=data):
            print(f"电流:{data.currents.to_list()}")

hand.stop_polling()
hand.stop_stream()

示例

检测过载电流

from linkerbot import L6
from linkerbot.hand.l6 import SensorSource, CurrentEvent

with L6(side="left", interface_name="can0") as hand:
    hand.start_polling({SensorSource.CURRENT: 0.05})

    try:
        for event in hand.stream():
            match event:
                case CurrentEvent(data=data):
                    for i, current in enumerate(data.currents.to_list()):
                        if current > 1000:
                            print(f"警告:关节 {i} 电流过高 ({current} mA)")
    finally:
        hand.stop_polling()
        hand.stop_stream()

记录电流数据

import time
from linkerbot import L6
from linkerbot.hand.l6 import SensorSource, CurrentEvent

with L6(side="left", interface_name="can0") as hand:
    records = []
    start = time.time()
    hand.start_polling({SensorSource.CURRENT: 0.1})

    try:
        for event in hand.stream():
            match event:
                case CurrentEvent(data=data):
                    records.append(
                        {
                            "time": data.timestamp - start,
                            "currents": data.currents.to_list(),
                        }
                    )
            if time.time() - start > 5:  # 记录 5 秒
                break
    finally:
        hand.stop_polling()
        hand.stop_stream()

    print(f"采集 {len(records)} 条数据")

温度读取

概述

通过 hand.temperature 读取 L6 灵巧手 6 个关节电机的温度数据(单位:°C)。

温度属性

属性说明
thumb_flex拇指屈曲关节电机
thumb_abd拇指侧摆关节电机
index食指关节电机
middle中指关节电机
ring无名指关节电机
pinky小指关节电机

读取温度

阻塞读取

data = hand.temperature.get_blocking(timeout_ms=500)
print(f"拇指温度:{data.temperatures.thumb_flex}°C")

参数

  • timeout_ms: 超时时间(毫秒),默认 100

返回值

  • TemperatureData: 包含 temperaturestimestamp

异常

  • TimeoutError: 超时未响应

缓存读取

非阻塞读取最近一次缓存的温度数据。

data = hand.temperature.get_snapshot()
if data:
    print(f"温度:{data.temperatures.to_list()}")

返回值

  • TemperatureDataNone(无缓存数据时)

流式读取

通过顶层 hand.stream() 统一接收所有传感器事件:

from linkerbot.hand.l6 import SensorSource, TemperatureEvent

hand.start_polling({SensorSource.TEMPERATURE: 0.1})

for event in hand.stream():
    match event:
        case TemperatureEvent(data=data):
            print(f"温度:{data.temperatures.to_list()}")

hand.stop_polling()
hand.stop_stream()

示例

读取所有关节电机温度

from linkerbot import L6

with L6(side="left", interface_name="can0") as hand:
    data = hand.temperature.get_blocking(timeout_ms=500)

    # 按属性访问
    print(f"拇指屈曲:{data.temperatures.thumb_flex}°C")
    print(f"食指:{data.temperatures.index}°C")

    # 转换为列表
    temps = data.temperatures.to_list()
    print(f"全部温度:{temps}")

    # 索引访问
    print(f"第一个关节电机:{data.temperatures[0]}°C")

温度监控

from linkerbot import L6
from linkerbot.hand.l6 import SensorSource, TemperatureEvent

with L6(side="left", interface_name="can0") as hand:
    hand.start_polling({SensorSource.TEMPERATURE: 0.1})

    try:
        for event in hand.stream():
            match event:
                case TemperatureEvent(data=data):
                    for i, temp in enumerate(data.temperatures.to_list()):
                        if temp > 60.0:
                            print(f"警告:关节电机 {i} 过热 ({temp}°C)")
    except KeyboardInterrupt:
        pass
    finally:
        hand.stop_polling()
        hand.stop_stream()

力传感器

L6 灵巧手配备 5 个手指的力传感器(拇指、食指、中指、无名指、小指),支持阻塞读取和缓存读取两种模式。

概述

通过 hand.force_sensor 访问力传感器功能:

from linkerbot import L6

with L6(side="left", interface_name="can0") as hand:
    data = hand.force_sensor.get_blocking()

数据结构

ForceSensorData - 单个手指的传感器数据:

  • values: 形状 (12, 6) 的 NumPy 数组(uint8)
  • timestamp: Unix 时间戳

AllFingersData - 全部 5 个手指的数据:

  • thumb, index, middle, ring, pinky: 各手指的 ForceSensorData

读取数据

阻塞读取

data = hand.force_sensor.get_blocking(timeout_ms=1000)
print(data.thumb.values)  # 拇指数据
print(data.index.values)  # 食指数据
参数类型默认值说明
timeout_msfloat1000超时时间(毫秒)

异常: TimeoutError(超时)、ValidationError(参数无效)

缓存读取

获取最近一次接收的数据(非阻塞):

data = hand.force_sensor.get_snapshot()
if data:
    print(f"拇指:{data.thumb.values[0]}")
    print(f"食指:{data.index.values[0]}")

返回值: AllFingersDataNone(任一手指无数据时)

流式读取

通过顶层 hand.stream() 统一接收所有传感器事件:

from linkerbot.hand.l6 import SensorSource, ForceSensorEvent

hand.start_polling({SensorSource.FORCE_SENSOR: 0.1})

for event in hand.stream():
    match event:
        case ForceSensorEvent(data=data):
            print(data.thumb.values)

hand.stop_polling()
hand.stop_stream()

示例

阻塞读取所有手指

from linkerbot import L6

with L6(side="left", interface_name="can0") as hand:
    data = hand.force_sensor.get_blocking(timeout_ms=1000)
    print(f"拇指:{data.thumb.values.shape}")  # (12, 6)
    print(f"食指:{data.index.values.shape}")

流式采集指定时长

import time
from linkerbot import L6
from linkerbot.hand.l6 import SensorSource, ForceSensorEvent

with L6(side="left", interface_name="can0") as hand:
    hand.start_polling({SensorSource.FORCE_SENSOR: 0.05})
    start = time.time()

    try:
        for event in hand.stream():
            match event:
                case ForceSensorEvent(data=data):
                    print(f"拇指:{data.thumb.values[0]}")
            if time.time() - start > 5:  # 采集 5 秒
                break
    finally:
        hand.stop_polling()
        hand.stop_stream()

故障管理

L6 灵巧手的故障检测与清除功能。

概述

通过 hand.fault 访问故障管理功能:

  • 清除故障码
  • 读取故障状态(阻塞式、缓存)

故障码表

故障码说明
NONE0无故障
PHASE_B_OVERCURRENT1B 相过流
PHASE_C_OVERCURRENT2C 相过流
PHASE_A_OVERCURRENT4A 相过流
OVERLOAD_18过载等级 1
OVERLOAD_216过载等级 2
MOTOR_OVERTEMP32关节电机过温
MCU_OVERTEMP64MCU 过温

清除故障

hand.fault.clear_faults()

清除所有关节的故障码。

读取故障

阻塞式读取

data = hand.fault.get_blocking(timeout_ms=500)

参数

  • timeout_ms:超时时间(毫秒),默认 100

返回值FaultData 对象,包含:

  • faultsL6Fault 故障数据
  • timestamp:时间戳

异常

  • TimeoutError:超时未收到响应

缓存读取

data = hand.fault.get_snapshot()

返回最近缓存的故障数据,无数据时返回 None

流式读取

通过顶层 hand.stream() 统一接收所有传感器事件:

from linkerbot.hand.l6 import SensorSource, FaultEvent

hand.start_polling({SensorSource.FAULT: 0.2})

for event in hand.stream():
    match event:
        case FaultEvent(data=data):
            if data.faults.has_any_fault():
                for code in data.faults.to_list():
                    if code.has_fault():
                        print(code.get_fault_names())

hand.stop_polling()
hand.stop_stream()

故障数据

L6Fault 属性

属性说明
thumb_flex拇指弯曲
thumb_abd拇指侧摆
index食指
middle中指
ring无名指
pinky小指

L6Fault 方法

# 检查是否有任何故障
faults.has_any_fault()  # -> bool

# 转为列表
faults.to_list()  # -> list[FaultCode]

# 索引访问
faults[0]  # thumb_flex

FaultCode 方法

# 检查单个关节是否有故障
faults.thumb_flex.has_fault()  # -> bool

# 获取故障名称
faults.thumb_flex.get_fault_names()  # -> list[str]

示例

检查并清除故障

from linkerbot import L6

with L6(side="left", interface_name="can0") as hand:
    # 读取故障状态
    data = hand.fault.get_blocking(timeout_ms=500)

    if data.faults.has_any_fault():
        print("检测到故障:")
        if data.faults.thumb_flex.has_fault():
            print(f"  拇指弯曲:{data.faults.thumb_flex.get_fault_names()}")
        if data.faults.index.has_fault():
            print(f"  食指:{data.faults.index.get_fault_names()}")

        # 清除故障
        hand.fault.clear_faults()
    else:
        print("无故障")

持续监控

from linkerbot import L6
from linkerbot.hand.l6 import SensorSource, FaultEvent

with L6(side="left", interface_name="can0") as hand:
    hand.start_polling({SensorSource.FAULT: 0.2})

    try:
        for event in hand.stream():
            match event:
                case FaultEvent(data=data):
                    if data.faults.has_any_fault():
                        for code in data.faults.to_list():
                            if code.has_fault():
                                print(code.get_fault_names())
    except KeyboardInterrupt:
        pass
    finally:
        hand.stop_polling()
        hand.stop_stream()

版本信息

获取 L6 灵巧手的版本信息。

概述

通过 hand.version 访问版本管理功能:

  • 获取设备完整信息(序列号、PCB/固件/机械版本)

获取设备信息

hand.version.get_device_info() -> DeviceInfo

返回 DeviceInfo 对象,包含:

属性类型说明
serial_numberstr设备序列号
pcb_versionVersionPCB 硬件版本
firmware_versionVersion固件版本
mechanical_versionVersion机械结构版本
timestampfloat获取时间(Unix 时间戳)

Version 对象包含 majorminorpatch 属性,字符串格式为 V{major}.{minor}.{patch}

异常

  • TimeoutError:请求超时

示例

读取设备信息

from linkerbot import L6

hand = L6(channel="can0", hand_id=1)

info = hand.version.get_device_info()
print(f"序列号:{info.serial_number}")
print(f"固件版本:{info.firmware_version}")

hand.close()

L20Lite 灵巧手

快速开始

from linkerbot import L20lite

with L20lite(side="left", interface_name="can0") as hand:
    # 设置角度
    hand.angle.set_angles([50, 30, 60, 60, 60, 60, 20, 20, 20, 20])

    # 读取角度
    data = hand.angle.get_blocking(timeout_ms=500)
    print(data.angles)

构造参数

参数类型说明
side"left" | "right"左手或右手
interface_namestrCAN 接口名,如 "can0"
interface_typestrCAN 接口类型,默认 "socketcan"。Windows 用法参考 CAN 总线 文档

关节说明

索引名称标识
0拇指弯曲thumb_flex
1拇指侧摆thumb_abd
2食指弯曲index_flex
3中指弯曲middle_flex
4无名指弯曲ring_flex
5小指弯曲pinky_flex
6食指侧摆index_abd
7无名指侧摆ring_abd
8小指侧摆pinky_abd
9拇指旋转thumb_yaw

功能模块

模块说明
hand.angle关节角度控制与读取
hand.speed速度控制与读取
hand.torque扭矩控制与读取
hand.temperature温度读取
hand.force_sensor力传感器数据
hand.fault故障读取
hand.version设备版本信息

统一流式读取

L20Lite 提供统一的事件流接口,通过 hand.stream()hand.start_polling() 获取所有传感器数据。初始化时会自动以默认间隔启动轮询(角度 60 Hz、力传感器 30 Hz),无需手动调用 start_polling()

from linkerbot import L20lite
from linkerbot.hand.l20lite import SensorSource, AngleEvent, TemperatureEvent

with L20lite(side="left", interface_name="can0") as hand:
    # 可同时指定多个传感器及各自的轮询间隔(秒)
    # 再次调用 start_polling() 会覆盖之前的设置
    hand.start_polling({SensorSource.ANGLE: 0.05, SensorSource.TEMPERATURE: 1.0})

    for event in hand.stream():
        match event:
            case AngleEvent(data=ad):
                print(f"角度:{ad.angles.to_list()}")
            case TemperatureEvent(data=td):
                print(f"温度:{td.temperatures.to_list()}")

    hand.stop_polling()
    hand.stop_stream()

SensorSource 可选值及默认频率

说明默认频率
SensorSource.ANGLE角度60 Hz
SensorSource.SPEED速度不默认轮询
SensorSource.TORQUE扭矩不默认轮询
SensorSource.TEMPERATURE温度不默认轮询
SensorSource.FORCE_SENSOR力传感器30 Hz

快照

获取所有传感器最新缓存数据:

snap = hand.get_snapshot()
print(snap.angle)  # AngleData | None
print(snap.speed)  # SpeedData | None
print(snap.torque)  # TorqueData | None
print(snap.temperature)  # TemperatureData | None
print(snap.force_sensor)  # AllFingersData | None

角度控制

通过 hand.angle 控制和读取 L20Lite 灵巧手的 10 个关节电机角度。

  • 角度范围: 0-100
  • 单位: 无量纲(映射到关节电机实际角度)

设置角度

from linkerbot import L20lite
from linkerbot.hand.l20lite import L20liteAngle

# 使用列表
hand.angle.set_angles([50.0, 30.0, 60.0, 60.0, 60.0, 60.0, 20.0, 20.0, 20.0, 20.0])

# 使用 L20liteAngle 对象
angles = L20liteAngle(
    thumb_flex=50.0,  # 拇指弯曲
    thumb_abd=30.0,  # 拇指侧摆
    index_flex=60.0,  # 食指弯曲
    middle_flex=60.0,  # 中指弯曲
    ring_flex=60.0,  # 无名指弯曲
    pinky_flex=60.0,  # 小指弯曲
    index_abd=20.0,  # 食指侧摆
    ring_abd=20.0,  # 无名指侧摆
    pinky_abd=20.0,  # 小指侧摆
    thumb_yaw=20.0,  # 拇指旋转
)
hand.angle.set_angles(angles)

读取角度

阻塞读取

from linkerbot import L20lite
from linkerbot.exceptions import TimeoutError

try:
    data = hand.angle.get_blocking(timeout_ms=500)
    print(f"拇指弯曲:{data.angles.thumb_flex}")
    print(f"全部角度:{data.angles.to_list()}")
except TimeoutError:
    print("读取超时")

缓存读取

data = hand.angle.get_snapshot()
if data:
    print(f"角度:{data.angles.to_list()}")
    print(f"时间戳:{data.timestamp}")

流式读取

通过顶层 hand.stream() 统一接收所有传感器事件:

from linkerbot.hand.l20lite import SensorSource, AngleEvent

hand.start_polling({SensorSource.ANGLE: 0.1})

try:
    for event in hand.stream():
        match event:
            case AngleEvent(data=data):
                print(f"角度:{data.angles.to_list()}")
finally:
    hand.stop_polling()
    hand.stop_stream()

完整示例

from linkerbot import L20lite

with L20lite(side="left", interface_name="can0") as hand:
    # 设置角度
    hand.angle.set_angles([0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0])

    # 读取当前角度
    data = hand.angle.get_blocking(timeout_ms=500)
    print(f"当前角度:{data.angles.to_list()}")

    # 渐进移动
    for i in range(0, 101, 10):
        hand.angle.set_angles([float(i)] * 10)

速度控制

通过 hand.speed 控制和读取 L20Lite 灵巧手各关节电机的运动速度。

  • 速度范围: 0-100
  • 单位: 无量纲(映射到关节电机实际转速)

设置速度

from linkerbot import L20lite
from linkerbot.hand.l20lite import L20liteSpeed

# 使用列表
hand.speed.set_speeds([50.0, 50.0, 50.0, 50.0, 50.0, 50.0, 50.0, 50.0, 50.0, 50.0])

# 使用 L20liteSpeed 对象
speeds = L20liteSpeed(
    thumb_flex=30.0,  # 拇指弯曲
    thumb_abd=30.0,  # 拇指侧摆
    index_flex=80.0,  # 食指弯曲
    middle_flex=80.0,  # 中指弯曲
    ring_flex=80.0,  # 无名指弯曲
    pinky_flex=80.0,  # 小指弯曲
    index_abd=50.0,  # 食指侧摆
    ring_abd=50.0,  # 无名指侧摆
    pinky_abd=50.0,  # 小指侧摆
    thumb_yaw=50.0,  # 拇指旋转
)
hand.speed.set_speeds(speeds)

读取速度

阻塞读取

from linkerbot.exceptions import TimeoutError

try:
    data = hand.speed.get_blocking(timeout_ms=500)
    print(f"拇指弯曲速度:{data.speeds.thumb_flex}")
    print(f"全部速度:{data.speeds.to_list()}")
except TimeoutError:
    print("读取超时")

缓存读取

data = hand.speed.get_snapshot()
if data:
    print(f"速度:{data.speeds.to_list()}")
    print(f"时间戳:{data.timestamp}")

流式读取

通过顶层 hand.stream() 统一接收所有传感器事件:

from linkerbot.hand.l20lite import SensorSource, SpeedEvent

hand.start_polling({SensorSource.SPEED: 0.1})

try:
    for event in hand.stream():
        match event:
            case SpeedEvent(data=data):
                print(f"速度:{data.speeds.to_list()}")
finally:
    hand.stop_polling()
    hand.stop_stream()

完整示例

from linkerbot import L20lite
from linkerbot.hand.l20lite import L20liteSpeed

with L20lite(side="left", interface_name="can0") as hand:
    # 拇指慢速,其他手指快速
    speeds = L20liteSpeed(
        thumb_flex=20.0,
        thumb_abd=20.0,
        index_flex=80.0,
        middle_flex=80.0,
        ring_flex=80.0,
        pinky_flex=80.0,
        index_abd=60.0,
        ring_abd=60.0,
        pinky_abd=60.0,
        thumb_yaw=60.0,
    )
    hand.speed.set_speeds(speeds)

    # 读取当前速度
    data = hand.speed.get_blocking(timeout_ms=500)
    print(f"当前速度:{data.speeds.to_list()}")

    # 设置角度后观察速度效果
    hand.angle.set_angles([100.0] * 10)

扭矩控制

通过 hand.torque 控制和读取 L20Lite 灵巧手的 10 个关节电机扭矩。

  • 扭矩范围: 0-100
  • 单位: 无量纲百分比

设置扭矩

from linkerbot import L20lite
from linkerbot.hand.l20lite import L20liteTorque

# 使用列表
hand.torque.set_torques([50.0, 50.0, 60.0, 60.0, 60.0, 60.0, 30.0, 30.0, 30.0, 30.0])

# 使用 L20liteTorque 对象
torques = L20liteTorque(
    thumb_flex=50.0,  # 拇指弯曲
    thumb_abd=50.0,  # 拇指侧摆
    index_flex=60.0,  # 食指弯曲
    middle_flex=60.0,  # 中指弯曲
    ring_flex=60.0,  # 无名指弯曲
    pinky_flex=60.0,  # 小指弯曲
    index_abd=30.0,  # 食指侧摆
    ring_abd=30.0,  # 无名指侧摆
    pinky_abd=30.0,  # 小指侧摆
    thumb_yaw=30.0,  # 拇指旋转
)
hand.torque.set_torques(torques)

读取扭矩

阻塞读取

from linkerbot.exceptions import TimeoutError

try:
    data = hand.torque.get_blocking(timeout_ms=500)
    print(f"拇指弯曲:{data.torques.thumb_flex}")
    print(f"全部扭矩:{data.torques.to_list()}")
except TimeoutError:
    print("读取超时")

缓存读取

data = hand.torque.get_snapshot()
if data:
    print(f"扭矩:{data.torques.to_list()}")
    print(f"时间戳:{data.timestamp}")

流式读取

通过顶层 hand.stream() 统一接收所有传感器事件:

from linkerbot.hand.l20lite import SensorSource, TorqueEvent

hand.start_polling({SensorSource.TORQUE: 0.1})

try:
    for event in hand.stream():
        match event:
            case TorqueEvent(data=data):
                print(f"扭矩:{data.torques.to_list()}")
finally:
    hand.stop_polling()
    hand.stop_stream()

完整示例

from linkerbot import L20lite
from linkerbot.hand.l20lite import L20liteTorque

with L20lite(side="left", interface_name="can0") as hand:
    # 设置扭矩
    hand.torque.set_torques([50.0] * 10)

    # 读取当前扭矩
    data = hand.torque.get_blocking(timeout_ms=500)
    print(f"当前扭矩:{data.torques.to_list()}")

    # 使用数据类精细控制
    torques = L20liteTorque(
        thumb_flex=70.0,
        thumb_abd=70.0,
        index_flex=50.0,
        middle_flex=50.0,
        ring_flex=50.0,
        pinky_flex=50.0,
        index_abd=40.0,
        ring_abd=40.0,
        pinky_abd=40.0,
        thumb_yaw=40.0,
    )
    hand.torque.set_torques(torques)

温度读取

通过 hand.temperature 读取 L20Lite 灵巧手 10 个关节电机的温度数据(单位:°C)。

温度属性

属性说明
thumb_flex拇指弯曲关节电机
thumb_abd拇指侧摆关节电机
index_flex食指弯曲关节电机
middle_flex中指弯曲关节电机
ring_flex无名指弯曲关节电机
pinky_flex小指弯曲关节电机
index_abd食指侧摆关节电机
ring_abd无名指侧摆关节电机
pinky_abd小指侧摆关节电机
thumb_yaw拇指旋转关节电机

读取温度

阻塞读取

from linkerbot.exceptions import TimeoutError

try:
    data = hand.temperature.get_blocking(timeout_ms=500)
    print(f"拇指弯曲温度:{data.temperatures.thumb_flex}°C")
except TimeoutError:
    print("读取超时")

参数

  • timeout_ms: 超时时间(毫秒),默认 100

返回值

  • TemperatureData: 包含 temperatures(L20liteTemperature)和 timestamp

异常

  • TimeoutError: 超时未响应

缓存读取

非阻塞读取最近一次缓存的温度数据。

data = hand.temperature.get_snapshot()
if data:
    print(f"温度:{data.temperatures.to_list()}")

返回值

  • TemperatureDataNone(无缓存数据时)

流式读取

通过顶层 hand.stream() 统一接收所有传感器事件:

from linkerbot.hand.l20lite import SensorSource, TemperatureEvent

hand.start_polling({SensorSource.TEMPERATURE: 0.1})

try:
    for event in hand.stream():
        match event:
            case TemperatureEvent(data=data):
                print(f"温度:{data.temperatures.to_list()}")
finally:
    hand.stop_polling()
    hand.stop_stream()

示例

读取所有关节电机温度

from linkerbot import L20lite

with L20lite(side="left", interface_name="can0") as hand:
    data = hand.temperature.get_blocking(timeout_ms=500)

    # 按属性访问
    print(f"拇指弯曲:{data.temperatures.thumb_flex}°C")
    print(f"食指弯曲:{data.temperatures.index_flex}°C")

    # 转换为列表
    temps = data.temperatures.to_list()
    print(f"全部温度:{temps}")

    # 索引访问
    print(f"第一个关节电机:{data.temperatures[0]}°C")

温度监控

from linkerbot import L20lite
from linkerbot.hand.l20lite import SensorSource, TemperatureEvent

with L20lite(side="left", interface_name="can0") as hand:
    hand.start_polling({SensorSource.TEMPERATURE: 0.1})

    try:
        for event in hand.stream():
            match event:
                case TemperatureEvent(data=data):
                    for i, temp in enumerate(data.temperatures.to_list()):
                        if temp > 60.0:
                            print(f"警告:关节电机 {i} 过热 ({temp}°C)")
    except KeyboardInterrupt:
        pass
    finally:
        hand.stop_polling()
        hand.stop_stream()

力传感器

L20Lite 灵巧手配备 5 个手指的力传感器(thumb, index, middle, ring, pinky),支持阻塞读取和缓存读取两种模式。

概述

通过 hand.force_sensor 访问力传感器功能:

from linkerbot import L20lite

with L20lite(side="left", interface_name="can0") as hand:
    data = hand.force_sensor.get_blocking(timeout_ms=1000)

数据结构

ForceSensorData - 单个手指的传感器数据:

  • values: 形状 (12, 6) 的 NumPy 数组(uint8)
  • timestamp: Unix 时间戳

AllFingersData - 全部 5 个手指的数据:

  • thumb, index, middle, ring, pinky: 各手指的 ForceSensorData

读取数据

阻塞读取

from linkerbot.exceptions import TimeoutError

try:
    data = hand.force_sensor.get_blocking(timeout_ms=1000)
    print(data.thumb.values)  # 拇指数据
    print(data.index.values)  # 食指数据
except TimeoutError:
    print("读取超时")
参数类型默认值说明
timeout_msfloat1000超时时间(毫秒),适用于整体操作

异常: TimeoutError(超时)、ValidationError(参数无效)

缓存读取

获取最近一次接收的数据(非阻塞):

data = hand.force_sensor.get_snapshot()
if data:
    print(f"拇指:{data.thumb.values[0]}")
    print(f"食指:{data.index.values[0]}")

返回值: AllFingersDataNone(任一手指无数据时)

单手指读取

通过 get_finger() 获取单个手指的力传感器管理器:

# 获取拇指传感器
thumb_sensor = hand.force_sensor.get_finger("thumb")
thumb_data = thumb_sensor.get_blocking(timeout_ms=1000)
print(thumb_data.values.shape)  # (12, 6)

可用手指名称:"thumb", "index", "middle", "ring", "pinky"

流式读取

通过顶层 hand.stream() 统一接收所有传感器事件:

from linkerbot.hand.l20lite import SensorSource, ForceSensorEvent

hand.start_polling({SensorSource.FORCE_SENSOR: 0.1})

try:
    for event in hand.stream():
        match event:
            case ForceSensorEvent(data=data):
                print(data.thumb.values)
finally:
    hand.stop_polling()
    hand.stop_stream()

示例

阻塞读取所有手指

from linkerbot import L20lite

with L20lite(side="left", interface_name="can0") as hand:
    data = hand.force_sensor.get_blocking(timeout_ms=1000)
    print(f"拇指:{data.thumb.values.shape}")
    print(f"食指:{data.index.values.shape}")
    print(f"中指:{data.middle.values.shape}")
    print(f"无名指:{data.ring.values.shape}")
    print(f"小指:{data.pinky.values.shape}")

流式采集指定时长

import time
from linkerbot import L20lite
from linkerbot.hand.l20lite import SensorSource, ForceSensorEvent

with L20lite(side="left", interface_name="can0") as hand:
    hand.start_polling({SensorSource.FORCE_SENSOR: 0.05})
    start = time.time()

    try:
        for event in hand.stream():
            match event:
                case ForceSensorEvent(data=data):
                    print(f"拇指:{data.thumb.values[0]}")
            if time.time() - start > 5:  # 采集 5 秒
                break
    finally:
        hand.stop_polling()
        hand.stop_stream()

故障管理

L20Lite 灵巧手的故障检测功能。

概述

通过 hand.fault 访问故障管理功能:

  • 读取故障状态(阻塞式、缓存)

故障码表

故障码说明
NONE0无故障
VOLTAGE_ABNORMAL1过压/欠压
ENCODER_ABNORMAL2磁编码器异常
OVERTEMPERATURE4温度过热
OVERCURRENT8电流过流
OVERLOAD32负载过载

读取故障

阻塞式读取

from linkerbot.exceptions import TimeoutError

try:
    data = hand.fault.get_blocking(timeout_ms=500)
except TimeoutError:
    print("读取超时")

参数

  • timeout_ms:超时时间(毫秒),默认 100

返回值FaultData 对象,包含:

  • faultsL20liteFault 故障数据
  • timestamp:时间戳

异常

  • TimeoutError:超时未收到响应

缓存读取

data = hand.fault.get_snapshot()
if data:
    print(f"有故障:{data.faults.has_any_fault()}")

返回最近缓存的故障数据,无数据时返回 None

故障数据

L20liteFault 属性

属性说明
thumb_flex拇指弯曲
thumb_abd拇指侧摆
index_flex食指弯曲
middle_flex中指弯曲
ring_flex无名指弯曲
pinky_flex小指弯曲
index_abd食指侧摆
ring_abd无名指侧摆
pinky_abd小指侧摆
thumb_yaw拇指旋转

L20liteFault 方法

# 检查是否有任何故障
faults.has_any_fault()  # -> bool

# 转为列表
faults.to_list()  # -> list[FaultCode]

# 索引访问
faults[0]  # thumb_flex

FaultCode 方法

# 检查单个关节电机是否有故障
faults.thumb_flex.has_fault()  # -> bool

# 获取故障名称
faults.thumb_flex.get_fault_names()  # -> list[str]

示例

检查故障状态

from linkerbot import L20lite

with L20lite(side="left", interface_name="can0") as hand:
    # 读取故障状态
    data = hand.fault.get_blocking(timeout_ms=500)

    if data.faults.has_any_fault():
        print("检测到故障:")
        if data.faults.thumb_flex.has_fault():
            print(f"  拇指弯曲:{data.faults.thumb_flex.get_fault_names()}")
        if data.faults.index_flex.has_fault():
            print(f"  食指弯曲:{data.faults.index_flex.get_fault_names()}")
    else:
        print("无故障")

遍历所有关节故障

from linkerbot import L20lite

with L20lite(side="left", interface_name="can0") as hand:
    data = hand.fault.get_blocking(timeout_ms=500)

    if data.faults.has_any_fault():
        for i, code in enumerate(data.faults.to_list()):
            if code.has_fault():
                print(f"关节 {i} 故障:{code.get_fault_names()}")

版本信息

获取 L20Lite 灵巧手的版本信息。

概述

通过 hand.version 访问版本管理功能:

  • 获取设备完整信息(序列号、PCB/固件/机械版本)

获取设备信息

hand.version.get_device_info() -> DeviceInfo

返回 DeviceInfo 对象,包含:

属性类型说明
serial_numberstr设备序列号
pcb_versionVersionPCB 硬件版本
firmware_versionVersion固件版本
mechanical_versionVersion机械结构版本
timestampfloat获取时间(Unix 时间戳)

Version 对象包含 majorminorpatch 属性,字符串格式为 V{major}.{minor}.{patch}

异常

  • TimeoutError:请求超时

示例

读取设备信息

from linkerbot import L20lite

with L20lite(side="left", interface_name="can0") as hand:
    info = hand.version.get_device_info()
    print(f"序列号:{info.serial_number}")
    print(f"PCB 版本:{info.pcb_version}")
    print(f"固件版本:{info.firmware_version}")
    print(f"机械版本:{info.mechanical_version}")

L25 灵巧手

快速开始

from linkerbot import L25

with L25(side="left", interface_name="can0") as hand:
    # 设置角度
    hand.angle.set_angles([50.0] * 16)

    # 读取角度
    data = hand.angle.get_blocking(timeout_ms=500)
    print(data.angles)

构造参数

参数类型说明
side"left" | "right"左手或右手
interface_namestrCAN 接口名,如 "can0"
interface_typestrCAN 接口类型,默认 "socketcan"。Windows 用法参考 CAN 总线 文档

关节说明

L25 灵巧手拥有 16 个自由度,分布在 5 根手指上。

索引名称标识
0拇指侧摆thumb_abd
1拇指旋转thumb_yaw
2拇指根部thumb_root1
3拇指指尖thumb_tip
4食指侧摆index_abd
5食指根部index_root1
6食指指尖index_tip
7中指侧摆middle_abd
8中指根部middle_root1
9中指指尖middle_tip
10无名指侧摆ring_abd
11无名指根部ring_root1
12无名指指尖ring_tip
13小指侧摆pinky_abd
14小指根部pinky_root1
15小指指尖pinky_tip

功能模块

模块说明
hand.angle关节角度控制与读取
hand.speed速度控制与读取
hand.torque扭矩控制与读取
hand.temperature温度读取
hand.force_sensor力传感器数据
hand.fault故障读取与清除
hand.version设备版本信息

统一流式读取

L25 提供统一的事件流接口,通过 hand.stream()hand.start_polling() 获取所有传感器数据。初始化时会自动以默认间隔启动轮询(角度 60 Hz、力传感器 30 Hz),无需手动调用 start_polling()

from linkerbot import L25
from linkerbot.hand.l25 import SensorSource, AngleEvent, TemperatureEvent

with L25(side="left", interface_name="can0") as hand:
    # 可同时指定多个传感器及各自的轮询间隔(秒)
    # 再次调用 start_polling() 会覆盖之前的设置
    hand.start_polling({SensorSource.ANGLE: 0.05, SensorSource.TEMPERATURE: 1.0})

    for event in hand.stream():
        match event:
            case AngleEvent(data=ad):
                print(f"角度:{ad.angles.to_list()}")
            case TemperatureEvent(data=td):
                print(f"温度:{td.temperatures.to_list()}")

    hand.stop_polling()
    hand.stop_stream()

start_polling 参数

参数类型默认值说明
intervalsdict[SensorSource, float]全部默认值每个传感器的轮询间隔(秒)

SensorSource 可选值及默认频率

说明默认频率
SensorSource.ANGLE角度60 Hz
SensorSource.SPEED速度不默认轮询
SensorSource.TORQUE扭矩不默认轮询
SensorSource.TEMPERATURE温度不默认轮询
SensorSource.FAULT故障不默认轮询
SensorSource.FORCE_SENSOR力传感器30 Hz

快照

获取所有传感器最新缓存数据:

snap = hand.get_snapshot()
print(snap.angle)  # AngleData | None
print(snap.speed)  # SpeedData | None
print(snap.torque)  # TorqueData | None
print(snap.temperature)  # TemperatureData | None
print(snap.fault)  # FaultData | None
print(snap.force_sensor)  # AllFingersData | None

角度控制

通过 hand.angle 控制和读取 L25 灵巧手的 16 个关节电机角度。

  • 角度范围: 0-100
  • 单位: 无量纲(映射到关节电机实际角度)

设置角度

from linkerbot import L25
from linkerbot.hand.l25 import L25Angle

# 使用列表(16 个关节)
hand.angle.set_angles([50.0] * 16)

# 使用 L25Angle 对象
angles = L25Angle(
    thumb_abd=50.0,  # 拇指侧摆
    thumb_yaw=30.0,  # 拇指旋转
    thumb_root1=60.0,  # 拇指根部
    thumb_tip=40.0,  # 拇指指尖
    index_abd=50.0,  # 食指侧摆
    index_root1=60.0,  # 食指根部
    index_tip=40.0,  # 食指指尖
    middle_abd=50.0,  # 中指侧摆
    middle_root1=60.0,  # 中指根部
    middle_tip=40.0,  # 中指指尖
    ring_abd=50.0,  # 无名指侧摆
    ring_root1=60.0,  # 无名指根部
    ring_tip=40.0,  # 无名指指尖
    pinky_abd=50.0,  # 小指侧摆
    pinky_root1=60.0,  # 小指根部
    pinky_tip=40.0,  # 小指指尖
)
hand.angle.set_angles(angles)

读取角度

阻塞读取

from linkerbot import L25
from linkerbot.exceptions import TimeoutError

try:
    data = hand.angle.get_blocking(timeout_ms=500)
    print(f"拇指侧摆:{data.angles.thumb_abd}")
    print(f"全部角度:{data.angles.to_list()}")
except TimeoutError:
    print("读取超时")

缓存读取

data = hand.angle.get_snapshot()
if data:
    print(f"角度:{data.angles.to_list()}")
    print(f"时间戳:{data.timestamp}")

流式读取

通过顶层 hand.stream() 统一接收所有传感器事件:

from linkerbot.hand.l25 import SensorSource, AngleEvent

hand.start_polling({SensorSource.ANGLE: 0.1})

try:
    for event in hand.stream():
        match event:
            case AngleEvent(data=data):
                print(f"角度:{data.angles.to_list()}")
finally:
    hand.stop_polling()
    hand.stop_stream()

完整示例

from linkerbot import L25

with L25(side="left", interface_name="can0") as hand:
    # 设置角度
    hand.angle.set_angles([0.0] * 16)

    # 读取当前角度
    data = hand.angle.get_blocking(timeout_ms=500)
    print(f"当前角度:{data.angles.to_list()}")

    # 渐进移动
    for i in range(0, 101, 10):
        hand.angle.set_angles([float(i)] * 16)

速度控制

通过 hand.speed 控制和读取 L25 灵巧手各关节电机的运动速度。

  • 速度范围: 0-100
  • 单位: 无量纲

设置速度

from linkerbot import L25
from linkerbot.hand.l25 import L25Speed

# 使用列表(16 个关节)
hand.speed.set_speeds([50.0] * 16)

# 使用 L25Speed 对象
speed = L25Speed(
    thumb_abd=30.0,  # 拇指侧摆
    thumb_yaw=30.0,  # 拇指旋转
    thumb_root1=30.0,  # 拇指根部
    thumb_tip=30.0,  # 拇指指尖
    index_abd=80.0,  # 食指侧摆
    index_root1=80.0,  # 食指根部
    index_tip=80.0,  # 食指指尖
    middle_abd=80.0,  # 中指侧摆
    middle_root1=80.0,  # 中指根部
    middle_tip=80.0,  # 中指指尖
    ring_abd=80.0,  # 无名指侧摆
    ring_root1=80.0,  # 无名指根部
    ring_tip=80.0,  # 无名指指尖
    pinky_abd=80.0,  # 小指侧摆
    pinky_root1=80.0,  # 小指根部
    pinky_tip=80.0,  # 小指指尖
)
hand.speed.set_speeds(speed)

读取速度

阻塞读取

from linkerbot import L25
from linkerbot.exceptions import TimeoutError

try:
    data = hand.speed.get_blocking(timeout_ms=500)
    print(f"拇指侧摆速度:{data.speeds.thumb_abd}")
    print(f"全部速度:{data.speeds.to_list()}")
except TimeoutError:
    print("读取超时")

缓存读取

data = hand.speed.get_snapshot()
if data:
    print(f"速度:{data.speeds.to_list()}")
    print(f"时间戳:{data.timestamp}")

流式读取

通过顶层 hand.stream() 统一接收所有传感器事件:

from linkerbot.hand.l25 import SensorSource, SpeedEvent

hand.start_polling({SensorSource.SPEED: 0.1})

try:
    for event in hand.stream():
        match event:
            case SpeedEvent(data=data):
                print(f"速度:{data.speeds.to_list()}")
finally:
    hand.stop_polling()
    hand.stop_stream()

完整示例

from linkerbot import L25
from linkerbot.hand.l25 import L25Speed

with L25(side="left", interface_name="can0") as hand:
    # 设置速度
    speed = L25Speed(
        thumb_abd=30.0,
        thumb_yaw=30.0,
        thumb_root1=30.0,
        thumb_tip=30.0,
        index_abd=80.0,
        index_root1=80.0,
        index_tip=80.0,
        middle_abd=80.0,
        middle_root1=80.0,
        middle_tip=80.0,
        ring_abd=80.0,
        ring_root1=80.0,
        ring_tip=80.0,
        pinky_abd=80.0,
        pinky_root1=80.0,
        pinky_tip=80.0,
    )
    hand.speed.set_speeds(speed)

    # 读取当前速度
    data = hand.speed.get_blocking(timeout_ms=500)
    print(f"当前速度:{data.speeds.to_list()}")

扭矩控制

通过 hand.torque 控制和读取 L25 灵巧手的 16 个关节电机扭矩。

  • 扭矩范围: 0-100
  • 单位: 无量纲百分比

设置扭矩

from linkerbot import L25
from linkerbot.hand.l25 import L25Torque

# 使用列表(16 个关节)
hand.torque.set_torques([50.0] * 16)

# 使用 L25Torque 对象
torques = L25Torque(
    thumb_abd=50.0,  # 拇指侧摆
    thumb_yaw=30.0,  # 拇指旋转
    thumb_root1=60.0,  # 拇指根部
    thumb_tip=40.0,  # 拇指指尖
    index_abd=50.0,  # 食指侧摆
    index_root1=60.0,  # 食指根部
    index_tip=40.0,  # 食指指尖
    middle_abd=50.0,  # 中指侧摆
    middle_root1=60.0,  # 中指根部
    middle_tip=40.0,  # 中指指尖
    ring_abd=50.0,  # 无名指侧摆
    ring_root1=60.0,  # 无名指根部
    ring_tip=40.0,  # 无名指指尖
    pinky_abd=50.0,  # 小指侧摆
    pinky_root1=60.0,  # 小指根部
    pinky_tip=40.0,  # 小指指尖
)
hand.torque.set_torques(torques)

读取扭矩

阻塞读取

from linkerbot.exceptions import TimeoutError

try:
    data = hand.torque.get_blocking(timeout_ms=500)
    print(f"拇指侧摆:{data.torques.thumb_abd}")
    print(f"全部扭矩:{data.torques.to_list()}")
except TimeoutError:
    print("读取超时")

缓存读取

data = hand.torque.get_snapshot()
if data:
    print(f"扭矩:{data.torques.to_list()}")
    print(f"时间戳:{data.timestamp}")

流式读取

通过顶层 hand.stream() 统一接收所有传感器事件:

from linkerbot.hand.l25 import SensorSource, TorqueEvent

hand.start_polling({SensorSource.TORQUE: 0.1})

try:
    for event in hand.stream():
        match event:
            case TorqueEvent(data=data):
                print(f"扭矩:{data.torques.to_list()}")
finally:
    hand.stop_polling()
    hand.stop_stream()

完整示例

from linkerbot import L25

with L25(side="left", interface_name="can0") as hand:
    # 设置扭矩
    hand.torque.set_torques([50.0] * 16)

    # 读取当前扭矩
    data = hand.torque.get_blocking(timeout_ms=500)
    print(f"当前扭矩:{data.torques.to_list()}")

    # 设置不同手指的扭矩
    hand.torque.set_torques(
        [
            80.0,
            80.0,
            80.0,
            80.0,  # 拇指
            60.0,
            60.0,
            60.0,  # 食指
            60.0,
            60.0,
            60.0,  # 中指
            60.0,
            60.0,
            60.0,  # 无名指
            60.0,
            60.0,
            60.0,  # 小指
        ]
    )

温度读取

通过 hand.temperature 读取 L25 灵巧手 16 个关节电机的温度数据(单位:°C)。

温度属性

属性说明
thumb_abd拇指侧摆关节电机
thumb_yaw拇指旋转关节电机
thumb_root1拇指根部关节电机
thumb_tip拇指指尖关节电机
index_abd食指侧摆关节电机
index_root1食指根部关节电机
index_tip食指指尖关节电机
middle_abd中指侧摆关节电机
middle_root1中指根部关节电机
middle_tip中指指尖关节电机
ring_abd无名指侧摆关节电机
ring_root1无名指根部关节电机
ring_tip无名指指尖关节电机
pinky_abd小指侧摆关节电机
pinky_root1小指根部关节电机
pinky_tip小指指尖关节电机

读取温度

阻塞读取

from linkerbot.exceptions import TimeoutError

try:
    data = hand.temperature.get_blocking(timeout_ms=500)
    print(f"拇指侧摆温度:{data.temperatures.thumb_abd}°C")
except TimeoutError:
    print("读取超时")

参数

  • timeout_ms: 超时时间(毫秒),默认 100

返回值

  • TemperatureData: 包含 temperatures(L25Temperature)和 timestamp

异常

  • TimeoutError: 超时未响应

缓存读取

非阻塞读取最近一次缓存的温度数据。

data = hand.temperature.get_snapshot()
if data:
    print(f"温度:{data.temperatures.to_list()}")

返回值

  • TemperatureDataNone(无缓存数据时)

流式读取

通过顶层 hand.stream() 统一接收所有传感器事件:

from linkerbot.hand.l25 import SensorSource, TemperatureEvent

hand.start_polling({SensorSource.TEMPERATURE: 0.1})

try:
    for event in hand.stream():
        match event:
            case TemperatureEvent(data=data):
                print(f"温度:{data.temperatures.to_list()}")
finally:
    hand.stop_polling()
    hand.stop_stream()

示例

读取所有关节电机温度

from linkerbot import L25

with L25(side="left", interface_name="can0") as hand:
    data = hand.temperature.get_blocking(timeout_ms=500)

    # 按属性访问
    print(f"拇指侧摆:{data.temperatures.thumb_abd}°C")
    print(f"食指侧摆:{data.temperatures.index_abd}°C")

    # 转换为列表
    temps = data.temperatures.to_list()
    print(f"全部温度:{temps}")

    # 索引访问
    print(f"第一个关节电机:{data.temperatures[0]}°C")

温度监控

from linkerbot import L25
from linkerbot.hand.l25 import SensorSource, TemperatureEvent

with L25(side="left", interface_name="can0") as hand:
    hand.start_polling({SensorSource.TEMPERATURE: 0.1})

    try:
        for event in hand.stream():
            match event:
                case TemperatureEvent(data=data):
                    for i, temp in enumerate(data.temperatures.to_list()):
                        if temp > 60.0:
                            print(f"警告:关节电机 {i} 过热 ({temp}°C)")
    except KeyboardInterrupt:
        pass
    finally:
        hand.stop_polling()
        hand.stop_stream()

力传感器

L25 灵巧手配备 5 个手指的力传感器(thumb, index, middle, ring, pinky),支持阻塞读取和缓存读取两种模式。

概述

通过 hand.force_sensor 访问力传感器功能:

from linkerbot import L25

with L25(side="left", interface_name="can0") as hand:
    data = hand.force_sensor.get_blocking()

数据结构

ForceSensorData - 单个手指的传感器数据:

  • values: 形状 (12, 6) 的 NumPy 数组(uint8)
  • timestamp: Unix 时间戳

AllFingersData - 全部 5 个手指的数据:

  • thumb, index, middle, ring, pinky: 各手指的 ForceSensorData

读取数据

阻塞读取

from linkerbot.exceptions import TimeoutError

try:
    data = hand.force_sensor.get_blocking(timeout_ms=1000)
    print(data.thumb.values)  # 拇指数据
    print(data.index.values)  # 食指数据
except TimeoutError:
    print("读取超时")
参数类型默认值说明
timeout_msfloat1000超时时间(毫秒)

异常: TimeoutError(超时)、ValidationError(参数无效)

缓存读取

获取最近一次接收的数据(非阻塞):

data = hand.force_sensor.get_snapshot()
if data:
    print(f"拇指:{data.thumb.values[0]}")
    print(f"食指:{data.index.values[0]}")

返回值: AllFingersDataNone(任一手指无数据时)

单手指读取

通过 get_finger() 获取单个手指的传感器管理器,支持独立的阻塞和缓存读取:

# 获取拇指的力传感器管理器
thumb_sensor = hand.force_sensor.get_finger("thumb")

# 阻塞读取拇指数据
thumb_data = thumb_sensor.get_blocking(timeout_ms=1000)
print(f"拇指:{thumb_data.values.shape}")

# 缓存读取拇指数据
thumb_data = thumb_sensor.get_snapshot()
if thumb_data:
    print(f"拇指:{thumb_data.values[0]}")

可用手指名称: "thumb", "index", "middle", "ring", "pinky"

流式读取

通过顶层 hand.stream() 统一接收所有传感器事件:

from linkerbot.hand.l25 import SensorSource, ForceSensorEvent

hand.start_polling({SensorSource.FORCE_SENSOR: 0.1})

try:
    for event in hand.stream():
        match event:
            case ForceSensorEvent(data=data):
                print(data.thumb.values)
finally:
    hand.stop_polling()
    hand.stop_stream()

示例

阻塞读取所有手指

from linkerbot import L25

with L25(side="left", interface_name="can0") as hand:
    data = hand.force_sensor.get_blocking(timeout_ms=1000)
    print(f"拇指:{data.thumb.values.shape}")
    print(f"食指:{data.index.values.shape}")
    print(f"中指:{data.middle.values.shape}")
    print(f"无名指:{data.ring.values.shape}")
    print(f"小指:{data.pinky.values.shape}")

流式采集指定时长

import time
from linkerbot import L25
from linkerbot.hand.l25 import SensorSource, ForceSensorEvent

with L25(side="left", interface_name="can0") as hand:
    hand.start_polling({SensorSource.FORCE_SENSOR: 0.05})
    start = time.time()

    try:
        for event in hand.stream():
            match event:
                case ForceSensorEvent(data=data):
                    print(f"拇指:{data.thumb.values[0]}")
            if time.time() - start > 5:  # 采集 5 秒
                break
    finally:
        hand.stop_polling()
        hand.stop_stream()

故障管理

L25 灵巧手的故障检测与清除功能。

概述

通过 hand.fault 访问故障管理功能:

  • 读取故障状态(阻塞式、缓存)
  • 清除所有关节故障

故障码表

L25 使用位标志(Flag)表示故障状态,每个关节电机可同时存在多个故障。

故障码说明
NONE0无故障
MOTOR_ROTOR_LOCK1电机转子锁死
MOTOR_OVER_CURRENT2电机过流
MOTOR_STALL_FAULT4电机堵转故障
VOLTAGE_ABNORMAL8电压异常
SELF_CHECK_ABNORMAL16自检异常
OVER_TEMPERATURE32过温
SOFT_ROTOR_LOCK64软件转子锁死
MOTOR_COMM_ABNORMAL128电机通讯异常

读取故障

阻塞式读取

from linkerbot.exceptions import TimeoutError

try:
    data = hand.fault.get_blocking(timeout_ms=500)
except TimeoutError:
    print("读取超时")

参数

  • timeout_ms:超时时间(毫秒),默认 100

返回值FaultData 对象,包含:

  • faultsL25Fault 故障数据
  • timestamp:时间戳

异常

  • TimeoutError:超时未收到响应

缓存读取

data = hand.fault.get_snapshot()
if data:
    print(f"是否有故障:{data.faults.has_any_fault()}")

返回最近缓存的故障数据,无数据时返回 None

清除故障

hand.fault.clear_faults()

向所有关节发送故障清除命令(fire-and-forget),不等待响应。

流式读取

通过顶层 hand.stream() 统一接收所有传感器事件:

from linkerbot.hand.l25 import SensorSource, FaultEvent

hand.start_polling({SensorSource.FAULT: 0.2})

try:
    for event in hand.stream():
        match event:
            case FaultEvent(data=data):
                if data.faults.has_any_fault():
                    for code in data.faults.to_list():
                        if code.has_fault():
                            print(code.get_fault_names())
finally:
    hand.stop_polling()
    hand.stop_stream()

数据类说明

L25Fault 属性

属性说明
thumb_abd拇指侧摆
thumb_yaw拇指旋转
thumb_root1拇指根部
thumb_tip拇指指尖
index_abd食指侧摆
index_root1食指根部
index_tip食指指尖
middle_abd中指侧摆
middle_root1中指根部
middle_tip中指指尖
ring_abd无名指侧摆
ring_root1无名指根部
ring_tip无名指指尖
pinky_abd小指侧摆
pinky_root1小指根部
pinky_tip小指指尖

L25Fault 方法

# 检查是否有任何故障
faults.has_any_fault()  # -> bool

# 转为列表
faults.to_list()  # -> list[L25FaultCode]

# 索引访问
faults[0]  # thumb_abd

L25FaultCode 方法

# 检查单个关节电机是否有故障
faults.thumb_abd.has_fault()  # -> bool

# 获取故障名称列表
faults.thumb_abd.get_fault_names()  # -> list[str]

示例

检查故障状态

from linkerbot import L25

with L25(side="left", interface_name="can0") as hand:
    # 读取故障状态
    data = hand.fault.get_blocking(timeout_ms=500)

    if data.faults.has_any_fault():
        print("检测到故障:")
        if data.faults.thumb_abd.has_fault():
            print(f"  拇指侧摆:{data.faults.thumb_abd.get_fault_names()}")
        if data.faults.index_abd.has_fault():
            print(f"  食指侧摆:{data.faults.index_abd.get_fault_names()}")
    else:
        print("无故障")

    # 清除所有故障
    hand.fault.clear_faults()

持续监控

from linkerbot import L25
from linkerbot.hand.l25 import SensorSource, FaultEvent

with L25(side="left", interface_name="can0") as hand:
    hand.start_polling({SensorSource.FAULT: 0.2})

    try:
        for event in hand.stream():
            match event:
                case FaultEvent(data=data):
                    if data.faults.has_any_fault():
                        for i, code in enumerate(data.faults.to_list()):
                            if code.has_fault():
                                print(f"关节 {i} 故障:{code.get_fault_names()}")
    except KeyboardInterrupt:
        pass
    finally:
        hand.stop_polling()
        hand.stop_stream()

版本信息

获取 L25 灵巧手的版本信息。

概述

通过 hand.version 访问版本管理功能:

  • 获取设备完整信息(序列号、PCB/固件/机械版本)

获取设备信息

hand.version.get_device_info() -> DeviceInfo

返回 DeviceInfo 对象,包含:

属性类型说明
serial_numberstr设备序列号
pcb_versionVersionPCB 硬件版本
firmware_versionVersion固件版本
mechanical_versionVersion机械结构版本
timestampfloat获取时间(Unix 时间戳)

Version 对象包含 majorminorpatch 属性,字符串格式为 V{major}.{minor}.{patch}

异常

  • TimeoutError:请求超时

示例

读取设备信息

from linkerbot import L25

with L25(side="left", interface_name="can0") as hand:
    info = hand.version.get_device_info()
    print(f"序列号:{info.serial_number}")
    print(f"PCB 版本:{info.pcb_version}")
    print(f"固件版本:{info.firmware_version}")
    print(f"机械版本:{info.mechanical_version}")

O6 灵巧手

快速开始

from linkerbot import O6

with O6(side="left", interface_name="can0") as hand:
    # 设置角度
    hand.angle.set_angles((10, 20, 30, 40, 50, 60))

    # 读取角度
    data = hand.angle.get_blocking(timeout_ms=500)
    print(data.angles)

构造参数

参数类型说明
side"left" | "right"左手或右手
interface_namestrCAN 接口名,如 "can0"
interface_typestrCAN 接口类型,默认 "socketcan"。Windows 用法参考 CAN 总线 文档

关节说明

索引名称标识
0拇指弯曲thumb_flex
1拇指侧摆thumb_abd
2食指index
3中指middle
4无名指ring
5小指pinky

功能模块

模块说明
hand.angle关节角度控制与读取
hand.speed速度控制(支持读取和 RPM 单位)
hand.acceleration加速度控制
hand.torque扭矩控制(支持 mA 单位)
hand.temperature温度读取
hand.force_sensor力传感器数据
hand.fault故障读取
hand.version设备版本信息

统一流式读取

O6 提供统一的事件流接口,通过 hand.stream()hand.start_polling() 获取所有传感器数据。初始化时会自动以默认间隔启动轮询(角度 60 Hz、力传感器 30 Hz),无需手动调用 start_polling()

from linkerbot import O6
from linkerbot.hand.o6 import SensorSource, AngleEvent, TemperatureEvent

with O6(side="left", interface_name="can0") as hand:
    # 可同时指定多个传感器及各自的轮询间隔(秒)
    # 再次调用 start_polling() 会覆盖之前的设置
    hand.start_polling({SensorSource.ANGLE: 0.05, SensorSource.TEMPERATURE: 1.0})

    for event in hand.stream():
        match event:
            case AngleEvent(data=ad):
                print(f"角度:{ad.angles.to_list()}")
            case TemperatureEvent(data=td):
                print(f"温度:{td.temperatures.to_list()}")

    hand.stop_polling()
    hand.stop_stream()

SensorSource 可选值及默认频率

说明默认频率
SensorSource.ANGLE角度60 Hz
SensorSource.TORQUE扭矩不默认轮询
SensorSource.SPEED速度不默认轮询
SensorSource.ACCELERATION加速度不默认轮询
SensorSource.TEMPERATURE温度不默认轮询
SensorSource.FAULT故障不默认轮询
SensorSource.FORCE_SENSOR力传感器30 Hz

快照

获取所有传感器最新缓存数据:

snap = hand.get_snapshot()
print(snap.angle)  # AngleData | None
print(snap.temperature)  # TemperatureData | None

角度控制

通过 hand.angle 控制和读取 O6 灵巧手的 6 个关节电机角度。

  • 角度范围: 0-100
  • 单位: 无量纲(映射到关节电机实际角度)

设置角度

from linkerbot import O6
from linkerbot.hand.o6 import O6Angle

# 使用列表
hand.angle.set_angles([50.0, 30.0, 60.0, 60.0, 60.0, 60.0])

# 使用 O6Angle 对象
angles = O6Angle(
    thumb_flex=50.0,  # 拇指屈曲
    thumb_abd=30.0,  # 拇指侧摆
    index=60.0,  # 食指
    middle=60.0,  # 中指
    ring=60.0,  # 无名指
    pinky=60.0,  # 小指
)
hand.angle.set_angles(angles)

读取角度

阻塞读取

from linkerbot import O6
from linkerbot.exceptions import TimeoutError

try:
    data = hand.angle.get_blocking(timeout_ms=500)
    print(f"拇指屈曲:{data.angles.thumb_flex}")
    print(f"全部角度:{data.angles.to_list()}")
except TimeoutError:
    print("读取超时")

缓存读取

data = hand.angle.get_snapshot()
if data:
    print(f"角度:{data.angles.to_list()}")
    print(f"时间戳:{data.timestamp}")

流式读取

通过顶层 hand.stream() 统一接收所有传感器事件:

from linkerbot.hand.o6 import SensorSource, AngleEvent

hand.start_polling({SensorSource.ANGLE: 0.1})

for event in hand.stream():
    match event:
        case AngleEvent(data=data):
            print(f"角度:{data.angles.to_list()}")

hand.stop_polling()
hand.stop_stream()

完整示例

from linkerbot import O6

with O6(side="left", interface_name="can0") as hand:
    # 设置角度
    hand.angle.set_angles([0.0, 0.0, 0.0, 0.0, 0.0, 0.0])

    # 读取当前角度
    data = hand.angle.get_blocking(timeout_ms=500)
    print(f"当前角度:{data.angles.to_list()}")

    # 渐进移动
    for i in range(0, 101, 10):
        hand.angle.set_angles([float(i)] * 6)

速度控制

通过 hand.speed 控制和读取 O6 灵巧手各关节电机的运动速度。

  • 速度范围: 0-100
  • 最大转速: 186.66 RPM(对应值 100)

设置速度

from linkerbot import O6
from linkerbot.hand.o6 import O6Speed

# 使用列表
hand.speed.set_speeds([50.0, 50.0, 50.0, 50.0, 50.0, 50.0])

# 使用 O6Speed 对象
speed = O6Speed(
    thumb_flex=30.0,  # 拇指弯曲
    thumb_abd=30.0,  # 拇指侧摆
    index=80.0,  # 食指
    middle=80.0,  # 中指
    ring=80.0,  # 无名指
    pinky=80.0,  # 小指
)
hand.speed.set_speeds(speed)

读取速度

阻塞读取

from linkerbot import O6
from linkerbot.exceptions import TimeoutError

try:
    data = hand.speed.get_blocking(timeout_ms=500)
    print(f"拇指弯曲速度:{data.speeds.thumb_flex}")
    print(f"全部速度:{data.speeds.to_list()}")
except TimeoutError:
    print("读取超时")

缓存读取

data = hand.speed.get_snapshot()
if data:
    print(f"速度:{data.speeds.to_list()}")
    print(f"时间戳:{data.timestamp}")

流式读取

通过顶层 hand.stream() 统一接收所有传感器事件:

from linkerbot.hand.o6 import SensorSource, SpeedEvent

hand.start_polling({SensorSource.SPEED: 0.1})

for event in hand.stream():
    match event:
        case SpeedEvent(data=data):
            print(f"速度:{data.speeds.to_list()}")

hand.stop_polling()
hand.stop_stream()

RPM 单位转换

O6Speed 支持 RPM(转/分钟)单位的转换。

转换为 RPM

from linkerbot.hand.o6 import O6Speed

speed = O6Speed(50.0, 50.0, 50.0, 50.0, 50.0, 50.0)
rpm_values = speed.to_rpm()
print(rpm_values)  # [93.33, 93.33, 93.33, 93.33, 93.33, 93.33]

从 RPM 创建

from linkerbot.hand.o6 import O6Speed

# 设置所有关节电机为 90 RPM
speed = O6Speed.from_rpm([90.0, 90.0, 90.0, 90.0, 90.0, 90.0])
hand.speed.set_speeds(speed)

# 拇指慢速,其他手指快速
speed = O6Speed.from_rpm([60.0, 60.0, 120.0, 120.0, 120.0, 120.0])
hand.speed.set_speeds(speed)

完整示例

from linkerbot import O6
from linkerbot.hand.o6 import O6Speed

with O6(side="left", interface_name="can0") as hand:
    # 使用 RPM 设置速度
    speed = O6Speed.from_rpm([90.0, 90.0, 120.0, 120.0, 120.0, 120.0])
    hand.speed.set_speeds(speed)

    # 读取当前速度
    data = hand.speed.get_blocking(timeout_ms=500)
    print(f"当前速度:{data.speeds.to_list()}")
    print(f"RPM: {data.speeds.to_rpm()}")

加速度控制

通过 hand.acceleration 控制和读取 O6 灵巧手的 6 个关节电机加速度。

  • 加速度范围: 0-100
  • 最大加速度: 2209.8 deg/s²(对应值 100)

设置加速度

from linkerbot import O6
from linkerbot.hand.o6 import O6Acceleration

# 使用列表
hand.acceleration.set_accelerations([80.0, 80.0, 80.0, 80.0, 80.0, 80.0])

# 使用 O6Acceleration 对象
accel = O6Acceleration(
    thumb_flex=80.0,  # 拇指屈曲
    thumb_abd=80.0,  # 拇指侧摆
    index=80.0,  # 食指
    middle=80.0,  # 中指
    ring=80.0,  # 无名指
    pinky=80.0,  # 小指
)
hand.acceleration.set_accelerations(accel)

读取加速度

阻塞读取

from linkerbot import O6
from linkerbot.exceptions import TimeoutError

try:
    data = hand.acceleration.get_blocking(timeout_ms=500)
    print(f"拇指屈曲:{data.accelerations.thumb_flex}")
    print(f"全部加速度:{data.accelerations.to_list()}")
except TimeoutError:
    print("读取超时")

缓存读取

data = hand.acceleration.get_snapshot()
if data:
    print(f"加速度:{data.accelerations.to_list()}")
    print(f"时间戳:{data.timestamp}")

流式读取

通过顶层 hand.stream() 统一接收所有传感器事件:

from linkerbot.hand.o6 import SensorSource, AccelerationEvent

hand.start_polling({SensorSource.ACCELERATION: 0.1})

for event in hand.stream():
    match event:
        case AccelerationEvent(data=data):
            print(f"加速度:{data.accelerations.to_list()}")

hand.stop_polling()
hand.stop_stream()

deg/s² 单位转换

O6Acceleration 支持与物理单位 deg/s²(度每秒平方)之间的转换。

转换为 deg/s²

from linkerbot.hand.o6 import O6Acceleration

accel = O6Acceleration(50.0, 50.0, 50.0, 50.0, 50.0, 50.0)
deg_s2_values = accel.to_deg_per_sec2()
print(deg_s2_values)  # [1104.9, 1104.9, 1104.9, 1104.9, 1104.9, 1104.9]

从 deg/s² 创建

from linkerbot.hand.o6 import O6Acceleration

# 设置所有关节电机加速度为 1000 deg/s²
accel = O6Acceleration.from_deg_per_sec2([1000.0] * 6)

# 设置不同的加速度
accel = O6Acceleration.from_deg_per_sec2(
    [
        1500.0,  # 拇指屈曲
        1200.0,  # 拇指侧摆
        1800.0,  # 食指
        1800.0,  # 中指
        1800.0,  # 无名指
        1800.0,  # 小指
    ]
)

完整示例

from linkerbot import O6
from linkerbot.hand.o6 import O6Acceleration

with O6(side="left", interface_name="can0") as hand:
    # 设置较低加速度(平滑运动)
    hand.acceleration.set_accelerations([30.0] * 6)

    # 使用 deg/s² 单位设置
    accel = O6Acceleration.from_deg_per_sec2([1000.0] * 6)
    hand.acceleration.set_accelerations(accel)

    # 读取当前加速度
    data = hand.acceleration.get_blocking(timeout_ms=500)
    print(f"当前加速度:{data.accelerations.to_list()}")
    print(f"对应 deg/s²: {data.accelerations.to_deg_per_sec2()}")

扭矩控制

通过 hand.torque 控制和读取 O6 灵巧手的 6 个关节电机扭矩。

  • 扭矩范围: 0-100(无量纲百分比)
  • 最大电流: 1657.5 mA(对应 100)

设置扭矩

from linkerbot import O6
from linkerbot.hand.o6 import O6Torque

# 使用列表
hand.torque.set_torques([50.0, 30.0, 60.0, 60.0, 60.0, 60.0])

# 使用 O6Torque 对象
torques = O6Torque(
    thumb_flex=50.0,  # 拇指屈曲
    thumb_abd=30.0,  # 拇指侧摆
    index=60.0,  # 食指
    middle=60.0,  # 中指
    ring=60.0,  # 无名指
    pinky=60.0,  # 小指
)
hand.torque.set_torques(torques)

# 使用 mA 单位
torques = O6Torque.from_milliamps([800.0, 800.0, 1000.0, 1000.0, 1000.0, 1000.0])
hand.torque.set_torques(torques)

读取扭矩

阻塞读取

from linkerbot.exceptions import TimeoutError

try:
    data = hand.torque.get_blocking(timeout_ms=500)
    print(f"拇指屈曲:{data.torques.thumb_flex}")
    print(f"全部扭矩:{data.torques.to_list()}")
except TimeoutError:
    print("读取超时")

缓存读取

data = hand.torque.get_snapshot()
if data:
    print(f"扭矩:{data.torques.to_list()}")
    print(f"时间戳:{data.timestamp}")

流式读取

通过顶层 hand.stream() 统一接收所有传感器事件:

from linkerbot.hand.o6 import SensorSource, TorqueEvent

hand.start_polling({SensorSource.TORQUE: 0.1})

for event in hand.stream():
    match event:
        case TorqueEvent(data=data):
            print(f"扭矩:{data.torques.to_list()}")

hand.stop_polling()
hand.stop_stream()

mA 单位转换

O6 支持扭矩值与 mA 电流单位的相互转换。

从 mA 创建

from linkerbot.hand.o6 import O6Torque

# 从 mA 值创建(范围 0-1657.5 mA)
torques = O6Torque.from_milliamps([800.0, 800.0, 1000.0, 1000.0, 1200.0, 1200.0])
print(f"归一化值:{torques.to_list()}")  # 转换为 0-100 范围

转换为 mA

torques = O6Torque(50.0, 50.0, 50.0, 50.0, 50.0, 50.0)
ma_values = torques.to_milliamps()
print(f"mA 值:{ma_values}")  # [828.75, 828.75, ...]

完整示例

from linkerbot import O6
from linkerbot.hand.o6 import O6Torque

with O6(side="left", interface_name="can0") as hand:
    # 设置扭矩
    hand.torque.set_torques([50.0, 50.0, 50.0, 50.0, 50.0, 50.0])

    # 读取当前扭矩
    data = hand.torque.get_blocking(timeout_ms=500)
    print(f"当前扭矩:{data.torques.to_list()}")
    print(f"对应 mA: {data.torques.to_milliamps()}")

    # 使用 mA 单位设置
    torques = O6Torque.from_milliamps([1000.0] * 6)
    hand.torque.set_torques(torques)

温度读取

通过 hand.temperature 读取 O6 灵巧手 6 个关节电机的温度数据(单位:°C)。

温度属性

属性说明
thumb_flex拇指屈曲关节电机
thumb_abd拇指侧摆关节电机
index食指关节电机
middle中指关节电机
ring无名指关节电机
pinky小指关节电机

读取温度

阻塞读取

data = hand.temperature.get_blocking(timeout_ms=500)
print(f"拇指温度:{data.temperatures.thumb_flex}°C")

参数

  • timeout_ms: 超时时间(毫秒),默认 100

返回值

  • TemperatureData: 包含 temperaturestimestamp

异常

  • TimeoutError: 超时未响应

缓存读取

非阻塞读取最近一次缓存的温度数据。

data = hand.temperature.get_snapshot()
if data:
    print(f"温度:{data.temperatures.to_list()}")

返回值

  • TemperatureDataNone(无缓存数据时)

流式读取

通过顶层 hand.stream() 统一接收所有传感器事件:

from linkerbot.hand.o6 import SensorSource, TemperatureEvent

hand.start_polling({SensorSource.TEMPERATURE: 0.1})

for event in hand.stream():
    match event:
        case TemperatureEvent(data=data):
            print(f"温度:{data.temperatures.to_list()}")

hand.stop_polling()
hand.stop_stream()

示例

读取所有关节电机温度

from linkerbot import O6

with O6(side="left", interface_name="can0") as hand:
    data = hand.temperature.get_blocking(timeout_ms=500)

    # 按属性访问
    print(f"拇指屈曲:{data.temperatures.thumb_flex}°C")
    print(f"食指:{data.temperatures.index}°C")

    # 转换为列表
    temps = data.temperatures.to_list()
    print(f"全部温度:{temps}")

    # 索引访问
    print(f"第一个关节电机:{data.temperatures[0]}°C")

温度监控

from linkerbot import O6
from linkerbot.hand.o6 import SensorSource, TemperatureEvent

with O6(side="left", interface_name="can0") as hand:
    hand.start_polling({SensorSource.TEMPERATURE: 0.1})

    try:
        for event in hand.stream():
            match event:
                case TemperatureEvent(data=data):
                    for i, temp in enumerate(data.temperatures.to_list()):
                        if temp > 60.0:
                            print(f"警告:关节电机 {i} 过热 ({temp}°C)")
    except KeyboardInterrupt:
        pass
    finally:
        hand.stop_polling()
        hand.stop_stream()

力传感器

O6 灵巧手配备 5 个手指的力传感器(thumb, index, middle, ring, pinky),支持阻塞读取和缓存读取两种模式。

概述

通过 hand.force_sensor 访问力传感器功能:

from linkerbot import O6

with O6(side="left", interface_name="can0") as hand:
    data = hand.force_sensor.get_blocking()

数据结构

ForceSensorData - 单个手指的传感器数据:

  • values: 形状 (10, 4) 的 NumPy 数组(uint8)
  • timestamp: Unix 时间戳

AllFingersData - 全部 5 个手指的数据:

  • thumb, index, middle, ring, pinky: 各手指的 ForceSensorData

读取数据

阻塞读取

data = hand.force_sensor.get_blocking(timeout_ms=1000)
print(data.thumb.values)  # 拇指数据
print(data.index.values)  # 食指数据
参数类型默认值说明
timeout_msfloat1000超时时间(毫秒)

异常: TimeoutError(超时)、ValidationError(参数无效)

缓存读取

获取最近一次接收的数据(非阻塞):

data = hand.force_sensor.get_snapshot()
if data:
    print(f"拇指:{data.thumb.values[0]}")
    print(f"食指:{data.index.values[0]}")

返回值: AllFingersDataNone(任一手指无数据时)

流式读取

通过顶层 hand.stream() 统一接收所有传感器事件:

from linkerbot.hand.o6 import SensorSource, ForceSensorEvent

hand.start_polling({SensorSource.FORCE_SENSOR: 0.1})

for event in hand.stream():
    match event:
        case ForceSensorEvent(data=data):
            print(data.thumb.values)

hand.stop_polling()
hand.stop_stream()

示例

阻塞读取所有手指

from linkerbot import O6

with O6(side="left", interface_name="can0") as hand:
    data = hand.force_sensor.get_blocking(timeout_ms=1000)
    print(f"拇指:{data.thumb.values.shape}")
    print(f"食指:{data.index.values.shape}")

流式采集指定时长

import time
from linkerbot import O6
from linkerbot.hand.o6 import SensorSource, ForceSensorEvent

with O6(side="left", interface_name="can0") as hand:
    hand.start_polling({SensorSource.FORCE_SENSOR: 0.05})
    start = time.time()

    try:
        for event in hand.stream():
            match event:
                case ForceSensorEvent(data=data):
                    print(f"拇指:{data.thumb.values[0]}")
            if time.time() - start > 5:  # 采集 5 秒
                break
    finally:
        hand.stop_polling()
        hand.stop_stream()

故障管理

O6 灵巧手的故障检测功能。

概述

通过 hand.fault 访问故障管理功能:

  • 读取故障状态(阻塞式、缓存)

故障码表

故障码说明
NONE0无故障
VOLTAGE_ABNORMAL1过压/欠压
ENCODER_ABNORMAL2磁编码器异常
OVERTEMPERATURE4温度过热
OVERCURRENT8电流过流
OVERLOAD32负载过载

读取故障

阻塞式读取

data = hand.fault.get_blocking(timeout_ms=500)

参数

  • timeout_ms:超时时间(毫秒),默认 100

返回值FaultData 对象,包含:

  • faultsO6Fault 故障数据
  • timestamp:时间戳

异常

  • TimeoutError:超时未收到响应

缓存读取

data = hand.fault.get_snapshot()

返回最近缓存的故障数据,无数据时返回 None

流式读取

通过顶层 hand.stream() 统一接收所有传感器事件:

from linkerbot.hand.o6 import SensorSource, FaultEvent

hand.start_polling({SensorSource.FAULT: 0.2})

for event in hand.stream():
    match event:
        case FaultEvent(data=data):
            if data.faults.has_any_fault():
                for code in data.faults.to_list():
                    if code.has_fault():
                        print(code.get_fault_names())

hand.stop_polling()
hand.stop_stream()

故障数据

O6Fault 属性

属性说明
thumb_flex拇指弯曲
thumb_abd拇指侧摆
index食指
middle中指
ring无名指
pinky小指

O6Fault 方法

# 检查是否有任何故障
faults.has_any_fault()  # -> bool

# 转为列表
faults.to_list()  # -> list[FaultCode]

# 索引访问
faults[0]  # thumb_flex

FaultCode 方法

# 检查单个关节电机是否有故障
faults.thumb_flex.has_fault()  # -> bool

# 获取故障名称
faults.thumb_flex.get_fault_names()  # -> list[str]

示例

检查故障状态

from linkerbot import O6

with O6(side="left", interface_name="can0") as hand:
    # 读取故障状态
    data = hand.fault.get_blocking(timeout_ms=500)

    if data.faults.has_any_fault():
        print("检测到故障:")
        if data.faults.thumb_flex.has_fault():
            print(f"  拇指弯曲:{data.faults.thumb_flex.get_fault_names()}")
        if data.faults.index.has_fault():
            print(f"  食指:{data.faults.index.get_fault_names()}")
    else:
        print("无故障")

持续监控

from linkerbot import O6
from linkerbot.hand.o6 import SensorSource, FaultEvent

with O6(side="left", interface_name="can0") as hand:
    hand.start_polling({SensorSource.FAULT: 0.2})

    try:
        for event in hand.stream():
            match event:
                case FaultEvent(data=data):
                    if data.faults.has_any_fault():
                        for code in data.faults.to_list():
                            if code.has_fault():
                                print(code.get_fault_names())
    except KeyboardInterrupt:
        pass
    finally:
        hand.stop_polling()
        hand.stop_stream()

版本信息

获取 O6 灵巧手的版本信息。

概述

通过 hand.version 访问版本管理功能:

  • 获取设备完整信息(序列号、PCB/固件/机械版本)

获取设备信息

hand.version.get_device_info() -> DeviceInfo

返回 DeviceInfo 对象,包含:

属性类型说明
serial_numberstr设备序列号
pcb_versionVersionPCB 硬件版本
firmware_versionVersion固件版本
mechanical_versionVersion机械结构版本
timestampfloat获取时间(Unix 时间戳)

Version 对象包含 majorminorpatch 属性,字符串格式为 V{major}.{minor}.{patch}

异常

  • TimeoutError:请求超时

示例

读取设备信息

from linkerbot import O6

hand = O6(side="left", interface_name="can0")

info = hand.version.get_device_info()
print(f"序列号:{info.serial_number}")
print(f"PCB 版本:{info.pcb_version}")
print(f"固件版本:{info.firmware_version}")
print(f"机械版本:{info.mechanical_version}")

hand.close()

A7 Lite 机械臂

A7 Lite 机械臂文档。

快速开始

from linkerbot import A7lite

with A7lite(
    side="left",
    interface_name="can0",
) as arm:
    arm.enable()  # 使能机械臂所有 7 个关节电机
    arm.home()  # 将机械臂移动到零位
    arm.disable()  # 失能机械臂所有 7 个关节电机

构造参数

参数类型默认值说明
side"left" | "right"必填左臂或右臂
interface_namestr必填CAN 接口名,如 "can0"
interface_typestr"socketcan"CAN 接口类型
tcp_offsetlist[float][0.0, 0.0, 0.0]TCP 偏移量
world_frame"urdf" | "maestro""urdf"世界坐标系约定

构造时会自动检查所有 7 个电机是否在线。

前置依赖: A7 Lite 依赖 Pinocchio 进行运动学计算,需要安装 kinetix 可选依赖:

pip install linkerbot-py[kinetix]

Windows 用户:Pinocchio 不支持 pip 安装,请使用 conda install pinocchio -c conda-forge

异常:

  • ImportError: 未安装 kinetix 可选依赖(Pinocchio)
  • ValueError: sideworld_frame 参数非法
  • StateError: 电机未响应 CAN 总线 / 上报数据超时

URDF 文件路径src/linkerbot/arm/kinetix/urdf/a7_lite__left.urdfa7_lite__right.urdf

坐标系说明

A7 Lite 支持两种世界坐标系,通过 world_frame 参数选择。

URDF 坐标系(默认)

URDF 模型文件的坐标系,原点位于机器人左右臂肩关节连线中点处,如图所示:

coordinate

  • X 轴:机器人背后指向机器人前方为 X 轴正方向
  • Y 轴:机器人视角左手边(面对机器人站立右手边)为 Y 轴正方向
  • Z 轴:由右手定则确定上方为 Z 轴正方向
  • Rx, Ry, Rz:表示按照 Z,Y,X 轴顺序,逆时针为正进行旋转

Maestro 坐标系

Maestro 坐标系,相对 URDF 坐标系进行了两步变换,如图所示:

coordinate

  1. 原点平移:将世界坐标原点从基座中心平移到 Joint 2(肩部第二关节)中心处
  2. Z 轴旋转 90°:在新原点将 URDF 坐标轴绕 Z 轴逆时针旋转 +90°

变换后:

  • X 轴:机器人视角右手边(面对机器人站立左手边)为 X 轴正方向
  • Y 轴:机器人背后指向机器人前方,为 Y 轴正方向
  • Z 轴:由右手定则确定上方为 Z 轴正方向

注意:这两种坐标的定义方式主要影响move_pmove_l涉及到Pose的部分,不影响move_j控制

关节定义

零位定义

所有关节角度为 [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0] 时,机械臂应当处于“自然下垂“姿态(关节相互垂直);调用 home() 可回到零位,如下图所示:

home

角度正方向定义

角度正方向遵从右手定则:以右手拇指指向旋转轴正方向,四指弯曲方向为正角度增大方向。

由于 URDF 中各关节轴可能为负方向(如 0 -1 0),实际正角度方向与直觉相反:

  • 轴为 0 -1 0(Y 轴负方向):发送更大角度时,关节绕 Y 轴方向旋转,即 Y 轴正方向指向自己时顺时针旋转。
  • 轴为 -1 0 0(X 轴负方向):发送更大角度增大时,关节绕 X 轴方向旋转,即 X 轴正方向指向自己时顺时针旋转。
  • 轴为 0 0 1(Z 轴正方向):发送更大角度时,关节绕 Z 轴正方向旋转,即从上方看为逆时针。

A7 Lite 左臂关节一览(URDF 坐标系)

索引关节名称物理含义旋转轴(URDF)角度范围(rad)
0L1_JOINT肩部俯仰Y 轴负方向(0 -1 0[-2.18, 3.75]
1L2_JOINT肩部横滚X 轴负方向(-1 0 0[-3.33, 0.19]
2L3_JOINT肩部偏转(上臂扭转)Z 轴正方向(0 0 1[-2.26, 2.26]
3L4_JOINT肘部俯仰Y 轴负方向(0 -1 0[-0.78, 1.74]
4L5_JOINT肘部偏转(前臂扭转)Z 轴正方向(0 0 1[-2.96, 2.96]
5L6_JOINT腕部俯仰Y 轴负方向(0 -1 0[-1.57, 1.57]
6L7_JOINT腕部横滚X 轴负方向(-1 0 0[-1.57, 1.57]

A7 Lite 右臂关节一览(URDF 坐标系)

索引关节名称物理含义旋转轴(URDF)角度范围(rad)
0R1_JOINT肩部俯仰Y 轴正方向(0 1 0[-3.75, 2.18]
1R2_JOINT肩部横滚X 轴负方向(-1 0 0[-0.19, 3.33]
2R3_JOINT肩部偏转(上臂扭转)Z 轴正方向(0 0 1[-2.26, 2.26]
3R4_JOINT肘部俯仰Y 轴正方向(0 1 0[-1.74, 0.78]
4R5_JOINT肘部偏转(前臂扭转)Z 轴正方向(0 0 1[-2.96, 2.96]
5R6_JOINT腕部俯仰Y 轴正方向(0 1 0[-1.57, 1.57]
6R7_JOINT腕部横滚X 轴负方向(-1 0 0[-1.57, 1.57]

Pose(TCP 位姿)表示

Pose 表示 TCP(Tool Center Point,工具中心点)位姿,是一个 Pydantic 模型,包含 6 个字段:

from linkerbot import Pose

pose = Pose(x=0.3, y=0.1, z=0.5, rx=0.0, ry=0.5, rz=0.0)
字段含义单位
x, y, z空间坐标位置米(m)
rx绕 X 轴旋转(Roll),范围 [-π, π]弧度(rad)
ry绕 Y 轴旋转(Pitch),范围 [-π/2, π/2]弧度(rad)
rz绕 Z 轴旋转(Yaw),范围 [-π, π]弧度(rad)
  • 姿态使用外旋 ZYX 欧拉角表示:先绕世界坐标轴的 Z 轴旋转 rz,再绕世界坐标轴的 Y 轴旋转 ry,最后绕世界坐标轴的 X 轴旋转 rx。

生命周期管理

API 参考

方法说明
enable()使能所有 7 个关节电机,会导致短暂失能。
disable()失能所有 7 个关节电机。
emergency_stop()紧急停止机械臂。此方法会阻塞约 2 秒。
calibrate_zero()将当前位置设为零位。需要机械臂处于失能状态。
reset_error()重置所有关节的错误状态。
close()关闭连接:等待运动完成后停止 CAN 调度器。使用 with 语句时会自动调用。

注意事项

  1. 调用 arm.enable() 使能时会导致机械臂短暂失能。
  2. 设置零位需要机械臂处于失能状态。
  3. emergency_stop() 是阻塞调用,会等待约 2 秒。

控制模式

当前仅支持 PP(Profile Position)模式,调用 enable() 时会自动设置。

ControlMode 枚举可从 linkerbot 导入:

from linkerbot import ControlMode

CAN 总线

interface_name 指定 CAN 接口名称(如 "can0"),interface_type 指定接口类型(默认 "socketcan")。

更多信息(包括 Windows 用法)请参阅 CAN 总线配置

功能模块

模块文档
运动控制(move_j/move_p/move_l)motion.md
运动学(FK/IK/关节限位)kinematics.md
状态读取(角度/速度/扭矩/温度)state.md
参数配置(速度/加速度/PID)config.md

运动控制

回零

将所有关节移动到零位([0.0] * 7)。

from linkerbot import A7lite

with A7lite(side="left", interface_name="can0") as arm:
    arm.enable()
    arm.home()  # 阻塞模式
    arm.home(blocking=False)  # 非阻塞模式

参数:

  • blocking: 是否阻塞等待运动完成,默认 True

move_j

通过发送每个关节的目标角度,控制机械臂运动。

from linkerbot import A7lite

with A7lite(side="left", interface_name="can0") as arm:
    arm.enable()
    arm.move_j([0.0, 0.0, 0.0, 1.57, 0.0, 0.0, 0.0], blocking=True)

参数:

  • target_joints: 7 个关节的目标角度(rad),类型 list[float]
  • blocking: 是否阻塞直到运动完成,默认 True,类型 bool

异常:

  • StateError: 机械臂运动中再次调用时
  • ValidationError: 目标角度超出关节限位时

move_p

通过发送机械臂 TCP 6D 位姿(x, y, z, rx, ry, rz),控制机械臂运动。通过逆运动学求解目标关节角度后执行关节运动,末端路径不保证为直线。

from linkerbot import A7lite
from linkerbot import Pose

with A7lite(side="left", interface_name="can0") as arm:
    arm.enable()
    target = Pose(x=0.3, y=0.1, z=0.5, rx=0.0, ry=0.5, rz=0.0)
    arm.move_p(target)

参数:

  • target_pose: 目标 TCP 位姿,类型 Pose
  • current_angles: IK 初始猜测关节角度,类型 list[float] | NoneNone 时从电机读取,
  • blocking: 是否阻塞直到运动完成,类型 bool,默认 True

异常:

  • StateError: 机械臂运动中再次调用时
  • RuntimeError: IK 求解失败时

move_l

通过发送机械臂 TCP 6D 位姿(x, y, z, rx, ry, rz),控制机械臂直线运动。末端沿笛卡尔空间直线运动,位置使用梯形速度规划,姿态使用球面线性插值(Slerp)。move_l 始终阻塞直到运动完成,无 blocking 参数。

from linkerbot import A7lite, Pose

with A7lite(side="left", interface_name="can0") as arm:
    arm.enable()
    target = Pose(x=0.0, y=0.33, z=-0.25, rx=1.85, ry=0.0, rz=-1.57)
    arm.move_l(target, max_velocity=0.1, acceleration=0.2)

参数:

  • target_pose: 目标 TCP 位姿,类型 Pose
  • max_velocity: TCP 线性运动梯形速度规划的最大速度(m/s),默认 0.05,范围 (0, 0.4]
  • max_angular_velocity: TCP 旋转运动梯形速度规划的最大角速度(rad/s),默认 0.3,范围 (0, 3.0]
  • acceleration: TCP 线性运动梯形速度规划的最大加速度(m/s²),默认 0.1,范围 (0, 1.0]
  • angular_acceleration: TCP 旋转运动梯形速度规划的最大角加速度(rad/s²),默认 0.1,范围 (0, 1.0]
  • current_pose: 起始 TCP 位姿,None 时由 current_angles 或电机读取计算,类型 Pose | None
  • current_angles: 起始关节角度,None 时从电机读取,类型 list[float] | None

异常:

  • StateError: 机械臂运动中再次调用时
  • ValidationError: 参数超出范围时
  • RuntimeError: 路径点 IK 求解失败时

运动状态检测

返回机械臂当前是否处于运动状态。

if arm.is_moving():
    print("正在运动中...")

返回值:

  • bool: 是否在运动中

等待运动完成

阻塞当前线程,直到运动计时器归零。通常配合 blocking=False 使用。

arm.move_j([0.0, 0.0, 0.0, 1.57, 0.0, 0.0, 0.0], blocking=False)
# ... 执行其他操作 ...
arm.wait_motion_done()  # 等待运动结束

示例

非阻塞运动与等待

from linkerbot import A7lite, Pose

with A7lite(side="left", interface_name="can0") as arm:
    arm.enable()
    arm.home()

    # 非阻塞 move_j:发送后立即返回,可执行其他操作
    arm.move_j([0.0, 0.0, 0.0, 1.57, 0.0, 0.0, 0.0], blocking=False)
    print("move_j 已发送,正在运动中...")
    arm.wait_motion_done()  # 等待运动完成

    # 阻塞 move_p:调用直接等到运动完成,无需 wait_motion_done
    target = Pose(x=0.0, y=0.33, z=-0.25, rx=1.85, ry=0.0, rz=-1.57)
    arm.move_p(target)

    # move_l 始终阻塞
    target = Pose(x=0.1, y=0.33, z=-0.2, rx=1.85, ry=0.0, rz=-1.57)
    arm.move_l(target, max_velocity=0.1, acceleration=0.2)

    arm.home()

运动学

正运动学

根据关节角度计算 TCP 位姿。

pose = arm.forward_kinematics([0.0, 0.0, 0.0, 1.57, 0.0, 0.0, 0.0])
print(f"TCP: ({pose.x:.3f}, {pose.y:.3f}, {pose.z:.3f})")

参数:

  • angles: 7 个关节角度(rad),类型 list[float]

返回值:

  • Pose: TCP 位姿

逆运动学

根据 TCP 位姿求解关节角度。

from linkerbot import Pose

target = Pose(x=0.0, y=0.33, z=-0.25, rx=1.85, ry=0.0, rz=-1.57)
angles = arm.inverse_kinematics(target)
print(f"关节角度:{angles}")

参数:

  • pose: 目标 TCP 位姿,类型 Pose
  • current_angles: IK 初始猜测关节角度,类型 list[float] | NoneNone 时从电机读取

返回值:

  • list[float]: 7 个关节角度(rad)

关节限位

# 获取当前关节限位
limits = arm.get_joint_limits()
print(limits)  # [(lower0, upper0), (lower1, upper1), ...]

# 设置自定义关节限位
arm.set_joint_limits([(-1.0, 1.0)] * 7)

状态读取

完整状态快照

一次性读取完整的机械臂状态,包含 TCP 位姿、所有关节的角度、速度、加速度、扭矩和温度。

from linkerbot import A7lite

with A7lite(side="left", interface_name="can0") as arm:
    arm.enable()
    state = arm.get_state()
    print(state.pose)
    print(f"第一个关节实际角度:{state.joint_angles[0].angle:.4f} rad")

返回值

  • State: 包含 posejoint_anglesjoint_control_anglesjoint_velocitiesjoint_control_velocitiesjoint_control_accelerationjoint_torquesjoint_temperatures

State 数据模型

属性说明
poseTCP 位姿(Pose)
joint_angles7 个关节的实际角度(list[AngleState])
joint_control_angles7 个关节的控制角度(list[AngleState])
joint_velocities7 个关节的实际速度(list[VelocityState])
joint_control_velocities7 个关节的控制速度(list[VelocityState])
joint_control_acceleration7 个关节的控制加速度(list[AccelerationState])
joint_torques7 个关节的实际扭矩(list[TorqueState])
joint_temperatures7 个关节的温度(list[TemperatureState])

各子状态模型(AngleState、VelocityState 等)均包含值字段和 timestamp(UNIX 时间戳,秒)。

子状态模型字段

模型值字段时间戳字段
AngleState.angle.timestamp
VelocityState.velocity.timestamp
AccelerationState.acceleration.timestamp
TorqueState.torque.timestamp
TemperatureState.temperature.timestamp

单项状态读取

get_state() 外,以下方法分别返回单一类型的状态数据,均返回 7 个关节的列表(get_pose() 除外)。

from linkerbot import A7lite

with A7lite(side="left", interface_name="can0") as arm:
    arm.enable()
    angles = arm.get_angles()
    velocities = arm.get_velocities()
    temps = arm.get_temperatures()
    pose = arm.get_pose()
    print(f"第二个关节实际角度:{angles[1]:.4f} rad")
    print(f"第三个关节实际速度:{velocities[2]:.4f} rad/s")
    print(f"第四个关节实际温度:{temps[3]:.1f} °C")
    print(f"TCP: ({pose.x:.3f}m, {pose.y:.3f}m, {pose.z:.3f}m)")

返回值

方法返回值单位
get_angles()7 个关节的实际角度rad
get_control_angles()7 个关节的控制角度rad
get_velocities()7 个关节的实际速度rad/s
get_control_velocities()7 个关节的控制速度rad/s
get_control_acceleration()7 个关节的控制加速度rad/s²
get_torques()7 个关节的实际扭矩Nm
get_temperatures()7 个关节的温度°C
get_pose()TCP 位姿m, rad

传感器数据

A7 Lite 使用电机主动上报模式,构造时自动启动。

数据类型单位上报频率
角度rad由电机上报周期决定
速度rad/s同上
扭矩Nm同上
温度°C同上

参数配置

速度与加速度限制

设置 7 个关节的速度和加速度限制,影响 move_jmove_p 等运动。

from linkerbot import A7lite

with A7lite(side="left", interface_name="can0") as arm:
    arm.enable()
    # 将所有关节速度限制设为 2.0 rad/s
    arm.set_velocities([2.0] * 7)
    # 分别设置各关节加速度限制
    arm.set_accelerations([10.0, 9.0, 8.0, 7.0, 6.0, 5.0, 4.0])
方法参数范围说明
set_velocities(velocities)7 个关节的速度上限(rad/s)[0.0, 50.0]默认 0.5
set_accelerations(accelerations)7 个关节的加速度上限(rad/s²)[1.0, 50.0]默认 10.0

异常ValidationError(必须提供恰好 7 个值,或值超出范围时)

PID 参数设置

设置 7 个关节的 PID 参数。

from linkerbot import A7lite

with A7lite(side="left", interface_name="can0") as arm:
    arm.set_position_kps([100.0] * 7)
    arm.set_velocity_kps([0.5] * 7)
    arm.set_velocity_kis([0.01] * 7)
    arm.set_velocity_filt_gains([0.8] * 7)
方法参数说明
set_position_kps(kps)7 个关节的位置 Kp 值位置环比例增益
set_velocity_kps(kps)7 个关节的速度 Kp 值速度环比例增益
set_velocity_kis(kis)7 个关节的速度 Ki 值速度环积分增益
set_velocity_filt_gains(gains)7 个关节的速度滤波器增益增益越大滤波越强,响应越滞后

参数速查

参数类型默认值范围
速度限制0.5 rad/s[0.0, 50.0] rad/s
加速度限制10.0 rad/s²[1.0, 50.0] rad/s²
move_l 最大平移速度0.05 m/s(0, 0.4] m/s
move_l 最大角速度0.3 rad/s(0, 3.0] rad/s
move_l 平移加速度0.1 m/s²(0, 1.0] m/s²
move_l 角加速度0.1 rad/s²(0, 1.0] rad/s²

示例

设置 PID 参数、速度和加速度限制

from linkerbot import A7lite

with A7lite(side="left", interface_name="can0") as arm:
    arm.set_position_kps([80.0] * 7)
    arm.set_velocity_kps([0.4] * 7)
    arm.set_velocity_kis([0.008] * 7)
    arm.set_velocity_filt_gains([0.9] * 7)
    arm.set_velocities([1.0] * 7)
    arm.set_accelerations([5.0] * 7)

A7 机械臂

A7 是一款自研 7 自由度机械臂。

快速开始

from linkerbot import A7

with A7(
    side="left",
    interface_name="can0",
) as arm:
    arm.enable()  # 使能机械臂所有 7 个关节电机
    arm.home()  # 将机械臂移动到零位
    arm.disable()  # 失能机械臂所有 7 个关节电机

构造参数

参数类型默认值说明
side"left" | "right"必填左臂或右臂
interface_namestr必填CAN 接口名,如 "can0"
interface_typestr"socketcan"CAN 接口类型
tcp_offsetlist[float][0.0, 0.0, 0.0]TCP 偏移量
world_frame"urdf" | "maestro""urdf"世界坐标系约定

构造时会自动检查所有 7 个电机是否在线。

前置依赖: A7 依赖 Pinocchio 进行运动学计算,需要安装 kinetix 可选依赖:

pip install linkerbot-py[kinetix]

Windows 用户:Pinocchio 不支持 pip 安装,请使用 conda install pinocchio -c conda-forge

异常:

  • ImportError: 未安装 kinetix 可选依赖(Pinocchio)
  • ValueError: sideworld_frame 参数非法
  • StateError: 电机未响应 CAN 总线 / 初始数据读取失败

URDF 文件路径src/linkerbot/arm/kinetix/urdf/a7__left.urdfa7__right.urdf

坐标系说明

A7 支持两种世界坐标系,通过 world_frame 参数选择。

URDF 坐标系(默认)

URDF 模型文件的坐标系,原点位于机械臂基座(Base_Link)中心,如图所示:

coordinate

  • X 轴:机器人背后指向机器人前方为 X 轴正方向
  • Y 轴:机器人视角左手边(面对机器人站立右手边)为 Y 轴正方向
  • Z 轴:由右手定则确定上方为 Z 轴正方向
  • Rx, Ry, Rz:表示按照 Z,Y,X 轴顺序,逆时针为正进行旋转

Maestro 坐标系

Maestro 坐标系,相对 URDF 坐标系进行了两步变换,如图所示:

coordinate

  1. 原点平移:将世界坐标原点从基座中心平移到 Joint 2(肩部第二关节)中心处
  2. Z 轴旋转 90°:在新原点将 URDF 坐标轴绕 Z 轴逆时针旋转 +90°

变换后:

  • X 轴:机器人视角右手边(面对机器人站立左手边)为 X 轴正方向
  • Y 轴:机器人背后指向机器人前方,为 Y 轴正方向
  • Z 轴:由右手定则确定上方为 Z 轴正方向

注意:这两种坐标的定义方式主要影响move_pmove_l涉及到Pose的部分,不影响move_j控制

关节定义

零位定义

所有关节角度为 [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0] 时,机械臂应当处于“自然下垂“姿态(关节相互垂直);调用 home() 可回到零位,如下图所示:

home

角度正方向定义

角度正方向遵从右手定则:以右手拇指指向旋转轴正方向,四指弯曲方向为正角度增大方向。

  • 轴为 0 1 0(Y 轴正方向):角度增大时,从正 Y 轴看为逆时针旋转
  • 轴为 1 0 0(X 轴正方向):角度增大时,从正 X 轴看为逆时针旋转
  • 轴为 0 0 -1(Z 轴负方向):角度增大时,从上方看为顺时针旋转
  • 轴为 0 -1 0(Y 轴负方向):角度增大时,从正 Y 轴看为顺时针旋转

A7 左臂关节一览(URDF 坐标系)

索引关节名称物理含义旋转轴(URDF)角度范围(rad)
0Left_Shoulder_Pitch_Joint肩部俯仰Y 轴正方向(0 1 0[-2.97, 1.05]
1Left_Shoulder_Roll_Joint肩部横滚X 轴正方向(1 0 0[-0.35, 3.49]
2Left_Shoulder_Yaw_Joint肩部偏转(上臂扭转)Z 轴负方向(0 0 -1[-2.79, 2.79]
3Left_Elbow_Pitch_Joint肘部俯仰Y 轴负方向(0 -1 0[-0.21, 2.355]
4Left_Wrist_Yaw_Joint腕部偏转(前臂扭转)Z 轴负方向(0 0 -1[-2.79, 2.79]
5Left_Wrist_Pitch_Joint腕部俯仰Y 轴负方向(0 -1 0[-1.57, 1.57]
6Left_Wrist_Roll_Joint腕部横滚X 轴正方向(1 0 0[-1.57, 1.57]

A7 右臂关节一览(URDF 坐标系)

索引关节名称物理含义旋转轴(URDF)角度范围(rad)
0Right_Shoulder_Pitch_Joint肩部俯仰Y 轴负方向(0 -1 0[-1.05, 2.97]
1Right_Shoulder_Roll_Joint肩部横滚X 轴正方向(1 0 0[-3.49, 0.35]
2Right_Shoulder_Yaw_Joint肩部偏转(上臂扭转)Z 轴负方向(0 0 -1[-2.79, 2.79]
3Right_Elbow_Pitch_Joint肘部俯仰Y 轴负方向(0 -1 0[-0.21, 2.355]
4Right_Wrist_Yaw_Joint腕部偏转(前臂扭转)Z 轴负方向(0 0 -1[-2.79, 2.79]
5Right_Wrist_Pitch_Joint腕部俯仰Y 轴正方向(0 1 0[-1.57, 1.57]
6Right_Wrist_Roll_Joint腕部横滚X 轴正方向(1 0 0[-1.57, 1.57]

Pose(TCP 位姿)表示

Pose 表示 TCP(Tool Center Point,工具中心点)位姿,是一个 Pydantic 模型,包含 6 个字段:

from linkerbot import Pose

pose = Pose(x=0.3, y=0.1, z=0.5, rx=0.0, ry=0.5, rz=0.0)
字段含义单位
x, y, z空间坐标位置米(m)
rx绕 X 轴旋转(Roll),范围 [-π, π]弧度(rad)
ry绕 Y 轴旋转(Pitch),范围 [-π/2, π/2]弧度(rad)
rz绕 Z 轴旋转(Yaw),范围 [-π, π]弧度(rad)
  • 姿态使用外旋 ZYX 欧拉角表示:先绕世界坐标轴的 Z 轴旋转 rz,再绕世界坐标轴的 Y 轴旋转 ry,最后绕世界坐标轴的 X 轴旋转 rx。

生命周期管理

API 参考

方法说明
enable()使能所有 7 个关节电机,会导致短暂失能。
disable()失能所有 7 个关节电机。
emergency_stop()紧急停止机械臂,使机械臂锁定在当前位姿。
calibrate_zero()将当前位置设为零位。需要机械臂处于失能状态。
reset_error()重置所有关节的错误状态。
close()关闭连接:停止轮询、等待运动完成后停止 CAN 调度器。使用 with 语句时会自动调用。
save_params()将当前 PID 等参数保存到电机闪存,断电后仍生效。

注意事项

  1. 调用 arm.enable() 使能时会导致机械臂短暂失能。
  2. 设置零位需要机械臂处于失能状态。

控制模式

当前仅支持 PP(Profile Position)模式,调用 enable() 时会自动设置。

ControlMode 枚举可从 linkerbot 导入:

from linkerbot import ControlMode

CAN 总线

interface_name 指定 CAN 接口名称(如 "can0"),interface_type 指定接口类型(默认 "socketcan")。

更多信息(包括 Windows 用法)请参阅 CAN 总线配置

功能模块

模块文档
运动控制(move_j/move_p/move_l)motion.md
运动学(FK/IK/关节限位)kinematics.md
状态读取(角度/速度/扭矩/温度)state.md
参数配置(速度/加速度/PID)config.md

运动控制

回零

将所有关节移动到零位。

from linkerbot import A7

with A7(side="left", interface_name="can0") as arm:
    arm.enable()
    arm.home()  # 阻塞模式
    arm.home(blocking=False)  # 非阻塞模式

参数:

  • blocking: 是否阻塞等待运动完成,默认 True

move_j

通过发送每个关节的目标角度,控制机械臂运动。

from linkerbot import A7

with A7(side="left", interface_name="can0") as arm:
    arm.enable()
    arm.move_j([0.0, 0.0, 0.0, 1.57, 0.0, 0.0, 0.0])

参数:

  • target_joints: 7 个关节的目标角度(rad),类型 list[float]
  • blocking: 是否阻塞直到运动完成,默认 True,类型 bool

异常:

  • StateError: 机械臂运动中再次调用时
  • ValidationError: 目标角度超出关节限位时

move_p

通过发送机械臂 TCP 6D 位姿(x, y, z, rx, ry, rz),控制机械臂运动。通过逆运动学求解目标关节角度后执行关节运动,末端路径不保证为直线。

from linkerbot import A7, Pose

with A7(side="left", interface_name="can0") as arm:
    arm.enable()
    target = Pose(x=0.0, y=0.33, z=-0.25, rx=1.85, ry=0.0, rz=-1.57)
    arm.move_p(target)

参数:

  • target_pose: 目标 TCP 位姿,类型 linkerbot.arm.Pose
  • current_angles: IK 初始猜测关节角度,类型 list[float] | NoneNone 时从电机读取,
  • blocking: 是否阻塞直到运动完成,类型 bool,默认 True

异常:

  • StateError: 机械臂运动中再次调用时
  • RuntimeError: IK 求解失败时

move_l

通过发送机械臂 TCP 6D 位姿(x, y, z, rx, ry, rz),控制机械臂直线运动。末端沿笛卡尔空间直线运动,位置使用梯形速度规划,姿态使用球面线性插值(Slerp)。move_l 始终阻塞直到运动完成,无 blocking 参数。

from linkerbot import A7, Pose

with A7(side="left", interface_name="can0") as arm:
    arm.enable()
    target = Pose(x=0.0, y=0.33, z=-0.25, rx=1.85, ry=0.0, rz=-1.57)
    arm.move_l(target)

参数:

  • target_pose: 目标 TCP 位姿,类型 Pose
  • max_velocity: TCP 线性运动梯形速度规划的最大速度(m/s),默认 0.05,范围 (0, 0.4]
  • max_angular_velocity: TCP 旋转运动梯形速度规划的最大角速度(rad/s),默认 0.3,范围 (0, 3.0]
  • acceleration: TCP 线性运动梯形速度规划的最大加速度(m/s²),默认 0.1,范围 (0, 1.0]
  • angular_acceleration: TCP 旋转运动梯形速度规划的最大角加速度(rad/s²),默认 0.1,范围 (0, 1.0]
  • current_pose: 起始 TCP 位姿,None 时由 current_angles 或电机读取计算,类型 Pose | None
  • current_angles: 起始关节角度,None 时从电机读取,类型 list[float] | None

异常:

  • StateError: 机械臂运动中再次调用时
  • ValidationError: 参数超出范围时
  • RuntimeError: 路径点 IK 求解失败时

运动状态检测

返回机械臂当前是否处于运动状态。

if arm.is_moving():
    print("正在运动中...")

返回值:

  • bool: 是否在运动中

等待运动完成

阻塞当前线程,直到运动计时器归零。通常配合 blocking=False 使用。

arm.move_j([0.0, 0.0, 0.0, 1.57, 0.0, 0.0, 0.0], blocking=False)
# ... 执行其他操作 ...
arm.wait_motion_done()  # 等待运动结束

示例

非阻塞运动与等待

from linkerbot import A7, Pose

with A7(side="left", interface_name="can0") as arm:
    arm.enable()
    arm.home()

    # 设置速度和加速度
    arm.set_velocities([1.0] * 7)
    arm.set_accelerations([10.0] * 7)

    # 非阻塞 move_j:发送后立即返回,可执行其他操作
    arm.move_j([0.0, 0.0, 0.0, 1.57, 0.0, 0.0, 0.0], blocking=False)
    print("move_j 已发送,正在运动中...")
    arm.wait_motion_done()  # 等待运动完成

    # 阻塞 move_p:调用直接等到运动完成,无需 wait_motion_done
    target = Pose(x=0.0, y=0.33, z=-0.25, rx=1.85, ry=0.0, rz=-1.57)
    arm.move_p(target)

    # move_l 始终阻塞
    target = Pose(x=0.1, y=0.33, z=-0.2, rx=1.85, ry=0.0, rz=-1.57)
    arm.move_l(target)

    arm.home()

运动学

正运动学

根据关节角度计算 TCP 位姿。

pose = arm.forward_kinematics([0.0, 0.0, 0.0, 1.57, 0.0, 0.0, 0.0])
print(f"TCP: ({pose.x:.3f}, {pose.y:.3f}, {pose.z:.3f})")

参数:

  • angles: 7 个关节角度(rad),类型 list[float]

返回值:

  • Pose: TCP 位姿

逆运动学

根据 TCP 位姿求解关节角度。

from linkerbot import Pose

target = Pose(x=0.0, y=0.33, z=-0.25, rx=1.85, ry=0.0, rz=-1.57)
angles = arm.inverse_kinematics(target)
print(f"关节角度:{angles}")

参数:

  • pose: 目标 TCP 位姿,类型 Pose
  • current_angles: IK 初始猜测关节角度,类型 list[float] | NoneNone 时从电机读取

返回值:

  • list[float]: 7 个关节角度(rad)

关节限位

# 获取当前关节限位
limits = arm.get_joint_limits()
print(limits)  # [(lower0, upper0), (lower1, upper1), ...]

# 设置自定义关节限位
arm.set_joint_limits([(-1.0, 1.0)] * 7)

状态读取

完整状态快照

一次性读取完整的机械臂状态,包含 TCP 位姿、所有关节的角度、速度、加速度、扭矩和温度。

from linkerbot import A7

with A7(side="left", interface_name="can0") as arm:
    arm.enable()
    state = arm.get_state()
    print(state.pose)
    print(f"第一个关节实际角度:{state.joint_angles[0].angle:.4f} rad")

返回值

  • State: 包含 posejoint_anglesjoint_control_anglesjoint_velocitiesjoint_control_velocitiesjoint_control_accelerationjoint_torquesjoint_temperatures

State 数据模型

属性说明
poseTCP 位姿(Pose)
joint_angles7 个关节的实际角度(list[AngleState])
joint_control_angles7 个关节的控制角度(list[AngleState])
joint_velocities7 个关节的实际速度(list[VelocityState])
joint_control_velocities7 个关节的控制速度(list[VelocityState])
joint_control_acceleration7 个关节的控制加速度(list[AccelerationState])
joint_torques7 个关节的实际扭矩(list[TorqueState])
joint_temperatures7 个关节的温度(list[TemperatureState])

各子状态模型均包含值字段和 timestamp(UNIX 时间戳,秒)。

子状态模型字段

模型值字段时间戳字段
AngleState.angle.timestamp
VelocityState.velocity.timestamp
AccelerationState.acceleration.timestamp
TorqueState.torque.timestamp
TemperatureState.temperature.timestamp

单项状态读取

get_state() 外,以下方法分别返回单一类型的状态数据,均返回 7 个关节的列表(get_pose() 除外)。

from linkerbot import A7

with A7(side="left", interface_name="can0") as arm:
    arm.enable()
    angles = arm.get_angles()
    velocities = arm.get_velocities()
    temps = arm.get_temperatures()
    pose = arm.get_pose()
    print(f"第二个关节实际角度:{angles[1]:.4f} rad")
    print(f"第三个关节实际速度:{velocities[2]:.4f} rad/s")
    print(f"第四个关节实际温度:{temps[3]:.1f} °C")
    print(f"TCP: ({pose.x:.3f}m, {pose.y:.3f}m, {pose.z:.3f}m)")

返回值

方法返回值单位
get_angles()7 个关节的实际角度rad
get_control_angles()7 个关节的控制角度rad
get_velocities()7 个关节的实际速度rad/s
get_control_velocities()7 个关节的控制速度rad/s
get_control_acceleration()7 个关节的控制加速度rad/s²
get_torques()7 个关节的实际扭矩Nm
get_temperatures()7 个关节的温度°C
get_pose()TCP 位姿m, rad

传感器数据

A7 使用轮询模式采集传感器数据,get_*() 返回的是上一次轮询时的值。

数据类型单位默认轮询间隔最大延迟
角度rad10 ms≈ 10 ms
速度rad/s10 ms≈ 10 ms
扭矩Nm50 ms≈ 50 ms
温度°C1000 ms≈ 1000 ms

如需更低延迟,可通过 start_polling() 方法自定义不同传感器的轮询间隔:

from linkerbot import A7
from linkerbot.arm.a7 import SensorType

with A7(side="left", interface_name="can0") as arm:
    arm.start_polling(
        {
            SensorType.POSITION: 0.005,  # 5 ms = 200 Hz
            SensorType.VELOCITY: 0.005,
            SensorType.TORQUE: 0.1,
            SensorType.TEMPERATURE: 2.0,
        }
    )
    arm.enable()
    arm.home()

参数配置

速度与加速度限制

设置 7 个关节的速度和加速度限制。

from linkerbot import A7

with A7(side="left", interface_name="can0") as arm:
    arm.enable()
    # 将所有关节速度限制设为 1.0 rad/s
    arm.set_velocities([1.0] * 7)
    # 分别设置各关节加速度限制
    arm.set_accelerations([10.0, 9.0, 8.0, 7.0, 6.0, 5.0, 4.0])
方法参数范围说明
set_velocities(velocities)7 个关节的速度上限(rad/s)[0.0, 50.0]默认 0.5
set_accelerations(accelerations)7 个关节的加速度上限(rad/s²)[1.0, 50.0]默认 10.0,同时设置加速度和减速度

异常ValidationError(必须提供恰好 7 个值,或值超出范围时)

PID 参数设置

设置 7 个关节的 PID 参数。

from linkerbot import A7

with A7(side="left", interface_name="can0") as arm:
    arm.set_position_kps([100.0] * 7)
    arm.set_velocity_kps([0.5] * 7)
    arm.set_velocity_kis([0.01] * 7)
方法参数说明
set_position_kps(kps)7 个关节的位置 Kp 值位置环比例增益
set_velocity_kps(kps)7 个关节的速度 Kp 值速度环比例增益
set_velocity_kis(kis)7 个关节的速度 Ki 值速度环积分增益

参数速查

参数类型默认值范围
速度限制0.5 rad/s[0.0, 50.0] rad/s
加速度限制10.0 rad/s²[1.0, 50.0] rad/s²
move_l 最大平移速度0.05 m/s(0, 0.4] m/s
move_l 最大角速度0.3 rad/s(0, 3.0] rad/s
move_l 平移加速度0.1 m/s²(0, 1.0] m/s²
move_l 角加速度0.1 rad/s²(0, 1.0] rad/s²

示例

设置 PID 参数和速度与加速度限制后运动

from linkerbot import A7

with A7(side="left", interface_name="can0") as arm:
    arm.set_position_kps([80.0] * 7)
    arm.set_velocity_kps([0.4] * 7)
    arm.set_velocity_kis([0.008] * 7)
    arm.enable()
    arm.set_velocities([1.0] * 7)
    arm.set_accelerations([10.0] * 7)
    arm.home(blocking=True)
    arm.move_j([0.0, 0.0, 0.0, 1.57, 0.0, 0.0, 0.0], blocking=True)

参数持久化

调用 save_params() 将当前 PID、速度、加速度等参数写入电机闪存,断电后仍然生效。

from linkerbot import A7

with A7(side="left", interface_name="can0") as arm:
    arm.set_position_kps([80.0] * 7)
    arm.set_velocity_kps([0.4] * 7)
    arm.save_params()  # 保存到闪存

注意:每次写入耗时约 1 ms,避免在实时控制循环中调用。