Chapter 12 · CAN 总线 / 汽车电子

CANopen 工业协议

工业自动化与部分车载的标准化上层协议:以对象字典为核心,用 PDO/SDO/NMT/SYNC/EMCY/心跳等通信对象组织节点,COB-ID 预定义连接集与 NMT 状态机让即插即用成为可能。

课程进度75%

关键概念

CANopen (CiA 301)
由 CiA(CAN in Automation)维护的高层协议,广泛用于工业自动化(电机驱动、电梯、医疗设备、轨道交通),也见于部分商用车与特种车辆。核心标准 CiA 301 定义通信规范,各设备类型有专门 profile(如 CiA 402 伺服驱动)。它让不同厂家的设备能在同一 CAN 网络上即插即用。
对象字典 (Object Dictionary, OD)
CANopen 的核心,是节点内所有可访问参数的「表格」。每一项由 Index(16 位,如 0x1000)+ Subindex(8 位)定位。地址区段:0x1000–0x1FFF 通信参数、0x2000–0x5FFF 厂商自定义、0x6000–0x9FFF 设备 profile 标准对象。外界通过读写 OD 条目来配置和控制节点。
SDO (Service Data Object)
服务数据对象,用于点对点、可靠地读写对象字典中任意条目(配置阶段用)。采用客户端-服务器模型:客户端(主站)发请求,服务器(从站节点)应答。默认 COB-ID:请求 0x600+NodeID、响应 0x580+NodeID。SDO 有确认机制、支持分段传输大数据,但开销大、速度慢,不适合实时。
PDO (Process Data Object)
过程数据对象,用于高效实时地传输过程数据(如实时位置、速度、IO 状态)。无协议开销,一帧 8 字节全是数据,也无需确认。TPDO(发送 PDO)由节点主动发出,RPDO(接收 PDO)由节点接收。默认 COB-ID:TPDO1 0x180+NodeID、RPDO1 0x200+NodeID 等。
PDO 映射与传输类型
映射:PDO 帧里的 8 字节内容并非固定,而是由映射参数(0x1600 段 RPDO、0x1A00 段 TPDO)指定「打包哪些 OD 条目」,可灵活组合。传输类型(0x1400/0x1800 通信参数的 subindex 2):0 = 同步非周期、1–240 = 每 N 个 SYNC 发一次、254/255 = 事件触发(异步,数据变化即发或按定时器)。
NMT (Network Management)
网络管理,由主站集中控制从站的运行状态。NMT 报文用最高优先级 COB-ID 0x000,2 字节:命令字 + 目标 NodeID(0 = 广播全部)。命令:0x01 启动(进 Operational)、0x02 停止、0x80 进预操作、0x81 复位节点、0x82 复位通信。
SYNC / EMCY / 心跳
SYNC(同步,COB-ID 0x080):主站周期广播,让多节点同步采样/动作。EMCY(紧急,0x080+NodeID):节点发生错误时上报错误码。心跳 Heartbeat(0x700+NodeID):节点周期发送自身 NMT 状态,主站据此判断节点是否在线(替代旧的节点保护 Node Guarding)。
COB-ID 与 EDS
COB-ID(Communication Object ID)= CAN 仲裁 ID,CANopen 用「预定义连接集」把功能码(高 4 位)与 NodeID(低 7 位)组合成 11 位 ID,因此 NodeID 范围 1–127。EDS(Electronic Data Sheet,电子数据表)是描述某设备对象字典的标准 INI 格式文件,工程工具导入 EDS 即知该设备支持哪些参数,生成配置文件 DCF。

COB-ID 预定义连接集

CANopen 把 11 位 CAN ID 拆成「4 位功能码 + 7 位 NodeID」,功能码决定优先级与对象类型:

通信对象功能码COB-ID 公式方向
NMT 控制00000x000(广播)主→从
SYNC00010x080(广播)主→全网
EMCY00010x080 + NodeID从→主
TPDO100110x180 + NodeID从→主
RPDO101000x200 + NodeID主→从
TPDO201010x280 + NodeID从→主
RPDO201100x300 + NodeID主→从
SDO 响应10110x580 + NodeID从→主
SDO 请求11000x600 + NodeID主→从
心跳/Boot-up11100x700 + NodeID从→主
为什么 NodeID 最大 127?

预定义连接集用 CAN 11 位 ID 的低 7 位作为 NodeID,7 位最大 127,NodeID=0 保留给广播。功能码占高 4 位,天然决定了优先级:功能码越小 ID 越小,仲裁时越优先。所以 NMT(0x000) 优先级最高、SYNC 次之、PDO 高于 SDO,恰好符合实时性需求。

对象字典结构

OD 是节点参数的树状表格,每个 Index 可含多个 Subindex。下面是一个典型节点 OD 的骨架:

对象字典 (Object Dictionary) 分区结构 ═══════════════════════════════════════════════════════════ Index 区段 内容举例 ──────────────────────────────────────────────────── 0x1000 设备类型 只读,标识设备 profile 0x1001 错误寄存器 1 字节错误标志位 0x1017 心跳生产者时间(ms) 写入即启用心跳 0x1018 身份对象 厂商ID/产品码/序列号 │ 0x1400+ RPDO 通信参数 COB-ID / 传输类型 0x1600+ RPDO 映射参数 打包哪些 OD 条目 0x1800+ TPDO 通信参数 COB-ID / 传输类型 / 定时 0x1A00+ TPDO 映射参数 打包哪些 OD 条目 │ 0x2000+ 厂商自定义区 自由定义的参数 │ 0x6000+ 设备 profile 区 CiA 402: 0x6040 控制字 (标准对象) 0x6041 状态字 0x6064 实际位置 ┌──── 一个 Index 的展开(0x1018 身份对象)────┐ │ Sub 0 = 子索引数量 (4) │ │ Sub 1 = Vendor ID (厂商编号) │ │ Sub 2 = Product Code (产品码) │ │ Sub 3 = Revision (版本) │ │ Sub 4 = Serial No. (序列号) │ └─────────────────────────────────────────────┘

NMT 状态机

每个 CANopen 节点上电后按固定状态机运行,主站用 NMT 命令切换状态:

CANopen 从站 NMT 状态机 ═══════════════════════════════════════════════════════════ 上电 / 复位 │ ▼ ┌──────────────────┐ │ Initialisation │ 初始化 OD、发 Boot-up(0x700+ID, 00) │ (初始化) │ └────────┬─────────┘ │ 自动进入 ▼ ┌──────────────────┐ NMT 0x80 (进预操作) ┌───►│ Pre-operational │◄──────────┐ │ │ (预操作) │ │ │ │ 可用 SDO,不发PDO │ │ │ └────────┬─────────┘ │ │ NMT 0x01 │ │ │ (启动) ▼ │ │ ┌──────────────────┐ NMT 0x02 (停止) │ │ Operational │────────┐ │ │ │ (操作) │ │ │ │ │ PDO+SDO 全可用 │ ▼ │ │ └──────────────────┘ ┌──────────────┐ │ │ Stopped │ └────────────────────────────┤ (停止) │ NMT 0x81/0x82 复位 │ 仅 NMT+心跳 │ └──────────────┘ 命令字: 01=启动 02=停止 80=预操作 81=复位节点 82=复位通信
只有 Operational 才发 PDO

节点在 Pre-operational 态可以用 SDO 配置(读写 OD),但不会收发 PDO 过程数据。因此典型启动流程是:等待所有从站发出 Boot-up 报文 → 用 SDO 配置各节点 PDO 映射 → 主站广播 NMT 0x01 启动 → 节点进入 Operational 开始实时数据交换。若忘记发启动命令,PDO 永远不动是常见新手问题。

SDO 上传 / 下载报文

SDO 读(上传 Upload)和写(下载 Download)用固定的命令字格式。「快速传输 Expedited」适合 ≤4 字节数据,一来一回即完成。

SDO 快速下载(写 4 字节到 OD 0x6040:00) 主站请求 COB-ID = 0x600 + NodeID ┌────┬────┬────┬────┬────┬────┬────┬────┐ │2B4060000F00 │ 00 │ 00 │ └────┴────┴────┴────┴────┴────┴────┴────┘ │ └──┴─ Index 0x6040(小端: 40 60) │ └─ Sub 0x00 └─ 数据 0x000F └─ 命令字 0x2B = 写,快速,2字节有效(n=1) 从站响应 COB-ID = 0x580 + NodeID ┌────┬────┬────┬────┬────┬────┬────┬────┐ │60 │ 40 │ 60 │ 00 │ 00 │ 00 │ 00 │ 00 │ └────┴────┴────┴────┴────┴────┴────┴────┘ └─ 0x60 = 下载成功确认 若出错 → 0x80 + Index + Sub + 4字节 Abort Code (如 0x06090011 = subindex 不存在)

用 python-canopen 读写节点

Python 的 canopen 库封装了 SDO/PDO/NMT,导入 EDS 即可用符号名访问对象字典。

# canopen_demo.py —— SDO 读写 + PDO 配置 + NMT 启动
import canopen

network = canopen.Network()
network.connect(channel="can0", interface="socketcan")

# 添加 NodeID=5 的从站,导入其 EDS
node = network.add_node(5, "drive.eds")

# ① SDO 读:读设备类型 0x1000(一来一回,可靠)
dev_type = node.sdo[0x1000].raw
print(f"设备类型: 0x{dev_type:08X}")

# ② SDO 写:写控制字 0x6040:00 = 0x000F(使能运行)
node.sdo[0x6040].raw = 0x000F

# ③ 配置 TPDO1:映射实际位置 0x6064,事件触发
node.tpdo.read()                      # 读现有配置
node.tpdo[1].clear()
node.tpdo[1].add_variable(0x6064)     # 打包"实际位置"
node.tpdo[1].trans_type = 255          # 255=异步事件触发
node.tpdo[1].event_timer = 100          # 每100ms发一次
node.tpdo[1].enabled = True
node.tpdo.save()                      # 写回节点 OD

# ④ NMT 启动:进入 Operational,PDO 开始收发
node.nmt.state = "OPERATIONAL"

# ⑤ 接收 TPDO 回调
def on_position(msg):
    pos = node.tpdo[1]["Position actual value"].phys
    print(f"实际位置: {pos}")

node.tpdo[1].add_callback(on_position)

import time; time.sleep(5)
network.disconnect()
SDO 慢、PDO 快,各司其职

配置阶段(改参数、设 PDO 映射)用 SDO——可靠有确认但慢;运行阶段(周期读位置、写目标速度)用 PDO——无开销无确认但快。一个正确的 CANopen 应用应该在 Pre-operational 阶段用 SDO 把一切配置妥当,进入 Operational 后只跑 PDO,避免运行时频繁 SDO 拖慢总线。

心跳与节点在线监测

节点周期发心跳(0x700+ID,1 字节 = 当前 NMT 状态),主站超时未收到即判定掉线。写入 OD 0x1017 生产者心跳时间(ms)即启用。

# 启用节点心跳并监测在线状态
node.sdo[0x1017].raw = 500            # 每 500ms 发一次心跳

def on_heartbeat(state):
    names = {0: "Boot-up", 4: "Stopped",
             5: "Operational", 127: "Pre-op"}
    print("节点状态:", names.get(state, state))

node.nmt.add_heartbeat_callback(on_heartbeat)
本章小结

CANopen(CiA 301)是工业与部分车用的高层协议,核心是对象字典 OD——用 Index(16 位)+ Subindex(8 位)定位节点所有参数,分通信区(0x1000–0x1FFF)、厂商区(0x2000+)、profile 区(0x6000+)。通信对象各司其职:SDO(0x600/0x580+ID)点对点可靠读写 OD,配置用,慢;PDO(TPDO 0x180+ID / RPDO 0x200+ID)无开销实时传过程数据,其内容由映射参数(0x1600/0x1A00)灵活组合,传输类型决定同步/周期/事件触发;NMT(0x000)集中管理节点状态机(初始化→预操作→操作/停止),只有 Operational 才收发 PDO;SYNC(0x080)同步、EMCY(0x080+ID)报错、心跳(0x700+ID)监测在线。COB-ID = 功能码 + NodeID,NodeID 1–127。EDS 电子数据表描述设备 OD,工程工具导入即用。典型流程:等 Boot-up → SDO 配置 → NMT 启动 → PDO 实时运行。

⚒️ 配套开发者工具箱 · JSON / 正则 / 时间戳 / Cron 等 17 款免费在线工具