利用二维码定位技术实现桌面机器人简易定位方案(下篇)

news2024/11/18 9:02:07

目录

  • 1、前言
  • 2、定位标签定义
  • 3、识别算法流程
  • 4、python编程
    • 4.1 查找三个回字定位点python
    • 4.2 根据三个定位点坐标位置关系,进行识别python
    • 4.3 根据实际坐标对当前图像进行矫正python
    • 4.4 计算物体的坐标值python
  • 总结
  • 本篇对应python源码

1、前言

机械手臂尤其是工业场景下大部分的应用是在一个平面(桌面)内完成一些抓取工作。一般可以用示教方式完成重复步骤。但是示教方式,对于一些活动的工件,或者相对比较开放的环境,不太适用,很多通过视觉来做定位。视觉定位最为稳定的要数二维码的识别,这个已经被无数行业场景所验证。
二维码定位本质上是轮廓监测及内外轮廓的固定关联关系的识别,以及基于平面的仿射变换:
二维码的定位原理是使用特定的图案和编码方式,在图像中确定二维码的位置和方向。二维码通常由多个小方块组成,其中包含了黑白相间的信息编码。
二维码的定位图案通常是一个大的正方形,内部包含了更小的正方形。这些小正方形被称为定位标记,用来表示二维码的位置和方向。定位标记的位置和大小是固定的,一般位于二维码的四个角落,用来确定二维码的边界。
我们可以在桌面机器人视觉定位中创造属于自己的类二维码定位框。这样就可以在精度要求不高的情况下稳定应用。
上篇我们把利用二维码定位技术的原理用于工作桌面平面定位的原理。在本篇继续完成实际应用工作。

2、定位标签定义

为了便于实操,我们在A4纸上三个定位标签并打印出来(打印资源见文后链接),效果如下:
在这里插入图片描述

测量实际的距离,左上右上的距离为182(或192)mm,(根据打印机设置以实际测量为准):
在这里插入图片描述

测量实际左上和左下的距离为265(或284)mm(根据打印机设置以实际测量为准):
在这里插入图片描述

我们可以定义坐标系如下:
在这里插入图片描述

根据以上三个定位点的位置关系,我们可以组织识别算法,可以在摄像头最准A4或(桌面)的任意旋转角度下,实现识别和定位。

3、识别算法流程

根据如上定位标签的几何关系特征,我们可以按照如下流程识别三个标签的位置,分别识别出哪个标签是左上,哪个标签是右上,哪个标签是左下。流程如下:
在这里插入图片描述
如上所示,按照三个点(A,B,C)组成的三角形,计算三条边,然后根据余弦公式,求出三角形的三个内角。可以看到,内角最大的一个就是我们的左上角的定位点(假如是B)。找到左上角定位点后,我们可以生成向量BA,和向量BC,然后可以根据两个向量的旋转关系得到哪个是右上角,哪个是左下角,如下图所示,α角为向量BA与x轴的夹角,β角是向量BC与x轴的夹角,可以看到根据角度和边长的关系,我们可以唯一确定哪个是右上角,哪个是左下角:
在这里插入图片描述
以上给出了两种情况,前提条件是将向量的与x轴的夹角转化到0~2pi的范围内。
根据第2节的标签位置定义,我们可以知道:dα/dβ趋向197/296或296/197

4、python编程

根据以上算法流程和思路,我们就可以用python实现了:

4.1 查找三个回字定位点python

以下程序是用于查找图片中的三个定位标签,存储到一个landmark_list的数组中。

	#读取图像
	image = cv2.imread('demo1.jpg')
    image = cv2.resize(image, (0, 0), fx=0.5, fy=0.5)
    cv2.imshow('sourceimg', image)

    # 转灰度、二值化、找轮廓
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)    
    _, thresh = cv2.threshold(gray, 100, 255,  cv2.THRESH_BINARY_INV)
    #查找轮廓    
    contours, hir = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

    # 定位标签存储
    landmark_list = []
    grandson_list = []
    landmark_centers=[]

    # 通过层级信息查找“回”字特征定位标签
    for index, landmark in enumerate(hir[0]):  # 查找最外层(A)轮廓        
        if landmark[2] != -1:#有儿子轮廓            
            son_index = landmark[2] 
            if hir[0][son_index][2]!=-1:#有孙子轮廓
                grandson_index=hir[0][son_index][2]                
                if hir[0][grandson_index][2]==-1:#没有子轮廓
                    
                    if cv2.arcLength(contours[grandson_index], True)<=0.0001:
                        pass
                    else:
                        dratio = cv2.arcLength(contours[index], True) / cv2.arcLength(contours[grandson_index], True)
                        #当回字形的内外轮廓比在2和3之间,人为是定位点
                        if 2 < dratio< 3:
                            landmark_list.append(landmark)
                            grandson_list.append(contours[grandson_index])
                            x, y, w, h = cv2.boundingRect(contours[grandson_index])
                            cx,cy=x + w//2, y + h//2
                            landmark_centers.append((cx,cy))
                           
     

4.2 根据三个定位点坐标位置关系,进行识别python

当获得定位点后,就可以按照上一节的原理进行识别,识别出哪个是左上角,哪个是右上角,哪个是左下角。
代码如下:

def find3P(points):
    #依次找到3个定位点顺序,右上,左上,左下
    if len(points)!=3:
        print("no 3 landmarks or more than 3")
        return False,None
    
    
    # 计算三个顶点对应的内角
    angles = []
    vectors=[]#向量1   
    pps=[[0,0],[0,0],[0,0]]#依次存储右上,左上,左下点
    for i in range(3):
        p1 = points[i]
        p2 = points[(i + 1) % 3]
        p3 = points[(i + 2) % 3]
        a = math.sqrt((p2[0] - p3[0])**2 + (p2[1] - p3[1])**2)
        b = math.sqrt((p1[0] - p3[0])**2 + (p1[1] - p3[1])**2)
        c = math.sqrt((p1[0] - p2[0])**2 + (p1[1] - p2[1])**2)
        angle = math.acos((c**2 + b**2 - a**2) / (2 * c * b))
        angles.append(angle)
        vector0= [p3[0]-p1[0],-p3[1]+p1[1]]#转化为xy坐标系
        vector1= [p2[0]-p1[0],-p2[1]+p1[1]]
        vectors.append([vector0,vector1])
    
    # 找出最大的内角对应的顶点
    left_top_index=angles.index(max(angles))
    pps[1]=[points[left_top_index][0] ,-points[left_top_index][1]]
    # 找出right top对应的顶点,与x轴夹角最大
    vector0,vector1=vectors[left_top_index]
    dtheta=cartesian_to_polar(vector1)-cartesian_to_polar(vector0) 
    
    #长度比检测
    if vector_magnitude(vector1)>vector_magnitude(vector0):
        if vector_magnitude(vector1)/vector_magnitude(vector0)<1.4:
            print("ratio wrong")
            return False,None
    else:
        if vector_magnitude(vector0)/vector_magnitude(vector1)<1.4:
            print("ratio wrong")
            return False,None

    
    if dtheta>math.pi :
        #大于pi,说明为左下角
        if vector_magnitude(vector1)/vector_magnitude(vector0)>1.4:
            pps[0]=[vector0[0]+pps[1][0],vector0[1]+pps[1][1]]
            pps[2]=[vector1[0]+pps[1][0],vector1[1]+pps[1][1]]
        else:
            pps[2]=[vector0[0]+pps[1][0],vector0[1]+pps[1][1]]
            pps[0]=[vector1[0]+pps[1][0],vector1[1]+pps[1][1]]
            
    else:
        #小于0,说明为右上角
        if vector_magnitude(vector0)/vector_magnitude(vector1)>1.4:
            pps[2]=[vector0[0]+pps[1][0],vector0[1]+pps[1][1]]
            pps[0]=[vector1[0]+pps[1][0],vector1[1]+pps[1][1]]
        else:
            pps[0]=[vector0[0]+pps[1][0],vector0[1]+pps[1][1]]
            pps[2]=[vector1[0]+pps[1][0],vector1[1]+pps[1][1]]
            
    
    for i in range(3):
        #转回图像坐标系
        pps[i][1]=int(-pps[i][1])
        pps[i][0]=int(pps[i][0])
    
    return True,pps

4.3 根据实际坐标对当前图像进行矫正python

当得到三个点的确切位置后,就可以利用opencv自带的cv2.getAffineTransform函数进行矫正。

PS:cv2.getAffineTransform是OpenCV库中的一个函数,用于获取仿射变换矩阵。仿射变换是一种二维几何变换,可以通过平移、旋转、缩放和剪切等操作来改变图像的形状和位置。

该函数需要输入三个点的坐标,这三个点分别是原始图像中的三个点和目标图像中对应的三个点。根据这些点的坐标关系,cv2.getAffineTransform函数会计算出一个2x3的仿射变换矩阵。

这个仿射变换矩阵可以用于对原始图像进行变换,使得原始图像中的三个点在目标图像中的位置与给定的目标点一致。通过cv2.warpAffine函数可以将原始图像进行仿射变换。

代码如下:

		#定义以真实的A4值上的三个坐标点,从右上、左上、左下三个点,一个像素代表1mm,为了显示方便查看我们把图像放大ratio倍
		realpoints=[[(10+182)*ratio, 10*ratio], [10*ratio, 10*ratio], [10*ratio, (10+265)*ratio]]    
		src_pts = np.array([pp3[0],pp3[1],pp3[2]], dtype="float32")
        dst_pts = np.array(realpoints, dtype="float32")
        #获取变换矩阵
        M = cv2.getAffineTransform(src_pts, dst_pts)
        #根据变换矩阵,得到矫正后的图像
        Rbackimg = cv2.warpAffine(image, M, (227*ratio, 324*ratio))
        #...

至此我们得到了矫正后的图像:
原来的图像:
在这里插入图片描述
矫正后的图像,可以看到跟我们开始打印的图像是一致了:
在这里插入图片描述

4.4 计算物体的坐标值python

在以上的基础上,我们就可以加入物体识别算法,如YOLO,或者颜色查找等算法获取物体的坐标,然后简单转化到实际坐标,这里我们直接用鼠标去点击矫正后的图像,直接输出指定位置的实际坐标:
在这里插入图片描述
如上图,点击物体,可以得出在真实坐标系下的坐标为(81.5,112.0)
分别点击三个点附近的点,可以看到,输出跟实际真实坐标分别是(182,0),(0,0),(0,265)相差不大。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

总结

通过二维码定位技术,我们可以方便构建桌面视觉物体识别的坐标系统,并可以结合机械臂进行抓取。
后续,我们可以配合一些图像识别算法,实现物体的识别,并结合机械臂进行抓取。注意,此种方法在摄像头与桌面不平衡度大时,远离左上点部分会产生较大误差。因此,摄像机的摆放尽量要与桌面(A4)值平行。

本篇对应python源码

所有python源码,和A4打印文件已经上传,地址如下:地址
或进公众号获取。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1610387.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

vue全屏后下拉框失效

如图&#xff0c;vue页面有个全屏功能 问题&#xff1a;全屏后下拉菜单消失 解决&#xff1a;加个这个 :teleported"false"如果不行试试这个 :popper-append-to-body"false"ok我话说完

【1425】java 外籍人员管理系统Myeclipse开发mysql数据库web结构jsp编程servlet计算机网页项目

一、源码特点 java 外籍人员管理系统是一套完善的java web信息管理系统 采用serlvetdaobean&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式 开发。开发环境为TOMCAT7.0,Myeclipse8.5开发&#xff…

Mac M3 安装Ollama和llama3,本地部署LobeChat和刘皇叔聊三国!

OllamaLobeChat&#xff0c;本地部署聊天助手 Ollama安装下载OllamaOllama常用指令和链接运行OllamaAPI 交互Ollama基于Llama 3角色扮演 LobeChat安装首先安装docker安装LobeChat的docker 镜像和运行 Ollama安装 下载Ollama 网址&#xff1a;https://ollama.com/ 支持macOS、…

每日一题(L2-011):玩转二叉树--建树+层序遍历

与L2-006近乎相同&#xff0c;先建树&#xff0c;然后遍历 #include<bits/stdc.h> using namespace std; int in[35]; int pre[35]; typedef struct Tree{int num;Tree* left;Tree* right; }T;T * build(int in1,int in2,int pre1,int pre2){T * tnew T;t->numpre[pr…

代码优化实践之税率计算问题

开篇 今天的问题来自于《编程珠玑》第三章【数据决定程序结构】&#xff0c;这里提出了几条代码优化相关的原则&#xff0c;受益不浅。下面是提到的几条原则&#xff1a; 使用数组重新编写重复代码。冗长的相似代码往往可以使用最简单的数据结构——数组来更好的表述&#xff1…

JS-29-Promise对象

一、JavaScript的异步操作 在JavaScript的世界中&#xff0c;所有代码都是单线程执行的。 由于这个“缺陷”&#xff0c;导致JavaScript的所有网络操作&#xff0c;浏览器事件&#xff0c;都必须是异步执行。异步执行可以用回调函数实现&#xff1a; function callback() {c…

三种空间数据的聚合算法

原始数据分布 给老外做的Demo&#xff0c;所以是英文界面。 原始数据分布情况如下&#xff1a; geojson文本内容&#xff1a; 三种方法基本原理 三种聚合算法来做一个例子&#xff08;500条记录&#xff09;。 方法1&#xff1a;按Ol默认方法进行聚类&#xff0c;使用Open…

Python+GDAL 栅格坐标系转换(自动计算输出像元大小)

GDAL对栅格进行坐标系转换不难&#xff0c;直接用gdal.Warp()就可以了 gdal.Warp("output", "input", dstSRSEPSG:***, xRes**, yRes**, targetAlignedPixelsTrue)麻烦的是&#xff0c;需要的参数xRes和yRes&#xff0c;gdal.Warp()不能自动计算。坐标系转…

手机号码空号过滤API:有效验证和过滤无效电话号码

随着移动通信技术的发展&#xff0c;手机号码成为人们日常生活和工作中不可或缺的一部分。然而&#xff0c;随着时间的推移&#xff0c;一些手机号码可能会变成空号&#xff0c;这给企业在进行电话营销和数据分析时带来了一定的困扰。为了解决这个问题&#xff0c;挖数据平台提…

java+idea+mysql采用医疗AI自然语言处理技术的3D智能导诊导系统源码

javaideamysql采用医疗AI自然语言处理技术的3D智能导诊导系统源码 随着人工智能技术的快速发展&#xff0c;语音识别与自然语言理解技术的成熟应用&#xff0c;基于人工智能的智能导诊导医逐渐出现在患者的生活视角中&#xff0c;智能导诊系统应用到医院就医场景中&#xff0c…

C++ 使用共享内存的进程通信方式模拟生产者消费者模型

编码环境如下 系统环境&#xff1a;linux 信号量&#xff1a;使用Linux操作系统的SystemV信号量 生产者代码如下 #include <iostream> #include <sys/sem.h> #include <sys/shm.h> #include <string.h>#define SEM_KEY 0x5678 #define SHM_KEY 0xAB…

九、Yocto创建SDK,给Makefile/CMake使用

文章目录 Yocto创建SDK、Toolchain&#xff0c;给Makefile/CMake使用一、介绍二、创建Yocto sdk三、使用sdk 配合makefile编译应用程序四、使用sdk 配合cmake编译应用程序 Yocto创建SDK、Toolchain&#xff0c;给Makefile/CMake使用 本篇文章为基于raspberrypi 4B单板的yocto实…

deepinV23安装cuDnn

文章目录 下载压缩包安装解压复制文件查看cudnn版本 注意&#xff1a; 要先安装CUDA 下载压缩包 官网&#xff1a;https://developer.nvidia.com/cudnn-downloads 若要下载非最新版&#xff0c;请点击网页底部的Archive of Previous Releases 方法1&#xff1a;使用wget命令…

C语言之文件操作【万字详解】

目录 一.什么是文件&#xff1f; 二.为什么要使用文件&#xff1f; 三.文件的分类 3.1.程序文件 3.2.数据文件 四.二进制文件和文本文件 五.文件的打开和关闭 &#xff08;重点&#xff09; 5.1流和标准流 5.1.1何为流&#xff1f; 5.1.2.标准流 5.2文件指针 5.3文件的打开和关…

基于粒子群算法-考虑需求响应的风-光-柴-储容量优化配置

部分代码&#xff1a; clc; clear; close all; num_wt3.7; %风机数量 num_pv214.57; %光伏板数量 num_g1; %柴油发电机数量 num_sb52.47; %蓄电池数量 %% 数据区 % DATExlsread(date.xlsx);%原始数据 load DATE; LoadDATE(3,:);%全年负荷数据 Speed_WTDATE(1,:);%全年风…

【文章复现】基于主从博弈的社区综合能源系统分布式协同 优化运行策略

随着能源市场由传统的垂直一体式结构向交互竞争型 结构转变&#xff0c;社区综合能源系统的分布式特征愈发明显&#xff0c;传统 的集中优化方法难以揭示多主体间的交互行为。该文提出一 种基于主从博弈的社区综合能源系统分布式协同优化运行 策略&#xff0c;将综合能源销售商…

计算机网络(三)数据链路层

数据链路层 基本概念 数据链路层功能&#xff1a; 在物理层提供服务的基础上向网络层提供服务&#xff0c;主要作用是加强物理层传输原始比特流的功能&#xff0c;将物理层提供的可能出错的物理连接改在为逻辑上无差错的数据链路&#xff0c;使之对网络层表现为一条无差错的…

【结构型模式】适配器模式

一、适配器模式概述 适配器模式的定义-意图&#xff1a;将一个类的接口转换成客户希望的另一个接口。适配器模式让那些接口不兼容的类可以一起工作。(对象结构模式->对象适配器/类结构模式->类适配器) 适配器模式包含三个角色&#xff1a;目标(Target)角色、适配者(Adapt…

又成长了,异常掉电踩到了MySQL主从同步的坑!

&#x1f4e2;&#x1f4e2;&#x1f4e2;&#x1f4e3;&#x1f4e3;&#x1f4e3; 哈喽&#xff01;大家好&#xff0c;我是【IT邦德】&#xff0c;江湖人称jeames007&#xff0c;10余年DBA及大数据工作经验 一位上进心十足的【大数据领域博主】&#xff01;&#x1f61c;&am…

VUE 使用 Vite 创建一个 vue3.0 + vite 项目

Vite 是一种新型前端构建工具&#xff0c;能够显著提升前端开发体验。它主要由两部分组成&#xff1a; 1. 一个开发服务器&#xff0c;它基于 原生 ES 模块 提供了 丰富的内建功能&#xff0c;如速度快到惊人的 模块热更新&#xff08;HMR&#xff09;。 2. 一套构建指令&#…