在工业自动化项目中,多轴协同运动是实现复杂轨迹加工的核心需求。当我们需要让两个或多个伺服轴按照特定几何路径(如直线、圆弧)同步运动时,单轴的点位控制就力不从心了。本文将深入探讨如何在 CODESYS 开发环境中,利用轴组(Axis Group)功能,实现直线和圆弧插补的完整实战。从核心概念、环境搭建、ST语言编程到调试排错,手把手带你构建一个可运行的多轴协同运动控制程序,无论是控制汇川伺服还是其他品牌驱动器,其原理和实现方法都是相通的。
1. 背景与核心概念:为什么需要轴组与插补?
在传统的单轴控制中,我们通常使用MC_MoveAbsolute或MC_MoveVelocity等功能块,让每个轴独立地运动到目标位置或保持恒定速度。然而,对于需要刀具或末端执行器沿特定轮廓路径移动的应用,例如激光切割、CNC加工、涂胶、焊接等,这种独立控制无法保证路径精度。这时,就需要引入轴组(Axis Group)和插补(Interpolation)技术。
轴组(Axis Group): 可以将多个物理轴或虚拟轴逻辑上组合成一个“组”,将这个组视为一个整体进行控制。控制器对这个组下达运动指令,由内部算法计算出组内每个轴应有的实时位置、速度,从而实现协同运动。
插补(Interpolation): 是轴组运动的核心算法。它根据给定的路径类型(如直线、圆弧、样条曲线)和参数,在运动过程中,实时计算组内各轴在每个控制周期内的设定位置。我们主要关注两种基础插补:
- 直线插补(Linear Interpolation):控制组内所有轴从起点同步线性运动到终点,在空间上形成一条直线轨迹。
- 圆弧插补(Circular Interpolation):控制组内至少两个轴,使末端点沿指定的圆弧路径运动。这需要定义圆心、半径、起始角和终止角等参数。
CODESYS 中的实现: CODESYS SoftMotion 库提供了强大的多轴运动控制功能。通过MC_Groups和MC_Interpolator相关的功能块,我们可以方便地在 PLC 的 ST 语言程序中实现上述复杂运动。理解并掌握这些功能块的使用,是进行高端运动控制项目开发的基石。
2. 环境准备与版本说明
在开始编程之前,确保你的开发环境已正确配置。以下配置是本文示例的基础,请根据你的实际硬件进行调整。
CODESYS 开发环境:
- 版本: CODESYS Development System V3.5.18.0 或更高版本(推荐使用较新的稳定版)。确保已安装SoftMotion组件。你可以在 CODESYS Store 中下载或通过安装包集成。
- 检查方法: 打开 CODESYS,在菜单栏选择“工具” -> “包管理器”,查看已安装的包中是否有“SoftMotion”相关的包。
运行时与硬件:
- 控制器: 任何支持 CODESYS SoftMotion 且性能足够的 PLC 或 IPC。例如,倍福(Beckhoff)CX系列、施耐德(Schneider)M系列、或基于 CODESYS Runtime 的第三方控制器。
- 伺服驱动器与电机: 本文以汇川伺服(如IS620N系列)为例,但其通信和控制原理(如通过 EtherCAT 或 Modbus)适用于其他支持标准 CiA 402 行规的伺服驱动器。
- 通信总线: EtherCAT 是实现高性能多轴同步运动的首选。本文假设项目使用 EtherCAT 总线连接各伺服轴。
软件组件:
- 在项目中需要添加
SoftMotion Basic和SoftMotion CNC库(通常包含在 SoftMotion 安装包中)。 - 正确配置 EtherCAT 主站和设备描述文件(ESI)。
- 在项目中需要添加
示例项目结构预览:
MyInterpolationProject ├── PLC_PRG (PRG) │ └── MAIN (Program) - 主程序,调用运动控制逻辑 ├── GVL (Global Variable List) - 全局变量声明,如轴、轴组、功能块实例 ├── POUs (Program Organization Units) │ ├── FB_AxisGroupControl (Function Block) - 轴组控制逻辑封装 │ └── FB_SingleAxisControl (Function Block) - 单轴使能/归零逻辑 └── Device (Device Tree) - 硬件配置,EtherCAT主站,伺服驱动器从站版本需要根据你的项目实际情况调整,本文重点演示配置思路和核心代码逻辑。
3. 核心功能块与原理拆解
CODESYS SoftMotion 提供了丰富的功能块。我们聚焦于实现轴组插补最关键的几个。
3.1 轴组管理:MC_GroupEnable与MC_GroupDisable
在控制轴组之前,必须使能(Enable)它。这类似于单轴的使能操作,但是针对整个组。
MC_GroupEnable: 使能一个轴组。当Execute引脚上升沿触发时,功能块开始使能组内所有已配置的轴。只有所有轴都使能成功,Done信号才会置位。MC_GroupDisable: 禁用一个轴组。会同时禁用组内所有轴。- 为什么重要: 轴组使能是进行任何插补运动的前提。它确保了组内所有轴都处于“可操作”状态。
3.2 插补运动控制:MC_MoveLinearAbsolute与MC_MoveCircularAbsolute
这是实现路径运动的核心功能块。
MC_MoveLinearAbsolute(直线插补):- 功能: 控制轴组从当前位置,沿直线同步运动到绝对目标位置。
- 关键参数:
Group: 目标轴组的引用。Execute: 上升沿触发运动。Position: 目标点坐标,是一个数组(ARRAY [1..N] OF LREAL),N为轴组内轴的数量。例如,对于X-Y两轴组,Position := [100.0, 50.0]表示运动到 (X=100, Y=50)。Velocity,Acceleration,Deceleration: 路径速度、加速度、减速度。Done: 运动完成信号。Busy: 运动执行中信号。CommandAborted: 运动被中止信号。
MC_MoveCircularAbsolute(圆弧插补):- 功能: 控制轴组沿二维或三维圆弧运动。本文以最常见的二维(平面)圆弧为例。
- 关键参数:
Group: 目标轴组。Execute: 上升沿触发。CircMode: 圆弧模式。常用mcCenter(通过圆心和终点定义圆弧)和mcRadius(通过半径、终点和旋转方向定义)。AuxPoint: 辅助点。当CircMode=mcCenter时,此参数表示圆心坐标(数组)。例如,从 (0,0) 开始,圆心在 (50,0),终点在 (100,0),则绘制一个半圆。Position: 圆弧终点坐标(数组)。PathChoice: 圆弧方向(mcClockwise顺时针,mcCounterClockwise逆时针)。Done,Busy等状态信号。
3.3 运动状态与停止控制:MC_GroupStop与MC_GroupReadStatus
MC_GroupStop: 紧急停止或平滑停止轴组运动。Deceleration参数决定停止的急缓程度。MC_GroupReadStatus: 读取轴组的当前状态,如是否使能、是否在运动中、当前路径速度、各轴当前位置等。用于监控和条件判断。
常见误区:
- 坐标混淆: 直线插补的
Position和圆弧插补的AuxPoint/Position都是基于轴组坐标系的空间点坐标,而不是单个电机的脉冲数。需要根据机械结构(如龙门架、SCARA)建立正确的运动学转换(Kinematics),CODESYS 也支持简单的运动学配置。 - 单位统一: 位置、速度、加速度的单位必须在整个项目中保持一致(如毫米、毫米/秒、毫米/秒²)。
- 状态机管理: 必须妥善管理功能块的
Execute信号和状态信号(Done,Busy,Error),避免同时触发多个运动指令导致冲突。通常使用状态机(State Machine)来管理运动流程。
4. 完整实战案例:X-Y平台绘制矩形和圆形
假设我们控制一个X-Y二维平台,两个轴分别由汇川伺服驱动,通过EtherCAT接入CODESYS控制器。目标是:先使能轴组,然后执行一个“回原点”操作(假设已实现),接着完成一个矩形路径(直线插补)和一个圆形路径(圆弧插补)的运动。
4.1 创建项目与硬件配置
- 新建项目: 打开 CODESYS,创建新项目,选择适合你控制器的设备(如“CODESYS Control Win V3”用于仿真,或具体的硬件设备类型)。
- 添加库: 在项目树中右键“库管理器”,添加
SoftMotion Basic和SoftMotion CNC。 - 配置设备树:
- 添加“EtherCAT Master”设备。
- 在 EtherCAT 主站下,通过“扫描”或手动添加,配置你的汇川伺服驱动器从站(需要对应的 ESI 文件)。
- 为每个伺服驱动器从站下,添加“AXIS_REF”对象,并将其映射到实际的驱动器通道。正确配置电机参数(每转脉冲数、齿轮比等)。
4.2 声明全局变量与轴组
在GVL(Global Variables) 中声明核心变量。
// GVL_GlobalVars VAR_GLOBAL // 单轴引用 (假设已在设备树中配置并链接到物理轴) xAxis: AXIS_REF; yAxis: AXIS_REF; // 轴组声明 xyGroup: AXES_GROUP_REF; // 功能块实例 fbGroupEnable: MC_GroupEnable; fbGroupDisable: MC_GroupDisable; fbMoveLinear: MC_MoveLinearAbsolute; fbMoveCircular: MC_MoveCircularAbsolute; fbGroupStop: MC_GroupStop; fbGroupStatus: MC_GroupReadStatus; // 控制信号 bEnableGroup: BOOL := FALSE; bStartRectangle: BOOL := FALSE; bStartCircle: BOOL := FALSE; bStop: BOOL := FALSE; // 状态显示 sGroupStatus: STRING(255); fPathVelocity: LREAL; aActualPosition: ARRAY[1..2] OF LREAL; // X, Y 实际位置 // 运动参数 fVelocity: LREAL := 50.0; // 路径速度 mm/s fAcceleration: LREAL := 100.0; // 加速度 mm/s² fDeceleration: LREAL := 100.0; // 减速度 mm/s² // 矩形顶点坐标 (单位:mm) aRectPoints: ARRAY[0..3] OF ARRAY[1..2] OF LREAL := [ [0.0, 0.0], // 点0: 起点/终点 [100.0, 0.0], // 点1 [100.0, 50.0],// 点2 [0.0, 50.0] // 点3 ]; nRectIndex: INT := 0; // 当前矩形顶点索引 // 圆形参数 fCircleRadius: LREAL := 25.0; // 半径 25mm aCircleCenter: ARRAY[1..2] OF LREAL := [50.0, 25.0]; // 圆心 (基于矩形中心) bCircleClockwise: BOOL := TRUE; // 顺时针 END_VAR4.3 编写主程序与运动逻辑
在PLC_PRG或一个自定义的功能块(如FB_AxisGroupControl)中编写核心逻辑。这里我们将逻辑放在主程序MAIN中以便演示。
// PROGRAM MAIN VAR // 状态机变量 eState: (STATE_IDLE, STATE_ENABLING, STATE_HOMING, STATE_READY, STATE_MOVING_RECT, STATE_MOVING_CIRCLE, STATE_STOPPING); bHomingDone: BOOL := FALSE; // 假设单轴回零已完成 END_VAR // 状态机执行 CASE eState OF STATE_IDLE: sGroupStatus := 'Idle'; IF bEnableGroup THEN eState := STATE_ENABLING; bEnableGroup := FALSE; // 复位按钮信号 END_IF STATE_ENABLING: // 使能轴组 fbGroupEnable( Group:= xyGroup, Execute:= NOT fbGroupEnable.Done, // 持续执行直到完成 EnablePosControl:= TRUE ); IF fbGroupEnable.Done THEN eState := STATE_HOMING; ELSIF fbGroupEnable.Error THEN sGroupStatus := CONCAT('Enable Error: ', DWORD_TO_STRING(fbGroupEnable.ErrorID)); eState := STATE_IDLE; END_IF STATE_HOMING: // 此处应调用单轴回零功能块,假设完成后 bHomingDone 为 TRUE // fbHomeX(axis:= xAxis, ...); // fbHomeY(axis:= yAxis, ...); IF bHomingDone THEN eState := STATE_READY; sGroupStatus := 'Ready for Motion'; END_IF STATE_READY: // 监控停止命令 IF bStop THEN eState := STATE_STOPPING; bStop := FALSE; ELSIF bStartRectangle THEN eState := STATE_MOVING_RECT; nRectIndex := 0; // 从第一个点开始 bStartRectangle := FALSE; ELSIF bStartCircle THEN eState := STATE_MOVING_CIRCLE; bStartCircle := FALSE; END_IF // 持续读取状态 fbGroupStatus(Group:= xyGroup, Enable:= TRUE); aActualPosition := fbGroupStatus.Position; fPathVelocity := fbGroupStatus.Velocity; STATE_MOVING_RECT: // 执行矩形路径(连续直线插补) IF NOT fbMoveLinear.Busy AND NOT fbMoveLinear.Done THEN // 触发移动到下一个点 fbMoveLinear( Group:= xyGroup, Execute:= TRUE, Position:= aRectPoints[nRectIndex], Velocity:= fVelocity, Acceleration:= fAcceleration, Deceleration:= fDeceleration ); ELSIF fbMoveLinear.Done THEN // 一个点运动完成,准备下一个点 fbMoveLinear.Execute := FALSE; nRectIndex := nRectIndex + 1; IF nRectIndex > 3 THEN // 矩形完成,回到 READY 状态 nRectIndex := 0; eState := STATE_READY; sGroupStatus := 'Rectangle Finished'; END_IF ELSIF fbMoveLinear.Error THEN sGroupStatus := CONCAT('Linear Move Error: ', DWORD_TO_STRING(fbMoveLinear.ErrorID)); eState := STATE_STOPPING; END_IF STATE_MOVING_CIRCLE: // 执行整圆圆弧插补 (通过圆心和终点定义,终点=起点) // 假设起点为 (aCircleCenter[1]+fCircleRadius, aCircleCenter[2]) 即 (75, 25) IF NOT fbMoveCircular.Busy AND NOT fbMoveCircular.Done THEN fbMoveCircular( Group:= xyGroup, Execute:= TRUE, CircMode:= mcCenter, // 使用圆心模式 AuxPoint:= aCircleCenter, // 圆心坐标 Position:= [aCircleCenter[1] + fCircleRadius, aCircleCenter[2]], // 终点坐标(与起点重合,形成整圆) PathChoice:= IIF(bCircleClockwise, mcClockwise, mcCounterClockwise), Velocity:= fVelocity, Acceleration:= fAcceleration, Deceleration:= fDeceleration ); ELSIF fbMoveCircular.Done THEN fbMoveCircular.Execute := FALSE; eState := STATE_READY; sGroupStatus := 'Circle Finished'; ELSIF fbMoveCircular.Error THEN sGroupStatus := CONCAT('Circular Move Error: ', DWORD_TO_STRING(fbMoveCircular.ErrorID)); eState := STATE_STOPPING; END_IF STATE_STOPPING: fbGroupStop( Group:= xyGroup, Execute:= TRUE, Deceleration:= fDeceleration ); IF fbGroupStop.Done THEN eState := STATE_READY; sGroupStatus := 'Stopped'; END_IF END_CASE // 在任意状态,如果收到禁用命令,可以跳转到禁用流程 // 这里简化处理,可以在 READY 或 IDLE 状态处理 bEnableGroup=FALSE 来调用 fbGroupDisable4.4 轴组初始化与链接
在程序初始化阶段(如MAIN的第一个扫描周期或一个专门的初始化功能块中),需要将物理轴链接到轴组,并设置组参数。
// 初始化代码 (可在 MAIN 的第一次扫描执行) IF NOT bInitDone THEN // 1. 创建轴组(通常在配置中完成,这里演示代码设置) // MC_GroupCreate 功能块或直接配置 // 2. 将轴添加到组中 MC_GroupAddAxis(Group:= xyGroup, Axis:= xAxis, AxisIdentifier:= 1); // X轴为组内第1轴 MC_GroupAddAxis(Group:= xyGroup, Axis:= yAxis, AxisIdentifier:= 2); // Y轴为组内第2轴 // 3. 设置组参数(可选) // MC_GroupSetParameter(...) bInitDone := TRUE; END_IF注意: 更常见的做法是在 CODESYS 的设备树或专门的“运动控制设置”编辑器中图形化地创建和配置轴组,这样更直观且不易出错。代码方式提供了更灵活的动态控制能力。
4.5 运行与验证
- 编译与下载: 将程序编译无误后,下载到你的控制器(或仿真环境)。
- 监控变量: 打开 CODESYS 的“在线”模式和“监视”视图,添加
GVL中的控制变量(bEnableGroup,bStartRectangle等)和状态变量(sGroupStatus,aActualPosition,fPathVelocity)进行监控。 - 操作流程:
- 将
bEnableGroup置为TRUE,观察状态从IDLE变为ENABLING再变为READY。fbGroupEnable.Done应为TRUE。 - (确保回零完成,
bHomingDone为TRUE)。 - 将
bStartRectangle置为TRUE,触发矩形运动。观察aActualPosition数组值的变化,它应依次经过[0,0]->[100,0]->[100,50]->[0,50]->[0,0]。fPathVelocity应在运动时接近设定的fVelocity。 - 矩形完成后,将
bStartCircle置为TRUE,触发圆形运动。观察aActualPosition轨迹应为一个以(50,25)为圆心、半径为25的圆。 - 在任何
READY或运动状态,将bStop置为TRUE,轴组应平滑停止。
- 将
- 可视化: 如果控制器支持,可以使用 CODESYS 的“可视化”编辑器创建一个简单的 XY 轨迹图,将
aActualPosition[1]和aActualPosition[2]绑定到曲线的 X 和 Y 坐标,实时观察运动路径。
5. 常见问题与排查思路
在实际调试中,你可能会遇到以下问题:
| 问题现象 | 可能原因 | 排查步骤与解决方案 |
|---|---|---|
轴组使能失败(fbGroupEnable.Error = TRUE) | 1. 组内某个物理轴未就绪(未上电、通信中断、驱动器报警)。 2. 轴组未正确创建或轴未添加到组中。 3. 运动资源冲突。 | 1. 检查每个单轴的状态字 (AXIS_REF.Status),确保其处于“可操作”状态。排查驱动器报警和通信。2. 在线查看轴组对象 xyGroup的属性,确认其包含的轴列表是否正确。3. 检查是否有其他程序或功能块正在占用该轴组。 |
| 直线/圆弧插补不执行或立即报错 | 1. 轴组未使能 (fbGroupEnable.Done不为TRUE)。2. 坐标数组维度与轴组轴数不匹配。 3. 圆弧参数非法(例如,半径太小、三点共线无法确定圆)。 4. 目标位置超出软限位。 | 1. 确保运动前轴组已使能且处于READY状态。2. 检查 Position和AuxPoint数组的声明长度,必须等于轴组中的轴数量。3. 计算圆心、半径、终点是否构成有效的几何圆。使用 CAD 软件或公式预先验证坐标。 4. 检查单轴和轴组的软限位参数是否设置正确。 |
| 运动轨迹偏差大或不同步 | 1. 各轴伺服环参数(PID)不一致或未优化。 2. 机械传动存在间隙或刚性不足。 3. 轴组内各轴的运动学关系配置错误(例如,未正确设置齿轮比、耦合比)。 | 1. 分别对每个单轴进行伺服调试,确保其定位准确、响应一致。 2. 检查机械结构,排除硬件问题。 3. 在轴组配置中,仔细检查运动学设置。对于简单的笛卡尔XY平台,通常使用“未耦合”或“直接”映射。 |
| 运动过程中出现剧烈抖动或异响 | 1. 速度、加速度设置过高,超过机械或电机负载能力。 2. 插补周期设置不合理。 3. 驱动器跟随误差过大报警。 | 1. 逐步降低fVelocity,fAcceleration,fDeceleration参数,观察现象是否改善。2. 检查 CODESYS 任务和 EtherCAT 主站的周期设置,确保运动控制任务周期稳定且足够快(通常1-4ms)。 3. 监控驱动器的跟随误差,适当调整伺服增益。 |
MC_MoveCircularAbsolute不画整圆 | 圆弧插补功能块一次只执行一段圆弧。要画整圆,需要确保起点、圆心、终点的几何关系构成一个完整的360度圆弧。在圆心模式下,如果终点坐标与起点坐标相同,且起点、圆心、终点不共线,则会执行一个整圆。 | 仔细计算起点、圆心、终点坐标。本文示例中,起点是(圆心X+半径, 圆心Y),终点与起点相同,即可绘制整圆。也可以使用mcRadius模式并指定Angle为 360 度。 |
6. 最佳实践与工程建议
将轴组插补功能投入实际生产项目时,遵循以下建议可以提升代码的可靠性、可维护性和安全性。
模块化与封装:
- 不要将所有逻辑堆砌在
MAIN中。如示例所示,将轴组使能、回零、插补运动、状态监控等分别封装成独立的功能块(FB)。 - 创建一个顶层的运动管理功能块,内部使用状态机来协调各个子功能块的调用。这样主程序只需调用这个管理块并给出高级指令(如“移动到A点”、“执行路径B”)。
- 不要将所有逻辑堆砌在
错误处理与恢复:
- 每个运动控制功能块都必须检查其
Error和ErrorID输出。 - 实现统一的错误处理程序,记录错误代码、时间戳和上下文信息,并触发安全的停止序列(如调用
MC_GroupStop)。 - 考虑设计错误复位和流程恢复机制,而不是简单地停机等待人工干预。
- 每个运动控制功能块都必须检查其
参数化与配方管理:
- 将运动参数(速度、加速度、坐标点)存储在全局变量列表(GVL)或专用的数据结构(如
STRUCT)中,甚至可以从文件(如CSV、JSON)或上位机HMI读取。 - 对于不同的加工产品(Recipe),可以轻松切换不同的参数集,提高设备柔性。
- 将运动参数(速度、加速度、坐标点)存储在全局变量列表(GVL)或专用的数据结构(如
安全与互锁:
- 运动指令的触发必须与安全条件(如光栅状态、门锁信号、急停按钮)进行硬接线或程序互锁。
- 使用
MC_GroupStop的Deceleration参数实现平滑的紧急停止,避免对机械造成冲击。 - 在程序开始时和周期性地检查轴组和单轴的状态,预防性维护。
性能优化:
- 任务周期: 运动控制任务应设置为高优先级、固定周期的任务。周期时间决定了插补的精度和命令更新率,通常为1-4ms。
- EtherCAT 同步: 确保 EtherCAT 主站配置了合适的分布式时钟(DC)同步模式,以实现纳秒级的多轴同步,这是高精度插补的硬件基础。
- 缓冲区管理: 对于复杂的连续路径(如CNC G代码),可以使用
MC_BufferMode相关的功能块进行运动指令缓冲,实现连续运动而不停顿。
仿真与测试:
- 在连接真实硬件前,充分利用 CODESYS 的SoftMotion 仿真功能。可以在没有物理伺服的情况下,测试所有的逻辑、状态机和轨迹规划是否正确。
- 使用 CODESYS 的跟踪(Trace)功能,录制关键变量(位置、速度、误差)随时间的变化,是分析运动性能、诊断问题的利器。
掌握 CODESYS 的轴组与插补功能,是打开高端运动控制应用大门的钥匙。从理解基本概念开始,通过本文提供的实战框架逐步搭建和调试,你就能让多个伺服轴优雅地跳出协同的“舞蹈”,精确绘制出所需的任何平面或空间轨迹。记住,耐心调试参数、严谨处理状态和错误,是工程成功的关键。