跳到主要内容

从站分组

从站分组功能允许将从站分配到不同的组(0-7),每组可配置独立的 PDO 周期分频器,实现不同组以不同频率交换数据

典型应用场景:

  • 伺服驱动(组0):默认组,每周期发送,31.25us 32KHz
  • IO 模块(组1):每4周期发送一次,125us 8000Hz
  • 传感器(组2):每6400周期发送一次,200ms 5Hz
核心简化
  • 无需手动启用组 — 系统在进入 SafeOp 时自动启用有从站的组,禁用空组
  • 属性式分频器 — 通过 master.groups[g].divider 直接设置
  • 每组独立收发 — 每个组对应独立的 PDO 数据报文

快速开始

from ethercat import EtherCATMaster, EcState

with EtherCATMaster() as master:
master.set_eni(r"C:\config.deni")
master.set_network(r"\\Device\\NPF_{GUID}")

# 1. 分配从站到组(必须在 SAFE_OP 之前)
# master[1], master[2] 不设置,保持 group=0(默认组,每周期发送)
master[3].group = 1 # IO 模块 → 组1(1-based 索引)
master[4].group = 1
master[5].group = 2 # 传感器 → 组2

# 2. 设置组分频器
master.groups[1].divider = 4 # 组1: 每4周期发送
master.groups[2].divider = 10 # 组2: 每10周期发送
# 组0 默认分频器=1,无需额外设置

# 3. 切换到 OP — 系统自动启用有从站的组
master.set_state(EcState.OP)
master.start()

# 4. 通过组索引器访问从站
io = master.groups[1][0] # 组1的第一个从站
sensor = master.groups[2][0] # 组2的第一个从站
print(f"组1: {len(master.groups[1])} 个从站")
print(f"组2: {len(master.groups[2])} 个从站")

组语义

组号含义
0默认组 — 未显式分组的从站自动归入此组
1-7显式分组 — 用户手动分配,组号无需连续
备注
  • 有效组范围为 0-7(共 8 组),每个组独立收发 PDO 数据
  • 组号可以不连续(如 1, 3, 5),系统会跳过空组
  • 进入 SafeOp 时自动启用有从站的组,禁用空组

从站组分配

通过 slave.group 属性设置从站的组归属。

slave.group

@property
def group(self) -> int

@group.setter
def group(self, value: int) -> None

获取或设置从站的组归属(0-7)。必须在 SAFE_OP 之前设置。

示例:

# 分配从站到组
master[1].group = 1 # 显式分到组1
master[2].group = 2 # 显式分到组2
# master[3].group 不设置,保持 0(默认组)

# 读取从站所在组
group = master[1].group # 1

master.groups[g][i]

@property
def groups(self) -> SlaveGroupAccessor

组索引器,支持 master.groups[组号] 获取该组从站列表,master.groups[组号][索引] 获取组内指定从站。

示例:

# 获取组1的所有从站
group1_slaves = master.groups[1]

# 遍历组内从站
for slave in master.groups[1]:
print(slave.name)

# 按索引访问
first = master.groups[1][0]

# 组内从站数
count = len(master.groups[1])

active_group_count

@property
def active_group_count(self) -> int

获取活跃组数量(扫描从站集合计算)。

组分频器

通过 master.groups[g] 设置分频器。

groups[g].divider

@property
def divider(self) -> int

@divider.setter
def divider(self, value: int) -> None

获取或设置组的 PDO 周期分频器 (1-255)。

  • 1 — 每周期发送(1000Hz,主周期 1ms 时)
  • 2 — 每2周期发送(500Hz)
  • 4 — 每4周期发送(250Hz)
  • 10 — 每10周期发送(100Hz)

示例:

# 设置分频器
master.groups[1].divider = 4 # 组1: 每4周期
master.groups[2].divider = 10 # 组2: 每10周期
# 组0 默认分频器=1(每周期),无需设置

# 读取分频器
div = master.groups[2].divider # 10

组属性:

  • groups[g].divider (int) — 获取或设置组的周期分频器 (1-255)
  • len(groups[g]) (int) — 获取组内从站数量

自动启用/禁用

进入 SafeOp 状态时,系统自动扫描每个组(0-7)是否有从站:

  • 有从站的组 -> 自动启用,确保分频器至少为 1
  • 空组 -> 自动禁用,跳过 PDO 收发

无需手动调用 set_group_enabled(),组号也无需连续。

组诊断

每组独立跟踪 PDO 丢帧统计。

按组查询丢帧

# 查询指定组的丢帧统计
stats = master.diagnostics_info.pdo.get_frame_loss_stats(1) # 组1
print(f"组1: 累计丢帧={stats.total_lost}, 连续={stats.consecutive_lost}")

stats2 = master.diagnostics_info.pdo.get_frame_loss_stats(2) # 组2
print(f"组2: 累计丢帧={stats2.total_lost}")

汇总所有组

# 不传参数,返回所有组的汇总统计
total = master.diagnostics_info.pdo.get_frame_loss_stats()
print(f"总丢帧: {total.total_lost}")

PDOFrameLoss 事件

PDO 连续丢帧事件的 group 参数标识哪个组发生了丢帧:

def on_loss(mi, group, consecutive, total):
print(f"组 {group} 丢帧: 连续={consecutive}, 累计={total}")

if group == 0:
# 伺服组丢帧,紧急处理
print("警告: 伺服组通信异常!")

master.on_pdo_frame_loss(on_loss)

配置时序

INIT → PRE_OP → SAFE_OP → OP
| | |
| | └─ 组配置已锁定,PDO 开始按组发送
| |
| └─ 设置从站组归属: slave.group = N
| 设置组分频器: master.groups[N].divider = divider
| (进入 SafeOp 时自动启用有从站的组)
|
└─ 初始化主站
警告
  • slave.group 在进入 SAFE_OP 时生效
  • 组分频器建议在初始化之后、切换到 OP 之前设置
  • 进入 SAFE_OP 后组配置自动锁定

完整示例

from ethercat import EtherCATMaster, EcState

with EtherCATMaster() as master:
master.set_eni(r"C:\config.deni")
master.set_network(r"\\Device\\NPF_{GUID}")

# 1. 分配从站到组
# master[1] ~ master[3] 不设置,保持 group=0(伺服驱动,默认组)
for i in range(4, 6):
master[i].group = 1 # IO 模块 -> 组1(1-based 索引)
# 其余从站保持 group=0(默认组,每周期发送)

# 2. 设置组分频器
master.groups[1].divider = 4 # 组1: 250Hz
# 组0 默认分频器=1(1000Hz),无需设置

# 3. 设置周期
master.config.loop_cycle = 1_000_000 # 1ms

# 4. PDO 丢帧监控(每组独立)
master.on_pdo_frame_loss(lambda mi, group, consecutive, total:
print(f"组 {group} 丢帧: 连续={consecutive}"))

# 5. 启动 — 自动启用有从站的组
master.set_state(EcState.OP)
master.start()

# 6. 查看组状态
print(f"活跃组: {master.active_group_count}")
print(f"组0: {len(master.groups[0])} 个从站")
print(f"组1: {len(master.groups[1])} 个从站")

# 7. 周期回调中按组处理
def on_pdo(mi):
for servo in master.groups[0]:
status = servo.coe.sdo_read_value(0x6041, 0, dtype='u16')

master.on_pdo_cycle(on_pdo, sync=False)