IgH EtherCAT Master 批量更新从站 SII EEPROM:从 ESI XML 生成到整机自动化升级

在 EtherCAT 从站开发过程中,经常会遇到这样一个场景:

从站固件已经更新了,PDO、设备名称、版本信息或者其他 EtherCAT 配置也发生了变化,因此需要同步更新从站 EEPROM 中的 SII 数据。

单个从站放在桌面上调试时,这件事情并不麻烦,可以使用 TwinCAT 或 SSC Tool 完成 EEPROM 更新。

但到了整机阶段,情况就完全不同了。

例如一台机器人内部已经串联了二三十个 EtherCAT 从站,如果每次升级 SII 都需要拆机、重新接入 TwinCAT,再一个一个更新,维护成本会非常高。

这次项目中遇到的实际问题就是:

整机已经使用 IgH EtherCAT Master 运行,但缺少一套从 Linux 主站侧批量更新所有关节模组 SII EEPROM 的机制。

后来发现,IgH 本身已经提供了 sii_readsii_write 接口。

最终整个升级链路整理成了:

修改 EtherCAT 从站配置
        ↓
生成新的 ESI XML
        ↓
ESI XML 转换为 SII Binary
        ↓
上传到 Linux 主站
        ↓
IgH ethercat sii_write
        ↓
Shell 脚本遍历整机节点
        ↓
批量更新 SII EEPROM
        ↓
重启 EtherCAT
        ↓
重新扫描并验证

本文把整个实现过程记录下来,同时把 ESI XML、SII EEPROM、二进制镜像三者之间的关系梳理清楚。


1. 先把几个概念说清楚:ESI XML、SII 和 EEPROM Binary

项目内部经常会说:

“给从站更新一下 XML。”

这个说法沟通起来比较方便,但严格来说并不准确。

EtherCAT 从站开发中,经常会遇到下面三种东西:

ESI XML
SII
EEPROM Binary

它们关系很密切,但并不是同一个概念。


1.1 ESI XML

ESI,全称:

EtherCAT Slave Information

它本质上是一份 XML 格式的设备描述文件。

其中可以描述:

  • Vendor ID;
  • Product Code;
  • Revision Number;
  • Device Name;
  • Mailbox;
  • CoE / FoE;
  • SyncManager;
  • FMMU;
  • RxPDO / TxPDO;
  • Distributed Clocks;
  • 对象字典;
  • 字符串信息;
  • EEPROM 配置等。

主站工程工具,例如 TwinCAT,会读取 ESI XML,以了解这个 EtherCAT 从站具有什么能力、PDO 如何组织、支持哪些同步方式。


1.2 SII

SII 的全称同样是:

Slave Information Interface

它表示存储在 EtherCAT 从站非易失性存储器中的设备信息。

从站上电以后,ESC 会从 EEPROM 中读取其中的一部分数据,用于完成自身初始化。

典型信息包括:

ESC Configuration
Vendor ID
Product Code
Revision Number
Serial Number
Mailbox
SyncManager
FMMU
PDO
DC
Strings

Beckhoff 官方资料也明确说明,EtherCAT 从站的 ESI EEPROM 用于保存 ESC 配置和设备描述信息,并且最前面的若干 Word 包含 ESC Configuration,之后是 Vendor ID、Product Code、Revision 等信息。


1.3 EEPROM Binary

SII 最终在 EEPROM 中并不是以 XML 文本形式存储的。

例如 XML 中可能写:

<Type ProductCode="#x12345678"
      RevisionNo="#x00010001">
    JointDrive
</Type>

真正写进 EEPROM 时,会被转换成按照 EtherCAT SII 格式组织的二进制数据。

因此可以把三者关系理解为:

              设备描述
                 │
                 ▼
            ESI XML
                 │
         SSC Tool / 配置工具
                 │
                 ▼
        SII EEPROM Image
          *.bin 二进制文件
                 │
                 ▼
            sii_write
                 │
                 ▼
        EtherCAT EEPROM

所以在本文后面的实际升级过程中,IgH 写入的不是:

xxx.xml

而是:

xxx.bin

SII EEPROM 二进制镜像


2. 为什么要从 IgH 主站侧做 SII 更新?

在项目初期,一般使用 TwinCAT 调试 EtherCAT 从站。

这种情况下升级 EEPROM 很方便。

但是整机阶段,EtherCAT 主站已经变成:

RK3588
   +
Linux
   +
IgH EtherCAT Master

整机内部又挂了大量关节从站。

如果仍然依赖 TwinCAT,就会出现这样的维护流程:

需要更新关节 EtherCAT 配置
        ↓
拆机
        ↓
单独接出 EtherCAT 从站
        ↓
Windows + TwinCAT
        ↓
写 EEPROM
        ↓
重新装回

一次还可以接受。

如果二十多个甚至三十多个节点全部需要更新,就非常低效。

因此更合理的方式应该是:

机器人保持原有 EtherCAT 拓扑
        ↓
把新的 SII Binary 放到 RK3588
        ↓
Linux 主站定位从站
        ↓
直接写 EEPROM

也就是说:

让机器人自己的 EtherCAT 主站,同时承担从站维护工具的角色。


3. ESI XML 如何转换成 SII .bin

这是整个升级链路里非常关键的一步。

因为 IgH:

ethercat sii_write

写入的是 SII 二进制内容,并不能简单地把 XML 文件直接作为输入。

因此需要先完成:

ESI XML
   ↓
SII EEPROM Binary

转换。

目前开发 EtherCAT 从站时,一个很方便的方法就是使用 Beckhoff 提供的 SSC Tool 中的 EEPROM Programmer


3.1 使用 SSC Tool 的 EEPROM Programmer

如果本身就在使用 SSC Tool 开发 EtherCAT 从站,那么这也是最顺手的方法。

打开:

EtherCAT Slave Stack Code Tool

选择:

Tools
  └── EEPROM Programmer

进入 EEPROM Programmer。

然后执行:

File
  └── Open

选择需要转换的 ESI 文件,例如:

Renesas EtherCAT RZT2 Joint.xml

如果一份 ESI XML 中描述了多个 EtherCAT Device,需要在工具中选择真正要生成的那个:

Device Description
        ↓
JointDrive V1.0.1

确认设备后:

File
  └── Save As

保存类型选择:

Binary (*.bin)

例如最终保存为:

joint_V1.0.1.bin

整个过程就是:

joint_V1.0.1.xml
        ↓
SSC Tool
        ↓
EEPROM Programmer
        ↓
File → Open
        ↓
选择 Device Description
        ↓
File → Save As
        ↓
Binary (*.bin)
        ↓
joint_V1.0.1.bin

TI 官方 EtherCAT Slave 文档给出的流程也是:

SSC Tool
 → Tools
 → EEPROM Programmer
 → File
 → Open
 → 加载 ESI XML
 → 选择设备
 → File
 → Save As
 → binary (*.bin)

Renesas 官方 EtherCAT 应用笔记中也明确给出:

SSC Tool → Tool → EEPROM Programmer
File → Open → ESI file
File → Save As → Binary

之后会在指定路径生成 ESI/SII Binary。

所以这不是自己“解析 XML 再拼二进制”,而是直接利用 EtherCAT 官方开发工具完成格式转换。


4. XML 转 BIN 的本质是什么?

第一次使用 EEPROM Programmer 时,很容易觉得它只是:

XML → BIN

做了一个“文件格式转换”。

其实背后远不止这么简单。

ESI XML 是结构化设备描述,而 SII 是一个有固定存储布局的二进制数据结构。

转换过程中,大致会完成:

ESI XML
   │
   ├── Vendor ID
   ├── Product Code
   ├── Revision
   ├── Serial Number
   ├── Mailbox
   ├── FMMU
   ├── SyncManager
   ├── PDO
   ├── DC
   └── Strings
          │
          ▼
    重新编码和组织
          │
          ▼
+--------------------------+
| SII Fixed Area           |
+--------------------------+
| Strings Category         |
+--------------------------+
| General Category         |
+--------------------------+
| FMMU Category            |
+--------------------------+
| SyncManager Category     |
+--------------------------+
| TxPDO Category           |
+--------------------------+
| RxPDO Category           |
+--------------------------+
| DC Category              |
+--------------------------+
| ...                      |
+--------------------------+
          │
          ▼
      EEPROM BIN

因此:

SII Binary 不是 XML 文件简单去掉标签后的结果。

它是按照 EtherCAT SII EEPROM 规范重新编码以后形成的一份二进制镜像。

这也是为什么不建议自己随便写一个 XML Parser 来生成 .bin

涉及 EEPROM 内容时,优先使用成熟的 EtherCAT 工具生成更稳妥。


5. 另一种非常实用的方法:TwinCAT 写入,再通过 IgH 反向导出 BIN

如果手头已经有一份经过 TwinCAT 验证、能够正常使用的 ESI XML,还可以利用一个比较实用的办法得到 .bin

思路是:

ESI XML
   ↓
TwinCAT
   ↓
写入真实从站 EEPROM
   ↓
切换到 IgH
   ↓
sii_read
   ↓
导出 EEPROM Binary

也就是说:

先让成熟配置工具完成 XML → SII 转换,然后直接从实际 EEPROM 中把完整镜像读回来。

例如先使用 TwinCAT 将新的 ESI 信息写入某一个测试从站。

确认从站能够:

INIT
 ↓
PREOP
 ↓
SAFEOP
 ↓
OP

正常运行以后,再接回 IgH 主站。

执行:

sudo ethercat sii_read -m 0 -p 1 > joint_V1.0.1.bin

这样得到的:

joint_V1.0.1.bin

就是这个实际从站当前 EEPROM 的完整 SII 内容。

ETG 的 Slave Implementation Guide 明确说明,配置工具可以基于 ESI 文件生成并写入 SII 内容

而 IgH 的 sii_read 可以读取完整 SII 内容,因此这个方法也非常适合做“黄金镜像”。


5.1 我更推荐保存一份“Golden SII”

如果某个版本已经在实际硬件上完整验证通过,例如:

Firmware : V1.3.2
ESI      : V1.3.2
EEPROM   : V1.3.2
PDO      : Verified
DC       : Verified
FoE      : Verified

那么我更倾向于把这个实际 EEPROM 导出来:

sudo ethercat sii_read -m 0 -p 1 \
    > joint_V1.3.2_golden.bin

作为正式发布包中的:

Golden SII Image

以后整机批量更新,统一使用这一份文件。

例如版本目录可以设计为:

JointDrive_V1.3.2/
│
├── firmware/
│   └── joint_fw_v1.3.2.bin
│
├── esi/
│   └── JointDrive_V1.3.2.xml
│
├── sii/
│   └── JointDrive_V1.3.2_golden.bin
│
└── release_note.md

这样就不会出现:

固件是 V1.3.2
ESI 是 V1.3.1
SII 又是另一份

这种版本错配。


6. 不建议直接手工修改 .bin

理论上 SII Binary 最终就是一个二进制文件。

因此用:

Hex Editor

当然可以打开。

TwinCAT 的 Advanced Settings 中甚至提供了 EEPROM Hex Editor,可以按字节查看 EEPROM 二进制内容。

但是除非是在定位问题,否则不建议把:

手动 Hex 修改 BIN

作为正常开发流程。

例如你只是想修改:

Revision Number

看起来可能只需要改几个字节。

但实际上 SII 中还存在:

固定区
Category
长度字段
字符串索引
CRC / 校验相关内容

如果不了解具体结构,手工修改非常容易留下隐患。

更稳妥的流程仍然应该是:

修改源 ESI / SSC 工程
       ↓
重新生成 XML
       ↓
EEPROM Programmer
       ↓
重新生成 SII BIN

这样 XML 才是配置的“源头”。


7. IgH 自带 SII 读写命令

完成 XML → BIN 以后,后面的事情就交给 IgH。

IgH 提供了两个非常实用的命令:

ethercat sii_read

以及:

ethercat sii_write

7.1 读取从站 SII

例如读取:

Master 0
Position 1

对应从站的 EEPROM:

sudo ethercat sii_read -m 0 -p 1 > joint1.bin

其中:

-m 0

表示:

Master 0

而:

-p 1

表示 EtherCAT 总线上的:

Position 1

最终:

joint1.bin

就是当前 EEPROM 的完整 SII 二进制镜像。

这个功能非常适合用于:

备份
验证
Golden Image 制作
问题对比
版本回滚

7.2 写入 SII

假设已经使用 SSC Tool 生成:

joint_V1.0.1.bin

那么:

sudo ethercat sii_write \
    -m 0 \
    -p 1 \
    joint_V1.0.1.bin

就可以把新的 SII 镜像写入目标从站。

整个实际开发链路现在已经完整闭环:

SSC Tool
   │
   ├── 修改 Object Dictionary
   ├── 修改 PDO
   ├── 修改 Device Info
   │
   ▼
生成 ESI XML
   │
   ▼
EEPROM Programmer
   │
   ▼
SII *.bin
   │
   ▼
scp / U盘 / 发布包
   │
   ▼
RK3588 Linux
   │
   ▼
ethercat sii_write
   │
   ▼
从站 EEPROM

8. 单个从站升级测试

在真正做整机批量升级之前,我先做了一个最小化实验。

Master 0 下挂了两个关节模组。

原来的 SII 版本都是:

V1.0.0

首先查看:

ethercat slaves

可以看到类似:

Master0

0  1000:0  PREOP  +  ...
1  1000:1  PREOP  +  Renesas EtherCAT RZ/T2 ... V1.0.0
2  1000:2  PREOP  +  Renesas EtherCAT RZ/T2 ... V1.0.0

这里准备只更新:

Master 0
Position 1

执行:

sudo ethercat sii_write \
    -m 0 \
    -p 1 \
    joint_V1.0.1.bin

写入完成后,重新启动从站。

再次执行:

ethercat slaves

可以看到:

Position 1 → V1.0.1
Position 2 → V1.0.0

说明:

m0 pos1

对应的 SII EEPROM 已成功更新。

这也验证了后面整机自动化升级需要的两个基本条件:

IgH 可以直接更新 SII EEPROM

Master ID + Position 可以定位单个从站

9. 从单节点升级到整机批量升级

单个节点成功以后,下一个问题就是:

总不能人工执行二三十遍 sii_write

如果手工执行:

ethercat sii_write -m 0 -p 1 ...
ethercat sii_write -m 0 -p 2 ...
ethercat sii_write -m 0 -p 3 ...
...

不仅效率低,而且很容易出现:

位置写错
漏写
重复写
文件选错
特殊设备误更新

因此下一步就是:

把整机 EtherCAT 拓扑显式写进脚本。


10. 建立整机节点映射

例如:

NODES=(
    "0 0 下半身ESC枢纽 SKIP"

    "0 1 左腿关节1"
    "0 2 左腿关节2"
    "0 3 左腿关节3"
    "0 4 左腿关节4"
    "0 5 左腿关节5"
    "0 6 左腿关节6"

    "0 7 右腿关节1"
    "0 8 右腿关节2"
    "0 9 右腿关节3"
    "0 10 右腿关节4"
    "0 11 右腿关节5"
    "0 12 右腿关节6"

    "0 13 下腰关节1"
    "0 14 下腰关节2"
    "0 15 下腰关节3"

    "1 0 上半身ESC枢纽 SKIP"

    "1 1 左臂关节1"
    "1 2 左臂关节2"
    "1 3 左臂关节3"
    "1 4 左臂关节4"
    "1 5 左臂关节5"
    "1 6 左臂关节6 SKIP"

    "1 7 右臂关节1"
    "1 8 右臂关节2"
    "1 9 右臂关节3"
    "1 10 右臂关节4"
    "1 11 右臂关节5"
    "1 12 右臂关节6 SKIP"

    "1 13 上腰关节1"
    "1 14 上腰关节2"
)

每个节点包含:

Master ID
Position
节点名称
特殊标志

例如:

0 3 左腿关节3

代表:

Master 0
Position 3
左腿关节3

SKIP 则表示:

当前设备使用独立 SII
不能使用本次通用关节镜像

11. Shell 脚本实现整机批量更新

完整脚本:

#!/bin/bash

# 人形机器人全身 SII EEPROM 批量更新脚本
#
# 用法:
# sudo ./robot_sii_update.sh <sii文件路径>

set -euo pipefail


if [[ $# -ne 1 ]]; then
    echo "用法: sudo $0 <sii 文件路径>"
    echo "示例: sudo $0 ./joint_drive_v2.bin"
    exit 1
fi


SII_FILE="$1"


if [[ ! -f "$SII_FILE" ]]; then
    echo "[错误] SII 文件不存在: $SII_FILE"
    exit 1
fi


GREEN='\033[0;32m'
RED='\033[0;31m'
YELLOW='\033[1;33m'
NC='\033[0m'


NODES=(
    "0 0 下半身ESC枢纽 SKIP"

    "0 1 左腿关节1"
    "0 2 左腿关节2"
    "0 3 左腿关节3"
    "0 4 左腿关节4"
    "0 5 左腿关节5"
    "0 6 左腿关节6"

    "0 7 右腿关节1"
    "0 8 右腿关节2"
    "0 9 右腿关节3"
    "0 10 右腿关节4"
    "0 11 右腿关节5"
    "0 12 右腿关节6"

    "0 13 下腰关节1"
    "0 14 下腰关节2"
    "0 15 下腰关节3"

    "1 0 上半身ESC枢纽 SKIP"

    "1 1 左臂关节1"
    "1 2 左臂关节2"
    "1 3 左臂关节3"
    "1 4 左臂关节4"
    "1 5 左臂关节5"
    "1 6 左臂关节6 SKIP"

    "1 7 右臂关节1"
    "1 8 右臂关节2"
    "1 9 右臂关节3"
    "1 10 右臂关节4"
    "1 11 右臂关节5"
    "1 12 右臂关节6 SKIP"

    "1 13 上腰关节1"
    "1 14 上腰关节2"
)


TOTAL=${#NODES[@]}

OK=0
FAIL=0
SKIP=0


echo "======================================================"
echo " 人形机器人全身 SII EEPROM 批量更新"
echo " SII 文件: $SII_FILE ($(stat -c%s "$SII_FILE") bytes)"
echo " 节点总数: $TOTAL"
echo "======================================================"
echo ""


for entry in "${NODES[@]}"; do

    read -r mid pos name flag <<< "$entry" 2>/dev/null || flag=""

    printf " [m%s pos%-2s] %-14s ... " \
           "$mid" "$pos" "$name"


    # 特殊节点跳过

    if [[ "$flag" == "SKIP" ]]; then

        echo -e "${YELLOW}跳过 (独立 SII)${NC}"

        ((SKIP++))

        continue
    fi


    # 检查目标从站是否在线

    if ! ethercat -m"$mid" slaves 2>/dev/null |
         awk -v p="$pos" \
         '$1==p{found=1}END{exit !found}'; then

        echo -e "${YELLOW}跳过 (离线)${NC}"

        ((SKIP++))

        continue
    fi


    # 写入 SII

    if ethercat -m"$mid" sii_write \
        --force \
        -p "$pos" \
        "$SII_FILE" \
        2>/dev/null; then

        echo -e "${GREEN}✓ 成功${NC}"

        ((OK++))

    else

        echo -e "${RED}✗ 失败${NC}"

        ((FAIL++))
    fi

done


echo ""

echo "======================================================"

printf " 完成: 成功 ${GREEN}%d${NC} 失败 ${RED}%d${NC} 跳过 ${YELLOW}%d${NC}\n" \
       "$OK" "$FAIL" "$SKIP"

echo "======================================================"

echo ""

echo "请重启 EtherCAT 服务使主站重新扫描从站:"

echo "systemctl restart ethercat"

echo ""


[[ $FAIL -eq 0 ]]

赋予执行权限:

chmod +x robot_sii_update.sh

执行:

sudo ./robot_sii_update.sh ./joint_V1.0.1.bin

12. 为什么脚本一定要做在线检测?

批量升级和单节点测试最大的区别在于:

不能假定整条 EtherCAT 总线永远是完整的。

现场可能发生:

某个关节没有上电
某条 EtherCAT 线松动
某个从站掉线
Master 1 没有启动
整机拓扑发生变化

因此写 EEPROM 前先执行:

ethercat -m"$mid" slaves

判断当前 Position 是否存在。

流程就是:

准备升级节点
      ↓
查询 Master
      ↓
目标 Position 是否存在?
   ↙                    ↘
 否                      是
 ↓                       ↓
记录 SKIP              执行写入

这样即使一个节点离线,也不会导致整个升级流程失控。


13. 为什么要统计 OK / FAIL / SKIP?

批量工具最忌讳最终只输出:

Done

因为操作完以后,你根本不知道哪些节点真正更新成功。

所以脚本中维护:

OK=0
FAIL=0
SKIP=0

最终给出:

======================================================
完成:

成功:24
失败:1
跳过:3
======================================================

这样可以立即知道:

多少节点真正升级成功
多少节点升级失败
多少节点没有被处理

对整机维护工具来说,这种结果统计非常重要。


14. 最终完整工程流程

到这里,整套流程就完全打通了。

从开发到整机升级可以概括为:

┌─────────────────────────────┐
│       EtherCAT SSC Tool      │
└──────────────┬──────────────┘
               │
               │ 修改 OD / PDO / DC / 版本
               ▼
┌─────────────────────────────┐
│          ESI XML             │
│    JointDrive_V1.0.1.xml     │
└──────────────┬──────────────┘
               │
               │ EEPROM Programmer
               ▼
┌─────────────────────────────┐
│        SII Binary            │
│    JointDrive_V1.0.1.bin     │
└──────────────┬──────────────┘
               │
               │ 发布到 RK3588
               ▼
┌─────────────────────────────┐
│        IgH Master            │
│      robot_sii_update.sh     │
└──────────────┬──────────────┘
               │
               │ sii_write
               ▼
┌─────────────────────────────┐
│      EtherCAT Slaves         │
│    全身关节 EEPROM 更新       │
└──────────────┬──────────────┘
               │
               │ sii_read
               ▼
┌─────────────────────────────┐
│      Read-back Verify        │
│     Binary / Hash Compare    │
└─────────────────────────────┘

15. 总结

这次功能最开始其实只是一个很现实的问题:

不想为了更新整机所有关节的 EtherCAT 配置,再把机器人拆开接 TwinCAT。

原始需求很简单。

但真正把整个链路做完整以后,会发现它实际涉及:

ESI XML
SII 数据结构
EEPROM Binary
SSC Tool
IgH Master
EtherCAT 拓扑
批量升级
版本管理
备份
验证
回滚

整个发布链路最终可以总结为:

SSC 工程
    ↓
生成 ESI XML
    ↓
EEPROM Programmer
    ↓
生成 SII BIN
    ↓
发布到 Linux 主站
    ↓
检查整机拓扑
    ↓
备份原 EEPROM
    ↓
批量 sii_write
    ↓
重启 EtherCAT
    ↓
重新扫描
    ↓
sii_read 回读
    ↓
Compare / Hash Verify

单独给一个 EtherCAT 从站写 EEPROM,其实并不难。

真正到了机器人整机阶段,值得解决的问题变成了:

如何保证正确的 SII 写入正确的设备;如何批量操作而不误伤特殊节点;如何确认真正写成功;以及升级失败以后如何恢复。

Logo

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

更多推荐