CiA 402 - 伺服驱动器协议
CiA 402 是 CANopen 驱动器和运动控制设备协议,是伺服驱动器最广泛使用的标准,掌握该协议是实现伺服控制的关键。
协议简介
什么是 CiA 402?
CiA 402 是一个基于 CoE (CANopen over EtherCAT) 的设备协议,专门为驱动器和运动控制设备定义标准接口。
协议层次关系:
EtherCAT 应用层
└── CoE (CANopen over EtherCAT)
├── CiA 401 (通用 I/O 模块)
├── CiA 402 (驱动器和运动控制) ← 当前协议
└── 其他 CiA 设备协议
CiA 402 定义了什么?
CiA 402 在 CoE 对象字典的 0x6000-0x67FF 范围内定义了:
- 标准状态机:统一的驱动器状态转换逻辑
- 操作模式:多种运动控制模式(位置、速度、转矩等)
- 标准对象:统一的控制和反馈参数
- PDO 映射:实时运动控制数据交换
- 互操作性:不同厂商驱动器可互换
为什么需要 CiA 402?
- 标准化接口 — 所有驱动器使用相同的对象地址和功能
- 即插即用 — 主站代码无需为不同品牌驱动器定制
- 降低成本 — 减少集成工作量和学习成本
- 行业认可 — 几乎所有伺服厂商都支持 CiA 402
状态机
CiA 402 定义了完整的驱动器状态机
主要状态
| 状态 | 说明 | 控制字 | 状态字 |
|---|---|---|---|
| Not ready to switch on | 初始化状态,驱动未就绪 | - | xxxx xxxx x0xx 0000 |
| Switch on disabled | 驱动禁用,可接收命令 | - | xxxx xxxx x1xx 0000 |
| Ready to switch on | 准备开启,电压未使能 | 0x06 | xxxx xxxx x01x 0001 |
| Switched on | 已开启,电压使能但未运行 | 0x07 | xxxx xxxx x01x 0011 |
| Operation enabled | 运行使能,可正常操作 | 0x0F | xxxx xxxx x01x 0111 |
| Quick stop active | 快速停止激活 | 0x02 | xxxx xxxx x00x 0111 |
| Fault reaction active | 故障反应中 | - | xxxx xxxx x0xx 1111 |
| Fault | 故障状态 | 0x80 | xxxx xxxx x0xx 1000 |
状态转换命令
| 命令 | 控制字 | 说明 |
|---|---|---|
| Shutdown | 0x06 | 关闭,进入 Ready to switch on |
| Switch on | 0x07 | 开启,进入 Switched on |
| Enable operation | 0x0F | 使能操作,进入 Operation enabled |
| Disable voltage | 0x00 | 禁用电压,返回 Switch on disabled |
| Quick stop | 0x02 | 快速停止 |
| Fault reset | 0x80 | 故障复位 |
控制字与状态字
控制字 (Controlword, 0x6040)
| Bit | 名称 | 说明 |
|---|---|---|
| 0 | Switch on | 开启命令 |
| 1 | Enable voltage | 使能电压 |
| 2 | Quick stop | 快速停止(0=激活) |
| 3 | Enable operation | 使能操作 |
| 4 | Operation mode specific | 新设定点 |
| 5 | Operation mode specific | 立即改变 |
| 6 | Operation mode specific | 相对 |
| 7 | Fault reset | 故障复位(上升沿) |
| 8 | Halt | 暂停 |
| 9-10 | Operation mode specific | 操作模式特定 |
| 11-15 | Manufacturer specific | 厂商特定 |
常用控制字:
const ushort SHUTDOWN = 0x06; // 0000 0110
const ushort SWITCH_ON = 0x07; // 0000 0111
const ushort ENABLE_OP = 0x0F; // 0000 1111
const ushort DISABLE_VOLTAGE = 0x00; // 0000 0000
const ushort QUICK_STOP = 0x02; // 0000 0010
const ushort FAULT_RESET = 0x80; // 1000 0000
状态字 (Statusword, 0x6041)
| Bit | 名称 | 说明 |
|---|---|---|
| 0 | Ready to switch on | 准备就绪 |
| 1 | Switched on | 已开启 |
| 2 | Operation enabled | 操作使能 |
| 3 | Fault | 故障 |
| 4 | Voltage enabled | 电压使能 |
| 5 | Quick stop | 快速停止(0=激活) |
| 6 | Switch on disabled | 开启禁用 |
| 7 | Warning | 警告 |
| 8 | Manufacturer specific | 厂商特定 |
| 9 | Remote | 远程模式 |
| 10 | Target reached | 目标到达 |
| 11 | Internal limit active | 内部限位激活 |
| 12 | Operation mode specific | 设定点确认 |
| 13 | Operation mode specific | Following error |
| 14-15 | Manufacturer specific | 厂商特定 |
操作模式
模式定义 (Modes of operation, 0x6060)
| 值 | 缩写 | 模式 | 说明 |
|---|---|---|---|
| 1 | PP | Profile Position Mode | 轮廓位置模式,点到点运动 |
| 3 | PV | Profile Velocity Mode | 轮廓速度模式,速度控制 |
| 4 | PT | Profile Torque Mode | 轮廓转矩模式,转矩控制 |
| 6 | HM | Homing Mode | 回零模式,建立位置参考 |
| 7 | IP | Interpolated Position Mode | 插补位置模式,轨迹跟踪 |
| 8 | CSP | Cyclic Synchronous Position Mode | 周期同步位置模式 |
| 9 | CSV | Cyclic Synchronous Velocity Mode | 周期同步速度模式 |
| 10 | CST | Cyclic Synchronous Torque Mode | 周期同步转矩模式 |
模式详解
PP - Profile Position Mode
特点:
- 点到点运动
- 内部轨迹规划
- 梯形/S形速度曲线
主要对象:
0x607A: Target position(目标位置)0x6081: Profile velocity(轮廓速度)0x6083: Profile acceleration(轮廓加速度)0x6084: Profile deceleration(轮廓减速度)
CSP - Cyclic Synchronous Position Mode
特点:
- 周期性位置指令
- 主站轨迹规划
- 最高实时性
主要对象:
0x607A: Target position(目标位置,每周期更新)0x60B2: Torque offset(转矩偏移)
CSV - Cyclic Synchronous Velocity Mode
特点:
- 周期性速度指令
- 适合速度跟随
主要对象:
0x60FF: Target velocity(目标速度)0x60B2: Torque offset(转矩偏移)
CST - Cyclic Synchronous Torque Mode
特点:
- 周期性转矩指令
- 力控应用
主要对象:
0x6071: Target torque(目标转矩)
常用对象
控制与状态
| 索引 | 名称 | 类型 | 访问 | 说明 |
|---|---|---|---|---|
| 0x6040 | Controlword | UNSIGNED16 | RW/RXPDO | 控制字 |
| 0x6041 | Statusword | UNSIGNED16 | RO/TXPDO | 状态字 |
| 0x6060 | Modes of operation | INTEGER8 | RW | 操作模式 |
| 0x6061 | Modes of operation display | INTEGER8 | RO | 实际操作模式 |
位置对象
| 索引 | 名称 | 类型 | 单位 | 说明 |
|---|---|---|---|---|
| 0x6062 | Position demand value | INTEGER32 | 内部单位 | 位置需求值 |
| 0x6063 | Position actual internal value | INTEGER32 | 内部单位 | 实际位置(内部) |
| 0x6064 | Position actual value | INTEGER32 | 用户单位 | 实际位置 |
| 0x607A | Target position | INTEGER32 | 用户单位 | 目标位置 |
| 0x607C | Home offset | INTEGER32 | 用户单位 | 回零偏移 |
| 0x607D | Software position limit | ARRAY | 用户单位 | 软件位置限制 |
速度对象
| 索引 | 名称 | 类型 | 单位 | 说明 |
|---|---|---|---|---|
| 0x606B | Velocity demand value | INTEGER32 | 内部单位 | 速度需求值 |
| 0x606C | Velocity actual value | INTEGER32 | 用户单位 | 实际速度 |
| 0x60FF | Target velocity | INTEGER32 | 用户单位 | 目标速度 |
| 0x6081 | Profile velocity | UNSIGNED32 | 用户单位 | 轮廓速度 |
| 0x6083 | Profile acceleration | UNSIGNED32 | 用户单位/s² | 轮廓加速度 |
| 0x6084 | Profile deceleration | UNSIGNED32 | 用户单位/s² | 轮廓减速度 |
转矩对象
| 索引 | 名称 | 类型 | 单位 | 说明 |
|---|---|---|---|---|
| 0x6071 | Target torque | INTEGER16 | 千分之额定 | 目标转矩 |
| 0x6077 | Torque actual value | INTEGER16 | 千分之额定 | 实际转矩 |
| 0x60B2 | Torque offset | INTEGER16 | 千分之额定 | 转矩偏移 |
代码示例
伺服使能流程
void EnableServo(Slave servo)
{
ushort statusword;
// 1. 检查并清除故障
byte[] sw = servo.CoE.SDORead(0x6041, 0);
statusword = BitConverter.ToUInt16(sw, 0);
if ((statusword & 0x08) != 0) // Bit 3 = Fault
{
servo.CoE.SDOWrite(0x6040, 0, BitConverter.GetBytes((ushort)0x80));
Thread.Sleep(100);
}
// 2. Shutdown
servo.CoE.SDOWrite(0x6040, 0, BitConverter.GetBytes((ushort)0x06));
Thread.Sleep(10);
// 3. Switch on
servo.CoE.SDOWrite(0x6040, 0, BitConverter.GetBytes((ushort)0x07));
Thread.Sleep(10);
// 4. Enable operation
servo.CoE.SDOWrite(0x6040, 0, BitConverter.GetBytes((ushort)0x0F));
Thread.Sleep(10);
// 验证状态
sw = servo.CoE.SDORead(0x6041, 0);
statusword = BitConverter.ToUInt16(sw, 0);
if ((statusword & 0x27) == 0x27) // Bits 0,1,2,5 = Operation enabled
{
Console.WriteLine("伺服使能成功");
}
}
位置模式(PP)
void ProfilePositionMove(Slave servo, int targetPos)
{
// 1. 设置操作模式为 PP
servo.CoE.SDOWrite(0x6060, 0, new byte[] { 1 });
// 2. 设置运动参数
servo.CoE.SDOWrite(0x6081, 0, BitConverter.GetBytes(1000u)); // 速度 1000
servo.CoE.SDOWrite(0x6083, 0, BitConverter.GetBytes(5000u)); // 加速度
servo.CoE.SDOWrite(0x6084, 0, BitConverter.GetBytes(5000u)); // 减速度
// 3. 写入目标位置
servo.CoE.SDOWrite(0x607A, 0, BitConverter.GetBytes(targetPos));
// 4. 触发运动(bit 4 = new set-point)
servo.CoE.SDOWrite(0x6040, 0, BitConverter.GetBytes((ushort)0x1F));
// 5. 等待到达
while (true)
{
byte[] sw = servo.CoE.SDORead(0x6041, 0);
ushort statusword = BitConverter.ToUInt16(sw, 0);
if ((statusword & 0x0400) != 0) // Bit 10 = Target reached
{
Console.WriteLine("到达目标位置");
break;
}
Thread.Sleep(10);
}
}
周期同步位置模式(CSP)
void CyclicSynchronousPosition(Slave servo)
{
// 1. 设置操作模式为 CSP
servo.CoE.SDOWrite(0x6060, 0, new byte[] { 8 });
// 2. 使能伺服
EnableServo(servo);
// 3. 周期性发送位置
int currentPos = 0;
while (running)
{
// 生成轨迹(示例:正弦波)
currentPos = (int)(10000 * Math.Sin(angle));
angle += 0.01;
// 通过 PDO 发送位置(需提前配置 PDO 映射)
byte[] posData = BitConverter.GetBytes(currentPos);
Array.Copy(posData, 0, outputBuffer, posOffset, 4);
// 或通过 SDO(非实时)
servo.CoE.SDOWrite(0x607A, 0, posData);
master.Exchange(); // 1ms 周期
Thread.Sleep(1);
}
}
单位与缩放
位置单位
通过以下对象配置:
0x6093: Position factor (分子/分母)0x6094: Velocity encoder factor
示例:将用户单位转换为编码器脉冲
用户单位 = 编码器脉冲 × (0x6093 分子 / 0x6093 分母)
速度单位
默认单位:用户单位/秒
转矩单位
千分之额定转矩(1000 = 100%)
回零模式(Homing)
回零方法 (0x6098)
| 方法编号 | 参考信号 | 说明 |
|---|---|---|
| 1-14 | 限位开关 + 零位信号 | 不同方向和序列组合 |
| 17-18 | 原点开关 | 基于原点开关 |
| 19-22 | 原点开关 + 零位信号 | 组合方式 |
| 23-27 | 原点开关(两侧) | 双侧原点开关 |
| 33-34 | 零位信号 | 仅使用零位信号 |
| 35 | 当前位置 | 当前位置设为原点 |
回零流程
void HomingSequence(Slave servo)
{
// 1. 设置回零模式
servo.CoE.SDOWrite(0x6060, 0, new byte[] { 6 });
// 2. 设置回零方法(例如:方法 1)
servo.CoE.SDOWrite(0x6098, 0, new byte[] { 1 });
// 3. 设置回零速度
servo.CoE.SDOWrite(0x6099, 1, BitConverter.GetBytes(500u)); // 搜索速度
servo.CoE.SDOWrite(0x6099, 2, BitConverter.GetBytes(100u)); // 零位速度
// 4. 启动回零(bit 4 = start)
servo.CoE.SDOWrite(0x6040, 0, BitConverter.GetBytes((ushort)0x1F));
// 5. 等待完成
while (true)
{
byte[] sw = servo.CoE.SDORead(0x6041, 0);
ushort statusword = BitConverter.ToUInt16(sw, 0);
if ((statusword & 0x1000) != 0) // Bit 12 = Homing attained
{
Console.WriteLine("回零完成");
break;
}
if ((statusword & 0x2000) != 0) // Bit 13 = Homing error
{
Console.WriteLine("回零错误");
break;
}
Thread.Sleep(10);
}
}
状态字解析
string GetDriveState(ushort statusword)
{
if ((statusword & 0x4F) == 0x00) return "Not ready to switch on";
if ((statusword & 0x4F) == 0x40) return "Switch on disabled";
if ((statusword & 0x6F) == 0x21) return "Ready to switch on";
if ((statusword & 0x6F) == 0x23) return "Switched on";
if ((statusword & 0x6F) == 0x27) return "Operation enabled";
if ((statusword & 0x6F) == 0x07) return "Quick stop active";
if ((statusword & 0x4F) == 0x0F) return "Fault reaction active";
if ((statusword & 0x4F) == 0x08) return "Fault";
return "Unknown state";
}
// 使用
byte[] sw = servo.CoE.SDORead(0x6041, 0);
ushort statusword = BitConverter.ToUInt16(sw, 0);
Console.WriteLine($"驱动状态: {GetDriveState(statusword)}");