
1. 伺服压机控制系统到底在控什么先把场景说清楚。伺服压机跟普通液压机、气动压机最大的区别在于它的“压”不是靠阀门开度或者气压大小去糊出来的而是靠伺服电机带动丝杠或者曲柄把位置、速度、扭矩三个量都捏在手里。压装轴承、压接端子、压合壳体、粉末成型这类工艺对“压到多深、用多大力、压多快、保压多久”都有明确曲线要求伺服压机就是干这个的。一套完整的伺服压机控制系统硬件上通常长这样伺服驱动器加伺服电机、压力传感器或者叫测力传感器装在压头或者机架上、位置反馈编码器或者光栅尺、PLC或者运动控制器、工控机也就是上位机、还有安全回路和IO。软件上就分成了两半——上位机软件和下位机软件。这两个词在工控圈里天天说但真让一个刚入行的人讲清楚“谁管什么、边界在哪”很多人是含糊的。我见过不少项目上位机把实时控制逻辑也揽过去了结果通讯一卡压装曲线直接抖成锯齿也见过下位机把工艺配方全写死在程序里换个产品型号就得重新下载固件。这些坑的根源都是架构分工没想明白。这篇文章就围绕“伺服压机控制系统的软件架构怎么分”这件事把上位机与下位机的职责边界、通讯方式重点讲Modbus这条线、数据流、以及实际落地时的取舍掰开揉碎讲一遍。适合正在做伺服压机项目、或者准备从普通PLC控制转向伺服压装控制的工程师也适合做上位机开发、需要跟下位机对接的朋友。2. 软件架构的整体分层与分工逻辑2.1 为什么一定要分上位机和下位机先回答一个最根本的问题为什么不能一台工控机全干了伺服压机的控制周期通常在1ms到4ms这个量级位置环、速度环、电流环在驱动器内部闭环但压装过程的力位混合控制、曲线插补、拐点判断往往需要运动控制器或者PLC在几百微秒到几毫秒的周期内完成。工控机跑的是Windows或者Linux通用系统任务调度不是硬实时的你让它去保证1ms的确定性基本是给自己找麻烦。所以实时性要求高的部分必须下沉到下位机这是分工的第一性原则。第二个原因是可靠性。下位机PLC、运动控制器是工业级设备掉电保持、看门狗、故障安全逻辑都是现成的。上位机负责的是人机交互、配方管理、数据存储、报表追溯、MES对接这些“非实时但重要”的活。两者分开上位机死机了下位机还能把当前这一模压完并安全停机下位机出问题了上位机至少能把报警记录下来。第三个原因是可维护性。产线上换一个产品型号操作工在上位机界面上选个配方就行不用动下位机程序。下位机程序只在工艺逻辑本身变化时才需要改。这个边界划清楚后期维护成本能差出好几倍。2.2 三层架构HMI层、控制层、驱动层实际项目里我更习惯把伺服压机控制系统拆成三层来看这样职责更清晰层级典型载体核心职责实时性要求上位机层工控机、触摸屏、PC配方管理、曲线显示、数据存储、报表、MES对接、用户权限非实时100ms级控制层PLC、运动控制器、软PLC压装流程控制、力位混合逻辑、拐点判断、IO逻辑、安全联锁硬实时1~10ms驱动层伺服驱动器电流环、速度环、位置环、扭矩输出硬实时微秒级上位机和控制层之间通过通讯交互控制层和驱动层之间通过总线EtherCAT、CANopen、脉冲方向等交互。Modbus通常出现在上位机与控制层之间或者控制层与某些智能仪表、传感器之间。这个后面会重点讲。2.3 分工的核心判断标准怎么判断一个功能该放上位机还是下位机我总结了几条实操标准看时间尺度控制周期在10ms以内的放下位机100ms以上才需要响应的放上位机。看失效后果这个功能失效了会不会导致压装失败、设备损坏、人身危险会就放下位机。看变更频率经常要改的配方、曲线参数、报警阈值放上位机很少改的安全逻辑、流程框架放下位机。看数据量高频采样的曲线数据比如每1ms一个点在下位机缓存批量传给上位机上位机不做高频采集。注意有些项目为了省成本用一台工控机加软PLC比如CODESYS Runtime同时跑上位机和下位机这在逻辑上是可行的但一定要把实时任务和非实时任务用不同的CPU核心或者不同的进程优先级隔离开否则界面一卡控制也跟着抖。3. 上位机到底管哪些事3.1 配方与工艺参数管理上位机最核心的职责之一就是配方管理。一个伺服压机可能要压几十种产品每种产品的目标位置、目标力、速度、保压时间、拐点判定条件都不一样。这些参数如果写在下位机程序里换个产品就要改程序重新下载产线根本受不了。上位机的做法是把所有工艺参数做成结构化的配方存在数据库或者配置文件里。操作工选一个配方号上位机通过通讯把参数下发给下位机。下位机收到后把这些参数装载到自己的控制逻辑里执行。这里有个细节要注意参数下发不是简单写几个寄存器就完事。要有一套握手机制——上位机写参数、下位机校验参数合法性、下位机返回“参数已接受”、上位机确认。否则参数写到一半通讯断了下位机拿着一半新一半旧的参数去压轻则压废件重则撞机。3.2 实时曲线显示与过程监控伺服压机的价值很大一部分体现在“压装曲线”上。横轴是位置或者时间纵轴是力一条好的压装曲线能直接反映压装质量。上位机负责把下位机上传的曲线数据画出来实时显示。但这里有个常见的误区很多人以为上位机要实时采集每一个力值点。实际上如果控制周期是1ms一秒就是1000个点通过Modbus RTU这种串行通讯去逐个读根本读不过来。正确的做法是下位机在压装过程中把曲线数据缓存在自己的内存里压装结束后一次性批量上传给上位机。上位机拿到完整曲线后再画图、存库、做判定。如果确实需要实时显示那下位机可以每隔10ms或20ms上传一个“抽稀”后的点用于界面刷新完整曲线还是压完后批量传。这样既保证了界面流畅又不丢数据。3.3 数据存储与质量追溯压装数据是要追溯的。每个工件的压装曲线、最终力值、最终位置、判定结果、时间戳、操作工、设备号这些都要存下来。上位机通常用数据库SQL Server、MySQL、SQLite都有来存这些数据然后提供查询、导出、报表功能。我做过的一个项目客户要求追溯期是三年每天两班倒每班大概压3000个件。算一下3000×2×365×3 ≈ 657万条记录。如果每条记录还带完整曲线假设500个点数据量就上去了。所以曲线数据要不要全存、存多密、存多久一定要提前跟客户确认不然数据库膨胀起来很麻烦。常见做法是关键参数全存完整曲线只存最近三个月老数据只留特征值和判定结果。3.4 用户权限与操作日志产线上的设备不是谁都能改参数的。上位机要做用户权限管理通常分三级操作工只能选配方、启动、看结果工艺员可以改配方参数工程师可以做系统设置和校准。每次参数修改、配方切换、手动操作都要记操作日志。这个功能看起来简单但实际做的时候要注意权限验证不能只做在界面上。有些上位机软件界面把按钮灰掉了但通讯协议里那个写寄存器的功能码还是能发出去。如果下位机不校验权限用第三方Modbus工具照样能改参数。所以关键参数的写操作下位机也要做一层权限校验比如需要先写一个“解锁寄存器”才能改参数。3.5 与MES/上位系统的对接现在很多工厂要求设备数据上传到MES或者SCADA系统。上位机通常通过OPC UA、MQTT或者数据库中间表的方式把压装结果、设备状态、报警信息传上去。这部分逻辑放在上位机是最合适的因为下位机的资源通常很紧张不适合跑这些IT侧的协议栈。4. 下位机到底管哪些事4.1 压装流程控制下位机的核心任务就是控制压装流程。一个典型的压装流程大概是这样收到启动信号检查安全条件安全门关闭、双手按钮按下等控制伺服电机快速下行到接近工件的位置快下切换到慢速下行开始压装慢下实时监测力和位置判断是否到达目标位置或者目标力到达目标后保压一段时间卸压伺服电机回程输出判定结果OK/NG这个流程里的每一步都要在下位机里用实时逻辑实现。特别是第4步的力位混合判断比如“压到目标位置停止但如果中途力超过上限就立即停止并判定NG”这种逻辑必须在几毫秒内响应放上位机根本来不及。4.2 力位混合控制与拐点判断伺服压装和普通压装最大的区别就是它能在压装过程中同时看力和位置。常见的控制模式有几种位置控制模式压到指定位置停止力作为监控量。适合压装深度要求严格的场合。力控制模式压到指定力停止位置作为监控量。适合压装力要求严格的场合。力位混合模式先位置控制到达某个位置后切换力控制或者先力控制到达某个力后切换位置控制。拐点判断是另一个关键。比如压轴承的时候轴承压入的瞬间力会有一个明显上升这个“拐点”出现的位置能反映配合间隙。下位机要在每个控制周期计算力的变化率判断拐点是否在合格范围内。这些逻辑用梯形图写会比较吃力通常用ST结构化文本或者C语言在运动控制器里实现。如果下位机是PLC建议用支持ST的型号如果是专用运动控制器一般支持C或者类似的高级语言。4.3 安全逻辑与急停处理安全逻辑必须在下位机。急停按钮按下、安全门打开、伺服报警、超压、超行程这些信号进来后下位机要在最短时间内切断伺服使能、停止运动、抱闸。这个响应时间通常在10ms以内而且不能依赖上位机。我见过一个案例某设备把急停信号先接到上位机上位机再通过Modbus告诉下位机停机。结果有一次上位机卡了急停按下去两秒后伺服才停压头已经撞到限位了。这个教训很深刻——安全链路必须是硬线或者现场总线直达下位机不能绕上位机。4.4 实时数据采集与缓存下位机要在压装过程中采集力值、位置、速度、电流等数据缓存在自己的内存里。缓存深度要算够假设控制周期1ms压装过程最长5秒那就是5000个点。每个点如果存力值和位置两个float就是40KB。这个内存开销对现代PLC或者运动控制器来说不算什么但要提前规划好。采集到的数据一部分用于实时判断比如超限报警一部分压完后批量传给上位机。传输的时候要注意数据完整性通常加一个校验和或者CRC。4.5 与驱动器的实时交互下位机和伺服驱动器之间的通讯通常是EtherCAT、CANopen、或者模拟量脉冲。这个通讯周期就是控制周期必须稳定。如果用的是Modbus RTU去控伺服那基本只能做速度模式或者位置模式做不了高动态的力位混合控制因为Modbus RTU的通讯周期通常都在10ms以上而且抖动大。所以这里要明确一个概念Modbus适合做上位机与控制层之间的监控级通讯不适合做控制层与驱动层之间的实时通讯。这个边界一定要分清。5. 上位机与下位机之间的通讯怎么设计5.1 为什么Modbus在伺服压机项目里这么常见Modbus在工控圈的地位有点像普通话在中国人里的地位——不是最好的但大家都会说。伺服压机项目里上位机和控制层之间用Modbus的非常多原因有几个PLC和运动控制器基本都支持Modbus RTU或者Modbus TCP上位机开发工具C#、LabVIEW、Qt都有现成的Modbus库调试方便Modbus Poll、Modbus Slave这些工具一抓一大把协议简单出问题了抓个报文就能看懂但Modbus也有明显的短板没有数据类型只有寄存器和线圈。一个float要占两个寄存器一个字符串要占多个寄存器字节序还要自己约定。这些在项目初期就要定好不然后期对接的时候两边理解不一致数据全是乱的。5.2 Modbus RTU与Modbus TCP怎么选对比项Modbus RTUModbus TCP物理层RS485/RS232以太网速率通常9600~115200bps10/100Mbps拓扑一主多从总线型一主多从星型实时性较差轮询周期长较好但受网络影响布线简单两根线需要交换机、网线适用场景设备少、数据量小、距离远设备多、数据量大、局域网伺服压机项目里如果只有一台设备上位机和控制层之间用Modbus RTU就够了。如果是多条产线、多台设备集中监控建议用Modbus TCP或者更高级的OPC UA。5.3 寄存器地址规划这是实际项目里最容易出问题的地方。上位机和下位机对寄存器地址的理解必须完全一致。我的做法是做一个通讯地址表Excel或者Markdown都行包含以下列地址名称数据类型读写单位说明40001控制字UINT16R/W-bit0启动bit1停止40002状态字UINT16R-bit0运行bit1报警40003-40004当前位置FLOAT32Rmm大端字节序40005-40006当前力值FLOAT32RN大端字节序40007-40008目标位置FLOAT32R/Wmm配方参数40009-40010目标力FLOAT32R/WN配方参数这个表要作为项目文档的一部分双方签字确认。后面调试的时候所有问题都对着这个表查。提示Modbus的寄存器地址有“协议地址”和“PLC地址”两种表示方式。协议地址从0开始PLC地址从1开始还有40001这种带功能码前缀的写法。对接的时候一定要问清楚对方用的是哪种不然会差1。5.4 数据交互的握手设计前面提过参数下发要有握手。具体怎么做我一般用这样的流程上位机写参数到指定寄存器区上位机写“参数更新请求”标志位比如控制字bit8置1下位机检测到请求校验参数范围校验通过下位机把参数装载到运行区清除请求位置“参数已接受”标志校验不通过下位机置“参数错误”标志并给出错误码上位机读取标志确认结果这个流程看起来啰嗦但能避免90%的参数下发问题。特别是产线上操作工手快连续切换配方的时候没有握手很容易出乱子。5.5 曲线数据的批量传输压装结束后下位机要把缓存的曲线数据传给上位机。数据量可能是几千个点用Modbus传的话要分多次读。我的做法是下位机在内存里开辟一个曲线缓冲区压装过程中写入压装结束后下位机把曲线长度、起始地址准备好上位机先读曲线长度然后按每次100个点的批量去读读完后上位机做校验确认数据完整如果数据量特别大或者实时性要求高可以考虑用Modbus TCP或者干脆用文件传输的方式下位机支持FTP的话。6. 实操从零搭一套最小可用的通讯框架6.1 下位机侧的准备假设下位机用的是一台支持Modbus RTU从站的PLC或者运动控制器。首先要配置串口参数波特率115200、8位数据位、1位停止位、无校验或者偶校验看对方要求。然后定义从站地址比如1号站。接着规划寄存器区。我一般把寄存器分成几个区状态区只读当前状态、报警码、当前位置、当前力值控制区读写启动、停止、复位、参数更新请求参数区读写配方参数曲线区只读曲线长度、曲线数据在下位机程序里把这些寄存器和实际的变量映射起来。如果是西门子PLC用Modbus库指令做映射如果是汇川、台达这些一般有现成的Modbus从站配置功能。6.2 上位机侧的开发上位机用C#做的话可以用NModbus或者EasyModbus库。用Python的话pymodbus很成熟。用LabVIEW的话有Modbus库。这里以C#为例讲一下核心逻辑。先建立连接using Modbus.Device; using System.IO.Ports; SerialPort port new SerialPort(COM3, 115200, Parity.None, 8, StopBits.One); port.Open(); IModbusSerialMaster master ModbusSerialMaster.CreateRtu(port); byte slaveId 1;读保持寄存器ushort startAddress 0; ushort numRegisters 10; ushort[] registers master.ReadHoldingRegisters(slaveId, startAddress, numRegisters);写单个寄存器master.WriteSingleRegister(slaveId, 0, 1); // 写控制字启动读float的时候要注意字节序。Modbus寄存器是16位的一个float32占两个寄存器。常见的字节序有ABCD、CDAB、BADC、DCBA四种。对接的时候一定要确认下位机用的是哪种。我一般用BitConverter处理byte[] bytes new byte[4]; Buffer.BlockCopy(registers, 0, bytes, 0, 4); float value BitConverter.ToSingle(bytes, 0);如果字节序不对就把bytes数组反转一下再转。6.3 轮询策略与超时处理上位机读下位机数据通常是轮询。轮询周期设多少看数据量。如果只是读状态和当前值100ms一次够了。如果要读曲线那就在压装结束后单独触发。轮询的时候一定要做超时和重试。Modbus RTU在RS485上跑偶尔丢一帧很正常。我的做法是单次读超时设200ms失败重试2次连续3次失败就报通讯故障界面上给提示。注意轮询不要太快。有些新手把轮询周期设成10ms结果RS485总线一直处于繁忙状态反而容易丢包。115200bps下读10个寄存器大概需要2ms左右加上从站响应时间实际一轮下来可能要5~10ms。轮询周期设50~100ms比较稳妥。6.4 一个完整的参数下发示例假设要把目标位置100.5mm、目标力5000N下发给下位机。// 目标位置100.5mm转成float的字节 float targetPos 100.5f; byte[] posBytes BitConverter.GetBytes(targetPos); // 按大端字节序排列 Array.Reverse(posBytes); // 如果下位机是大端 ushort posHigh BitConverter.ToUInt16(posBytes, 0); ushort posLow BitConverter.ToUInt16(posBytes, 2); // 写参数 master.WriteMultipleRegisters(slaveId, 6, new ushort[] { posHigh, posLow }); // 目标力5000N float targetForce 5000f; byte[] forceBytes BitConverter.GetBytes(targetForce); Array.Reverse(forceBytes); ushort forceHigh BitConverter.ToUInt16(forceBytes, 0); ushort forceLow BitConverter.ToUInt16(forceBytes, 2); master.WriteMultipleRegisters(slaveId, 8, new ushort[] { forceHigh, forceLow }); // 置参数更新请求位 master.WriteSingleRegister(slaveId, 0, 0x0100); // 假设bit8是参数更新请求 // 等待下位机确认 Thread.Sleep(50); ushort[] status master.ReadHoldingRegisters(slaveId, 1, 1); if ((status[0] 0x0200) ! 0) // 假设bit9是参数已接受 { Console.WriteLine(参数下发成功); } else { Console.WriteLine(参数下发失败); }这段代码看起来简单但实际项目里要加很多保护通讯异常处理、参数范围校验、超时重试、界面状态更新。这些细节决定了软件是能用还是好用。7. 常见问题与排查技巧实录7.1 通讯不上怎么办这是最常见的问题。排查顺序应该是物理层RS485的A/B线有没有接反终端电阻有没有接用万用表量一下差分电压空闲时应该在1V左右。串口参数波特率、数据位、停止位、校验位两边必须完全一致。我遇到过好几次是校验位设错了。从站地址上位机发的从站地址和下位机设的是不是一样寄存器地址是不是差了1协议地址和PLC地址搞混了软件串口有没有被其他程序占用用Modbus Poll先试一下能通说明硬件没问题。7.2 数据读出来是乱码或者明显不对大概率是字节序或者数据类型的问题。先确认下位机里这个变量是什么类型占几个寄存器字节序是什么。然后用Modbus Poll读原始寄存器值手动算一下。比如读出来两个寄存器是0x42C8和0x0000按大端float解析是100.0按小端就是别的数。还有一种可能是地址偏移。有些下位机的Modbus地址是从1开始的有些是从0开始的。差1的话读出来的就是隔壁寄存器的值。7.3 通讯时断时续RS485总线在工业现场很容易受干扰。常见原因没有终端电阻或者终端电阻阻值不对应该是120Ω总线太长超过1200米115200bps下建议不超过100米走线跟动力线捆在一起没有屏蔽或者屏蔽层没接地多个从站的时候某个从站的通讯芯片有问题拉低了整个总线我的经验是RS485线一定要用双绞屏蔽线屏蔽层单端接地走线远离变频器和伺服驱动器。如果现场干扰实在严重考虑换成光纤或者Modbus TCP。7.4 参数下发后不生效先检查握手流程有没有走完。上位机写了参数但下位机可能还没处理。用Modbus Poll监控一下控制字和状态字的变化。如果下位机一直不置“参数已接受”可能是校验没通过看看下位机的错误码。还有一种情况是参数写到了错误的寄存器区。比如下位机有两个参数区一个用于当前运行一个用于下次运行写错了地方就不会立即生效。7.5 曲线数据传输出错曲线数据量大传输时间长容易出问题。常见的是数据错位或者丢包。解决办法每包数据加序号和CRC校验上位机收到后校验不对就重传传输过程中不要让下位机开始下一次压装否则缓冲区会被覆盖如果用的是Modbus RTU把波特率提到115200减少传输时间7.6 常见问题速查表现象可能原因排查方法完全通讯不上接线错误、串口参数不对、从站地址不对检查A/B线、核对串口参数、用Modbus Poll测试数据乱码字节序不对、数据类型不对读原始寄存器手动解析验证时断时续干扰、终端电阻、总线过长检查屏蔽接地、加终端电阻、缩短总线参数不生效握手未完成、地址写错监控控制字和状态字、核对地址表曲线数据错位丢包、缓冲区覆盖加序号和CRC、传输时禁止新压装响应慢轮询周期太长、数据量太大优化轮询策略、减少单次读取量8. 架构选型的一些个人体会做伺服压机项目这些年我在架构上踩过的坑比在具体代码上踩的还多。有几个体会比较深。第一不要为了省硬件成本把上下位机合并。看起来省了一台工控机或者一个PLC但后期实时性和可靠性的问题会让你把省下的钱加倍吐出来。上位机和下位机分开各干各的活是最稳妥的。第二通讯协议的选择要匹配数据量和实时性要求。Modbus RTU适合小数据量、低频次的场景。如果曲线数据量大、实时性要求高考虑Modbus TCP或者OPC UA。如果控制层和驱动层之间要高速通讯用EtherCAT或者CANopen别用Modbus。第三地址表和握手协议是项目的地基。这两个东西在项目初期花半天时间定好后期能省好几天调试时间。我现在的习惯是任何Modbus项目先出地址表双方确认后再写代码。第四下位机的程序要留调试接口。比如预留几个寄存器可以强制置位某些中间变量方便现场排查问题。但要注意这些调试接口在正式交付前要能关闭不然有安全隐患。第五上位机的数据存储要提前规划容量。曲线数据是膨胀最快的一定要问清楚客户要存多久、存多密。如果客户说“全部存、永久存”那就要跟他算一下硬盘容量和数据库性能不然运行半年数据库就卡了。最后分享一个小技巧在Modbus通讯里我习惯用一个“心跳寄存器”。上位机每隔一秒写一个递增的值下位机检测到这个值在变就知道上位机还活着。如果超过3秒没变下位机就认为上位机掉线了可以做一些安全处理比如禁止启动新的压装循环。这个机制在无人值守的产线上特别有用。