Skip to content


概念介绍

在EtherCAT(以太网控制自动化技术)中,设备的状态管理通过不同的操作状态来实现,主要包括Pre-Operational(PreOp)、Safe-Operational(SafeOp)和Operational(Op)三种状态。

具体而言,存在以下的转换:

PreOp → SafeOp: 当设备完成初始化和配置后,主站可以将其状态从 PreOp 切换到 SafeOp,以进行安全相关的数据交换。

SafeOp → Op: 当系统确认安全条件满足后,主站可以将设备状态从 SafeOp 切换到 Op,进入全功能操作状态。

Op → SafeOp/PreOp: 在需要重新配置或出现故障时,主站可以将设备状态从 Op 切换回 SafeOp 或 PreOp。

C++

开源主机方案: SOEM,需要指出的是SOEM的运行需要ROOT权限

bash
git clone https://github.com/OpenEtherCATsociety/SOEM.git
cd SOEM
mkdir build
cd build
cmake ..
make
cd test/linux/simple_test
./simple_test eth0 # 填主站连接的网卡名

Python

SOEM有一个python封装库,可以用python实现ethercat通信

bash
# 安装
pip install pysoem

函数介绍

其核心的类方法介绍如下:

python
import pysoem
master = pysoem.Master() # 创建一个master对象
master.open('eth0') # 打开指定网卡开始通信
master.config_init() # 返回找到的从站数量
master.config_dc() # 定位DC从机,测量传播延迟,返回值为bool型,表示是否找到了DC从机


master.config_map() # 配置所有从站的PDO映射. PREOP -> SAFEOP - each slave's config_func is called. returns IO map size (sum of all PDO in an out data)
master.state_check(pysoem.SAFEOP_STATE, 50000) # 检查从站是否进入 SAFEOP 状态


master.state = pysoem.OP_STATE # 将主站state属性设置为Op
master.write_state() # Write all slaves state
master.state_check(pysoem.OP_STATE, 50000) # 检查从站是否进入 OP 状态. returns Requested state, or found state after timeout
master.read_state() # 读取所有从站的状态,返回发现的最低状态


master.send_processdata() # 发送过程数据到从站
master.receive_processdata(2000) # 从从站接收过程数据。


master.slaves() # Gets a list of the slaves found during config_init. The slave instances are of type CdefSlave.
slave = master.slaves[slave_pos] # 获取索引为slave_pos的从站对象
slave.sdo_write(index, subindex, data, ca=False) # Write to a CoE object.


slave.id # Product Code of the slave, read out from the slaves SII during config_init.
slave.man # Vendor ID of the slave, read out from the slaves SII during config_init.
slave.config_func # Slaves callback function that is called during config_map. When the state changes from Pre-Operational state to Operational state.


master.close() # 关闭通信

代码展示

基础代码展示

python
import pysoem

master = pysoem.Master()
master.open('eth0')
master.config_dc()

if master.config_init() > 0:
    master.config_map()
    
    for slave in master.slaves:
        print(f"slave name is {slave.name}")
        print(f"slave state is {slave.state}")
        print(f"slave id is {slave.id}")

    master.state_check(pysoem.SAFEOP_STATE, 50000)
    master.state_check(pysoem.OP_STATE, 50000)
    master.write_state()

    master.send_processdata()
    master.receive_processdata(2000)
    slave = master.slaves[0]
    # print(slave.input)

else:
    print("No slaves found")

master.state = pysoem.INIT_STATE
master.write_state()
master.close()

实操案例

以下是一个16通道EtherCAT变送器的代码展示

python
import pysoem
import time
import csv
from datetime import datetime

master = pysoem.Master()
master.open('eth0')
master.config_dc()

if master.config_init() > 0:
    master.config_map()
    
    for slave in master.slaves:
        print(f"slave name is {slave.name}")
        print(f"slave state is {slave.state}")
        print(f"slave id is {slave.id}")

    master.state_check(pysoem.SAFEOP_STATE, 50000)
    master.state_check(pysoem.OP_STATE, 50000)
    master.write_state()
    master.in_op = True

    with open('output.csv', 'w', newline='') as csvfile:
        csvwriter = csv.writer(csvfile)
        # 写入 CSV 文件的表头
        csvwriter.writerow(['Timestamp'] + [f'NET_{i}' for i in range(1, 17)])

        try:
            while True:
                master.send_processdata()
                master.receive_processdata(2000)

                # 读取 TxPDO 数据
                slave = master.slaves[0]
                
                
                # 假设 NET_1 到 NET_16 每个数据占用 4 字节 (32 位)
                data_offset = 0
                data_length = 4
                
                # 获取当前时间戳
                timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f')
                
                row_data = [timestamp]


                for i in range(1, 17):
                    data_end = data_offset + data_length
                    value = int.from_bytes(slave.input[data_offset:data_end], byteorder='little', signed=True)
                    print(f"NET_{i}: {value}")
                    row_data.append(value)
                    data_offset += data_length
                
                csvwriter.writerow(row_data)

                # 添加一个小延迟以避免 CPU 占用过高
                time.sleep(0.01)

        except KeyboardInterrupt:
            print("KeyboardInterrupt")
        # print(slave.input)

        finally:
            master.state = pysoem.INIT_STATE
            master.write_state()
            master.close()