跳到主要内容

SoE (Servo over EtherCAT)

SoE 协议将 SERCOS 伺服通信协议封装在 EtherCAT 邮箱中,适用于 SERCOS 兼容的伺服驱动器。

通过 slave.SoE 访问。从站不支持 SoE 时为 null

SoE vs CoE

大多数 EtherCAT 伺服驱动器使用 CoE + CiA 402 协议栈。 SoE 仅用于 SERCOS 兼容 的驱动器(如 Bosch Rexroth IndraDrive 系列)。 两者不能混用——从站支持哪种取决于硬件。

SERCOS IDN 体系

SoE 通过 IDN(Identification Number) 寻址驱动器参数。每个 IDN 是一个 16 位编号,代表一个参数(位置、速度、控制字等)。

IDN 命名规则:

前缀范围说明
S-x-xxxx0x0000 – 0x7FFFSERCOS 标准参数(所有厂商通用)
P-x-xxxx0x8000 – 0xBFFF产品特定参数
0xC000 – 0xFFFF厂商自定义参数

常用标准 IDN(伺服控制相关):

IDNSERCOS 名说明数据类型
S-0-0001 (0x0001)Cycle Time通信周期时间uint, μs
S-0-0011 (0x000B)Position Feedback 1实际位置int, inc
S-0-0012 (0x000C)Position Command目标位置int, inc
S-0-0013 (0x000D)Velocity Feedback实际速度int, rpm
S-0-0014 (0x000E)Velocity Command目标速度int, rpm
S-0-0016 (0x0010)AT Config输入映射配置列表list
S-0-0019 (0x0013)Drive Status驱动器状态字ushort
S-0-0024 (0x0018)MDT Config输出映射配置列表list
S-0-0036 (0x0024)Max Velocity最大速度限制uint, rpm
S-0-0040 (0x0028)Homing Velocity回零速度uint, rpm
S-0-0064 (0x0040)Drive Control Word驱动器控制字ushort
S-0-0071 (0x0047)Drive Status Word驱动器状态字ushort

元素标志(Element Flags)— 读取 IDN 的不同"层面":

标志含义
0x01数据状态
0x02参数名称(字符串)
0x04属性(数据类型/长度/权限位域)
0x08单位(字符串)
0x10最小值
0x20最大值
0x40数据值(默认,最常用)
0x80默认值

基本读写

Read(ushort idn, byte elementFlags = 0x40, int timeoutMs = 500)

public byte[]? Read(ushort idn, byte elementFlags = 0x40, int timeoutMs = 500)

读取 IDN 参数原始字节。非可用状态自动切换。

参数:

  • idn (ushort) — IDN 编号
  • elementFlags (byte) — 元素标志(默认 0x40 = 数据值)
  • timeoutMs (int) — 超时时间(毫秒)

返回值:

  • byte[]? — 读取的数据,失败返回 null

示例:

// 读取通信周期时间 (S-0-0001)
byte[]? data = slave.SoE.Read(0x0001, 0x40);
if (data != null)
{
uint cycleTime = BitConverter.ToUInt32(data, 0);
Console.WriteLine($"周期时间: {cycleTime} μs");
}

Write(ushort idn, byte[] data, byte elementFlags = 0, int timeoutMs = 500)

public bool Write(ushort idn, byte[] data, byte elementFlags = 0, int timeoutMs = 500)

写入 IDN 参数。非可用状态自动切换。

参数:

  • idn (ushort) — IDN 编号
  • data (byte[]) — 要写入的数据
  • elementFlags (byte) — 元素标志(默认 0 = 数据值)
  • timeoutMs (int) — 超时时间(毫秒)

返回值:

  • bool — 成功返回 true

示例:

// 设置目标位置 (S-0-0012)
byte[] position = BitConverter.GetBytes(100000);
bool success = slave.SoE.Write(0x000C, position);

类型化读取

所有方法签名一致:ReadXxx(ushort idn, byte elementFlags = 0x40, int timeoutMs = 500)

方法返回类型说明
ReadInt16short?读取 16 位有符号整数
ReadInt32int?读取 32 位有符号整数
ReadUInt16ushort?读取 16 位无符号整数
ReadUInt32uint?读取 32 位无符号整数
ReadFloatfloat?读取单精度浮点数
ReadDoubledouble?读取双精度浮点数
ReadStringstring?读取字符串

示例:

// 读取实际位置 (S-0-0011)
int? actualPos = slave.SoE.ReadInt32(0x000B);

// 读取驱动器状态字 (S-0-0071)
ushort? status = slave.SoE.ReadUInt16(0x0047);

// 读取参数名称
string? name = slave.SoE.ReadString(0x000B, elementFlags: 0x02);

类型化写入

所有方法签名一致:WriteXxx(ushort idn, T value, byte elementFlags = 0, int timeoutMs = 500),返回 bool

方法值类型说明
WriteInt16short写入 16 位有符号整数
WriteInt32int写入 32 位有符号整数
WriteUInt16ushort写入 16 位无符号整数
WriteUInt32uint写入 32 位无符号整数

示例:

// 设置目标位置 (S-0-0012)
slave.SoE.WriteInt32(0x000C, 50000);

// 写控制字 (S-0-0064)
slave.SoE.WriteUInt16(0x0040, 0x0006);

元数据读取

ReadName(ushort idn, int timeoutMs = 500)

public string? ReadName(ushort idn, int timeoutMs = 500)

读取 IDN 参数名称(内部使用 ElementFlag 0x02)。

ReadUnit(ushort idn, int timeoutMs = 500)

public string? ReadUnit(ushort idn, int timeoutMs = 500)

读取 IDN 参数单位(内部使用 ElementFlag 0x08)。

ReadMinValue / ReadMaxValue / ReadDefaultValue

public byte[]? ReadMinValue(ushort idn, int timeoutMs = 500)
public byte[]? ReadMaxValue(ushort idn, int timeoutMs = 500)
public byte[]? ReadDefaultValue(ushort idn, int timeoutMs = 500)

读取 IDN 参数的最小值(0x10)、最大值(0x20)、默认值(0x80)。

示例:

string? name = slave.SoE.ReadName(0x000B);
string? unit = slave.SoE.ReadUnit(0x000B);
byte[]? min = slave.SoE.ReadMinValue(0x000B);
byte[]? max = slave.SoE.ReadMaxValue(0x000B);

Console.WriteLine($"参数: {name} ({unit})");
if (min != null && max != null)
Console.WriteLine($"范围: {BitConverter.ToInt32(min, 0)} - {BitConverter.ToInt32(max, 0)}");

参数信息

GetAvailableIDNs(int timeoutMs = 5000)

public List<ushort> GetAvailableIDNs(int timeoutMs = 5000)

获取从站支持的所有 IDN 列表。

返回值:

  • List<ushort> — IDN 编号列表

GetParameterInfo(ushort idn, int timeoutMs = 5000)

public SoEParameter GetParameterInfo(ushort idn, int timeoutMs = 5000)

获取 IDN 参数的完整信息(名称、单位、属性、当前值、最小/最大值等)。

返回值:

  • SoEParameter — 包含完整的参数信息

相关结构:

public class SoEParameter
{
public ushort IDN { get; } // IDN 编号
public string Name { get; } // 参数名称
public string Unit { get; } // 单位
public SoEAttributes Attributes { get; } // 属性(数据类型、长度等)
public byte[] Value { get; } // 当前值
public byte[] DefaultValue { get; } // 默认值
public byte[] MinValue { get; } // 最小值
public byte[] MaxValue { get; } // 最大值
public string SercosIDN { get; } // SERCOS IDN 格式(如 "S-0-0001")
public string Category { get; } // 类别("Standard" / "Product" / "Vendor")
public bool IsReadOnly { get; } // 是否只读
public string AccessMode { get; } // 访问权限("RO" / "RW" / "RW*")
public string FormattedValue { get; } // 格式化的当前值
public string FormattedMinValue { get; } // 格式化的最小值
public string FormattedMaxValue { get; } // 格式化的最大值
}
public struct SoEAttributes
{
public ushort EvaluationFactor; // 评价因子
public byte Length; // 数据长度
public bool IsList; // 是否为列表
public bool IsCommand; // 是否为命令
public SoEDataType DataType; // 数据类型
public byte Decimals; // 小数位数
public bool WriteProtectedPreOp; // PreOp 状态写保护
public bool WriteProtectedSafeOp; // SafeOp 状态写保护
public bool WriteProtectedOp; // Op 状态写保护
}
public enum SoEDataType
{
Binary = 0, // 二进制
UInt = 1, // 无符号整数
Int = 2, // 有符号整数
Hexadecimal = 3, // 十六进制
String = 4, // 字符串
IDN = 5, // IDN 引用
Float = 6, // 浮点数
Parameter = 7 // 参数
}

示例:

var param = slave.SoE.GetParameterInfo(0x000B);
Console.WriteLine($"IDN: {param.SercosIDN} - {param.Name}");
Console.WriteLine($"单位: {param.Unit}, 访问: {param.AccessMode}");
Console.WriteLine($"当前值: {param.FormattedValue}");
Console.WriteLine($"范围: {param.FormattedMinValue} - {param.FormattedMaxValue}");

AT/MDT 映射

SERCOS 使用 AT(Acknowledge Telegram)和 MDT(Master Data Telegram)传输周期数据:

  • AT = 从站→主站的周期输入(实际位置、实际速度、状态字…)
  • MDT = 主站→从站的周期输出(目标位置、目标速度、控制字…)

映射配置决定了哪些 IDN 参数被包含在周期数据帧中。

GetIDNMapping(int timeoutMs = 5000)

public SoEMappingInfo GetIDNMapping(int timeoutMs = 5000)

读取当前驱动器的 AT/MDT 映射配置。

返回值:

  • SoEMappingInfo — 映射信息对象

相关结构:

public class SoEMappingInfo
{
public List<SoEMappingEntry> ATMapping { get; } // AT(输入)映射列表
public List<SoEMappingEntry> MDTMapping { get; } // MDT(输出)映射列表
public int ATBitSize { get; } // AT 总位数
public int MDTBitSize { get; } // MDT 总位数
public int ATByteSize { get; } // AT 总字节数
public int MDTByteSize { get; } // MDT 总字节数
}

public class SoEMappingEntry
{
public ushort IDN { get; } // IDN 编号
public string Name { get; } // 参数名称
public int BitLength { get; } // 数据长度(位)
public int ByteLength { get; } // 数据长度(字节)
}

GetAllDriveMappings(int timeoutMs = 5000)

public Dictionary<byte, SoEMappingInfo> GetAllDriveMappings(int timeoutMs = 5000)

获取所有驱动器的映射信息(最多扫描 8 个驱动器)。

返回值:

  • Dictionary<byte, SoEMappingInfo> — 驱动器编号到映射信息的字典

标准 IDN 常量

public static class StandardIDN
{
public const ushort IDNList = 0x0000; // IDN 列表 (S-0-0000)
public const ushort ATConfig = 0x0010; // AT 配置 - 输入映射 (S-0-0016)
public const ushort MDTConfig = 0x0018; // MDT 配置 - 输出映射 (S-0-0024)
public const ushort CycleTime = 0x0001; // 通信周期时间 (S-0-0001)
public const ushort DriveStatus = 0x0010; // 驱动器状态 (S-0-0016)
}

完整示例

参数扫描

if (slave.SoE)
{
var idns = slave.SoE.GetAvailableIDNs();
Console.WriteLine($"从站支持 {idns.Count} 个 IDN 参数\n");

foreach (var idn in idns.Take(10))
{
var info = slave.SoE.GetParameterInfo(idn);
Console.WriteLine($"{info.SercosIDN}: {info.Name} = {info.FormattedValue} {info.Unit} [{info.AccessMode}]");
}
}

伺服驱动器控制

if (slave.SoE)
{
// 1. 读取驱动器状态
ushort? statusWord = slave.SoE.ReadUInt16(0x0047);
Console.WriteLine($"状态字: 0x{statusWord:X4}");

// 2. 查看 AT/MDT 映射(了解周期数据布局)
var mapping = slave.SoE.GetIDNMapping();
Console.WriteLine($"\nAT 映射(输入 {mapping.ATByteSize} 字节):");
foreach (var entry in mapping.ATMapping)
Console.WriteLine($" IDN 0x{entry.IDN:X4}: {entry.Name} ({entry.ByteLength}B)");

Console.WriteLine($"\nMDT 映射(输出 {mapping.MDTByteSize} 字节):");
foreach (var entry in mapping.MDTMapping)
Console.WriteLine($" IDN 0x{entry.IDN:X4}: {entry.Name} ({entry.ByteLength}B)");

// 3. 写控制字使能驱动器 (S-0-0064)
slave.SoE.WriteUInt16(0x0040, 0x0006);

// 4. 设置目标位置 (S-0-0012)
slave.SoE.WriteInt32(0x000C, 50000);

// 5. 读取实际位置 (S-0-0011)
int? actualPos = slave.SoE.ReadInt32(0x000B);
Console.WriteLine($"\n实际位置: {actualPos} inc");
}