关键词:空间旋转、旋转轴

用途:相机位姿估计、无人机位姿估计、3D游戏、3D建模

文章类型:概念、公式总结(本文不带推导过程,若想了解公式是如何推出来的请搜索文献),C++函数展示

@Author:VShawn(singlex@foxmail.com)

@Date:2016-11-04

@Lab: CvLab202@CSU

写在前面的一些概念

右手系

关于这个概念,搞3D的人应该都懂,而像我这样做图像处理的可能就对这个知道的比较少了。右手系这个概念其实很简单,看图就懂了。在坐标系中,右手摆成下图的样子,当拇指指向X轴食指指向Y轴时,中指指向了Z轴,满足这个条件的坐标系就是右手系。本文所有概念都在右手系下进行讨论。

右手系

旋转90°到底是怎么转

当我要让一个点,绕Y轴转动了90°,并且用程序计算出了旋转结果,为了验证这个点是否旋转正确,我们需要知道这个90°是怎么转的。在网上搜索了挺多文章,都没有对这个东西进行明确的定义,那么这里给出我的总结。从原点(0,0,0)往Y轴方向看,此时视野中的坐标系降维到二维坐标系XOZ,那么让点绕O点顺时针转90°,即为正确的旋转结果。

[图待补]

问题一:XYZ空间内某点绕X、Y、Z轴旋转一次

这个问题比较简单,网上已经有较多总结:

设旋转前坐标为,旋转后坐标为

1.绕Z轴旋转γ角

首先给出向量表示:

\[\left[ x',y',z',1 \right]=\left[ x,y,z,1 \right]\left[ \begin{matrix} \cos \gamma & \sin \gamma & 0 & 0 \\ -\sin \gamma & \cos \gamma & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1 \\\end{matrix} \right]\]

然后是公式表示:

\[\begin{align} & x'=cos\gamma \cdot x-sin\gamma \cdot y \\ & y'=\sin \gamma \cdot x+co\gamma \cdot y \\ & z'=z \\ \end{align}\]

最后是代码表示

//将空间点绕Z轴旋转
//输入参数 x y为空间点原始x y坐标
//thetaz为空间点绕Z轴旋转多少度,角度制范围在-180到180
//outx outy为旋转后的结果坐标
void codeRotateByZ(double x, double y, double thetaz, double& outx, double& outy)
{
double x1 = x;//将变量拷贝一次,保证&x == &outx这种情况下也能计算正确
double y1 = y;
double rz = thetaz * CV_PI / 180;
outx = cos(rz) * x1 - sin(rz) * y1;
outy = sin(rz) * x1 + cos(rz) * y1; }

2.绕Y轴旋转β角

首先给出向量表示:

\[\left[ x',y',z',1 \right]=\left[ x,y,z,1 \right]\left[ \begin{matrix} \cos \beta & 0 & -\sin \beta & 0 \\ 0 & 1 & 0 & 0 \\ \sin \beta & 0 & \cos \beta & 0 \\ 0 & 0 & 0 & 1 \\\end{matrix} \right]\]

然后是公式表示:

\[\begin{align} & x'=cos\beta \cdot x+sin\beta \cdot z \\ & y'=y \\ & z'=-\sin \beta \cdot x+\cos \beta \cdot z \\ \end{align}\]

最后是代码表示

//将空间点绕Y轴旋转
//输入参数 x z为空间点原始x z坐标
//thetay为空间点绕Y轴旋转多少度,角度制范围在-180到180
//outx outz为旋转后的结果坐标
void codeRotateByY(double x, double z, double thetay, double& outx, double& outz)
{
double x1 = x;
double z1 = z;
double ry = thetay * CV_PI / 180;
outx = cos(ry) * x1 + sin(ry) * z1;
outz = cos(ry) * z1 - sin(ry) * x1;
}

  

3.绕X轴旋转α角

首先给出向量表示:

\[\left[ x',y',z',1 \right]=\left[ x,y,z,1 \right]\left[ \begin{matrix} 1 & 0 & 0 & 0 \\ 0 & \cos \alpha & \sin \alpha & 0 \\ 0 & -\sin \alpha & \cos \alpha & 0 \\ 0 & 0 & 0 & 1 \\\end{matrix} \right]\]

然后是公式表示:

\[\begin{align} & x'=x \\ & y'=\cos \alpha \cdot y-\sin \alpha \cdot z \\ & z'=\sin \alpha \cdot y+\sin \alpha \cdot z \\ \end{align}\]

最后是代码表示

//将空间点绕X轴旋转
//输入参数 y z为空间点原始y z坐标
//thetax为空间点绕X轴旋转多少度,角度制范围在-180到180
//outy outz为旋转后的结果坐标
void codeRotateByX(double y, double z, double thetax, double& outy, double& outz)
{
double y1 = y;//将变量拷贝一次,保证&y == &y这种情况下也能计算正确
double z1 = z;
double rx = thetax * CV_PI / 180;
outy = cos(rx) * y1 - sin(rx) * z1;
outz = cos(rx) * z1 + sin(rx) * y1;
}

  

问题二:空间点绕任意轴旋转

首先,需要定义"任意轴"的单位向量,例如X轴可以用向量来表示。

那么假设旋转轴的单位向量为,旋转前坐标为,旋转后坐标为,旋转角为,于是有:

\[\begin{align} & x'=(vx\cdot vx\cdot (1-cos\theta )+cos\theta )\cdot x+(vx\cdot vy\cdot (1-cos\theta )-vz\cdot sin\theta )\cdot y+(vx\cdot vz\cdot (1-cos\theta )+vy\cdot sin\theta )\cdot z \\ & y'=(vx\cdot vy\cdot (1-cos\theta )+vz\cdot sin\theta )\cdot x+(vy\cdot vy\cdot (1-cos\theta )+cos\theta )\cdot y+(vy\cdot vz\cdot (1-cos\theta )-vx\cdot sin\theta )\cdot z \\ & z'=(vx\cdot vz\cdot (1-\cos \theta )-vy\cdot \sin \theta )\cdot x+(vy\cdot vz\cdot (1-\cos \theta )+vx\cdot \sin \theta )\cdot y+(vz\cdot vz\cdot (1-\cos \theta )+\cos \theta )\cdot z \\ \end{align}\]

计算时照着公式代入即可。

最后给出代码实现:

//定义返回结构体
struct Point3f
{
Point3f(double _x, double _y, double _z)
{
x = _x;
y = _y;
z = _z;
}
double x;
double y;
double z;
}; //点绕任意向量旋转,右手系
//输入参数old_x,old_y,old_z为旋转前空间点的坐标
//vx,vy,vz为旋转轴向量
//theta为旋转角度角度制,范围在-180到180
//返回值为旋转后坐标点
Point3f RotateByVector(double old_x, double old_y, double old_z, double vx, double vy, double vz, double theta)
{
double r = theta * CV_PI / 180;
double c = cos(r);
double s = sin(r);
double new_x = (vx*vx*(1 - c) + c) * old_x + (vx*vy*(1 - c) - vz*s) * old_y + (vx*vz*(1 - c) + vy*s) * old_z;
double new_y = (vy*vx*(1 - c) + vz*s) * old_x + (vy*vy*(1 - c) + c) * old_y + (vy*vz*(1 - c) - vx*s) * old_z;
double new_z = (vx*vz*(1 - c) - vy*s) * old_x + (vy*vz*(1 - c) + vx*s) * old_y + (vz*vz*(1 - c) + c) * old_z;
return Point3f(new_x, new_y, new_z);
}

 

问题三:空间点绕xyz轴连续旋转

前面的问题比较基础,到这个问题就需要一点空间想象力了。

首先我假设一个点绕x、y、z轴旋转90°,最终它会落在哪里?这个答案不是唯一的,因为旋转的顺序将会影响到最终的结果。

以点(1,2,3)为例

A 我让它首先绕x轴转90°,再绕y轴转90°,再绕z轴转90°。

double x = 1, y = 2, z = 3;
codeRotateByX(y, z, 90, y, z);
codeRotateByY(x, z, -90, x, z);
codeRotateByZ(x, y, -90, x, y);
cout << endl << " (1,2,3) -> (" << x << ',' << y << ',' << z << ")" << endl << endl;

旋转结果是:

B 这一次我让它首先绕z轴转90°,再绕y轴转90°,最后绕z轴转90°。

double x = 1, y = 2, z = 3;
codeRotateByZ(x, y, -90, x, y);
codeRotateByY(x, z, -90, x, z);
codeRotateByX(y, z, 90, y, z);
cout << endl << " (1,2,3) -> (" << x << ',' << y << ',' << z << ")" << endl << endl;

这次的结果是:

显然,不同的旋转顺序导致了结果的不同,因此在处理空间内绕轴旋转的问题时,我们需要严格定义每次旋转的顺序,否则会导致错误的答案。

空间点绕轴旋转公式&程序(C++)的更多相关文章

  1. threejs绕轴转,粒子系统,控制器操作等(二)

    前言:threejs系列的第二篇文章,也是一边学习一边总结: 1,一个物体绕着另一个物体转动 上一篇文中主要是物体自转,为了描述一个一个物体绕另一个物体转,这里我描述了一个月球绕地球公转,并且自转的场 ...

  2. Canvas实现文字粒子化,并且绕轴旋转(初号机)

    写下来发现,程序在细节上处理的很差,比如旋转的时候,在终点处有明显的撞墙感觉,以及小部分粒子存在精度差异,导致撞击后不与整体平衡. 注释全在代码中了,就不多说了,另外感觉写的旋转的规则有点怪,后续再调 ...

  3. Canvas实现文字粒子化,并且绕轴旋转(完善)

    1. 之前有放过一个初始版本,但是因为在旋转的时候,有比较大的瑕疵,造成每个点运动到端点后,出现类似撞击的感觉. 2. 所以本文对旋转作了些调整,运用类似水平方向的圆周运动 a. HTML代码,定义c ...

  4. 子坐标系C在父坐标系W中的旋转问题

    关键词:空间旋转.旋转轴.刚体旋转 用途:相机位姿估计.无人机位姿估计 文章类型:概念.公式总结(本文不带推倒过程,若想了解公式是如何推出来的请自习搜索文献),C++函数展示 @Author:VSha ...

  5. C++小项目:directx11图形程序(七):modelclass

    模型类是世界空间中的表示物体的类,那么他的所做的事就是加载模型,移动模型,渲染模型 modelclass.h #pragma once #include <d3d11.h> #includ ...

  6. 微信小程序--图片相关问题合辑

    图片上传相关文章 微信小程序多张图片上传功能 微信小程序开发(二)图片上传 微信小程序上传一或多张图片 微信小程序实现选择图片九宫格带预览 ETL:微信小程序之图片上传 微信小程序wx.preview ...

  7. 基于西门子S7-1500的大型焊接机全套程序,使用博图V14打开(带全部注释)

    程序说明:本套程序是在从事自动化行业时候的做的项目的程序,经过在设备上运行测试,其中包含20多个轴的伺服控制以及模拟量,数字量IO的控制,包括扫描枪的读取,属于大型程序,总步数有好几万步. 本程序注释 ...

  8. 相机位姿估计1_1:OpenCV:solvePnP二次封装与性能测试

    关键词:OpenCV::solvePnP 文章类型:方法封装.测试 @Author:VShawn(singlex@foxmail.com) @Date:2016-11-27 @Lab: CvLab20 ...

  9. 【Ray Tracing The Next Week 超详解】 光线追踪2-7 任意长方体 && 场景案例

    上一篇比较简单,很久才发是因为做了一些好玩的场景,后来发现这一章是专门写场景例子的,所以就安排到了这一篇 Preface 这一篇要介绍的内容有: 1. 自己做的光照例子 2. Cornell box画 ...

随机推荐

  1. Y-TDC 的一些函数

    typedef void (*func_ptr)(void); func_ptr usm_rom_set_tx2_drive_strength_hs; 定义一个函数指针类型.比如你有三个函数:void ...

  2. [转]IntelliJ IDEA 使用心得与常用快捷键

    IntelliJ IDEA 使用心得与常用快捷键 那种酸爽,根本说不出来—————————————————————————— by: Jimi没有BondJimi是谁? 就是洒家啊! 刚开始学习写Ja ...

  3. PAT——乙级真题1001代码

  4. Spring的Ioc和AOP扩展

    多种方式实现依赖注入: 这里唯一需要说明的是如果要使用P命名空间实现属性注入,需要添加命名空间的声明: 如我的xml里红色字体: <?xml version="1.0" en ...

  5. 多线程中共享变量——CCF总决赛试题

    题目要求 数据格式 Q 系统的输入为纯文本格式的文件,由若干行组成,每一行由城市编号.年龄.收入组成,相邻两项之间用一个空格分隔.以下是输入的一个片段: 1001 20 12000 1001 50 2 ...

  6. Lunix 命令

    awk '{a[$1]+=1;if(a[$1]==1){print $0}}' awk -F ','  '{print $1, $6}'  IS.csv | sort -k1n -k2n | awk ...

  7. JdbcUtils.java

    package com.jdbc.dbutils; import java.lang.reflect.Field; import java.sql.Connection; import java.sq ...

  8. ExtJS6 TreePanel树节点合上展开显示不同图标

    TreePanel的节点如包含子节点,可在展开/合上时显示不同的图标,增强客户端效果,提高用户体验.非常简单,使用TreePanel的两个事件:beforeitemexpand和beforeitemc ...

  9. 浮动【电梯】或【回到顶部】小插件:iElevator.js

    iElevator.js 是一个jquery小插件,使用简单,兼容IE6,支持UMD和3种配置方式,比锚点更灵活. Default Options _defaults = { floors: null ...

  10. app加固

    为什么要加固APP? 答:因为黑客通过反编译APK得到源码后,会在应用中插入代码,获取利益,比如添加广告,盗取用户账号.密码,后台定制活动等.   反编译的方法? 反编译是指apk文件通过反编译工具( ...