1. 项目背景介绍

  • 最近参与了一个锂电池UV点胶机的项目,其中对胶水高度(胶水最高点到基准面的距离)的测量,选用的是基恩士LJ-V7001线激光。
  • 如下图,灰色矩形代表电芯,蓝色条状物就是UV胶,线激光的测量线与UV胶边缘垂直,沿着箭头方向移动,直到扫描完整条边缘。在这里插入图片描述
  • 下图是点完UV胶后电池的截面图,线激光就是要测量T1和T2的值。
    在这里插入图片描述

2. 基恩士线激光的设置

  • 线激光的使用相较与相机来说,复杂一些,这里说一下本项目中的简单设置。
  • 线激光的基本设置都可以在LJ-Navigator这个软件中进行,
  • 在这里插入图片描述

需要注意的有:

  • 触发模式:编码器触发、连续触发(调试时实时查看效果)
  • 批处理:是否使用批处理,以及批处理的点数。如果使用了批处理,当采集完设定点数后,回调函数会自动被调用,返回所有数据。
  • 需要注意的是,当你使用两个激光头时,返回的数据会将两个激光头的数据拼接到一起返回。
    在这里插入图片描述
  • 在自动运行时,我们一般使用高速,这样传输数据更快。
    在这里插入图片描述

3. 使用Halcon处理深度图

3.1 线激光高度数据转为深度图

      Z = bufferArray;
      HTuple X, Y;
      HOperatorSet.GenImageConst(out ho_Image, "real", width, height);
      HOperatorSet.GetRegionPoints(ho_Image, out X, out Y);
      HOperatorSet.SetGrayval(ho_Image, X, Y, Z);

在这里插入图片描述

5. Halcon图像处理方法

  • 先把电芯区域抠图出来
    在这里插入图片描述
  • 当我们去其中一行数据进行分析时会发现一个问题,那就是基准面是倾斜的,如果直接用胶水的高度减去基准面的高度计算的结果一定是错的。所以我们需要找到胶水的最高点,然后计算最高点到基准线的垂直距离。
  • 当然其中还有一点需要注意,那就是应该如何判断一个点是在基准线上方还是下方,比如最右边也就是最边缘的点距离基准线的距离也比较远,但它明显不是胶水的最高点,我们可以通过基准线两个端点和当前被计算的点之间形成的角度来判断。具体实现可以看下面的代码。
  • 在这里插入图片描述
    在这里插入图片描述

6. Halcon代码

*dev_update_off ()
read_image (Image1, 'B1.tif')

gen_rectangle1 (ROI, 1.39873, 19.5497, 1184.31, 777.679)

* 获取电芯区域和图像
GetCellRegion (Image1, ROI, RegionTop, RegionBottom, ImageReducedTop, ImageReducedBottom)

cut1:=10
cut2:=10

X_Offset1:=40
X_Offset2:=75

X_Measure1:=3
X_Measure2:=45

* 计算T1T2
CalculateT1T2 (RegionTop, ImageReducedTop, RegionBottom, ImageReducedBottom, \
               cut1, cut2, X_Offset1, X_Offset2, X_Measure1, X_Measure2, T1, T2)

*dev_update_on ()


  • GetCellRegion
    * 获得传感器上下图像
    reduce_domain (Image1, ROI, ImageReduced)
    threshold (ImageReduced, Regions, -500000, 500000)
    connection (Regions, ConnectedRegions)
    select_shape (ConnectedRegions, SelectedRegions, 'area', 'and', 32450.4, 1000000)
    sort_region (SelectedRegions, SortedRegions, 'character', 'true', 'row')
    select_obj (SortedRegions, RegionBottom, 1)
    select_obj (SortedRegions, RegionTop, 2)
    reduce_domain (ImageReduced, RegionBottom, ImageReducedBottom)
    reduce_domain (ImageReduced, RegionTop, ImageReducedTop)
    return ()
    
  • CalculateT1T2
    smallest_rectangle1 (RegionTop, Row1, Column1, Row2, Column2)
    Row1:=Row1+cut1
    Row2:=Row2-cut2
    rowNum:=Row2-Row1
    
    lineX:=[]
    lineY:=[]
    
    measureX:=[]
    measureY:=[]
    
    T1Tmp:=[]
    
    gen_empty_obj (glueCrossT)
    gen_empty_obj (glueCrossB)
    
    for Index := 0 to rowNum-1 by 1
        gen_rectangle1 (Rectangle, Row1+Index, Column1, Row1+Index+1, Column2)
        
        intersection (Rectangle, RegionTop, RegionIntersection)
        get_region_points (RegionIntersection, Rows, Columns)
        
        * 整体高度曲线
        get_grayval (ImageReducedTop, Rows, Columns, Grayval) 
        tuple_median (Grayval, Median)
        Grayval:=(Grayval-Median)/1000
        tuple_gen_sequence (1, |Grayval|, 1, X)
        Y:=Grayval
        gen_contour_polygon_xld (Contour, Y, X)
        
        * 拟合基准直线
        if(|Grayval|-X_Offset2>0)
            tuple_gen_sequence (|Grayval|-X_Offset2, |Grayval|-X_Offset1, 1, lineX)
            lineY:=Y[|Grayval|-X_Offset2:|Grayval|-X_Offset1]
            gen_contour_polygon_xld (contour, lineY, lineX)
            fit_line_contour_xld (contour, 'tukey', -1, 0, 5, 2, RowBegin, ColBegin, RowEnd, ColEnd, Nr, Nc, Dist)
            gen_contour_polygon_xld (std_line, [RowBegin,RowEnd], [ColBegin,ColEnd])
            
            * 选择测量区间
            tuple_gen_sequence (|Grayval|-X_Measure2, |Grayval|-X_Measure1, 1, measureX)
            measureY:=Y[|Grayval|-X_Measure2:|Grayval|-X_Measure1]
            
            * 判断点在直线的上方还是下方
            * 如果点在直线下方,则认为没有胶水,高度为0
            * 使用角度判断
            
            tuple_gen_const (|measureX|, ColBegin, NewColBegin)
            tuple_gen_const (|measureX|, RowBegin, NewRowBegin)
            tuple_gen_const (|measureX|, ColEnd, NewColEnd)
            tuple_gen_const (|measureX|, RowEnd, NewRowEnd)
            
            angle_ll (NewColBegin, NewRowBegin, NewColEnd, NewRowEnd,\
                      NewColEnd, NewRowEnd, measureX, measureY, Angle)
            
            ag:=deg(Angle)
                   
            distance_pl (measureX, measureY, ColBegin, RowBegin, ColEnd, RowEnd, Distance)
            
            for I := 0 to |ag|-1 by 1
                if (ag[I]>0)
                    Distance[I] :=0
                endif
            endfor
            
            tuple_max (Distance, Max)
            T1Tmp:=[T1Tmp,Max]
            
    *         dev_clear_window ()
    *         dev_set_color ('red')
    *         dev_display (Contour)
    *         dev_set_color ('blue')
    *         dev_display (std_line)
    
    *      stop()
        endif
        
             
    endfor
    
    * T1平均处理
    num:=50
    T1:=[]
    for I := 0 to |T1Tmp|-num-1 by 1
        tuple_median (T1Tmp[I:I+num], Median)
        T1[I]:=Median
    endfor
    
    T1:=T1/100
    
    * dev_clear_window ()
    * tuple_gen_sequence (1, |T1|, 1, X)
    * Y:=T1*1000
    * gen_contour_polygon_xld (Contour1, Y, X)
    
    
    * dev_display (Contour1)
    
    
    * stop()
    
    **************下面
    * 获得单独一行的高度曲线图
    smallest_rectangle1 (RegionBottom, Row1, Column1, Row2, Column2)
    
    Row1:=Row1+cut1
    Row2:=Row2-cut2
    rowNum:=Row2-Row1
    
    lineX:=[]
    lineY:=[]
    
    measureX:=[]
    measureY:=[]
    
    MaxDist:=[]
    T2Tmp:=[]
    for Index := 0 to rowNum-1 by 1
        gen_rectangle1 (Rectangle, Row1+Index, Column1, Row1+Index+1, Column2)
        
        intersection (Rectangle, RegionBottom, RegionIntersection)
        get_region_points (RegionIntersection, Rows, Columns)
        
        * 整体高度曲线
        get_grayval (ImageReducedBottom, Rows, Columns, Grayval) 
        tuple_median (Grayval, Median)
        Grayval:=(Grayval-Median)/1000
        tuple_gen_sequence (1, |Grayval|, 1, X)
        Y:=Grayval
        gen_contour_polygon_xld (Contour, Y, X)
        
        * 拟合基准直线
        if(|Grayval|-X_Offset2>0)
            tuple_gen_sequence (|Grayval|-X_Offset2, |Grayval|-X_Offset1, 1, lineX)
            lineY:=Y[|Grayval|-X_Offset2:|Grayval|-X_Offset1]
            gen_contour_polygon_xld (contour, lineY, lineX)
            fit_line_contour_xld (contour, 'tukey', -1, 0, 5, 2, RowBegin, ColBegin, RowEnd, ColEnd, Nr, Nc, Dist)
            gen_contour_polygon_xld (std_line, [RowBegin,RowEnd], [ColBegin,ColEnd])
            
            * 选择测量区间
            tuple_gen_sequence (|Grayval|-X_Measure2, |Grayval|-X_Measure1, 1, measureX)
            measureY:=Y[|Grayval|-X_Measure2:|Grayval|-X_Measure1]
            
            tuple_gen_const (|measureX|, ColBegin, NewColBegin)
            tuple_gen_const (|measureX|, RowBegin, NewRowBegin)
            tuple_gen_const (|measureX|, ColEnd, NewColEnd)
            tuple_gen_const (|measureX|, RowEnd, NewRowEnd)
            
            angle_ll (NewColBegin, NewRowBegin, NewColEnd, NewRowEnd,\
                      NewColEnd, NewRowEnd, measureX, measureY, Angle)
            
            ag:=deg(Angle)
            
            distance_pl (measureX, measureY, ColBegin, RowBegin, ColEnd, RowEnd, Distance)
            
            for I := 0 to |ag|-1 by 1
            if (ag[I]<0)
                    Distance[I] :=0
                endif
            endfor
            
            tuple_max (Distance, Max)
            
            T2Tmp:=[T2Tmp,Max]
            dev_clear_window ()
            dev_set_color ('red')
            dev_display (Contour)
            dev_set_color ('blue')
            dev_display (std_line)
    
            stop()
        endif
        
    endfor
    
    * T1平均处理
    num:=50
    T2:=[]
    for I := 0 to |T2Tmp|-num-1 by 1
        tuple_median (T2Tmp[I:I+num], Median)
        T2[I]:=Median
    endfor
    
    T2:=T2/100
    
    return ()