SETTLE约束算法中的坐标变换问题

技术背景

在之前的两篇文章中,我们分别讲解了SETTLE算法的原理和基本实现SETTLE约束算法的批量化处理。SETTLE约束算法在水分子体系中经常被用到,该约束算法具有速度快、可并行、精度高的优点。本文我们需要探讨的是该约束算法中的一个细节,问题是这样定义的,给定坐标系(XYZ)下的两个已知三角形(\Delta A_0B_0C_0)和三角形(\Delta A_1B_1C_1),以三角形(\Delta A_0B_0C_0)构造一个平面(\pi_0),将(\pi_0)平移到三角形(\Delta A_1B_1C_1)的质心位置,作为新坐标系的(X’Y’)平面,再使得(Y’Z’)平面过(A_1)点,以此来构造一个新的坐标系(X’Y’Z’),求两个坐标系之间的变换。

理论推导

坐标系(OXYZ)和(O’X’Y’Z’)之间的变换,只有平移和旋转,没有伸缩。那么关于平移的部分,我们只需要考虑两个原点位置之间的向量差即可。而旋转部分,需要一些技巧,至少我们需要找到三个合适的点用于计算这个旋转矩阵。比如说,假定三角形(\Delta A_1B_1C_1)在坐标系(OXYZ)和(O’X’Y’Z’)之中的位置都是已知的,那么我们就可以按照下述公式来计算旋转矩阵(R):

[R\left[ \begin{matrix} X_A && X_B && X_C\ Y_A && Y_B && Y_C\ Z_A && Z_B && Z_C \end{matrix} \right]= \left[ \begin{matrix} X’_A && X’_B && X’_C\ Y’_A && Y’_B && Y’_C\ Z’_A && Z’_B && Z’_C \end{matrix} \right] ]

然后在等式两边各乘上一个逆矩阵就可以得到旋转矩阵:

[R=\left[ \begin{matrix} X’_A && X’_B && X’_C\ Y’_A && Y’_B && Y’_C\ Z’_A && Z’_B && Z’_C \end{matrix} \right]\left[ \begin{matrix} X_A && X_B && X_C\ Y_A && Y_B && Y_C\ Z_A && Z_B && Z_C \end{matrix} \right]^{-1} ]

然而不幸的是,我们并不能直接得到三角形(\Delta A_1B_1C_1)在坐标系(O’X’Y’Z’)之中的位置,这需要一些计算。因此,我们可以考虑另辟蹊径,找其他更容易计算的三个向量,用来计算我们所需要的旋转矩阵。

第一个向量

我们找的第一个向量是(Z’)轴,或者用向量表示就是(\vec{O’Z’}=[0, 0, 1]^T),因为(Z’)轴跟平面(\pi_0)是垂直的关系,也就是垂直于三角形(\Delta A_0B_0C_0)。因此对应的可以用三角形(\Delta A_0B_0C_0)的任意两条边的外积来计算向量(\vec{O’Z’}=R\cdot[\vec{A_0B_0}\times \vec{A_0C_0}])(注意做归一化处理)。

第二个向量

如果分别用(D_1)和(D’1)来表示三角形(\Delta A_1B_1C_1)在坐标系(OXYZ)和坐标系(O’X’Y’Z’)下的质心位置。这里我们找的第二个向量,就是(\vec{D’_1A’_1})。这里因为(A’_1)点在(Y’Z’)平面上,因此(X’{A’_1}=0)。而向量(\vec{D’_1A’_1})和(Z’)轴的夹角,我们可以在坐标系(OXYZ)下计算:

[cos \theta=\frac{(\vec{A_0B_0}\times \vec{A_0C_0})\cdot\vec{D_1A_1}}{|\vec{A_0B_0}\times \vec{A_0C_0}|\cdot|\vec{D_1A_1}|}, sin \theta=\sqrt{1-cos^2\theta} ]

计算出来夹角之后,就可以得到(\vec{D’_1A’_1}=[0, sin\theta, cos\theta]^T),即:(\vec{D’_1A’_1}=R\cdot\vec{D_1A_1})。

第三个向量

到这一步为止,其实我们还是没有计算出(\vec{D’_1B’_1})和(\vec{D’_1C’_1})的值,因此我们第三个向量,在前两个向量的基础之上,用叉乘的方法再构造一个(X’)轴的向量,即(\vec{O’X’}=[1, 0, 0]^T),旋转矩阵计算方法为:

[\vec{O’X’}=R\cdot \left[(\vec{A_0B_0}\times \vec{A_0C_0})\times \vec{D_1A_1}\right] ]

小结

这样一来,我们就得到了三个向量在两个坐标系下的坐标,可以用于建立方程组,计算两个坐标之间的变换关系,如果写成矩阵乘法形式就是:

[R = \left[ \begin{matrix} 0&&0&&1\ 0&&\sqrt{1-(\frac{(\vec{A_0B_0}\times \vec{A_0C_0})\cdot\vec{D_1A_1}}{|\vec{A_0B_0}\times \vec{A_0C_0}|\cdot|\vec{D_1A_1}|})^2}&&0\ 1&&\frac{(\vec{A_0B_0}\times \vec{A_0C_0})\cdot\vec{D_1A_1}}{|\vec{A_0B_0}\times \vec{A_0C_0}|\cdot|\vec{D_1A_1}|}&&0 \end{matrix} \right]\left(\left[ \begin{matrix} \vec{A_0B_0}\times \vec{A_0C_0}\ \vec{D_1A_1}\ (\vec{A_0B_0}\times \vec{A_0C_0})\times \vec{D_1A_1} \end{matrix} \right]^{T}\right)^{-1} ]

代码实现

这里先提一下代码实现和测试的思路。我们首先用Python来构造2个三角形,得到一个新的三角形。然后我们再根据上述的公式,计算得到一个坐标旋转矩阵。最后我们再输入一些便于手动计算的点(或者是直接用前面三角形的三个角,或者是中间的一些向量都是可以的),用旋转矩阵进行变换,来测试一下是否我们所需要的坐标变换之后的结果。

In [1]: import numpy as np

In [2]: T0 = np.array([[0, 0, 1], [0, -1, 0],[0, 1, 0]], np.float32)

In [3]: T1 = np.array([[1, 0, 1], [0, -1, 0], [0, 1, 0]], np.float32)

In [4]: D0 = np.mean(T0, axis=-2)

In [5]: D1 = np.mean(T1, axis=-2)

In [6]: A0B0 = T0[1]-T0[0]

In [7]: A0C0 = T0[2]-T0[0]

In [8]: v0 = np.cross(A0B0, A0C0)

In [9]: v0 /= np.linalg.norm(v0)

In [10]: v1 = T1[0]-D1

In [11]: v1 /= np.linalg.norm(v1)

In [12]: v2 = np.cross(v0, v1)

In [13]: v2 /= np.linalg.norm(v2)

In [14]: M1 = np.vstack((v0, v1, v2))

In [15]: M1 = M1.T

In [16]: iM1 = np.linalg.inv(M1)

In [17]: cost = np.dot(v0, v1)/np.linalg.norm(v0)/np.linalg.norm(v1)

In [18]: M0 = np.array([[0, 0, 1], [0, np.sqrt(1 - cost**2), 0], [1, cost, 0]])

In [19]: R = np.dot(M0, iM1)

In [20]: R
Out[20]:
array([[ 0.00000000e+00, -1.00000000e+00,  0.00000000e+00],
       [ 0.00000000e+00,  0.00000000e+00,  9.99999916e-01],
       [ 1.00000000e+00,  0.00000000e+00,  5.00651538e-08]])

In [21]: np.dot(R, v0)
Out[21]: array([0., 0., 1.])

In [22]: np.dot(R, v1)
Out[22]: array([0.        , 0.70710671, 0.7071068 ])

In [23]: np.dot(R, v2)
Out[23]: array([1., 0., 0.])

In [24]: np.dot(R, T1[0]-D1)
Out[24]: array([0.        , 0.66666657, 0.66666666])

In [25]: np.dot(R, T1[1]-D1)
Out[25]: array([ 1.        , -0.33333332, -0.33333336])

In [26]: np.dot(R, T1[2]-D1)
Out[26]: array([-1.        , -0.33333332, -0.33333336])

上面这个案例的流程是这样的,我们先创建两个不一样大小的绿色三角形和红色三角形,我们将要做的事情是以绿色三角形为(X’Y’)平面,红色三角形的质心为原点,并使得(Y’Z’)平面过(A_1)点,以此来构造一个新的坐标系,并计算前后两个坐标系之间的变换。

这里需要一些空间想象能力,我们可以先将绿色的三角形平面平移到过红色三角形的质心位置,同时将坐标系原点移动到红色三角形的质心位置,再旋转坐标轴,使得(Y’Z’)平面过(A_1)点。这样一来通过上一个章节中的旋转矩阵的构造方法,我们就可以计算出所有的向量在两个坐标系下的旋转变换。

当然,需要注意的是,这个变换只是一个旋转变换,由于坐标系发生了平移,所以需要有一个固定的参考点,才能够精确的得到某一个给定的点的坐标变换。比如我们上述python代码中的24、25、26都是对红色三角形的三个顶点关于质心的相对位置的坐标变换,在坐标变换前后,顶点坐标都需要减去质心的坐标。

总结概要

在已知两个三角形顶点坐标的情况下,我们要以其中的一个三角形平面去构造一个新的坐标系,并且需要找到新旧坐标系之间的变换关系。这是一个比较简单的立体几何的问题,寻找两个坐标系之间的变换矩阵。如果是常规思路,可以先根据两个三角形之间的相对位置去计算一下在新坐标系下两个三角形的新的顶点坐标,从而可以取三个点来构造一个坐标变换矩阵,进而推广到所有向量在这两个坐标系之间的变换关系。而本文提供了一种相对更容易求解、也比较直接的思路,并给出了相关的Python代码实现过程。

版权声明

本文首发链接为:https://www.cnblogs.com/dechinphy/p/xyz-transform.html

作者ID:DechinPhy

更多原著文章请参考:https://www.cnblogs.com/dechinphy/

打赏专用链接:https://www.cnblogs.com/dechinphy/gallery/image/379634.html

腾讯云专栏同步:https://cloud.tencent.com/developer/column/91958

CSDN同步链接:https://blog.csdn.net/baidu_37157624?spm=1008.2028.3001.5343

51CTO同步链接:https://blog.51cto.com/u_15561675

Original: https://www.cnblogs.com/dechinphy/p/xyz-transform.html
Author: DECHIN
Title: SETTLE约束算法中的坐标变换问题

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/714227/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • IEEE浮点数向偶数舍

    CSAPP ​ 向偶数舍入初看上去好像是个相当随意的目标——有什么理由偏向取偶数呢?为什么不始终把位于两个可表示的值中间的值都向上舍入呢?使用这种方法的一个问题就是很容易假想到这样…

    技术杂谈 2023年7月11日
    090
  • Thymeleaf 简介

    Thymeleaf 是一款用于渲染 XML/XHTML/HTML5 内容的模板引擎。它与 JSP,Velocity,FreeMaker 等模板引擎类似,也可以轻易地与 Spring…

    技术杂谈 2023年5月31日
    078
  • 正则表达式规则与语法

    正则表达式(regular expression)就是用一个”字符串”来描述一个特征,然后去验证另一个”字符串”是否符合这个特征。比…

    技术杂谈 2023年5月31日
    0117
  • 3. SpringBoot整合Redis

    1.下载地址以及安装 https://github.com/MicrosoftArchive/redis/releases 我是安装的windows版本所以选择这个: 解压之后运行…

    技术杂谈 2023年7月24日
    056
  • docker容器编排原来这么丝滑~

    前言: 请各大网友尊重本人原创知识分享,谨记本人博客:南国以南i 概念介绍: Docker 这个东西所扮演的角色,容易理解,它是一个容器引擎,也就是说实际上我们的容器最终是由Doc…

    技术杂谈 2023年7月10日
    085
  • 《码出高效》Chapter2面向对象-读书笔记

    《码出高效Java开发手册》第2章 面向对象 面向对象的抽象、封装、继承、多态的理念,使企业应用大规模化成为可能,有效地降低了软件开发成本、维护成本和复用成本。OOP实践了软件工程…

    技术杂谈 2023年7月10日
    068
  • Java并发编程之AQS以及源码解析

    文章目录 概览 实现思路 实现原理 * 源自CLH锁 AQS数据模型 CAS操作 主要方法 * 自定义同步器的实现方法 AQS定义的模板方法 源码解读 * 等待状态释义 AQS获取…

    技术杂谈 2023年7月24日
    0115
  • 一文搞懂│php 中的 DI 依赖注入

    404. 抱歉,您访问的资源不存在。 可能是网址有误,或者对应的内容被删除,或者处于私有状态。 代码改变世界,联系邮箱 contact@cnblogs.com 园子的商业化努力-困…

    技术杂谈 2023年7月11日
    072
  • nginx location配置详细解释

    【原文链接】:https://blog.tecchen.xyz ,博文同步发布到博客园。由于精力有限,对文章的更新可能不能及时同步,请点击上面的原文链接访问最新内容。欢迎访问我的个…

    技术杂谈 2023年7月11日
    058
  • 海报轮播实例

    功能:两种模式,图片轮播和单列展示,可修改图片和链接,可设置图片高度和底边距 切换本质: 图片轮播模式使用sliderView,在图片轮播模式下,单列图片的container将隐藏…

    技术杂谈 2023年6月1日
    0105
  • js引入时的三种方式

    javascript;gutter:true; Document</p> <pre><code>alert('沙漠骆驼');…

    技术杂谈 2023年5月31日
    095
  • 有意义的学习,都要先回答三个问题

    我们都知道, 在现代经济中, 我们不能停止学习。但如何保持自我教育是一个复杂的问题。 获得一个正式学位,比如 MBA 或博士学位, 是否值得? 你是否应该采取更有针对性的方法, 参…

    技术杂谈 2023年5月31日
    092
  • hasura graphql-engine centos 7 二进制文件

    昨天自己构建了一个简单的hasura graphql-engine centos 7 二进制文件,可以使用 参考使用 下载 wget https: chmod +x graphql…

    技术杂谈 2023年5月30日
    091
  • Twitter系统架构参考

    Twitter系统架构参考 Push、Pull模式 每时每刻都有用户在Twitter上发表内容,Twitter工作是规划如何组织内容并把它发送用户的粉丝。 实时是真正的挑战,5秒内…

    技术杂谈 2023年6月1日
    0100
  • tcprstat和tcpstat性能监控

    tcprstat是percona用来监测mysql响应时间的。不过对于任何运行在TCP协议上的响应时间,都可以用。 下面是一个监控示例,监控分析mysql的3306端口。 根据上面…

    技术杂谈 2023年5月31日
    0112
  • vue总是报错:Trailing spaces not allowed

    翻译: Trailing spaces not allowed:不允许尾随空格 1-报错: 2-解决: 你的某些行的空格多了,删掉就行了 以我的截图为例 代码12行出错 选中12行…

    技术杂谈 2023年6月1日
    092
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球