CANopen 协议入门教程
CANopen 协议入门教程
基于 CANopen CiA 标准,结合伺服电机驱动实战场景编写。
目录
- CANopen 概述
- CAN 总线基础回顾
- CANopen 协议分层
- 对象字典(Object Dictionary)
- PDO —— 过程数据对象
- SDO —— 服务数据对象
- NMT —— 网络管理
- SYNC 同步与心跳机制
- CiA402 驱动行规
- 节点启动流程
- 常见调试方法
- 参考资料
1. CANopen 概述
1.1 什么是 CANopen
CANopen 是一种建立在 CAN 总线之上的高层应用层协议,由 CiA(CAN in Automation)组织标准化。它定义了设备如何通过 CAN 总线进行通信、配置和状态管理,广泛应用于工业自动化、机器人、伺服驱动、传感器等领域。
1.2 核心特点
| 特点 | 说明 |
|---|---|
| 基于 CAN 总线 | 物理层和数据链路层使用 CAN 2.0A(11 位标识符) |
| 主从架构 | 一个主站(Master)管理多个从站(Slave) |
| 对象字典 | 每个从站都有一个标准化的参数表 |
| 多种通信服务 | PDO(实时数据)、SDO(配置数据)、NMT(网络管理)、SYNC(同步) |
| 设备行规 | 标准化设备行为,如 CiA402 伺服驱动行规 |
1.3 CANopen vs 原始 CAN 报文
原始 CAN 报文的 11 位 ID 在 CANopen 中被赋予了严格定义:
CAN ID = Function Code(4 bit) + Node ID(7 bit)
例如:0x181 = 0x180(TPDO1 功能码)+ 0x01(节点 1)
2. CAN 总线基础回顾
2.1 CAN 帧结构
CANopen 仅使用数据帧(部分使用远程帧),标准格式如下:
┌─────────┬──────┬──────┬────────┬────────┬──────────────┐
│ SOF │ ID │ RTR │ DLC │ Data │ CRC/ACK/EOF │
│ 1 bit │11 bit│ 1 bit│ 4 bit │0-8 byte│ │
└─────────┴──────┴──────┴────────┴────────┴──────────────┘
- ID(COB-ID):11 位标识符,CANopen 中严格定义了每一段的含义
- DLC:数据长度(0-8 字节)
- Data:最多 8 字节数据
2.2 常用 CAN 命令
# 查看 CAN 接口
ip link show can0
# 设置 CAN 接口(1Mbps)
sudo ip link set can0 type can bitrate 1000000
sudo ip link set can0 up
# 抓取 CAN 报文
candump can0
# 发送 CAN 报文
cansend can0 601#40600000
# 关闭 CAN 接口
sudo ip link set can0 down
3. CANopen 协议分层
CANopen 协议栈从下到上分为四层:
┌─────────────────────────────────────┐
│ 应用层(Application) │
│ CiA402 驱动行规、设备配置文件 │
├─────────────────────────────────────┤
│ 对象字典(Object Dictionary) │
│ 所有通信和应用的参数中心 │
├──────┬──────┬──────┬──────┬──────────┤
│ PDO │ SDO │ NMT │ SYNC │ Heartbeat│
│实时 │ 配置 │ 管理 │ 同步 │ 心跳 │
├──────┴──────┴──────┴──────┴──────────┤
│ CAN 数据链路层(Data Link) │
│ CAN 2.0A 帧封装 │
├─────────────────────────────────────┤
│ CAN 物理层(Physical) │
│ 差分信号、CAN_H/CAN_L │
└─────────────────────────────────────┘
通信对象优先级
| 通信对象 | COB-ID 范围 | 优先级 |
|---|---|---|
| NMT | 0x000 | 最高 |
| SYNC | 0x080 | |
| Emergency | 0x080 + NodeID | |
| PDO1 (tx) | 0x180 + NodeID | |
| PDO1 (rx) | 0x200 + NodeID | |
| PDO2 (tx) | 0x280 + NodeID | |
| PDO2 (rx) | 0x300 + NodeID | |
| PDO3 (tx) | 0x380 + NodeID | |
| PDO3 (rx) | 0x400 + NodeID | |
| PDO4 (tx) | 0x480 + NodeID | |
| PDO4 (rx) | 0x500 + NodeID | |
| SDO (tx) | 0x580 + NodeID | |
| SDO (rx) | 0x600 + NodeID | |
| Heartbeat | 0x700 + NodeID | 最低 |
COB-ID 越小,CAN 总线优先级越高。 所以 NMT 报文的优先级最高。
4. 对象字典(Object Dictionary)
4.1 概念
对象字典是 CANopen 设备的核心,每个从站设备都有一个对象字典,通过16 位索引(Index)+ 8 位子索引(Subindex) 来寻址。
对象字典地址 = 0xIndex + 0xSubindex
示例:0x6040:00 → 控制字(Controlword)
0x6041:00 → 状态字(Statusword)
0x607A:00 → 目标位置(Target Position)
4.2 常用对象字典区域
| 索引范围 | 用途 |
|---|---|
| 0x1000~0x1FFF | 通信行规区域(通信参数、设备类型等) |
| 0x2000~0x5FFF | 制造商特定区域(各厂家自定义参数) |
| 0x6000~0x9FFF | 设备行规区域(CiA402 标准参数) |
4.3 伺服驱动常用对象字典(CiA402)
| 索引 | 子索引 | 名称 | 说明 |
|---|---|---|---|
| 0x6040 | 0x00 | Controlword | 控制字,控制状态机切换 |
| 0x6041 | 0x00 | Statusword | 状态字,反馈当前状态 |
| 0x6060 | 0x00 | Modes of Operation | 操作模式设置 |
| 0x6061 | 0x00 | Modes of Operation Display | 当前操作模式显示 |
| 0x6064 | 0x00 | Position Actual Value | 实际位置 |
| 0x606C | 0x00 | Velocity Actual Value | 实际速度 |
| 0x607A | 0x00 | Target Position | 目标位置 |
| 0x60FF | 0x00 | Target Velocity | 目标速度 |
| 0x6081 | 0x00 | Profile Velocity | 轮廓速度 |
| 0x6083 | 0x00 | Profile Acceleration | 加速度 |
| 0x6084 | 0x00 | Profile Deceleration | 减速度 |
| 0x6071 | 0x00 | Target Torque | 目标转矩 |
| 0x6077 | 0x00 | Torque Actual Value | 实际转矩 |
| 0x603F | 0x00 | Error Code | 错误码 |
5. PDO —— 过程数据对象
5.1 概述
PDO(Process Data Object) 是 CANopen 中用于实时、周期性数据交换的机制,是 CANopen 性能的核心。
- 不需要应答:生产者发送,消费者接收,无需确认
- 数据长度:最多 8 字节(一个 CAN 帧)
- 传输效率高:无协议开销,直接映射对象字典条目
- 适合实时控制:位置、速度、控制字、状态字等
5.2 RPDO 与 TPDO
| 类型 | 方向 | 示例 | 用途 |
|---|---|---|---|
| RPDO(Receive PDO) | 主站 → 从站 | 0x200 + NodeID | 发送控制命令 |
| TPDO(Transmit PDO) | 从站 → 主站 | 0x180 + NodeID | 上报状态数据 |
5.3 PDO 映射
PDO 并不天然存在,必须通过 SDO 配置对象字典,告诉从站"把哪些数据映射到哪个 PDO 通道"。
PDO 映射示例(TPDO1):
对象字典 0x1A00:00 → 映射条目数量(如 2 个)
对象字典 0x1A00:01 → 0x60410010 → 映射 0x6041:00(状态字),16 bit
对象字典 0x1A00:02 → 0x60610008 → 映射 0x6061:00(模式显示),8 bit
映射后 TPDO1 的数据布局:
┌───────────────┬──────────────┐
│ 状态字(16bit) │ 模式显示(8bit)│
│ data[0..1] │ data[2] │
└───────────────┴──────────────┘
5.4 PDO 传输模式
| 模式 | 配置值 | 说明 |
|---|---|---|
| 同步周期 | 1~240 | 每 N 个 SYNC 触发一次 |
| 同步非周期 | 0 | 收到 SYNC 后触发,但不由 SYNC 强制轮询 |
| 事件触发 | 254 | 数据变化时触发(默认) |
| 事件定时 | 255 | 定时器触发,配合事件定时器使用 |
实际应用中最常见的两种模式:
同步模式(sync_pdo = true):
主站发送 SYNC → 所有从站同步锁存/输出 PDO
事件定时模式(sync_pdo = false):
每个从站按自己的定时器周期发送 PDO
5.5 代码实践
PDO 帧的构建(以 RPDO1 发送控制字为例):
COB-ID = 0x200 + NodeID (RPDO1 基地址 + 节点 ID)
Data[0..1] = 控制字(16 位小端)
Data[2] = 操作模式(8 位)
示例:发送 0x000F(使能)给节点 1
→ CAN ID: 0x201
→ Data: 0F 00 01
TPDO 的解析(以 TPDO1 接收状态字为例):
COB-ID = 0x180 + NodeID (TPDO1 基地址 + 节点 ID)
Data[0..1] = 状态字(16 位小端)
Data[2] = 操作模式显示(8 位)
示例:节点 1 返回状态
→ CAN ID: 0x181
→ Data: 37 00 01 → 状态字 = 0x0037(使能状态)
6. SDO —— 服务数据对象
6.1 概述
SDO(Service Data Object) 用于配置和参数读写,采用客户端-服务器模型。
- 一问一答:发送请求,等待响应(确认服务)
- 支持任意长度:超过 4 字节的数据可使用分段传输
- 效率较低:不适合实时周期性通信
- 用于配置:设置 PDO 映射、修改参数、读取错误码等
6.2 SDO 通信流程
主站(客户端) 从站(服务器)
│ │
│ SDO 请求(0x600 + NodeID) │
│───────────────────────────────>│
│ │
│ SDO 响应(0x580 + NodeID) │
│<───────────────────────────────│
│ │
6.3 SDO 报文格式
SDO 请求(主站 → 从站,COB-ID = 0x600 + NodeID)
Byte 0 : 命令字
Byte 1-2 : 索引(小端)
Byte 3 : 子索引
Byte 4-7 : 数据
命令字说明:
0x40 → 读请求(Upload)
0x2F → 写 1 字节(Download,1 byte)
0x2B → 写 2 字节(Download,2 byte)
0x23 → 写 4 字节(Download,4 byte)
SDO 响应(从站 → 主站,COB-ID = 0x580 + NodeID)
Byte 0 : 响应命令字
Byte 1-2 : 索引(小端)
Byte 3 : 子索引
Byte 4-7 : 数据
响应命令字说明:
0x4F → 读响应 1 字节
0x4B → 读响应 2 字节
0x43 → 读响应 4 字节
0x60 → 写成功确认
0x80 → 写失败(中止码)
6.4 实际报文示例
例 1:读取节点 1 的当前状态字(0x6041:00)
请求:CAN ID 0x601,数据 40 41 60 00 00 00 00 00
↑ ───↑─── ↑
命令 索引 子索引 数据(读请求填 0)
响应:CAN ID 0x581,数据 4F 41 60 00 37 00 00 00
↑ ───↑─── ↑ ──↑──
命令 索引 子索引 状态字 = 0x0037
例 2:写入节点 1 的目标位置(0x607A:00)为 10000
请求:CAN ID 0x601,数据 23 7A 60 00 10 27 00 00
↑ ───↑─── ↑ ────↑────
命令 索引 子索引 目标位置 = 10000(0x2710)
响应:CAN ID 0x581,数据 60 7A 60 00 00 00 00 00
↑
写成功确认
6.5 SDO 超时处理
SDO 是一问一答模式,主站发送请求后需要等待从站响应。如果在一定时间内没有收到响应(或收到中止码),则视为超时,需要处理错误。
常见 SDO 中止码:
0x05030000 → 触发器未就绪
0x06010000 → 不支持该访问方式
0x06020000 → 对象字典中不存在该索引
0x08000000 → 数据错误/设备故障
0x08000020 → 数据传送已中止
0x08000021 → 本地控制已中止传送
7. NMT —— 网络管理
7.1 NMT 状态机
CANopen 网络中的每个从站都遵循 NMT 状态机:
┌───────────────┐
│ Initialisation│
└───────┬───────┘
│ 自动切换
┌───────▼───────┐
│ Pre-Operational│ ← 只能 SDO 通信,不能 PDO
└───────┬───────┘
│ NMT Start
┌───────▼───────┐
│ Operational │ ← 可进行 PDO 和 SDO 通信
└───────┬───────┘
│ NMT Stop
┌───────▼───────┐
│ Stopped │ ← 只响应 NMT 命令
└───────────────┘
| 状态 | 可使用的通信服务 |
|---|---|
| Initialisation | 无 |
| Pre-Operational | SDO(配置)、NMT、Heartbeat |
| Operational | SDO、PDO(实时通信)、NMT、SYNC、Heartbeat |
| Stopped | 仅 NMT |
7.2 NMT 命令
COB-ID = 0x000(广播,所有节点接收)
Data[0] = 命令字
Data[1] = 目标节点 ID(0 = 广播给所有节点)
命令字:
0x01 → Start(进入 Operational)
0x02 → Stop(进入 Stopped)
0x80 → Enter Pre-Operational
0x81 → Reset Node(复位节点)
0x82 → Reset Communication(复位通信参数)
示例:启动节点 1
CAN ID: 0x000
Data: 01 01
7.3 启动节点的一般流程
1. 从站上电 → 自动进入 Pre-Operational
2. 主站通过 SDO 配置 PDO 映射等参数
3. 主站发送 NMT Start → 从站进入 Operational
4. 开始 PDO 通信
8. SYNC 同步与心跳机制
8.1 SYNC 同步对象
SYNC 对象用于多轴同步,由主站周期性广播:
COB-ID: 0x080
DLC: 0(无数据)
- 所有从站在收到 SYNC 后同步锁存输入/输出
- 在同步 PDO 模式下,从站收到 SYNC 后触发 PDO 发送
- 保证多轴之间的时序同步
8.2 心跳机制(Heartbeat)
心跳用于监测从站是否在线,由从站周期发送:
COB-ID: 0x700 + NodeID
Data[0]: 当前 NMT 状态
状态值:
0x00 → Boot-up(启动)
0x04 → Stopped
0x05 → Operational
0x7F → Pre-Operational
- 主站通过心跳判断从站是否掉线
- 掉线后主站可执行重连或故障处理
9. CiA402 驱动行规
9.1 概述
CiA402 是 CANopen 的伺服/驱动器标准行规,定义了驱动器的标准行为,包括状态机、控制字、状态字、操作模式等。
9.2 CiA402 状态机
驱动器的状态机是 CANopen 应用的核心概念:
┌──────────────┐
│ Not Ready to │
│ Switch On │
└──────┬───────┘
│ 自动切换
┌──────▼───────┐
│ Switch On │
┌───>│ Disabled │
│ └──────┬───────┘
│ │ 控制字 = 0x0006(Shutdown)
│ ┌──────▼───────┐
│ │ Ready to │
│ │ Switch On │
│ └──────┬───────┘
│ │ 控制字 = 0x0007(Switch On)
│ ┌──────▼───────┐
│ │ Switched │
│ │ On │
│ └──────┬───────┘
│ │ 控制字 = 0x000F(Enable Operation)
│ ┌──────▼───────┐
│ │ Operation │ ← 正常使能状态
│ │ Enabled │
│ └──────┬───────┘
│ │ 故障
│ ┌──────▼───────┐
│ │ Fault │
│ │ Reaction │
│ └──────┬───────┘
│ │
│ ┌──────▼───────┐
│ │ Fault │
│ └──────┬───────┘
│ │ 控制字 = 0x0080(Fault Reset)
└───────────┘
9.3 控制字(Controlword,0x6040)
| 命令 | 控制字值 | 说明 |
|---|---|---|
| Shutdown | 0x0006 | 关闭电源,切换到 Ready to Switch On |
| Switch On | 0x0007 | 上电,切换到 Switched On |
| Disable Operation | 0x0001 | 停止运行,切换到 Switched On |
| Quick Stop Active | 0x000B | 急停,切换到 Quick Stop Active |
| Enable Operation | 0x000F | 使能,切换到 Operation Enabled |
| Fault Reset | 0x0080 | 故障复位 |
9.4 状态字(Statusword,0x6041)
bit 位含义:
| Bit | 含义 |
|---|---|
| 0 | Ready to Switch On |
| 1 | Switched On |
| 2 | Operation Enabled |
| 3 | Fault |
| 4 | Voltage Enabled |
| 5 | Quick Stop |
| 6 | Switch On Disabled |
| 7 | Warning |
| 8 | — |
| 9 | Remote |
| 10 | Target Reached |
| 11 | Internal Limit Active |
| 12~13 | 操作模式特定 |
典型状态字值:
| 值 | 含义 |
|---|---|
| 0x0000 | Not Ready to Switch On |
| 0x0040 | Switch On Disabled |
| 0x0021 | Ready to Switch On |
| 0x0023 | Switched On |
| 0x0027 | Operation Enabled |
| 0x0008 | Fault |
| 0x0010 | Quick Stop Active |
9.5 操作模式
| 模式 | 代码 | 说明 |
|---|---|---|
| 位置模式(Profile Position) | 1 | 点到点定位,带轮廓速度/加速度 |
| 速度模式(Profile Velocity) | 3 | 速度控制 |
| 回零模式(Homing) | 6 | 回机械原点 |
9.6 完整使能流程
步骤 1:发送控制字 0x0006(Shutdown)
状态字 → 0x0021(Ready to Switch On)
步骤 2:发送控制字 0x0007(Switch On)
状态字 → 0x0023(Switched On)
步骤 3:发送控制字 0x000F(Enable Operation)
状态字 → 0x0027(Operation Enabled)✓ 使能完成
10. 节点启动流程
一个完整的 CANopen 从站启动流程:
┌──────────────────────────────────────────────────┐
│ 1. 物理连接 │
│ - CAN 接口 up(ip link set can0 up) │
│ - 从站上电 │
├──────────────────────────────────────────────────┤
│ 2. 从站自动进入 Pre-Operational │
│ - 发送 Boot-up 心跳(0x700 + NodeID) │
├──────────────────────────────────────────────────┤
│ 3. 主站通过 SDO 配置从站 │
│ - 设置 PDO 映射(RPDO1~4, TPDO1~4) │
│ - 设置通信参数(传输类型、事件定时器) │
│ - 设置操作模式、轮廓参数等 │
├──────────────────────────────────────────────────┤
│ 4. 主站发送 NMT Start(0x000) │
│ - 从站进入 Operational 状态 │
├──────────────────────────────────────────────────┤
│ 5. 开始 PDO 通信 │
│ - 主站通过 RPDO 发送控制字/目标位置 │
│ - 从站通过 TPDO 上报状态字/实际位置 │
│ - 可选:SYNC 同步模式 │
├──────────────────────────────────────────────────┤
│ 6. CiA402 使能(使电机上电) │
│ - Controlword: 0x0006 → 0x0007 → 0x000F │
│ - 状态字变为 0x0027 → 电机可运行 │
└──────────────────────────────────────────────────┘
11. 常见调试方法
11.1 使用 candump 抓包
# 抓取所有报文
candump can0
# 过滤特定节点(如节点 1 的 SDO)
candump can0,601:7FF,581:7FF
# 过滤特定 COB-ID
candump can0,181:7FF # TPDO1 节点1
11.2 使用 cansend 手动测试
# NMT 启动节点 1
cansend can0 000#0101
# 读取节点 1 状态字(0x6041:00)
cansend can0 601#4041600000000000
# 读取节点 1 实际位置(0x6064:00)
cansend can0 601#4064600000000000
# 使能节点 1(三步走,RPDO1 控制字 + 操作模式)
cansend can0 201#060001 # Shutdown + 位置模式
cansend can0 201#070001 # Switch On + 位置模式
cansend can0 201#0F0001 # Enable + 位置模式
11.3 使用 Python 快速原型
import can
import struct
# 连接 CAN 接口
bus = can.interface.Bus(channel='can0', bustype='socketcan')
# NMT 启动节点 1
msg = can.Message(arbitration_id=0x000, data=[0x01, 0x01])
bus.send(msg)
# PDO 使能节点 1(RPDO1)
msg = can.Message(arbitration_id=0x201, data=[0x0F, 0x00, 0x01])
bus.send(msg)
# SDO 读取状态字
msg = can.Message(arbitration_id=0x601,
data=[0x40, 0x41, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00])
bus.send(msg)
# 接收响应
response = bus.recv(timeout=0.1)
if response.arbitration_id == 0x581:
status_word = struct.unpack_from('<H', response.data, 4)[0]
print(f"Statusword: 0x{status_word:04X}")
11.4 常见问题排查
| 问题 | 可能原因 | 排查方法 |
|---|---|---|
| SDO 超时 | 节点未上电 / Node ID 不对 / 波特率不匹配 | 检查物理连接,candump 看是否有回复 |
| PDO 无数据 | 节点不在 Operational 状态 | 检查是否发送了 NMT Start |
| 使能失败 | 状态机跳转错误 / 安全条件不满足 | 检查状态字,确认每一步的跳转条件 |
| 心跳超时 | 节点掉线 / 总线错误 | 检查 CAN 线缆,查看错误计数器 |
| 运动不执行 | 操作模式不匹配 / 轮廓参数未设置 | 检查模式显示,设置轮廓速度和加速度 |
12. 参考资料
- CiA 301 — CANopen 应用层和通信行规
- CiA 402 — CANopen 驱动和运动控制设备行规
- CiA 305 — CANopen 层设置服务(LSS)
- Linux 工具:
candump,cansend,cansniffer,canconfig - 在线资源:CAN in Automation
写在学习之后: CANopen 的精髓在于"对象字典"这个中心化的设计思路——所有通信参数、设备参数、运行数据都通过对象字典索引来访问,上层协议(PDO/SDO/NMT)只是围绕这个中心的不同访问方式。理解了对象字典,就理解了 CANopen 的"道"。
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐



所有评论(0)