项目标题里写了“工业物联网实战”,这个项目我听了很多次“老旧货梯”就头疼。但真正上手做下来,发现这里面的门道其实比新建一个智能产线还多。某制造企业有一台用了快二十年的货梯,五层楼,继电器接触器控制,每天的物料转运全靠人工按按钮。后来车间上了几台自主移动机器人,结果卡在乘梯这个环节,一卡就是半年。今天聊聊我和团队如何用声明式梯控改造的思路,把这台“老家伙”变成能听懂机器人说话的智能电梯,重点拆解架构实现和协议重构这两个环节,希望能给正在跟老旧设备较劲的同仁一些参考。
这个项目适合谁看?如果你是负责自动化产线改造的工程师,正在为AGV乘梯发愁,或者你需要把老设备接入工业物联网平台,那么这篇内容应该能帮上忙。我会把从信号采集到接口设计,从代码实现到现场调试的全部过程还原出来,包括一些踩坑记录。
1. 项目背景与需求拆解:老旧货梯与机器人乘梯的隐形障碍
1.1 老旧货梯的现状:继电器时代的产物
这台货梯是某制造企业2005年左右安装的。所谓继电器接触器控制,就是所有逻辑都靠物理继电器和接触器的通断组合来实现。比如你按了一下“上行”按钮,线圈得电,触点吸合,电机开始转;到了一层,机械限位开关被撞开,继电器断开,电机停。这套系统没有PLC程序,更没有通信接口,只有一堆布满灰尘的端子和线排。
听起来很原始,但它在当年是很成熟的技术,稳定、抗干扰、维护起来也就万用表加螺丝刀的事。问题在于,机器人不会用肢体去按按钮,也无法感知电梯门有没有开。想让机器人自主乘梯,就必须要让电梯具备对外交互能力。
我们盘点了这台梯的“家底”,几个典型特征:
- 控制回路电压是AC110V,触点信号多,公共端复杂。
- 楼层检测靠各楼层的干簧管,轿厢顶部有随行电缆。
- 没有任何现成的总线接口,连调试用的串口都没有。
- 维保人员只懂电气原理图,不懂网络通信。
所以第一步不是写代码,而是搞清楚这台电梯现有的信号有哪些、哪些能采集、哪些必须要避开。
1.2 需求拆解:从“能按按钮”到“声明式调度”
项目目标很明确:让机器人在不需要人类帮助的情况下,自主呼叫电梯、进入轿厢、选择目标楼层、到达后出梯。最初设想过一个方案,用机械臂去按按钮。但机械臂需要定位按钮位置,而且不同梯型的按钮布局不一样,可靠性低,维护成本高,很快就放弃了。
深入思考后发现,机器人真正需要的不是模拟人手,而是“把请求告诉电梯系统,等待结果”。这就引出了“声明式调度”的概念:机器人只需要声明“我从3层到7层,很重要”,梯控系统负责分解成一系列电梯控制动作。与之相对的是“命令式”方式:机器人要一条条执行“按下上行按钮”、“等待开门”、“进入后按楼层7”、“确认电梯到达7层”。
我在实际设计前做了一张对比表,用来跟团队和客户对齐:
| 对比维度 | 命令式 | 声明式 |
|---|---|---|
| 控制粒度 | 精确到每个物理动作 | 精确到任务目标 |
| 容错能力 | 任何一步失败都需要人工恢复 | 状态机自动超时与复位 |
| 适配成本 | 不同电梯需要重写操作逻辑 | 只需适配底层信号,上层不变 |
| 故障追踪 | 往往需要逐条日志分析 | 状态转换清晰,定位较快 |
最终选择了声明式。这也成了整个项目的核心设计理念:让复杂逻辑下沉,把简洁留给上层。后续选型、协议设计,都是围绕这个理念展开的。
2. 技术选型与整体架构:为什么声明式架构更可靠
2.1 协议重构前必须想清楚的事
很多人一听“协议重构”就觉得要把电梯整个控制系统推倒重来,其实恰恰相反。老旧货梯的安全回路、机械结构、电机驱动系统都属于特种设备范畴,不能随意改动。我们说的协议重构,是把电梯对外暴露的物理触点信号(按钮、指示灯、传感器)重新封装成标准化消息,让上层系统能读懂、能操作,而电梯本身的核心控制逻辑保持不变。
这背后的原因很现实:改造成本、审批周期、风险控制。如果替换整台电梯控制器,相当于做一次特种设备改造,不仅费用高,而且需要重新验收,可能影响生产。旁路改造则是在电梯原有的信号回路基础上,用高阻抗的干接点隔离采集,不做任何短接或切断,最大限度降低安全风险。
改造前需要明确采集哪些信号,我们列了一份信号清单:
- 当前楼层:从轿厢平层开关或楼层编码器读取。
- 运行方向:从上行/下行接触器辅助触点读取。
- 门状态:开门到位开关和关门到位开关。
- 门锁状态:串联在安全回路中的门锁触点。
- 内呼按钮:轿厢内各楼层选层按钮。
- 外呼按钮:各层厅门的上行/下行召唤按钮。
这些信号通过隔离模块接入IO网关。网关向上提供Modbus TCP接口,用于控制;同时将状态通过MQTT发布到边缘服务器。
2.2 分层架构设计与核心组件
整个系统分四层,每一层只负责单一职责,避免上层逻辑与硬件深度耦合:
应用层:机器人调度系统,它只关心“任务请求”,不关心电梯型号。机器人会发送“从A层到B层”的请求,并订阅电梯状态事件。
声明式接口层:这是核心,通常运行在边缘网关或工控机上。它维护任务队列、解析请求、控制适配层完成动作,并向应用层提供REST API和MQTT主题。
适配层:负责把操作指令转化为物理动作。比如“呼叫上行”对应某一路继电器的闭合,“选择3层”对应内呼板上对应的继电器动作。有些方案还会用PLC来做这一层,我们手头是边缘网关加IO模块,所以直接用了IO模块的Modbus寄存器。
物理层:继电器阵列、光耦隔离模块、传感器触点、接触器辅助触点。这一层是整个系统里“最容易脏”的部分,也是最需要谨慎对待的部分。
这里给个简单图示:
[机器人调度系统] <-HTTP/MQTT-> [梯控服务(Edge)] <-Modbus TCP/GPIO-> [IO模块] <-干接点-> [电梯原控制系统]每层的修改点有明确边界。比如机器人接入时,只需要调用梯控服务的接口;电梯换维保单位时,只影响适配层,不影响应用层。
2.3 声明式接口的设计思路
接口设计是决定这个项目好不好用的关键。我们定义了两个核心接口:任务请求和状态订阅。
任务请求采用JSON格式,示例如下:
{ "request_id": "b7a1f8e2", "call_type": "dispatch", "from_floor": 3, "to_floor": 7, "priority": "high", "timeout_sec": 60 }其中“call_type”统一为“dispatch”,表示这是一个完整的派梯请求,梯控服务会自行处理上行呼叫、等待开门、内呼登记、到达确认等。机器人的任务就是发送这个请求,然后等待状态事件。
状态事件同样用JSON:
{ "car_id": "LIFT-01", "floor": 3, "direction": "up", "door_state": "open", "fault_code": 0 }为什么选择MQTT而不是直接让机器人读Modbus寄存器?因为Modbus是点对点协议,如果三台机器人同时读一个寄存器,需要自己做并发控制,而且数据含义不直观。MQTT是发布订阅模式,一台电梯就是一个主题,机器人按需订阅,逻辑清晰,容错性也更好。REST API用于低频管理操作,比如更新电梯配置、查询故障记录。
有一个设计细节值得说:接口里没有暴露任何与电梯操作强相关的字段,比如“按下上行按钮”、“打开内呼板”这些命令在应用层是不可见的。这就保证了上层系统永远不需要知道电梯的物理细节,真正做到声明式。
提示:接口版本化也很重要。我们在URL路径中加入/v1,后续改造不同梯型时,通过新版本扩展,不会影响已经上线的机器人。
3. 实操过程:从继电器到总线到声明式接口的实现细节
3.1 改造前准备:收集电梯资料与制定安全策略
这个环节我们花了两周,比写代码的时间还多。安全永远是第一位的。第一次进场时,我们先找来电梯的电气原理图,跟厂家维保工程师逐页翻,标出所有跟按钮、指示灯、传感器相关的回路。特别注意安全回路,比如门锁回路、急停回路,这些是绝对不能动的地方。
为了不干扰原控制逻辑,所有外部采集信号都用干接点隔离模块。干接点是一种无源触点,通过光耦隔离,与电梯原回路完全电隔离。控制端则使用带有中间继电器的驱动板,驱动板线圈电压选DC24V,触点容量要求较高,至少AC120V/2A,保证能驱动原控制回路。
必备工具列了一个清单:万用表、示波器、绝缘电阻表、DC24V开关电源、信号隔离模块、继电器驱动板、IO集线器、一台装了Docker的轻量边缘网关。现场还拉了一根独立的网络线,单独组成梯控内部局域网,避免与办公网络冲突。
注意:任何对安全回路的改动,哪怕只是加一根线,都必须先与电梯维保单位确认,并且做好封线记录。特种设备监管不是开玩笑的。
安全策略上,我们做了三件事。第一,增加独立的急停开关,放在电梯控制柜旁边,紧急情况下能直接让梯控系统释放全部控制权。第二,所有软件中都加入“看门狗”逻辑,网关死机时自动断开继电器驱动,防止误动作。第三,改造期间全程有电梯维保工程师在旁监护,每次通电测试前都要口头确认一遍。
3.2 核心实现:梯控服务端的功能代码解析
我们的梯控服务跑在一台边缘网关上,操作系统是轻量Linux环境,业务代码用Python编写。Python的优点在于生态好、搞Modbus和MQTT都方便,缺点是不适合做硬实时控制,但梯控这个场景响应时间在几百毫秒完全够用。
下面是适配层的核心代码片段,演示如何读取楼层和发送外呼指令:
import json import time import modbus_tk import modbus_tk.defines as cst from modbus_tk import modbus_tcp import paho.mqtt.client as mqtt class ElevatorAdapter: def __init__(self, host, port, unit_id): self.master = modbus_tcp.TcpMaster(host, port) self.master.set_timeout(1.0) self.unit_id = unit_id self.mqtt = mqtt.Client() self.mqtt.connect("127.0.0.1", 1883, keepalive=60) def read_floor(self): # 读取输入寄存器地址0,表示当前楼层 res = self.master.execute(self.unit_id, cst.READ_INPUT_REGISTERS, 0, 1) return res[0] def request_up(self, floor): # 触发指定楼层的上行召唤按钮 # 这里以IO模块的线圈地址对应各楼层举例 coil_addr = 10 + floor * 2 self.master.execute(self.unit_id, cst.WRITE_SINGLE_COIL, coil_addr, 1) time.sleep(3) # 模拟按钮保持时间 self.master.execute(self.unit_id, cst.WRITE_SINGLE_COIL, coil_addr, 0) def publish_state(self): floor = self.read_floor() state = {"car_id": "LIFT-01", "floor": floor} self.mqtt.publish("iot/elevator/state", json.dumps(state))当然这只是示意代码,实际工程中还有大量防御性处理,比如异常重连、超时重试、状态缓存。
为了把IO映射和业务逻辑解耦,我们把所有硬件地址写在一个YAML配置里。现场调整某层的传感器接线时,只改配置,不用改Python代码:
# io_mapping.yaml io_module: host: 192.168.1.20 port: 502 unit_id: 1 coils: up_call_2: 0 up_call_3: 2 down_call_2: 1 down_call_3: 3 internal_call_2: 4 internal_call_3: 5 inputs: floor_1: 0 floor_2: 1 gate_open: 2 gate_closed: 3声明式接口层则是一个Flask服务,接收机器人发来的JSON请求,解析后调用适配层,并返回请求ID。同时启动一个状态机,跟踪当前电梯在哪个阶段:空闲、响应召唤、等待进梯、运行中、到达输出。
状态机是声明式架构的核心,它把任务拆分为明确定义的有限状态,每个状态都有超时时间。比如“响应召唤”状态超过20秒没有检测到门打开,就自动复位,取消本次请求并上报失败。这个机制很重要,能避免机器人卡在电梯门口。
3.3 协议重构:把“点对点信号”封装成语义动作
协议重构听起来高大上,落到实地就是做一张映射表:物理信号和语义动作之间的一一对应。比如原来维保师傅要靠万用表测量某两个端子判断电梯是否到1层,现在我们把平层传感器的信号采集上来,在代码里封装成“floor_reached(1)”这样一个函数。
以“呼叫上行”为例,原来按一下按钮是硬接线动作,现在变成三步:梯控服务收到“dispatch”请求后,先从状态机判断电梯是否可用;然后驱动对应楼层的外呼继电器,保持一定时间再释放;同时监听电梯状态,直到电梯到达目标层并且门打开。
这里有两个细节值得分享。第一个是去抖:继电器和触点都有机械抖动,直接读取信号可能由“0”变成“1”再变“0”,造成误判。我们用软件滤波,连续采样50次,每5毫秒一次,如果50次中超过40次为高,才认为信号到位。第二个是互锁:有些老旧电梯的上下行继电器不能同时得电,否则可能烧电机,所以我们在驱动逻辑中加了“三秒内不允许切换方向”的限制。
我把这部分封装成协议转换模块,输入是标准语义指令,输出是实际的IO操作。这样当电梯维保单位更换某种继电器时,只需要修改这个模块,不用动上层接口。
4. 调试与排查:现场最常踩的坑
4.1 继电器粘连与信号干扰
改造进入实测阶段时,出现了一个非常隐蔽的问题:电梯每隔几个小时就会自己上一趟,没人按按钮,机器人也是待命状态。开始怀疑软件逻辑,查了很久没找到原因,最后带着万用表去控制柜排查,发现是驱动外呼按钮的继电器触点粘住了。原来继电器在断开瞬间会产生电弧,触点表面形成碳化层,用久了就粘连在一起,导致外呼按钮回路一直被接通。
解决方法是双管齐下。软件上增加互锁逻辑,比如驱动完上行外呼后设置3秒冷却时间,并且在每次驱动前检测线圈电流,如果异常就报警。硬件上替换成带灭弧功能的继电器,同时在继电器触点并联RC吸收电路,减少电弧。
这个经历让我明白,梯控改造里最“原始的”硬件反而是最容易出问题的地方,不要以为上了物联网就万事大吉。
4.2 状态感知不准导致的丢单与卡死
另一个高频故障是机器人进入电梯后一直卡在轿厢里,系统没确认到达目标楼层。排查发现,一层楼装了平层感应器,但它发出的信号有时会丢脉冲,原因可能是感应器安装位置偏了,或者磁铁退磁。
我们从两个维度做了修正:
- 双通道确认:不只靠平层信号,还结合楼层编码器计算脉冲数。当两者结果一致时才认为到达目标楼层。
- 超时强制复位:如果连续10秒没有状态变化,就主动收梯门,并尝试重置请求状态。原则是宁可不动作,不能卡死。
加了这个机制后,丢单率明显下降,机器人乘梯成功率从最开始不到80%拉到了99%以上。
4.3 常见问题速查表
调试期整理了下面这张表,基本涵盖了能遇到的典型场景:
| 问题现象 | 排查点 | 解决方案 |
|---|---|---|
| 电梯无响应 | 继电器驱动是否动作、IO模块通信是否正常 | 检查Modbus连接和线圈状态 |
| 楼层读数跳变 | 传感器损坏或电压不稳 | 用示波器抓波形,更换平层开关 |
| 门开了机器人没进 | 机器人路径规划偏差,或状态上报顺序不对 | 增加“door_open”事件,机器人收到后再移动 |
| 多台机器人同时唤梯 | 任务冲突,状态机串扰 | 引入请求队列,同一时间只处理一个任务 |
| 网关重启后电梯不受控 | 恢复逻辑不完整 | 配置持久化,重启后自动查询电梯当前状态 |
这张表后来直接变成了运维手册,减少了大量重复沟通。
5. 效果评估与经验总结:梯控改造的价值与边界
5.1 改造后的性能指标与实际收益
改造交付后,客户做了两周的数据对比。机器人乘梯成功率稳定在99.2%,人工干预次数下降90%,平均等待时间从原来的人工呼叫3分钟降到45秒。这些数据是怎么采的?机器人任务日志里记录了每次发请求到实际到达的时间,梯控服务也记录了每次请求的响应时间、等待时间、是否成功。
投入产出比也很直观。这次项目主要成本是边缘网关、IO模块、继电器、传感器和人工调试费用,远低于更换一部新电梯。而且梯控系统上线后,物料搬运环节不再依赖人,夜班也可以靠机器人持续工作,节省的人力成本是长期的。
5.2 安全与运维的进一步思考
梯控系统接入工业物联网后,安全成了不可回避的话题。我强烈建议任何做类似改造的人,都要考虑“影子模式”和“点名机制”。影子模式是说梯控系统在默认情况下只监听,不发指令,只有当收到合法请求且满足安全条件时,才短暂接管控制权。点名机制则是让梯控系统每隔30秒向机器人调度系统发送心跳,如果心跳中断,机器人就不能再发指令,避免产生“瞎子操作”场景。
运维上,由于老旧电梯的继电器和触点都属于易损件,我们设置了每月巡检提醒,建议操作人员用万用表检查关键继电器触点电阻,如果比初始值高20%就要更换。梯控服务侧也要定期看日志,关注有没有异常重连、状态机卡死等现象。
5.3 后续扩展方向:多梯联动与预测性维护
这个项目虽然针对的是单台货梯,但架构其实是可扩展的。下一步很自然的演进是多梯联动。当厂里有三台甚至五台电梯时,梯控服务可以收集所有电梯的状态,结合每台机器人的任务优先级做群控分配。声明式接口的好处在这里就体现出来了,上层调度系统完全不需要知道哪台梯符合机器人所需空间,梯控系统会告诉你哪个电梯响应最快、门最合适。
另一个方向是预测性维护。我们的梯控服务记录了每天电梯的运行次数、每次召唤到到达的时间、继电器动作次数。这些数据积累下来,可以用简单的统计模型来判断继电器是否快要磨损。比如某层外呼按钮被频繁触发,触发次数超过阈值后,系统会提前建议维保人员检查该层信号触点。
按我的经验,老旧设备改造的魅力就在于:你越尊重原来的物理世界,越理解信号的本质,上层数字化就越扎实。梯控只是一小步,但它背后打通的数据链,能让整座工厂的生产调度上一个台阶。如果你也在做类似的改造,记住一个基本原则:先摸清楚每一根线是干什么的,再谈云端联动。