36伺服单件分离系统
硬件配置
- 主站: Windows 10 × 1 + Intel i5 × 1
- 运动控制: 杰美康 JAWD-C2 EtherCAT 双轴伺服驱动器 × 18 + ≥200W 大扭矩伺服电机 × 36
- 视觉系统: 海康 MV-CS060-10GC 面阵工业相机 × 1 (俯视辊筒床, 识别包裹位置与轮廓)
双轴驱动方案
采用杰美康 JAWD-C2 双轴伺服驱动器,一台驱动器同时控制两段辊筒电机(Axis A + Axis B), 18 台驱动器即可驱动 36 段辊筒。相比 36 台单轴驱动器,设备数量减半、EtherCAT 总线帧长度缩短、 接线复杂度大幅降低,综合成本节省约 35%。
成本分析
| 类别 | 型号/规格 | 参考单价 (¥) | 数量 | 小计 (¥) |
|---|---|---|---|---|
| 双轴伺服驱动器 | 杰美康 JAWD-C2 | 1,600 | 18 | 28,800 |
| 伺服电机 | ≥200W 大扭矩伺服 (按负载选型) | 450 | 36 | 16,200 |
| 面阵工业相机 | 海康 MV-CS060-10GC | 3,000 | 1 | 3,000 |
| 工控机 | Intel i5 级别 | 3,500 | 1 | 3,500 |
| 整线参考总计 | ≈ ¥51,500 |
以上为核心部件参考价 (RMB),不含辊筒机架、线缆等。 电机功率需根据辊筒负载、包裹重量及加减速要求实际选型,重载场景可能需要 400W 或更高 也可以选择步进。
性能指标
- 处理能力: ≥ 6000 件/小时
- 分离成功率: ≥ 99.5%
- 辊筒床尺寸: 6行 × 6列, 可适配 50mm ~ 800mm 包裹
- 最大输送速度: 2.5m/s
应用场景
快递分拣中心单件分离机(Singulator)。36 段独立辊筒按 6行×6列 矩阵排布组成辊筒床,包裹从入口侧批量涌入,堆叠或紧贴在辊筒床上。面阵相机从正上方俯视整个矩阵,实时识别每个包裹的位置和轮廓。视觉算法为每个辊筒区域分配不同速度,利用相邻区域的速差将紧贴的包裹逐步拉开间距,最终从出口侧单列排出,送入下游交叉带分拣机逐件处理。
系统架构
机器视觉方案
本案例仅展示单相机基础方案。我们可根据实际场景提供更多机器视觉解决方案,包括多相机拼接覆盖超宽辊筒床、3D 结构光测量包裹体积、深度学习包裹分割与追踪等。
方案与产品服务
我们是专业的工业自动化技术公司,拥有多家行业专用设备制造商合作者。除软件授权外,同样提供整线方案设计、硬件选型、设备采购及技术支持等一站式服务,欢迎咨询合作。
代码示例
PDO 结构体定义
using System.Runtime.InteropServices;
/// <summary>
/// 杰美康 JAWD-C2 双轴伺服驱动器输入 PDO (TxPDO)
/// CSV 模式: 0x1A00 (Axis A) + 0x1A10 (Axis B)
/// CiA 402 多轴: Axis B 对象索引 = Axis A + 0x0800
/// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct JAWD_Input
{
// ---- Axis A ----
public ushort StatusWord_A; // 0x6041:00 状态字
public int ActualPosition_A; // 0x6064:00 实际位置 (编码器计数)
public int ActualVelocity_A; // 0x606C:00 实际速度 (rpm)
public short ActualTorque_A; // 0x6077:00 实际转矩 (‰额定)
public uint DigitalInputs_A; // 0x60FD:00 驱动器数字输入
// ---- Axis B ----
public ushort StatusWord_B; // 0x6841:00 状态字
public int ActualPosition_B; // 0x6864:00 实际位置 (编码器计数)
public int ActualVelocity_B; // 0x686C:00 实际速度 (rpm)
public short ActualTorque_B; // 0x6877:00 实际转矩 (‰额定)
public uint DigitalInputs_B; // 0x68FD:00 驱动器数字输入
}
/// <summary>
/// 杰美康 JAWD-C2 双轴伺服驱动器输出 PDO (RxPDO)
/// CSV 模式: 0x1600 (Axis A) + 0x1610 (Axis B)
/// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct JAWD_Output
{
// ---- Axis A ----
public ushort ControlWord_A; // 0x6040:00 控制字
public sbyte ModesOfOperation_A; // 0x6060:00 操作模式 (9=CSV)
public int TargetVelocity_A; // 0x60FF:00 目标速度 (rpm)
// ---- Axis B ----
public ushort ControlWord_B; // 0x6840:00 控制字
public sbyte ModesOfOperation_B; // 0x6860:00 操作模式 (9=CSV)
public int TargetVelocity_B; // 0x68FF:00 目标速度 (rpm)
}
/// <summary>
/// 辊筒区域 (6×6 矩阵中的一个单元, 内部使用)
/// </summary>
public class RollerZone
{
public int DriveIndex; // 对应 JAWD 驱动器从站索引
public bool IsAxisB; // true=AxisB, false=AxisA
}
控制代码
using System;
using System.Threading;
using DarraEtherCAT_Master;
/// <summary>
/// 单件分离控制器
/// 职责: EtherCAT 伺服管理, 对外暴露 6×6 速度控制矩阵
/// 分离算法由视觉系统独立实现, 通过 SpeedMatrix 下发每个区域的目标速度 (rpm)
/// </summary>
class SingulationController
{
const int ROWS = 6;
const int COLS = 6;
const int ZONE_COUNT = ROWS * COLS; // 36
const int DRIVE_COUNT = 18;
const int SLAVE_DRIVE_START = 0;
const sbyte MODE_CSV = 9;
// 内部驱动映射 (行优先, 每台JAWD驱动同行相邻两区域)
readonly RollerZone[,] grid = new RollerZone[ROWS, COLS];
readonly object matrixLock = new object();
// ============ 对外控制矩阵 (视觉系统读写) ============
/// <summary>
/// 6×6 辊筒速度控制矩阵, 单位: rpm
/// 视觉系统直接写入每个区域的目标转速, 控制器每 1ms 同步到伺服
/// SpeedMatrix[row, col]: row 0=入口侧, row 5=出口侧
/// </summary>
public readonly int[,] SpeedMatrix = new int[ROWS, COLS];
// ============ 主流程 ============
void InitGrid()
{
for (int r = 0; r < ROWS; r++)
{
for (int c = 0; c < COLS; c++)
{
int linearIdx = r * COLS + c;
grid[r, c] = new RollerZone
{
DriveIndex = SLAVE_DRIVE_START + (linearIdx / 2),
IsAxisB = (linearIdx % 2) == 1
};
}
}
}
void Run()
{
InitGrid();
var master = new DarraEtherCAT()
.LoadENI("config/singulation_36servo.xml")
.Build();
if (master == null)
{
Console.WriteLine("主站初始化失败!");
return;
}
try
{
Console.WriteLine($"发现 {master.Slaves.Count} 个从站");
var (success, msg) = master.SetState(EcState.OP);
if (!success)
{
Console.WriteLine($"无法进入 OP 状态: {msg}");
return;
}
EnableAllServos(master);
Console.WriteLine("36伺服单件分离系统启动!");
Console.WriteLine($"辊筒矩阵: {ROWS}×{COLS}, 等待视觉系统写入 SpeedMatrix...\n");
// 预计算每台驱动器对应的矩阵行列索引
// 每台 JAWD 驱动对应矩阵中连续两个区域: AxisA = linearIdx, AxisB = linearIdx+1
int[,] driveZone = new int[DRIVE_COUNT, 4]; // [d, 0]=rowA, [d,1]=colA, [d,2]=rowB, [d,3]=colB
for (int d = 0; d < DRIVE_COUNT; d++)
{
int zA = d * 2, zB = d * 2 + 1;
driveZone[d, 0] = zA / COLS; driveZone[d, 1] = zA % COLS;
driveZone[d, 2] = zB / COLS; driveZone[d, 3] = zB % COLS;
}
// 主循环: 将 SpeedMatrix 同步到伺服
int cycleCount = 0;
while (true)
{
// 按驱动器遍历, 每台驱动器仅获取一次 PDO 映射, 同时写入双轴速度
for (int d = 0; d < DRIVE_COUNT; d++)
{
ref var output = ref master.Slaves[SLAVE_DRIVE_START + d]
.OutputsMapping<JAWD_Output>();
output.TargetVelocity_A = SpeedMatrix[driveZone[d, 0], driveZone[d, 1]];
output.TargetVelocity_B = SpeedMatrix[driveZone[d, 2], driveZone[d, 3]];
}
if (++cycleCount % 1000 == 0)
Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] 运行中");
Thread.Sleep(1); // 1ms 控制周期
}
}
finally
{
StopAllServos(master);
master.Dispose();
}
}
void EnableAllServos(DarraEtherCAT master)
{
ushort[] steps = { 0x0006, 0x0007, 0x000F };
foreach (var cw in steps)
{
for (int d = 0; d < DRIVE_COUNT; d++)
{
ref var output = ref master.Slaves[SLAVE_DRIVE_START + d]
.OutputsMapping<JAWD_Output>();
if (cw == 0x0006)
{
output.ModesOfOperation_A = MODE_CSV;
output.ModesOfOperation_B = MODE_CSV;
}
output.ControlWord_A = cw;
output.ControlWord_B = cw;
}
Thread.Sleep(10);
}
Console.WriteLine($"{DRIVE_COUNT}台双轴驱动器 ({ZONE_COUNT}轴) 全部使能完成");
}
void StopAllServos(DarraEtherCAT master)
{
for (int d = 0; d < DRIVE_COUNT; d++)
{
ref var output = ref master.Slaves[SLAVE_DRIVE_START + d]
.OutputsMapping<JAWD_Output>();
output.TargetVelocity_A = 0;
output.TargetVelocity_B = 0;
output.ControlWord_A = 0x0006;
output.ControlWord_B = 0x0006;
}
Thread.Sleep(10);
Console.WriteLine("所有伺服已停止");
}
static void Main(string[] args) => new SingulationController().Run();
}
视觉系统使用示例:
// 视觉算法线程直接写入速度矩阵 (rpm)
var ctrl = new SingulationController();
// 设置某个区域加速
ctrl.SpeedMatrix[2, 3] = 3000; // 第3行第4列: 3000 rpm
// 批量设置整行减速
for (int c = 0; c < 6; c++)
ctrl.SpeedMatrix[1, c] = 500; // 第2行全部: 500 rpm