Skip to main content

ARX-D-CAN Python SDK

面向达妙电机机械臂的通用 Python SDK,支持单臂、双臂、可选夹爪,以及 DM Device、dm-serial、SocketCAN 和 SocketCAN-FD 通信后端。

单臂保留通用配置接口;双臂采用底层完整产品 Runtime:

  • ArxDCanArm 控制一条独立 CAN 通道上的单臂;
  • ArxDCanDualArm 只提交固定左右整机数据帧,不创建或暴露底层通道、电机与控制器;
  • Yunyi 双臂实时产品配置由 C++ Runtime 内置,YAML/URDF 仅供单臂兼容、展示和外部工具。

当前双臂默认配置是 Yunyi V1.0,默认通过两块刷入 gs_usb 固件的 DM-USB2FDCAN Dual,以 SocketCAN-FD 的 can-left/can-right 分别连接左右臂。以后增加 其他产品时不需要再创建产品专用 Python 类。

安装

python -m pip install -e .

底层通信与原生安全运行时使用 motor-drive-layer==0.10.21(Motor ABI 0.5.0-cpp,Runtime ABI 2.15)。该发行包只包含 libmotor_abi.so、 libarticore_runtime.so 和平台所需的 DM Device 运行库,不再提供可导入的 Python 模块。 本 SDK 在 arx_d_can._motor_abi 内维护私有 ctypes ABI 声明,对外接口保持不变。 普通用户不需要另行下载 DM_SDK,也不需要配置系统 Pinocchio 或 ROS 动态库路径。 内置 Yunyi 模型的 FK、IK、Jacobian 和刚体动力学不需要安装 Python Pinocchio;用于控制 计算的产品模型和 Pinocchio C++ 实现都在私有 Runtime 中。SDK 仍保留 Yunyi URDF,供 可视化、机器人描述和外部工具使用,但 Python 不解析它执行内置运动学或动力学计算。

单臂 API

from arx_d_can import ArxDCanArm

arm = ArxDCanArm(
    model="yunyi_v1_0_right",
    control_mode="pv",
)
arm.connect()

try:
    state = arm.read_state()
    print(state.positions)

    arm.enable()                          # 自动配置构造时选择的模式
    arm.set_joint_pv([0.0] * 7)           # Runtime 在底层平滑推进目标
finally:
    arm.close()

普通位置设置按构造模式使用 set_joint_mit() 或 set_joint_pv():

arm.set_joint_mit(
    target,
    # velocity 可选;省略时使用当前机型允许的最大统一速度
)

用户只提交最终位置和一个统一速度。Runtime 在内部控制周期中限制位置 reference;最新完整目标原子覆盖旧目标,不排队,也不要求 Python 持续刷新。MIT 实际 发送 dq=0、tau=0 和产品 Kp/Kd;PV 使用相同的 reference 和统一协议速度限制。 SDK 不限制用户重复调用 set_joint_mit() / set_joint_pv() 的频率:调用快于 Runtime 内部周期时只消费最新目标,调用慢于内部周期时持续保持最近目标,两种情况都不会因 “调用 Hz 超限”而报错。底层调度周期不作为用户侧最大调用频率。 普通 MIT/PV 的 velocity 统一使用 0~100 速度档位:0 暂停 reference 推进, 100 对应当前模式和产品允许的最大普通速度,省略时默认 100。百分比到 rad/s 的换算、 逐周期步长和各关节绝对速度上限全部由 C++ Runtime 管理。 原始 MIT/PV 数据包、逐关节速度、Kp/Kd、前馈力矩和 streaming 生命周期不作为普通 用户 API 公开,只供 SDK 内部重力补偿等高级控制器使用。

关节数量不由代码写死,而是来自所选机型配置。当前公开控制模式只有经过验证的 pv 和 mit。模式在创建机械臂时确定,enable() 自动完成配置;使能期间禁止 切换模式。

原生运动学与动力学

内置产品通过稳定 ABI 直接调用 Runtime 私有模型,不向 SDK 暴露精确 URDF、质量、 惯量、质心或 Pinocchio 类型:

from arx_d_can import NativeRobotModel, RobotSide

with NativeRobotModel(side=RobotSide.RIGHT) as model:
    q = [0.0] * model.info.dof
    pose = model.fk(q)
    jacobian = model.jacobian(q)
    gravity = model.gravity(q)
    mass = model.mass_matrix(q)
    result = model.ik(pose, q)

同一对象还提供 coriolis_matrix()、nonlinear_effects()、rnea() 和 aba()。 SDK 不再包含 Python FK/IK、Pinocchio 模型加载或笛卡尔 CLIK 兼容层。

双臂 API

from arx_d_can import ArxDCanDualArm

robot = ArxDCanDualArm()  # 默认 MIT;PV 使用 control_mode="pv"
robot.connect()

try:
    assert robot.enable()                         # 全部电机
    assert robot.is_enabled()
    robot.set_joint_mit(
        left=[0.0] * 7,
        right=[0.0] * 7,
    )
finally:
    robot.close()

默认构造固定加载 Yunyi V1.0 产品配置。with_grippers=True 创建 14 个关节和 左右两个夹爪;with_grippers=False 只创建 14 个关节 Motor。左右机型、can-left、 can-right、SocketCAN-FD+BRS 和通信速率均属于产品内部配置,不作为双臂构造参数。

构造函数立即通过 articore_runtime_create_product("yunyi_v1_0", ...) 持有一个非空 原生 Runtime。该对象内部拥有 can-left、can-right、ControllerGroup、14 或 16 个 Motor、 产品方向/量程/限位、默认 MIT 参数、重力模型、worker 和全部资源生命周期; disconnect() 只切回 DISCONNECTED,不会销毁或重建 Runtime。左右 14 个关节作为一个 完整批次接受或拒绝,并使用一个统一的 velocity;速度省略时由 C++ 产品配置选择默认值。

法兰位姿直接由 C++ 产品 Runtime 使用最新完整七关节反馈和内置 Pinocchio 模型计算, Python 不读取 URDF、不拼接 CAN 帧,也不重复执行 FK:

left_pose = robot.get_pose("left")
# [x, y, z, roll, pitch, yaw],单位为米和弧度

sample = robot.get_pose_sample("right")
print(sample.values, sample.timestamp_ns, sample.sequence)

get_pose() 不触发 CAN 请求,只读取 Runtime 的最新反馈缓存,因此可以用于高频监控。 需要判断数据是否持续更新时使用 get_pose_sample() 的时间戳和序列号,并结合 robot.safety_health 与 robot.get_fps()。当前产品没有 TCP 偏移配置,因此只返回法兰 位姿;驱动反馈没有可靠的供电电压/相电流语义,因此不增加这些字段。

使能和失能都返回布尔确认值,并可选择一个稳定电机角色:

assert robot.enable("left/joint1")
assert robot.is_enabled("left/joint1")
assert robot.motor_power_state().name == "MIXED"
assert robot.disable("left/joint1")
assert robot.is_disabled()

# 调零只有在底层完成整机反馈验证后才返回 True
assert robot.set_zero()

不传角色时操作全部已安装电机。单电机操作只用于非运动维护;部分使能后 Runtime 状态为 PARTIALLY_ENABLED,不会接受任何运动指令。随后调用 robot.enable() 会执行完整双通道原子 使能、当前位置保持和反馈确认;robot.disable() 则确认全部电机失能。夹爪关闭的产品不会 创建夹爪角色。

双臂类同时公开 submit_raw_mit(),供遥操作控制器等需要逐帧设置 q/dq/Kp/Kd/tau_ff 的高级应用使用,但不在普通用户 example 中调用。该接口保持 左右 14 轴原子更新和 Runtime 流式看门狗语义。Python 只展平左右数组;数量、NaN/Inf、 模式、位置、速度、力矩、Kp/Kd、反馈、Motor 和 Transport 校验及方向/量程转换均由 C++ Runtime 完成。原生 ABI 将完整帧非阻塞写入容量为一的 latest-target mailbox。 Runtime ABI 2.6 在每个 真实发送周期使用最新原生 q/dq 重新计算完整 MIT 合成力矩,把它限制在逐关节 配置的完整 torque_limit 内;超限时同比缩小该关节的 Kp、Kd 和 tau_ff,未超限时不修改命令。

重力补偿

Yunyi 重力补偿由 Runtime 原生控制循环管理。单臂可直接使用高层接口:

from arx_d_can import ArxDCanArm

arm = ArxDCanArm(model="yunyi_v1_0_right", control_mode="mit")
arm.connect()
arm.enable()
arm.start_gravity_compensation(transition_ms=1000)
print(arm.gravity_compensation_status)
arm.stop_gravity_compensation()
arm.close()

双臂使用同样的 start_gravity_compensation()、stop_gravity_compensation() 和 gravity_compensation_status:

from arx_d_can import ArxDCanDualArm

robot = ArxDCanDualArm(control_mode="mit")
robot.connect()
robot.enable()
robot.start_gravity_compensation(transition_ms=500)
print(robot.gravity_compensation_status)
robot.stop_gravity_compensation()
robot.disable()
robot.close()

SDK 在 runtime.connect() 前为每个活动侧绑定 yunyi_v1_0 原生产品模型。启动时 Runtime 独占机械臂输出:渐入阶段降低 MIT Kp/Kd 并增加重力前馈,进入 ACTIVE 后 发送 Kp=0、Kd=0、dq=0 和 tau_ff=gravity(q)。最终合力矩仍经过逐周期限制器。 read_cached_state() 仅读取反馈,不会从 Python 提交控制命令。重力补偿期间普通 MIT/PV 指令会被 Runtime 拒绝,夹爪命令仍可使用。

停止重力补偿会平滑降低重力前馈、恢复产品 MIT Kp/Kd,并回到停止瞬间当前位置保持; 它不会自动失能,应用应继续调用 disable() 和 close()。 gravity_compensation_status 提供阶段、渐变进度、控制周期数、参与关节和经过力矩限制 后的实际前馈。

原生重力补偿仍属于需要真机验收的力矩控制功能。首次运行应使用机械支撑或悬挂、 物理急停和无载荷环境,并在自定义 YAML 的各关节 effort_limit 中先填写较低限制; 确认左右侧绑定、joint1~joint7 顺序和重力力矩方向后,再逐步提高限制。应分别在 零位、中间位和接近限位姿态检查,不能把 Runtime 逐周期力矩限制当作安全认证功能。

内置 Yunyi 重力补偿不依赖 Python Pinocchio。公开示例不要求用户填写 Kp/Kd、补偿比例或发送频率:

python -m arx_d_can.examples.single_arm.example_11_gravity_compensation \
  --arm-model yunyi_v1_0_right
python -m arx_d_can.examples.dual_arm.example_15_gravity_compensation

Examples

示例按使用形态组织,而不是按产品名组织:

arx_d_can/examples/
├── single_arm/             # 通用单臂示例
│   ├── example_01_scan_ids.py
│   ├── example_02_read_state.py
│   ├── example_03_clear_faults.py
│   ├── example_04_send_position.py
│   ├── example_05_set_gripper_opening.py
│   ├── example_06_benchmark_read_rate.py
│   ├── example_08_return_zero.py
│   ├── example_09_diagnose_status.py
│   ├── example_10_set_zero_current_position.py
│   └── example_11_gravity_compensation.py
└── dual_arm/               # 对应的双臂示例,当前默认 Yunyi
    ├── example_01_scan_ids.py
    ├── example_02_switch_control_mode.py
    ├── example_03_enable_disable.py
    ├── example_04_read_state.py
    ├── example_05_clear_faults.py
    ├── example_06_send_position_pv.py
    ├── example_07_send_position_mit.py
    ├── example_08_set_gripper_openings.py
    ├── example_09_benchmark_read_rate.py
    ├── example_11_return_zero.py
    ├── example_12_diagnose_status.py
    ├── example_13_set_zero_current_position.py
    ├── example_15_gravity_compensation.py
    ├── example_16_record_gravity_trajectory.py
    └── example_17_replay_trajectory.py

双臂重力示教回放从使能开始始终使用同一种 raw PV 或 raw MIT,由 Runtime 自动调度 完整左右臂目标,不在普通接口与 raw 接口之间切换。回放提供 none、linear、 quintic 三种插值方式;MIT 使用插值位置、dq=0、产品 YAML Kp/Kd 和 tau_ff=0; 其中 none 是零阶保持,quintic 使用 10u³ - 15u⁴ + 6u⁵ 五次 S 曲线。

单臂示例继续支持 --arm-model,因此同一套示例可以用于其他机械臂:

python -m arx_d_can.examples.single_arm.example_02_read_state \
  --arm-model yunyi_v1_0_right

python -m arx_d_can.examples.single_arm.example_04_send_position \
  --arm-model yunyi_v1_0_right \
  --positions "0,-20,-20,0,0,0,0" \
  --mode pv

双臂示例目前直接使用 Yunyi 默认左右配置:

python -m arx_d_can.examples.dual_arm.example_04_read_state

ID 扫描由 motor-drive-layer 0.10.14 在每条通道的一次连接内批量完成;扫描过程只请求 反馈,结束时不会发送使能、失能或运动控制帧。

默认读取一次;需要以 100 Hz 持续读取时使用:

python -m arx_d_can.examples.dual_arm.example_04_read_state \
  --mode continuous

持续模式固定以 100 Hz 请求反馈,默认只以 10 Hz 刷新终端;任何一次完整反馈失败 都会立即抛出,不会由示例吞掉或累计重试。可用 --display-hz 调整显示频率。

连接后 Runtime 绑定会每 0.1 秒根据两条 CAN 通道新增的成功接收帧计算一次总帧率。 get_fps() 只返回最近一次缓存结果,不发送 CAN、不等待下一个统计窗口:

robot.connect()
print(robot.get_fps())  # 双通道总 CAN FPS;刚连接时为 0.0

夹爪

单臂保留高级夹爪接口;双臂产品接口只使用 0~1000 开合度和 1~5 力度等级:

from arx_d_can import GripperForceLevel

arm.set_gripper_opening(1000)

robot.set_grippers(left=1000, right=500, gripper_level=3)
robot.set_grippers(
    left=0,
    right=0,
    gripper_level=5,
)

双臂普通用户不再传速度、Motor 坐标、力矩或 Kp/Kd。夹爪运动速度、位置转换、 接触、堵转、过载和保持策略均由 Runtime 产品参数固定管理。无夹爪 Runtime 调用 set_grippers() 会安全返回成功,读取状态时 state.left.gripper 和 state.right.gripper 都是 None。

单臂和双臂夹爪 Demo 都要求显式填写目标,不提供隐含的 open/close 动作:

python -m arx_d_can.examples.single_arm.example_05_set_gripper_opening \
  --opening 1000 \
  --speed 1000 \
  --force-level 5

python -m arx_d_can.examples.dual_arm.example_08_set_gripper_openings \
  --left-gripper 1000 \
  --right-gripper 500 \
  --gripper-level 3

以 5 档力闭合双臂夹爪:

python -m arx_d_can.examples.dual_arm.example_08_set_gripper_openings \
  --left-gripper 0 \
  --right-gripper 0 \
  --gripper-level 5

Yunyi 夹爪在连接前绑定 motor 内置的 yunyi_gripper_v1 产品 profile。开合映射、 最大速度、五档力控、接触/堵转/过载检测、保持增益和回退策略均由底层统一标定,SDK 不再保存或传递这些参数。双臂原生 运行时启用后,单侧原始夹爪命令会被拒绝,避免绕过整机安全状态机。更换自定义末端时 单臂自定义末端仍可使用 ArxDCanArm(enable_gripper=False);Yunyi 双臂通过 with_grippers 固定选择有夹爪或无夹爪拓扑。张开和闭合均由 Runtime 生成受控速度 斜坡,正常夹爪控制跟随整机底层调度,不使用 Python 控制线程。

read_state() 同时公开结构化夹爪控制状态:

state = robot.read_state()
print(state.left_gripper.opening)
print(state.left_gripper.control_state)
print(state.left_gripper.contact_detected)
print(state.left_gripper.overload)

单臂使用同一份结构化状态:

gripper = arm.gripper_safety_health
print(gripper.control_state)
print(gripper.contact_detected)
print(gripper.overload)

Yunyi 产品配置

Yunyi 左右臂使用一份产品级配置:

arx_d_can/config/yunyi_v1_0.yaml
├── gripper_profile: yunyi_gripper_v1
├── arms.left
└── arms.right

左右臂仍是两条独立 CAN 通道。为兼容已有代码,通用单臂 API 仍可使用 yunyi_v1_0_left 和 yunyi_v1_0_right 两个机型名,但它们会选择同一份产品配置中 对应的一侧。YAML 只在对象创建时解析,发送热路径不会重复加载配置。

通信后端

transport channel 示例 用途
dm-serial /dev/ttyACM0 达妙串口 USB2CAN
dm-device 0、1 原厂 DM-USB2FDCAN Dual 固件
socketcan can0 Linux 经典 CAN
socketcanfd can0 Linux CAN-FD

Yunyi 默认使用两块刷入 gs_usb 固件的 DM-USB2FDCAN Dual。Linux 负责配置 CAN-FD 接口,SDK 固定打开 can-left/can-right 并显式启用 BRS。双臂用户接口不允许覆盖 transport、channel 或 baud;其他通信后端仅供单臂和底层维护工具使用。

0.10.14 不提供官方 macOS 或 Windows 预编译包。

ArxDCanArm(model="yunyi_v1_0_left") 默认使用 can-left, ArxDCanArm(model="yunyi_v1_0_right") 默认使用 can-right;ArxDCanDualArm() 固定同时 打开这两个 SocketCAN-FD 接口。

DM Device 默认正式使用 CAN-FD+BRS,仲裁速率为 1 Mbps、数据速率为 5 Mbps;5 Mbps 数据段的 87.5% 采样点由 motor 底层固定配置,SDK 不提供用户参数。达妙电机必须预先 设置为 CAN_BR=9,否则无法与该默认模式通信。SDK 内部保持以下调用:

Controller.from_dm_device(
    device="usb2canfd-dual",
    channel=channel,
    bitrate=1_000_000,
    data_bitrate=5_000_000,
)

需要经典 CAN 兼容时,应在 motor 底层显式将 bitrate 与 data_bitrate 设为相同值; 普通 Yunyi 用户无需接触这些参数。

Linux SocketCAN 需要先配置接口,例如经典 CAN 1 Mbps:

sudo ip link set can0 down
sudo ip link set can0 type can bitrate 1000000 restart-ms 100
sudo ip link set can0 up
ip -details -statistics link show can0

SocketCAN 速率由 ip link 设置,Python 的 baud 不会修改 Linux CAN 接口。 Yunyi 电机需要设置为 CAN_BR=9。两块适配器首次安装时,根据序列号和实际使用的 通道号安装热插拔规则:

sudo ./deploy/linux/install-yunyi-canfd.sh \
  LEFT_ADAPTER_SERIAL LEFT_CHANNEL \
  RIGHT_ADAPTER_SERIAL RIGHT_CHANNEL

安装后,udev 会把两路稳定命名为 can-left、can-right;systemd 会在开机和每次 USB 重新插入时自动设置 1 Mbps 仲裁速率、5 Mbps 数据速率、CAN-FD 和 txqueuelen=1000,无需再次手工运行 ip link。适配器具有两个物理通道,通道号使用 0 或 1。可以通过 udevadm info /sys/class/net/canX 查看序列号,通过 cat /sys/class/net/canX/dev_port 查看通道号。

SDK 会显式调用 Controller.from_socketcanfd(channel, enable_brs=True),确保帧包含 CANFD_BRS。部分 gs_usb 固件不支持 berr-reporting 或 restart-ms,因此默认 配置命令不要求这两个选项。

安全与通信健康

ArxDCanArm 和 ArxDCanDualArm 通过本 SDK 的私有 ctypes 层调用 motor-drive-layer 0.10.21 的 C ABI。双臂 Python 对象只维护一个不透明产品 Runtime 指针和结果类型转换; 它不创建 Controller、ControllerGroup、Motor,也不保存左右使能、模式或故障的镜像状态。 固定频率控制、安全状态机、机器人模型和重力补偿全部位于 C++ Runtime。常驻原生线程使用 steady_clock 执行看门狗、反馈检查、安全保持、故障锁存和失能确认,不依赖 Python GIL。状态为 DISCONNECTED → READY → ENABLED → RUNNING → DEGRADED → SAFE_STOP。偶发反馈抖动只计数; 持续延迟会由底层降速、降力矩,超过安全时间后保持当前位置且拒绝新目标。通信恢复后仍需显式 recover();Runtime 会刷新全部反馈并同步当前位置,回到 ENABLED 等待新轨迹,不会自动重放 旧目标。只有确认的电机故障码、意外失能、非法反馈或 transport 断开进入 FAULT,普通通信延迟 写入 safety_reason 而不是 fault_reason,Runtime Fault 本身也不会切换电机协议模式。

ABI 2.4 的 connect() 会并行获取全部已配置关节和已安装夹爪的新鲜反馈,完整反馈 屏障通过后才进入 READY;因此连接成功后可以立即调用 read_state() 或 read_cached_state()。READY 状态的低频反馈刷新同样由 Runtime 负责,SDK 不会绕过 Runtime 主动请求反馈,也不会用零值掩盖缺失电机。 单臂只读取或诊断时可使用 connect(read_only=True):该连接仍会创建反馈 Runtime,但不会 写入 MIT/PV 模式、通信看门狗或其他电机寄存器。官方读取、读取频率和诊断示例均使用 该路径。只读 Runtime 持有 Motor lease,因此不能在同一连接上直接调用 enable(); 需要控制时必须先 close(),再用普通 connect() 重新连接后调用 enable()。 0.10.12 修复了寄存器写 ACK 在 send() 返回前到达时被误判为旧响应的竞态,覆盖 ensure_mode() 和 set_can_timeout_ms()。SDK 仍保留 fail-closed 寄存器读回保护: 只有模式寄存器 10 或看门狗寄存器 9 与目标值完全一致时才接受写 ACK 异常,读回失败 或数值不一致仍然终止配置。 0.10.14 将 SocketCAN 和 SocketCAN-FD socket 设为非阻塞。发送队列满时默认最多等待 20 ms;EAGAIN、EWOULDBLOCK 或 ENOBUFS 不再让 Controller worker 永久阻塞。 需要调整时可设置 MOTOR_DRIVE_LAYER_SOCKETCAN_SEND_TIMEOUT_MS,有效范围为 1~60000 ms。超时会作为明确的 transport fault 返回,并写入 RuntimeTransportHealth.send_errors 和 last_error;双臂一侧队列堵塞时,批次会在 有限时间内失败并进入 Runtime 安全状态机,另一侧 worker、close_bus() 和 Runtime shutdown 不会被无限拖住。适配器固件、gs_usb、USB 供电或 TX URB 停止仍可能触发 底层队列堵塞,但不会再被软件放大成永久卡死。 SDK 的同步反馈和寄存器读回等待默认统一为 1000 ms。反馈新鲜度是独立的安全判据: 通用窗口默认为 50 ms,Yunyi 双通道产品配置使用 300 ms, 反馈健康检查仍为 100 Hz。缺失或超过窗口的反馈会累计到结构化健康诊断,但不会仅因 反馈超时把运行中的 Runtime 切换到 SAFE_HOLD 或 FAULT。 连接失败会抛出携带 ConnectReport 的 RuntimeTransactionError。诊断代码应直接读取 通道、电机和缺失反馈字段,不要解析异常字符串:

from arx_d_can import RuntimeTransactionError

try:
    robot.connect()
except RuntimeTransactionError as exc:
    report = exc.report
    print(report.error_code, report.received_count, report.expected_count)
    print(report.channels)
    print(report.motors)
    raise

0.10.10 保留了 DM Device 回调入口复制完整帧并保留物理 channel 的行为,底层同时校验 channel、 仲裁 ID 和 payload CAN ID;与反馈速度明显不相容的单帧位置跳变会在进入缓存前丢弃, 继续保留上一帧,不触发全局 FAULT 或失能。SDK 不重复实现这些过滤, read_cached_state() 的调用方式保持不变。底层完整性统计仅供内部硬件诊断使用,不加入 普通机器人接口。 若整个 Python 进程退出,进程内原生线程也会随之终止;SDK 因此在配置阶段同时写入 motor_communication_timeout_ms(Yunyi 默认 500 ms),由电机固件在主机进程消失后 执行最终通信超时失能。

双臂 Runtime 在构建时从同一产品标定生成并内置完整命令限位和原生产品绑定,不依赖 Python 在连接前读取 URDF。SDK 不实现接近限位减速;超出标定位置范围的目标由 Runtime 拒绝,范围内的运动速度由调用者指定。Runtime 同时校验命令速度和力矩限位。 motor-drive-layer 0.10.14 不再把反馈 位置、速度或力矩与命令限位比较,因此实际反馈轻微越界不会单独触发 FAULT、 SAFE_HOLD 或失能。原生重力补偿直接使用有限、已使能的实际七轴反馈,不经过 Python 保持目标。首次普通 MIT/PV 命令即使从超出配置硬限位的实际 反馈位置开始,也会由 Runtime 连续向合法目标推进,不会仅因此触发 FAULT 或 SAFE_HOLD;电机故障码、通信异常、发送失败和无法确认失能仍按原生安全状态机处理。

robot = ArxDCanDualArm(control_mode="pv")
robot.connect()
health = robot.safety_health
print(health.state)
print(health.fault_reason)
print(health.left_transport)
print(health.right_transport)
print(health.disable_confirmed)

双臂运行时要求左右关节命令整体接受或拒绝。连接到原生运行时后,单侧对象不能绕过 双臂批量状态机;普通位置更新必须调用双臂的 set_joint_mit() 或 set_joint_pv()。 单臂 ArxDCanArm 使用只包含一个 Controller 的同一原生运行时。SDK 不再包含 Python 看门狗、安全保持或夹爪防堵转执行循环;机械臂和夹爪正常运行时统一由原生 线程自动调度,安全保持同样由底层管理。调度周期会根据实际控制器拓扑自动选择, 但不作为用户调用频率的上限。相关职责由 motor-drive-layer 0.10.14 承担。Runtime ABI 2.6 在两侧 Controller 均报告 socketcanfd + can_fd + can_fd_brs 时允许原生 Runtime 最高运行到 500 Hz。motor-drive-layer 0.10.8 消除了 raw mailbox 提交与物理发送之间的 锁竞争,并让缓存状态读取只使用 MotorHandle 内部快照锁。标准 SDK 公开 submit_raw_mit() 已完成双臂 16 台、500 Hz、30 秒真机验收:提交 500.02 Hz,16 台 反馈 497.36~499.36 Hz,两侧收发错误为零,最终 16/16 失能。因此 Yunyi SocketCAN-FD+BRS 产品配置默认请求 500 Hz。SDK 始终使用 runtime.control_hz 获取真实 调度频率,不根据 transport capability 自行覆盖产品配置;其他 transport 仍由 Runtime 按 capability 限频。 Runtime ABI 2.6 在每个原生 MIT 发送周期(包括重复 mailbox 最新目标)根据最新 缓存反馈执行完整 P+D+前馈力矩保护。限制值直接使用用户配置的 torque_limit:未超限时 scale=1.0,只在超限时同比缩放 Kp、Kd 和 tau_ff。过期但仍有效的缓存样本可继续 用于计算,同时由 health 报告其年龄;缓存不存在、数据非法 或明确报告电机未使能时,整批仍会被拒绝。Yunyi 4310 V1.1 的 产品力矩范围仍为 7 N·m;底层 TMAX=10 只用于 MIT 报文编码映射,不作为机械 effort 上限。 双臂 MIT 运行时,14 个关节和两个夹爪由同一个 ControllerGroup 批次调度;夹爪反馈 年龄直接读取实时 Motor 缓存,SDK 不再增加单独的夹爪发送或反馈刷新路径。

使能时,SDK 先完成控制模式和电机参数配置,然后只调用一次原生 runtime.enable()。 ABI 2.4 Runtime 会并行刷新 CH0/CH1 的失能反馈,读取全部电机当前位置,生成安全保持 目标,并行使能左右通道并确认所有电机均返回新鲜 ENABLED 反馈。失败时 Runtime 会 回滚失能全部电机。SDK 不再提前调用左右臂或夹爪的物理使能接口。

普通运行故障会锁存 FAULT 并停止活动控制目标,但不会自动让其他健康关节掉电:仍可控制 的机械臂和夹爪继续发送保护保持,夹爪反馈丢失时保留最后安全夹持目标。只有用户明确 调用 disable()(或执行明确的急停策略)才请求全部电机物理失能。

使能失败时直接读取统一的 Runtime 健康状态:

try:
    robot.enable()
except RuntimeError:
    health = robot.safety_health
    print(health.state, health.fault_reason)
    print(health.motor_faults, health.unconfirmed_disable)
    raise

disable() 在 FAULT 状态下仍会尝试物理失能全部电机,但不会清除故障锁存; recover() 只有在物理失能、 反馈新鲜且通信健康均得到确认后才会回到 READY。

ABI 2.4 的 disable() 和 close() 使用同一个确定性失能事务:停止接收新命令,等待 在途批次完成,建立 ControllerGroup 与 USB/CAN 队列屏障,并行失能 CH0/CH1 并确认 所有电机的新鲜失能反馈;第一轮未确认的电机只会被定向重发一次。正常关闭不再依赖 电机通信超时。失能或关闭失败时同样读取 safety_health:

try:
    robot.close()
except RuntimeError:
    health = robot.safety_health
    print(health.fault_reason)
    print(health.unconfirmed_disable)
    raise

如果 close() 未确认全部电机失能,Runtime、ControllerGroup 和 Transport 均保持原样, 不会释放 native 句柄或资源租用;调用方可继续读取 safety_health 并重试 close()。只有关闭 事务成功后才会按所有权顺序释放资源。

Python native 句柄和资源租用由 SDK 的私有 ABI 层统一处理;SDK 始终按照 Runtime → ControllerGroup → Controller/Transport 的顺序清理。使能、失能和运行故障统一通过 robot.safety_health 对外报告。

运行实机运动示例时,应先激活 Python 环境再执行 python -m ...。如果必须使用 conda run,需要添加 --no-capture-output:

conda run --no-capture-output -n at python -m \
  arx_d_can.examples.dual_arm.example_06_send_position_pv \
  --left "0,0,0,90,0,0,0" \
  --right "0,0,0,90,0,0,0" \
  --velocity 60

PV 的 --velocity 是双臂所有关节共用的 0~100 速度档位。Runtime 在原生实际 控制周期内换算物理速度并限步,新的调用直接覆盖最终目标,不排队。关节产品配置中的 物理速度限制仍是绝对安全上限。

MIT 示例只接收左右臂目标角度,控制参数由机型配置统一管理:

python -m arx_d_can.examples.dual_arm.example_07_send_position_mit \
  --left "0,0,0,90,0,0,0" \
  --right "0,0,0,90,0,0,0" \
  --velocity 60

MIT 示例使用相同的 0~100 速度档位,Kp/Kd 读取产品配置, 目标速度和前馈力矩为零。普通 MIT/PV 调用都是非阻塞的最新值控制。

普通 conda run -n ... 会缓存子进程输出,并可能在 Ctrl+C 时由 Conda 自己截获 中断,使 Python 的 finally 清理逻辑没有机会完成。运动控制不能依赖这种运行方式。

  • 单次双通道批量发送失败或 streaming 命令超时会进入 SAFE_HOLD;
  • 反馈缺失或过期只更新健康诊断,不改变 Runtime 运行状态;
  • 安全保持失败、设备掉线、非法反馈、电机故障或意外失能会锁存 FAULT;夹爪 故障默认执行 motor 产品 profile 内置的保持策略;
  • read_state() 和 read_cached_state() 都读取 Runtime 后台持续刷新的完整缓存;
  • communication_health 提供结构化通信状态;
  • close() 默认停止后台任务、失能电机并关闭总线。
health = arm.communication_health
print(health.healthy)
print(health.consecutive_feedback_failures)
print(health.last_error)

C++ 安全运行时不是安全认证功能。产品仍需要物理急停、电机侧通信超时,以及垂直负载 场景所需的机械制动或防坠机构。

自定义机型

内置机型注册在 arx_d_can/config/models.yaml。增加单臂机型时创建自己的硬件 YAML, 然后注册机型名称即可继续使用全部 single_arm 示例:

from arx_d_can import ArxDCanArm, available_models

print(available_models())
arm = ArxDCanArm(model="my_arm")

本地调试未注册配置时,也可以使用高级参数:

arm = ArxDCanArm(config_path="/path/to/my_arm.yaml")

开发验证

python -m pip install ".[dev]"
python -m pytest --import-mode=importlib --rootdir=tests tests
python -m pip wheel --no-deps . --wheel-dir dist

Metadata

Release files for arx-d-can 0.3.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Built distribution (wheel)

Table of built distributions (wheels) for arx-d-can 0.3.0
File Interpreter ABI Platform
arx_d_can-0.3.0-py3-none-any.whl Python 3 none any Details

Release files / arx_d_can-0.3.0-py3-none-any.whl

Download URL arx_d_can-0.3.0-py3-none-any.whl
Size 165.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
9a8650316003df5747f008839884bb3558aecfdf7af8733358a2772987d2b974
BLAKE2b-256 checksum
How to use checksums
87696c97af2a73143069b2b8aec15411c5bff154a9d85dddffc8789aa2aba1a7
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.10.20

Release history Release notifications | RSS feed

0.3.5

2 release files

0.3.4

2 release files

0.3.3

2 release files

This release

0.3.0 This release

1 release file

0.2.2

2 release files

0.2.1

2 release files

0.2.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page