4. MOTOMAN程序结构与节拍影响

在MOTOMAN(安川)机器人系统中,程序结构不只是“代码怎么组织”的问题,它直接决定了控制器在每个控制周期里要处理多少指令、要切换多少次任务上下文、要跳转多少层调用栈。很多现场节拍优化做到最后发现瓶颈不在轨迹速度,而在程序结构本身:主程序里塞了太多逻辑判断、子程序被反复调用却没有做入口收敛、任务优先级设置不当导致运动任务被I/O轮询抢占。本节从主程序、子程序、任务优先级、调用层级四个维度,说明它们如何影响循环时间,并给出可落地的结构优化方法。

主程序:循环时间的“骨架”

MOTOMAN的JOB分为主程序(通常命名为MAIN或MASTER)和子程序(SUB)。主程序承担的是流程调度职责:初始化、等待外部信号、调用子程序、判断循环条件。它本身不应该包含大量运动指令。原因在于,主程序每执行一条指令都要经过解释器解析,而CALL指令的跳转开销远小于把几十行运动指令平铺在主程序里。

一个典型的反面例子是:把取件、搬运、放件、回原点全部写在主程序里,循环体长达200行。此时控制器在每个循环开始时需要重新扫描整个程序段,程序指针移动距离长,且一旦中间某步需要插入条件分支,就要用JUMP标签跳转,跳转标签越多,解析器维护的跳转表越大。实测经验表明,在DX200/FS100控制器上,主程序每减少100行非运动指令,单循环可释放约3~8ms的解析时间——这个数值随控制器型号和程序复杂度浮动,属于示例量级,不是固定值。

正确做法是:主程序只保留“循环入口—调用—判断—跳回”四类指令,把具体动作下沉到子程序。例如:

// MAIN.JOB 主程序结构示例
NOP
CALL JOB:INIT          // 初始化
*LOOP
CALL JOB:PICK          // 取件子程序
CALL JOB:PLACE         // 放件子程序
CALL JOB:HOME_CHECK    // 原点判断
JUMP *LOOP IF IN#(1)=ON  // 循环条件
END

这样主程序只有约8行,循环体解析开销被压到最低,节拍收益直接体现在每个循环的“非运动时间”上。

子程序:调用层级与栈深度

子程序的价值在于复用和模块化,但调用层级不是越深越好。MOTOMAN控制器对CALL嵌套有栈深度限制(通常为8~16层,视控制器而定),每进入一层子程序,控制器要保存返回地址、局部变量和当前程序指针。层级越深,入栈/出栈操作越多,单次调用开销越大。

更隐蔽的问题是“子程序里再调子程序”的链式结构。例如PICK调用APPROACH,APPROACH又调用MOVE_SAFE,MOVE_SAFE再调用CHECK_POS。四层调用下来,每次取件要经历4次入栈和4次出栈。如果取件动作本身只有0.8s,而调用开销累计达到15~25ms,占比就不可忽视了。

优化原则是:把高频调用的短动作合并到同一层子程序内,只对真正需要复用的逻辑保留独立子程序。例如把APPROACH和MOVE_SAFE合并为一个PICK_APPROACH,层级从4层降到2层,调用开销可减少一半以上。同时,子程序内部应避免在循环中反复CALL同一个工具子程序,能内联的短逻辑优先内联。

MOTOMAN程序调用层级与节拍影响示意 深调用链(4层,开销大) MAIN → CALL PICK PICK → CALL APPROACH APPROACH → CALL MOVE_SAFE MOVE_SAFE → CALL CHECK_POS 入栈/出栈 4次,调用开销累计 约 15~25 ms/循环(示例量级) 浅调用链(2层,开销小) MAIN → CALL PICK_APPROACH PICK_APPROACH 内联安全移动 CHECK_POS 仅保留必要判断 入栈/出栈 2次,调用开销减半 约 6~10 ms/循环(示例量级) 节拍收益:每循环释放约 8~15 ms (按 1.0s 循环计,约 1%~1.5%) 优化

任务优先级:谁先占用控制周期

MOTOMAN控制器支持多任务并行执行,每个任务有独立的优先级(通常用PRIORITY或任务调度参数设置)。优先级决定了当多个任务同时就绪时,控制器先执行哪一个。这里的关键认知是:运动任务和I/O任务共享同一个控制周期,如果I/O轮询任务的优先级高于运动任务,运动指令的插补周期就会被拉长,表现为机器人动作“一顿一顿”,节拍自然变慢。

现场常见的错误配置是:把I/O监视任务设为高优先级,把MOVE任务设为低优先级。正确的做法是让运动任务保持最高优先级,I/O任务在运动任务的等待间隙执行。在DX200上,可以通过JOB属性中的任务优先级参数调整,一般建议运动任务优先级设为HIGH,辅助逻辑任务设为NORMAL或LOW。

另一个容易被忽略的点是WAIT指令的优先级行为。当主程序执行WAIT IN#(1)=ON时,如果该等待发生在高优先级任务中,控制器会持续轮询该信号,占用控制周期。优化方法是用WAIT配合超时判断,或者把等待逻辑放到低优先级任务中,让运动任务在等待期间仍能执行其他动作。

调用层级对循环时间的量化影响

把上述因素合起来看,调用层级对循环时间的影响可以拆成三部分:入栈/出栈开销、程序指针跳转开销、局部变量初始化开销。在DX200控制器上,单次CALL的典型开销约为2~5ms(示例量级),其中入栈/出栈占大头。如果每个循环有6次CALL,累计就是12~30ms;如果优化到3次,就降到6~15ms。

更关键的是,调用层级深的时候,程序指针需要在多个JOB之间切换,控制器的指令预取缓冲区会被频繁刷新,导致运动指令的预取失效。这部分的隐性开销往往比显式的CALL开销更大。因此,减少调用层级不仅省的是调用指令本身,还省了预取失效带来的额外周期。

结构优化检查清单(可直接对照现场程序):
  • 主程序行数是否超过30行?超过则把运动指令下沉到子程序。
  • 是否存在4层以上的CALL嵌套?有则合并短动作子程序。
  • 运动任务优先级是否高于I/O任务?否,则调整任务优先级。
  • 循环体内是否有WAIT长时间占用高优先级任务?有则移入低优先级任务或加超时。
  • 子程序是否在循环中被反复CALL且内容很短?短逻辑优先内联。

实操:一次结构优化的节拍对比

假设某搬运工位原始程序结构为:主程序120行,包含取件、搬运、放件、回原点全部动作;子程序PICK调用APPROACH,APPROACH调用MOVE_SAFE,共4层;运动任务优先级为NORMAL,I/O任务为HIGH。原始循环时间实测为1.05s(示例数据,非实测)。

优化操作:把主程序压缩到10行,只保留循环调度;把APPROACH和MOVE_SAFE合并为PICK_APPROACH,层级降到2层;把运动任务优先级改为HIGH,I/O任务改为LOW;把循环内的WAIT改为带超时的WAIT ... TIMEOUT并放到低优先级任务。优化后循环时间降至0.98s(示例数据),单循环释放约70ms,按每小时3600次循环计算,每小时可多完成约250次搬运。

需要说明的是,上述数值属于教学示例量级,实际收益取决于控制器型号、程序复杂度、I/O数量和轨迹长度。现场应通过MOTOMAN的循环时间监视功能或外部秒表实测验证,不要直接套用示例数值。

注意:调整任务优先级前,必须确认I/O任务降级后不会导致安全信号响应延迟。安全相关的I/O(如急停、安全门)应保留在独立的高优先级安全任务中,不参与本节所述的节拍优化调整。

总结来说,MOTOMAN程序结构对节拍的影响不是线性的,而是“结构越深、优先级越乱,隐性开销越大”。优化的核心思路是:主程序做薄、子程序做浅、运动任务做高、等待逻辑做低。把这四件事做到位,往往不需要改任何轨迹速度参数,就能拿到可观的节拍收益。