跳到主要内容

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-C21,6001828,800
伺服电机≥200W 大扭矩伺服 (按负载选型)4503616,200
面阵工业相机海康 MV-CS060-10GC3,00013,000
工控机Intel i5 级别3,50013,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