3、通讯协议入门:Modbus RTU/TCP协议基础、数据帧结构、寄存器映射表
各位同学,今天我们来聊聊Modbus协议。说实话,这是咱们风电SCADA系统里最基础、也最绕不开的东西。我做了这么多年风电项目,几乎每个场站都在用Modbus。你想想看,风机里的PLC、变频器、各种传感器,它们怎么跟中控室说话?靠的就是这个协议。
3.1 Modbus协议是个啥?
Modbus诞生于1979年,比我都大几岁。但它至今还在工业现场活跃,为什么?因为它简单、可靠、开放。说白了,它就是一种主从通讯的规则——主站问,从站答。
在咱们风电场景里,主站通常是SCADA服务器或者上位机,从站就是风机里的各种设备。比如我负责的一个项目,一台风机里有20多个Modbus从站设备,包括变桨系统、偏航系统、齿轮箱油温传感器等等。
核心要点:Modbus是请求-响应模式。主站不发请求,从站绝不主动说话。这一点跟咱们平时聊天不一样,它更像审讯——你不问,我不说。
3.2 Modbus RTU vs Modbus TCP
这两种是咱们最常用的变种。我刚开始接触时也搞混过,后来做了几个项目才彻底明白。
| 对比项 | Modbus RTU | Modbus TCP |
|---|---|---|
| 物理层 | RS-232 / RS-485 | 以太网 |
| 传输方式 | 串行,二进制 | 网络,TCP/IP |
| 最大距离 | 约1200米(RS-485) | 取决于网络 |
| 典型场景 | 风机塔筒内的传感器 | 风机与中控室通讯 |
| 校验方式 | CRC16 | TCP校验 |
嗯,这里要注意:RTU的CRC校验是必须自己算的,很多新手在这里栽跟头。我曾经调试一个项目,数据总是偶尔跳变,查了两天才发现是CRC计算有bug。
3.3 数据帧结构——看懂报文
咱们先看RTU的帧结构。它长这样:
| 地址码(1字节) | 功能码(1字节) | 数据区(N字节) | CRC校验(2字节) |
举个例子,我要读取风机1的发电机转速(寄存器地址40001):
发送:01 03 00 00 00 01 84 0A
接收:01 03 02 00 64 B9 AF
我来拆解一下:
- 01:从站地址,风机1号
- 03:功能码,读取保持寄存器
- 00 00:起始寄存器地址(40001对应地址0)
- 00 01:读取1个寄存器
- 84 0A:CRC校验
接收到的数据里,00 64就是转速值,换算成十进制是100。嗯,100转/分钟,这个值看起来合理。
我的小技巧:调试时用串口助手抓报文,先看地址码和功能码对不对,再看数据长度是否匹配。CRC错了直接丢包,这是最常见的故障点。
3.4 寄存器映射表——设备的"字典"
寄存器映射表,说白了就是设备的说明书。它告诉你哪个地址存了什么数据,是什么类型,能不能读写。
咱们风电里常见的寄存器类型:
- 线圈(0x): 开关量输出,比如启动/停止风机
- 离散输入(1x): 开关量输入,比如急停按钮状态
- 输入寄存器(3x): 只读模拟量,比如风速、温度
- 保持寄存器(4x): 可读写模拟量,比如设定功率
我见过最坑的一次,是某厂家把风速数据放在3x寄存器,但文档写成了4x。结果我们按4x去读,读出来的值全是乱的。后来用示波器抓波形,才发现地址对不上。
下面是一个典型的风机寄存器映射表示例:
| 寄存器地址 | 数据类型 | 描述 | 单位 | 读写 |
|---|---|---|---|---|
| 40001 | UINT16 | 发电机转速 | rpm | 只读 |
| 40002 | FLOAT32 | 有功功率 | kW | 只读 |
| 40010 | UINT16 | 风机状态 | - | 只读 |
| 40100 | UINT16 | 功率设定值 | kW | 读写 |
注意:FLOAT32类型在Modbus里占2个寄存器(4字节),字节序有高低位之分。有的设备用大端,有的用小端。我曾经因为字节序搞反,读出来的功率值大了256倍,差点把风机烧了。
3.5 实战:用Python读取风机数据
咱们写个简单的例子,用Python的modbus库读取风机转速:
from pymodbus.client import ModbusTcpClient
# 连接风机PLC
client = ModbusTcpClient('192.168.1.100', port=502)
client.connect()
# 读取保持寄存器40001
result = client.read_holding_registers(0, 1, unit=1)
if not result.isError():
speed = result.registers[0]
print(f"发电机转速:{speed} rpm")
else:
print("读取失败,检查通讯")
client.close()
这段代码看着简单,但实际项目中要考虑超时、重连、异常处理。我一般会加个while循环,断线自动重连,毕竟风机现场环境恶劣,通讯偶尔中断很正常。
3.6 避坑指南
最后分享几个我踩过的坑:
- 地址偏移: 有些设备寄存器地址从0开始,有些从1开始。文档写40001,实际报文里地址可能是0。这个一定要确认。
- 波特率匹配: RTU通讯时,主站和从站的波特率、数据位、停止位、校验位必须完全一致。我曾经因为校验位设错了,折腾了一下午。
- 多寄存器读取: 连续读取多个寄存器时,注意不要超过设备支持的最大长度。有的设备一次最多读125个寄存器,多了就报错。
好了,Modbus协议的基础就讲到这里。这东西不难,但细节多。你只要把帧结构搞明白,把寄存器映射表看懂,剩下的就是多练。我在现场调试时,经常拿着笔记本蹲在风机塔筒里,一边看报文一边对文档,慢慢就熟了。