右手坐标系,空间点绕轴旋转公式&程序(Python和C++程序)

简介: 右手坐标系,空间点绕轴旋转公式&程序(Python和C++程序)

1 右手坐标系

右手坐标系如下图:

  • X拇指指向X轴
  • Y食指指向Y轴
  • Z中指指向Z轴

image.png

可能很多人还是记不住,我个人总结两个小技巧,很容易牢记右手坐标系

  • X、Y、Z的顺序刚好也是对应手指大拇指、食指、中指也是顺序排列的
  • 做一个右手坐标系的姿势,然后让食指(Y轴)指向别人的姿势,很容易就可以把右手坐标系画出来

如下,是我画的右手坐标系:

image.png

1.1 旋转90度是什么样的

例如X轴顺时针旋转90度,为了验证这个旋转结果是否正确,我们需要弄清楚这个90度是怎么旋转的:

  • 可以从原点(0,0,0)向X轴看,此时就可以把三维坐标系降维到二维坐标系YOZ
  • 绕X轴旋转的过程中,自然X轴的坐标是不变的,只有Y和Z是变化的。

例如:(2, 0, 3)绕X轴顺时针旋转90度,旋转后的坐标为:(2, 3, 0)

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

首先假设:

  • 旋转前坐标为:$(x, y, z)$
  • 旋转后坐标为:$(x^{\prime}, y^{\prime}, z^{\prime})$

下面用一些内容的简单说明:

1、角度弧度的相互转化(关于角度和弧度的更多介绍参考

  • 角度弧度: $弧度 = 角度 \times \frac{\pi}{180}$
  • 弧度角度:$角度 = 弧度 \times \frac{180}{\pi}$

2、python的math模块和numpy模块计算正弦sin和余弦cos输入的参数值都是弧度制

如下,计算sin90度

>>> import numpy as np
>>> import math
>>> np.sin(90*(np.pi/180))
1.0
>>> math.sin(90*(math.pi/180))
1.0
>>>

2.1 XYZ空间内某点绕Z轴旋转γ角

1、绕Z轴旋转:Z轴的坐标是不变化的

$$ \left[x^{\prime}, y^{\prime}, z^{\prime}, 1\right]=[x, y, z, 1]\left[\begin{array}{cccc} \cos \gamma & \sin \gamma & 0 & 0 \\ -\sin \gamma & \cos \gamma & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1 \end{array}\right] $$

然后公式可以表示为:

$$ \begin{array}{l} x^{\prime}=\cos \gamma \cdot x-\sin \gamma \cdot y \\ y^{\prime}=\sin \gamma \cdot x+\cos \gamma \cdot y \\ z^{\prime}=z \end{array} $$

2、python实现XYZ空间内某点绕Z轴旋转γ角

# 1、绕Z周旋转gamma角
def rotate_Z(x, y, z,  gamma):
    gamma = gamma * (np.pi / 180)
    x_r = np.cos(gamma)*x - np.sin(gamma)*y
    y_r = np.sin(gamma)*x + np.cos(gamma)*y
    z_r = z
    print(f"{(x, y, z)} rotate {gamma*(180/np.pi)} degrees around the Z-axis,result {(x_r, y_r, z_r)}")
    return x_r, y_r, z_r

3、C++实现XYZ空间内某点绕Z轴旋转γ角


//将空间点绕Z轴旋转
//输入参数 x y为空间点原始x y坐标
//thetaz为空间点绕Z轴旋转多少度,角度制范围在-180180
//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.2 XYZ空间内某点绕Y轴旋转β角

1、绕Z轴旋转:Z轴的坐标是不变化的

$$ \left[x^{\prime}, y^{\prime}, z^{\prime}, 1\right]=[x, y, z, 1]\left[\begin{array}{cccc} \cos \beta & 0 & -\sin \beta & 0 \\ 0 & 1 & 0 & 0 \\ \sin \beta & 0 & \cos \beta & 0 \\ 0 & 0 & 0 & 1 \end{array}\right] $$

然后公式可以表示为:

$$ \begin{array}{l} x^{\prime}=\cos \beta \cdot x+\sin \beta \cdot z \\ y^{\prime}=y \\ z^{\prime}=-\sin \beta \cdot x+\cos \beta \cdot z \end{array} $$

2、python实现XYZ空间内某点绕Y轴旋转β角

# 2、绕Y轴旋转beta角
def rotate_Y(x, y, z, beta):
    beta = beta * (np.pi / 180)
    x_r = np.cos(beta)*x + np.sin(beta)*z
    y_r = y
    z_r = -np.sin(beta)*x + np.cos(beta)*z
    print(f"{(x, y, z)} rotate {beta*(180/np.pi)} degrees around the Y-axis,result {(x_r, y_r, z_r)}")
    return x_r, y_r, z_r

3、C++实现XYZ空间内某点绕Y轴旋转β角


//将空间点绕Y轴旋转
//输入参数 x z为空间点原始x z坐标
//thetay为空间点绕Y轴旋转多少度,角度制范围在-180180
//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;
}

2.3 XYZ空间内某点绕X轴旋转α角

1、绕Z轴旋转:Z轴的坐标是不变化的

$$ \left[x^{\prime}, y^{\prime}, z^{\prime}, 1\right]=[x, y, z, 1]\left[\begin{array}{cccc} 1 & 0 & 0 & 0 \\ 0 & \cos \alpha & \sin \alpha & 0 \\ 0 & -\sin \alpha & \cos \alpha & 0 \\ 0 & 0 & 0 & 1 \end{array}\right] $$

然后公式可以表示为:

$$ \begin{array}{l} x^{\prime}=x \\ y^{\prime}=\cos \alpha \cdot y-\sin \alpha \cdot z \\ z^{\prime}=\sin \alpha \cdot y+\cos \alpha \cdot z \end{array} $$

2、python实现XYZ空间内某点绕X轴旋转α角

# 3、绕X轴旋转alpha角
def rotate_X(x, y, z, alpha):
    alpha = alpha * (np.pi / 180)
    x_r = x
    y_r = np.cos(alpha)*y - np.sin(alpha)*z
    z_r = np.sin(alpha)*y + np.cos(alpha)*z
    print(f"{(x, y, z)} rotate {alpha*(180/np.pi)} degrees around the X-axis,result {(x_r, y_r, z_r)}")
    return x_r, y_r, z_r

3、C++实现XYZ空间内某点绕X轴旋转α角


//将空间点绕X轴旋转
//输入参数 y z为空间点原始y z坐标
//thetax为空间点绕X轴旋转多少度,角度制范围在-180180
//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;

完整代码:

import numpy as np

# 1、绕Z周旋转gamma角

def rotate_Z(x, y, z,  gamma):
    gamma = gamma * (np.pi / 180)
    x_r = np.cos(gamma)*x - np.sin(gamma)*y
    y_r = np.sin(gamma)*x + np.cos(gamma)*y
    z_r = z
    print(f"{(x, y, z)} rotate {gamma*(180/np.pi)} degrees around the Z-axis,result {(x_r, y_r, z_r)}")
    return x_r, y_r, z_r


# 2、绕Y轴旋转beta角
def rotate_Y(x, y, z, beta):
    beta = beta * (np.pi / 180)
    x_r = np.cos(beta)*x + np.sin(beta)*z
    y_r = y
    z_r = -np.sin(beta)*x + np.cos(beta)*z
    print(f"{(x, y, z)} rotate {beta*(180/np.pi)} degrees around the Y-axis,result {(x_r, y_r, z_r)}")
    return x_r, y_r, z_r


# 3、绕X轴旋转alpha角
def rotate_X(x, y, z, alpha):
    alpha = alpha * (np.pi / 180)
    x_r = x
    y_r = np.cos(alpha)*y - np.sin(alpha)*z
    z_r = np.sin(alpha)*y + np.cos(alpha)*z
    print(f"{(x, y, z)} rotate {alpha*(180/np.pi)} degrees around the X-axis,result {(x_r, y_r, z_r)}")
    return x_r, y_r, z_r



# https://zh.wikipedia.org/wiki/%E6%97%8B%E8%BD%AC%E7%9F%A9%E9%98%B5
# 可能是坐标系的不同,维基百科上的斜对角的sin的负号,和我们使用的刚好是交换过来的

if __name__ == '__main__':
    # 1、绕Z轴旋转,Z值不变
    # 点(2, 0, 12)绕Z轴旋转90度,旋转后 (0, 2, 12)
    rotate_Z(2, 0, 12, 90)

    # 2、绕Y轴旋转,Y值不变
    # 点(3, 4, 0)绕Y轴顺时针旋转90度,旋转后 (0, 4, -3)
    rotate_Y(3, 4, 0, 90)

    # 3、绕X轴旋转,X值不变
    # 点(5, 0, 7)绕X轴顺时针旋转90度,旋转后 (5, -7, 0)
    rotate_X(5, 0, 7, 90)

    x, y, z = 1, 2, 3
    x, y, z = rotate_X(x, y, z, 90)
    x, y, z = rotate_Y(x, y, z, -90)
    x, y, z = rotate_Z(x, y, z, -90)
    # 结果:(1,2,3) -> (-3, 2, 1)

    x, y, z = 1, 2, 3
    x, y, z = rotate_Z(x, y, z, 90)
    x, y, z = rotate_Y(x, y, z, -90)
    x, y, z = rotate_X(x, y, z, -90)
    # 结果: (1,2,3) - > (-3, -2, -1)
    # 因此不同的旋转顺序会导致旋转的结果不一样

如果你看维基百科,会发现在维基百科上我们绕每个轴的旋转矩阵,斜对角的两个sin正弦值的正负号刚好是交换过来的,这个可能就可坐标系的选择有关了,我们这里选择的是右手坐标系

目录
相关文章
|
10天前
|
存储 Shell 区块链
怎么把Python脚本打包成可执行程序?
该文档介绍了如何将Python脚本及其运行环境打包成EXE可执行文件,以便在不具备Python环境的计算机上运行。首先确保Python脚本能够正常运行,然后通过安装PyInstaller并使用`--onefile`参数将脚本打包成独立的EXE文件。此外,还提供了去除命令行窗口和指定可执行文件图标的详细方法。这些步骤帮助用户轻松地将Python程序分发给最终用户。
怎么把Python脚本打包成可执行程序?
|
5天前
|
Python
探索Python编程的奥秘:打造你的第一个程序
【9月更文挑战第8天】本文将带你进入Python编程的世界,通过一个有趣的项目——制作一个简单的猜数字游戏,让你快速入门。我们不仅会分享代码编写的步骤,还会讲解每一行代码的含义和作用,确保即使是编程新手也能跟上节奏。文章末尾附有完整代码,方便读者实践和学习。
20 12
|
1天前
|
JSON 监控 数据挖掘
Python I/O管理新篇章:优化你的程序,让数据流动更顺畅
【9月更文挑战第13天】在数据驱动时代,Python因其在数据分析、科学计算及Web开发中的广泛应用而备受青睐。本文探讨了Python I/O管理的关键方面,包括理解I/O瓶颈、使用缓冲技术、并发与异步I/O、高效数据序列化及监控调试。通过具体示例,展示了如何优化文件读写和网络通信,提高程序性能。掌握这些技能,可使数据在程序中流动更加顺畅。
9 3
|
1天前
|
Python
惊!Python进程间通信IPC,让你的程序秒变社交达人,信息畅通无阻
【9月更文挑战第13天】在编程的世界中,进程间通信(IPC)如同一场精彩的社交舞会,每个进程通过优雅的IPC机制交换信息,协同工作。本文将带你探索Python中的IPC奥秘,了解它是如何让程序实现无缝信息交流的。IPC如同隐形桥梁,连接各进程,使其跨越边界自由沟通。Python提供了多种IPC机制,如管道、队列、共享内存及套接字,适用于不同场景。通过一个简单的队列示例,我们将展示如何使用`multiprocessing.Queue`实现进程间通信,使程序如同社交达人般高效互动。掌握IPC,让你的程序在编程舞台上大放异彩。
8 3
|
9天前
|
算法 程序员 Linux
Python编程入门:构建你的第一个程序
【9月更文挑战第4天】编程是现代技术发展的基石,而Python作为一门简洁、易学且功能强大的编程语言,已成为众多初学者的首选。本文将引导你通过一个简单的Python程序,探索编程世界的奥秘,并了解如何利用Python实现基本的算法逻辑。无论你是完全的新手还是希望巩固基础的开发者,这篇文章都将为你提供一个清晰的学习路径。从安装Python环境开始,到编写第一个程序,我们将一步步揭开编程的神秘面纱。
WK
|
9天前
|
机器学习/深度学习 Java 程序员
为什么Python比C++慢很多?
Python相较于C++较慢主要体现在:动态类型系统导致运行时需解析类型,增加开销;作为解释型语言,逐行转换字节码的过程延长了执行时间;自动内存管理和垃圾回收机制虽简化操作但也带来了额外负担;全局解释器锁(GIL)限制了多线程性能;尽管Python库方便灵活,但在性能上往往不及C++底层库。然而,Python在某些领域如数据分析、机器学习中,凭借其高级别抽象和简洁语法仍表现出色。选语言需依据具体应用场景和需求综合考量。
WK
29 1
|
14天前
|
Python
Python编程入门:构建你的第一个程序
【8月更文挑战第31天】本文是面向初学者的Python编程指南,将引导读者通过实际案例学习Python基础。我们将一起编写一个简单的程序来展示如何运用变量、控制结构和函数等概念。文章末尾附有完整代码示例供参考和实践。
|
14天前
|
小程序 Python
Python 编程入门:打造你的第一个程序
【8月更文挑战第31天】 在数字化时代,编程已成为一项宝贵的技能。本文将通过一个简单示例引导初学者步入Python编程的世界。我们将从基础语法开始,逐步构建一个小程序,并在此过程中探索编程的逻辑思维与问题解决策略。无论你是科技爱好者还是职场新人,这篇文章都将为你开启编程之旅提供助力。
|
14天前
|
程序员 Python
Python 编程入门:打造你的第一个程序
【8月更文挑战第31天】编程初学者常常在起步时感到迷茫。本文将通过浅显易懂的方式,带领读者从零开始,一步步构建他们的第一个 Python 程序。我们将探索 Python 的安装、基础语法,并通过一个实际的项目——简易计算器,来巩固学习成果。无论你的背景如何,这篇文章都将为你打开编程世界的大门,让你轻松上手,享受编程的乐趣。
|
14天前
|
IDE 程序员 开发工具
Python编程入门:构建你的第一个程序
【8月更文挑战第31天】 本文是专为编程新手设计的Python入门指南,旨在通过一个简单实例引导读者了解Python编程的基础。文章将逐步展示如何从零开始编写一个“Hello, World!”程序,并解释代码中每一部分的功能,帮助初学者建立对Python语言的初步认识和理解。通过本文,读者将学会设置编程环境、编写简单的Python代码以及运行和调试程序。