RDK X5,你的下一代CAN电机调试助手

平台:RDK X5
电机:DJI M2006 、M3508、GM6020
屏:3.5 寸 ST7796S SPI LCD(480×320)+ GT911 触摸
关键词:SocketCAN、tcan4x5x、m_can、PyQt5、PID

打过RoboMaster 的小伙伴都有一个痛点, 就是机械经常需要动电机去测试一些机构是否合理,如弹仓拨轮,发射等,但是嵌入式不一定有空,有了RDK X5 DJI控制上位机软件,这个痛点就会迎刃而解,纯小白,只要接上线,打开软件,就能控制电机,调节PID,甚至还能看曲线,让 RDK X5 像一台普通工控机一样去驱动 DJI 电机。先看效果:


1. RDK X5 硬件介绍

RDK X5集成了TCAN4550 TCAN4550是带有集成 CAN FD 收发器的 CAN FD 控制器,支持高达 8Mbps 的数据速率。此 CAN FD 控制器符合 ISO11898-1:2015 高速控制器局域网 (CAN) 数据链路层的规范,并满足 ISO11898–2:2016 高速CAN 规范的物理层要求。TCAN4550 通过串行外设接口 (SPI) 在 CAN 总线和系统处理器之间提供了一个接口,同时支持经典 CAN 和 CAN FD,并为不支持CAN FD 的处理器实现 端口扩展或 CAN 支持。TCAN4550 提供 CAN FD 收发器功能:传输到总线的差分传输能力和从总线接收的差分接收能力。

这意味着你不需要外挂任何 USB-CAN 适配器或 STM32 中转板,X5 本身就能直接接入 CAN 总线。配合板载的算力,X5 可以成为一台"算法 + 运动控制"一体机——视觉/SLAM 在一颗 SoC 上跑,控制环也在同一颗 SoC 上跑,省掉外挂 MCU + 串口/USB 中转的麻烦。

X5 的 CANFD 接口详细引脚定义和电气规格,可参考官方文档:
https://developer.d-robotics.cc/rdk_doc/Quick_start/hardware_introduction/rdk_x5#canfd-接口

小结:RDK X5 自带 TCAN4x5x CAN 控制器,出厂就有 can0 接口,是做"视觉 + 运控"一体化的天然好平台。


2. CAN 基础使用

2.1 接线与终端电阻

DJI C610/C620 电调的 CAN 接口走的是标准 CAN 2.0B 差分总线,接线如下:

   RDK X5           DJI C610 电调
 ┌──────────┐               ┌──────────────┐
 │ CAN0_H   ├───────────────┤ CAN_H        │
 │ CAN0_L   ├───────────────┤ CAN_L        │
 │ GND      ├───────────────┤ GND          │
 └──────────┘               └──────────────┘
                  │       │
                 [120Ω 终端电阻 跨接在 CAN_H / CAN_L 上]

几个必须注意的点:

  • 120Ω 终端电阻必装。整条 CAN 总线两端各需要一颗 120Ω 电阻。保持总线电阻为60欧,这里打开的是rdk x5的can电阻和c610电调的电阻。
  • GND 必须共地。CAN 是差分信号但不是悬空的,没有共地会出现莫名其妙的 bus-off。
  • 电调供电独立:C610 走 24V,建议用带电流表的稳压电源,方便看堵转电流。
  • 不要把 H/L 接反,接反不会烧东西,但 can0 会一直收不到任何报文。

提示:先用万用表量一下 CAN_H 和 CAN_L 之间的电阻,应该接近 60Ω(两颗 120Ω 并联)。这是判断终端电阻有没有装对的最快方法。

2.2 安装 CAN 驱动修复包

RDK X5 出厂的 hobot-boot 包对 m_can 的中断处理有个小问题。参考话题:

D-Robotics 后来发布了修复版的 boot 包,装上就好了:

# 下载修复包
wget https://archive.d-robotics.cc/downloads/fix_deb/hobot-boot_3.1.0-20260518154347_arm64.deb

# 安装并重启
sudo dpkg -i hobot-boot_3.1.0-20260518154347_arm64.deb
sudo reboot

重启后 can0 的 RX 就能正常工作了。

2.3 配置 can0 接口

重启完成后,配置 CAN 接口:

sudo ip link set can0 down 2>/dev/null
sudo ip link set can0 type can bitrate 1000000 restart-ms 100
sudo ip link set can0 up
ip -details link show can0

注意三点:

  1. bitrate 必须设为 1Mbps。DJI 电调的波特率是写死的 1M,不能改。
  2. restart-ms 100:bus-off 后 100ms 自动恢复,调试期间被电调拔插一下不至于要手动重启 can0。
  3. ip -details 输出里的 state ERROR-ACTIVE,如果是 BUS-OFF,那基本就是终端电阻或接线有问题。

把它写成 systemd 服务或者放进 /etc/network/interfaces.d/can0 都行,看个人习惯。

2.4 用 candump / cansend 测试

物理层通不通,最快的验证方法不是写 Python,而是 cansend

# 给 motor1(ID 1)发 1000 正向电流
# 0x03E8 = 1000,big-endian
cansend can0 200#03E8000000000000

如果电机"嗡"一下转起来,恭喜,物理层完全没问题。如果没反应,请按以下顺序排查:

  • candump can0 有没有看到 0x201 周期性反馈帧(电调每 1ms 反馈一次)?没有 → 检查接线 / 终端电阻 / fix 包。
  • 电调的拨码(C610 上有 4 位拨码盘)有没有正确设置 ID?1 号电调拨到 1。
  • 24V 上电没?C610 没有供电是不会动的。

2.5 SocketCAN 编程基础(Python)

Linux SocketCAN 把 CAN 总线抽象成一个网络接口,用标准 socket API 就能收发 CAN 帧,不需要任何额外 SDK。

import socket, struct

# 创建 CAN socket
s = socket.socket(socket.AF_CAN, socket.SOCK_RAW, socket.CAN_RAW)
s.bind(('can0',))

# 发送一帧 CAN 数据
# SocketCAN 帧格式:can_id(4B) + dlc(1B) + pad(3B) + data(8B)
data = struct.pack('>hhhh', 1000, 0, 0, 0)      # motor1 = 1000mA
frame = struct.pack('=IB3x8s', 0x200, 8, data)   # '=' 强制 native byte order
s.send(frame)

# 接收一帧
raw = s.recv(16)
can_id, dlc, _, data = struct.unpack('=IB3x8s', raw)

两个要点:

  • struct.pack('=IB3x8s', ...) 里的 = 是关键,强制 native byte order,否则在 ARM64 上对齐会错位。
  • CAN socket 默认会收到总线上所有帧;可以用 s.setsockopt(socket.SOL_CAN_RAW, socket.CAN_RAW_FILTER, ...) 做硬件级过滤,减少 CPU 开销。

3. SPI LCD 屏幕配置

为了让 GUI 更好用,我在 X5 上接了一块 3.5 寸 ST7796S SPI LCD(480×320)+ GT911 触摸屏。这块小屏可以直接手指触控,比 SSH 命令行操作电机方便太多。

ST7796S + GT911 这一套在 RDK X5 上的完整点亮过程(包括内核模块、设备树 overlay、X11 配置),我之前在 D-Robotics 论坛写过一篇详细教程,可以参考:

点亮之后系统会多出一个 /dev/fb1 和 X11 上的一块新 screen,可以直接跑 Qt/PyQt5 应用。也可以使用 D-Robotics 提供的 Cathay-Launch-UI 仓库一键部署所有驱动和配置。

小结:3.5 寸 SPI 屏点亮后,X5 就有了一块可以手指触控的显示终端,非常适合做电机的现场调试 GUI。


4. DJI RoboMaster 电机控制

这是本文的核心部分。DJI RoboMaster 系列电机(M2006、M3508、GM6020)通过对应电调(C610/C620)与主控通信。M2006 和 M3508 共用同一套 CAN 协议,但 GM6020 的发送帧 ID 和接收帧 ID 与它们不同,混用时需要特别注意。

4.1 DJI CAN 协议

M2006/M3508 和 GM6020 的 CAN 帧结构相同(8 字节、大端序),但 帧 ID 完全不同,混用时必须区分电机类型。

发送帧(主控 → 电调)

M2006 / M3508(电流控制):

CAN ID 控制的电机 字节 0~1 字节 2~3 字节 4~5 字节 6~7
0x200 motor 1~4 M1 电流 (i16 BE) M2 电流 M3 电流 M4 电流
0x1FF motor 5~8 M5 电流 M6 电流 M7 电流 M8 电流

GM6020:

CAN ID 控制的电机 字节 0~1 字节 2~3 字节 4~5 字节 6~7
0x1FF motor 1~4 M1 电压 (i16 BE) M2 电压 M3 电压 M4 电压
0x2FF motor 5~8 M5 电压 M6 电压 M7 电压 M8 电压

GM6020:

CAN ID 控制的电机 字节 0~1 字节 2~3 字节 4~5 字节 6~7
0x1FE motor 1~4 M1 电流 (i16 BE) M2 电流 M3 电流 M4 电流
0x2FE motor 5~8 M5 电流 M6 电流 M7 电流 M8 电流
  • DLC 固定 8,即使你只控制一个电机,剩下的位置补 0。
  • 大端序(big-endian),Python 里用 struct.pack('>h', value) 打包。
  • 0x1FF 冲突0x1FF 同时承载 M3508 ID 5~8 的电流命令和 GM6020 ID 1~4 的电压命令。如果你的系统里同时有这两组电机,它们的命令会在同一帧的同一字节位置冲突,此时需要通过 += 合并,或者避免这种配置。

接收帧(电调 → 主控)

M2006 / M3508:

CAN ID = 0x200 + motor_id,例如 1 号电机反馈帧 ID 是 0x201

GM6020:

CAN ID = 0x204 + motor_id,例如 1 号 GM6020 反馈帧 ID 是 0x205,2 号是 0x206

混用时的歧义问题0x205~0x208 这个范围既可能是 M3508 的 ID 5~8(0x200+5~8),也可能是 GM6020 的 ID 1~4(0x204+1~4)。解歧的方法是根据系统中配置的电机类型来判断——如果你在 ID 1 上挂的是 GM6020,那 0x205 就是 GM6020 ID=1 的反馈;如果挂的是 M3508,那 0x205 就是 M3508 ID=5 的反馈。

反馈帧格式三款电机完全一致:

字节 内容 类型 单位
0~1 转子机械角度 ecd u16 BE 0~8191(对应 0~360°)
2~3 实际转速 rpm i16 BE rpm
4~5 实际转矩电流 cur i16 BE 与发送同尺度
6 电调温度 u8
7 保留 - -

解包示例:

ecd, rpm, cur, temp = struct.unpack('>HhhB', data[:7])
angle_deg = ecd * 360.0 / 8192.0

4.2 电机型号与规格

电机 配套电调 电流范围 特点
M2006 C610 ±10000 小体积,低扭矩,适合弹舱
M3508 C620 ±16384 RoboMaster 标配驱动轮电机
GM6020 C620 ±25000 云台电机,空心轴,支持角度直控

三款电机的反馈帧格式相同,但 GM6020 的发送帧 ID 和接收帧 ID 与 M2006/M3508 不同(详见 4.1 节),混用时务必注意。

4.3 控制模式

DJI 电调本身只提供电流控制(M2006/M3508)或电压控制(GM6020),更高级的控制环需要主控自己实现:

  • 电流模式:直接下发电流值,电机输出对应扭矩。最底层,响应最快,但需要自己做一切闭环。
  • 速度模式:在电流模式之上套一层 PI 环,目标 = rpm,输出 = 电流命令。适合做轮速控制。
  • 位置模式:在速度模式之上再套一层 P 环(或 PD 环),目标 = 角度,输出 = 目标 rpm,再由速度环输出电流。适合云台/舵轮。

4.4 PID 在线参数整定与曲线观看

5 PyQt5 GUI 应用

光命令行还不够直观,我做了一个跑在 3.5 寸 SPI 屏上的图形界面,可以拨滑条调电流、切换控制模式、看实时曲线。

GUI 架构

┌─────────────────────────────────────────┐
│  GUI 主线程 (PyQt5)                      │
│  ├─ 滑条/按钮  → 写共享 target_state    │
│  └─ QChart     ← 读 ring-buffer         │
├─────────────────────────────────────────┤
│  控制线程 (1kHz, time.perf_counter)     │
│  ├─ 读反馈 (非阻塞 recv)                 │
│  ├─ PID 计算                             │
│  └─ struct.pack + s.send()              │
└─────────────────────────────────────────┘

要点:

  • GUI 和控制环必须分线程。Qt 的事件循环卡几十毫秒很正常,但 DJI 看门狗只有约 100ms,主线程一卡电机就停了。
  • 反馈帧用非阻塞 recvs.setblocking(False),在 1kHz 周期里 try 一次,能读就读,读不到就跳过。
  • 窗口全屏showFullScreen() + setWindowFlag(Qt.FramelessWindowHint),480×320 全屏没有标题栏才好用手指点。
  • 图表:用 QtChart + QSplineSeries,X 轴用 ring buffer 滑窗,只保留最近 5 秒数据,否则点多了渲染就拖死了。

功能特性

  • 多电机支持:最多 8 路电机同时控制,每路独立滑条和模式切换。
  • 控制模式切换:电流 / 速度 / 位置三种模式,一键切换。
  • 实时曲线:RPM、角度、电流三条曲线实时滚动显示。
  • 暗色主题:480×320 小屏上暗色主题对比度更好,手指操作也更舒适。
  • 一键停止:紧急停止按钮,所有电机电流立即归零。


仓库

完整代码:

在板子上克隆仓库直接运行下面命令就能跑

  cd ~/dji_motor_ctl
  ./scripts/run.sh

预告

RDK X5 你的下一代CAN逻辑分析仪

为什么我的CAN接口回环测试,即使接上示波器在RDK上发送CAN通讯的指令 我用示波器还是无法观测到CANL和CANH的电平变化 始终都是2.5V左右,是不是我哪里没有配置好

你的系统版本是多少,
安装 CAN 驱动修复包了吗

安装了安装了,我用示波器和万用表测CANL和CANH都只有2.5V,不会产生波形,是不需要配置其他的东西了吧

系统是Ubuntu 22.04.5 LTS

RDK的CAN的文章太少了 教程也大多层次不齐 内容挺简略的,大大有好的文章或者步骤吗

在终端中执行命令sudo rdkos_info检查一下是不是3.5.0,文档的话可以参考RDK X3/X5 DOC

RDK OS Version 3.4.1

升级到3.5.0并安装补丁包

感谢建议 我试一下

成功了 谢谢您11111

1 个赞