接手过别人写的机器人程序的人,大概都体会过那种绝望:一个主程序从头拉到尾几百行,点位全是 P1 到 P200 光秃秃的编号,寄存器 R[1] 到 R[50] 一个注释没有,异常处理就一句“报警停机等人来”。这种程序,能跑,但你想改一个动作、加一个工位、查一个偶发停机,得先花两天把作者的脑回路逆向出来。我这些年在现场调试、也接手改过不少这类程序,今天就把“机器人程序怎么写才好维护”这件事,按模块化、命名、注释、异常处理、交接文档五块讲透。

先把结论放前面:机器人程序好不好维护,跟它跑得快不快、示教得准不准是两码事——好维护的核心是三条,结构分层让人一眼看懂调用关系、命名和注释让人不用猜语义、异常处理让人知道出了岔子机器人会怎么办。 下面每块都尽量给能直接落地的做法,代码示例用通用机器人程序的伪代码写,不绑死某一家的具体语法,你换到自己的品牌上照着改就行。

一、烂程序在现场长什么样

先描述一下“反面教材”,你对照着看自己手上的程序有没有中招。我接手过的最典型的烂程序,通常有这么几个特征:

  • 一根肠子通到底:整个流程塞在一个主程序里,取料、加工、检测、码垛全揉一块,几百行没有一个子程序,改任何一步都要在长文里大海捞针。
  • 裸编号满天飞:点位是 P[1]…P[200],寄存器是 R[1]…R[50],IO 是 DO[12]、DI[7],没有一个名字,没有一行注释,鬼知道 R[23] 存的是啥。
  • 魔术数字硬编码:等待时间直接写 WAIT 1.5,速度直接写 100,工位偏移直接写坐标数字,全散在正文里,改一个参数要全篇搜。
  • 异常靠裸奔:没有报警分级,任何异常都一律急停,或者干脆不判断,抓空了照样往下走,撞了才知道。
  • 零文档:没有 IO 表、没有点位说明、没有版本记录,作者一走,这套程序就成了没人敢动的黑箱。

一句话总结烂程序的通病:它把“能跑通”当成了终点,而好维护的程序把“下一个人能看懂、能安全地改”当成起点。 量产线的程序生命周期动辄好几年,写它的人和维护它的人大概率不是同一个,好维护本质上是写给未来那个接手的人看的。

二、程序架构:分层,让调用关系一眼可见

好维护的第一步,是把“一根肠子”拆成清晰的层次。我习惯把机器人程序分成三层:

  • 主控层(主程序):只做流程编排和状态机调度,负责“现在该干哪一步、出异常了跳哪去”,本身不写具体动作细节。主程序应该短到能一屏看完整个流程骨架。
  • 工序层(子程序):一个完整的工艺动作封装成一个子程序,比如“取料”“上料”“检测”“码垛”,各自独立、职责单一。
  • 功能块层(基础动作/工具函数):更细的可复用动作,比如“打开夹爪”“等待信号应答”“回安全点”,被工序层反复调用。

用伪代码给个骨架感受一下这个分层:

PROGRAM Main            ; 主控层:只做编排,看不到动作细节
  CALL Init_Home        ; 初始化并回安全原点
  WHILE SystemRunning
    state = Get_State()
    SWITCH state
      CASE READY:   CALL Proc_PickBlank    ; 取料工序
      CASE LOADED:  CALL Proc_LoadMachine  ; 上料工序
      CASE DONE:    CALL Proc_Unload       ; 下料工序
      CASE ALARM:   CALL Handle_Alarm      ; 异常处理入口
    END SWITCH
  END WHILE
END

SUBPROGRAM Proc_PickBlank    ; 工序层:一个工艺动作一个子程序
  CALL Move_SafePoint        ; 功能块层:可复用基础动作
  CALL Gripper_Open
  MOVE_TO pBlankPick  SPEED vApproach
  CALL Gripper_Close_WithCheck   ; 带抓取确认,不裸奔
  CALL Move_SafePoint
END

这个骨架的好处是:流程逻辑和动作细节分离——想看整条线怎么走,只读主程序;想改某一步的具体动作,只进对应子程序,互不干扰。 加一个新工位,就多写一个工序子程序、在主程序状态机里挂一个分支,不用去动别的部分。这也是为什么我坚持主程序里绝不写具体点位移动:一旦主程序里混进了动作细节,分层就塌了,又变回一根肠子。

有个原则值得记:一个子程序只干一件事,长度控制在能一屏读完的范围内;如果一个子程序里出现了两个明显不相干的职责,就该拆。 职责单一的子程序,才谈得上复用和单独测试。

三、命名规范:让名字自己说话

裸编号是机器人程序可维护性的头号杀手。控制器底层用的是 P[]、R[]、DO[] 这类编号没错,但只要你的品牌支持别名/注释,就一定要给它们起有意义的名字。我团队里约定的命名习惯,供你参考:

  • 点位:用“位置+用途”命名,比如 pBlankPick(毛坯抓取点)、pMachineLoad(机床上料点)、pSafeHome(安全原点),别用 P1、P2。
  • 寄存器/变量:用“类型前缀+含义”,比如 nRetryCount(重试次数)、bPartPresent(工件到位标志)、vApproach(接近速度),一眼知道存的是什么、是数还是标志。
  • IO 信号:用“方向+设备+含义”,比如 DO_Gripper_Close(输出:夹爪闭合)、DI_Machine_DoorOpen(输入:机床门已开),别让人对着 DO[12] 猜。
  • 子程序:用“动作+对象”,动词打头,比如 Proc_PickBlankHandle_AlarmMove_SafePoint

下面这张表,把裸编号和规范命名摆一起,你就明白差在哪了:

维度 裸编号写法 规范命名写法 维护时的差别
点位 MOVE P[23] MOVE pMachineLoad 前者要翻点位表查 P23 是哪,后者名字自解释
变量 R[5] = R[5] + 1 nRetryCount = nRetryCount + 1 前者不知在累加啥,后者一眼是重试计数
IO IF DI[7] THEN IF DI_Machine_DoorOpen THEN 前者要对照 IO 表,后者读代码即知语义
参数 WAIT 1.5 WAIT tClampSettle 前者魔术数字散落,后者集中可改

一条能显著提升可维护性的做法:把所有魔术数字(等待时间、速度、偏移量、重试上限)抽成命名常量或参数,集中定义在程序头部,别散在正文里硬编码。 这样调参数时你改一处就够,不用全篇搜 1.5 到底是哪个 1.5、改了会不会误伤别处。

四、IO 与寄存器注释:写给三个月后的自己

命名解决了“是什么”,注释要补上“为什么”和“在哪用”。机器人程序里最该注释、也最容易被省略的,是 IO 和寄存器。

我的注释原则是注释意图和约束,不注释显而易见的动作MOVE pSafeHome 后面写“移动到安全原点”是废话;但 WAIT tClampSettle 后面注一句“等卡盘夹紧稳定,此值受气压波动影响,低于 0.8s 偶发夹不牢”就极有价值——它告诉未来的维护者这个数字是怎么来的、能不能随便改。

几个我一定会留注释的地方:

  • IO 表必须成文:每个用到的 DI/DO,编号、名字、语义、对接的哪台设备,单独列一张 IO 表,注释里引用。这张表是机器人和 PLC/外设协作的合同,口头约定迟早出错。
  • 寄存器用途集中登记:哪个寄存器存什么、取值范围、谁写谁读,在程序头部集中注释一遍。最怕的是同一个寄存器在两个子程序里被当成不同含义用。
  • 握手信号标清方向和应答关系:跟外设交互的信号,注明是“我置位对方读”还是“对方置位我读”、配对的应答信号是哪个。信号语义错位是多方集成时最难查的坑。

给段带注释的伪代码感受下密度:

; ---- 参数区(集中定义,改这里就够)----
CONST tClampSettle = 0.8   ; 卡盘夹紧稳定等待, 受气压影响, 勿低于0.8s
CONST nRetryMax    = 3     ; 抓取失败重试上限, 超限报警呼人
CONST vApproach    = 200   ; 接近速度 mm/s

; ---- 带确认的抓取, 不裸奔 ----
SUBPROGRAM Gripper_Close_WithCheck
  SET DO_Gripper_Close = ON      ; 输出:夹爪闭合
  WAIT DI_Gripper_Closed = ON  TIMEOUT 2.0   ; 等夹爪到位反馈, 2s超时
  IF TIMEOUT THEN
    RAISE Alarm_GripFail          ; 未拿到到位信号, 抛可恢复报警
  END IF
  IF DI_PartPresent = OFF THEN    ; 夹爪闭了但没检测到工件=抓空
    RAISE Alarm_PickEmpty
  END IF
END

核心一句:注释的价值不在于复述代码干了什么,而在于记录代码为什么这么写、这个数字的边界在哪——前者代码自己会说,后者只有当时那个人知道,不写下来就永远丢了。

五、异常处理与恢复:报警分级 + 安全位回退

异常处理是区分“能跑的程序”和“量产扛得住的程序”的分水岭。烂程序的异常处理就一句“出错就急停”,好程序会把异常分级、每级配不同的处置策略。

我习惯把异常分成三级:

  • 一级:可自恢复的软异常。比如抓空、抓偏、来料没到。处置是原地重试,设重试上限(比如三次),超限再升级报警。这类异常在量产里天天有,能自己扛过去的绝不惊动人。
  • 二级:需要人工介入但不危险的故障。比如某工位反复抓空超限、来料长时间缺料。处置是机器人回到安全位停下、报警呼人,但不触发急停,人来了处理完能接着跑。
  • 三级:安全相关的硬故障。比如撞机、安全门被打开、驱动报错。处置是立即安全停机,这类必须由安全回路/安全 PLC 独立保证,绝不能靠普通逻辑去兜。

三级异常和它们的处置策略,我列成表方便对照落地:

异常级别 典型场景 处置策略 恢复方式
一级 软异常 抓空、抓偏、来料未到 原地重试, 计数, 超限升二级 自动重试, 无需人工
二级 需介入 反复抓空超限、长时缺料 回安全位, 报警呼人, 不急停 人工处理后从断点续做
三级 硬故障 撞机、安全门开、驱动报错 立即安全停机 排查后重新初始化

异常处理里最见功力、也最该做扎实的,是恢复逻辑。很多程序异常处理写了一半——会报警、会停,但停完不知道怎么接着干,只能人工把机器人拖回原点重来,丢掉半成品、浪费节拍。好的恢复逻辑要做到:

  • 异常时先回退到明确的安全位置,而不是就地停在一个可能干涉的姿态上。安全位是预先规划好的、任意状态都能安全抵达的点。
  • 记录断点状态:停之前机器人处于哪个工序、手上有没有夹着工件、在哪个工位。恢复时靠这个状态判断该续做还是安全卸料,而不是无脑回原点。
  • 恢复要判断现场再动:复位后别急着按原路径跑,先确认工件在不在、夹爪状态对不对、干涉区清没清,再决定下一步。

给段恢复逻辑的伪代码:

SUBPROGRAM Handle_Alarm
  CALL Move_ToSafePoint         ; 先回安全位, 不停在干涉姿态
  SAVE breakpointState          ; 记录断点:工序/是否夹料/工位
  ALARM_TO_HMI  currentAlarm    ; 上报报警到人机界面, 留日志
  WAIT ResetSignal              ; 等人工确认复位
  ; ---- 复位后判断现场再决定, 别无脑回原点 ----
  IF breakpointState.HoldingPart THEN
    CALL SafeUnload             ; 手上有料, 先安全卸掉
  END IF
  CALL Recheck_Interference     ; 确认干涉区已清
  RESUME_FROM breakpointState   ; 从断点安全续做
END

一句能救很多量产停机的话:异常处理的重点不是“怎么停下来”,而是“停下来之后怎么干净地回到已知的安全状态再续做”——前者谁都会写,后者才是量产稳定性的真正来源。 我见过太多演示当天顺跑漂亮、一进量产就因为异常恢复没做扎实而三天两头长停的项目,问题几乎都出在这一节偷了懒。

六、交接文档:让程序离得开原作者

最后一块最容易被跳过,却直接决定这套程序的寿命——交接文档。程序写得再漂亮,如果只有作者一个人看得懂,它就是个定时炸弹,人一走就没人敢碰。

一套能交接的机器人程序,我认为至少得配齐这几样:

  • IO 表:每个 DI/DO 的编号、名字、语义、对接设备,前面说过,这是和外设协作的合同。
  • 点位说明表:关键点位的名字、含义、是示教的还是计算的、动过要注意什么。
  • 状态机/流程图:主程序的状态流转画成一张图,一图胜过读几百行代码。
  • 参数清单:所有可调参数(速度、等待、重试上限、偏移)的名字、当前值、可调范围、调整影响。
  • 异常处置手册:每个报警号对应什么原因、现场怎么处理、能不能自恢复。这份是给操作和维护人员的救命文档。
  • 版本记录:谁在什么时候改了什么、为什么改。量产程序改动频繁,没有版本记录,出了问题根本追不回是哪次改动引入的。

判断一套机器人程序能不能交接的标准很简单:把作者调走,换一个懂这个品牌基础的工程师,只靠程序注释和这套文档,他能不能在不问原作者的情况下看懂流程、改一个工位、处理一个报警。 能,这程序就是活的;不能,它迟早变成没人敢动的黑箱,然后在某次非改不可的时候把整条线拖下水。

写到最后想强调一点:上面这套模块化、命名、注释、异常分级、交接文档的做法,到 2026 年主流控制器的编程环境大多都支持得很好——别名、子程序、结构化异常处理、离线仿真早就不是稀罕功能,缺的从来不是工具,而是写程序时有没有把“下一个接手的人”放在心上。好维护的程序不是天赋,是习惯:每写一个裸编号就顺手起个名,每拍一个魔术数字就顺手抽成常量,每写一段动作就顺手想想它异常了怎么办。这些习惯攒下来,你的程序自然就经得起量产和交接了。而这套习惯真正的回报,往往不在你写它的时候,而在一年后某个深夜,接手的人只靠注释和文档就把一个偶发停机查明白、没给你打那通电话——那一刻,才算真的写好了一套机器人程序。

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐