从站分组
从站分组功能允许将从站分配到不同的组(1-7;group=0 表示未分配,映射前自动归入组 1),每组可配置独立的 PDO 周期分频器,实现不同组以不同频率交换数据。
典型应用场景(主周期 31.25us,WDK 驱动最低支持):
- 伺服驱动(组1):默认组,每周期发送,31.25us 32KHz
- IO 模块(组2):每4周期发送一次,125us 8000Hz
- 传感器(组3):每255周期发送一次(分频器上限 255),约 8ms 125Hz
核心简化
- 无需手动启用组 — 系统在进入 SafeOp 时自动启用有从站的组,禁用空组
- 属性式分频器 — 通过
master.groups[g].divider直接设置 - 每组独立收发 — 每个组对应独立的 PDO 数据报文
功能概览
| 类别 | API | 类型 | 读写 | 说明 |
|---|---|---|---|---|
| 组分配 | slave.group | int | 读写 | 从站组归属(0-7,0=未分配,映射前自动归入组 1),须在 SAFE_OP 之前设置 |
| 组访问 | master.groups | _GroupAccessor | 只读 | 组索引器 |
| master.groups[g] | _GroupView | 只读 | 组视图(索引 0-7;组 0 是未分配标记,不独立收发) | |
| master.groups[g][i] | Slave | 只读 | 组内从站(0-based) | |
| len(master.groups[g]) | int | 只读 | 组内从站数量 | |
| 组分频 | master.groups[g].divider | int | 读写 | PDO 周期分频器(1-255,组号 1-7) |
| 组诊断 | master.active_group_count | int | 只读 | 活跃组数量(空组不计) |
快速开始
from darra_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(未分配,映射前自动归入组 1,每周期发送)
master[3].group = 2 # IO 模块 → 组2(1-based 索引)
master[4].group = 2
master[5].group = 3 # 传感器 → 组3
# 2. 设置组分频器(组级配置只接受组号 1-7)
master.groups[2].divider = 4 # 组2: 每4周期发送
master.groups[3].divider = 10 # 组3: 每10周期发送
# 组1(默认组)分频器默认=1,无需额外设置
# 3. 切换到 OP — 系统自动启用有从站的组
master.set_state(EcState.OP)
master.start()
# 4. 通过组索引器访问从站
io = master.groups[2][0] # 组2的第一个从站
sensor = master.groups[3][0] # 组3的第一个从站
print(f"组2: {len(master.groups[2])} 个从站")
print(f"组3: {len(master.groups[3])} 个从站")
组语义
| 组号 | 含义 |
|---|---|
| 0 | 未分配标记 — 不设置时保持 0;进入 SAFE_OP 映射前自动归入组 1,不独立收发 |
| 1-7 | 有效 PDO 组 — 用户手动分配,组号无需连续;未显式分组的从站最终都在组 1 |
备注
- 有效组范围为 1-7,每个组独立收发 PDO 数据
slave.group可写 0-7;组级配置与查询(divider、set_group_cycle_divider()、set_group_enabled()、按组丢帧统计)只接受 1-7,对组 0 设置无效- 组号可以不连续(如 1, 3, 5),系统会跳过空组
- 进入 SafeOp 时自动启用有从站的组,禁用空组
从站组分配
通过 slave.group 属性设置从站的组归属。
slave.group
@property
def group(self) -> int
@group.setter
def group(self, value: int) -> None
获取或设置从站的组归属(0-7,0=未分配,映射前自动归入组 1)。必须在 SAFE_OP 之前设置。
示例:
# 分配从站到组
master[1].group = 1 # 显式分到组1
master[2].group = 2 # 显式分到组2
# master[3].group 不设置,保持 0(未分配,映射前归入组 1)
# 读取从站所在组
group = master[1].group # 1
master.groups[g][i]
@property
def groups(self) -> _GroupAccessor
组索引器 (内部类型 _GroupAccessor),支持 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-7(组 0 是未分配标记,设置无效);各组分频器默认 1。
1— 每周期发送(1000Hz,主周期 1ms 时)2— 每2周期发送(500Hz)4— 每4周期发送(250Hz)10— 每10周期发送(100Hz)
示例:
# 设置分频器(组号 1-7)
master.groups[2].divider = 4 # 组2: 每4周期
master.groups[3].divider = 10 # 组3: 每10周期
# 组1(默认组)分频器默认=1(每周期),无需设置
# 读取分频器
div = master.groups[3].divider # 10
组属性:
| 类别 | 属性 | 类型 | 读写 | 说明 |
|---|---|---|---|---|
| 组分频 | groups[g].divider | int | 读写 | 组的 PDO 周期分频器 (1-255) |
| 组视图 | len(groups[g]) | int | 只读 | 组内从站数量 |
自动启用/禁用
进入 SafeOp 状态时,系统自动扫描每个组(1-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 == 1:
# 伺服组丢帧,紧急处理
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 darra_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(伺服驱动,未分配,映射前归入组 1)
for i in range(4, 6):
master[i].group = 2 # IO 模块 -> 组2(1-based 索引)
# 其余从站保持 group=0(映射前归入组 1,每周期发送)
# 2. 设置组分频器(组号 1-7)
master.groups[2].divider = 4 # 组2: 250Hz
# 组1(默认组)分频器默认=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"组1: {len(master.groups[1])} 个从站")
print(f"组2: {len(master.groups[2])} 个从站")
# 7. 周期回调中按组处理(读过程映像,不要在回调里做 SDO)
def on_pdo(mi):
for servo in master.groups[1]:
raw = servo.pdo.inputs
if raw and len(raw) >= 2:
status = int.from_bytes(raw[:2], 'little')
master.on_pdo_cycle(on_pdo)