从某一点出发沿任意一方向旋转矩阵计算思考与实现

news2024/11/15 9:07:09

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

上期讲到
绕任一向量旋转矩阵计算思考与实现
点击前往
点击前往

问题提出

之前讲到绕任一向量旋转矩阵实现,原来的向量都是从原点出发,现在把出发点改变成任意一点。

请添加图片描述

除了需要计算旋转矩阵,还可以支持2个旋转矩阵的乘法操作。就是2个旋转矩阵依次作用以后的一个综合结果。

问题分析

从某点出发沿某一方向旋转实现

从原点出发的旋转是已经实现的,我的思路是把坐标系统一平移一个(-x0, -y0,-z0),然后作用旋转矩阵,最后再平移回去。

用公式表示, RT1表示旋转矩阵,O1表示出发点。

P ′ = R T 1 ⋅ ( P − O 1 ) + O 1 ( 1 ) P' = RT1\cdot(P-O1)+O1 (1) P=RT1(PO1)+O11

既我们只要用一个旋转矩阵RT加一个出发点O就可以表示这个旋转动作。那么如果是2个旋转动作叠加怎么去用一个旋转动作来表示呢。

旋转相乘

有一种方法是用奇次矩阵来表示一个平移和旋转的组合,然后矩阵可以相乘,但是这种方式需要用到4*4的矩阵效率上没有3*3的矩阵来得高。

我这边通过公式推导得到一种新的表示方法。

首先对式子(1)进行展开再合并同类项得到

P ′ = R T 1 ⋅ P + ( I − R T 1 ) ⋅ O 1 P'=RT1\cdot P+(I-RT1)\cdot O1 P=RT1P+(IRT1)O1

( I − R T 1 ) ⋅ O 1 是一个常量, (I-RT1)\cdot O1 是一个常量, (IRT1)O1是一个常量,

即只要一个表示形如 R T ⋅ P + Q ( Q 是一个常量 ) 的形式, 即只要一个表示形如 RT\cdot P+Q(Q是一个常量)的形式, 即只要一个表示形如RTP+Q(Q是一个常量)的形式,

就可用一个矩阵加一个出发点的形式来表示。 就可用一个矩阵加一个出发点的形式来表示。 就可用一个矩阵加一个出发点的形式来表示。

出发点 O = ( I − R T ) − 1 Q 出发点O=(I-RT)^{-1}Q 出发点O=(IRT)1Q

现在有第二个旋转RT2,O2作用于P’ 得到

P ′ ′ = R T 2 ⋅ P ′ + ( I − R T 2 ) ⋅ O 2 P''=RT2\cdot P' + (I-RT2)\cdot O2 P′′=RT2P+(IRT2)O2

= R T 2 ⋅ R T 1 ⋅ P + R T 2 ⋅ ( I − R T 1 ) ⋅ O 1 + ( I − R T 2 ) ⋅ O 2 =RT2\cdot RT1 \cdot P + RT2\cdot (I-RT1)\cdot O1 + (I-RT2)\cdot O2 =RT2RT1P+RT2(IRT1)O1+(IRT2)O2

上式中 , R T = R T 2 ⋅ R T 1 上式中, RT = RT2\cdot RT1 上式中,RT=RT2RT1

O = ( I − R T ) − 1 ⋅ [ R T 2 ⋅ ( I − R T 1 ) ⋅ O 1 + ( I − R T 2 ) ⋅ O 2 ] O = (I-RT)^{-1}\cdot [RT2\cdot (I-RT1)\cdot O1 + (I-RT2)\cdot O2] O=(IRT)1[RT2(IRT1)O1+(IRT2)O2]

这样,我们就得了两个刚体变换的综合变换。

但是上面的方法有一个问题,当(I-RT)不可逆时,那么该公式失效。
我的理解是,当(I-RT)不可逆时,O并不是没有解,而是有很多个解。

那么我们要另选他法。

再观察(1)式,其实我们并不需要求出O,只要求出Q就可以了。

R T = R T 2 ⋅ R T 1 , Q = R T 2 ⋅ Q 1 + Q 2 RT = RT2\cdot RT1, Q=RT2\cdot Q_1 + Q_2 RT=RT2RT1,Q=RT2Q1+Q2

这个方法与奇次矩阵相比,计算量小很多。

代码实现

  • 代码链接点击前往
  • 代码链接点击前往
  • 代码链接点击前往

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;

            trans = (Eigen::Matrix3d::Identity() - mat) * start; // 存储总体位移
        }
        
        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 + trans;
        }

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

代码测试


可以看出一个点依次作用与用一次综合矩阵的效果是一样的。


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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/175771.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Ajax面试题目

更多Ajax面试题目可以查看专栏内容 1.AJAX应用和传统Web应用有什么不同&#xff1f; 传统的web前端与后端的交互中&#xff0c;浏览器直接访问Tomcat的Servlet来获取数据。Servlet通过转发把数据发送给浏览器。当我们使用AJAX之后&#xff0c;浏览器是先把请求发送到XMLHttpR…

Swift之struct二进制大小分析

随着Swift的日渐成熟和给开发过程带来的便利性及安全性&#xff0c;京喜App中的原生业务模块和基础模块使用Swift开发占比逐渐增高。本次讨论的是struct对比Class的一些优劣势&#xff0c;重点分析对包体积带来的影响及规避措施。 一、基础知识 1、类型对比 引用类型&#xff…

独立看门狗与窗口看门狗

定义 看门狗的本质是一个定时器&#xff0c;在启动后&#xff0c;需要在一定时间内再给它一个信号&#xff0c;俗称“喂狗”&#xff0c;如果没有按时“喂狗”&#xff0c;说明MCU可能处于非正常状态&#xff0c;这时看门狗就向MCU发送个复位信号&#xff0c;使整个系统重启&a…

51单片机数码管显示

文章目录前言一、数码管简介二、数码管原理图三、数码管显示原理四、静态数码管代表编写五、动态数码管总结前言 这篇文章将介绍数码管的显示其中包含了动态数码管和静态数码管两种。 一、数码管简介 数码管其实就是由多个发光二极管封装在一起组成“8”字型的器件当分别点亮…

【数据结构】超详细——堆的实现

一、堆的概念及性质 1.1 什么是堆&#xff1f; 堆是一种完全二叉树&#xff08;具体在下一章讲述&#xff09;&#xff0c;若二叉树的深度h&#xff0c;除了第h层外其余各层节点数满了&#xff0c;只有第h层缺额且该层结点靠左&#xff1b;任何一个数组可以看作完全二叉树&…

【14】C语言_函数简介

目录 1、C语言中函数的分类: 2、库函数 3、自定义函数 1、C语言中函数的分类: 1.库函数 2.自定义函数 2、库函数 为什么会有库函数? 1.我们知道在我们学习C语言编程的时候&#xff0c;总是在一个代码编写完成之后迫不及待的想知道结果&#xff0c;想把这个结果打印到我们的屏…

ESP32设备驱动-LX1972可见光传感器驱动

LX1972可见光传感器驱动 1、LX1972介绍 LX1972 是一款低成本硅光传感器,其光谱响应非常接近人眼。专利电路在 520nm 处产生峰值光谱响应,IR 响应小于峰值响应的 5%,高于 900nm。 光电传感器是一个 PIN 二极管阵列,具有线性、准确和非常可重复的电流传递函数。 芯片上的…

扫盲-从零开始搭建阿里云流媒体服务器/音视频编解码/

1.基础概念 2.简单模式-HTTP文件服务器 存储采用NAS&#xff0c;服务器配置 采用FASTDFS (192条消息) Linux新手入门系列&#xff1a;FastDFS单机部署一键安装脚本_IT小胖豆的博客-CSDN博客 有几个坑&#xff1a; 常用命令&#xff1a; tail -20f /usr/local/nginx/logs/er…

3小时精通opencv(三)图片裁剪与形状绘制

3小时精通opencv(三)图片裁剪与形状绘制 参考视频资源:3h精通Opencv-Python 文章目录3小时精通opencv(三)图片裁剪与形状绘制图片裁剪绘制形状绘制直线绘制矩形绘制圆形绘制文字整体代码图片裁剪 图片裁剪不需要使用opencv中特有的函数, 对于opencv中读取到的图像, 直接当做矩…

15. python数据类型转换

1. 隐式类型转换 - 自动完成 在隐式类型转换中&#xff0c;Python 会自动将一种数据类型转换为另一种数据类型&#xff0c;不需要我们去干预。 (1) 以下实例中&#xff0c;我们对两种不同类型的数据进行运算&#xff0c;较低数据类型&#xff08;整数&#xff09;就会转换为较…

java 探花交友项目day4 MongoDB

数据库表 接口定义 其他都比较简单 我们讲黑名单查询页面的设计 DubboService public class BlackListApiImpl extends ServiceImpl<BlackListMapper,BlackList> implements BlackListApi{Autowiredprivate BlackListMapper blackListMapper;Autowiredprivate UserInf…

Allegro如何输出坐标文件操作指导

Allegro如何输出坐标文件操作指导 PCB在SMT的时候会需要用坐标文件,Allegro支持输出坐标文件,如下图 具体操作如下 选择Tools选择report出现repor

LINUX学习之正则表达式(十二)

普通正则 元字符 元字符匹配描述.匹配除了换行符以外的任意单个字符*前导字符出现0次或连续多次.*任意长度字符^行首(以…开头)$行尾(以…结尾)^$空行[]匹配括号里任意单个字符或一组单个字符[^]匹配不包含括号里任一单个字符或一组单个字符^[]匹配以括号里任意单个字符或一组…

Python技能树-推导式

Python 列表推导式(1) Python 独步天下的推导式表达式&#xff0c;使用列表推导式过滤出偶数列表 # -*- coding: UTF-8 -*- if __name__ __main__:list [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]print()print("# 使用列表推导式过滤出偶数")# TODO(you): 请在此实现过滤代…

Allegro如何添加平衡铜操作指导

Allegro如何添加平衡铜操作指导 PCB在加工的时候,工厂会添加平衡铜,Allegro支持自动加上平衡铜,如下图 具体操作如下 选择Manufacture点击Thieving

比较器: Comparable 与 Comparator 区别

比较器&#xff1a; Comparable 与 Comparator 区别 每博一文案 师父说: 人不能精得过火&#xff0c;太精明的人往往让人生厌&#xff0c;人也别傻的可怜&#xff0c;一腔热血付出却白忙一场。 太精明的人&#xff0c;凡事都想要争个明明白白&#xff0c;每一分钱都要和人计较…

macOS Ventura 13.1 系统问题:掉电快 充电慢

今年一月份升级了 MBA 的系统&#xff0c;之后的笔记本&#xff1a; 使用过程&#xff1a;电量不禁用&#xff0c;掉电很快。 充电过程&#xff1a;很慢。而且存在一定几率&#xff1a;电量充到某个值&#xff08;如30%&#xff09;之后不管再充多久还是这个电量值。 系统信息…

HDFS高可用单NameNode从standby恢复为active(二)

1、背景 有一个hdfs高可用集群&#xff0c;因为某些操作&#xff0c;导致其中一个namenode的信息全部丢失了。最后只剩下一个完整的namenode信息和datanode信息。于是在在启动hdfs后发现独有的namenode始终处于standby状态。即使通过hdfs haadmin -transitionToActive命令也不能…

java面向接口编程2023027

那就再进一步&#xff1a;面向接口编程 面向接口编程前面已经提到&#xff0c;接口体现的是一种规范和实现分离的设计哲学&#xff0c;充分利用接口可以极好地降低程序各模块之间的耦合&#xff0c;从而提高系统的可扩展性和可维护性。 基于这种原则&#xff0c;很多软件架构设…

Leetcode——第 329 场周赛

题目链接&#xff1a; 交替数字和 根据第 K 场考试的分数排序 执行逐位运算使字符串相等 拆分数组的最小代价 题目描述 交替数字和 给你一个正整数 n 。n中的每一位数字都会按下述规则分配一个符号&#xff1a; 最高有效位 上的数字分配到 正 号。 剩余每位上数字的符号都与…