CANopen 协议入门教程

基于 CANopen CiA 标准,结合伺服电机驱动实战场景编写。


目录

  1. CANopen 概述
  2. CAN 总线基础回顾
  3. CANopen 协议分层
  4. 对象字典(Object Dictionary)
  5. PDO —— 过程数据对象
  6. SDO —— 服务数据对象
  7. NMT —— 网络管理
  8. SYNC 同步与心跳机制
  9. CiA402 驱动行规
  10. 节点启动流程
  11. 常见调试方法
  12. 参考资料

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 的"道"。

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐