跳到主要内容

装配工位

TIA 设备名 = 配置工具站点名(例如 asm-st-01)。

硬件配置

  • PLC:西门子 S7-1200 × 1(夹具夹紧 / 放行 / NG 剔除)
  • 工控机:Windows 10 × 1 + Intel i5 × 1,跑 Darra Profinet Slave
  • 视觉:工业面阵相机 × 1 + 镜头 × 1 + 环形光源 × 1
  • 夹紧工装:气缸 × 1,夹紧到位磁性开关 × 1。PLC 管夹紧、松开、放行、NG 剔除
  • 网络:标准千兆网卡 × 1(与 PLC 同一 PROFINET 网)

成本分析

类别型号/规格参考单价 (¥)数量小计 (¥)
PLC西门子 S7-1200 CPU 1214C1,00011,000
工控机i5 无风扇双网口1,20011,200
面阵相机海康 MV-CA013 130 万 USB39501950
镜头C 口工业定焦2001200
环形光源机器视觉 LED1501150
气动夹具亚德客附磁气缸 + 磁性开关1201120
整线参考总计≈ ¥3,620

淘宝常见成交价(2026-09),不含机架、气管、线缆、治具底板。PLC 取 1214C 正品常见区间,相机取海康 130 万 USB3 店售价,不取几十元板级货。

性能指标

  • 对位判定:本地完成,过程数据只回 OK / NG / 完成
  • 配方:结构体字段 Recipe 选本地模板,工位可挂多套
  • 节拍:视觉小于 150 ms/件(仿真用睡眠代替)
  • 图、偏移明细不进过程数据

应用场景

压装 / 拧紧 / 点胶前的装配工位。比 小型视觉质检 多三路:夹紧到位、完成边沿、多配方模板。PLC 管夹紧、放行、NG 剔除;工控机按配方在本地对位。

工位时序

  1. 有料 — PLC 置 Present
  2. 夹紧到位才触发 — PLC 夹紧,等磁性开关 Clamped。未夹紧不得拉 Trigger。工控机侧同样:无 Clamped 的上升沿丢掉。
  3. 对位上升沿Clamped 后 PLC 拉高 Trigger。工控机只认 0→1,且 PresentClamped、未忙碌。
  4. 忙碌互斥 — 认沿后清上次 OK/NG/DoneReady=0Busy=1。忙碌期间新沿丢掉。
  5. 配方选本地模板Recipe 只当模板号,对位图和偏移留在工控机。
  6. Done 边沿 — 判定结束:OkNgBusy=0Ready=1Done 0→1。PLC 用 Done 上升沿步进(OK 放行 / NG 剔除 / 松开)。Done 保持到下一件认沿时拉回 0,下一件结束再产生新的上升沿。
  7. 掉线 — 曾经连上过、后来又断开:Fault=1Ready 拿掉。重连后重新置就绪,当前 Trigger 电平不当成新沿。
过程数据

过程数据只回 OK / NG / 完成和配方号。对位图、偏移明细、存档路径留在工控机。

过程数据

方向字段工位含义
来自 PLC(I)Present有料
来自 PLC(I)Clamped夹紧到位;未到位不认 Trigger
来自 PLC(I)Trigger对位触发,只认上升沿
来自 PLC(I)Recipe配方号,本地选对位模板
发给 PLC(Q)Ready可接下一件
发给 PLC(Q)Busy正在对位,互斥
发给 PLC(Q)Ok对位合格 → PLC 放行
发给 PLC(Q)Ng对位不合格 → PLC 打剔除
发给 PLC(Q)Fault掉线/故障
发给 PLC(Q)Done本件判定结束,PLC 采上升沿

连上之后再按上面握手读写。仿真按件数造 OK/NG(每 7 件 1 个 NG)。真机把 Thread.Sleep 换成取图和对位。

using System;
using System.Runtime.InteropServices;
using System.Threading;
using Darra.PnS;

[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct FromPlc
{
public bool Present;
public bool Clamped;
public bool Trigger;
public short Recipe;
}

[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct ToPlc
{
public bool Ready;
public bool Busy;
public bool Ok;
public bool Ng;
public bool Fault;
public bool Done;
}

using (var device = new DarraPnS())
{
PnSErrorCode rc = device.Start();
if (rc != PnSErrorCode.Success)
return;

string[] templates = { "default.bin", "housing-a.bin", "housing-b.bin" };
ToPlc output = default;
bool lastTrigger = true;
bool wasConnected = false;
bool busy = false;
int piece = 0;
bool running = true;

while (running)
{
if (!device.Connected)
{
if (wasConnected)
{
output = new ToPlc { Fault = true };
device.ProcessData.Write(ref output);
lastTrigger = true;
busy = false;
}
wasConnected = false;
Thread.Sleep(20);
continue;
}

if (!wasConnected)
{
output = new ToPlc { Ready = true };
device.ProcessData.Write(ref output);
lastTrigger = true;
busy = false;
wasConnected = true;
}

FromPlc input = new FromPlc();
device.ProcessData.Read(ref input);

bool rising = input.Trigger && !lastTrigger;
lastTrigger = input.Trigger;

if (rising && input.Present && input.Clamped && !busy)
{
busy = true;
output = new ToPlc { Busy = true };
device.ProcessData.Write(ref output);

string template = templates[0];
int recipe = input.Recipe;
if (recipe >= 0 && recipe < templates.Length)
template = templates[recipe];

Thread.Sleep(120);

piece++;
bool ok = (piece % 7) != 0;

output = new ToPlc { Ready = true, Ok = ok, Ng = !ok, Done = true };
device.ProcessData.Write(ref output);
busy = false;

Console.WriteLine(
"第 " + piece + " 件 模板 " + template + " → " + (ok ? "OK 放行" : "NG 剔除") + " Done");
}

Thread.Sleep(10);
}

device.Stop();
}