从站分组
从站分组功能允许将从站分配到不同的组(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)