摘要:在现代工业自动化产线中,PLC负责运动执行、AI视觉负责图像检测已成为标配,但二者独立运行时常出现时序紊乱、数据交互不畅、协同逻辑缺失等问题。本文以C#上位机为调度核心,基于Modbus TCP协议实现PLC与AI视觉系统的闭环协同控制,从系统架构、协议规划、代码开发到现场联调全流程讲解,提供可直接落地的工业级解决方案,适用于零部件质检、工件定位、自动分拣等典型工控场景,可直接收录至C#工业上位机开发专栏。

一、前言

随着工业4.0与智能制造的普及,AI视觉技术在工业领域的应用愈发广泛,无论是外观缺陷检测、尺寸测量还是目标定位,都能大幅提升产线精度与效率。但在实际工控场景中,视觉系统仅能完成图像识别,无法直接控制伺服、气缸、输送带等执行机构;而PLC虽擅长运动控制,却不具备复杂图像推理能力。

二者如何高效联动,是工业项目落地的核心痛点。

C#凭借WinForm/WPF快速开发UI界面、完善的异步通信机制、成熟的工控类库,成为连接PLC与AI视觉的最佳调度载体。而Modbus作为工业通用通信协议,兼容性覆盖西门子、三菱、汇川、信捷等几乎所有主流PLC,无需额外驱动即可实现稳定数据交互。

本文将完整实现:

  • C#上位机与PLC的Modbus TCP稳定通信
  • AI视觉检测结果实时回传至上位机
  • 基于视觉结果驱动PLC执行分拣、放行、报警等动作
  • 工业级异常处理、重连机制与时序控制
  • 完整可移植的项目代码与现场调试方案

二、系统整体架构设计

本系统采用三层协同架构,C#上位机为核心调度层,Modbus TCP为通信桥梁,PLC为执行层,AI视觉为感知层,各模块分工明确、数据闭环交互。

Modbus TCP 502端口

Socket TCP通信

C#上位机调度中心

PLC运动控制单元

视觉结果回传

伺服电机/气缸/输送带

工业相机图像采集

YOLO/Halcon视觉检测

待测工件

UI交互+日志记录+数据存储

模块职责划分

  1. AI视觉模块:通过工业相机采集工件图像,使用YOLOv8/Halcon完成缺陷检测、坐标定位,将OK/NG结果、偏移坐标通过Socket发送至上位机。
  2. C#上位机模块:实现人机交互、Modbus通信、视觉数据解析、协同逻辑调度、异常报警与数据记录。
  3. Modbus通信模块:负责上位机与PLC之间的线圈、寄存器数据交互,传输控制指令与状态信号。
  4. PLC执行模块:接收上位机指令,控制输送带启停、相机触发、合格工件放行、不合格工件剔除等动作。

三、Modbus TCP协议与寄存器地址规划

Modbus TCP是基于以太网的工业通信协议,默认端口502,具有通信稳定、传输速度快、适配性强等优势,是现代工控项目首选通信方式。

1. 核心功能码

本项目仅使用工业场景最常用功能码,兼顾稳定性与开发效率:

  • 0x01:读线圈状态 → 读取PLC输入信号
  • 0x05:写单个线圈 → 控制PLC输出动作
  • 0x03:读保持寄存器 → 读取PLC状态与视觉结果
  • 0x06:写单个寄存器 → 下发触发指令与检测结果
  • 0x10:写多个寄存器 → 下发坐标偏移数据

2. 寄存器与线圈地址规划(通用适配版)

为保证项目可移植性,地址规划遵循通用工控标准,适配绝大多数PLC设备:

地址类型 起始地址 数据长度 功能说明 数据传输方向
线圈 00001 1位 工件到位检测信号 PLC→上位机
线圈 00002 1位 不合格工件剔除指令 上位机→PLC
保持寄存器 40001 1字 视觉采集触发信号 上位机→PLC
保持寄存器 40002 1字 视觉检测结果(0=OK,1=NG) 上位机→PLC
保持寄存器 40003 1字 工件X轴偏移坐标 上位机→PLC
保持寄存器 40004 1字 工件Y轴偏移坐标 上位机→PLC

注意:Modbus地址存在0起始与1起始差异,使用NModbus4时默认采用0起始地址,开发时需与PLC程序严格对应。

四、C# Modbus TCP通信模块开发

本项目基于.NET 6框架开发,使用NModbus4开源工控库实现Modbus TCP主站功能,该库轻量稳定、支持异步调用,适配工业高实时性场景。

1. 环境搭建

  1. 新建WinForm项目,选择.NET 6框架
  2. NuGet安装NModbus4
  3. 准备Modbus Slave模拟器用于前期调试
  4. 配置PLC IP地址、端口号、从站ID

2. Modbus通信帮助类

该类封装连接、断开、线圈读写、寄存器读写等核心方法,支持异步操作,避免UI卡顿。

using Modbus.Device;
using System.Net.Sockets;

namespace PLCVisionControl
{
    public class ModbusTcpHelper
    {
        private TcpClient _tcpClient;
        private ModbusIpMaster _modbusMaster;
        private readonly string _plcIp = "192.168.2.100";
        private readonly int _port = 502;
        private readonly byte _slaveId = 1;

        /// <summary>
        /// 连接PLC
        /// </summary>
        public async Task<bool> ConnectPlc()
        {
            try
            {
                _tcpClient = new TcpClient();
                await _tcpClient.ConnectAsync(_plcIp, _port);
                _modbusMaster = ModbusIpMaster.CreateIp(_tcpClient);
                // 设置超时时间,适配工业现场干扰
                _modbusMaster.Transport.ReadTimeout = 300;
                _modbusMaster.Transport.WriteTimeout = 300;
                return true;
            }
            catch
            {
                return false;
            }
        }

        /// <summary>
        /// 断开连接
        /// </summary>
        public void Disconnect()
        {
            _tcpClient?.Close();
            _modbusMaster = null;
        }

        /// <summary>
        /// 读保持寄存器
        /// </summary>
        public async Task<ushort[]> ReadHoldingRegisters(ushort startAddress, ushort count)
        {
            if (!IsConnected()) throw new Exception("Modbus未连接");
            return await _modbusMaster.ReadHoldingRegistersAsync(_slaveId, startAddress, count);
        }

        /// <summary>
        /// 写单个寄存器
        /// </summary>
        public async Task WriteSingleRegister(ushort address, ushort value)
        {
            if (!IsConnected()) throw new Exception("Modbus未连接");
            await _modbusMaster.WriteSingleRegisterAsync(_slaveId, address, value);
        }

        /// <summary>
        /// 读线圈
        /// </summary>
        public async Task<bool> ReadCoil(ushort address)
        {
            if (!IsConnected()) throw new Exception("Modbus未连接");
            var coils = await _modbusMaster.ReadCoilsAsync(_slaveId, address, 1);
            return coils[0];
        }

        /// <summary>
        /// 写单个线圈
        /// </summary>
        public async Task WriteSingleCoil(ushort address, bool value)
        {
            if (!IsConnected()) throw new Exception("Modbus未连接");
            await _modbusMaster.WriteSingleCoilAsync(_slaveId, address, value);
        }

        /// <summary>
        /// 判断连接状态
        /// </summary>
        public bool IsConnected()
        {
            return _tcpClient?.Connected ?? false;
        }
    }
}

五、AI视觉系统对接与数据解析

AI视觉系统完成推理后,通过Socket TCP将检测结果以JSON格式发送至上位机,上位机接收后解析数据,再通过Modbus写入PLC。

1. 视觉数据格式

{
    "DetectResult": 0,
    "OffsetX": 5.2f,
    "OffsetY": 2.8f,
    "IsSuccess": true
}
  • DetectResult:0=合格,1=不合格
  • OffsetX/Y:坐标偏移量
  • IsSuccess:视觉检测是否正常完成

2. 视觉结果实体类

public class VisionDetectResult
{
    public int DetectResult { get; set; }
    public float OffsetX { get; set; }
    public float OffsetY { get; set; }
    public bool IsSuccess { get; set; }
}

3. Socket服务端接收视觉数据

上位机作为Socket服务端,监听指定端口,实时接收视觉系统推送的检测结果:

using System.Net;
using System.Net.Sockets;
using System.Text;

public class VisionSocketServer
{
    private Socket _socketServer;
    public event Action<VisionDetectResult> OnVisionDataReceived;

    public async Task StartServer(int port = 8888)
    {
        _socketServer = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        _socketServer.Bind(new IPEndPoint(IPAddress.Any, port));
        _socketServer.Listen(10);

        while (true)
        {
            var client = await _socketServer.AcceptAsync();
            _ = Task.Run(() => ReceiveClientData(client));
        }
    }

    private async Task ReceiveClientData(Socket client)
    {
        var buffer = new byte[1024];
        while (true)
        {
            var len = await client.ReceiveAsync(buffer, SocketFlags.None);
            if (len == 0) break;

            var json = Encoding.UTF8.GetString(buffer, 0, len);
            var result = System.Text.Json.JsonSerializer.Deserialize<VisionDetectResult>(json);
            OnVisionDataReceived?.Invoke(result);
        }
    }
}

六、PLC与AI视觉协同控制逻辑

协同控制是本项目核心,采用状态机闭环流程,彻底解决工业现场常见的时序错乱、信号丢失、重复触发等问题。

无信号

到位信号有效

检测失败

检测成功

OK

NG

系统初始化

等待下一工件

上位机触发视觉采集

AI视觉拍照+推理

接收视觉检测结果

上位机发出报警信号

解析OK/NG与坐标数据

数据写入PLC对应寄存器

判断检测结果

PLC执行工件放行

PLC执行剔除动作

复位所有状态位

协同逻辑核心规则

  1. 只有工件到位后,才允许触发视觉采集,避免空拍、误拍
  2. 视觉检测未完成前,禁止PLC执行下一周期动作
  3. 检测结果写入PLC后,PLC立即读取并执行对应动作
  4. 单次流程结束后强制复位状态位,防止信号粘连
  5. 加入超时机制,视觉超时未返回数据则自动报警复位

七、上位机主界面与协同调度代码

主窗体负责UI展示、PLC连接控制、视觉数据监听与协同调度,代码简洁易维护,适配工业现场操作习惯。

namespace PLCVisionControl
{
    public partial class MainForm : Form
    {
        private readonly ModbusTcpHelper _modbusHelper = new();
        private readonly VisionSocketServer _visionServer = new();

        public MainForm()
        {
            InitializeComponent();
            // 绑定视觉数据接收事件
            _visionServer.OnVisionDataReceived += VisionServer_OnDataReceived;
        }

        /// <summary>
        /// 连接PLC
        /// </summary>
        private async void btn_ConnectPlc_Click(object sender, EventArgs e)
        {
            var isConnected = await _modbusHelper.ConnectPlc();
            lbl_PlcStatus.Text = isConnected ? "PLC已连接" : "PLC连接失败";
            lbl_PlcStatus.ForeColor = isConnected ? Color.Green : Color.Red;
        }

        /// <summary>
        /// 启动视觉监听服务
        /// </summary>
        private async void btn_StartVision_Click(object sender, EventArgs e)
        {
            await _visionServer.StartServer(8888);
            lbl_VisionStatus.Text = "视觉服务已启动";
            lbl_VisionStatus.ForeColor = Color.Green;
        }

        /// <summary>
        /// 视觉数据接收与协同调度
        /// </summary>
        private async void VisionServer_OnDataReceived(VisionDetectResult result)
        {
            try
            {
                if (!result.IsSuccess)
                {
                    MessageBox.Show("视觉检测异常!");
                    return;
                }

                // 1. 写入检测结果到PLC寄存器40002
                await _modbusHelper.WriteSingleRegister(1, (ushort)result.DetectResult);

                // 2. 写入坐标数据(浮点转整型,避免传输误差)
                ushort x = (ushort)(result.OffsetX * 10);
                ushort y = (ushort)(result.OffsetY * 10);
                await _modbusHelper.WriteSingleRegister(2, x);
                await _modbusHelper.WriteSingleRegister(3, y);

                // 3. 根据结果控制PLC剔除线圈
                if (result.DetectResult == 1)
                {
                    await _modbusHelper.WriteSingleCoil(1, true);
                    lbl_DetectResult.Text = "检测不合格:已剔除";
                    lbl_DetectResult.ForeColor = Color.Red;
                }
                else
                {
                    lbl_DetectResult.Text = "检测合格:已放行";
                    lbl_DetectResult.ForeColor = Color.Green;
                }

                // 4. 延时复位线圈
                await Task.Delay(500);
                await _modbusHelper.WriteSingleCoil(1, false);
            }
            catch (Exception ex)
            {
                MessageBox.Show($"协同控制异常:{ex.Message}");
            }
        }
    }
}

八、工业现场调试与优化方案

1. 分步调试流程

  1. Modbus通信调试:使用Modbus Slave模拟PLC,测试上位机读写功能是否正常
  2. 视觉通信调试:使用Socket调试工具发送模拟数据,验证解析逻辑
  3. PLC联调:接入真实PLC,测试线圈与寄存器联动
  4. 产线联调:对接视觉相机与执行机构,优化时序延迟

2. 工业级优化策略

  1. 心跳重连机制:定时检测Modbus连接状态,断开后自动重连
  2. 数据滤波处理:对坐标数据做平滑处理,避免抖动
  3. 超时保护:视觉检测超过500ms未返回则自动复位
  4. 日志记录:记录每次检测结果、通信状态,便于故障追溯
  5. 抗干扰处理:工业现场屏蔽网线接地,关闭防火墙与杀毒软件

九、常见问题与避坑指南

  1. Modbus通信超时

    • 检查PLC IP与端口配置,确认网络通畅
    • 适当延长超时时间至300-500ms
    • 排查交换机、网线等硬件问题
  2. 视觉与PLC时序不同步

    • 增加状态锁机制,未完成上一周期禁止新周期
    • 优化视觉推理速度,控制在200ms内
    • 调整PLC扫描周期,保证数据实时响应
  3. 坐标数据传输误差

    • 浮点型数据放大10倍转为整型传输
    • 增加数据校验位,防止丢包错乱
  4. PLC信号粘连

    • 执行完成后强制复位线圈与寄存器
    • 程序中加入延时消抖处理

十、总结与拓展

本文完整实现了C#上位机+Modbus TCP+PLC+AI视觉的协同控制系统,解决了工业场景中视觉检测与运动控制脱节的核心问题,代码结构清晰、可直接移植到西门子S7-200SMART、三菱FX5U、汇川H5U等主流PLC,适用于零部件质检、工件定位、自动分拣、尺寸测量等智能制造场景。

本方案具备以下优势:

  • 通用性强,Modbus协议适配绝大多数工控设备
  • 实时性高,异步通信保证毫秒级响应
  • 稳定性好,完善的异常处理与重连机制
  • 扩展性强,可快速对接数据库、MES系统、云端平台

后续可拓展方向:

  1. 接入OPC UA协议,适配更多工业设备与系统
  2. 对接SQL Server数据库,实现检测数据统计与报表生成
  3. 加入视频监控与远程控制功能
  4. 优化AI视觉模型,提升检测精度与速度
  5. 对接MES系统,实现生产数据全流程追溯
Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐