跳到主要内容

云犀Python SDK

Gloria-M-SDK 是 Gloria-M 系列机械夹爪的 Python 开发包,通过串口转 CAN 适配器与电机通信,封装了 MIT/PV 控制、参数读写和状态解析等接口。

本文档基于 Synria-Robotics/Gloria-M-SDK main 版本。

1. 介绍

1.1 核心能力

核心能力
  • 通过串口转 CAN 适配器控制 Gloria-M 夹爪电机
  • 提供 GloriaGripper 门面类作为推荐入口,封装 MotorAPI / MotionAPI / ParamAPI
  • 支持 MIT 模式和 PV 模式两种常用控制方式
  • 支持使能、失能、设零点、模式切换等基础控制命令
  • 支持读取位置、速度、力矩等反馈状态
  • 支持参数读写和保存,例如 PMAXVMAXTMAXCTRL_MODE
  • 支持连杆夹爪力控示例,并可结合空载力矩基线做补偿
  • 提供完整异常体系(GloriaSdkError 及子类)

1.2 SDK 目录结构

官方仓库 main 的核心结构如下:

Gloria-M-SDK/
├── src/gloria_m_sdk/
│ ├── __init__.py # 包入口,导出公开 API
│ ├── client.py # GloriaGripper 门面层(推荐入口)
│ ├── exceptions.py # 异常体系(GloriaSdkError 及子类)
│ ├── api/ # API 层:按领域拆分的子 API
│ │ ├── __init__.py
│ │ ├── base.py # BaseAPI(共享控制器访问)
│ │ ├── motor_api.py # MotorAPI:使能/失能/模式/归零/读取
│ │ ├── motion_api.py # MotionAPI:send_mit / send_pos_vel
│ │ └── param_api.py # ParamAPI:读写寄存器、保存、应用限制
│ ├── actuator.py
│ ├── controller.py
│ ├── protocol_mit.py
│ ├── serial_can_adapter.py
│ ├── param_config.py
│ ├── registers.py
│ ├── types.py
│ ├── constants.py
│ └── gripper_baseline.py
├── demos/
│ ├── 01_gripper_quicktest.py # PV 模式往复运动测试
│ ├── 02_pv_control.py # PV 模式柔顺闭合
│ ├── 03_mit_linkage_force_control.py # MIT 连杆夹爪力控
│ ├── mit_close_baseline.py # MIT 空载闭合基线采集
│ └── baseline/ # 基线数据 CSV 输出目录
├── requirements.txt
└── pyproject.toml

2. 安装与准备

2.1 环境要求

  • Python 3.11 及以上版本
  • 串口转 CAN 适配器
  • 已上电的 Gloria-M 夹爪或对应电机

2.2 安装步骤

克隆指定版本源码:

git clone https://github.com/Synria-Robotics/Gloria-M-SDK.git -b main
cd Gloria-M-SDK

安装依赖:

pip install -r requirements.txt
pip install -e .

2.3 串口连接检查

  • Windows:在设备管理器中确认串口号,例如 COM5
  • Linux:常见端口为 /dev/ttyUSB0/dev/ttyACM0
  • 默认波特率为 921600

Linux 如遇权限问题,可执行:

sudo usermod -a -G dialout $USER

重新登录后再访问串口。

3. 公共 API 概览

gloria_m_sdk 在包入口导出了以下常用对象:

3.1 推荐入口(门面层)

名称说明
GloriaGripper门面类,封装了 MotorAPI / MotionAPI / ParamAPI,推荐作为首选入口

3.2 底层对象(低层访问)

名称说明
Actuator执行器配置对象,包含命令 ID、反馈 ID、限幅和安全位置范围
ActuatorState保存当前位置、速度、力矩和最近更新时间
SerialCanAdapter串口转 CAN 通信适配器
CanController底层控制器,负责命令发送、回包解析和参数读写
Variable电机参数寄存器枚举,例如 CTRL_MODEPMAXVMAX
TorqueBaseline空载力矩基线读取与插值对象,用于连杆夹爪力控

3.3 公共类型

名称说明
ControlMode控制模式枚举,包含 MITPOS_VEL
LimitsMIT 模式打包/解包的缩放范围,常见值为 pmax=3.14vmax=10.0tmax=12.0
PositionRange安全位置范围,超出后会被自动夹紧
MIT_SAFE_Q_MIN / MIT_SAFE_Q_MAX包内提供的默认安全范围常量
apply_limits_and_save将限幅参数写入电机并保存(旧接口,建议改用 gripper.params.apply_limits()

3.4 异常体系

名称说明
GloriaSdkError基类,可一网打尽所有 SDK 异常
GloriaConnectionError串口打不开
GloriaCommunicationError超时或帧格式错误
GloriaConfigError参数越界或配置无效
GloriaModeError模式切换未被电机确认

4. GloriaGripper 门面 API

GloriaGripper 是门面类,封装了底层五层架构,推荐所有新代码使用此入口

4.1 构造参数

GloriaGripper(
port, # "COM5" 或 "/dev/ttyUSB0"
*,
baudrate=921_600,
command_id=0x01, # 电机命令 CAN ID
feedback_id=0x101, # 电机反馈 CAN ID
limits=None, # Limits(pmax, vmax, tmax),默认 (3.14, 10, 12)
safe_position=None, # PositionRange(min, max) — 位置限幅
baseline_csv=None, # 空载扭矩基线 CSV 路径
timeout=0.5, # 串口读超时 [s]
)

4.2 属性

属性类型说明
stateActuatorState最新反馈快照(位置、速度、扭矩)
current_modeControlMode | None电机最后确认的控制模式;set_mode() 前为 None
is_connectedboolTrue 表示串口已打开

4.3 .motor — MotorAPI

方法说明
enable()发送使能命令
disable()发送失能命令
set_zero()将当前位置设为零点
set_mode(mode)切换控制模式;失败则抛出 GloriaModeError
refresh()广播请求状态并更新 gripper.state
poll()解析待处理 RX 包,更新执行器状态

4.4 .motion — MotionAPI

方法说明
send_mit(*, kp, kd, q, dq, tau)发送 MIT 扭矩控制帧
send_pos_vel(*, position, velocity)发送 PV 位置+速度帧

4.5 .params — ParamAPI

方法说明
read(rid, *, timeout_s)读取寄存器;超时返回 None
write_f32(rid, value)写入 float32 寄存器
write_u32(rid, value)写入 uint32 寄存器
save()将参数持久化到 Flash
apply_limits(limits)写入 PMAX/VMAX/TMAX 并保存

4.6 快速开始示例

from gloria_m_sdk import GloriaGripper, ControlMode

with GloriaGripper("COM5") as g:
g.motor.set_mode(ControlMode.POS_VEL)
g.motor.enable()
g.motor.refresh()
print(f"位置 = {g.state.position:.3f} rad")

# 移动到开仓位置
g.motion.send_pos_vel(position=2.5, velocity=1.0)

5. 示例脚本(Demos)

5.1 01_gripper_quicktest.py

PV 模式往返快测。夹爪在 open_qclose_q 之间持续往返,反馈位置稳定后自动切换方向,用于验证串口通信、CAN ID 和开合方向是否正确。

运行示例:

python demos/01_gripper_quicktest.py --port COM5 --id 0x01 --close-q 0.0 --open-q 2.5 --vel 1.0

核心逻辑:

from gloria_m_sdk import (
Actuator,
CanController,
ControlMode,
Limits,
PositionRange,
SerialCanAdapter,
apply_limits_and_save,
)

safe_q = PositionRange(min=min(args.open_q, args.close_q), max=max(args.open_q, args.close_q))
limits = Limits(pmax=3.14, vmax=10.0, tmax=12.0)

act = Actuator(
name="gripper_cycle",
command_id=args.id,
feedback_id=args.fb_id,
limits=limits,
safe_position=safe_q,
)

with SerialCanAdapter(args.port, baudrate=args.baud, timeout=0.5) as adapter:
apply_limits_and_save(adapter, motor_id=args.id, limits=limits)

ctrl = CanController(adapter)
ctrl.register(act)

ok = ctrl.set_control_mode(act, ControlMode.POS_VEL)
print(f"[mode] set PV: {ok}")
ctrl.enable(act)
ctrl.refresh_state(act)

while True:
target = float(args.close_q) if cycle % 2 == 0 else float(args.open_q)
ctrl.send_pos_vel(act, position=target, velocity=args.velocity, poll=True)

5.2 02_pv_control.py

PV 模式柔顺闭合。先以较高速度张开到 open_q,再以低速缓慢闭合到 close_q 并保持,适合柔顺夹取场景。

运行示例:

python demos/02_pv_control.py --port COM5 --open-q 2.5 --close-q 0.0 --close-vel 0.3

执行阶段:张开 → 缓慢闭合 → 保持。关键代码:

with SerialCanAdapter(args.port, baudrate=args.baud, timeout=0.5) as adapter:
apply_limits_and_save(adapter, motor_id=args.id, limits=limits)

ctrl = CanController(adapter)
ctrl.register(act)

ok = ctrl.set_control_mode(act, ControlMode.POS_VEL)
print(f"[mode] set PV: {ok}")
ctrl.enable(act)
ctrl.refresh_state(act)

open_q = act.clamp_position(args.open_q)
_move_to(ctrl, act, target=open_q, velocity=args.open_vel,
loop_sleep=args.loop_sleep, print_hz=args.print_hz,
settle_threshold=args.settle_threshold, settle_time=args.settle_time,
timeout=args.timeout)

close_q = act.clamp_position(args.close_q)
_move_to(ctrl, act, target=close_q, velocity=args.close_vel,
loop_sleep=args.loop_sleep, print_hz=args.print_hz,
settle_threshold=args.settle_threshold, settle_time=args.settle_time,
timeout=args.timeout)

hold_end = time.perf_counter() + args.hold_time
while time.perf_counter() < hold_end:
ctrl.send_pos_vel(act, position=close_q, velocity=0.0, poll=True)

5.3 03_mit_linkage_force_control.py

MIT 模式连杆夹爪力控。结合力臂曲线和反馈力矩做接触判定,实现"张开 → 接近 → 接触 → 保持 → 释放"闭环控制。

运行示例:

python demos/03_mit_linkage_force_control.py --port COM5 --id 0x01 --fb-id 0x101 --open-q 2.77 --close-q 0.003 --target-force 15

如果是 4340 强力版本夹爪,需指定对应基线文件并调整力阈值:

python demos/03_mit_linkage_force_control.py --port COM5 --baseline-csv ".\demos\baseline\close_baseline_4340.csv" --target-force 30 --contact-force 60

如未指定 --baseline-csv,脚本默认使用 .\demos\baseline\close_baseline_4310.csv

夹持力近似关系:

Fgripτcloser(q)F_{grip} \approx \frac{\tau_{close}}{r(q)}

其中 r(q)r(q) 为当前位置的等效力臂。关键初始化:

from gloria_m_sdk import (
Actuator,
CanController,
ControlMode,
Limits,
PositionRange,
SerialCanAdapter,
TorqueBaseline,
Variable,
apply_limits_and_save,
)

safe_q = PositionRange(min=min(raw_open_q, raw_close_q), max=max(raw_open_q, raw_close_q))
limits = Limits(pmax=3.14, vmax=10.0, tmax=12.0)
baseline = TorqueBaseline.from_csv(args.baseline_csv) if args.baseline_csv else None

act = Actuator(
name="linkage_gripper",
command_id=args.id,
feedback_id=args.fb_id,
limits=limits,
safe_position=safe_q,
)

with SerialCanAdapter(args.port, baudrate=args.baud, timeout=0.5) as adapter:
apply_limits_and_save(adapter, motor_id=args.id, limits=limits)

ctrl = CanController(adapter)
ctrl.register(act)

ok = ctrl.set_control_mode(act, ControlMode.MIT)
print(f"[mode] set MIT: {ok}")
ctrl.enable(act)
ctrl.refresh_state(act)

控制循环根据阶段动态调整参数,通过 send_mit() 发送:

ctrl.send_mit(
act,
kp=float(kp),
kd=float(kd),
q=float(q_target),
dq=0.0,
tau=float(tau_cmd),
poll=True,
)

注意事项:

  • close_q <= open_q 方向约定必须成立
  • open_tau 为正,close_tau 为负
  • radius-profile 为占位值,需替换为实际标定数据
  • 建议加载空载基线 baseline-csv 以提升夹持力精度

5.4 mit_close_baseline.py

MIT 空载闭合基线采集。在 MIT 模式下以固定负扭矩让夹爪空载闭合,记录闭合过程中的位置、速度、反馈扭矩和估算夹持力,输出基线 CSV 文件供 03_mit_linkage_force_control.py 使用,用于扣除自身摩擦和机构阻力。

建议在没有夹持物的情况下运行:

python demos/mit_close_baseline.py --port COM5 --close-tau -1.25

常用参数:

参数默认值说明
--portCOM12串口号
--baud921600串口波特率
--id0x01电机命令 CAN ID
--fb-id0x201电机反馈 CAN ID
--open-q2.77夹爪最大张开位置 [rad]
--close-q0.003夹爪闭合位置 [rad]
--close-tau-1.25闭合方向扭矩 [N·m],必须为负值
--kd0.8MIT 扭矩控制阻尼项
--stop-force0.0估算夹持力达到该阈值后停止,0 表示禁用 [N]
--radius-mm12.0用于估算夹持力的等效力臂 [mm]
--timeout5.0最长采集时间 [s]
--position-epsilon0.02判定到达闭合位置的容差 [rad]
--bin-width0.05按位置生成基线曲线时的分桶宽度 [rad]
--save-dirdemos/baselineCSV 输出目录
--save-prefixclose_baselineCSV 文件名前缀
--no-savefalse只运行测试,不保存 CSV

运行完成后默认生成两个 CSV 文件:

demos/baseline/{save_prefix}_{timestamp}_raw.csv
demos/baseline/{save_prefix}_{timestamp}_binned.csv

原始采样 CSV 每一行对应控制循环中的一次采样:

字段说明
elapsed_s从本次测试开始到当前采样点的时间 [s]
position_rad当前电机位置反馈 [rad]
velocity_rad_s当前电机速度反馈 [rad/s]
tau_cmd_nm当前发送的 MIT 扭矩命令 [N·m]
tau_fb_nm电机反馈扭矩 [N·m]
force_est_n根据反馈扭矩估算的夹持力 [N],计算方式为 max(0, -tau_fb_nm) / (radius_mm / 1000)

分桶基线 CSV 会把位置相近的原始采样点归为一组,并对每组求平均,适合后续作为基线曲线使用:

字段说明
position_mean_rad当前位置分桶内的平均位置 [rad]
velocity_mean_rad_s当前位置分桶内的平均速度 [rad/s]
tau_fb_mean_nm当前位置分桶内的平均反馈扭矩 [N·m]
force_est_mean_n当前位置分桶内的平均估算夹持力 [N]
sample_count当前位置分桶内包含的原始采样点数量

5.5 获取夹爪运行参数

5.5.1 实时状态

位置、速度、力矩在每次控制回包后自动写入 act.state

ctrl.refresh_state(act)

print(f"position = {act.state.position:+.3f} rad")
print(f"velocity = {act.state.velocity:+.3f} rad/s")
print(f"torque = {act.state.torque:+.3f} Nm")
print(f"updated = {act.state.updated_at:.3f}")

发送命令时带 poll=True 即可自动更新,也可手动调用 ctrl.refresh_state(act)

使用 GloriaGripper 时,通过 gripper.state 访问:

gripper.motor.refresh()
print(f"position = {gripper.state.position:+.3f} rad")

5.5.2 读取寄存器参数

使用 GloriaGripper 时,可通过 gripper.params.read() 高层接口读取任意寄存器:

from gloria_m_sdk import GloriaGripper, Variable

with GloriaGripper("COM5") as g:
mode = g.params.read(Variable.CTRL_MODE)
pmax = g.params.read(Variable.PMAX)
vmax = g.params.read(Variable.VMAX)
tmax = g.params.read(Variable.TMAX)

print(f"CTRL_MODE = {mode}")
print(f"PMAX = {pmax}")
print(f"VMAX = {vmax}")
print(f"TMAX = {tmax}")

使用低层 CanController 时,需要直接向 0x7FF 发送读参帧:

import struct
import time

from gloria_m_sdk import Variable


def read_param_once(adapter, *, target_id: int, rid: int, timeout_s: float = 0.03):
can_id_l = int(target_id) & 0xFF
can_id_h = (int(target_id) >> 8) & 0xFF

# 0x33 表示读参数
adapter.send(0x7FF, bytes([can_id_l, can_id_h, 0x33, int(rid) & 0xFF, 0, 0, 0, 0]))

deadline = time.time() + float(timeout_s)
expect_ids = {int(target_id), int(0x100 + int(target_id)), 0x00}
is_u32 = (7 <= int(rid) <= 10) or (13 <= int(rid) <= 16) or (35 <= int(rid) <= 36)

while time.time() < deadline:
for pkt in adapter.read_packets():
if len(pkt.data) != 8:
continue
if int(pkt.can_id) not in expect_ids and not (
pkt.data[0] == can_id_l and pkt.data[1] == can_id_h
):
continue
if pkt.data[2] not in (0x33, 0x55):
continue
if int(pkt.data[3]) != (int(rid) & 0xFF):
continue

if is_u32:
return struct.unpack("<I", pkt.data[4:8])[0]
return struct.unpack("<f", pkt.data[4:8])[0]

time.sleep(0.002)

return None

使用示例:

mode = read_param_once(adapter, target_id=act.command_id, rid=int(Variable.CTRL_MODE))
pmax = read_param_once(adapter, target_id=act.command_id, rid=int(Variable.PMAX))
vmax = read_param_once(adapter, target_id=act.command_id, rid=int(Variable.VMAX))
tmax = read_param_once(adapter, target_id=act.command_id, rid=int(Variable.TMAX))
pm = read_param_once(adapter, target_id=act.command_id, rid=int(Variable.p_m))
xout = read_param_once(adapter, target_id=act.command_id, rid=int(Variable.xout))

print(f"CTRL_MODE = {mode}")
print(f"PMAX = {pmax}")
print(f"VMAX = {vmax}")
print(f"TMAX = {tmax}")
print(f"p_m = {pm}")
print(f"xout = {xout}")
提示

应用层控制优先使用 act.state(position / velocity / torque);仅在排查模式切换或限幅配置时再读寄存器,避免高频循环中引入过多读参请求。

6. 底层 CanController 方法速查

信息

CanController 属于低层接口,供需要精细控制的高级用户使用。普通用户推荐使用 GloriaGripper 门面(见第 4 节)。

方法作用
register(act)注册执行器
enable(act)使能电机
disable(act)失能电机
set_zero(act)设当前位置为零点
set_control_mode(act, mode)切换控制模式(MIT / POS_VEL)
refresh_state(act)广播请求状态回包
send_mit(act, *, kp, kd, q, dq, tau)发送 MIT 命令
send_pos_vel(act, *, position, velocity)发送 PV 命令

7. 参数约定与注意事项

7.1 默认参数

参数默认值
波特率921600
命令 CAN ID0x01
反馈 CAN ID0x101
MIT 缩放pmax=3.14vmax=10.0tmax=12.0

7.2 位置和方向约定

  • MIT_SAFE_Q_MIN=0.0MIT_SAFE_Q_MAX=2.7 为通用安全范围
  • demos 默认 open_q > close_q(如 open_q=2.5close_q=0.0
  • 正力矩 = 张开,负力矩 = 闭合

实际使用中 PositionRangeopen_q / close_q、力矩符号三者需与机构方向一致,否则会出现方向反转或限位抖动。

7.3 安全建议

  1. 首次调试用低速、小力矩
  2. 先单次开合验证,再进循环或力控
  3. 退出前始终调用 disable()
  4. 力控先验证空载基线和力臂曲线

8. 故障排查

现象 / 异常排查方向
GloriaConnectionError / 串口打不开检查串口号、设备管理器中是否出现 CAN 适配器;Linux 下检查 dialout 组权限
GloriaCommunicationError / 超时或帧错误检查波特率(默认 921600)、CAN 线缆是否接好;确认 feedback_id 与电机实际反馈 ID 匹配
GloriaModeError / 模式切换失败检查 command_id / feedback_id;低层 set_control_mode() 返回 False 同理
GloriaConfigError / 参数越界检查 LimitsPositionRange 取值是否合法
无状态反馈确认 poll=True 或手动调用 gripper.motor.poll() / ctrl.poll();检查反馈 ID
开合方向相反检查 open_q / close_q 定义、力矩符号、PositionRange 区间