视觉标记定位aruco使用

2023-05-16

本文的目的是实现生成一张marker broad图片,告诉标记检测程序tag在真实世界中的实际大小。
检测成功后得到marker的id,四个角点坐标,marker到相机的平移和旋转。

1.下载安装参考

opencv 中的aruco源码下载要到下面地址
opencv 中的aruco源码下载
https://github.com/opencv/opencv_contrib/tree/master/modules/aruco
https://github.com/opencv/opencv_contrib/releases/tag/3.3.0

2.生成单个marker图片

程序如下

#include <opencv2/opencv.hpp>
#include <opencv2/aruco.hpp>
#include <opencv2/calib3d/calib3d.hpp>
#include "opencv2/core/core.hpp"
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <iostream>

using namespace std;
using namespace cv;

int main()
{
    cv::Mat markerImage;
    cv::Ptr<cv::aruco::Dictionary> dictionary = 	cv::aruco::getPredefinedDictionary(cv::aruco::DICT_6X6_250);
    cv::aruco::drawMarker(dictionary, 23, 200, markerImage, 1);
    imwrite("./aruco_tag.png",markerImage);
    imshow("test", markerImage);//显示marker
    waitKey();
    return 0;
}

cv::aruco::drawMarker
第一个参数是之前创建的Dictionary对象。
第二个参数是marker的id,在这个例子中选择的是字典DICT_6X6_250 的第23个marker。注意到每个字典是由不同数目的Marker组成的,在这个例子中的字典中,有效的Id数字范围是0到249。不在有效区间的特定id将会产生异常。
三个参数,200,是输出Marker图像的大小。在这个例子中,输出的图像将是200x200像素大小。注意到这一参数需要满足能够存储特定字典 的所有位。举例来说,你不能为6x6大小的marker生成一个5x5图像(这还没有考虑到Marker的边界)。除此之外,为了避免变形,这一参数最好和位数+边界的大小成正比,或者至少要比marker的大小大得多(如这个例子中的200),这样变形就不显著了
第四个参数是输出的图像。
最终,最后一个参数是一个可选的参数,它指定了Marer黑色边界的大小。这一大小与位数数目成正比。例如,值为2意味着边界的宽度将会是2的倍数。默认的值为1。

3.打印并标定相机内参

注意,打印的时候如果用像素为200200的图像打印,实际打印大小为20cm20cm,那么一个像素对应1毫米。
内参标定就不介绍了,此实验使用内参为

intrinsic_matrix: !!opencv-matrix
   rows: 3
   cols: 3
   dt: d
   data: [ 420.019, 0., 330.8676, 0.,
       419.6044, 217.8731, 0., 0., 1. ]
distortion_vector: !!opencv-matrix
   rows: 1
   cols: 4
   dt: d
   data: [ -0.3549, 0.1151, -0.0035, -0.0029 ]

的相机拍出来的图像如下
在这里插入图片描述

4.检测marker并得到id和相对位移

确定好实际打印出来的marker的边长和内参就可以检测并计算了。
其中markerlength表示marker的实际物理长度。
使用上面的图像和内参程序如下

#include <opencv2/opencv.hpp>
#include <opencv2/aruco.hpp>
#include <opencv2/calib3d/calib3d.hpp>
#include "opencv2/core/core.hpp"
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <iostream>
#include <eigen3/Eigen/Core>
#include <eigen3/Eigen/Geometry>
#include <opencv2/core/eigen.hpp>

using namespace std;
using namespace cv;

int main()
{
    cv::Mat m_image=imread("./mark.jpg");
    if(m_image.empty())
    {
        cout<<"m_image  is empty"<<endl;
        return 0;
    }
    //read para
   double markerlength=0.105;
   cv::Mat intrinsics = (Mat_<double>(3, 3) <<
                         420.019, 0.0, 330.8676,
                         0.0,419.6044, 217.8731,
                         0.0, 0.0, 1.0);

    cv::Mat distCoeffs=(cv::Mat_<double>(4, 1) <<  -0.3549, 0.1151, -0.0035, -0.0029);
    cv::Mat  imageCopy;
    m_image.copyTo(imageCopy);
    cv::Ptr<cv::aruco::Dictionary> dictionary = cv::aruco::getPredefinedDictionary(cv::aruco::DICT_6X6_250);

    std::vector<int> ids;
    std::vector<std::vector<cv::Point2f>> corners;
    cv::aruco::detectMarkers(m_image, dictionary, corners, ids);//检测靶标
    // if at least one marker detected
    if (ids.size() > 0) {
        cv::aruco::drawDetectedMarkers(imageCopy, corners, ids);//绘制检测到的靶标的框
        for(unsigned int i=0; i<ids.size(); i++)
        {
           std::vector<cv::Vec3d> rvecs, tvecs;
          cv::aruco::estimatePoseSingleMarkers(corners[i], markerlength, intrinsics, distCoeffs, rvecs, tvecs);//求解旋转矩阵rvecs和平移矩阵tvecs
            cv::aruco::drawAxis(imageCopy,intrinsics,distCoeffs, rvecs[i], tvecs[i], 0.1);
            //3.rotaion vector to eulerAngles
            cv::Mat rmat;
            Rodrigues(rvecs[i], rmat);
            Eigen::Matrix3d rotation_matrix3d;
            cv2eigen(rmat,rotation_matrix3d);
            Eigen::Vector3d eulerAngle = rotation_matrix3d.eulerAngles(0,1,2);//(0,1,2)表示分别绕XYZ轴顺序,即 顺序,逆时针为正
            cout<<"pitch "<<eulerAngle.x()<<"yaw "<<eulerAngle.y()<<"roll"<<eulerAngle.z()<<endl;
            cout<<"x= "<<tvecs[i][0]<<"y="<<tvecs[i][1]<<"z="<<tvecs[i][2]<<endl;
        }
    }
    cv::imshow("out", imageCopy);
    cv::waitKey();

    return 0;
}

其中
The parameters of detectMarkers are:
The first parameter is the image where the markers are going to be detected.
The second parameter is the dictionary object, in this case one of the predefined dictionaries (DICT_6X6_250).
The detected markers are stored in the markerCorners and markerIds structures:
markerCorners is the list of corners of the detected markers. For each marker, its four corners are returned in their original order (which is clockwise starting with top left). So, the first corner is the top left corner, followed by the top right, bottom right and bottom left.
markerIds is the list of ids of each of the detected markers in markerCorners. Note that the returned markerCorners and markerIds vectors have the same sizes.
The fourth parameter is the object of type DetectionParameters. This object includes all the parameters that can be customized during the detection process. This parameters are commented in detail in the next section.
The final parameter, rejectedCandidates, is a returned list of marker candidates, i.e. those squares that have been found but they do not present a valid codification. Each candidate is also defined by its four corners, and its format is the same than the markerCorners parameter. This parameter can be omitted and is only useful for debugging purposes and for ‘refind’ strategies (see refineDetectedMarkers() ).

5实验效果

输出

pitch 3.12894yaw -0.0187251roll-1.5281
x= -0.011554y=-0.0038433z=0.17224

在这里插入图片描述

6.生成多个marker组成的board

参考http://www.pianshen.com/article/2639341324/

#include <opencv2/opencv.hpp>
#include <opencv2/aruco.hpp>
#include <opencv2/calib3d/calib3d.hpp>
#include "opencv2/core/core.hpp"
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <iostream>

using namespace std;
using namespace cv;

int main()
{
    int markersX = 5;//X轴上标记的数量
    int markersY = 5;//Y轴上标记的数量   本例生成5x5的棋盘
    int markerLength = 100;//标记的长度,单位是像素
    int markerSeparation = 20;//每个标记之间的间隔,单位像素
    int dictionaryId = cv::aruco::DICT_4X4_50;//生成标记的字典ID
    int margins = markerSeparation;//标记与边界之间的间隔

    int borderBits = 1;//标记的边界所占的bit位数
    bool showImage = true;

    Size imageSize;
    imageSize.width = markersX * (markerLength + markerSeparation) - markerSeparation + 2 * margins;
    imageSize.height =
        markersY * (markerLength + markerSeparation) - markerSeparation + 2 * margins;

    Ptr<aruco::Dictionary> dictionary =
        aruco::getPredefinedDictionary(aruco::PREDEFINED_DICTIONARY_NAME(dictionaryId));

    Ptr<aruco::GridBoard> board = aruco::GridBoard::create(markersX, markersY, float(markerLength),
        float(markerSeparation), dictionary);

    // show created board
    Mat boardImage;
    board->draw(imageSize, boardImage, margins, borderBits);

    if (showImage) {
        imwrite("./aruco_tag_board.png",boardImage);
        imshow("board", boardImage);
        waitKey(0);
    }

    return 0;
}

参考文献
https://blog.csdn.net/A_L_A_N/article/details/83657878

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

视觉标记定位aruco使用 的相关文章

随机推荐

  • Simulink中从Workspace中读取时序数据的方法

    1 首先 xff0c 我从adams得到是时长5秒的500组加速度数据 xff0c 将其存为txt格式 并放入matlab路径中 xff0c 其第一列为时间序号 xff0c 234列为三轴的加速度数据 2 在workspace中使用 tex
  • 简单聊聊Betaflight的三种飞行模式

    大概查了一下网上介绍Betaflight飞行模式的文章很多 xff0c 讲了很多很全面 xff0c 但这里我们去粗取精 xff0c 只谈常用的三种模式Angle xff0c Horizon和Acro模式 下面的内容全部翻译自这个英文网站 1
  • Vim中快速定位到某一行的方法

    1 定位到第一行 xff1a 1 43 shift 43 G 2 定位到最后一行 xff1a shift 43 G 3 定位到第x行 xff1a x 43 shift 43 G 或在Vim中 xff1a xff1a x 补充 xff1a 在
  • PX4源码学习(一):结构概述

    最近在做PX4固件的移植开发工作 xff0c 由于之前没有这方面开发经验 xff0c 加之PX4源码又比较庞杂 xff0c 所以想要通过一点一点的学习梳理和实践 xff0c 使这部分工作能够尽快开展起来 博客中如有错误 xff0c 恳请大家
  • PX4(Pixhawk)和Audupilot(APM)的区别与联系

    一 各自的简要介绍 pixhawk是硬件平台 xff0c PX4是pixhawk的原生固件 xff0c 专门为pixhawk开发 APM xff08 Ardupilot Mega xff09 也是硬件 xff0c Ardupilot是APM
  • Makefile和Cmake的区别和联系

    最近在搞无人机飞控的学习 xff0c 大致了解了下PX4的文件结构和编译 xff0c 它的文件中有许多Makefile和Cmake文件 xff0c 对其在整个文件编译过程中的作用不甚了解 在进行一番查询后 xff0c 终于有了个大致的认识
  • 浮点数在计算机中的表示,程序中浮点数的取值和比较。

    小数的十进制 二进制转换 十进制 gt 二进制 整数部分除2取余 xff0c 小数部分乘2取整 考虑 8 25 整数部分8进行除2取余 xff0c 除2商4余0 除2商2余0 除2商1余0 除2商0余1 所以结果是1000 最后一个余数在最
  • 关于PX4系统移植的新的硬件平台一些尝试总结

    最近尝试将PX4的firmware v1 11 0移植到某stm32h7的飞控平台上 xff08 该飞控硬件 xff0c 适配ardupilot和betaflight的固件 xff0c 但不支持PX4 xff0c 跟厂家沟通过 xff0c
  • RTK中浮点解、固定解的区别

    1 RTK固定解 xff08 fix xff09 简言之 xff0c 拥有固定解意味着解算出了正确的解 在常规条件下 xff0c 你拥有了1 3cm的测量精度 2 RTK浮点解 xff08 float xff09 又称差分解 xff0c 此
  • 飞机的姿态角总结

    飞机的俯仰 横滚 航行角统称姿态角 是飞机机体系相对地理系的相对转角 1 航向角为机体纵轴OYb轴在水平面上投影与OYt之间的夹角 xff0c 取值范围为 0 xff0c 360 xff0c 以机体从北向东偏转为正 2 俯仰角为机体纵轴OY
  • Matlab一些设置记录

    在使用matlab时经常要查一些命令 xff0c 索性在这里整理做一个集合 1 设置figrue背景为白色 xff08 默认为灰色 xff0c 直接截图贴图使用时有一丢丢影响效果 xff09 set 0 39 defaultfigureco
  • 如何学好嵌入式的嵌入式

    近来嵌入式挺火 xff0c 于是大家都往这里挤 我想提醒大家的是 xff0c 嵌入式马上也会成为如今的软件业 在你进来之前请先考虑清楚 但只要我们真的学精了一样东西 xff0c 不管它将来变成什么样 xff0c 哪怕最后只剩下一个人 xff
  • Python全局变量和局部变量(超详细,纯干货,保姆级教学)

    全局变量定义 在函数外部定义的变量 所有函数内部都可以使用这个变量 局部变量定义 在函数内部定义的变量 这个变量只能在定义这个变量的函数内部使用 第一种 xff1a global定义全局变量在自定义函数内部 定义看起来一愣一愣的 xff0c
  • stm32——手动移植HAL库以及错误解决方案(以STM32F103ZE为例)

    寄存器编程的缺点 xff1a 代码可读性差 xff0c 二次开发难度大 xff0c 而且要每次都查阅用户手册 xff0c 非常麻烦 HAL库 xff1a HAL库封装出了一层通用性的接口 xff0c 标准化了一套通用性的接口 xff0c 大
  • MATLAB在线编辑器online

    话不多说直接上网址 https matlab mathworks com 这个和下载的MATLAB功能一模一样 xff0c 这是我找了几个例子运行出来的结果 xff0c 和我想要的一模一样 xff0c 不过对于大多数人而言 xff0c 这个
  • stm32——使用结构体描述寄存器映射

    将地址信息放在一个头文件中方便管理 xff0c 存放地址和偏移量 STM32的外设寄存器的组织形式是 基于基地址 43 寄存器偏移地址 比如 xff0c 在RCC的基地址基础上 xff0c 偏移0x00得到RCC CR寄存器 xff0c 偏
  • 江科大stm32-概述

    第一章 STM32概述 1 1 资源介绍 STM32F103C8T6 51单片机使用的是5V供电 xff0c 还有USB输出的电压也是5V xff0c 5V是不在这个供电电压范围内的 xff0c 不能直接给STM32供电 xff0c 如果是
  • 在eclipse中查看你用的tomcat的路径

    打开eclipse xff0c 选择window gt Preferences gt Server gt Runtime Environments选择你的tomcat然后点Edit xff0c 就会出现它的路径了
  • 安装龙蜥或CentOS 7时出现dracut- initqueue timeout解决方法

    在安装龙蜥7 9操作系统时 xff0c 出现dracut initqueue timeout starting starting timeout scripts报错 CentOS 7 9出现此问题也可以参考同样的方法 如何制作启动盘和系统盘
  • 视觉标记定位aruco使用

    本文的目的是实现生成一张marker broad图片 xff0c 告诉标记检测程序tag在真实世界中的实际大小 检测成功后得到marker的id 四个角点坐标 marker到相机的平移和旋转 xff11 xff0e 下载安装参考 openc