1. 伺服压机控制系统架构总览
1.1 为什么伺服压机需要上位机与下位机分工
伺服压机在工业现场干的活,说白了就是用伺服电机精确控制压头的位移、速度和压力,把工件压到指定位置或者压到指定力值。这个过程中,位置精度经常要求到0.01mm级别,压力控制精度要求到满量程的千分之几,响应周期通常在毫秒级。这种活如果让一台普通工控机或者PC直接去干,基本没戏——操作系统调度抖动、通信延迟、多任务抢占,随便一个环节都能让压装曲线变成一团乱麻。
所以行业里普遍的做法是拆成两层:下位机负责硬实时控制,跑在PLC、运动控制器或者专用伺服驱动器上,直接管电机使能、位置环、速度环、力矩环,以及高速IO的采集和输出;上位机负责参数配置、工艺配方管理、曲线显示、数据存储、MES对接、报警记录这些非实时或者弱实时的活。两者之间通过通信链路交换数据,常见的就是Modbus RTU、Modbus TCP、EtherCAT、Profinet、CANopen这些。
这个分工的核心逻辑就一句话:让快的归快,让慢的归慢。下位机不需要花哨的界面,它要的是确定性;上位机不需要硬实时,它要的是灵活性和可扩展性。把这两者混在一起,要么实时性保不住,要么界面卡成幻灯片。
1.2 上位机与下位机的职责边界划分
职责边界这件事,我在实际项目里踩过不少坑。刚开始做的时候,总想把逻辑判断都塞到上位机,觉得改起来方便,结果通信一抖动,压装动作就出问题。后来慢慢总结出一套划分原则,基本能覆盖大多数伺服压机场景。
下位机该管的事:
- 伺服电机的使能、急停、限位保护
- 位置环、速度环、力矩环的闭环控制
- 压装过程中的高速数据采集(比如每1ms采一次位置和压力)
- 多段压装曲线的执行(快下、慢下、保压、回程)
- 与安全回路相关的硬线信号处理
- 通信断线时的本地安全策略(比如保持当前位置或者缓回原点)
上位机该管的事:
- 工艺配方的编辑、存储、下发
- 压装曲线的实时显示和历史回放
- 生产数据统计、SPC分析、报表导出
- 用户权限管理、操作日志记录
- 与MES、ERP等上层系统的数据对接
- 多台压机的集中监控
边界划清楚之后,通信协议的设计就有了依据。下位机只暴露必要的寄存器或者对象字典,上位机通过读写这些寄存器来间接控制。千万不要让上位机直接去控制电机换向或者修改PID参数,这些必须由下位机在本地闭环里完成。
1.3 常见架构风格对比与选型建议
伺服压机控制系统的软件架构,常见的有三种风格,各有各的适用场景。
| 架构风格 | 典型实现 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|
| 单体式 | 下位机跑全部逻辑,上位机只做HMI | 实时性最好,通信简单 | 扩展性差,配方管理弱 | 单机设备,工艺固定 |
| 分层式 | 下位机管控制,上位机管数据 | 职责清晰,扩展性好 | 通信协议设计复杂 | 大多数伺服压机 |
| 微服务式 | 多个上位机服务分别管配方、曲线、MES | 灵活,可独立部署 | 部署复杂,调试麻烦 | 大型产线,多机协同 |
我个人的经验是,90%的伺服压机项目用分层式就够了。微服务式听起来高级,但现场维护成本高,除非是几十台压机联线的汽车零部件产线,否则没必要。单体式适合那种工艺十年不变的老设备改造,但新项目不建议。
选型的时候还要考虑通信方式。Modbus RTU成本低,但速率上不去,适合数据量小的场景;Modbus TCP速率快一些,但实时性还是不如EtherCAT。如果压装曲线要求每0.5ms采一个点,那必须上EtherCAT或者Profinet IRT,Modbus根本扛不住。
2. 下位机核心功能与实现细节
2.1 伺服控制环路的软件实现
下位机的核心就是伺服控制环路。伺服压机通常需要三环控制:电流环、速度环、位置环。电流环在驱动器内部跑,周期通常是几十微秒;速度环和位置环可以在驱动器里跑,也可以在运动控制器里跑,周期一般是1ms或者更短。
软件实现上,位置环的输出是速度指令,速度环的输出是电流指令。对于压装应用,还需要一个压力环,通过压力传感器反馈来调整位置或者速度。压力环的周期可以比位置环慢一些,5ms到10ms都行,因为压力传感器的响应本身就没那么快。
代码层面,下位机通常用IEC 61131-3的ST语言或者C语言写。ST语言可读性好,适合逻辑复杂的工艺段;C语言效率高,适合对周期要求极严的场合。我见过不少项目用ST写主体逻辑,用C写底层驱动和通信中断,两者通过共享内存或者函数接口交互。
一个典型的压装循环,下位机的执行流程是这样的:
- 收到上位机下发的配方号和启动命令
- 检查安全条件(急停、限位、气压等)
- 伺服使能,切换到位置模式
- 快下到接近工件的位置(速度模式,力矩限幅)
- 切换到压力控制或者位置控制,慢速压入
- 到达目标位置或者目标压力后,保压计时
- 保压结束,切换到位置模式回程
- 伺服下使能,上报结果给上位机
这个流程里,第4步到第6步是核心,也是下位机软件最需要打磨的地方。快下转慢下的切换点如果设不好,要么撞工件,要么节拍太长。保压阶段的压力波动如果控制不住,压装质量就不稳定。
2.2 实时数据采集与曲线生成
压装曲线的质量直接决定了工艺分析的可信度。下位机采集数据的周期,我一般建议不低于1kHz,也就是每1ms采一个点。如果压装过程只有200ms,那也就200个点,数据量不大,但足够看出曲线的细节。
采集的数据通常包括:时间戳、位置、速度、压力、力矩、电机电流。这些数据在下位机里先缓存在环形缓冲区里,等压装结束后再打包上传给上位机。不要一边压装一边往上传,通信抖动会影响实时性。
环形缓冲区的大小要算好。假设1kHz采集,压装周期最长5秒,那就是5000个点。每个点如果存6个float,就是120KB。下位机的RAM一般够用,但如果是低端PLC,可能就要压缩一下,比如位置和压力用int32,时间戳用相对值。
曲线生成的时候,上位机拿到的是原始点,需要做滤波和插值。滤波用滑动平均或者低通滤波都行,看噪声情况。插值主要是为了显示平滑,但分析的时候一定要用原始数据,插值后的数据只能看,不能用来算指标。
2.3 通信协议栈的选型与配置
下位机的通信协议栈,Modbus RTU和Modbus TCP是最常见的。Modbus RTU跑在RS485上,接线简单,成本低,但速率一般就115200bps,传大数据量吃力。Modbus TCP跑以太网,速率快,但协议栈本身有开销,实时性不如EtherCAT。
如果只是传配方和结果数据,Modbus RTU完全够用。如果要传实时曲线,建议用Modbus TCP或者直接上EtherCAT。我做过一个项目,压装曲线要求每0.5ms采一个点,用Modbus TCP传,上位机显示的时候明显卡顿,后来换成EtherCAT才流畅。
Modbus的寄存器映射要提前规划好。输入寄存器放只读数据,比如当前位置、当前压力、状态字;保持寄存器放可读写数据,比如目标位置、目标压力、配方号;线圈放开关量,比如启动、停止、复位。映射表一旦定下来,就不要随便改,不然上位机那边要跟着改,容易出错。
注意:Modbus RTU的帧间隔要严格遵守3.5个字符时间,否则从站可能不响应。用C#写上位机的时候,SerialPort的ReadTimeout和WriteTimeout要设好,不然通信断了都不知道。
2.4 安全逻辑与故障处理
下位机的安全逻辑是最后一道防线。急停信号必须硬线接入下位机,不能通过通信传。通信断了,急停还能用;急停断了,通信再好也没用。
故障处理要分等级。一级故障(比如急停、超程、伺服报警)立即停机,切断伺服使能,上报上位机;二级故障(比如通信超时、压力传感器断线)先尝试恢复,恢复不了再停机;三级故障(比如曲线偏差大)只记录报警,不停机,但要在结果里标记。
通信断线的时候,下位机要有本地策略。我一般设成保持当前位置3秒,然后缓回安全位置。如果3秒内通信恢复,就继续执行;恢复不了,就回原点等上位机重新连接。这个策略要在下位机里写死,不能依赖上位机。
3. 上位机软件架构与功能模块
3.1 上位机技术栈选型:C#、Qt还是LabVIEW
上位机的技术栈选型,主要看团队背景和项目需求。C# + WPF是工业上位机的主流,开发效率高,界面好看,Modbus库也成熟。Qt + C++适合跨平台,性能好,但开发周期长。LabVIEW适合测试测量场景,图形化编程,但大型项目维护起来费劲。
我个人的偏好是C# + WPF + Modbus库。C#的异步编程模型很适合通信场景,async/await写起来清爽。WPF的数据绑定和MVVM模式,做实时曲线显示很方便。Modbus库用NModbus或者EasyModbus都行,NModbus更灵活,EasyModbus更简单。
如果项目要求跨平台,那就Qt。Qt的QModbus模块虽然不如NModbus成熟,但基本功能都有。LabVIEW的话,如果团队里有人熟悉,做快速原型可以,但正式项目我还是建议用C#或者Qt。
数据库方面,SQLite适合单机,SQL Server或者MySQL适合多机联网。配方和结果数据量不大,SQLite完全够用。如果要和MES对接,那就上SQL Server,方便做数据同步。
3.2 通信层设计与Modbus封装
通信层是上位机的核心模块,设计好坏直接影响稳定性和可维护性。我一般把通信层分成三层:物理层、协议层、业务层。
物理层管串口或者网口的打开、关闭、参数配置。协议层管Modbus帧的组装和解析。业务层管具体的数据读写,比如读配方、写目标位置、读曲线数据。
C#里封装Modbus,我习惯用接口加实现的方式。先定义一个IModbusService接口,包含ReadHoldingRegisters、WriteSingleRegister、ReadInputRegisters这些方法。然后分别实现ModbusRtuService和ModbusTcpService。业务层只依赖接口,不依赖具体实现,这样切换通信方式的时候不用改业务代码。
public interface IModbusService { Task<ushort[]> ReadHoldingRegistersAsync(byte slaveId, ushort startAddress, ushort count); Task WriteSingleRegisterAsync(byte slaveId, ushort address, ushort value); Task<bool[]> ReadCoilsAsync(byte slaveId, ushort startAddress, ushort count); Task WriteSingleCoilAsync(byte slaveId, ushort address, bool value); }串口通信的时候,读写要加锁,不然多线程同时操作会出问题。Modbus RTU的帧间隔也要注意,两次请求之间至少隔3.5个字符时间。115200bps下,一个字符大概87微秒,3.5个字符就是300微秒左右。实际写代码的时候,我一般加5ms的延时,保险一点。
Modbus TCP的话,注意事务标识符要递增,单元标识符在TCP里其实用不上,但很多设备还是要求填。连接超时和读写超时要设好,不然网络断了程序会卡死。
3.3 配方管理与工艺参数下发
配方管理是上位机最常用的功能。一个配方通常包含:配方号、配方名、快下速度、慢下速度、目标位置、目标压力、保压时间、回程速度、力矩限幅这些参数。
配方存在数据库里,界面上用DataGrid显示,支持增删改查。下发配方的时候,先写参数到保持寄存器,再写配方号,最后写启动命令。顺序不能乱,不然下位机可能用旧参数执行。
参数下发要做校验。比如目标位置不能超过行程范围,目标压力不能超过传感器量程,保压时间不能为负。校验不通过就弹窗提示,不要直接下发。
配方切换的时候,如果压机正在运行,要先停止再切换。不要在线切换配方,除非下位机支持动态参数更新,而且你确认过安全性。
3.4 实时曲线显示与数据存储
实时曲线显示是上位机最考验性能的地方。压装过程中,下位机每1ms采一个点,上位机要实时显示。如果直接用WPF的Polyline,几千个点一刷,界面就卡了。
我的做法是用DrawingVisual或者WriteableBitmap,自己画曲线。数据来了之后,先存到缓冲区,然后用一个定时器(比如50ms)刷新一次界面。刷新的时候只画可见区域,超出范围的点不画。
数据存储分两级:内存缓存和数据库持久化。压装过程中,数据先存内存;压装结束后,再批量写入数据库。不要一边压装一边写数据库,IO操作会影响实时性。
曲线数据存的时候,原始点要存,滤波后的点可以存也可以不存。原始点是分析的基础,滤波后的点只是显示用。如果存储空间紧张,可以只存原始点,显示的时候再滤波。
3.5 报警记录与用户权限管理
报警记录要分活动报警和历史报警。活动报警实时显示,历史报警存数据库。报警内容要包含:时间、报警码、报警描述、报警等级、确认状态。
用户权限一般分三级:操作员只能看和启动,工艺员可以改配方,管理员可以改系统参数和用户管理。权限控制要在上位机做,但关键操作下位机也要校验,防止上位机被绕过。
操作日志要记录谁在什么时候做了什么操作。比如“张三在2024-01-01 10:00:00修改了配方001的目标压力,从5kN改为6kN”。日志存数据库,支持按时间、用户、操作类型查询。
4. 上位机与下位机通信实战
4.1 Modbus寄存器映射表设计
寄存器映射表是上下位机之间的契约,设计好了事半功倍。我一般按功能分块:
| 区块 | 地址范围 | 类型 | 说明 |
|---|---|---|---|
| 状态区 | 0x0000-0x00FF | 输入寄存器 | 当前位置、压力、状态字、报警码 |
| 控制区 | 0x1000-0x10FF | 保持寄存器 | 目标位置、目标压力、配方号、命令字 |
| 配方区 | 0x2000-0x2FFF | 保持寄存器 | 配方参数,每个配方占32个寄存器 |
| 曲线区 | 0x3000-0x3FFF | 输入寄存器 | 曲线数据,环形缓冲区 |
| 线圈区 | 0x0000-0x00FF | 线圈 | 启动、停止、复位、急停复位 |
状态字用位定义,比如bit0表示伺服使能,bit1表示运行中,bit2表示报警,bit3表示到位。上位机读一个寄存器就能知道下位机的大致状态,不用读一堆寄存器。
命令字也用位定义,比如bit0表示启动,bit1表示停止,bit2表示复位。上位机写命令字的时候,先写1,下位机执行后清0,这样避免重复执行。
4.2 通信时序与握手协议
通信时序要设计好,不然会出现命令丢了或者重复执行的问题。我一般用请求-应答-确认三步握手。
上位机写命令字(比如启动),下位机收到后执行,执行完把命令字清0,同时把状态字里的“命令已执行”位置1。上位机轮询状态字,看到“命令已执行”位为1,就把命令字清0,同时把“命令已执行”位清0。这样一轮握手完成。
如果上位机写了命令字,但下位机没反应,超时后上位机要重发。重发次数一般设3次,3次都没反应就报通信故障。
轮询周期要合理。状态区可以100ms轮询一次,曲线区可以500ms轮询一次,配方区只在需要的时候读写。不要把所有寄存器都放在一个轮询周期里,那样通信负载太大。
4.3 通信异常处理与重连机制
通信异常是常态,处理好了系统就稳。常见的异常有:超时、校验错误、从站异常码、连接断开。
超时的话,先重试,重试3次还不行就报故障。校验错误一般是干扰或者波特率不对,检查接线和参数。从站异常码要看Modbus协议文档,比如异常码02表示非法数据地址,说明寄存器映射表对不上。
连接断开的话,上位机要自动重连。重连间隔可以设1秒、2秒、5秒递增,避免频繁重连把网络搞崩。重连成功后,要重新同步状态,比如读一遍状态区和配方区。
实操心得:串口通信的时候,USB转RS485转换器很容易出问题。我遇到过转换器驱动不稳定,跑几个小时就断一次。后来换成带光电隔离的工业级转换器,就稳了。另外,RS485的A/B线不要接反,接反了通信不上,但设备不会坏,就是收不到数据。
4.4 多台压机集中监控的实现
多台压机集中监控,上位机要同时和多个下位机通信。如果用Modbus RTU,一个串口只能接一台,多台就要多个串口或者用RS485总线。RS485总线的话,轮询要快,不然每台压机的响应时间会很长。
我的做法是每台压机一个通信线程,线程之间独立,互不影响。一台断了,其他台还能正常监控。数据汇总到一个全局的数据中心,界面从数据中心读数据。
如果是Modbus TCP,那就简单了,每台压机一个IP,上位机开多个TCP连接就行。但要注意连接数限制,有些下位机的TCP连接数有限,比如最多4个,那就要规划好。
集中监控的界面一般用大屏显示,每台压机一个卡片,显示状态、当前曲线、报警信息。点击卡片可以进入单机详情页。数据存储的时候,要带上压机编号,方便查询。
5. 常见问题与排查技巧实录
5.1 通信不稳定问题排查
通信不稳定是最常见的问题,表现就是数据时有时无,或者偶尔报校验错误。排查思路如下:
- 检查物理层:接线是否牢固,A/B线是否接反,屏蔽线是否接地。RS485的屏蔽层要单端接地,两端接地会形成地环路,反而引入干扰。
- 检查波特率:上下位机的波特率、数据位、停止位、校验位必须一致。115200bps下,线缆长度不要超过50米,再长就要降波特率。
- 检查终端电阻:RS485总线两端要接120欧姆终端电阻,中间设备不要接。没有终端电阻,信号反射会导致通信错误。
- 检查电源:下位机和上位机如果不在同一个电源系统,地电位差可能导致通信芯片损坏。用隔离型转换器可以解决。
- 检查软件:串口打开后,如果长时间不通信,有些转换器会进入休眠。可以定时发心跳包保持连接。
Modbus TCP的话,检查网络是否通,IP是否冲突,防火墙是否拦了502端口。用ping和telnet测试一下。
5.2 曲线数据丢点与时间戳对齐
曲线数据丢点,一般是下位机采集周期和通信周期不匹配。下位机1ms采一个点,上位机500ms读一次,一次读500个点。如果下位机的缓冲区只有400个点的空间,那就会丢100个点。
解决办法是加大下位机的缓冲区,或者提高上位机的读取频率。我一般把缓冲区设成采集周期的2倍以上,比如1ms采集,缓冲区至少2000个点。
时间戳对齐的话,下位机的时间戳是相对时间,从压装开始算。上位机收到数据后,用本地时间加上相对时间,得到绝对时间。但上下位机的时钟可能有偏差,所以时间戳只用相对值,不要用绝对值。
5.3 配方下发失败与参数校验
配方下发失败,常见原因有:寄存器地址不对、数据类型不匹配、下位机拒绝写入。
寄存器地址不对,检查映射表。数据类型不匹配,比如下位机是int16,上位机发的是float,那就要做转换。下位机拒绝写入,一般是参数超范围,或者下位机处于运行状态不允许改参数。
参数校验要在上位机做一遍,下位机再做一遍。上位机校验是为了用户体验,下位机校验是为了安全。两边的校验规则要一致,不然会出现上位机认为合法、下位机认为非法的情况。
5.4 上位机卡顿与界面刷新优化
上位机卡顿,一般是界面刷新太频繁,或者数据处理太耗时。优化思路:
- 减少刷新频率:曲线显示50ms刷一次就够了,不用每来一个点就刷。
- 异步处理:通信和数据处理放在后台线程,界面线程只负责显示。
- 数据分页:历史曲线查询的时候,不要一次查几万个点,分页查,每页1000个点。
- 使用虚拟化:DataGrid显示大量数据的时候,开启虚拟化,只渲染可见行。
- 避免频繁GC:C#里不要频繁创建大对象,比如每次刷新都new一个Brush,应该复用。
WPF的话,可以用CompositionTarget.Rendering事件来驱动刷新,比DispatcherTimer更平滑。但要注意,这个事件触发频率很高,里面不要做耗时操作。
5.5 常见问题速查表
| 现象 | 可能原因 | 排查方法 | 解决措施 |
|---|---|---|---|
| 通信超时 | 接线松动、波特率不对、终端电阻缺失 | 检查接线、核对参数、测量电阻 | 重新接线、统一参数、加终端电阻 |
| 校验错误 | 干扰、地电位差、线缆过长 | 检查屏蔽、测量地电位、缩短线缆 | 加隔离、单端接地、降波特率 |
| 曲线丢点 | 缓冲区太小、读取太慢 | 检查缓冲区大小、提高读取频率 | 加大缓冲区、优化读取逻辑 |
| 配方下发失败 | 地址错、类型错、参数超范围 | 核对映射表、检查数据类型、校验参数 | 修正映射、转换类型、限制范围 |
| 上位机卡顿 | 刷新太频繁、数据处理耗时 | 降低刷新率、异步处理、分页查询 | 优化刷新逻辑、后台线程处理 |
| 伺服报警 | 过载、超程、编码器故障 | 查看驱动器报警码、检查机械 | 排除机械故障、复位报警 |
最后再分享一个小技巧:调试Modbus通信的时候,我习惯用Modbus Poll和Modbus Slave先模拟一遍。Modbus Poll当主站,Modbus Slave当从站,把寄存器映射表跑通,再去连真实设备。这样能排除很多协议层面的问题,省得在设备上瞎折腾。另外,Modbus校验码在线计算工具也很好用,手动算CRC16容易出错,用工具算一下,对比一下报文,很快就能定位问题。