news 2026/10/15 15:12:15

工业一体机上用Python做Modbus多设备轮询采集

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
工业一体机上用Python做Modbus多设备轮询采集

工位终端上线之后,第一件要做的活通常是把下面几台设备的数采上来。现场最常见的是 Modbus:电表、温控器、PLC、变频器、称重仪表,很多都支持 Modbus RTU 或者 Modbus TCP。这篇给一套能直接用的 Python 实现,跑在工业一体机的 Linux 系统上,做多设备轮询采集,并且带断线重连和超时保护。文中示例基于 pymodbus 3.x,实际版本以你环境里的接口为准。

## 一、环境准备

工业一体机上装的是 Linux(Ubuntu 或者 Debian 都行),先建虚拟环境,别直接往系统 Python 里装。

```bash
sudo apt update
sudo apt install -y python3-venv python3-pip
python3 -m venv /opt/daq/venv
/opt/daq/venv/bin/pip install pymodbus pyserial
```

如果是串口采集(RTU),确认设备节点存在,并给当前用户串口权限:

```bash
ls -l /dev/ttyS0 /dev/ttyUSB0
sudo usermod -aG dialout $USER
```

改完用户组要重新登录才生效。

## 二、配置文件:把点位表抽出来

不要把从站地址和寄存器地址写死在代码里。现场改点位是常事,抽成 YAML 或者 JSON,改完重启服务就行。

`devices.json`:

```json
{
"poll_interval": 2.0,
"devices": [
{
"name": "meter_1",
"mode": "tcp",
"host": "192.168.1.11",
"port": 502,
"unit": 1,
"points": [
{"key": "voltage", "func": "input", "addr": 0, "count": 1, "scale": 0.1},
{"key": "current", "func": "input", "addr": 1, "count": 1, "scale": 0.01}
]
},
{
"name": "temp_ctrl_1",
"mode": "rtu",
"port": "/dev/ttyS0",
"baudrate": 9600,
"parity": "N",
"stopbits": 1,
"bytesize": 8,
"unit": 2,
"points": [
{"key": "pv", "func": "holding", "addr": 100, "count": 1, "scale": 0.1}
]
}
]
}
```

`scale` 是工程量换算系数,很多仪表返回的是整数,除以 10 或者 100 才是实际的电压、温度。

## 三、核心采集循环

关键点有三个:每个设备独立超时、失败不阻塞其他设备、失败后自动重建连接。

```python
import json
import time
import logging

from pymodbus.client import ModbusTcpClient, ModbusSerialClient

logging.basicConfig(
level=logging.INFO,
format="%(asctime)s %(levelname)s %(message)s",
)
log = logging.getLogger("daq")

FUNC_MAP = {
"holding": "read_holding_registers",
"input": "read_input_registers",
}


def build_client(dev):
if dev["mode"] == "tcp":
return ModbusTcpClient(dev["host"], port=dev["port"], timeout=1)
return ModbusSerialClient(
port=dev["port"],
baudrate=dev["baudrate"],
parity=dev.get("parity", "N"),
stopbits=dev.get("stopbits", 1),
bytesize=dev.get("bytesize", 8),
timeout=1,
)


def read_device(dev):
"""读一台设备的所有点位,失败返回 None"""
client = build_client(dev)
if not client.connect():
log.warning("connect failed: %s", dev["name"])
return None

values = {}
try:
for pt in dev["points"]:
method = getattr(client, FUNC_MAP[pt["func"]])
rr = method(pt["addr"], count=pt["count"], slave=dev["unit"])
if rr.isError():
log.warning("read error: %s.%s", dev["name"], pt["key"])
return None
raw = rr.registers[0]
values[pt["key"]] = round(raw * pt.get("scale", 1), 3)
except Exception as exc: # noqa: BLE001
log.warning("exception on %s: %s", dev["name"], exc)
return None
finally:
client.close()

return values


def main():
with open("devices.json", "r", encoding="utf-8") as fp:
cfg = json.load(fp)

interval = float(cfg.get("poll_interval", 2.0))
cycle = 0

while True:
cycle += 1
for dev in cfg["devices"]:
data = read_device(dev)
if data is None:
# 单台失败不影响其他设备,下一轮自动重连
continue
log.info("[%s] %s", dev["name"], data)
# 这里接你的上报逻辑:写本地库 / 发 MQTT / 写文件
time.sleep(interval)


if __name__ == "__main__":
main()
```

这套写法里,每次采集都新建客户端、用完就关。看着有点笨,好处是连接状态永远干净——上一轮坏掉的连接不会带到下一轮。轮询周期在秒级以上的场景,这点开销可以忽略。如果你需要更短的周期,再把连接做成常驻并加心跳检测。

## 四、几个现场很容易踩的坑

第一个坑是 unit 和 slave 参数名。pymodbus 3.x 里原来的 `slave` 改成过 `unit`,不同小版本的参数名和位置有差异,报 `TypeError` 的时候先看这个。建议在项目里锁版本,别让自动升级把现场跑崩。

第二个坑是把点位的起始地址按 0 还是按 1 算。很多仪表手册写的是 1-based 地址,代码里用的是 0-based,中间差 1。现象是能读到数,但跟你以为的那个量对不上。第一次接入新设备时,逐个点位和手册核对一遍,别嫌慢。

第三个坑是串口多设备共线。RS485 总线上一串设备,必须保证波特率、校验、停止位一致。总线上如果有一台设备参数不对,可能拖着整条总线一起超时。排查的时候先把其他设备断开,一台一台接。

第四个坑是没有看门狗。采集程序卡死,画面还在转,但数据已经半天没更新了,这种情况最坑。Linux 上可以配 systemd 的 `WatchdogSec`,让服务异常时自动重启;或者在主循环里打时间戳,超时主动退出。

还有一种更隐蔽的情况:程序没死,但读到的值是"卡住的旧值"。原因是异常处理写得太宽,读到错误以后没有把值标记为无效,上层系统还在拿上一轮的数显示。处理办法是每个点位带一个时间戳和质量位,超过若干个轮询周期没更新就标记为坏质量,让上层知道这条数据不可信。工业现场很怕"看起来有数据",因为没人会去核对,等到发现的时候可能已经跑了很久。

再补充一点关于日志的。现场排查全靠日志,所以日志要能定位到具体设备和具体点位。上面示例里每行都带了设备名,这不够,建议把点位 key 也打出来,并且把原始寄存器值和换算后的值都记一份。电压这种量,如果显示 220 而实际是 22,只有对比原始值才能立刻判断是 scale 写错了还是设备真返回了异常值。日志文件别无限增长,用 logging 的 RotatingFileHandler 做轮转,或者交给系统日志管理。

```ini
# /etc/systemd/system/daq.service
[Unit]
Description=Modbus DAQ
After=network.target

[Service]
ExecStart=/opt/daq/venv/bin/python /opt/daq/main.py
Restart=always
RestartSec=3
WatchdogSec=30
Type=notify

[Install]
WantedBy=multi-user.target
```

## 五、数据怎么往上走

采集完本地聚合,再往上推。推荐在本地留一份缓存,断网的时候先存,网络恢复补传。上层对接方式按现场选:MQTT 适合推给物联网平台,HTTP 适合对接自有系统,OPC UA 适合跟既有的 SCADA 打通。

有一点要提醒:别让采集线程和执行机构控制逻辑挤在同一个进程里抢资源。工业一体机如果同时要跑 HMI 和采集,建议把采集做成独立服务,通过本地 socket 或者共享文件交给 HMI 显示。这样谁卡住都不会拖垮另一个。

本地缓存的实现方式很多,轻量场景可以先用 SQLite 或者按天切分的文件,把带时间戳的数据落盘;网络恢复后由一个补传任务按时间顺序推送,并去重。要注意缓存盘的空间,长期断网时得有上限和清理策略,别把系统盘写满导致整个设备出问题。

硬件层面,工位终端选型时把串口数量、网口数量、宽温范围这些确认好,能少很多返工。广州微嵌的工业一体机和嵌入式计算机在接口配置上可以按需组合,常见的一体化采集终端配置能直接对上,现场对接时省事一些。

## FAQ

**问:pymodbus 报参数错误,提示 slave/unit 不对怎么办?**
答:这是版本差异导致的。pymodbus 3.x 中部分接口的参数名经历调整,建议锁定一个版本(在 requirements 里写死),并按该版本文档核对参数名,不要依赖自动升级。

**问:一台设备掉线会不会影响其他设备的采集?**
答:按上面的写法不会。每台设备独立建立连接、独立超时,失败返回 None 后跳过,下一轮自动重连。需要注意的是串口总线场景,一台设备参数不一致可能拖累整条总线。

**问:读回来的数值和实际值差一个倍数,是什么原因?**
答:多数是工程量换算系数没设对,需要在点位表里配置 scale。另外要确认寄存器地址的起始基准(0-based 还是 1-based),这两种情况都会导致数值对不上。

**问:怎么防止采集程序卡死不重启?**
答:可以用 systemd 的 Restart=always 配合 WatchdogSec 做看门狗;也可以在主循环里记录时间戳,超过阈值主动退出让服务重启。不要依赖人工发现程序无响应。

**问:采集终端的硬件选型有什么要注意的?**
答:按现场确认串口和网口的数量、是否需要宽温、安装方式和散热形式。广州微嵌的工业一体机和工业平板电脑在接口和尺寸上可以按需组合,配套多系统支持,做这类采集终端时适配起来省事一些。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/15 15:11:29

计算机系统核心组件与工作原理详解

1. 计算机系统基础概念解析计算机系统是现代信息技术的核心载体,理解其基本组成是每一位技术从业者的必修课。作为一名在计算机体系结构领域工作多年的工程师,我经常遇到刚入行的同事对计算机基础概念存在各种误解。今天我们就从最基础的层面&#xff0c…

作者头像 李华
网站建设 2026/10/15 15:10:52

Rust 1.93增强musl网络功能,提升云原生与系统编程性能

1. Rust 1.93版本更新概述Rust 1.93版本带来了一项重要改进——对musl标准库的网络功能进行了显著增强。作为系统级编程语言,Rust与musl的深度整合一直是其跨平台能力的重要支撑。这次更新主要针对网络相关系统调用的封装进行了优化,使得基于musl构建的R…

作者头像 李华
网站建设 2026/10/15 15:10:15

Forge 1.20.1模组开发全流程:环境配置、方块注册与避坑指南

简介:资源为Minecraft 1.20.1版本配套的Forge模组开发工具包(MDK),面向准备在该版本开展模组开发、或希望深入学习Forge工程结构的玩家和Java开发者,无论是初次接触模组开发的新手,还是需要在该版本上进行功…

作者头像 李华
网站建设 2026/10/15 15:10:11

位运算技巧:lowbit原理与应用解析

1. 题目背景与核心思路LeetCode 3314这道题涉及位运算中的经典操作lowbit,这是一个在算法竞赛和面试中频繁出现的技巧。我第一次遇到这个问题是在准备某大厂面试时,当时被要求优化一个统计二进制中1的数量的算法。lowbit操作正是解决这类问题的利器。low…

作者头像 李华
网站建设 2026/10/15 15:09:30

YOLOv5单目测距:从检测框到三维距离的工程实践

简介:本资源面向计算机视觉初学者与自动驾驶、机器人导航方向的开发者,提供一套基于YOLOv5与单目深度估计的距离测算Python实现方案,帮助在无额外深度硬件的情况下从单张彩色图像中推断目标距离。压缩包共132个文件,约13.87MB&…

作者头像 李华