C#开发的运动控制和视觉项目

最近在车间里折腾了一套基于C#的运动控制+视觉定位系统,今天就跟大伙唠唠实战中的那些代码片段和踩坑经验。别被工业场景吓到,其实用C#搞自动化比你想象中简单——毕竟.NET的硬件封装库真不是吃素的。

先看运动控制这块硬骨头。我们用过固高、雷塞这些国产卡,它们的C# SDK其实大同小异。初始化运动控制卡时,这个try-catch块救了我无数次:

try
{
    // 固高卡初始化示例
    if (GTS.mc.GT_Open(0, 1, out short boardId) == GTS.mc.GT_ERR_SUCCESS)
    {
        GTS.mc.GT_LoadConfig(boardId, "motor.cfg");
        GTS.mc.GT_ClrSts(boardId, 1); // 清状态
    }
}
catch (DllNotFoundException ex)
{
    MessageBox.Show($"驱动文件没找到!检查dll位置\n{ex.Message}");
}

这代码有个坑爹的地方——很多运动控制卡的dll是32位的,记得把项目平台目标改成x86,不然运行时直接给你抛DllNotFoundException。之前因为这个翻车,愣是排查了两个小时...

视觉定位部分更刺激。用AForge.NET做图像采集,配合OpenCVSharp做处理是常规操作。检测圆形工件的代码可能是这样的:

// 找圆大法
var circles = Cv2.HoughCircles(processedImage, 
    HoughMethods.Gradient, 
    1.0, // dp值调小能检测更密集的圆
    50,  // 最小圆心距
    param1: 200, 
    param2: 30,  // 这个参数决定误检率
    minRadius: 10, 
    maxRadius: 60);

if (circles.Length > 0)
{
    var target = circles.OrderBy(c => c.Center.DistanceTo(centerPoint)).First();
    return new PointF(target.Center.X, target.Center.Y);
}

注意HoughCircles的参数2(param2)是个魔鬼参数。有次现场光照变化,这个值没调好,直接把工件旁边的螺丝孔识别成目标了,结果机械臂上演了一出"大力出奇迹",场面一度非常尴尬。

当运动控制遇上视觉反馈,坐标系对齐才是真正的修罗场。这个转换矩阵我刻在DNA里了:

// 像素坐标转机械坐标
public PointF PixelToWorld(PointF pixelPoint)
{
    // 标定后的转换参数
    double kx = 0.02; // mm/pixel
    double ky = -0.02; // Y轴通常要取反
    double offsetX = 325.4;
    double offsetY = -287.1;

    return new PointF(
        (float)(pixelPoint.X * kx + offsetX),
        (float)(pixelPoint.Y * ky + offsetY)
    );
}

重点是这个ky的负号——相机安装方向不同会导致Y轴方向相反。有次学徒忘了这个负号,机械臂运动轨迹直接镜像反转,差点把传送带上的工件全扫到地上。

调试时强烈建议加上运动仿真模式。我通常会封装一个虚拟运动接口:

public interface IMotionController
{
    void MoveTo(PointF target);
    
    // 运行时切换真实/虚拟控制器
    bool IsSimulation { get; set; }
}

// 虚拟实现
public class SimMotionController : IMotionController
{
    public void MoveTo(PointF target)
    {
        Debug.WriteLine($"虚拟运动到 ({target.X:F2}, {target.Y:F2})");
        Thread.Sleep(200); // 模拟运动耗时
    }
}

这套机制至少帮我省了80%的调试时间——不用每次都真动设备,在工位上就能验证逻辑。特别是做视觉纠错逻辑时,可以快速模拟各种偏移情况。

最后说个血泪教训:运动控制线程和UI线程一定要分开!用BackgroundWorker太老套,现在推荐用async/await:

public async Task RunAlignmentAsync()
{
    try
    {
        await Task.Run(() => 
        {
            // 运动控制代码
            motion.MoveTo(homePosition);
            var img = camera.Capture();
            var pos = vision.FindTarget(img);
            motion.MoveTo(pos);
        });
    }
    catch (MotionTimeoutException ex)
    {
        // 处理超时
    }
}

但注意!有些运动控制卡的API根本就不是线程安全的,这时候得加锁或者用Control.Invoke。之前遇到过随机出现的GTERRACCESS错误,最后发现是跨线程调用SDK函数导致的。

搞这类项目就像在钢丝上跳舞——既要保证实时性,又要处理各种异常。但看着机械臂按视觉定位精准抓取的那一刻,代码里的那些坑都值了。下次有机会再聊聊怎么用WPF做酷炫的3D运动监控界面,那又是另一个深坑...

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐