第四章:数据采集协议解析:串口通信(RS232/RS485)基础、Modbus RTU协议详解、如何从呼吸机读取实时数据帧

各位同学,欢迎来到第四章。这一章,我们要啃一块硬骨头——数据采集协议。说白了,就是搞明白呼吸机到底是怎么把数据“说”出来的,而我们又该怎么“听懂”。

我刚开始做呼吸机数据采集那会儿,最头疼的就是这个环节。设备接上了,灯也亮了,但就是读不到数据。后来才发现,不是协议没配对,就是字节顺序搞反了。嗯,这些坑,今天咱们一个一个填上。

4.1 串口通信基础:RS232 与 RS485

呼吸机这类医疗设备,最常用的通信接口就是串口。别被 RS232、RS485 这些名字吓到,它们本质上就是两种不同的“说话方式”。

4.1.1 RS232:点对点的老将

RS232 是最经典的串口标准。它的特点是:

  • 传输距离短:一般不超过 15 米。我在医院机房调试时,经常得把电脑搬到呼吸机旁边,就是因为线长了信号就飘。
  • 速率不高:常见的是 9600、19200、115200 bps。呼吸机数据量不大,9600 就够用。
  • 信号电平:用 ±12V 表示逻辑 0 和 1。所以不能直接连单片机,得用 MAX232 这类芯片转换。
  • 全双工:可以同时收发数据。

关键参数速查表

参数RS232RS485
传输方式单端差分
最大距离约15米1200米
最大节点数1对1最多256个
常用速率9600-1152009600-115200
抗干扰能力

4.1.2 RS485:远距离多节点的利器

RS485 就厉害多了。它用差分信号传输,抗干扰能力特别强。我在一个 ICU 项目中,呼吸机放在病房,采集主机放在护士站,中间隔了 50 多米,用的就是 RS485。

RS485 的几个要点:

  • 半双工:同一时间只能发或收。需要程序控制收发切换。
  • 总线拓扑:所有设备挂在一根总线上。注意要加终端电阻,一般是 120Ω。
  • 地址寻址:每个设备有唯一地址。主机轮询,从机应答。

我的经验:RS485 布线时,一定要用双绞线。我见过有人用普通平行线,结果 20 米外数据全是错的。还有,屏蔽层单端接地,别两头都接,否则会形成地环路。

4.2 Modbus RTU 协议详解

Modbus 是工业领域最通用的协议之一。呼吸机厂商也特别喜欢用 Modbus RTU。为什么?因为它简单、可靠、占用资源少。

4.2.1 协议帧结构

Modbus RTU 的每一帧数据,就像一封信。结构如下:

| 地址码 (1字节) | 功能码 (1字节) | 数据区 (N字节) | CRC校验 (2字节) |

举个例子,读取呼吸机当前潮气量:

  • 地址码:0x01(呼吸机地址)
  • 功能码:0x03(读取保持寄存器)
  • 数据区:起始地址 + 读取数量
  • CRC:循环冗余校验,保证数据没被干扰

常见功能码

功能码含义用途
0x01读取线圈状态读取开关量
0x02读取离散输入读取状态位
0x03读取保持寄存器读取模拟量(最常用)
0x04读取输入寄存器读取只读参数
0x06写单个寄存器设置参数
0x10写多个寄存器批量设置

4.2.2 数据模型与寄存器映射

呼吸机里的数据,都映射到 Modbus 的寄存器空间。比如:

  • 寄存器 0x0000:潮气量(单位 mL)
  • 寄存器 0x0001:呼吸频率(单位 次/分)
  • 寄存器 0x0002:气道压力(单位 cmH₂O)
  • 寄存器 0x0003:氧浓度(单位 %)

每个寄存器是 16 位(2 字节)。如果数据超过 16 位,比如 32 位的累计值,就会占用连续两个寄存器。

注意字节序:Modbus RTU 默认是大端模式(Big-Endian),即高字节在前。但有些呼吸机厂商会改成小端。我曾经被这个坑过——读出来的潮气量是 65535,后来才发现是字节序反了。

4.3 实战:从呼吸机读取实时数据帧

理论讲完了,咱们来点实际的。下面是我写的一个 Python 示例,演示如何通过串口读取呼吸机的实时数据。

4.3.1 准备工作

你需要:

  • 一台支持 Modbus RTU 的呼吸机(或模拟器)
  • USB 转 RS232/RS485 转换器
  • Python 环境 + pymodbus 库

安装 pymodbus:

pip install pymodbus

4.3.2 读取潮气量和呼吸频率

from pymodbus.client import ModbusSerialClient

# 初始化串口客户端
client = ModbusSerialClient(
    method='rtu',
    port='COM3',        # 根据实际情况修改
    baudrate=9600,
    bytesize=8,
    parity='N',
    stopbits=1,
    timeout=1
)

# 连接设备
if client.connect():
    print("连接成功!")
    
    # 读取保持寄存器,从地址0开始,读2个寄存器
    result = client.read_holding_registers(
        address=0, 
        count=2, 
        slave=1
    )
    
    if not result.isError():
        tidal_volume = result.registers[0]  # 潮气量
        resp_rate = result.registers[1]     # 呼吸频率
        print(f"潮气量: {tidal_volume} mL")
        print(f"呼吸频率: {resp_rate} 次/分")
    else:
        print("读取失败:", result)
    
    client.close()
else:
    print("连接失败,请检查串口设置")

4.3.3 连续采集与数据解析

实际项目中,我们需要连续采集。我习惯用一个循环,每秒读一次:

import time
import struct

def parse_ventilator_data(registers):
    """解析呼吸机数据帧"""
    data = {}
    # 假设寄存器映射如下:
    # 0: 潮气量 (uint16)
    # 1: 呼吸频率 (uint16)
    # 2-3: 累计通气量 (uint32, 大端)
    
    data['tidal_volume'] = registers[0]
    data['resp_rate'] = registers[1]
    
    # 解析32位值
    combined = (registers[2] << 16) | registers[3]
    data['total_volume'] = combined
    
    return data

# 连续采集
client = ModbusSerialClient(method='rtu', port='COM3', baudrate=9600)
client.connect()

try:
    while True:
        result = client.read_holding_registers(0, 4, slave=1)
        if not result.isError():
            data = parse_ventilator_data(result.registers)
            print(f"时间: {time.strftime('%H:%M:%S')}, "
                  f"潮气量: {data['tidal_volume']} mL, "
                  f"呼吸频率: {data['resp_rate']} bpm, "
                  f"累计通气量: {data['total_volume']} L")
        time.sleep(1)
except KeyboardInterrupt:
    print("采集停止")
finally:
    client.close()

避坑指南:我曾经遇到一个情况,呼吸机每隔 5 秒才更新一次寄存器。我每秒读一次,读到的全是旧数据。后来加了时间戳比对,才发现这个问题。建议先读几次,观察数据变化规律。

4.4 常见问题与调试技巧

做串口通信,难免遇到问题。我总结了几条经验:

  1. 先确认物理连接:用示波器或逻辑分析仪看 TX/RX 引脚有没有波形。没有波形,说明硬件没通。
  2. 用串口助手验证:先用 SSCOM 或 Putty 发一条 Modbus 命令,看呼吸机有没有回复。能回复,说明协议没问题。
  3. 检查 CRC 校验:很多问题出在 CRC 计算错误。可以用在线 CRC 计算器验证。
  4. 注意超时时间:呼吸机响应时间可能比较慢,我一般设 500ms 到 1s 的超时。
  5. 日志记录:把原始数据帧打印出来,方便排查。比如:
def hex_dump(data):
    return ' '.join(f'{b:02X}' for b in data)

# 发送前打印
print(f"发送: {hex_dump(request)}")
# 接收后打印
print(f"接收: {hex_dump(response)}")

重要提醒:医疗设备的数据采集,一定要确保不影响设备正常运行。我建议先用模拟器测试,确认无误后再接真实设备。另外,采集频率不要太高,一般 1-2 秒一次就够了,避免给呼吸机增加负担。

好了,这一章的内容就到这里。串口通信和 Modbus 协议,是呼吸机数据采集的基石。你想想看,掌握了这些,市面上 80% 的医疗设备你都能搞定。下一章,我们会把这些数据存到数据库里,并搭建一个 Web 监控界面。到时候,你就可以在手机上实时查看呼吸机数据了。

记住,实践出真知。找个呼吸机模拟器练练手,比看十遍书都管用。有问题随时交流,咱们下章见。