首页/目录/全部文章

全部文章

八个专题的源码、算法与协议笔记都在这里。

笔记列表

帧结构与寻址

帧结构与寻址

EtherType 0x88A4 · 一帧多 datagram · 四种寻址(位置/节点/逻辑/广播)
本篇是理解 EtherCAT 数据面的核心:datagram + WKC + FMMU + SyncManager


1. EtherCAT 帧格式

EtherCAT 报文嵌在标准以太网帧内(EtherType = 0x88A4),因此普通网卡、普通交换机
(主站侧)、Wireshark 都能直接处理。也可封装在 UDP/IP 中(UDP 端口 0x88A4 = 34980,
用于跨路由段,牺牲实时性,工程上少用)。

1.1 整帧字节布局

1
2
3
4
5
6
7
8
9
10
11
 0        6        12    14   16                                       尾部
┌────────┬────────┬─────┬────┬──────────┬──────────┬───┬──────────┬────┬─────┐
│ 目的MAC │ 源MAC │0x88A4│ECAT│datagram 1│datagram 2│...│datagram n│ pad│ FCS │
│ 6B │ 6B │ 2B │头2B│ │ │ │ │ │ 4B │
└────────┴────────┴─────┴────┴──────────┴──────────┴───┴──────────┴────┴─────┘

EtherCAT 头(2 字节): ▼
┌───────────────────┬──────┬────────┐
│ Length (bit 0-10) │ 保留 │ Type │
│ 后续 datagram 总长 │ 1bit │ 4bit=1 │
└───────────────────┴──────┴────────┘
约束
帧总长 标准以太网限制:64 ~ 1518 字节
一帧可携带的 datagram 数据 最多约 1486 字节
Type 字段 0x1 = EtherCAT 命令(datagram),其他值保留
过程数据超过一帧 主站自动拆成多帧,每帧独立 WKC 校验

1.2 Datagram(子报文)结构

一帧内可串联多个 datagram,每个 datagram 是一次独立的读/写事务
有自己的命令、地址和 WKC。

1
2
3
4
5
6
7
8
9
10
11
12
┌─────┬─────┬───────────────┬───────────────┬─────┬────────────┬─────┐
│ Cmd │ Idx │ Address │ Len + 标志位 │ IRQ │ Data │ WKC │
│ 1B │ 1B │ 4B │ 2B │ 2B │ 0~1486B │ 2B │
└─────┴─────┴───────────────┴───────────────┴─────┴────────────┴─────┘
头部固定 10 字节 尾部 2 字节

Len+标志位(2B):
┌──────────────────┬────────┬───┬───┐
│ Length (bit 0-10)│ 保留3b │ C │ M │
└──────────────────┴────────┴───┴───┘
C = Circulating(帧在环内循环标记)
M = More(=1 表示后面还有 datagram)
字段 大小 含义
Cmd 1 B 命令类型(APRD/FPWR/LRW/BRD…,见 §2)
Idx 1 B 主站自用索引,从站原样带回,用于匹配收发/检测丢帧
Address 4 B 含义随寻址方式变:ADP(2B)+ADO(2B) 或 32 位逻辑地址
Len + 标志 2 B 数据长度(11 bit)、C 循环位、M 后续位
IRQ 2 B 从站事件请求汇总(各从站按位或写入)
Data 0~1486 B 读命令:从站往里填;写命令:从站从中取
WKC 2 B 工作计数器,被处理一次就递增(见 §3)

2. 寻址方式详表

Address 字段 4 字节,物理寻址时拆为 ADP(Address Position,选中”哪个从站”)
ADO(Address Offset,从站 ESC 内的物理内存偏移)。

寻址方式 命令 Address 含义 典型用途
位置寻址(自动增量) APRD/APWR/APRW ADP=位置计数(每经过一个从站硬件 +1,值为 0 的那个从站执行)+ ADO 上电初始扫描:此时从站还没有地址
节点寻址(固定地址) FPRD/FPWR/FPRW ADP=configured station address(主站启动时写入寄存器 0x0010)+ ADO 扫描后所有单从站访问:读状态、写配置、邮箱
逻辑寻址 LRD/LWR/LRW 32 位逻辑地址(4 GB 逻辑空间),FMMU 命中才处理 周期过程数据主用:一个 LRW 服务全网 PDO
广播 BRD/BWR ADP 被每个从站 +1(回程时=从站数),所有从站都执行,ADO 有效 全网读 AL Status、广播清零、DC 初始化

2.1 完整命令表

编号 命令 含义
0 NOP 空操作
1 / 2 / 3 APRD / APWR / APRW 位置寻址 读 / 写 / 读写
4 / 5 / 6 FPRD / FPWR / FPRW 节点寻址 读 / 写 / 读写
7 / 8 / 9 BRD / BWR / BRW 广播 读 / 写 / 读写
10 / 11 / 12 LRD / LWR / LRW 逻辑寻址 读 / 写 / 读写
13 ARMW 位置寻址读,后续所有从站写(多播写,DC 时间分发用)
14 FRMW 节点寻址读,后续所有从站写(DC:读参考时钟、分发给全网)

2.2 位置寻址的细节

初始扫描时从站没有任何配置地址,主站用位置寻址逐个”点名”:

  • 帧穿过每个从站时,ESC 硬件把 ADP 加 1
  • 看到 ADP == 0 的从站执行本 datagram
  • 所以主站要访问第 k 个从站(k 从 1 计),发送 ADP = 1 - k(第 1 个用 0,第 2 个用 0xFFFF…)。

扫描完成后主站用 APWR 0x0010 给每个从站写入 configured station address,
之后一律改用 FPxx——因为位置寻址在从站掉线/热插拔时序号会漂移,不可靠。

另有 别名地址(alias,寄存器 0x0012,来自 EEPROM):由用户在从站上预设,
换线序也不变,工程上常用于”按站名找设备”。


3. WKC:工作计数器 = 可靠性心跳

datagram 尾部 2 字节 WKC 发出时置 0,每个从站成功处理一次就按规则递增
主站收到返回帧后比对”期望 WKC”,不等就说明有从站没干活(掉线、状态不对、配置错)。

命令类型 从站成功操作 WKC 增量
读(xRD) 读成功 +1
写(xWR) 写成功 +1
读写(xRW) 读成功 +1,写成功 +2 都成功 +3

期望值举例

场景 期望 WKC
BRD 0x0130(广播读 AL Status,全网 N 个从站) N
FPWR 写某一个从站 1
LRW 过程数据:M 个从站被 FMMU 命中读、K 个被命中写、J 个读+写 M×1 + K×2 + J×3

工程意义:

  • WKC 是 EtherCAT 每周期自带的健康检查,无需额外心跳报文;
  • SOEM/IgH 等主站在每个周期检查 WKC,小于期望即报警/降级;
  • WKC 只说明”有问题”,不说明”谁有问题”——需再用 FPRD 逐站读状态定位。

4. FMMU:逻辑地址 → 物理内存映射

逻辑寻址的基础。主站把全网过程数据规划成一片连续的4 GB 逻辑地址空间(过程映像),
每个从站的 FMMU 负责认领其中一段:

1
2
3
4
5
6
7
8
9
10
      主站逻辑过程映像(一个 LRW datagram 覆盖)
0x00010000 ┌──────────────┐
│ 从站1 输出 2B │──FMMU(写)──▶ 从站1 物理 0x1100
├──────────────┤
│ 从站2 输出 8B │──FMMU(写)──▶ 从站2 物理 0x1100
├──────────────┤
│ 从站1 输入 4B │◀─FMMU(读)─── 从站1 物理 0x1180
├──────────────┤
│ 从站2 输入 8B │◀─FMMU(读)─── 从站2 物理 0x1180
└──────────────┘
FMMU 特性 说明
粒度 位级:逻辑起始位/结束位、物理起始位都可配,1 个 bit 的 IO 也能紧凑排布
方向 每个 FMMU 单元配置为读(输入)或写(输出)
数量 每 ESC 若干个(如 ET1100 有 8 个),配置寄存器区 0x0600 起,每单元 16 字节
配置者 主站在 PreOp→SafeOp 迁移前按 ENI/在线计算写入
效果 全网 PDO 交换压缩为一条 LRW:帧流过时各从站硬件各取所需、各填所属

FMMU 配置单元内容(每 16 字节):逻辑起始地址(4B)、长度(2B)、逻辑起始位(1B)、
逻辑结束位(1B)、物理起始地址(2B)、物理起始位(1B)、类型读/写(1B)、激活(1B)、保留(3B)。


5. SyncManager:防撕裂的内存访问协调

ESC 内存同时被两侧访问:总线侧(帧流过)和 PDI 侧(从站本地 MCU)。
SyncManager(SM)保证双方永远读到/写到一致的完整数据,两种模式:

缓冲模式(3-buffer) 邮箱模式(1-buffer 握手)
用途 周期过程数据(PDO) 非周期邮箱(SDO/FoE/EoE…)
缓冲 3 个物理缓冲轮转 1 个缓冲
写方 永远有空闲缓冲可写,从不阻塞 写满后锁定,对方读走前不可再写
读方 永远读到最新的完整一致数据 读走后缓冲才释放
数据丢失 允许覆盖旧数据(过程数据只关心最新值) 不丢失(请求/应答语义)
撕裂 硬件保证无撕裂(不会读到半新半旧)

典型 SM 分配约定(多数从站遵循):

SM 方向 模式 内容
SM0 主→从 邮箱 邮箱输出(主站发请求)
SM1 从→主 邮箱 邮箱输入(从站回应答)
SM2 主→从 3-buffer 过程数据输出(RxPDO)
SM3 从→主 3-buffer 过程数据输入(TxPDO)

SM 配置寄存器区 0x0800 起,每单元 8 字节:物理起始地址(2B)、长度(2B)、
控制(1B:模式/方向/中断)、状态(1B)、激活(1B)、PDI 控制(1B)。


6. ESC 内存布局概览

地址范围 内容
0x0000 – 0x0FFF 寄存器区(4 KB,所有 ESC 统一编址)
0x1000 过程数据 RAM(用户内存,大小因芯片而异:ET1100 8 KB、LAN9252 4 KB)

寄存器区常用地址(后续各篇会反复引用):

寄存器 内容
0x0000 ESC 类型/版本/支持特性
0x0010 Configured Station Address(节点寻址用)
0x0012 Configured Station Alias(别名,来自 EEPROM)
0x0100 / 0x0110 DL Control / DL Status(端口环路控制与链路状态)
0x0120 / 0x0130 / 0x0134 AL Control / AL Status / AL Status Code(状态机,见 02 篇)
0x0300 各端口 CRC 错误计数器(排查坏线/干扰的利器
0x0500 EEPROM 访问接口(SII)
0x0600 FMMU 配置(每单元 16 B)
0x0800 SyncManager 配置(每单元 8 B)
0x0900 分布式时钟 DC 寄存器(见 03 篇)

7. EEPROM / SII:从站的”出厂身份证”

每个从站挂一片 EEPROM(I²C),内容称为 SII(Slave Information Interface)。
上电时 ESC 硬件自动加载开头几个字(PDI 配置、别名地址等)到寄存器;
其余由主站通过 0x0500 寄存器接口读取。

SII 内容(按字地址组织) 用途
字 0x0000–0x0007 PDI 控制/配置、Configured Station Alias、校验和(上电自动加载)
字 0x0008 起 Vendor ID、Product Code、Revision、Serial Number(主站识别设备、与 ENI 比对)
邮箱配置字段 标准/Bootstrap 邮箱的偏移与大小、支持的邮箱协议位图(CoE/FoE/EoE…)
字 0x0040 起:分类区(Category) Strings、General、FMMU、SyncManager、TxPDO/RxPDO 描述等可变长条目

工程要点:

  • 主站启动扫描 = 位置寻址逐站读 SII 身份四元组 → 与 ENI 期望比对 → 不符则拒绝进入 Op;
  • SII 中的 SM/FMMU 描述让主站可以无 ESI 文件在线组网(SOEM 的默认做法);
  • ESI XML 是 SII 的”源文件”,厂商工具把 ESI 烧写进 EEPROM。

下一篇

02-状态机与邮箱协议.md —— ESM 四态迁移、AL Status Code 排错表、邮箱握手与 CoE(SDO/PDO 映射配置全流程)。

状态机与邮箱协议

状态机与邮箱协议

ESM(EtherCAT State Machine):Init → PreOp → SafeOp → Op(+ Bootstrap)
邮箱 = 非周期通道(SM0/SM1 握手),最常用协议 CoE(CANopen over EtherCAT)
排错核心:AL Status Code(寄存器 0x0134)


1. ESM 状态机

1.1 状态与允许的通信

stateDiagram-v2
  [*] --> Init
  Init --> PreOp : IP(配地址+邮箱SM)
  PreOp --> SafeOp : PS(配PDO映射+FMMU+DC)
  SafeOp --> Op : SO(输出使能)
  Op --> SafeOp
  SafeOp --> PreOp
  PreOp --> Init
  Op --> Init : 可直接降级
  Init --> Bootstrap : 仅固件升级
  Bootstrap --> Init
状态 邮箱(SDO 等) 过程数据输入(TxPDO) 过程数据输出(RxPDO) 典型工作
Init 主站写站地址、配置 SM0/SM1 邮箱
PreOp CoE 参数配置、PDO 映射修改
SafeOp ✓(输入有效) ✗(输出不生效,从站保持安全值) 主站校验输入、启动 DC 同步
Op 正常运行
Bootstrap 仅 FoE 固件升级(只能从 Init 进入)

规则:升级必须逐级(Init→PreOp→SafeOp→Op),降级可跳级(Op→Init 合法)。

1.2 迁移机制:AL Control / AL Status

状态迁移全部由主站发起

1
2
3
4
5
6
7
8
9
10
11
12
主站 FPWR 0x0120 (AL Control) = 目标状态


从站应用检查前置条件(SM 配置对不对、DC 是否同步…)

┌────┴─────┐
成功 失败
│ │
▼ ▼
0x0130 0x0130 = 原状态 + Error 位(bit4)
= 新状态 0x0134 (AL Status Code) = 错误码
主站须写 AL Control 的 Error Ack 位清错后重试
寄存器 含义
0x0120 AL Control 主站写:目标状态(低 4 位)+ Error Ack(bit 4)
0x0130 AL Status 从站写:当前状态 + Error 标志(bit 4)
0x0134 AL Status Code 从站写:失败原因码

状态编码:Init=1、PreOp=2、Bootstrap=3、SafeOp=4、Op=8。

1.3 常见 AL Status Code(排错表)

代码 含义 常见根因
0x0000 无错误
0x0011 请求的状态迁移非法 跳级升 Op、从站不支持该迁移
0x0012 未知的请求状态 AL Control 写了非法值
0x0013 Bootstrap 不支持 该从站无固件升级功能
0x0014 无有效固件 升级中断/固件损坏
0x0016 邮箱配置无效(→PreOp) SM0/SM1 起始地址或长度与 SII 不符
0x0017 SyncManager 配置无效 SM2/SM3 长度 ≠ PDO 映射实际字节数(改映射后最常见
0x0018 无有效输入 从站应用尚未提供输入数据
0x0019 无有效输出 主站过程数据没发或长度不对
0x001A 同步错误 DC 模式下从站没等到 SYNC 信号、主站发帧抖动过大、Shift time 配错
0x001B SyncManager 看门狗超时 主站周期帧中断(>watchdog 时间没收到输出数据),Op 掉回 SafeOp
0x001C SyncManager 类型无效 SM 方向/模式配置错
0x001D 输出配置无效 RxPDO 映射/0x1C12 分配与 SM2 不一致
0x001E 输入配置无效 TxPDO 映射/0x1C13 分配与 SM3 不一致
0x001F 看门狗配置无效 看门狗分频/超时寄存器非法
0x0030 DC 无效同步配置 SYNC0/SYNC1 周期与主站周期不匹配

完整代码表见 ETG.1000.6 / ETG.1020 规范。上表为工程中最常遇到的子集。
注意各家文档对个别码的措辞略有差异,以从站手册为准。


2. 邮箱协议框架

邮箱走 SM0(主→从)/ SM1(从→主),1-buffer 握手模式(见 01 篇 §5):
写满即锁定、读走才释放,保证请求/应答不丢失、不撕裂。

2.1 邮箱头(6 字节,所有邮箱协议共用)

1
2
3
┌──────────┬──────────┬────────────────┬──────────────────────┐
│ Length 2B│ Address 2B│ Channel/Prio 1B│ Type(4b)+Cnt(3b)+保留 │
└──────────┴──────────┴────────────────┴──────────────────────┘
Type 值 协议 一句话
0x1 AoE ADS over EtherCAT(Beckhoff 私有路由协议)
0x2 EoE 隧道标准以太网帧——通过 EtherCAT 给从站”上网”(配置页面、诊断),非实时
0x3 CoE CANopen over EtherCAT,绝对主流,本篇重点
0x4 FoE File over EtherCAT——固件升级(Bootstrap 状态下用)
0x5 SoE Servodrive over EtherCAT——SERCOS 行规(IDN 参数),部分伺服厂商用
0xF VoE Vendor specific——厂商私有协议

Cnt(3 bit 计数器)用于检测邮箱报文丢失/重复:从站回环计数,主站校验。

2.2 邮箱读的轮询

从站不能主动发帧,所以”从→主”邮箱靠主站轮询 SM1 状态位(FPRD 0x080D 一带的
SM1 状态寄存器)发现”有数据”,再 FPRD 读走。主站栈通常把这个轮询合并进周期帧。


3. CoE 详解

CoE 把 CANopen(CiA 301)的对象字典 + SDO + PDO 概念原样搬到 EtherCAT 邮箱上,
CANopen 设备行规(如伺服的 CiA 402)可直接复用。

3.1 对象字典布局

索引范围 内容 例子
0x1000 设备类型 bit0-15 行规号(402=伺服)
0x10080x100A 设备名/硬件/软件版本 字符串
0x1018 Identity 子索引 1-4:Vendor ID / Product Code / Revision / Serial(与 SII 一致)
0x16000x17FF RxPDO 映射(主→从,输出) 0x1600:01 = 0x6040:00, 16bit
0x1A000x1BFF TxPDO 映射(从→主,输入) 0x1A00:01 = 0x6041:00, 16bit
0x1C00 SM 通信类型 SM0=邮箱出、SM1=邮箱入、SM2=输出、SM3=输入
0x1C12 SM2 的 PDO 分配(挂哪些 RxPDO) 0x1C12:01 = 0x1600
0x1C13 SM3 的 PDO 分配(挂哪些 TxPDO) 0x1C13:01 = 0x1A00
0x1C32 / 0x1C33 SM2/SM3 同步参数 同步模式(FreeRun/SM 事件/DC)、周期、Shift
0x6000 行规区 CiA 402:0x6040 控制字、0x6041 状态字、0x607A 目标位置、0x6064 实际位置、0x6060 运行模式

PDO 映射条目为 32 位:索引(16b) | 子索引(8b) | 位长(8b)
0x60400010 = 对象 0x6040:00、16 bit。

3.2 SDO 服务

服务 用途
SDO Download 主站写对象(参数下发)
SDO Upload 主站读对象
Expedited(快速) 数据 ≤4 字节,塞在请求/应答头里,一来一回搞定——最常见
Normal + Segmented 数据超过邮箱容量时分段传输(如字符串、大数组)
Complete Access 一次读/写整个对象的所有子索引(子索引 0 起),配 PDO 映射时省很多来回
SDO Information 枚举对象字典(在线读出对象列表和描述)

3.3 PDO 映射配置完整流程(示例)

目标:把 RxPDO 0x1600 改为映射 0x6040 控制字(16bit) + 0x607A 目标位置(32bit)
全部操作必须在 PreOp 状态下,步骤有严格顺序(先禁用再改再启用):

1
2
3
4
5
6
7
8
9
10
① SDO 写 0x1C12:00 = 0          ← 断开 SM2 的 PDO 分配
② SDO 写 0x1600:00 = 0 ← 清空映射表(子索引数=0)
③ SDO 写 0x1600:01 = 0x60400010 ← 条目1:0x6040:00, 16 bit
④ SDO 写 0x1600:02 = 0x607A0020 ← 条目2:0x607A:00, 32 bit
⑤ SDO 写 0x1600:00 = 2 ← 生效:映射条目数=2
⑥ SDO 写 0x1C12:01 = 0x1600 ← 把 0x1600 挂到 SM2
⑦ SDO 写 0x1C12:00 = 1 ← 生效:分配 1 个 PDO
⑧ (TxPDO 同理改 0x1A00 + 0x1C13)
⑨ 主站按新映射重算过程映像大小 → 重配 SM2/SM3 长度和 FMMU
⑩ AL Control → SafeOp → Op

常见坑:

  • 跳过 ①② 直接写条目 → 从站拒绝(SDO Abort);
  • 改了映射但主站没重配 SM 长度 → 迁移 SafeOp 时报 0x0017 / 0x001D
  • 从站是否支持改映射看 ESI/手册(很多设备映射固定,只能选预置 PDO)。

3.4 紧急报文 Emergency

从站异常时通过邮箱(CoE 服务号 1)主动上报(主站轮询邮箱时取到),
格式与 CANopen EMCY 相同,共 8 字节:

1
2
3
┌──────────────┬──────────────┬──────────────────────┐
│ Error Code 2B│ Error Reg 1B │ 厂商自定义数据 5B │
└──────────────┴──────────────┴──────────────────────┘

例:伺服过流 0x2310、编码器故障 0x7305(错误码沿用 CiA 301/402 定义)。
主站栈通常把 Emergency 转成日志/回调——调伺服时务必打印它,比 AL Status Code 信息量大。


4. 状态机 × 邮箱:主站启动全流程

1
2
3
4
5
6
7
Init:    APWR 写站地址 → FPWR 配 SM0/SM1(按 SII 邮箱字段)
│ AL Control = PreOp
PreOp: CoE SDO 配置参数、PDO 映射(§3.3)→ 配 SM2/SM3、FMMU、DC
│ AL Control = SafeOp
SafeOp: 周期帧开始跑(LRW),输入有效、输出不生效 → 校验 WKC 与 DC 收敛
│ AL Control = Op
Op: 输出使能,正常控制;WKC/AL Status 持续监控

下一篇

03-分布式时钟与同步.md —— DC 分布式时钟:传播延迟测量、系统时间分发(ARMW/FRMW)、SYNC0/SYNC1 信号与 <1 µs 同步的实现。

03 分布式时钟(DC)与同步

03 分布式时钟(DC)与同步

规范依据:ETG.1000.4(DL 协议,DC 寄存器)、ETG.1020(协议增强)、各 ESC 手册(如 Beckhoff ET1100/ET1200、TI ESC、HMS/Microchip LAN9252 等)
本篇为知识整理,寄存器地址以 ETG/ET1100 定义为准;个别 ESC 实现细节(滤波深度上限、端口数)随芯片不同,已在文中标注。

1. 为什么需要分布式时钟

多轴机器人/机床做插补运动时,”同一时刻”必须对所有轴成立:

  • 各伺服必须在同一物理时刻锁存编码器位置(否则主站看到的多轴位置不是同一快照,动力学/耦合计算失真);
  • 各伺服必须在同一物理时刻把新的目标值作用到电流环/位置环(否则轮廓轨迹在轴间错位,表现为加工纹路、圆度误差、机械抖动)。

EtherCAT 帧是串行经过各从站的:帧到达第 1 个从站和第 100 个从站之间有传播延迟(每个 ESC 转发延迟约几百 ns + 线缆延迟约 5 ns/m)。如果从站”收到帧就动作”,各轴动作时刻天然错开数 µs~几十 µs。

DC(Distributed Clocks)的目标:让所有支持 DC 的从站共享一个 System Time(系统时间),同步精度典型 < 1 µs(实测常见 < 100 ns),从站的采样/输出不再由”帧到达”触发,而是由本地 ESC 按 System Time 产生的 SYNC0/SYNC1 硬件中断触发。

1
2
3
4
5
6
不用 DC(帧触发):                     用 DC(SYNC0 触发):
帧 ─→ 轴1 ─→ 轴2 ─→ 轴3 帧提前送达各轴缓冲区
↑动作 ↑动作 ↑动作 │
t0 t0+Δ1 t0+Δ2 SYNC0 ───┼───┬───┬─── 同一 System Time
(轴间错位 Δ = 传播延迟) ↓ ↓ ↓
轴1 轴2 轴3 同时刻动作(<1µs 偏差)

2. 参考时钟(Reference Clock)

角色 定义
参考时钟 拓扑中第一个支持 DC 的从站的本地时钟(ESC 内部 64-bit ns 计数器),作为全网 System Time 的基准
从时钟 其余支持 DC 的从站,通过偏移 + 漂移补偿对齐参考时钟
主站时钟 主站 PC/控制器自己的时钟,可选参与:既可以让主站时钟对齐参考时钟(常见),也可以反向让参考时钟跟随主站/外部时钟(如 grandmaster、ETG.1020 的 external sync)

要点:

  • 参考时钟选”第一个 DC 从站”是因为它离主站最近、帧最先到达,便于以它的时间戳为基准分发;这是约定而非协议强制,主站栈(IgH、SOEM、TwinCAT)默认如此,也允许指定其他从站。
  • System Time 的纪元定义为 2000-01-01 00:00:00,单位 ns,64-bit(低 32-bit 约 4.29 s 回绕,仅低精度设备使用 32-bit)。
  • 主站通常不是时钟基准——主站发帧存在 OS 调度抖动,让硬件 ESC 做基准更稳;主站只需保证发帧周期大致稳定,剩下交给 DC。

3. 传播延迟测量(Propagation Delay Measurement)

3.1 机制

主站在初始化阶段(通常 PREOP 之前/期间)发送一条对寄存器 0x0900BWR(广播写):

  1. 该帧经过每个从站的每个开放端口时,ESC 硬件把”帧首到达本端口的本地时刻”锁存到接收时间戳寄存器:
    • 0x0900 端口 0、0x0904 端口 1、0x0908 端口 2、0x090C 端口 3(各 32-bit,本地时钟);
    • 0x0918(64-bit)锁存帧经过 ECAT 处理单元的本地时刻,用于偏移计算。
  2. 主站随后用读命令(APRD/FPRD)把各从站的这些时间戳读回;
  3. 主站离线计算各段线缆+转发延迟,把每个从站的累计延迟写入 0x0928 System Time Delay

3.2 为什么”环形”可测

EtherCAT 物理上是逻辑环:帧从从站的端口 0 进入,沿”处理方向”走到线末端,再沿”转发方向”原路返回。因此同一条测量帧会经过每个端口两次方向之一,每个从站能同时拿到”去程到达时刻”和”回程到达时刻”:

1
2
3
4
主站 ──→ [从站A p0]──→[p1] ══线缆══ [从站B p0]──→(末端回环)
↑回程 ←────────────────── ←──┘
A 的时间戳: t_A0 (p0 去程), t_A1 (p1 回程)
B 的时间戳: t_B0 (p0 去程=回环点)

对从站 A:t_A1 − t_A0 = 帧走完 A 下游整段并返回的往返时间(用 A 自己的钟测,不需要 A/B 时钟同步——这是关键)。逐级相减即可剥出每段延迟,线性拓扑下相邻两站间延迟:

[
d_{A\to B} \approx \frac{(t_{A1}-t_{A0}) - (t_{B1}-t_{B0})}{2}
]

(末端从站只有单端口开放时 t_B1−t_B0 取 0 或 ESC 内部环回延迟;公式假设线缆上下行对称,EtherCAT 100BASE-TX 全双工满足此假设。)

每个往返时间都是单个从站自己的时钟差值,晶振频偏只造成 ppm 级误差,所以延迟测量可以在时钟尚未同步时进行。主站通常多次测量取平均以压制抖动。


4. 偏移补偿与漂移补偿

DC 对齐分两步:先一次性搬走”大偏差”(offset),再持续伺服”晶振频差”(drift)。

4.1 偏移补偿(System Time Offset, 0x0920

每个 ESC 的本地时钟从上电起自由计数,彼此初值毫无关系(可能差几分钟)。主站利用延迟测量帧中读到的 0x0918(各站看到同一帧的本地时刻)计算:

1
2
3
SystemTime(从站) = 本地时钟 + SystemTimeOffset(0x0920)
目标: SystemTime(从站) ≈ SystemTime(参考时钟)
主站写入: 0x0920 ← t_ref(帧经过参考站时刻) − t_local(帧经过本站时刻) + 累计传播延迟

写入 0x0920 后各站的 System Time 已粗对齐(残差 = 测量误差 + 写入期间的漂移)。

4.2 漂移补偿(持续分发 + 时间控制环)

晶振有 ±20~100 ppm 频差,粗对齐后每秒仍会漂移几十 µs,必须持续伺服。主站在周期性过程数据帧里(或独立帧)加入一条 ARMW 或 FRMW 命令,地址指向参考从站的 0x0910(System Time,64-bit)

  • ARMW/FRMW(Read Multiple Write)语义:帧到达被寻址的从站(参考时钟)时读出其 System Time 放入数据区;帧经过其余所有从站时把该值写入它们的 0x0910 比较逻辑。
  • 每个从时钟 ESC 硬件计算 接收到的参考时间 + 本站 0x0928 延迟 与本站 System Time 之差,存入 0x092C System Time Difference,并驱动内部时间控制环:微调本地计数器步进速率(加/减计数),把差值收敛到 0。

相关调节/滤波寄存器(ET1100 定义):

寄存器 名称 作用
0x0930 Speed Counter Start 时间控制环的调速力度(带宽),复位后需主站写一次以启动漂移补偿
0x0932 Speed Counter Diff 当前调速偏差(只读,调试用)
0x0934 System Time Difference Filter Depth 对时差样本的平均滤波深度
0x0935 Speed Counter Filter Depth 对调速量的滤波深度
0x092C System Time Difference 当前本站与参考时钟差(bit31 为符号,监控同步质量就读它)

运维要点:主站可周期性 BRD 读 0x092C,所有从站的差值绝对值持续 < 1 µs(常见 < 100 ns)即认为 DC 收敛。IgH/SOEM 都提供了对应接口(如 SOEM 的 ec_DCtime、IgH 的 ecrt_master_sync_slave_clocks())。


5. SYNC0 / SYNC1 信号

同步时钟只是手段,SYNC 脉冲才是真正驱动应用的东西。ESC 内部有一个由 System Time 驱动的脉冲发生单元:

寄存器 作用
0x0980 Cyclic Unit Control(SYNC/latch 单元归主站还是本地 µC 控制)
0x0981 Activation:使能 SYNC0/SYNC1 输出、单次/循环模式
0x0990(64-bit) Start Time:第一个 SYNC0 脉冲的 System Time(”首周期对齐”就靠它)
0x09A0 SYNC0 Cycle Time(ns),如 1 000 000 = 1 ms
0x09A4 SYNC1 Cycle Time(相对 SYNC0 的偏移/周期,具体语义见 ESC 手册)
0x0984 脉冲宽度(Pulse Length)

要点:

  • SYNC0 是 ESC 硬件产生的中断脉冲,接到从站本地 µC/FPGA/驱动控制器的中断脚——精度由硬件保证,与帧到达时刻解耦。
  • 所有从站配置相同的 SYNC0 周期和经过对齐的 Start Time,于是全网 SYNC0 沿在 System Time 轴上重合 → 各站同时刻动作。
  • 伺服的典型用法:在 SYNC0 上升沿锁存编码器位置(采样一致性)、并把上个周期收到的目标值切换进控制环(输出一致性);SYNC1 常用于给应用一个相对 SYNC0 延后的第二事件(如”SYNC0 采样、SYNC1 上送”或 ADC 转换完成时刻),是否支持及语义由从站定义。
  • 从站应用侧的同步行为通过 CoE 对象 0x1C32(输出/RxPDO 同步参数)/0x1C33(输入/TxPDO 同步参数) 描述与配置::01 Sync Type(见下表)、:02 Cycle Time、:03 Shift Time 等。

6. 三种同步模式对比

Free Run SM-Synchronous DC-Synchronous
触发源 从站本地定时器,与总线无关 SM2/SM3 事件(过程数据帧到达时刻) SYNC0/SYNC1 硬件中断(System Time)
0x1C32:01 Sync Type 0 1 2(SYNC0)/ 3(SYNC1)
轴间一致性 受帧传播延迟影响(轴间错位 µs 级)且吃主站发帧抖动 < 1 µs,与帧到达时刻解耦
对主站抖动的容忍 无要求 抖动直接进入从站动作时刻 只需帧”按时到达缓冲区”,抖动被 SYNC0 吸收
硬件要求 ESC 即可 ESC 支持 DC + 从站应用接 SYNC 中断
典型场景 IO 采集、无实时性要求 单轴/低要求运动、简单 IO 同步 多轴插补、CSP/CSV/CST 伺服、分布式采样

7. 主站侧要求

DC 把从站侧的抖动问题解决了,但对主站提出了新要求:

7.1 周期发帧与抖动控制

  • 主站必须以固定周期(如 1 ms)发送过程数据帧,抖动应远小于周期(经验值:< 周期的 10%,越小裕量越充足)。
  • Linux 下通常组合:PREEMPT_RT 内核 + 实时线程(SCHED_FIFO)+ clock_nanosleep(TIMER_ABSTIME) 绝对时间唤醒 + CPU 隔离/中断绑核;网卡侧避免中断合并(coalescing)带来的收帧延迟。

7.2 发帧时机必须领先 SYNC0

DC 模式下的时序约束:目标数据必须在 SYNC0 沿之前已经写入从站的 SM 缓冲区,否则从站在 SYNC0 时刻拿到的是旧数据(或直接报同步错误):

1
2
3
4
5
System Time 轴 →
──┬────────────────────────┬────────────────────────┬──
SYNC0(n) SYNC0(n+1) SYNC0(n+2)
│←— shift/裕量 —→│
主站发帧 ──传播──→ 数据落入各从站 SM 缓冲 ✓(必须早于 SYNC0(n+1))
  • 主站周期任务的唤醒相位要与 System Time 对齐(主站时钟先对齐参考时钟,或按读回的 ec_DCtime 做相位闭环);
  • 发帧时刻 + 最坏传播/处理延迟 + 抖动 < SYNC0 时刻,不足则配置 Shift Time0x1C32:03 或把 0x0990 起始时间整体平移)把 SYNC0 相对”帧到达”后移;
  • 输入方向同理:若希望主站每周期读到”刚采样”的数据,需安排 SYNC0(采样)早于该周期的帧经过(TxPDO 上送)。

7.3 Sync/Latch 编程模型

主站配置 DC 的典型步骤(IgH/SOEM/TwinCAT 内部大同小异):

  1. 扫描拓扑,确定第一个 DC 从站为参考时钟;
  2. BWR 0x0900 触发时间戳锁存 → 读回 → 计算并写各站 0x0928(延迟)与 0x0920(偏移);
  3. 在周期帧中加入 ARMW/FRMW 0x0910 持续漂移补偿,等待 0x092C 收敛;
  4. 0x09A0/0x09A4 周期、0x0990 起始时间(取”当前 System Time + 若干整周期 + shift”,保证是未来时刻且全网一致),写 0x0981 激活 SYNC0/1;
  5. 主站周期任务与 System Time 相位锁定,进入 OP。

另外 ESC 还提供 Latch0/Latch1 输入(0x09A8 控制、0x09B0 起时间戳寄存器):外部信号沿触发时锁存 System Time,用于探针功能(touch probe,对应 CiA 402 的 0x60B8~`0x60BD`)、外部事件打时间戳。


8. 常见 DC 问题排查

现象 可能原因 查什么 / 怎么办
从站拒绝进 OP,AL Status Code 0x001A(Synchronization Error) 主站发帧抖动过大 / 帧频繁迟到,从站连续在 SYNC0 时刻没等到新数据 主站实时性(cyclictest 量化抖动)、网卡中断合并、周期任务相位;看从站 0x092C 是否收敛
AL Code 0x0030~`0x0036`(Invalid DC SYNC config / PLL Error / Sync Timeout / Invalid Cycle Time 等) SYNC0 周期与 0x1C32:02 不一致、周期超出从站支持范围、激活顺序错误 核对 ESI 中支持的周期;先写周期与起始时间再写 0x0981 激活
各轴动作仍有固定错位 Shift Time 配置不当,或部分从站落在 SM-Sync 模式 读各站 0x1C32:01 确认 Sync Type=2;统一 shift 策略
首周期丢步/报错,之后正常 0x0990 Start Time 写成了过去时刻,或各站起始时间未对齐到同一 System Time 栅格 Start Time = 当前 System Time 向上取整到周期边界 + ≥2 个周期裕量
同步精度差(0x092C 抖动大) 漂移补偿帧频率过低、0x0930 未写(补偿环未启动)、滤波深度不合适 每周期都带 ARMW/FRMW;检查 0x0930/0x0934/0x0935
热插拔/换线后错位 拓扑变化后传播延迟未重测 重新执行延迟测量并更新 0x0928
长时间运行后主站与总线相位漂移 主站只做了初始对齐、未持续锁相 主站周期任务对 ec_DCtime(参考时钟时间)做 PI 相位闭环

下一篇:04-CiA402伺服控制.md

04 CiA 402 伺服控制

04 CiA 402 伺服控制

规范依据:CiA 402(IEC 61800-7-201/301, “Drives and motion control device profile”)、ETG.6010(CiA 402 在 EtherCAT 上的实现导则)
CiA 402 原是 CANopen 设备行规,在 EtherCAT 上通过 CoE(CANopen over EtherCAT) 承载:对象字典、SDO、PDO 概念原样沿用;EtherCAT 只是把 PDO 装进过程数据帧、SDO 走邮箱。市面绝大多数 EtherCAT 伺服(安川、松下、汇川、Elmo、科尔摩根、Copley…)都实现该行规。

1. 总体结构

1
2
3
4
5
6
7
8
9
10
11
12
主站运动控制 (插补/轨迹)
│ 每周期 RxPDO: 控制字 + 目标值 │ 每周期 TxPDO: 状态字 + 实际值
▼ ▲
EtherCAT 过程数据 (SM2/SM3, DC-SYNC0)──────┘

从站 CoE 对象字典
├─ 0x6040/0x6041 控制字/状态字 ──→ 402 电源状态机
├─ 0x6060/0x6061 操作模式设定/显示
└─ 0x60xx 各模式的目标值/实际值/参数


驱动器控制环 (位置环→速度环→电流环) → 电机

两条独立但正交的轴线:状态机(能不能动:上电→使能)与操作模式(怎么动:位置/速度/转矩)。


2. 402 电源状态机

stateDiagram-v2
    [*] --> NotReadyToSwitchOn : 上电自检
    NotReadyToSwitchOn --> SwitchOnDisabled : 自动
    SwitchOnDisabled --> ReadyToSwitchOn : Shutdown (0x06)
    ReadyToSwitchOn --> SwitchedOn : Switch On (0x07)
    SwitchedOn --> OperationEnabled : Enable Operation (0x0F)
    OperationEnabled --> SwitchedOn : Disable Operation (0x07)
    SwitchedOn --> ReadyToSwitchOn : Shutdown (0x06)
    ReadyToSwitchOn --> SwitchOnDisabled : Disable Voltage (0x00)
    OperationEnabled --> QuickStopActive : Quick Stop (0x02)
    QuickStopActive --> SwitchOnDisabled : 快停完成(视0x605A)
    QuickStopActive --> OperationEnabled : Enable Operation (0x0F)*
    OperationEnabled --> SwitchOnDisabled : Disable Voltage (0x00)
    NotReadyToSwitchOn --> FaultReactionActive : 故障
    OperationEnabled --> FaultReactionActive : 故障(任意状态)
    FaultReactionActive --> Fault : 故障反应完成
    Fault --> SwitchOnDisabled : Fault Reset (bit7 ↑沿)

(* Quick Stop 后能否直接回 Operation Enabled 取决于 0x605A Quick Stop Option Code;故障可从任意状态进入 Fault Reaction Active,图中只画一条。)

2.1 状态判定:状态字掩码表

状态由 0x6041 的低位按掩码判定(先与掩码,再比较值):

状态 掩码 含义
Not Ready to Switch On 0x004F 0x0000 上电初始化中,不响应控制字
Switch On Disabled 0x004F 0x0040 初始化完成,等 Shutdown 命令
Ready to Switch On 0x006F 0x0021 主回路可上电
Switched On 0x006F 0x0023 主回路已上电,未使能
Operation Enabled 0x006F 0x0027 已使能,执行控制环
Quick Stop Active 0x006F 0x0007 快停减速中
Fault Reaction Active 0x004F 0x000F 故障反应(按 0x605E 减速)中
Fault 0x004F 0x0008 故障态,等 fault reset

2.2 状态迁移命令:控制字组合

命令 bit7 fr bit3 eo bit2 qs bit1 ev bit0 so 典型值
Shutdown 0 × 1 1 0 0x0006
Switch On 0 0 1 1 1 0x0007
Enable Operation 0 1 1 1 1 0x000F
Disable Voltage 0 × × 0 × 0x0000
Quick Stop 0 × 0 1 × 0x0002
Disable Operation 0 0 1 1 1 0x0007
Fault Reset ↑沿 × × × × 0x0080

(so=switch on, ev=enable voltage, qs=quick stop 低有效, eo=enable operation, fr=fault reset。)


3. 控制字 0x6040 / 状态字 0x6041 位定义

3.1 控制字 0x6040(UINT16, RxPDO)

名称 说明
0 Switch On 状态机命令位
1 Enable Voltage 状态机命令位
2 Quick Stop 低有效:置 0 触发快停
3 Enable Operation 状态机命令位
4–6 模式相关 PP: bit4 new set-point / bit5 change set immediately / bit6 relative;HM: bit4 启动回零;CSP/CSV/CST 不用
7 Fault Reset 上升沿复位故障
8 Halt 暂停当前运动(按减速度停),模式相关
9 模式相关 如 PP 的 change on set-point
10 保留
11–15 厂商自定义

3.2 状态字 0x6041(UINT16, TxPDO)

名称 说明
0 Ready to Switch On 状态机状态位
1 Switched On 状态机状态位
2 Operation Enabled 状态机状态位
3 Fault 故障
4 Voltage Enabled 主回路电压已建立
5 Quick Stop 低有效:0 表示快停生效中
6 Switch On Disabled 状态机状态位
7 Warning 告警(不改变状态)
8 厂商自定义
9 Remote 控制字受总线控制(=1 才听主站的)
10 Target Reached 到达目标(PP/PV/HM 等;Halt 时表示已停)
11 Internal Limit Active 内部限制生效(软限位/转矩限幅等)
12 模式相关 PP: set-point acknowledge;HM: homing attained;CSP/CSV/CST: drive follows command(驱动器正在跟随目标值,使能后应检查此位)
13 模式相关 HM: homing error;CSP: following error
14–15 厂商自定义

4. 操作模式(0x6060 / 0x6061)

0x6060 Modes of Operation(INT8,写)设定模式;必须读 0x6061 Modes of Operation Display 确认生效(驱动器可能延迟切换或拒绝)。

模式 目标值对象 插补/轨迹在哪算 典型用途
1 PP 轮廓位置 0x607A + 0x6081 轮廓速度等 驱动器(梯形/S 曲线自己生成) 点到点,主站只给终点
2 VL 速度(变频器) 0x6042 驱动器 变频器类
3 PV 轮廓速度 0x60FF 驱动器(按加减速斜坡) 恒速/调速
4 PT 轮廓转矩 0x6071 + 0x6087 转矩斜率 驱动器 张力/压装
6 HM 回零 0x6098 方法等 驱动器 上电找零
7 IP 插补位置 0x60C1 主站给点+驱动器插值 老式周期位置(多被 CSP 取代)
8 CSP 周期同步位置 0x607A 每周期更新 主站(细插补),驱动器仅在周期间做微插值 多轴插补主力
9 CSV 周期同步速度 0x60FF 每周期更新 主站 主站闭位置环
10 CST 周期同步转矩 0x6071 每周期更新 主站 力控/主站闭全环

支持哪些模式查 0x6502 Supported Drive Modes(位图)。

4.1 CSP vs PP:插补分工

PP(轮廓位置) CSP(周期同步位置)
主站每周期发什么 不需要每周期发;一次给目标位置+速度+加减速,bit4 触发 每个总线周期发一个新的 0x607A(轨迹上的采样点)
轨迹生成 驱动器内部轨迹发生器 主站运动规划器(多轴耦合插补只能在这做)
驱动器做什么 全部:轨迹+三环 位置环+速度环+电流环;周期间可做线性/二阶微插值平滑
多轴协调 差(各驱动器独立生成轨迹) 好(主站统一插补 + DC 同步执行)
对总线要求 周期性 + DC(见 03 篇

机器人关节控制几乎都用 CSP(或力控用 CST):ros2_control 的周期性 write/read 模型与 CSP 一一对应。


5. 关键对象速查表

索引 名称 类型 方向 说明
0x603F Error Code U16 T 最近故障码(沿用 CiA 301/402 错误码,如 0x2310 过流、0x3210 过压、0x7305 编码器故障、0x8611 跟随误差过大;厂商可扩展)
0x6040 Controlword U16 R 控制字
0x6041 Statusword U16 T 状态字
0x605A Quick Stop Option Code I16 SDO 快停后行为(减速停后保持/断使能等)
0x605E Fault Reaction Option Code I16 SDO 故障时的减速策略
0x6060 / 0x6061 模式设定 / 显示 I8 R / T 见上表
0x6064 Position Actual Value I32 T 实际位置(用户单位/counts)
0x6065 Following Error Window U32 SDO 跟随误差报警窗口
0x606C Velocity Actual Value I32 T 实际速度
0x6071 Target Torque I16 R 目标转矩(千分之额定转矩)
0x6072 Max Torque U16 SDO 转矩限幅
0x6077 Torque Actual Value I16 T 实际转矩
0x607A Target Position I32 R 目标位置(PP/CSP)
0x607C Home Offset I32 SDO 零点偏置:机械零 = 回零参考点 + offset
0x607D Software Position Limit I32×2 SDO 软限位 min/max(sub1/sub2)
0x6081 Profile Velocity U32 SDO/R PP 轮廓速度
0x6083 / 0x6084 Profile Accel / Decel U32 SDO PP 加/减速度
0x6085 Quick Stop Deceleration U32 SDO 快停减速度
0x6098 Homing Method I8 SDO 回零方法(1–37 标准 + 负值厂商法)
0x6099 Homing Speeds U32×2 SDO sub1 找开关速度 / sub2 找零脉冲速度
0x609A Homing Acceleration U32 SDO 回零加速度
0x60B0/0x60B1/0x60B2 Position/Velocity/Torque Offset R 周期同步模式下的前馈叠加量
0x60C2 Interpolation Time Period SDO 插补周期(须与总线周期一致,如 1 ms)
0x60F4 Following Error Actual Value I32 T 实际跟随误差
0x60FF Target Velocity I32 R 目标速度(PV/CSV)
0x6502 Supported Drive Modes U32 SDO 支持的模式位图

(方向:R = RxPDO 常映射(主→从),T = TxPDO 常映射(从→主),SDO = 通常初始化时用 SDO 配置。多轴一体驱动器第二轴对象在 0x6800+,按 CiA 402 axis offset 0x800 递增。)


6. 典型 PDO 映射示例(CSP 模式)

PDO 映射对象:RxPDO 映射 0x1600、TxPDO 映射 0x1A00;SM 分配对象 0x1C12(SM2/输出)、0x1C13(SM3/输入)。映射条目格式 = 索引(16) | 子索引(8) | 位长(8)

1
2
3
4
5
6
7
8
RxPDO 0x1600(主站 → 驱动器,共 6 字节)
0x6040:00 /16bit 控制字
0x607A:00 /32bit 目标位置

TxPDO 0x1A00(驱动器 → 主站,共 10 字节)
0x6041:00 /16bit 状态字
0x6064:00 /32bit 实际位置
0x60F4:00 /32bit 跟随误差

工程常见扩展:RxPDO 加 0x6060(8bit) 以便在线切模式、0x60B1/0x60B2 速度/转矩前馈、0x6072 转矩限幅;TxPDO 加 0x6061(8bit)、0x606C 实际速度、0x6077 实际转矩、0x603F 错误码、0x60FD 数字输入。修改映射须在 PREOP 状态下通过 SDO 完成(先清 0x1C12/0x1C13 与映射对象的 sub0,写条目,再恢复 sub0)。


7. 使能时序完整示例

前提:从站已进 EtherCAT OP 状态、模式已设为 8(CSP)且 0x6061 读回 8、DC 已收敛。每一步写控制字后轮询状态字直到掩码匹配(每个总线周期查一次,带超时如 500 ms):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
步骤 0: 若 (SW & 0x004F) == 0x0008        // Fault
写 CW = 0x0080 (fault reset, 需上升沿,之后写回 0x0000)
等待 (SW & 0x004F) == 0x0040 // Switch On Disabled

步骤 1: 写 CW = 0x0006 // Shutdown
等待 (SW & 0x006F) == 0x0021 // Ready to Switch On

步骤 2: 写 CW = 0x0007 // Switch On(主回路上电)
等待 (SW & 0x006F) == 0x0023 // Switched On

步骤 3: 【CSP 关键】把 0x607A 预置为当前实际位置 0x6064
(否则使能瞬间目标≠实际 → 电机飞车或立刻报跟随误差)

步骤 4: 写 CW = 0x000F // Enable Operation
等待 (SW & 0x006F) == 0x0027 // Operation Enabled
(可再确认 SW bit12 = 1, drive follows command)

之后: 每周期 CW 保持 0x000F,0x607A 按插补轨迹更新(相邻周期增量 = 目标速度×周期)
下使能: 写 0x0006(Shutdown, 自由停/抱闸按驱动器配置)或 0x0002(Quick Stop, 按 0x6085 减速)

许多主站栈把这段写成状态机每周期跑一步(非阻塞),而不是阻塞等待——CSP 下控制字本身就在周期 PDO 里。


8. 故障处理

  • 状态字 bit3=1 进入 Fault;先经 Fault Reaction Active(按 0x605E 定义减速),停稳后落到 Fault 态。
  • 0x603F Error Code 获取原因;多数驱动器还提供 0x1003(预定义错误历史,CiA 301)和厂商诊断对象;EtherCAT 侧可结合 AL Status Code 与 CoE Emergency 报文(邮箱,含同样的错误码)定位。
  • 复位:排除故障源后控制字 bit7 打一个上升沿0x00800x0000),状态回到 Switch On Disabled,再走一遍使能时序。
  • 注意:有些故障(编码器断线、STO 触发)复位无效,需断电或恢复安全回路;跟随误差类故障复位前务必把 0x607A 重新对齐 0x6064

9. 回零(Homing)要点

  1. 切模式 0x6060 = 6,确认 0x6061 = 6
  2. SDO 配置:0x6098 方法(常用:1/2 负/正限位+Index,17/18 限位开关不找 Index,33/34 仅 Index,35/37 = 当前位置即零点——绝对值编码器系统常用 35/37)、0x6099 两段速度、0x609A 加速度、0x607C 零点偏置;
  3. 在 Operation Enabled 下置控制字 bit4=1 启动回零;
  4. 监视状态字:bit12=1 且 bit10=1 = 回零成功(homing attained + target reached);bit13=1 = 回零出错(未找到开关等);
  5. 完成后把 bit4 清零,切回工作模式(如 CSP),并再次将 0x607A 对齐 0x6064

方法编号 35 与 37 语义相同(”以当前位置为零”),37 是 CiA 402 后期版本为一致性新增的推荐编号;负值方法为厂商自定义。


10. 多轴同步要点(DC + CSP)

  • 总线周期常用 1 ms / 500 µs / 250 µs(高动态场景 125 µs);周期越短跟随误差越小、主站实时性要求越高。0x60C2 插补周期须与之一致。
  • 全部轴配 DC-Synchronous0x1C32:01 = 2),SYNC0 周期 = 总线周期;各轴在同一 SYNC0 沿锁存位置、切换目标值 → 轴间一致性 < 1 µs(见 03 篇)。
  • 主站每周期做一次多轴插补,把同一时刻的各轴目标位置写入各自 RxPDO,一帧带全所有轴(EtherCAT 单帧多从站寻址天然支持)。
  • 主站发帧相位须领先 SYNC0 足够裕量(shift time),否则轴收到旧目标值 → 表现为周期性抖动或 AL 0x001A
  • 检查项:所有轴 0x6061 = 8、状态字 bit12 = 1、跟随误差 0x60F4 稳定且远小于 0x6065 窗口。
  • ros2_control 集成时:read()0x6064/0x606C/0x6077 映射到 state interface,write() 把 command interface 写入 0x607A(或 0x60FF/0x6071),周期任务与 EtherCAT 周期同源。

11. 排障 checklist

现象 可能原因 查什么
控制字写了没反应 状态字 bit9 (remote)=0,或从站没进 OP AL 状态、PDO 是否在刷新
卡在 Switched On 进不了 Enabled STO 未闭合、主回路未上电(bit4=0)、驱动器条件不满足 状态字 bit4、驱动器面板/诊断对象
使能瞬间飞车 / 立即报警 CSP 下 0x607A 未预置为实际位置 使能时序步骤 3
运行中报跟随误差 目标增量超速度极限、周期抖动、增益不足 0x60F4 趋势、主站抖动、0x6065 窗口
切模式不生效 只写了 0x6060 没确认 0x6061;部分驱动器要求非使能态切换 0x6061、查手册约束
Fault 复位不掉 bit7 没有产生上升沿(一直保持 0x80)、故障源仍在 写 0x80 后回 0x00;读 0x603F
多轴轨迹错位 未启 DC、shift 配置不当、某轴仍 SM-Sync 各轴 0x1C32:010x092C 收敛情况
PDO 映射改不了 不在 PREOP、映射对象只读(固定映射的驱动器) ESI/手册确认是否支持动态映射

下一篇:05-主站实现-IgH与SOEM.md

05 主站实现:IgH EtherCAT Master 与 SOEM

05 主站实现:IgH EtherCAT Master 与 SOEM

本文重点:Linux 平台上两个最常用的开源 EtherCAT 主站栈——IgH EtherCAT Master(内核态)与 SOEM(用户态)——的架构、API 使用模式、对比选型,以及运行主站所需的实时环境配置。
面向读者:准备在 PC/工控机上跑 EtherCAT 主站的机器人/工控工程师。

具体 API 签名、版本行为以官方文档为准:IgH(etherlab.orggitlab.com/etherlab.org/ethercat)、SOEM(github.com/OpenEtherCATsociety/SOEM)。


1. 主站栈全景

EtherCAT 主站只需要一块普通以太网卡(MAC 层直接收发原始帧),不需要专用硬件,因此主站的差异全部体现在软件栈上:

主站 类型 运行环境 许可 典型场景
Beckhoff TwinCAT 商用 Windows + 实时扩展 商业许可 事实标准、工程工具链最完整(扫描、ESI 管理、示波)
Acontis EC-Master 商用 Linux/Windows/RTOS/裸机 商业许可 需要商业支持、认证、跨平台的产品
KPA EtherCAT Master 商用 多平台 商业许可 同上
IgH EtherCAT Master(EtherLab) 开源 Linux(内核模块) GPLv2(内核部分)/ LGPL(用户库,以随源码的 COPYING 为准) Linux 实时控制、ros2_control 集成的主流选择
SOEM(Simple Open EtherCAT Master) 开源 用户态 C 库,可移植 GPLv2(带链接例外的历史版本存在,以仓库 LICENSE 为准) 嵌入式、快速原型、对内核无侵入
SOEM 的兄弟项目 SOES 开源 从站协议栈 同 SOEM 自研从站固件

许可细节随版本变动(SOEM 曾从 GPLv2+例外调整过表述),商用闭源集成前务必阅读当前版本的 LICENSE 文件,以官方仓库为准。

选型的第一性问题:

  1. 要不要硬实时 + DC 高精度同步? → 伺服多轴插补(CSP 周期 ≤1ms)基本要求 IgH + PREEMPT_RT(或 Xenomai/RTAI),或商用栈。
  2. 能不能改内核 / 装内核模块? → 不能(如某些嵌入式 Linux、容器环境)则选 SOEM。
  3. 要不要商业支持与责任兜底? → 要则 Acontis/KPA/TwinCAT。

2. IgH EtherCAT Master(EtherLab)

2.1 架构:内核态主站

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
┌────────────────────────────────────────────┐
│ 用户态应用(周期实时任务) │
│ libethercat (ecrt_* API) │
├──────────────┬─────────────────────────────┤
│ /dev/EtherCAT0 字符设备(ioctl / mmap) │
├──────────────┴─────────────────────────────┤
│ ec_master.ko 内核主站模块 │
│ ├─ 状态机(总线扫描、从站配置、EEPROM) │
│ ├─ Domain 管理(过程数据映像) │
│ └─ Datagram 队列 / 帧组装 │
├────────────────────────────────────────────┤
│ 网卡驱动层 │
│ ec_generic.ko(通用,走内核网络栈收发) │
│ ec_igb.ko / ec_e1000e.ko / ec_8139too.ko │
│ (网卡专用 native 驱动,绕过网络栈,低抖动) │
└────────────────────────────────────────────┘

关键点:

  • 主站运行在内核:周期帧的组装、收发在内核上下文完成,配合 native 网卡驱动可以把中断路径做到最短。
  • 网卡驱动两种模式
    • ec_generic:不改网卡驱动,通过内核网络协议栈的 raw 接口收发。部署最简单(任何网卡都能用),但每帧多走一遍网络栈,抖动更大。
    • native 驱动ec_igbec_e1000eec_r8169 等):IgH 为特定网卡型号打补丁的专用驱动,网卡被主站独占、完全绕过 Linux 网络栈。支持的网卡型号和内核版本强绑定,选硬件前先查官方仓库 devices/ 目录和文档。
  • 一块网卡 = 一条总线:被主站接管的网卡不再出现在 ip link 中作普通网卡使用。
  • 配置文件 /etc/ethercat.conf(或发行版对应位置):MASTER0_DEVICE(网卡 MAC 地址)、DEVICE_MODULES(驱动模块名)。

2.2 应用 API:典型生命周期

用户态通过 libethercat(头文件 ecrt.h)与 /dev/EtherCAT0 交互。典型代码骨架(函数名以你所用版本的 ecrt.h 为准):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include <ecrt.h>

// ── 1. 初始化阶段(非实时,只做一次)──────────────────
ec_master_t *master = ecrt_request_master(0); // 取得主站 0

ec_domain_t *domain = ecrt_master_create_domain(master); // 过程数据域

// 每个从站:按 (alias, position, vendor_id, product_code) 定位
ec_slave_config_t *sc = ecrt_master_slave_config(
master, 0, 0, VENDOR_ID, PRODUCT_CODE);

// 配置 PDO 映射(数组描述 SM → PDO → Entry 层级)
ecrt_slave_config_pdos(sc, EC_END, slave_syncs);

// 把 PDO Entry 注册进 domain,拿到其在过程映像中的字节偏移
unsigned int off_target_pos;
ec_pdo_entry_reg_t regs[] = {
{0, 0, VENDOR_ID, PRODUCT_CODE, 0x607A, 0x00, &off_target_pos},
{}
};
ecrt_domain_reg_pdo_entry_list(domain, regs);

// (可选)DC 分布式时钟:ecrt_slave_config_dc(sc, assign_activate,
// sync0_cycle_ns, sync0_shift_ns, sync1_cycle_ns, sync1_shift_ns);

// ── 2. 激活:此后不允许再改配置 ─────────────────────
ecrt_activate(master);
uint8_t *pd = ecrt_domain_data(domain); // 过程映像基地址

// ── 3. 周期实时循环(如 1kHz)───────────────────────
while (running) {
wait_next_cycle(); // clock_nanosleep 绝对时间

ecrt_master_receive(master); // 收上一周期返回的帧
ecrt_domain_process(domain); // 校验 WKC、更新输入映像

int32_t actual = EC_READ_S32(pd + off_actual_pos); // 读输入
EC_WRITE_S32(pd + off_target_pos, target); // 写输出

ecrt_domain_queue(domain); // 输出映像入队
ecrt_master_send(master); // 发出本周期帧
}

// ── 4. 退出 ─────────────────────────────────────────
ecrt_release_master(master);

要点:

阶段 规则
ecrt_request_master 内核模块已加载、从站已上电(主站会自动扫描总线)
ecrt_activate 所有 domain / slave_config / PDO 注册必须完成;激活后配置冻结
周期循环 receive → process → (业务) → queue → send 的顺序固定;先收后发,本周期收到的是上一周期发出的帧
DC 模式 还需周期调用 ecrt_master_application_time() / ecrt_master_sync_reference_clock() / ecrt_master_sync_slave_clocks()(具体组合以版本文档为准)
实时约束 循环内不得 malloc、不得写日志到磁盘、不得调用可能阻塞的系统调用

2.3 ethercat 命令行工具

IgH 附带的 ethercat 工具直接与内核主站交互,是不写一行代码就能验证总线的手段(详细排障用法见 07-组网调试与排障.md):

1
2
3
4
5
6
7
8
9
ethercat slaves            # 列出从站:位置、状态(INIT/PREOP/SAFEOP/OP)、名称
ethercat slaves -v # 详细信息:Vendor/Product、DC 支持、端口拓扑
ethercat pdos # 显示各从站当前的 SM/PDO/Entry 映射
ethercat sdos # 列出对象字典(支持 SDO Info 的从站)
ethercat upload -p 0 0x6041 0x00 # SDO 读(从站0 状态字)
ethercat download -p 0 0x6060 0x00 8 # SDO 写(运行模式=CSP)
ethercat states OP # 请求所有从站迁移到 OP(也可 -p N 指定)
ethercat reg_read -p 0 0x0300 8 # 读 ESC 寄存器(错误计数器)
ethercat xml # 按扫描结果生成从站描述 XML

3. SOEM(Simple Open EtherCAT Master)

3.1 架构:用户态、无内核依赖

SOEM 是一个纯 C 库,通过 raw socket(Linux 上 AF_PACKET,Windows 上 WinPcap/npcap)直接收发 0x88A4 帧:

1
2
3
4
5
6
7
8
9
┌─────────────────────────────┐
│ 应用(可以就是 main 函数) │
│ SOEM 库(静态/动态链接) │
│ ec_send_processdata() ... │
├─────────────────────────────┤
│ raw socket (AF_PACKET) │
├─────────────────────────────┤
│ 普通网卡驱动(不修改) │
└─────────────────────────────┘
  • 不需要内核模块,不需要网卡专用驱动,sudo(或 CAP_NET_RAW)即可运行 → 部署极简,适合嵌入式和交叉编译。
  • 代价:每帧都经过内核网络栈 + 系统调用边界,抖动天花板比 IgH native 驱动高;实时性依赖应用自己做好(RT 调度、绑核)。
  • 单进程内嵌主站:主站生命周期 = 应用进程生命周期,没有”多个应用共享一个主站”的概念。

3.2 典型 API 流程

以广泛流传的 simple_test.c 模式为例(函数名基于经典 ec_ 前缀 API;新版 SOEM 2.x 改为显式 context API,以官方仓库当前文档为准):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include "ethercat.h"

char IOmap[4096];

// 1. 绑定网卡并初始化 raw socket
if (ec_init("eth0") <= 0) { /* 失败:权限/网卡名 */ }

// 2. 扫描总线,枚举从站 → ec_slave[] 数组,从站进入 PRE-OP
if (ec_config_init(FALSE) <= 0) { /* 没找到从站 */ }
printf("%d slaves found\n", ec_slavecount);

// 3. 构建过程映像:读各从站 PDO 配置,把输入/输出映射进 IOmap
ec_config_map(&IOmap);

// 4. 配置分布式时钟(测量传播延迟、写偏移补偿)
ec_configdc();

// 5. 请求 SAFE-OP → OP
ec_slave[0].state = EC_STATE_OPERATIONAL;
ec_send_processdata(); // OP 前需先交换一次有效过程数据
ec_receive_processdata(EC_TIMEOUTRET);
ec_writestate(0);
ec_statecheck(0, EC_STATE_OPERATIONAL, EC_TIMEOUTSTATE);

// 6. 周期循环
while (running) {
wait_next_cycle();
ec_send_processdata();
int wkc = ec_receive_processdata(EC_TIMEOUTRET);
if (wkc < expectedWKC) { /* 记录并进入排障逻辑 */ }

// 直接通过 ec_slave[n].outputs / .inputs 指针读写 IOmap
}

ec_close();

SDO 访问:ec_SDOread() / ec_SDOwrite()(非周期,配置阶段使用)。

3.3 SOEM 的适用位置

  • 嵌入式控制器(RT Linux 板卡、甚至配合移植层跑在 RTOS 上)。
  • 工具类程序:扫描器、EEPROM 烧写器、产测脚本。
  • 从站开发时的对测主站。
  • 轴数少、周期 ≥1ms、对抖动要求不极端的机器。

4. IgH vs SOEM 对比

维度 IgH EtherCAT Master SOEM
运行位置 内核模块 + 用户库 纯用户态库
网卡要求 generic 任意网卡;native 驱动仅支持特定型号(低抖动) 任意网卡(raw socket)
实时性上限 高(native 驱动 + PREEMPT_RT,周期可到 100µs 级) 中(受网络栈/系统调用抖动限制,1ms 级周期为宜)
部署复杂度 高:编译内核模块、匹配内核版本、配置驱动接管网卡 低:编译一个库,给权限即可
DC 分布式时钟 完整支持(参考时钟同步、SYNC0/1 配置 API) 支持(ec_configdc + ec_dcsync0),主站侧时钟伺服需应用自己实现
许可 GPLv2(内核);用户库许可以源码 COPYING 为准 GPLv2(历史上带链接例外,以 LICENSE 为准)
多应用共享主站 支持(内核主站可被多个用户程序 request) 不支持(主站内嵌于单进程)
多主站/多网卡 支持多个 master 实例(EtherCAT0/1…) 每进程可 init 不同网卡,冗余口有 ec_init_redundant
命令行工具 ethercat 工具链完整 自带示例程序(slaveinfo、eepromtool 等)
内核版本耦合 强(补丁/驱动需随内核维护)
典型搭档 ros2_control(ethercat_driver_ros2)、EtherLab、LinuxCNC 嵌入式、SOES 对测、原型

经验法则:多轴伺服 + DC 同步插补 → IgH;快速验证、嵌入式、IO 为主 → SOEM。


5. 实时环境:主站跑得准的前提

EtherCAT 的确定性一半靠总线协议,另一半靠主站按时发帧。普通 Linux 调度下,周期任务可能被随意抢占、迁移 CPU、被中断打断,抖动轻松达到几百微秒甚至毫秒级——这会直接变成 DC 同步误差和从站看门狗超时。

5.1 为什么不能用普通调度

干扰源 后果 对策
CFS 公平调度 周期任务被普通负载挤占 SCHED_FIFO 高优先级
CPU 迁移 cache 冷、唤醒延迟 isolcpus + CPU affinity
无关中断 打断实时任务 IRQ affinity 移走无关中断
内核不可抢占段 毫秒级延迟尖峰 PREEMPT_RT 补丁内核
页缺失/换出 不可预期停顿 `mlockall(MCL_CURRENT
CPU 频率调节/C-state 唤醒延迟 performance governor、限制 idle 深度

5.2 配置 checklist

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 1. PREEMPT_RT 内核(发行版 rt 内核或自编译)
uname -v # 应含 PREEMPT_RT

# 2. 隔离 CPU(GRUB 内核参数,隔离 2、3 号核)
# isolcpus=2,3 nohz_full=2,3 rcu_nocbs=2,3

# 3. 实时任务绑到隔离核 + FIFO 优先级
taskset -c 2 chrt -f 80 ./my_ethercat_app
# 代码内等价:pthread_setaffinity_np + sched_setscheduler(SCHED_FIFO)

# 4. EtherCAT 网卡中断绑到实时核(IgH native 驱动独占网卡时由驱动处理;
# SOEM/generic 模式下手动绑)
grep eth0 /proc/interrupts # 找 IRQ 号
echo 4 > /proc/irq/<IRQ>/smp_affinity # 掩码 0x4 = CPU2

# 5. 关闭该网卡的中断合并与节能(SOEM/generic 模式)
ethtool -C eth0 rx-usecs 0 tx-usecs 0
ethtool -K eth0 gro off gso off tso off

# 6. 锁内存 + 预热栈(代码内)
# mlockall(MCL_CURRENT | MCL_FUTURE);

# 7. 周期定时:绝对时间 clock_nanosleep,避免相位漂移
1
2
3
4
5
6
7
8
9
// 周期任务标准写法:TIMER_ABSTIME 消除累积误差
struct timespec next;
clock_gettime(CLOCK_MONOTONIC, &next);
while (running) {
next.tv_nsec += CYCLE_NS;
while (next.tv_nsec >= 1000000000) { next.tv_nsec -= 1000000000; next.tv_sec++; }
clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &next, NULL);
do_cycle();
}

验证手段(cyclictest、抖动统计)见 07-组网调试与排障.md


6. ESI 与 ENI 文件的角色

文件 全称 谁提供 内容 谁消费
ESI EtherCAT Slave Information(XML) 从站厂商 单个从站的身份(Vendor/Product)、SM 配置、可选 PDO 映射、DC 模式、EEPROM 内容模板 配置工具(TwinCAT 等)、离线组态;从站 EEPROM(SII)里存的是其精简二进制形式
ENI EtherCAT Network Information(XML) 配置工具从 ESI + 拓扑生成 整条总线的配置:从站顺序、初始化命令序列、周期帧结构、过程映像布局、DC 参数 遵循 ETG.2100 的主站(TwinCAT、Acontis 等)直接加载执行

开源栈的差异:

  • IgH 和 SOEM 都不需要 ENI 文件。它们在线扫描总线(读 EEPROM/SII、读对象字典),配置由应用代码(ecrt_slave_config_pdos / ec_config_map)表达——等价于把 ENI 的信息写进了程序。
  • ESI 文件对开源栈仍有用:查从站的 PDO 对象、DC 模式参数(如 AssignActivate 值 0x0300)、EEPROM 烧写恢复。
  • 商用工具链(TwinCAT)走 “导入 ESI → 组态 → 导出 ENI → 主站执行” 的离线流程,可追溯性更好,是量产设备的常见做法。

7. 小结与选型建议

  • ros2_control 多轴伺服:IgH + PREEMPT_RT + native 网卡驱动,是社区验证最充分的组合(见下一篇)。
  • 嵌入式 / IO 采集 / 原型:SOEM,半天可以跑通第一帧。
  • 量产、需认证与支持:评估 Acontis / KPA / TwinCAT。
  • 无论哪个栈,实时环境配置(§5)不可省略——主站软件再好,调度抖动 1ms 一样丢同步。

下一篇06-ROS2与EtherCAT集成.md —— 把 EtherCAT 主站接入 ros2_control 的两条路线。

06 ROS 2 与 EtherCAT 集成

06 ROS 2 与 EtherCAT 集成

本文重点:如何把 EtherCAT 主站接入 ros2_control——使用现成的 ethercat_driver_ros2,或自写 hardware_interface::SystemInterface;周期链路、DC 与控制周期对齐、MoveIt 到伺服 CSP 的完整数据流。
前置阅读:05-主站实现-IgH与SOEM.md
涉及第三方包的接口细节以其官方文档/仓库为准。


1. ros2_control 架构回顾

EtherCAT 在 ROS 2 里的接入点是 ros2_control 的硬件抽象层

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
┌──────────────────────────────────────────────┐
│ Controller Manager(update_rate,如 1000Hz) │
│ ├─ joint_trajectory_controller (JTC) │
│ ├─ joint_state_broadcaster │
│ └─ ... │
│ ↑ state interfaces ↓ command interfaces
├──────────────────────────────────────────────┤
│ Resource Manager │
├──────────────────────────────────────────────┤
│ hardware_interface::SystemInterface(插件) │
│ on_init / on_configure / on_activate │
│ read() ←── 反馈 write() ──→ 指令 │
└──────────────┬───────────────────────────────┘
│ ← EtherCAT 主站(IgH / SOEM)
伺服/IO 从站

每个控制周期 Controller Manager 依次执行:read()(硬件 → state interface)→ 各 controller 的 update()write()(command interface → 硬件)。EtherCAT 集成的本质就是:在 read/write 里完成一次过程数据交换

硬件通过 URDF 的 <ros2_control> 标签声明:

1
2
3
4
5
6
7
8
9
10
11
<ros2_control name="my_arm" type="system">
<hardware>
<plugin>my_pkg/MyEthercatSystem</plugin>
<param name="master_id">0</param>
</hardware>
<joint name="joint1">
<command_interface name="position"/>
<state_interface name="position"/>
<state_interface name="velocity"/>
</joint>
</ros2_control>

2. 路线一:ethercat_driver_ros2(推荐起点)

ICube-Robotics/ethercat_driver_ros2 是社区较成熟的通用方案(细节、支持的 ROS 发行版与 API 以官方仓库文档为准):

特性 说明
主站后端 基于 IgH EtherCAT Master(需先按上一篇部署内核模块)
架构 提供通用 EthercatDriver SystemInterface + ec_module 插件体系(pluginlib)
内置模块 通用 CiA402 驱动器模块、通用 GPIO 模块等;厂商专用从站可自写插件
配置方式 URDF <ros2_control> 标签指定从站插件 + YAML 文件描述 PDO 通道映射,无需写 C++(标准从站的情况下)

2.1 配置模式(示意)

URDF 中每个 joint/gpio 关联一个 ec_module 及其 YAML:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<ros2_control name="ec_system" type="system">
<hardware>
<plugin>ethercat_driver/EthercatDriver</plugin>
<param name="master_id">0</param>
<param name="control_frequency">1000</param>
</hardware>
<joint name="joint1">
<command_interface name="position"/>
<state_interface name="position"/>
<ec_module name="my_servo">
<plugin>ethercat_generic_plugins/EcCiA402Drive</plugin>
<param name="alias">0</param>
<param name="position">0</param>
<param name="slave_config">$(find my_pkg)/config/servo.yaml</param>
</ec_module>
</joint>
</ros2_control>

YAML 把从站的 PDO 通道映射到 state/command interface(示意结构,键名以官方文档为准):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
vendor_id: 0x00000000        # 从站厂商 ID(ethercat slaves -v 可查)
product_id: 0x00000000
assign_activate: 0x0300 # DC SYNC0 使能(来自 ESI)
rpdo: # 主站 → 从站(输出)
- index: 0x1607
channels:
- {index: 0x607A, sub_index: 0, type: int32,
command_interface: position, factor: 1000, offset: 0}
- {index: 0x6040, sub_index: 0, type: uint16} # 控制字由 CiA402 状态机管理
tpdo: # 从站 → 主站(输入)
- index: 0x1A07
channels:
- {index: 0x6064, sub_index: 0, type: int32,
state_interface: position, factor: 0.001}
- {index: 0x6041, sub_index: 0, type: uint16} # 状态字

factor/offset 完成编码器计数 ↔ 弧度的线性换算;CiA402 插件内部处理 Operation Enabled 状态机(控制字/状态字握手),应用只看到 position 接口。

2.2 何时不够用

  • 厂商从站有非标对象/特殊初始化序列(SDO 启动脚本超出配置能力时)→ 写自定义 ec_module 插件;
  • 需要 SOEM 后端、或对主站生命周期有特殊管理需求 → 路线二。

3. 路线二:自写 SystemInterface

直接在 SystemInterface 里调用 IgH ecrt_*(或 SOEM ec_*)API。生命周期回调与 EtherCAT 阶段的对应:

回调 EtherCAT 侧动作 注意
on_init(HardwareInfo) 只解析 URDF <ros2_control> 参数(从站位置、PDO 表、换算系数),分配好所有缓冲 不碰硬件;info_.joints 里拿 param
on_configure ecrt_request_master、创建 domain、slave_config、注册 PDO entry 失败要返回 ERROR 而不是崩溃
on_activate ecrt_activate、取 domain 数据指针、SDO 写运行模式(如 0x6060=8 CSP)、驱动器使能状态机 激活后配置冻结;首个 write 前 command 要初始化为当前反馈值,防止跳变
read() ecrt_master_receive + ecrt_domain_process,从过程映像读 0x6064 等 → state interface
write() command interface → 过程映像 0x607A 等,ecrt_domain_queue + ecrt_master_send
on_deactivate 驱动器下使能、停发目标 从站看门狗会兜底,但主动下使能更干净

3.1 read/write 周期与 update_rate 的关系

  • Controller Manager 的 update_rate(controller_manager.yaml)决定 read/update/write 的调用频率——它就是 EtherCAT 的总线周期,因为每次 write 发一帧。
  • 因此 update_rate 必须等于你规划的 EtherCAT cycle time(如 1000Hz = 1ms),并且 controller_manager 所在线程/进程要按上一篇 §5 做实时化(SCHED_FIFO、绑核、mlockall;ros2_control 支持在具备权限时以实时优先级运行 update 循环,具体机制以其文档为准)。
  • receive 在 read() 开头、send 在 write() 结尾,天然形成 “上周期帧在本周期开头收回” 的流水线,与 IgH 推荐用法一致。

3.2 实时循环纪律

read/write 运行在控制主循环里,等同硬实时代码:

  • 禁止内存分配:所有 vector 在 on_init 里 resize 好;不要用会分配的字符串操作。
  • 禁止常规日志RCLCPP_INFO 涉及锁和分配。需要日志用节流宏(低频)或无锁环形缓冲 + 后台线程落盘;错误状态先记标志位,出循环再报。
  • 禁止阻塞调用:SDO 读写(ecrt_slave_config_create_sdo_request 的异步方式除外)、service、参数读取都不进循环。
  • WKC / domain state 异常时不要立即抛异常终止,先按帧计数容忍与降级策略处理(见下一篇)。

4. 端到端周期链路

以 1ms 周期、CSP 模式伺服为例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
t=0.0ms  controller_manager 周期唤醒
├─ hw.read()
│ ecrt_master_receive / ecrt_domain_process ← 收回上周期帧
│ 过程映像 0x6064(实际位置) → state_interface
├─ JTC.update()
│ 轨迹采样插值 → command_interface(position)
├─ hw.write()
│ command → 过程映像 0x607A(目标位置)
│ ecrt_domain_queue / ecrt_master_send → 帧发出

├─ 帧在总线上"飞行",逐个从站 on-the-fly 读写(数十µs 量级)

t≈DC相位 各伺服 SYNC0 事件(由 DC 同步,全轴同时刻)
│ 伺服锁存 0x607A 作为本周期目标 → 内部位置环执行
│ 同时锁存编码器 → 写入 0x6064 供下周期上送

t=1.0ms 下一周期 read() 收到上述反馈 → 循环

三个”周期”必须对齐:controller_manager update_rate = 主站发帧周期 = 从站 SYNC0 周期


5. DC 与 ros2_control 周期对齐

问题 说明 对策
cycle time 不一致 SYNC0 周期(ecrt_slave_config_dc / YAML assign_activate 配套参数)≠ update_rate 周期,会导致目标丢失或重复执行 三处配置写同一个值,代码里加断言
主站相位漂移 clock_nanosleep(CLOCK_MONOTONIC) 与 DC 参考时钟(从站 0 的本地时钟)是两个时钟源,会缓慢漂移 周期性 ecrt_master_application_time() + ecrt_master_sync_reference_clock(),或实现主站时钟伺服(微调睡眠周期跟踪 DC);IgH 亦支持以参考从站为准调整(版本相关,以官方文档为准)
发帧抖动预算 帧必须在 SYNC0 之前送达从站;抖动过大 → 从站报 DC 同步丢失(AL 0x001A 等) 经验预算:发帧时刻 + 最大抖动 + 总线传输时间 < SYNC0 时刻。用 sync0_shift 把 SYNC0 相位后移(如周期的 30%~50%),给主站抖动留裕量
抖动来源排序 调度延迟 ≫ 网络栈 ≫ 总线传播 cyclictest 先量调度抖动;若 max > 100µs 量级,先解决 OS 配置再怀疑其他

经验值参考(非规范):1ms 周期下,主站唤醒抖动控制在 ±20µs 内、sync0_shift 设为 400µs 左右,是常见的稳定工作点;具体以实测为准。


6. MoveIt / JTC 到 CSP 目标位置的数据流

1
2
3
4
5
6
7
8
9
10
11
12
13
14
MoveIt (move_group)
└─ 规划出 JointTrajectory(离散路点,非实时)
│ FollowJointTrajectory action

joint_trajectory_controller(实时,每周期)
└─ 按当前时间在路点间样条插值 → 期望位置 q_d
│ command_interface: position

SystemInterface.write()
└─ q_d × 换算系数(rad → 编码器计数)→ 0x607A Target Position

EtherCAT 帧 → 伺服 SYNC0 锁存 → 驱动器内部位置环(通常还有前馈)

0x6064 Actual Position → read() → joint_state_broadcaster → /joint_states → MoveIt 监控

要点:

  • CSP 模式下插值责任在主站侧(JTC 每周期给一个新目标),伺服只做位置环跟踪 + 少量内插;轨迹平滑度取决于 JTC 插值 + 周期稳定性。
  • 模式选择:CSP(周期目标位置)最常用;CSV/CST(速度/力矩)对应 velocity/effort command interface;PP(点到点)不适合 JTC 这种周期流式指令。
  • 单位换算与方向(减速比、编码器分辨率、正方向定义)是最常见的”动起来不对”的原因,先在无负载下单轴验证。

7. 实践路线建议(checklist)

按顺序,每步通过再进下一步:

    1. IgH 主站部署完成,ethercat slaves 能看到全部从站且进 PREOP
    1. ethercat upload/download 手动读写对象字典,确认驱动器 CiA402 状态机能走到 Operation Enabled(先不带 ros2_control)
    1. ethercat states OP + 观察 WKC 正常,跑通”裸主站”周期循环(可用 IgH 自带示例)
    1. 接 ros2_control:先只挂 joint_state_broadcaster,验证 read 链路(/joint_states 数值、单位正确)
    1. 单轴 + forward_command_controller 小幅度指令,验证 write 链路与方向/换算
    1. 挂 JTC,ros2 action send_goal 发短轨迹
    1. 最后接 MoveIt;全程用 cyclictest / 主站统计监控抖动

原则:总线问题用总线工具(ethercat 命令、Wireshark)解决,别隔着 ros2_control 猜。排障手段见下一篇。


8. 与本工作区 armRobot 项目的关系

/home/cp/work2/ros2Learn/app/armRobot 是一个 6 轴机械臂 ROS 2 栈(Humble + MoveIt 2 + ros2_control)。就 EtherCAT 而言,其 README 的现状是:

  • arm_hw 提供 mock / UART 两种后端;真机 launch 的 hardware 参数虽然接受 ethercat 取值,但会明确拒绝启动——“当前没有 EtherCAT master、PDO 映射或真实 EtherCAT backend”,即 EtherCAT transport 只是预留的边界,没有实现。
  • 现有真机链路走 UART 帧协议(arm_protocol → MCU 固件),伺服控制在 MCU 侧。

若将来真实接入 EtherCAT 伺服,本篇的两条路线都适用:

路线 做法 对 armRobot 的改动
ethercat_driver_ros2 URDF 里把 arm_hw 插件换成 ethercat_driver/EthercatDriver,每轴配 CiA402 YAML 主要是 description/bringup 配置层;arm_safetycommand_allowed 门逻辑需要在新硬件层重新落点
自写 SystemInterface arm_hw 内实现 EtherCAT 后端(IgH API),复用其现有接口边界 代码量大,但能保留现有 mock/uart/ethercat 统一的插件选择结构与安全联锁

注意 armRobot 强调的安全边界仍然成立:EtherCAT 化不改变”外部硬接线 STO 切断能量、ROS 安全链不能替代 STO”的原则。


下一篇07-组网调试与排障.md —— 布线、上电检查、WKC/AL Status/Wireshark 排障与性能验证。

07 组网、调试与排障

07 组网、调试与排障

本文重点:EtherCAT 网络的硬件布线规范、上电检查流程、WKC / AL Status / 错误计数器 / Wireshark 四大诊断手段、典型故障案例、看门狗机制与性能验证方法。
前置阅读:05-主站实现-IgH与SOEM.md06-ROS2与EtherCAT集成.md
寄存器地址与 AL Status Code 以 ETG.1000 规范及所用 ESC(如 Beckhoff ET1100/LAN9252)数据手册为准。


1. 硬件与布线

项目 规范/建议 说明
线缆 Cat5e 及以上,屏蔽(SF/UTP 或更好) 100BASE-TX,两对线即可,但工业环境务必用屏蔽线并良好接地
单段长度 ≤100m(100BASE-TX) 超长距离用光纤从站端口
拓扑 物理上线形/星形/树形均可,逻辑恒为环 帧按端口顺序遍历,末端从站开放端口自动闭环(loopback)
IN/OUT 方向 不能接反 从站端口有方向:上一站接本站 IN(Port 0),本站 OUT(Port 1)接下一站 IN。接反轻则拓扑错乱、寻址异常,重则部分从站不可见。布线后对照 ethercat slaves 顺序核对
EBUS 背板 模块化 IO(如 Beckhoff EL 系列)内部用 E-bus(LVDS 背板总线)级联 耦合器(如 EK1100)做 100BASE-TX ↔ E-bus 转换;注意 E-bus 供电电流预算,模块多时需供电扩展模块
接地/EMI 屏蔽层 360° 端接、远离动力线、变频器输出线用屏蔽电缆 EMI 是 RX Error 计数器增长的头号来源
连接器 RJ45 或 M8/M12(D-code) 振动场合优先 M8/M12

2. 上电检查流程

按顺序逐级确认,任何一级失败先解决再往下:

1
2
3
4
5
6
7
8
9
10
11
① 物理层:每段链路两端 Link/Activity LED 亮

② 主站扫描:ethercat slaves 数量 == 实际从站数

③ 状态机:全部从站 INIT → PREOP(主站自动)

④ 配置:SDO 可读写(ethercat upload),PDO 映射下发成功

⑤ SAFEOP:输入数据有效(只读不写输出)

⑥ OP:过程数据双向交换,WKC == 期望值稳定
1
2
3
4
ethercat master            # 主站状态、link up、发送/接收帧统计
ethercat slaves # 期望输出形如:0 0:0 OP + EK1100 ...
ethercat slaves -v # 每站 DC 能力、端口连接关系(拓扑核对)
ethercat states -p 3 OP # 手动请求某从站迁移状态

从站数不对时的二分定位:从主站侧开始逐段插拔,ethercat slaves 观察数量变化,坏点在”最后一个可见从站”的下游第一段(线缆或下一站 IN 口)。


3. 诊断手段

3.1 WKC(Working Counter)异常分析

每个 datagram 尾部的 WKC 由成功处理该 datagram 的从站硬件递增(LRW 访问:读成功 +1、写成功 +2;只读/只写各 +1,见 ETG.1000.4)。主站把实收 WKC 与期望值比较:

现象 含义 定位方法
WKC == 期望 全部从站正常处理
WKC 恒定偏少 某从站未处理(掉出 OP、地址不匹配、配置错) ethercat slaves 看谁不在 OP;差值 = 缺席从站贡献值
WKC = 0 帧根本没经过任何配置从站 查链路、主站网卡、从站顺序变化
WKC 偶发抖动(偶尔少 1~2) 传输误码使某站 CRC 判帧无效而不处理 查错误计数器(§3.3)定位坏线段/EMI

IgH 中 ecrt_domain_state() 返回 working_counterwc_state(ZERO/INCOMPLETE/COMPLETE);SOEM 中 ec_receive_processdata() 直接返回 WKC,与 expectedWKC = ec_group[0].outputsWKC * 2 + ec_group[0].inputsWKC 比较(SOEM 示例中的惯用算法)。

处理原则:周期循环里 WKC 异常先计数、超过阈值(如连续 N 周期)再降级/停机,单次抖动不触发急停。

3.2 AL Status Code 速查

从站拒绝状态迁移或自行退级时,会在 AL Status Code 寄存器(0x0134)给出原因:

1
ethercat slaves -v         # 输出中含 AL state 与 status code
Code 含义 常见原因
0x0000 无错误
0x0011 Invalid requested state change 跳级请求(如 INIT→OP)
0x0016 Invalid mailbox configuration (PREOP) SM0/SM1 配置与 EEPROM 不符
0x001A Synchronization error DC 同步丢失:主站发帧抖动过大 / 周期不一致
0x001B Sync manager watchdog SM 看门狗超时:主站停发/漏发过程数据
0x001D Invalid output configuration SM2(输出)配置错,PDO 映射与实际长度不符
0x001E Invalid input configuration SM3(输入)配置错
0x0029 Freerun needs 3-buffer mode 同步模式配置矛盾
0x002B Invalid input & output 双向 PDO 配置均错
0x002C Fatal sync error SYNC0 事件长期缺失
0x002D No sync error(SAFEOP 时未收到 SYNC) DC 未启用/AssignActivate 错
0x0030 Invalid DC SYNC configuration SYNC0/1 周期、偏移非法
0x0032 PLL error 从站 DC PLL 未锁定
0x0034 Invalid DC cycle time 周期与从站支持范围不符
0x0050 EEPROM no access EEPROM 被占用/损坏
0x0051 EEPROM error SII 内容校验失败

(完整表见 ETG.1000.6 / ESC 数据手册;厂商可扩展 0x8000 以上私有码,查从站手册。)

3.3 错误计数器寄存器(定位坏线缆/EMI)

ESC 每端口维护硬件错误计数器,只增不减,写任意值清零。核心地址(ET1100 类 ESC,以数据手册为准):

寄存器 名称 含义
0x0300+2n RX Error Counter (Port n) 物理层符号错误 + 帧错误,上游线段质量的直接指标
0x0301+2n Invalid Frame Counter (Port n) CRC 错帧
0x0308+n Forwarded RX Error Counter (Port n) 上游已标记错误的帧被转发的次数(错误发生在更上游)
0x030C ECAT Processing Unit Error Counter 到达处理单元的错帧
0x0310+n Link Lost Counter (Port n) 链路丢失次数:接触不良、连接器松动的指标
1
2
3
4
5
6
# 读从站 3 的端口错误计数器(8 字节:Port0~3 的 RX Err/Invalid Frame)
ethercat reg_read -p 3 0x0300 8
# 读 Lost Link 计数器
ethercat reg_read -p 3 0x0310 4
# 清零后跑一段时间再读,观察增长
ethercat reg_write -p 3 0x0300 -t uint32 0

定位逻辑:错误在链路上被第一个检测到它的端口计数并标记。沿帧行进方向找第一个 RX Error 增长的端口,坏点就在该端口的上游线段(线缆、连接器、或上游从站 PHY)。全网清零 → 运行 30 分钟 → 逐站读取,是例行体检做法。

3.4 Wireshark 抓包(EtherType 0x88A4)

Wireshark 自带 EtherCAT dissector,能解析 datagram 头、命令类型、地址、WKC:

1
2
3
4
5
6
7
8
9
10
11
# 方式一:SOEM/generic 驱动时直接抓主站网卡
sudo tshark -i eth0 -f "ether proto 0x88a4" -w ecat.pcapng

# 方式二:IgH native 驱动独占网卡、无法本机抓包时,
# 在主站与首从站之间插 TAP 或镜像口交换机

# 常用显示过滤器
ecat # 所有 EtherCAT datagram
ecat.cmd == 12 # LRW(逻辑读写,过程数据)
ecat.cnt < 6 # WKC 小于期望值的帧(数值按你的期望改)
ecat_mailbox.coe # CoE 邮箱(SDO 交互)

看什么:

  • 每帧内的 datagram 列表(主站一个周期通常发 1 帧含多个 datagram:LRW 过程数据 + BRD 状态巡检 + DC 同步 ARMW/FRMW);
  • 每个 datagram 的 WKC 返回值
  • 周期帧的时间间隔抖动(Statistics → I/O Graph);
  • SDO 交互失败时看 CoE Abort Code。

3.5 IgH ethercat 命令排障示例

1
2
3
4
5
6
7
8
9
10
ethercat master                       # 帧统计:tx/rx count、loss
ethercat slaves -v # 状态 + AL status code + 端口拓扑 + DC
ethercat reg_read -p 0 0x0130 2 # AL Status 寄存器
ethercat reg_read -p 0 0x0134 2 # AL Status Code
ethercat reg_read -p 0 0x092C 4 # DC 系统时间差(同步质量,带符号)
ethercat pdos -p 2 # 核对 PDO 映射是否与预期一致
ethercat sii_read -p 1 > sii.bin # 备份 EEPROM(SII)
ethercat sii_write -p 1 sii.bin # 恢复/烧写 EEPROM(谨慎!)
ethercat eoe # EoE 状态(若使用)
ethercat domains -v # domain 的 WKC 期望值与实际

子命令名称与参数随 IgH 版本略有差异(如 eeprom 相关操作在不同版本叫 sii_read/sii_write),以 ethercat --help 和官方文档为准。


4. 典型故障案例表

症状 高概率原因 排查动作
从站周期性掉到 SAFEOP,AL code 0x001A(Sync error) 主站发帧抖动过大 / DC 参考时钟漂移 / cycle time 三处不一致 cyclictest 量调度抖动;核对 update_rate、主站周期、SYNC0 周期一致;加大 sync0_shift;检查是否忘了周期调用 DC 同步 API
WKC 抖动(偶发少 1) 线缆/连接器误码、EMI §3.3 错误计数器沿线定位;换屏蔽线、检查接地、远离动力线
进 OP 后驱动器无输出/不动 PDO 映射与驱动器实际配置不符;控制字状态机没走到 Operation Enabled;看门狗参数不当 ethercat pdos 核对映射;ethercat upload -p N 0x6041 0 看状态字;读 0x0400(SM 看门狗分频/时间寄存器区)核对看门狗配置与触发状态(0x0440)
扫描到的从站数少于实际 IN/OUT 接反、某段链路断、从站没上电 顺链路查 LED;二分插拔;ethercat slaves -v 看末站端口状态
从站全部消失又恢复(间歇) 供电跌落、连接器振动松动 读 0x0310 Lost Link 计数器逐站排查
EEPROM 损坏(从站身份异常、0x0050/0x0051) 烧写中断、干扰 用备份 sii_write 恢复;无备份则从厂商 ESI 重新生成 SII 烧写
SDO 读写超时 从站还在 INIT / mailbox 配置错 确认至少 PREOP;ethercat slaves -v 查 mailbox 支持
换了一个从站后总线起不来 位置寻址下配置与实际型号不匹配 核对 vendor/product;用 alias 站号替代纯位置寻址提高可维护性

5. 从站看门狗机制

从站 ESC 内置 SM 看门狗(Sync Manager Watchdog):每次输出 SM(通常 SM2)被主站过程数据写一次就”喂狗”;主站停发或漏发超过看门狗时间,从站硬件把输出置为安全态(数字量输出清零、伺服按配置停机),并报 AL 0x001B 退到 SAFEOP。

  • 相关寄存器(ESC 通用布局,以数据手册为准):0x0400(看门狗分频器)、0x0420(SM 看门狗时间)、0x0440(看门狗状态)、0x0442(看门狗计数器)。默认典型值 100ms。
  • 工程意义:这是”主站崩溃/断线后设备不会带着旧指令继续跑”的最后防线。看门狗时间应设为周期的合理倍数(如 10~100 倍):太短则正常抖动误触发,太长则失去保护意义。
  • 看门狗只保证输出进安全态,不等于功能安全——人身安全仍依赖 STO 等硬接线安全回路(呼应 armRobot 的安全边界原则)。

6. 性能验证

6.1 调度抖动:cyclictest

1
2
3
# 在目标隔离核上以目标周期测 1 小时
sudo cyclictest -m -p 90 -t 1 -a 2 -i 1000 -D 3600 -h 400 -q
# -i 1000 = 1ms 周期; -a 2 = 绑 CPU2; -h 直方图

判读(经验值):1ms 控制周期下,PREEMPT_RT 调优后 max latency 通常应在几十 µs 以内;max 上百 µs 需先修 OS 配置(BIOS 节能、SMI、中断绑核)再谈总线。必须带典型负载测(跑着 ROS 栈、有网络流量),空载数据没有意义。

6.2 周期抖动统计(应用内)

在周期循环里用 clock_gettime 记录相邻唤醒间隔,维护 min/max/直方图,定期(低频、循环外)输出。IgH 用户也可参考其示例中的 latency/jitter 统计写法。同时监控 ethercat master 的帧丢失计数与 DC 系统时间差(0x092C)随时间的分布。

6.3 总线利用率估算

一帧的线上时间:

1
2
3
4
5
6
帧长(字节) ≈ 各从站 PDO 数据之和
+ 每个 datagram 头 12B(10B 头 + 2B WKC)
+ EtherCAT 帧头 2B + 以太网头 14B + FCS 4B
(不足 60B 补 pad)+ 前导码/IFG 折合 20B

100 Mbps 下 1 字节 ≈ 80 ns

算例:10 个伺服,每站 RxPDO 8B + TxPDO 8B,单个 LRW datagram 承载:

1
2
3
数据 160B + datagram 头 12B + 帧/以太网开销 40B ≈ 212B
线上时间 ≈ 212 × 80ns ≈ 17µs
1ms 周期 → 总线利用率 ≈ 1.7%(再加 BRD 巡检、DC datagram,仍 <3%)

结论:中小系统总线本身远不是瓶颈,瓶颈几乎总在主站调度抖动。当从站很多、周期很短(如 250µs + 上百从站)时才需要精算帧长,必要时拆分多个周期不同的 domain/帧。

6.4 长跑验收

  • 24h 连续运行:WKC 异常计数 = 0(或低于允许阈值)
  • 全站错误计数器增量为 0(EMI 验收)
  • DC 同步偏差(0x092C)分布稳定,无漂移趋势
  • cyclictest max 满足预算,无孤立尖峰

7. 排障 Checklist(速查)

上电起不来:

  • 链路 LED 全亮?(不亮:线/电/PHY)
  • ethercat slaves 数量正确?(少站:IN/OUT 方向、二分定位断点)
  • 全部到 PREOP?(卡 INIT:EEPROM/SII 问题,查 0x0050/51)
  • SDO 能读 0x1000 设备类型?(不能:mailbox 配置)
  • SAFEOP → OP 被拒?(读 AL Status Code 对表 §3.2)

运行中异常:

  • WKC 恒定偏少 → 哪个从站不在 OP?
  • WKC 偶发抖动 → 错误计数器沿线定位(0x0300/0x0310)
  • 掉 SAFEOP + 0x001A → 主站抖动/周期一致性(cyclictest、三周期核对)
  • 掉 SAFEOP + 0x001B → 主站是否停发/漏发(看门狗)
  • 输出没动作 → PDO 映射、CiA402 状态字、看门狗状态 0x0440

环境与工具:

  • PREEMPT_RT + 绑核 + 中断亲和 已配置(见 05 §5
  • 抓包通道可用(镜像口/TAP),Wireshark 过滤 ecat
  • EEPROM 已备份(sii_read),ESI 文件存档
  • 从站固件/硬件版本、拓扑顺序有记录(换件后核对)

返回目录README.md

MQTT 总览与工作原理

MQTT 总览与工作原理

MQTT:轻量级发布/订阅消息传输协议
跑在 TCP/IP(或其它有序、无损、双向连接)之上
设计目标:小代码体积、低带宽、弱网友好(IoT / M2M)

权威摘要来自 OASIS MQTT 5.0 规范引言:
https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html


1. MQTT 是什么

MQTT 是 Client–Server 架构下的 发布/订阅(Pub/Sub) 协议:

  • Client:连接 Broker,可 Publish、可 Subscribe
  • Server(Broker):接收发布、按订阅关系转发
属性
标准组织 OASIS MQTT TC
现行标准 MQTT 5.0(OASIS Standard, 2019-03-07)
广泛兼容版 MQTT 3.1.1(亦为 ISO/IEC 20922)
历史版 MQTT 3.1
传输 通常 TCP;也可 WebSocket;MQTT-SN 面向非 TCP
默认端口 1883(明文)/ 8883(TLS)
载荷 对内容无关(JSON/Protobuf/二进制均可)

规范列表:mqtt.org — Specifications


2. 核心交互模型

1
2
3
4
5
6
7
8
Publisher                Broker                 Subscriber
│ │ │
│──── CONNECT ────────▶│◀────── CONNECT ────────│
│◀─── CONNACK ─────────│─────── CONNACK ───────▶│
│ │◀────── SUBSCRIBE ──────│
│ │─────── SUBACK ────────▶│
│──── PUBLISH ────────▶│───── PUBLISH ─────────▶│
│ │ │

特点:

  • 发布者不需要知道订阅者是谁(解耦)
  • 一对多自然支持(同主题多订阅)
  • 应用只关心 Topic + Payload + QoS

3. 为什么用 MQTT(而不是裸 TCP / HTTP)

需求 MQTT 如何满足
设备多、连接弱 固定头部很小;Keep Alive / 会话恢复
一对多分发 Broker 按 Topic 扇出
投递可靠性可选 QoS 0/1/2
异常下线通知 Last Will and Testament
后上线也能拿到状态 Retain 消息
跨语言 各语言客户端库丰富

不适合的场景(需另选):

  • 硬实时微秒级控制环(更像 EtherCAT / 实时 DDS)
  • 需要强类型接口契约与发现(ROS 2 DDS / IDL 更合适)
  • 超大文件传输(用对象存储 + 通知更合适)

4. 与常见中间件对比(机器人语境)

MQTT ROS 2 DDS HTTP/REST
模型 Pub/Sub + Broker Pub/Sub(去中心或 discovery) 请求/响应
中心节点 通常需要 Broker 通常无单一 Broker 服务端
典型用途 云边、设备遥测、跨网段 机器人内部实时通信 配置/API
QoS 0/1/2 DDS QoS(更丰富) 应用层自建
发现 约定 Topic DDS Discovery URL

工程上常见分层:

1
2
3
机器人内部:ROS 2 / DDS / 实时总线
↕ 桥接(mqtt_client 等)
厂区/云端:MQTT Broker

5. 版本怎么选

版本 建议
3.1.1 兼容面最大;存量设备首选
5.0 新项目优先:Reason Code、用户属性、共享订阅、主题别名、更好的错误报告与流控
3.1 仅遗留

MQTT 5 细节见 04-MQTT5新特性.md


6. 最小心智模型

  1. Client 连上 Broker(CONNECT)
  2. 订阅感兴趣的 Topic(SUBSCRIBE)
  3. 向某 Topic 发消息(PUBLISH)
  4. Broker 转发给匹配的订阅者
  5. 用 QoS / Retain / Will 补齐可靠性与运维语义

下一篇:01-主题与发布订阅模型.md

主题与发布订阅模型

主题与发布订阅模型

Topic 是 MQTT 的路由键:字符串、层级、可通配
再叠加 Retain / Will / Shared Subscription,构成完整应用语义


1. Topic 基本规则

  • 使用 / 分层,例如:factory/line1/robot3/joint_states
  • 区分大小写
  • 不要以 $ 开头做普通业务主题($ 常留给系统主题,如 $SYS/$share/
  • 层级为空一般应避免(如 a//b),具体以实现为准,规范有约束

1.1 设计建议(工程)

1
2
3
4
5
{域}/{设备或服务}/{资源}/{类型}
例:
siteA/agv07/battery/state
siteA/agv07/cmd/velocity
siteA/warehouse/door3/event

原则:

  • 读路径即可理解含义
  • 发布“状态”与“命令”分开
  • 避免把巨大动态集合塞进单一巨型主题

2. 通配符(仅订阅可用)

通配符 含义 示例
+ 单层通配 siteA/+/battery/state
# 多层通配(必须是末尾) siteA/agv07/#

注意:

  • 发布时 Topic 不能含通配符
  • # 匹配多层,包括 0 层(视实现/规范细节,订阅设计时要小心过宽)
  • 过宽订阅(如 #)会压垮客户端与网络

3. 发布 / 订阅解耦

1
Publisher ──PUBLISH──▶ Broker ──匹配订阅──▶ Subscriber(s)

同一 Client 可同时:

  • 订阅若干 Topic
  • 向另一些 Topic 发布

Broker 不解析 Payload(对内容不可知),应用层自行约定 JSON / Protobuf / CDR 等。


4. 保留消息(Retain)

PUBLISH 设置 Retain=1 时:

  • Broker 为该 Topic 保存最后一条保留消息
  • 新订阅者一订阅匹配主题,立即收到这条保留消息(若存在)

用途:

  • 设备当前状态(在线/模式/配置摘要)
  • “后来的订阅者也能立刻知道现状”

清理:向同 Topic 发 空载荷 + Retain=1(常见做法)删除保留消息。


5. 遗嘱(Last Will and Testament, LWT)

在 CONNECT 时声明 Will Topic / Will Payload / Will QoS / Will Retain:

  • Client 异常断线(网络中断、Keep Alive 超时等)时,Broker 代为发布 Will
  • 正常 DISCONNECT 通常触发 Will(MQTT 5 可用 Will Delay 等更精细控制)

典型用法:

1
2
3
Will Topic:   devices/agv07/status
Will Payload: {"online":false}
Will Retain: true

配合上线时 Publish {"online":true} Retain,形成在线状态机。


6. 共享订阅(Shared Subscription,MQTT 5)

形式:

1
2
$share/{ShareName}/{TopicFilter}
例:$share/workers/jobs/new

同一 ShareName 组内,匹配消息只投递给组内一个订阅者 → 负载均衡消费队列语义。

对比:

普通订阅 共享订阅
同组多消费者 每人一份拷贝 组内一份、轮询/均衡
典型场景 状态广播 任务分发、日志处理

7. 系统主题(实现相关)

许多 Broker 提供 $SYS/# 观察连接数、流量、版本等。
非协议强制统一,以各 Broker 文档为准(Mosquitto / EMQX 各有约定)。


8. Topic 设计 Checklist

  • 层级稳定、命名可读
  • 命令 Topic 与状态 Topic 分离
  • 订阅尽量具体,避免无脑 #
  • 关键状态使用 Retain + LWT
  • 多消费者抢任务用 Shared Subscription(MQTT 5)
  • 敏感 Topic 配合 ACL(见安全篇)

下一篇:02-控制报文与会话.md

控制报文与会话

控制报文与会话

MQTT 在已建立的网络连接上交换 Control Packet
第一个包必须是 CONNECT;会话与 Keep Alive 决定“断线后还记不记得你”

依据:OASIS MQTT 5.0 / 3.1.1 控制报文章节。

**官方字节级格式(Fixed Header / CONNECT / PUBLISH / SUBSCRIBE…)**见:
10-MQTT控制报文格式总览.md 起整组文档(对齐 OASIS §2 / §3)。


1. 控制报文一览

报文 方向 作用
CONNECT C→S 连接请求(协议级登录)
CONNACK S→C 连接确认
PUBLISH 双向 发布消息
PUBACK 双向 QoS1 确认
PUBREC / PUBREL / PUBCOMP 双向 QoS2 握手
SUBSCRIBE C→S 订阅
SUBACK S→C 订阅确认
UNSUBSCRIBE C→S 取消订阅
UNSUBACK S→C 取消确认
PINGREQ / PINGRESP C↔S Keep Alive 心跳
DISCONNECT 双向(5.0 更完整) 断开
AUTH 双向(仅 5.0 增强认证交换

固定头很小:报文类型 + flags + Remaining Length。


2. CONNECT 关键字段(概念)

字段 含义
Protocol Name / Level MQTT / 版本(3.1.1 或 5.0)
Client Identifier 客户端 ID(会话主键之一)
Clean Session / Clean Start 是否清理旧会话
Keep Alive 秒;最大空闲间隔相关
Username / Password 可选认证
Will* 遗嘱相关标志与载荷
Properties(5.0) Session Expiry、Receive Maximum 等

规范要求:网络连接建立后,客户端发送的第一包必须是 CONNECT


3. 会话(Session)

会话状态可能包括:

  • 订阅集合
  • 未完成的 QoS1/2 报文流
  • (实现相关)排队消息

3.1 Clean Session(3.1.1) vs Clean Start + Session Expiry(5.0)

版本 控制方式
3.1.1 Clean Session=1 不保留;=0 尽量恢复
5.0 Clean Start + Session Expiry Interval 更精细

断线重连时:

  • 若会话仍在:可恢复订阅与未完成流(少丢、少重订)
  • 若已清理:需重新 SUBSCRIBE

4. Keep Alive

Client 声明 Keep Alive(秒)。若在约 1.5 × Keep Alive 内无控制报文往来,Broker 可断开连接,并可能触发 Will。

Client 侧用 PINGREQ 保活;Server 回 PINGRESP

经验:

  • 移动网络:不要设过短导致假掉线,也不要过长导致故障发现慢
  • 常见起点:30~60s,按现场调

5. 订阅流

1
2
Client: SUBSCRIBE (Packet ID, Topic Filter, Requested QoS, ...)
Broker: SUBACK (Packet ID, Reason Codes / Granted QoS)

Broker 可降级 QoS(Granted ≤ Requested)。
应用必须看 SUBACK,不要假设一定按请求 QoS 成交。


6. 发布流(按 QoS)

详见 03-QoS与消息投递.md。概要:

  • QoS0:PUBLISH 即止
  • QoS1:PUBLISH → PUBACK
  • QoS2:PUBLISH → PUBREC → PUBREL → PUBCOMP

7. 正常断开 vs 异常断开

情况 典型行为
发 DISCONNECT 后关连接 正常下线;通常不触发 Will
TCP 重置 / 超时 / Keep Alive 失败 异常;可能触发 Will、会话按策略保留或过期

MQTT 5 的 DISCONNECT 可带 Reason Code,便于诊断“为什么踢人”。


8. 多连接与 Client ID

  • 同一 Client ID 重复连接:Broker 通常踢掉旧连接(行为以实现/设置为准)
  • 千万不要多设备共用一个 Client ID(会互相踢)
  • 动态设备可用随机 ID + Clean Start,或稳定 ID + 会话恢复策略

下一篇:03-QoS与消息投递.md