1. 摘要

  • 检测需求:检测磁铁有无
  • 检测逻辑:
    • 方式1:使用多个CogBlobTool工具,和磁铁一一对应,检测有无
    • 方式2:利用磁铁对称的性质,旋转坐标系的角度,用1个CogBlobTool工具,就能查找所有的磁铁【本文重点讲述方式2】

2. 产品图象

在这里插入图片描述

3.检测流程

思路:

  1. 先对工件进行定位【实际生产时,会有轻微的上料偏差】

  2. 再使用工件中心的“圆形”做二次定位

  3. 以“圆形”的中心,作为旋转中心,旋转坐标系。产品中有6组磁铁,360度一共有6组磁铁,所以坐标系每次旋转60度数.

工具组流程:

在这里插入图片描述

高级脚本:

#region namespace imports
using System;
using System.Collections;
using System.Drawing;
using System.IO;
using System.Windows.Forms;
using Cognex.VisionPro;
using Cognex.VisionPro.ToolBlock;
using Cognex.VisionPro3D;
using Cognex.VisionPro.ImageProcessing;
using Cognex.VisionPro.PMAlign;
using Cognex.VisionPro.CalibFix;
using Cognex.VisionPro.Caliper;
using Cognex.VisionPro.Blob;
#endregion

public class CogToolBlockAdvancedScript : CogToolBlockAdvancedScriptBase
{
  #region Private Member Variables
  private Cognex.VisionPro.ToolBlock.CogToolBlock mToolBlock;
  
  CogGraphicCollection gc = new CogGraphicCollection();
  private double[] allBlobsX = new double[6];//保存blob在局部坐标系下的位置
  private double[] allBlobsY = new double[6];//保存blob在局部坐标系下的位置
  
  #endregion

  public override bool GroupRun(ref string message, ref CogToolResultConstants result)
  {
    try
    {
      gc.Clear();
      mToolBlock.Outputs["result"].Value = true;
      Array.Clear(allBlobsX, 0, allBlobsX.Length);
      Array.Clear(allBlobsY, 0, allBlobsY.Length);

      // 获取工具
      CogImageConvertTool convert = mToolBlock.Tools["CogImageConvertTool1"] as CogImageConvertTool;
      CogPMAlignTool pma1 = mToolBlock.Tools["CogPMAlignTool1"] as CogPMAlignTool;
      CogFixtureTool fix1 = mToolBlock.Tools["CogFixtureTool1"] as CogFixtureTool;
      CogFindCircleTool findCircle1 = mToolBlock.Tools["CogFindCircleTool1"] as CogFindCircleTool;
      CogFixtureTool fix2 = mToolBlock.Tools["CogFixtureTool2"] as CogFixtureTool;
      CogBlobTool blob1 = mToolBlock.Tools["CogBlobTool1"] as CogBlobTool;

      // 运行基础工具
      convert.Run();
      pma1.Run();
      fix1.Run();
      findCircle1.Run();

      // 6方向循环检测
      for (int i = 0; i < 6; i++)
      {
        CogTransform2DLinear newTrans = new CogTransform2DLinear();
        newTrans.TranslationX = findCircle1.Results.GetCircle().CenterX;
        newTrans.TranslationY = findCircle1.Results.GetCircle().CenterY;
        newTrans.Rotation = pma1.Results[0].GetPose().Rotation + i * (Math.PI / 3);//!!!每次旋转60度

        fix2.RunParams.UnfixturedFromFixturedTransform = newTrans;
        fix2.Run();
        blob1.Run();

        double dx = 0;
        double dy = 0;

        if (blob1.Region != null && blob1.Region is CogRectangleAffine)
        {
          CogRectangleAffine rec1 = (CogRectangleAffine) blob1.Region;
          newTrans.MapPoint(rec1.CenterX, rec1.CenterY, out dx, out dy);//!!!把【局部坐标系】的点 → 转换回【图像像素坐标系】。(rec1.CenterX, rec1.CenterY)是blob在局部坐标系中的值;(dx,  dy)是blob在图像像素坐标系中的值
        }

        // 赋值OK / NG
        if (blob1.Results.GetBlobs().Count == 1)
        {
          addText(dx, dy, "OK", CogColorConstants.Green, 10);
          allBlobsX[i] = dx;
          allBlobsY[i] = dy;
        }
        else
        {
          addText(dx, dy, "NG", CogColorConstants.Red, 10);
          mToolBlock.Outputs["result"].Value = false;
          allBlobsX[i] = -9999;
          allBlobsY[i] = -9999;
        }
      }

      // 数组每个元素单独格式化保留2位小数
      mToolBlock.Outputs["strX"].Value = string.Join(",", Array.ConvertAll(allBlobsX, x => x.ToString("0.00")));//Array.ConvertAll:把allBlobsX数组里每一个数字x,都变成保留 2 位小数的字符串
      mToolBlock.Outputs["strY"].Value = string.Join(",", Array.ConvertAll(allBlobsY, y => y.ToString("0.00")));
    }
    catch (Exception ex)
    {
      message = ex.Message;
      result = CogToolResultConstants.Error;
      mToolBlock.Outputs["result"].Value = false;

      for (int k = 0; k < 6; k++)
      {
        allBlobsX[k] = -9999;
        allBlobsY[k] = -9999;
      }
      // catch里同样用ConvertAll格式化
      mToolBlock.Outputs["strX"].Value = string.Join(",", Array.ConvertAll(allBlobsX, x => x.ToString("0.00")));
      mToolBlock.Outputs["strY"].Value = string.Join(",", Array.ConvertAll(allBlobsY, y => y.ToString("0.00")));
    }

    return false;
  }

  public void addText(double x, double y, string str, CogColorConstants color, int size)
  {
    CogGraphicLabel label = new CogGraphicLabel();
    label.SetXYText(x, y, str);
    label.Color = color;
    label.Font = new Font("黑体", size);
    gc.Add(label);
  }

  #region When the Current Run Record is Created
  /// <summary>
  /// Called when the current record may have changed and is being reconstructed
  /// </summary>
  /// <param name="currentRecord">
  /// The new currentRecord is available to be initialized or customized.</param>
  public override void ModifyCurrentRunRecord(Cognex.VisionPro.ICogRecord currentRecord)
  {
  }
  #endregion

  #region When the Last Run Record is Created
  /// <summary>
  /// Called when the last run record may have changed and is being reconstructed
  /// </summary>
  /// <param name="lastRecord">
  /// The new last run record is available to be initialized or customized.</param>
  public override void ModifyLastRunRecord(Cognex.VisionPro.ICogRecord lastRecord)
  {
    if(gc.Count > 0)
    {
      foreach(ICogGraphic item in gc)
      {
        ”mToolBlock.AddGraphicToRunRecord(item, lastRecord, lastRecord.SubRecords[0].RecordKey, "");
      }
    }
  }
  #endregion

  #region When the Script is Initialized
  /// <summary>
  /// Perform any initialization required by your script here
  /// </summary>
  /// <param name="host">The host tool</param>
  public override void Initialize(Cognex.VisionPro.ToolGroup.CogToolGroup host)
  {
    // DO NOT REMOVE - Call the base class implementation first - DO NOT REMOVE
    base.Initialize(host);


    // Store a local copy of the script host
    this.mToolBlock = ((Cognex.VisionPro.ToolBlock.CogToolBlock) (host));
  }
  #endregion

}

注意事项:

  1. 每次运行必须清空数组

    这句非常关键:

    Array.Clear(allBlobsX, 0, 6);
    

    如果不清空 → 上一次的结果会留在数组里,导致数据错乱

  2. NG 必须给数组赋值(不能空着)

    allBlobsX[i] = -9999;
    

    不赋值 → 数组是 0,不知道是真 0 还是没检测到。

  3. Blob.Region 必须判空**

    if (blob1.Region != null)
    

    不判空 → 没检测到目标时直接报错。

  4. 6 次循环必须每次都运行 fix2 + blob1

    fix2.Run();
    blob1.Run();
    

    少一个 Run → 坐标不刷新,6 个位置全一样。

  5. 把【局部坐标系】的点 → 转换回【图像像素坐标系】,否则文本标签的显示位置会出问题

    newTrans.MapPoint(rec1.CenterX, rec1.CenterY, out dx, out dy);//!!!把【局部坐标系】的点 → 转换回【图像像素坐标系】。(rec1.CenterX, rec1.CenterY)是blob在局部坐标系中的值;(dx,  dy)是blob在图像像素坐标系中的值
    
  6. 数组不能直接 ToString (“0.00”)

    错误写法:

    allBlobsX.ToString("0.00"); // ❌ 报错
    

    正确写法:

    Array.ConvertAll(allBlobsX, x => x.ToString("0.00")); // ✅
    

    因为:数组不能格式化,只有数字才能格式化

  7. string.Join 才能把数组变成逗号字符串

    string.Join(",", 数组)
    

    不加 Join → 输出的是 System.Double[],不是数字。

  1. 文本标签永远输出在第一个工具的图像上

    ”mToolBlock.AddGraphicToRunRecord(item, lastRecord, lastRecord.SubRecords[0].RecordKey, "");“这句话啥意思?
    

    AddGraphicToRunRecord函数:

    mToolBlock.AddGraphicToRunRecord(
        item,        // 你要画的东西(圆、点、线)
        lastRecord,  // 往“上次运行结果”里画//lastRecord = 上一次运行的结果记录所有图像、结果都存在这里。
        路径,        // 画在哪一张图像上
        ""           // 图层名字(空=默认图层)
    );
    

    lastRecord.SubRecords[0].RecordKey:

    1. lastRecord
    上次运行完的所有结果大箱子。里面装了:图像、工具结果、数据、图层…… 一切东西。
    2. SubRecords[0]
    - 大箱子里的【第一张图片】
    - SubRecords = 所有子结果
    - [0] =0 号(第 1 个)
    几乎永远 = 你正在看的这张输入图 / 输出图
    你可以理解成:SubRecords [0] = 当前画面
    3. RecordKey
    这张图片的 “唯一身份证号”
    VisionPro 必须靠这个 ID 才能找到
    

更多推荐