工业视觉_73:机器人喷涂_工件区域喷涂最佳路径生成

工业视觉_73:机器人喷涂_工件区域喷涂最佳路径生成

工业视觉_73:机器人喷涂_工件区域喷涂最佳路径生成 

/*
工业视觉_73:机器人喷涂_工件区域喷涂最佳路径生成
                
未来20年里,机器换人,智能使用机器人,改造传统工厂成智慧工厂,将成为最火的行业之一.
工业视觉,目标很明确:"快,准,稳"三个字. 快:开发快,运行速度快;准:高精度;稳:稳健可靠
使用高级语言做工程主要优势在:已经有丰富的数据结构和成熟的类型库,如List,Dictionary,Lambda,Accord,...
所以,目前"机器换人"项目大多采用工控电脑,搭建 Windows7+VS2019+EMGU(或AForge+Accord),这一方案见效最快.
Halcon,Emgu的视觉库都很强大,而AForge+Accord库更全面,更丰富(如:数学,人工智能,机器学习).
                
机器人喷涂,是指:喷漆,喷胶,喷塑.
经相机返回的图像,包含了不同工件的颜色与形状.
我们可以通过视觉对工件区域生成运动路径用机器人喷之.
二维的,用四轴机器人即可实现.
三维的,用深度相机采集点云数据,计算法向量,计算机器人的空间姿态(如欧拉角,旋转矩阵,四元数等),用六轴机器人喷之.
所有相机,均要做好纠偏与标定等工作,以便与机器人进行精确匹配.

本文系作者在"安吉八塔机器人公司"产品线研发中的拓展与随笔.
[已经发布,链接:...] 

    ---------     编撰:    项道德(微信:daode1212),2021-07-24
             
    */


//一,基本图像与绘制工具准备:##########################################################

pictureBox1.Image = Image.FromFile("2D147.jpg");//多对象,白色为背景
MSG("马上呈现: 工件区域喷涂最佳路径生成,黑白处理");//自定义函数"MSG()"即"MessageBox.Show()"
Bitmap bmp = (Bitmap)(pictureBox1.Image);
int ww = bmp.Width, hh = bmp.Height;
string txtAll = "";
Graphics g = Graphics.FromImage(bmp);
SolidBrush bh0 = new SolidBrush(Color.FromArgb(0, 0, 0));
SolidBrush bh1 = new SolidBrush(Color.FromArgb(161, 0, 161));
SolidBrush bh2 = new SolidBrush(Color.FromArgb(111, 111, 0));
SolidBrush bh3 = new SolidBrush(Color.FromArgb(211, 211, 211));
Pen pen0 = new Pen(Color.FromArgb(182, 122, 182), 1);
Pen pen1 = new Pen(Color.FromArgb(255, 122, 0), 1);
Pen pen2 = new Pen(Color.FromArgb(0, 110, 255), 1);
Pen pen3 = new Pen(Color.FromArgb(0, 255, 110), 1);

//二,运用指针,将边缘线膨胀:
List<Point> LP = new List<Point>();
List<Point> LP2 = new List<Point>();
            
//为减少计算,使用八位灰度位图: PixelFormat.Format8bppIndexed
unsafe
{
    BitmapData data = bmp.LockBits(new System.Drawing.Rectangle(0, 0, bmp.Width, bmp.Height),
                                    ImageLockMode.ReadOnly,
                                    System.Drawing.Imaging.PixelFormat.Format8bppIndexed);
    var ptr = (byte*)data.Scan0.ToPointer();
    int wBit = data.Stride;

    for (int j = 1; j < data.Width - 1; j +=2)
    {
        for (int i = 1; i < data.Height - 1; i +=2)
        {
            byte up = ptr[(i - 1) * wBit + j];
            byte dn = ptr[(i + 1) * wBit + j];
            byte lt = ptr[i * wBit + (j - 1)];
            byte rt = ptr[i * wBit + (j + 1)];
            byte me = ptr[i * wBit + j];
            //边缘及有彩色(ptr[i * wBit + j]>16)的: 
            if (Math.Abs(up - dn) + Math.Abs(lt - rt) > 127|| me >16)
            {
                LP.Add(new Point(j, i));
            }
        }
    }
    bmp.UnlockBits(data);
}
foreach (var p in LP)
{
    g.FillEllipse(bh0, p.X - 5, p.Y - 5, 10, 10);
}
pictureBox1.Image = bmp;
MSG("马上呈现:黑区点集");
            

//三,针对黑色区域:
//为减少计算,使用八位灰度位图: PixelFormat.Format8bppIndexed
unsafe
{
    BitmapData data = bmp.LockBits(new System.Drawing.Rectangle(0, 0, bmp.Width, bmp.Height),
                                    ImageLockMode.ReadOnly,
                                    System.Drawing.Imaging.PixelFormat.Format8bppIndexed);
    var ptr = (byte*)data.Scan0.ToPointer();
    int wBit = data.Stride;

    for (int j = 1; j < data.Width - 1; j += 16)
    {
        for (int i = 1; i < data.Height - 1; i += 16)
        {
            bool b00 = ptr[(i - 1) * wBit + (j - 1)] == 0;
            bool b01 = ptr[(i - 1) * wBit + j] == 0;
            bool b02 = ptr[(i - 1) * wBit + (j + 1)] == 0;
            bool b10 = ptr[i * wBit + (j - 1)] == 0;
            bool b11 = ptr[i * wBit + j] == 0;
            bool b12 = ptr[i * wBit + (j + 1)] == 0;
            bool b20 = ptr[(i + 1) * wBit + (j - 1)] == 0;
            bool b21 = ptr[(i + 1) * wBit + j] == 0;
            bool b22 = ptr[(i + 1) * wBit + (j + 1)] == 0;
            if ((b00 && b01 && b02 && b10 && b11 && b12 && b20 && b21 && b22))//框内全为黑
            {
                LP2.Add(new Point(j, i));
            }
        }
    }
    bmp.UnlockBits(data);
}
foreach (var p in LP2)
{
    g.FillEllipse(bh1, p.X - 2, p.Y - 2, 4, 4);
}
txtAll = "LP.Count():" + LP.Count() + ",LP2.Count():" + LP2.Count();

textBox1.Text = txtAll;
pictureBox1.Image = bmp;
MSG("马上呈现: 双层循环距离对比算法生成有序点集");

//四,生成有序点集d1---双层循环距离对比算法:            
Dictionary<Point, int> d1 = new Dictionary<Point, int>();//字典,用于记录新的集合
Stack<Point> STK = new Stack<Point>();//堆栈,用于记下当前点的邻近的点

int sx = LP2.ElementAt(0).X; int sy = LP2.ElementAt(0).Y;  //搜索的开始点
STK.Push(new Point(sx, sy));// 堆栈初始点           

int xg = int.MaxValue; int yg = int.MaxValue; //记录搜索时的最近点,初始时,应移动搜索区间之外
double disMin = int.MaxValue; //记录最近距离
double dis;//计算当前距离
double dMaxForSplit = 24;//不同聚类之间的距离,影响分类数目
int m = 0;//聚类的编号

//迭代搜索:
while (LP2.Count > 0)
{
    if (LP2.Contains(new Point(sx, sy))) LP2.Remove(new Point(sx, sy));
    foreach (Point u in LP2)
    {
        int xi = u.X;
        int yi = u.Y;
        //dis = Math.Sqrt(0.75*(xi - sx) * (xi - sx) + (yi - sy) * (yi - sy)); //横线优先
        //dis = Math.Sqrt((xi - sx) * (xi - sx) + 0.75*(yi - sy) * (yi - sy)); //竖线优先
        dis = Math.Sqrt((xi - sx) * (xi - sx) + (yi - sy) * (yi - sy)); //不限制
        if (dis <= disMin)
        {
            disMin = dis; //更新最近距离值
            xg = xi; yg = yi; //更新搜索时的最近点
            if (dis < dMaxForSplit)
            {
                STK.Push(u);//符合聚类之间的距离的,先压入到堆栈中
            }
        }
    }

    //预搬运,以提高速度:
    if (STK.Count > 0)
    {
        foreach (var u in STK)
        {
            LP2.Remove(u);
            if (!d1.ContainsKey(u)) d1.Add(u, m);
        }
    }

    if (disMin <= dMaxForSplit)//符合聚类之间的距离的,更新到D1中,继续
    {
        sx = xg; sy = yg;
        if (!d1.ContainsKey(new Point(xg, yg))) d1.Add(new Point(xg, yg), m);
    }
    else //不符合聚类之间的距离的,到堆栈中取出一元,继续
    {
        if (STK.Count > 0)
        {
            Point Pg = STK.Pop();
            sx = Pg.X; sy = Pg.Y;
        }
    }

    //当距离超过聚类之间的距离,且堆栈也空时,新一聚类开始:
    if (disMin > dMaxForSplit && STK.Count == 0)
    {
        m++; sx = xg; sy = yg;
        if (!d1.ContainsKey(new Point(xg, yg))) d1.Add(new Point(xg, yg), m);
    }

    disMin = int.MaxValue; //最近距离初始化
}

Point p0 = new Point(sx, sy);
foreach (Point z in d1.Keys)
{
    pen0 = new Pen(Color.FromArgb((137 * d1[z]) % 255, 255 - (67 * d1[z]) % 255, (39 * d1[z]) % 255), 8);
    double ds = Math.Sqrt((sx - z.X) * (sx - z.X) + (sy - z.Y) * (sy - z.Y));
    if (ds < 24)//机器人开启喷枪
    {
        g.DrawLine(pen0, sx, sy, z.X, z.Y);
    }
    else //机器人关闭喷枪
    {
        bh0 = new SolidBrush(Color.FromArgb((137 * d1[z]) % 255, 255 - (67 * d1[z]) % 255, (39 * d1[z]) % 255));
        g.FillEllipse(bh0, sx - 4, sy - 4, 8, 8);
        g.FillEllipse(bh0, z.X - 4, z.Y - 4, 8, 8);
        pen0 = new Pen(Color.LightGray, 1);
        g.DrawLine(pen0, sx, sy, z.X, z.Y);
    }
    sx = z.X; sy = z.Y;
}

//显示结果:
textBox1.Text = txtAll;
pictureBox1.Image = bmp;

 

上一篇:离散化


下一篇:洛谷P1443 马的遍历 题解 bfs dfs