ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

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

2021-07-24 11:02:48  阅读:219  来源: 互联网

标签:sy sx wBit 喷涂 int 73 new 工件 ptr


 

/*
工业视觉_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;

 

标签:sy,sx,wBit,喷涂,int,73,new,工件,ptr
来源: https://blog.csdn.net/baidu_39430410/article/details/119054698

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有