3D位姿变换案例

名称及缩写:

基地:Base (B)

侦察机/四轴飞行器:Quadcopter (Q)

火炮坦克:CannonTank(C)

敌军目标:Enemy(E)

案例一:侦察机汇报基地

image-20200906220522076

题目:

如上图,已知

  • 一个侦察机相对于总部的位置 P_1 = {^B}P_Q = [1.5, 2.8, 3.2]

  • 侦察机其姿态 RPY = [15, 30, 10] 单位为角度

求其位姿 T_1 = {^B_Q}T

  • 若已知目标相对于侦察机的位置为 P_2 = {^Q}P_E = [-0.2, 3.4, 2.1]

求敌军目标相对于基地的坐标位置 {^B}P_E

求解:

根据坐标系位姿的定义,我们可以将 RPY 作为欧拉角的 z, y, x 构建旋转矩阵,然后和位置共同构建 4x4 变换矩阵。

随后可以使用如下方式得到 {^B}P_E :

{^B}P_E = {^B_Q}T \cdot {^Q}P_E

实现:

  • 准备依赖: transformation.py
"""
坐标转换工具
"""
import numpy as np
from math import cos, sin, atan2

np.set_printoptions(precision=3, suppress=True, formatter={'float': "{:.3f}".format})


def euler2matrix(theta, format="degree"):
    """
    将欧拉角转成旋转矩阵
    :param theta: 按照 x, y, z顺序的旋转角度
    :param format: 指定格式,默认为角度,否则为弧度
    :return: 一次按照动轴ZYX运动的欧拉角旋转矩阵
            等同于按照定轴XYZ的RPY运动
    """

    if format == "degree":
        theta = np.deg2rad(theta)

    q_x, q_y, q_z = theta

    R_x = np.array([[1, 0, 0],
                    [0, cos(q_x), -sin(q_x)],
                    [0, sin(q_x), cos(q_x)]])

    R_y = np.array([[cos(q_y), 0, sin(q_y)],
                    [0, 1, 0],
                    [-sin(q_y), 0, cos(q_y)]])

    R_z = np.array([[cos(q_z), -sin(q_z), 0],
                    [sin(q_z), cos(q_z), 0],
                    [0, 0, 1]])

    return R_z @ R_y @ R_x


def is_rotation_matrix(R):
    # 求R的转置矩阵 (也是逆矩阵)
    Rt = np.transpose(R)
    # 矩阵点乘自己的逆 = 单位矩阵
    should_be_identity = np.dot(R, Rt)
    I = np.identity(3, dtype=R.dtype)
    n = np.linalg.norm(I - should_be_identity)
    return n < 1e-6


def matrix2euler(R):
    """
    旋转矩阵转欧拉角
    :param R: 旋转矩阵,必须是正交矩阵(其逆等于其转置)
    :return: 欧拉角 x, y, z
    """
    assert (is_rotation_matrix(R))

    sy = np.sqrt(R[0, 0] * R[0, 0] + R[1, 0] * R[1, 0])
    # 判断是否是奇异矩阵
    singular = sy < 1e-6

    print("singular: ", singular)

    if not singular:
        z = atan2(R[1, 0], R[0, 0])
        y = atan2(-R[2, 0], sy)
        x = atan2(R[2, 1], R[2, 2])
    else:
        z = 0
        y = atan2(-R[2, 0], sy)
        x = -atan2(R[1, 2], R[1, 1])

    return np.array([x, y, z])


def merge_pose(R, t):
    mat = np.eye(4, dtype=R.dtype)
    mat[:3, :3] = R
    mat[:3, 3] = t
    return mat


def split_pose(T):
    return T[:3, :3], T[:3, 3]


if __name__ == '__main__':
    matrix = euler2matrix([-120, 20, 90])
    print(matrix)

    euler = matrix2euler(matrix)
    print(np.rad2deg(euler))

    pose = merge_pose(matrix, np.array([1, 2, 3]))
    print(pose)

    print(split_pose(pose))
  • 编写实现 Exercise-1.py
import numpy as np

from transformation import euler2matrix, merge_pose

# 侦察机的位置姿态
p1 = np.array([1.5, 2.8, 3.2])
rpy = np.array([15, 30, 10])

# 目标在侦察机坐标系的位置
p2 = np.array([-0.2, 3.4, 2.1])

if __name__ == '__main__':
    R1 = euler2matrix(rpy)

    T1 = merge_pose(R1, p1)
    print(T1)

    p2 = np.append(p2, 1.)

    result = T1 @ p2

    print(result[:3])

案例二:基地通知侦察机

image-20200906220554153

题目:

如图,已知

  • 侦察机相对于总部的位置 P_1 = {^B}P_Q = [1.5, 2.8, 3.2]
  • 侦察机姿态 RPY = [15, 30, 10] 单位为角度,
  • 目标相对于基地B的位置为 P_2 = {^B}P_E = [2.286, 5.721, 5.819]

求敌军目标E相对于侦察机Q的坐标位置 {^Q}P_E

求解:

利用P1和RPY构建无人机位姿 {^B_Q}T,已知如下等式: {^B}P_E = {^B_Q}T \cdot {^Q}P_E ,使左右两边同时左乘 {^B_Q}T^{-1} = {^Q_B}T 可得:

{^B_Q}T^{-1} \cdot {^B}P_E = {^Q}P_E

实现: Exercise-2.py

import numpy as np

from transformation import euler2matrix, merge_pose

# 侦察机的位置姿态
p1 = np.array([1.5, 2.8, 3.2])
rpy = np.array([15, 30, 10])

# 目标在基地坐标系的位置
p2 = np.array([2.286, 5.721, 5.819])

if __name__ == '__main__':
    R1 = euler2matrix(rpy)

    T1 = merge_pose(R1, p1)
    print(T1)

    p2 = np.append(p2, 1.)

    result = np.linalg.inv(T1) @ p2

    print(result[:3])

案例三:侦察机通知坦克

image-20200906220625230

如图,已知:

  • 侦察机Q相对于基地B的位姿为: T_1 = {^B_Q}T 其中(p1=[1.5, 2.8, 3.2], rpy1=[15, 30, 10])
  • 敌军E相对于侦察机Q的位置为: P_2 = {^Q}P_E 其中(p2=[-0.2, 3.4, 2.1])
  • 坦克C相对于基地B的位姿为: T_3 = {^B_C}T 其中(p3=[2.0, -4.0, 0.5], rpy3=[0, 15, 45])

求敌军目标E在坦克C当前状态下的坐标位置 {^C}P_E .