前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【传感器标定】相机与雷达外参标定(理论与代码)

【传感器标定】相机与雷达外参标定(理论与代码)

作者头像
读书猿
发布2024-02-05 15:12:40
1760
发布2024-02-05 15:12:40
举报
文章被收录于专栏:无人驾驶感知无人驾驶感知

前言

1、相机外参标定需要提前已知相机内参,相机内参标定这里不细谈, 推荐一篇博客计算机视觉(相机标定;内参;外参;畸变系数

2、外参标定转化数学问题:计算一个三维坐标系到另一个三维坐标系的旋转与平移。

3、标定方案参考论文地址。实际标定对比,此方法精度高、操作方便。

4、标靶:贴一张虚拟的标靶图片:

(实际标靶要根据需求自行制作)

一、相机外参标定

1.1、apritag识别

1、在标靶四个角贴上标签

2、识别四个标签得到在图像坐标系下的坐标

代码语言:javascript
复制
def calibration_point(self):
    img_gray = cv2.cvtColor(self.image, cv2.COLOR_BGR2GRAY)
    detector = apriltag.Detector(searchpath=["./"])
    results = detector.detect(img_gray)
    markerCenter = []
    for i in range(len(results)):
        tmp_obj = results[i]
        markerCenter.append(list(tmp_obj.center))
    return markerCenter
1.2、求相机坐标系下PA,PB,PC方向的单位向量

由像素坐标与相机坐标转换关系

取Zc = 1 。则 x, y, z = (u - cx) / fx, (v - cy) / fy, 1

归一化后可得单位向量PA,PB,PC

1.3、求角APB,角BPC,角CPA

由向量PA,PB,PC容易求出角APB,角BPC,角CPA

例:cos(角APB) = PA .PB

1.4、求PA,PB,PC的长

转化数学问题:四面体PABC,已知长AB、AC、BC,角APB,角BPC,角CPA,求PA、PB、PC的长。

通过已知条件联立方程可以求出解。通过消元法可以解

公式推导参考:消元法

通过余弦定理建立方程,转化后得出一个二元二次方程组。

消元法求解,可能有四组解。

代码语言:javascript
复制
def solve_for_length(self, threePoint):
    solution = []  # 保存的解
    lenAB, lenBC, lenCA = self.w, self.h, np.sqrt(self.w * self.w + self.h * self.h)
    p, q, r = self.get_three_point_r(threePoint)
    p, q, r = 2 * p, 2 * q, 2 * r
    a, b = np.power(lenAB / lenCA, 2), np.power(lenBC / lenCA, 2)
    a2, b2, p2, q2, r2, pr = a * a, b * b, p * p, q * q, r * r, p * r
    pqr = q * pr
    if p2 + q2 + r2 - pqr - 1 == 0:  # 四点共面无解
        return "四点共面,无解"
    ab, a_2, a_4 = a * b, 2 * a, 4 * a
    A = -2 * b + b2 + a2 + 1 + ab * (2 - r2) - a_2
    B = q * (-2 * (ab + a2 + 1 - b) + r2 * ab + a_4) + pr * (b - b2 + ab)
    C = q2 + b2 * (r2 + p2 - 2) - b * (p2 + pqr) - ab * (r2 + pqr) + (a2 - a_2) * (2 + q2) + 2
    D = pr * (ab - b2 + b) + q * ((p2 - 2) * b + 2 * (ab - a2) + a_4 - 2)
    E = 1 + 2 * (b - a - ab) + b2 - b * p2 + a2
    temp = (p2 * (a - 1 + b) + r2 * (a - 1 - b) + pqr - a * pqr)
    b0 = b * temp * temp
    if b0 == 0:  # 当b0=0时求得y=0 不符题意
        return "y=0,无解"

    unknownX = sympy.symbols('x')  # 求解x
    solve = sympy.solve(A * unknownX ** 4 + B * unknownX ** 3 + C * unknownX ** 2 + D * unknownX + E, unknownX)
    n = len(solve)  # 判断解的个数,有重根算一个
    if n == 0:
        return "x无根,无解"

    r3, pr2 = r2 * r, p * r2
    r3q = r3 * q
    inv_b0 = 1.0 / b0

    for i in range(n):
        x = solve[i]
        if abs(np.imag(complex(x))) < 1e-10:
            x = np.real(complex(x))
        else:
            continue
        if x < 0:  # 不符合题意,舍去此解
            continue
        x2 = x * x
        b1 = ((1 - a - b) * x2 + (q * a - q) * x + 1 - a + b) * (((r3 * (a2 + ab * (2 - r2) - a_2 + b2 - 2 * b + 1))
                    * x + (r3q * (2 * (b - a2) + a_4 + ab * (r2 - 2) - 2) + pr2 * (1 + a2 + 2 * (ab - a - b) + r2 * 
                    ( b - b2) + b2))) * x2 + (r3 * (q2 * (1 - 2 * a + a2) + r2 * (b2 - ab) - a_4 + 2 * (a2 - b2) + 2) + 
                    r * p2 * (b2 + 2 * (ab - b - a) + 1 + a2) + pr2 * q * (a_4 + 2 * (b - ab - a2) - 2 - r2 * b)) * x + 
                    2 * r3q * (a_2 - b - a2 + ab - 1) + pr2 * (q2 - a_4 + 2 * (a2 - b2) + r2 * b + q2 * ( a2 - a_2) + 2) 
                    + p2 * (p * (2 * (ab - a - b) + a2 + b2 + 1) + 2 * q * r * (b + a_2 - a2 - ab - 1))))
        if b1 <= 0:  # 求得y<0 舍去此解
            continue
        y = inv_b0 * b1
        v = x * x + y * y - x * y * r
        if v < 0:  # 小于0的解不符合
            continue
        lenPB = lenCA / np.sqrt(v)
        lenPC = x * lenPB
        lenPA = y * lenPB
        solution.append([lenPA, lenPB, lenPC])  # 符合条件的解
    return solution
1.5、svd分解求解旋转平移矩阵

根据1.4得出 四组 点A、B、C在相机坐标系下坐标

通过svd分解分别算四组旋转平移矩阵

svd分解数学推导参考:svd分解推导

计算点D在旋转平移后的重投影误差,选取重投影误差最小的作为最终标定结果。

代码语言:javascript
复制
def get_rt(childList, parentList):
    x = get_data(childList)
    y = get_data(parentList)
    zeroMatrix = np.zeros((3, 3))
    for i in range(y.shape[0]):
        newY = y[i].reshape(1, y.shape[1])
        newX = x[i].reshape(1, y.shape[1])
        zeroMatrix = zeroMatrix + newX.T @ newY

    svd = np.linalg.svd(zeroMatrix)
    U, E, V = svd
    midMatrix = np.array([[1, 0, 0],
                          [0, 1, 0],
                          [0, 0, np.linalg.det(V.T @ U.T)]])
    r = V.T @ midMatrix @ U.T
    t = get_avg(parentList).T - r @ get_avg(childList).T
    return r, t

二、雷达外参标定、雷达与相机联合

2.1、通过雷达获取标定板
代码语言:javascript
复制
# 最小二乘法拟合平面 ax + by + cz - 1 = 0
def fit(M):
    np_ones = np.ones((len(M), 1))
    M_T = M.transpose()
    result = np.dot(np.dot(np.linalg.inv(np.dot(M_T, M)), M_T), np_ones)
    return result
代码语言:javascript
复制
def get_plane(self, count):
    pcd = o3d.io.read_point_cloud(self.path[-1])
    cloud_point = np.asarray(pcd.points)
    minX, maxX, minY, maxY, minZ, maxZ = self.fiter
    points, point_x, point_z = [], [], []
    for point in cloud_point:
        if minX < point[0] < maxX and minY < point[1] < maxY and minZ < point[2] < maxZ:
            points.append(list(point))
            point_x.append(point[0])
            point_z.append(point[2])
    min_z, max_z, avg_x = min(point_z), max(point_z), np.average(point_x)
    gap = 0.02
    sum_max, sum_min = 0, 0
    while sum_max < count or sum_min < count:
        if sum_max < count:
            sum_max = 0
            max_z -= gap
        if sum_min < count:
            sum_min = 0
            min_z += gap
        for data in point_z:
            if max_z - gap < data < max_z + gap:
                sum_max += 1
            if min_z - gap < data < min_z + gap:
                sum_min += 1
    result_cloud_point = []
    for path in self.path:
        pcd = o3d.io.read_point_cloud(path)
        cloud_point = np.asarray(pcd.points)
        for point in cloud_point:
            if minX < point[0] < maxX and minY < point[1] < maxY and min_z - gap < point[2] < max_z + gap:
                result_cloud_point.append(point)
    a, b, c = fit(np.array(result_cloud_point)).reshape(-1)
    for i in range(len(result_cloud_point) - 1, 0, -1):
        x, y, z = result_cloud_point[i]
        if np.abs(a * x + b * y + c * z - 1) / np.sqrt(a * a + b * b + c * c) > 0.03:
            del result_cloud_point[i]
    return result_cloud_point

在较为空旷的场地,很容易可以获取到标定板的大致位置

2.2、拟合圆求中心点
代码语言:javascript
复制
def circle_fit(self, point_list, A):
    M, L = [], []
    for i in range(len(point_list) - 1):
        x1, y1, z1 = point_list[i]
        x2, y2, z2 = point_list[i + 1]
        M.append([x2 - x1, y2 - y1, z2 - z1])
        l = (x2 * x2 + y2 * y2 + z2 * z2 - (x1 * x1 + y1 * y1 + z1 * z1)) / 2
        L.append(l)
    B = np.array(M)
    L2 = np.array(L).reshape(len(L), 1)
    B_T = B.transpose()
    A_T = A.transpose()
    left = np.vstack((np.hstack((np.dot(B_T, B), A)), np.hstack((A_T, np.array([0]).reshape(1, 1)))))
    right = np.vstack((np.dot(B_T, L2), np.array([1]).reshape(1, 1)))
    C = np.dot(np.linalg.inv(left), right)
    center_x, center_y, center_z = C[0][0], C[1][0], C[2][0]
    return center_x, center_y, center_z
2.3、相机与雷达联合标定

由于我们已知标定板型号,通过abcd计算出雷达坐标系下ABCD坐标(注意一一对应),通过svd分解求相机到雷达的旋转平移矩阵。

2.4、雷达外参

定义好世界坐标系(车体),已知a,b,c,d在世界坐标系坐标,易计算雷达外参。

三、实际操作注意事项

1、雷达安装位置不同,标定板摆放高度、距离也应不同,确定后就保持不变。

2、雷达型号不同,获取圆中心点难度不同。有时看不全、或者线束不够,可以采取相应的算法解决。

3、如果仅凭点对点计算旋转平移。角度会有一定误差。通过平面拟合与法向量可以迭代优化角度误差。

4、此方法标定结果,相机重投影误差<1像素,雷达旋转基本无误差,雷达平移误差基本是雷达自身误差。

四、参考链接

1、https://blog.csdn.net/QYJ_WORKHARDING/article/details/124867821

2、https://arxiv.org/pdf/1705.04085.pdf

3、https://zhuanlan.zhihu.com/p/140077137

4、https://zhuanlan.zhihu.com/p/108858766

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2023-11-22,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 一、相机外参标定
    • 1.1、apritag识别
      • 1.2、求相机坐标系下PA,PB,PC方向的单位向量
        • 1.3、求角APB,角BPC,角CPA
          • 1.4、求PA,PB,PC的长
            • 1.5、svd分解求解旋转平移矩阵
            • 二、雷达外参标定、雷达与相机联合
              • 2.1、通过雷达获取标定板
                • 2.2、拟合圆求中心点
                  • 2.3、相机与雷达联合标定
                    • 2.4、雷达外参
                    • 三、实际操作注意事项
                    • 四、参考链接
                    领券
                    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档