绕任一向量旋转矩阵计算思考与实现

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

欢迎关注更多精彩
关注我学习常用算法与数据结构一题多解降维打击。

问题提出

请添加图片描述

如图所示在空间中有一向量A问点O绕A方向逆时针旋转角度α的矩阵如何表示。

问题分析

问题化规

直接去构造一个矩阵是比较困难的。
我们知道绕XYZ三个方向的旋转矩阵是可以直接给出的。分别如下。
角度根据右手定则绕各轴逆时针旋转θ

绕 X 轴表示为 X ( θ ) = [ 1 0 0 0 c o s θ − s i n θ 0 s i n θ c o s θ ] 绕X轴表示为X(\theta)=\begin{bmatrix}1&0&0\\0&cos\theta&-sin\theta\\0&sin\theta&cos\theta\end{bmatrix} X轴表示为X(θ)= 1000cosθsinθ0sinθcosθ

绕 Y 轴表示为 Y ( θ ) = [ c o s θ 0 s i n θ 0 1 0 - s i n θ 0 c o s θ ] 绕Y轴表示为Y(\theta)=\begin{bmatrix}cos\theta&0&sin\theta\\0&1&0\\-sin\theta&0&cos\theta\end{bmatrix} Y轴表示为Y(θ)= cosθ0sinθ010sinθ0cosθ

绕 Z 轴表示为 Z ( θ ) = [ c o s θ − s i n θ 0 s i n θ c o s θ 0 0 0 1 ] 绕Z轴表示为Z(\theta)=\begin{bmatrix}cos\theta&-sin\theta&0\\sin\theta&cos\theta&0\\0&0&1\end{bmatrix} Z轴表示为Z(θ)= cosθsinθ0sinθcosθ0001

一个直观的想法就是先把向量A转到与X轴相同的方向。
也就是沿着A与X叉乘方向旋转β如图所示。

请添加图片描述

图中向量M分别与向量A向量X垂直可知向量M处于平面YOZ中。

设上述旋转为RM

那么O点最终结果可以表示如下

O ′ = R M − 1 ⋅ X ( α ) ⋅ R M ⋅ O O'=RM^{-1}\cdot X(\alpha) \cdot RM\cdot O O=RM1X(α)RMO

由于向量M并不与XYZ轴中任意一轴平行所以还是不好直接给出RM表达式。

但是向量M处于平面YOZ可以行将向量M旋转到与Y轴平行再按照上述同理操作。设旋转到Y轴矩阵为RY。

R M = R Y − 1 ⋅ Y ( − β ) ⋅ R Y RM=RY^{-1}\cdot Y(-\beta)\cdot RY RM=RY1Y(β)RY

= X ( − γ ) − 1 ⋅ Y ( − β ) ⋅ X ( − γ ) =X(- \gamma)^{-1}\cdot Y(-\beta)\cdot X(- \gamma) =X(γ)1Y(β)X(γ)

致此所有旋转都转化成和X轴Y轴相关的旋转。

求解过程

RY计算。

RY的作用是把M转到与Y轴相同位置。

M可以由A与X叉乘得到

设 M = ( 0 , M y , M z ) , 由于 M 处于 Y O Z 平面可知 M x = 0 设M=(0, My, Mz), 由于M处于YOZ平面可知Mx=0 M(0,My,Mz),由于M处于YOZ平面可知Mx=0

请添加图片描述
由上图可以知

R Y = X ( − γ ) = X ( γ ) T RY=X(-\gamma)=X(\gamma)^T RY=X(γ)=X(γ)T

= [ 1 0 0 0 c o s γ − s i n γ 0 s i n γ c o s γ ] T =\begin{bmatrix}1&0&0\\0&cos\gamma&-sin\gamma\\0&sin\gamma&cos\gamma\end{bmatrix}^T = 1000cosγsinγ0sinγcosγ T

= [ 1 0 0 0 c o s γ s i n γ 0 - s i n γ c o s γ ] =\begin{bmatrix}1&0&0\\0&cos\gamma&sin\gamma\\0&-sin\gamma&cos\gamma\end{bmatrix} = 1000cosγsinγ0sinγcosγ

= [ 1 0 0 0 M y M z 0 - M z M y ] =\begin{bmatrix}1&0&0\\0&My&Mz\\0&-Mz&My\end{bmatrix} = 1000MyMz0MzMy

R Y − 1 = R Y T RY^{-1}=RY^T RY1=RYT

有了RY后可以先将A乘上RY。这样A就会被旋转到平面ZOX上来。

A ′ = R Y ⋅ A = ( A x ′ , 0 , A z ′ ) A' = RY\cdot A = (Ax',0, Az') A=RYA=(Ax,0,Az)

同理

R X = Y ( − β ) = Y ( β ) T RX=Y(-\beta) = Y(\beta)^T RX=Y(β)=Y(β)T

= [ c o s β 0 − s i n β 0 1 0 s i n β 0 c o s β ] = \begin{bmatrix}cos\beta&0&-sin\beta\\0&1&0\\sin\beta&0&cos\beta\end{bmatrix} = cosβ0sinβ010sinβ0cosβ

= [ A x ′ 0 A z ′ 0 1 0 - A z ′ 0 A x ′ ] = \begin{bmatrix}Ax'&0&Az'\\0&1&0\\-Az'&0&Ax'\end{bmatrix} = Ax0Az010Az0Ax

旋转后的P’

P ′ = R Y − 1 ⋅ R X − 1 ⋅ X ( α ) ⋅ R X ⋅ R Y ⋅ P P'=RY^{-1}\cdot RX^{-1}\cdot X(\alpha)\cdot RX\cdot RY\cdot P P=RY1RX1X(α)RXRYP

代码实现


namespace acamcad {
    const double pi = acos(-1);
    using Point = Eigen::Vector3d;
    class RigidRTMatrix {
    private:
        Eigen::Matrix3d mat;
        Eigen::Vector3d trans;
        
    public:
        RigidRTMatrix(Point start, Point end, double theta) {
            cout << "generate RigidRTMatrix 2" << endl;
            Eigen::Vector3d v = end - start;
            cout << "v:" << v << endl;
            cout << "angle:" << theta << endl;
            assert(!v.isZero());
            // Point::Zero();
            v.normalize();
            Eigen::Vector3d X(1,0,0);
            Eigen::Vector3d m = v.cross(X);
            // todo m=0时特殊处理
            if (m.isZero()) {
                if (v.dot(X) > 0) m = { 0,1,0 }; // 直接等于Y轴
                else m = { 0,-1,0 }; // 等于Y轴的反轴
            }

            auto RY = GetRY(m);
            // 将v 旋转至ZOX 平面。
            auto vZOX = RY * v;
            auto RX = GetRX(vZOX);

            auto Xrotate = GetXRotate(theta);

            mat = RY.transpose() * RX.transpose() * Xrotate * RX * RY;
            cout << "mat create :" << mat << endl;
        }
        
        RigidRTMatrix() {

        }
        
        // 给定YOX平面上的单位M向量将其旋转到Y轴上。
        Eigen::Matrix3d GetRY(Eigen::Vector3d m) {
            assert(!m.isZero());
            m.normalize();
            Eigen::Matrix3d RY;
            RY.setIdentity();
            RY(1, 1) = m.y();
            RY(1, 2) = m.z();
            RY(2, 1) = -m.z();
            RY(2, 2) = m.y();
            return RY;
        }

        // 给定ZOX平面上的单位M向量将其旋转到X轴上。
        Eigen::Matrix3d GetRX(Eigen::Vector3d m) {
            assert(!m.isZero());
            m.normalize();
            Eigen::Matrix3d RX;
            RX.setIdentity();
            RX(0, 0) = m.x();
            RX(0, 2) = m.z();
            RX(2, 0) = -m.z();
            RX(2, 2) = m.x();
            return RX;
        }

        // 给定ZOX平面上的单位M向量将其旋转到X轴上。
        Eigen::Matrix3d GetXRotate(double theta) {
            double rad = theta / 180 * pi;
            Eigen::Matrix3d X;
            X.setIdentity();
            X(1, 1) = cos(rad);
            X(1, 2) = -sin(rad);
            X(2, 1) = sin(rad);
            X(2, 2) = cos(rad);
            return X;
        }

        double angleMod(double theta) {
            while (theta < -180)theta += 360;
            while (theta > 180)theta -= 360;
            return theta;
        }

        Point Trans(Point &a) {
            return mat * a;
        }

        friend static RigidRTMatrix operator*(RigidRTMatrix& a, RigidRTMatrix& b) {
            RigidRTMatrix multi;
            multi.mat = a.mat * b.mat;
            return multi;
        }
    };
}

数据测试

测试代码链接点击前往

测试代码链接点击前往

测试代码链接点击前往

效果展示


往外的三条线分别是X,Y,Z中间那么是向量111红点是0.5,0,0.5)
绿点是红点沿111逆时针转90度结果。


本人码农希望通过自己的分享让大家更容易学懂计算机知识。

欢迎添加我的公众号进群交流。

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6