跳到主要内容

从站分组

从站分组功能允许将从站分配到不同的组(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.groupint读写从站组归属(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].dividerint读写PDO 周期分频器(1-255,组号 1-7)
组诊断master.active_group_countint只读活跃组数量(空组不计)

快速开始​

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].dividerint读写组的 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)