在上一篇【Originbot探索发现③】AprilTag探索 中,从AprilTag码入手,分析了AprilTag码工具包的安装使用,主要原理以及标定原理,本节将继续探索,首先针对Originbot小车的相机进行标定,然后去通过AprilTag码获取平面的法向量。
1. 获取Originbot相机内参
具体操作步骤:
- 首先下载一个棋盘格的图打印下来,我这里提供一个 棋盘格.pdf,点开就能下载
- 平铺在桌面上,用Originbitd的相机对其拍n张不同角度的照片,下面给一个快速查看Originbot图像的方式,参考相机驱动与图像可视化:
SSH连接OriginBot成功后,在终端中输入如下指令:
ros2 launch websocket hobot_websocket.launch.py
- 然后同一网络的PC打开浏览器,网址栏输入Originbit的IP,点击web展示端,加可以看到图像了,然后直接截图
- 将截图的照片放在同一个文件夹内
- 运行标定程序读到相机参数
下面直接放代码:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2023/6/10 13:37
# @Author : Chenan_Wang
# @File : calib.py
# @Project : charging
# @Software : PyCharm
import numpy as np
import cv2 as cv
import glob
criteria = (cv.TERM_CRITERIA_EPS + cv.TERM_CRITERIA_MAX_ITER, 30, 0.001)
objp = np.zeros((8 * 5, 3), np.float32)
objp[:, :2] = np.mgrid[0:8, 0:5].T.reshape(-1, 2)
# 用于存储所有图像中的对象点和图像点的数组。
obj_points = [] # 世界坐标系中的3d点
img_points = [] # 图像坐标系中的2d点
images = glob.glob("C:/Users/***/Desktop/image/*jpg") # 修改存放路径
win_name = "Verify"
cv.namedWindow(win_name, cv.WND_PROP_FULLSCREEN)
cv.setWindowProperty(win_name, cv.WND_PROP_FULLSCREEN, cv.WINDOW_FULLSCREEN)
print("getting images")
for f_name in images:
img = cv.imread(f_name)
print(f_name)
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
# 找到棋盘角
ret, corners = cv.findChessboardCorners(gray, (8, 5), None)
if ret:
obj_points.append(objp)
# 第三个为搜索窗口的一半尺寸。若为(5,5),那就表示用 (5*2+1)×(5*2+1)=11×11 大小的搜索窗口。
corners2 = cv.cornerSubPix(gray, corners, (5, 5), (-1, -1), criteria)
img_points.append(corners)
# 绘制并显示角
cv.drawChessboardCorners(img, (8, 5), corners2, ret)
cv.imshow(win_name, img)
cv.waitKey(500)
cv.destroyAllWindows()
size = gray.shape[::-1]
ret, cam_mtx, dist, rvecs, tvecs = cv.calibrateCamera(obj_points, img_points, size, None, None)
print(">==> Starting calibration")
print("ret:", ret) # 所有校正点偏差距离平方和
print("mtx:\n", cam_mtx) # 内参数矩阵K
print("dist:\n", dist) # 畸变系数 distortion coefficients = (k_1, k_2, p_1, p_2, k_3)
print("rvecs:\n", rvecs) # 旋转向量 (外参数)
print("tvecs:\n", tvecs) # 平移向量 (外参数)
cv.waitKey()
cv.destroyAllWindows()
期间会看到如图所示的标定过程
最后得到如下的输出:
拿到内矩阵参数矩阵K:
mtx = np.array([[1421.94379, 0.0, 1904.90777],
[0.0, 1431.00638, 1013.22526],
[0.0, 0.0, 1.0]])
2. 估算平面法向量
接下来查看法向量,这样就可以通过二维的AprilTag码推算出相机(Originbot小车与贴有码的充电桩的相对位置了)
先通过刚刚截图的手段,用Originbot获取一张AprilTag码的图片,如下
同样放出完整代码,将刚刚标定得到的相机内参填入进去
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2023/6/11 15:06
# @Author : Chenan_Wang
# @File : AprilTag_Test.py
# @Project : charging
# @Software : PyCharm
import pupil_apriltags as apriltag # Windows
# import apriltag # Ubuntu
import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
from scipy.spatial.transform import Rotation
img = cv.imdecode(
np.fromfile('C:/Users/INTEL/Desktop/AprilTag_img/008.png', dtype=np.uint8),
cv.IMREAD_COLOR)
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
at_detector = apriltag.Detector(families='tag25h9') # Windows
# at_detector = apriltag.Detector(apriltag.DetectorOptions(families='tag36h11 tag25h9')) # Ubuntu
tags = at_detector.detect(gray)
for tag in tags:
# print('tag', tag)
for i in range(4):
cv.circle(img, tuple(tag.corners[i].astype(int)), 4, (255, 0, 0), 3)
cv.circle(img, tuple(tag.center.astype(int)), 4, (255, 0, 0), 5)
# 获取单应矩阵
homo = tags[0].homography
# 相机内参矩阵
mtx = np.array([[1421.94379, 0.0, 1904.90777],
[0.0, 1431.00638, 1013.22526],
[0.0, 0.0, 1.0]])
# 单应矩阵分解
num, Rs, Ts, Ns = cv.decomposeHomographyMat(homo, mtx)
print("num: {}".format(num), "\nRs: {}".format(Rs), "\nTs: {}".format(Ts), "\nNs: {}".format(Ns))
angle_dim = []
r = Rotation.from_matrix(Rs[1].T)
euler_angle = r.as_euler('xyz').T * 180 / np.pi
angle_dim.append(euler_angle[2])
dir_angle = (euler_angle[2] - 5) * np.pi / 180 * 1.8
ARROW_LENGTH = 120
delta_x = np.sin(dir_angle) * ARROW_LENGTH
delta_y = ARROW_LENGTH / 2 * np.cos(dir_angle)
new_center = tags[0].center + np.array([delta_x, delta_y])
cv.line(img, tuple(new_center.astype(int)), tuple(tags[0].center.astype(int)), (255, 0, 0), 2) # 画出法向量
cv.circle(img, tuple(new_center.astype(int)), 4, (0, 0, 255), 5) # 法向量方向标红圈
cv.imshow('img', img)
cv.waitKey()
cv.destroyAllWindows()
输出如下:
num: 4
Rs: (array([[-0.08762995, 0.35394522, 0.93115185],
[-0.77831311, -0.60773327, 0.15776239],
[ 0.62173121, -0.71090298, 0.32873584]]), array([[-0.08762995, 0.35394522, 0.93115185],
[-0.77831311, -0.60773327, 0.15776239],
[ 0.62173121, -0.71090298, 0.32873584]]), array([[ 0.51211893, 0.71833206, 0.47088561],
[-0.72576777, 0.65509626, -0.21002389],
[-0.45934229, -0.23419639, 0.85682945]]), array([[ 0.51211893, 0.71833206, 0.47088561],
[-0.72576777, 0.65509626, -0.21002389],
[-0.45934229, -0.23419639, 0.85682945]]))
Ts: (array([[ 0.63170677],
[ 1.28788424],
[-0.00364431]]), array([[-0.63170677],
[-1.28788424],
[ 0.00364431]]), array([[ 0.57120038],
[-0.23733832],
[-1.29426164]]), array([[-0.57120038],
[ 0.23733832],
[ 1.29426164]]))
Ns: (array([[ 0.19462906],
[ 0.91219589],
[-0.36058034]]), array([[-0.19462906],
[-0.91219589],
[ 0.36058034]]), array([[-0.83473401],
[ 0.37089171],
[ 0.40701163]]), array([[ 0.83473401],
[-0.37089171],
[-0.40701163]]))
可以看到有四种可能的结果。
查看输出图片,看到最终效果如下:
实际法向量差不多就是向上的,基本正确,具体精度和偏移量需要后续进一步的计算
如果将代码中的:
img = cv.imdecode(
np.fromfile('C:/Users/INTEL/Desktop/AprilTag_img/008.png', dtype=np.uint8),
cv.IMREAD_COLOR)
修改为摄像头的视频流信息,设定好帧率,就可以通过视频信息,将AprilTag码在Originbot前面不同角度测试,获取实时的效果了。
评论(0)
您还未登录,请登录后发表或查看评论