点的旋转(4):四元数的乘法

  • 前言
  • 矢量积
  • 旋转
  • C++代码

前言

同样的,跟前面一眼四元数的乘法也代表了旋转,四元数的积有多种定义,这里我们只讲被用在旋转操作的上积 —— 矢量积

矢量积

对于两个四元数q,p:
q = w 1 + x 1 i + y 1 j + z 1 k q = w_1 + x_1i+y_1j+z_1k q=w1​+x1​i+y1​j+z1​k
p = w 2 + x 2 i + y 2 j + z 2 k p = w_2 + x_2i+y_2j+z_2k p=w2​+x2​i+y2​j+z2​k
我们来看q右乘p,按照分配律
q p = ( w 1 ∗ w 2 − x 1 ∗ x 2 − y 1 ∗ y 2 − z 1 ∗ z 2 ) + qp = (w_1*w_2-x_1*x_2-y_1*y_2-z_1*z_2) + qp=(w1​∗w2​−x1​∗x2​−y1​∗y2​−z1​∗z2​)+
( x 1 ∗ w 2 + w 1 ∗ x 2 + z 1 ∗ y 2 − z 1 ∗ y 2 ) i + (x_1*w_2+w_1*x_2+z_1*y_2-z_1*y_2) i+ (x1​∗w2​+w1​∗x2​+z1​∗y2​−z1​∗y2​)i+ ( y 1 ∗ w 2 + w 1 ∗ y 2 + z 1 ∗ x 2 − x 1 ∗ z 2 ) j + (y_1*w_2+w_1*y_2+z_1*x_2-x_1*z_2) j+ (y1​∗w2​+w1​∗y2​+z1​∗x2​−x1​∗z2​)j+ ( z 1 ∗ w 2 + w 1 ∗ z 2 + x 1 ∗ y 2 − x 2 ∗ y 1 ) k + (z_1*w_2+w_1*z_2+x_1*y_2-x_2*y_1) k+ (z1​∗w2​+w1​∗z2​+x1​∗y2​−x2​∗y1​)k+
按照同样算法算pq结果是等于-qp的
也就是
p q = − q p pq = -qp pq=−qp

旋转

重新审视qp
(1)q右乘p,我们将q作为一个旋转操作,作用于某个值p上
qp即为:p被q按照右手法则变换
(2)p左乘q,我们将p作为一个旋转操作,作用于某个值q上
qp即为:q被p按照左手法则变换

如何旋转一个三维空间的点呢?
现在,假设我们这里有一个旋转操作q,和一个三维坐标v
我们将v看作w = 0处的一个四元数p
p = ( 0 , v ) p = (0,v) p=(0,v)
记 t = q p t = qp t=qp
经过一次旋转后 w t w_t wt​ 不为0了,这在三维空间看起来已经变形了
为什么呢,因为一次旋转涉及i,j,k三个轴的变化,他们是联动的,
当你从i轴向j轴转动时,k轴向ij向量积的方向转动

你可能会问一个轴怎么转动向他所指的方向转动?
事实上这个轴是投影在三维空间的一个半径为无穷大的圆...在这里有一个3Blue1Brown的交互性视频,如果你无法理解我的概念可以自己去试着操作一下超球(Hypersphere)
https://eater.net/quaternions/video/quatmult

看,这是旋转之前的投影

现在我们绕k轴旋转
q 1 = 0.8 + 0.0 i + 0.0 j + 0.6 k q_1 = 0.8 + 0.0i + 0.0j + 0.6k q1​=0.8+0.0i+0.0j+0.6k
q 2 = 1 q_2 = 1 q2​=1

q 1 p ∗ 1 q_1p*1 q1​p∗1
q 1 q_1 q1​模长1,确保四维超球不会变形
我们看

可以看到确实旋转了,我们来看看k轴的情况


球体变形了,他膨胀了!

为什么?我们明明限制了 q 1 q_1 q1​的模长1
根据运算结果,w不为0时偏离了我们的三维空间,导致超球在三维的投影变形了

如何去修复他?
我们左乘一个 q 2 q_2 q2​,使得其按左手法则,向相反的方向旋转k轴,将超球拉回w=0处
当然,为了不变形超球, q 2 q_2 q2​模长 = 1,
我们做稍稍调整, q 2 = ( w , − q 1 . x , − q 1 . y , − q 1 . z ) q_2 = (w,-q_1.x,-q_1.y,-q_1.z) q2​=(w,−q1​.x,−q1​.y,−q1​.z)
并用 q 2 q_2 q2​左乘p,这样便可以抵消w的偏移

你可以同时拿出你的右手和左手,让食指从i轴的方向旋至j轴的方向,一个对应q1,另一个对应q2
两根拇指的方向相反,但是食指的转动的方向相同

也就是说:为了调整偏离w = 0的操作我们旋过了两倍 q 1 q_1 q1​的操作。
或者说,要旋转 ( α , β , γ ) (α,β,γ) (α,β,γ)欧拉角,我们应该使q1等于其一半
q 1 = e u l e r ( α 2 , β 2 , γ 2 ) q_1 = euler(\frac{α}{2},\frac{β}{2},\frac{γ}{2}) q1​=euler(2α​,2β​,2γ​)
之后 q 1 p q 2 q_1pq_2 q1​pq2​ 即可

至此,四元数的三维空间旋转应用已经结束,下面给出C++代码

C++代码

quaternion.h


// 四元数
// Ayww
// 2018年12月30日15:10:11#ifndef _QUATERNION_
#define _QUATERNION_
#define _USE_MATH_DEFINES
#include <cmath>
#include <memory>
#include <tuple>class quaternion {public:
#ifdef QUATERNION_DOUBLEusing _Myvt = double;
#elseusing _Myvt = float;
#endifusing vec3 = std::tuple<_Myvt, _Myvt, _Myvt>;_Myvt w;_Myvt x;_Myvt y;_Myvt z;public:explicit quaternion(_Myvt a = 0, _Myvt b = 0, _Myvt c = 0, _Myvt d = 0) :w(a), x(b), y(c), z(d) {}~quaternion(){}quaternion::quaternion(const quaternion &r) : w(r.w), x(r.x), y(r.y), z(r.z) {}quaternion::quaternion(quaternion &&r) : w(r.w), x(r.x), y(r.y), z(r.z) {}quaternion& operator=(quaternion const&);quaternion& operator=(quaternion &&);quaternion operator*(quaternion const &r);quaternion operator+(quaternion const &r);// 求模的平方_Myvt norm_square(void);// 求模auto norm(void)->decltype(sqrt(w));// 共轭quaternion conjugate(void);// 逆quaternion inv(void);// 加quaternion& add(quaternion const& qr);// 右乘quaternion& multi(quaternion const& qr);// 到欧拉角vec3 to_euler(void);// 欧拉角转换到四元数quaternion& from_euler(_Myvt, _Myvt, _Myvt);// 到三维向量vec3 to_vector3(void);// 三维向量扩充到四元数quaternion& from_vector3(_Myvt, _Myvt, _Myvt);
};quaternion operator*(quaternion::_Myvt const& l,quaternion const &r);
quaternion operator*(quaternion const &l, quaternion::_Myvt const& r);#endif // !_QUATERNION_

quaternion.cpp

#include "quaternion.h"quaternion operator*(quaternion::_Myvt const& l, quaternion const &r) {return quaternion(l*r.w, l*r.x, l*r.y, l*r.z);
}
quaternion operator*(quaternion const &r, quaternion::_Myvt const& l) {return quaternion(l*r.w, l*r.x, l*r.y, l*r.z);
}quaternion& quaternion::operator=(quaternion const& r) {w = r.w;x = r.x;y = r.y;z = r.z;return *this;
}
quaternion& quaternion::operator=(quaternion && r) {w = r.w;x = r.x;y = r.y;z = r.z;r.w = 0;r.x = 0;r.y = 0;r.z = 0;return *this;
}quaternion::_Myvt quaternion::norm_square() {return w*w + x*x + y*y + z*z;
}quaternion::vec3 quaternion::to_vector3() {return vec3(x, y, z);
}quaternion& quaternion::from_vector3(_Myvt v1, _Myvt v2, _Myvt v3) {w = 0;x = v1;y = v2;z = v3;return *this;
}// Z - Y - X Euler angles
// https://www.cnblogs.com/21207-iHome/p/6894128.html
std::tuple<quaternion::_Myvt, quaternion::_Myvt, quaternion::_Myvt> quaternion::to_euler() {_Myvt a, b, c;const _Myvt _eps = 0.0009765625f;const _Myvt _thres = 0.5f - _eps;_Myvt _test = w*y - x*z;if (_test < -_thres || _test > _thres) {// 奇异姿态,俯仰角为±90°int s = _test < .0 ? -1 : 1;c = -2 * s * atan2(x, w); // yawb = s * (3.1415926535 / 2.0); // pitcha = 0; // roll}else {a = atan2(2 * (y*z + w*x), w*w - x*x - y*y + z*z);b = asin(-2 * (x*z - w*y));c = atan2(2 * (x*y + w*z), w*w + x*x - y*y - z*z);}return std::make_tuple(a, b, c);
}quaternion& quaternion::from_euler(_Myvt r, _Myvt p, _Myvt y) {this->w = cos(r)*cos(p)*cos(y) + sin(r)*sin(p)*sin(y);this->x = sin(r)*cos(p)*cos(y) - cos(r)*sin(p)*sin(y);this->y = cos(r)*sin(p)*cos(y) + sin(r)*cos(p)*sin(y);this->z = cos(r)*cos(p)*sin(y) - sin(r)*sin(p)*cos(y);return *this;
}quaternion quaternion::conjugate() {return quaternion(w, -x, -y, -z);
}quaternion quaternion::inv(void) {auto a = 1.0 / (x*x + y*y + z*z + w*w);return quaternion(a*w, a*-x, a*-y, a*-z);
}auto quaternion::norm(void)->decltype(sqrt(w)) {return sqrt(x*x + y*y + z*z + w*w);
}quaternion quaternion::operator*(quaternion const &qr) {quaternion q;q.w = w*qr.w - x*qr.x - y*qr.y - z*qr.z;q.x = w*qr.x + x*qr.w + y*qr.z - z*qr.y;q.y = w*qr.y - x*qr.z + y*qr.w + z*qr.x;q.z = w*qr.z + x*qr.y - y*qr.x + z*qr.w;return q;
}
quaternion quaternion::operator+(quaternion const &r) {return quaternion(w+r.w,x+r.x, y + r.y, z + r.z);
}quaternion& quaternion::add(quaternion const& qr) {x += qr.x;y += qr.y;z += qr.z;w += qr.w;return *this;
}quaternion& quaternion::multi(quaternion const& qr) {quaternion q;q.w = w*qr.w - x*qr.x - y*qr.y - z*qr.z;q.x = w*qr.x + x*qr.w + y*qr.z - z*qr.y;q.y = w*qr.y - x*qr.z + y*qr.w + z*qr.x;q.z = w*qr.z + x*qr.y - y*qr.x + z*qr.w;this->x = q.x;this->y = q.y;this->z = q.z;this->w = q.w;return *this;
}

测试代码

#include <iostream>
using namespace std;
#include "quaternion.h"int main(int argc, char**argv) {quaternion q,v,invq;const float pi = 3.1415926535f;v.from_vector3(1, 1, 0);       // 定义旋转点为 (1,1,0)const float deg_z = pi / 4.f;   // 定义本次旋转为:绕z轴逆时针转动 45°q.from_euler(0, 0, deg_z*0.5f);   // 将绕z轴转动22.5的欧拉角,转换为四元数invq = q.inv();                 // 求出q的逆v = q*v*invq;                  // 先用q来旋转v,此时 w 轴偏离了0 ,在三维空间的投影变形,我们再用q^-1使其转回w = 0的位置auto t = v.to_vector3();cout << "旋转后坐标,x = " << get<0>(t) << ", y = " << get<1>(t) << ", z = " << get<2>(t) << endl;return 0;
}

结果:
x = 0 , y = 2 , z = 0 x = 0 , y =\sqrt{2},z = 0 x=0,y=2 ​,z=0

点的旋转(4):四元数的乘法相关推荐

  1. C语言实现四元数的乘法(三维矢量、四元数以及旋转矢量与四元数相乘源码)

    四元数的乘法 四元数 四元数的运算 源码 四元数 在将三维矢量代数推广至乘法和除法运算的研究中,爱尔兰数学家.物理学家哈密顿于1843年创建了四元数((quaternion)和四元数代数.四元数是指由 ...

  2. 四元数组旋转_四元数应用——顺序无关的旋转混合

    四元数系列: ----------------------------------------- 知乎首文.应@杨智为 的邀请来帮忙贡献一篇文章. 好多年不写文章了,已经不知道该怎么写了,哪里写的不好 ...

  3. 【自动驾驶】30.c++实现基于eigen实现欧拉角(RPY), 旋转矩阵, 旋转向量, 四元数之间的变换(附代码)

    矩阵的使用可参考系列博客:点击此处 原文链接:基于eigen实现欧拉角(RPY), 旋转矩阵, 旋转向量, 四元数之间的变换. 也可以参考另一篇博客:eigen 中四元数.欧拉角.旋转矩阵.旋转向量. ...

  4. 20230208 对偶四元数的乘法

    文章目录 前言 一.对偶四元数是什么? 二.对偶四元数乘法 总结 前言 对偶四元数也称为螺旋算子,能够有效描述位置和姿态的耦合关系. 一.对偶四元数是什么? 对偶四元数是同时描述位置和姿态的一种工具, ...

  5. GimbalLock万向节锁、 欧拉角坐标旋转、 四元数旋转

    也许你都知道四元数这么个东西,也许你还知道万向锁.但是对于弄懂它们还是不那么容易的--起码对于我就是如此了.今天是丢三落四日,我就自己来捡捡吧.--ZwqXin.com 事先声明,原理神马的,其实我也 ...

  6. 三维空间坐标的旋转算法详解_视觉slam | 三维空间刚体运动的五种表达:旋转矩阵 变化矩阵 欧拉角 旋转向量 四元数及互相转换...

    原po:高翔slam十四讲-刚体运动 1.旋转矩阵 考虑一次旋转 Before: 坐标系(e1,e2,e3), 向量(a1,a2,a3) After: 坐标系(e1',e2',e3'), 向量(a1' ...

  7. Unity旋转之四元数(开关车门,第一人称控制器)

    在Unity中,常用的旋转方式有三种,分别是transform.Rotate(参数),欧拉角旋转,和今天我们要聊的四元数.虽然四元数理解起来不如欧拉角那么直观,但是它却能很好的避免了万向锁问题,而且在 ...

  8. 用于描述三维矢量旋转的四元数法的一点理解

    描述三维矢量的空间旋转主要有三种方法:欧拉角法,轴角法以及四元数法,故首先对这个三种方法的特点进行描述与比较 1.欧拉角法:使用三个元素[α,β,γ],以及世界坐标系[Xw,Yw,Zw]来描述一个矢量 ...

  9. Matlab 点云旋转之四元数

    文章目录 一.简介 1.1基础知识 1.2四元数旋转 1.3矩阵形式 二.实现代码 三.实现效果 参考资料 一.简介 三维旋转中绕不开的一种表达方式就是四元数,在三维空间中欧拉角的一个很大的问题在于其 ...

最新文章

  1. Nat. Mach. Intell. | 华科同济医学院剑桥联手推出新冠预测模型!
  2. 如何知道刚刚插入数据库那条数据的id
  3. postman接口测试和压力测试
  4. 【second】Flatten Binary Tree to Linked List
  5. 【大学物理】磁场的高斯定理
  6. ORACLE 一些时间查询方式
  7. 【报告分享】2021大社交趋势观察报告.pdf(附下载链接)
  8. Android百度SDK定位
  9. SpringBoot以jar包部署需要注意的thymeleaf页面映射问题
  10. c++ 多字节 转换为 unicode
  11. JDK如何安装与配置环境变量
  12. 接口:基于FPGA的HDMI接口设计
  13. MEM/MBA英语基础(04) 句子结构 翻译划分练习
  14. echarts饼状图显示百分比
  15. V-REP 插件教程
  16. Environment Mapping
  17. 《私募股权基金投资基础知识》---第四章
  18. php756中医,百人诈骗团伙冒充老中医微信钓鱼九千余人被骗
  19. mongodb 副本集搭建
  20. (63)计数器设计(递增计数器)

热门文章

  1. egg-mongoose的 update only works with $ operators报错
  2. IOS AR技术开发
  3. 静寂的声韵里悄然于心底溅扩出岁月的气息
  4. Python:成绩分类
  5. Vue实现ECharts柱状图数据轮播(自动分页加载)
  6. ssm+Vue计算机毕业设计在线音乐网站(程序+LW文档)
  7. 联合国报告:全球经济环境正朝着错误方向前进
  8. html下拉列表框做日期,几种常用的控件(下拉框 可选框 起止日期 在HTML页面直接读取当前时间)...
  9. sqlite如何与mysql连接数据库连接_sqlite 数据库连接问题以及解决方法
  10. 2023数模美赛猜词题集锦