gpt4 book ai didi

math - 在四元数旋转上应用镜像效果的有效方法?

转载 作者:行者123 更新时间:2023-12-01 17:32:38 25 4
gpt4 key购买 nike

四元数表示旋转 - 它们不包含有关缩放或镜像的信息。然而,仍然可以镜像旋转的效果。

考虑 x-y 平面上的镜像(我们也可以将其称为沿 z 轴的镜像)。围绕在 x-y 平面上镜像的 x 轴的旋转将被取消。绕 y 轴旋转也是如此。但是,绕 z 轴的旋转将保持不变。

另一个示例:绕 x-y 平面镜像的轴 (1,1,1) 旋转 90° 将产生绕 (1,1,-1) 的 -90° 旋转。为了帮助直观,如果您可以可视化轴的描述和指示旋转的圆形箭头,那么镜像该可视化就可以指示新的旋转应该是什么。

我找到了一种计算旋转镜像的方法,如下所示:

  • 获取四元数的角轴表示形式。
  • 对于 x、y 和 z 轴中的每一个。
    • 如果沿该轴的缩放比例为负值(镜像):
      • 对角度和轴取反。
  • 从修改后的角度和轴获取更新的四元数。

这仅支持沿主轴 x、y 和 z 进行镜像,因为这就是我所需要的。但它适用于任意旋转。

但是,从四元数到角轴以及从角轴到四元数的转换成本很高。我想知道是否有一种方法可以直接对四元数本身进行转换,但我对四元数数学的理解不足以让我自己达到任何目的。

(由于计算高效方法的重要性,发布在 StackOverflow 而不是数学相关论坛上。)

最佳答案

我刚刚花了相当多的时间来找出这个问题的明确答案,所以我将其发布在这里以供记录。

简介

正如其他答案中所指出的,镜像效果不能表示为旋转。然而,给定从坐标系 C1 到坐标系 C2 的旋转 R1to2,我们可能有兴趣在对 C1 和 C2 应用相同的镜像效果时有效计算等效旋转(例如,我面对着将左手坐标系中给出的输入四元数转换为表示相同旋转但在右手坐标系中的四元数的问题。

就旋转矩阵而言,可以考虑如下:

R_mirroredC1_to_mirroredC2 = M_mirrorC2 * R_C1_to_C2 * M_mirrorC1

这里,R_C1_to_C2R_mirroredC1_to_mirroredC2 都表示有效旋转,因此在处理四元数时,如何从 高效计算 q_mirroredC1_to_mirroredC2 q_C1_to_C2

解决方案

以下假设q_C1_to_C2=[w,x,y,z]:

  • 如果 C1 和 C2 沿 X 轴镜像(即 M_mirrorC1=M_mirrorC2=diag_3x3(-1,1,1)),则 q_mirroredC1_to_mirroredC2=[w,x,-y ,-z]
  • 如果 C1 和 C2 沿 Y 轴镜像(即 M_mirrorC1=M_mirrorC2=diag_3x3(1,-1,1)),则 q_mirroredC1_to_mirroredC2=[w,-x,y ,-z]
  • 如果 C1 和 C2 沿 Z 轴镜像(即 M_mirrorC1=M_mirrorC2=diag_3x3(1,1,-1)),则 q_mirroredC1_to_mirroredC2=[w,-x,- y,z]

当考虑 C1 和 C2 的不同镜像轴时,我们有以下结论:

  • 如果 C1 沿 X 轴镜像,C2 沿 Y 轴镜像(即 M_mirrorC1=diag_3x3(-1,1,1) & M_mirrorC2=diag_3x3(1,- 1,1)) 然后q_mirroredC1_to_mirroredC2=[z,y,x,w]
  • 如果 C1 沿 X 轴镜像,C2 沿 Z 轴镜像(即 M_mirrorC1=diag_3x3(-1,1,1) & M_mirrorC2=diag_3x3( 1,1,-1)) 然后q_mirroredC1_to_mirroredC2=[-y,z,-w,x]

  • 如果 C1 沿 Y 轴镜像,C2 沿 X 轴镜像(即 M_mirrorC1=diag_3x3(1,-1,1) & M_mirrorC2=diag_3x3( -1,1,1)) 然后 q_mirroredC1_to_mirroredC2=[z,-y,-x,w]

  • 如果 C1 沿 Y 轴镜像,C2 沿 Z 轴镜像(即 M_mirrorC1=diag_3x3(1,-1,1) & M_mirrorC2=diag_3x3( 1,1,-1)) 然后 q_mirroredC1_to_mirroredC2=[x,w,z,y]

  • 如果 C1 沿 Z 轴镜像,C2 沿 X 轴镜像(即 M_mirrorC1=diag_3x3(1,1,-1) & M_mirrorC2=diag_3x3( -1,1,1)) 然后q_mirroredC1_to_mirroredC2=[y,z,w,x]

  • 如果 C1 沿 Z 轴镜像,C2 沿 Y 轴镜像(即 M_mirrorC1=diag_3x3(1,1,-1) & M_mirrorC2=diag_3x3(1,- 1,1)) 然后q_mirroredC1_to_mirroredC2=[x,w,-z,-y]

测试程序

这是一个基于 OpenCV 的小型 C++ 程序来测试这一切:

#include <opencv2/opencv.hpp>
#define CST_PI 3.1415926535897932384626433832795

// Random rotation matrix uniformly sampled from SO3 (see "Fast random rotation matrices" by J.Arvo)
cv::Matx<double,3,3> get_random_rotmat()
{
double theta1 = 2*CST_PI*cv::randu<double>();
double theta2 = 2*CST_PI*cv::randu<double>();
double x3 = cv::randu<double>();
cv::Matx<double,3,3> R(std::cos(theta1),std::sin(theta1),0,-std::sin(theta1),std::cos(theta1),0,0,0,1);
cv::Matx<double,3,1> v(std::cos(theta2)*std::sqrt(x3),std::sin(theta2)*std::sqrt(x3),std::sqrt(1-x3));
return -1*(cv::Matx<double,3,3>::eye()-2*v*v.t())*R;
}

cv::Matx<double,4,1> rotmat2quatwxyz(const cv::Matx<double,3,3> &R)
{
// Implementation from Ceres 1.10
const double trace = R(0,0) + R(1,1) + R(2,2);
cv::Matx<double,4,1> quat_wxyz;
if (trace >= 0.0) {
double t = sqrt(trace + 1.0);
quat_wxyz(0) = 0.5 * t;
t = 0.5 / t;
quat_wxyz(1) = (R(2,1) - R(1,2)) * t;
quat_wxyz(2) = (R(0,2) - R(2,0)) * t;
quat_wxyz(3) = (R(1,0) - R(0,1)) * t;
} else {
int i = 0;
if (R(1, 1) > R(0, 0))
i = 1;
if (R(2, 2) > R(i, i))
i = 2;

const int j = (i + 1) % 3;
const int k = (j + 1) % 3;
double t = sqrt(R(i, i) - R(j, j) - R(k, k) + 1.0);
quat_wxyz(i + 1) = 0.5 * t;
t = 0.5 / t;
quat_wxyz(0) = (R(k,j) - R(j,k)) * t;
quat_wxyz(j + 1) = (R(j,i) + R(i,j)) * t;
quat_wxyz(k + 1) = (R(k,i) + R(i,k)) * t;
}
// Check that the w element is positive
if(quat_wxyz(0)<0)
quat_wxyz *= -1; // quat and -quat represent the same rotation, but to make quaternion comparison easier, we always use the one with positive w
return quat_wxyz;
}

cv::Matx<double,4,1> apply_quaternion_trick(const unsigned int item_permuts[4], const int sign_flips[4], const cv::Matx<double,4,1>& quat_wxyz)
{
// Flip the sign of the x and z components
cv::Matx<double,4,1> quat_flipped(sign_flips[0]*quat_wxyz(item_permuts[0]),sign_flips[1]*quat_wxyz(item_permuts[1]),sign_flips[2]*quat_wxyz(item_permuts[2]),sign_flips[3]*quat_wxyz(item_permuts[3]));
// Check that the w element is positive
if(quat_flipped(0)<0)
quat_flipped *= -1; // quat and -quat represent the same rotation, but to make quaternion comparison easier, we always use the one with positive w
return quat_flipped;
}

void detect_quaternion_trick(const cv::Matx<double,4,1> &quat_regular, const cv::Matx<double,4,1> &quat_flipped, unsigned int item_permuts[4], int sign_flips[4])
{
if(abs(quat_regular(0))==abs(quat_flipped(0))) {
item_permuts[0]=0;
sign_flips[0] = (quat_regular(0)/quat_flipped(0)>0 ? 1 : -1);
}
else if(abs(quat_regular(0))==abs(quat_flipped(1))) {
item_permuts[1]=0;
sign_flips[1] = (quat_regular(0)/quat_flipped(1)>0 ? 1 : -1);
}
else if(abs(quat_regular(0))==abs(quat_flipped(2))) {
item_permuts[2]=0;
sign_flips[2] = (quat_regular(0)/quat_flipped(2)>0 ? 1 : -1);
}
else if(abs(quat_regular(0))==abs(quat_flipped(3))) {
item_permuts[3]=0;
sign_flips[3] = (quat_regular(0)/quat_flipped(3)>0 ? 1 : -1);
}
if(abs(quat_regular(1))==abs(quat_flipped(0))) {
item_permuts[0]=1;
sign_flips[0] = (quat_regular(1)/quat_flipped(0)>0 ? 1 : -1);
}
else if(abs(quat_regular(1))==abs(quat_flipped(1))) {
item_permuts[1]=1;
sign_flips[1] = (quat_regular(1)/quat_flipped(1)>0 ? 1 : -1);
}
else if(abs(quat_regular(1))==abs(quat_flipped(2))) {
item_permuts[2]=1;
sign_flips[2] = (quat_regular(1)/quat_flipped(2)>0 ? 1 : -1);
}
else if(abs(quat_regular(1))==abs(quat_flipped(3))) {
item_permuts[3]=1;
sign_flips[3] = (quat_regular(1)/quat_flipped(3)>0 ? 1 : -1);
}
if(abs(quat_regular(2))==abs(quat_flipped(0))) {
item_permuts[0]=2;
sign_flips[0] = (quat_regular(2)/quat_flipped(0)>0 ? 1 : -1);
}
else if(abs(quat_regular(2))==abs(quat_flipped(1))) {
item_permuts[1]=2;
sign_flips[1] = (quat_regular(2)/quat_flipped(1)>0 ? 1 : -1);
}
else if(abs(quat_regular(2))==abs(quat_flipped(2))) {
item_permuts[2]=2;
sign_flips[2] = (quat_regular(2)/quat_flipped(2)>0 ? 1 : -1);
}
else if(abs(quat_regular(2))==abs(quat_flipped(3))) {
item_permuts[3]=2;
sign_flips[3] = (quat_regular(2)/quat_flipped(3)>0 ? 1 : -1);
}
if(abs(quat_regular(3))==abs(quat_flipped(0))) {
item_permuts[0]=3;
sign_flips[0] = (quat_regular(3)/quat_flipped(0)>0 ? 1 : -1);
}
else if(abs(quat_regular(3))==abs(quat_flipped(1))) {
item_permuts[1]=3;
sign_flips[1] = (quat_regular(3)/quat_flipped(1)>0 ? 1 : -1);
}
else if(abs(quat_regular(3))==abs(quat_flipped(2))) {
item_permuts[2]=3;
sign_flips[2] = (quat_regular(3)/quat_flipped(2)>0 ? 1 : -1);
}
else if(abs(quat_regular(3))==abs(quat_flipped(3))) {
item_permuts[3]=3;
sign_flips[3] = (quat_regular(3)/quat_flipped(3)>0 ? 1 : -1);
}
}

int main(int argc, char **argv)
{
cv::Matx<double,3,3> M_xflip(-1,0,0,0,1,0,0,0,1);
cv::Matx<double,3,3> M_yflip(1,0,0,0,-1,0,0,0,1);
cv::Matx<double,3,3> M_zflip(1,0,0,0,1,0,0,0,-1);

// Let the user choose the configuration
char im,om;
std::cout << "Enter the axis (x,y,z) along which input ref is flipped:" << std::endl;
std::cin >> im;
std::cout << "Enter the axis (x,y,z) along which output ref is flipped:" << std::endl;
std::cin >> om;
cv::Matx<double,3,3> M_iflip,M_oflip;
if(im=='x') M_iflip=M_xflip;
else if(im=='y') M_iflip=M_yflip;
else if(im=='z') M_iflip=M_zflip;
if(om=='x') M_oflip=M_xflip;
else if(om=='y') M_oflip=M_yflip;
else if(om=='z') M_oflip=M_zflip;

// Generate random quaternions until we find one where no two elements are equal
cv::Matx<double,3,3> R;
cv::Matx<double,4,1> quat_regular,quat_flipped;
do {
R = get_random_rotmat();
quat_regular = rotmat2quatwxyz(R);
} while(quat_regular(0)==quat_regular(1) || quat_regular(0)==quat_regular(2) || quat_regular(0)==quat_regular(3) ||
quat_regular(1)==quat_regular(2) || quat_regular(1)==quat_regular(3) ||
quat_regular(2)==quat_regular(3));

// Determine and display the appropriate quaternion trick
quat_flipped = rotmat2quatwxyz(M_oflip*R*M_iflip);
unsigned int item_permuts[4]={0,1,2,3};
int sign_flips[4]={1,1,1,1};
detect_quaternion_trick(quat_regular,quat_flipped,item_permuts,sign_flips);
char str_quat[4]={'w','x','y','z'};
std::cout << std::endl << "When iref is flipped along the " << im << "-axis and oref along the " << om << "-axis:" << std::endl;
std::cout << "resulting_quat=[" << (sign_flips[0]>0?"":"-") << str_quat[item_permuts[0]] << ","
<< (sign_flips[1]>0?"":"-") << str_quat[item_permuts[1]] << ","
<< (sign_flips[2]>0?"":"-") << str_quat[item_permuts[2]] << ","
<< (sign_flips[3]>0?"":"-") << str_quat[item_permuts[3]] << "], where initial_quat=[w,x,y,z]" << std::endl;

// Test this trick on several random rotation matrices
unsigned int n_errors = 0, n_tests = 10000;
std::cout << std::endl << "Performing " << n_tests << " tests on random rotation matrices:" << std::endl;
for(unsigned int i=0; i<n_tests; ++i) {

// Get a random rotation matrix and the corresponding quaternion
cv::Matx<double,3,3> R = get_random_rotmat();
cv::Matx<double,4,1> quat_regular = rotmat2quatwxyz(R);
// Get the quaternion corresponding to the flipped coordinate frames, via the sign trick and via computation on rotation matrices
cv::Matx<double,4,1> quat_tricked = apply_quaternion_trick(item_permuts,sign_flips,quat_regular);
cv::Matx<double,4,1> quat_flipped = rotmat2quatwxyz(M_oflip*R*M_iflip);
// Check that both results are identical
if(cv::norm(quat_tricked-quat_flipped,cv::NORM_INF)>1e-6) {
std::cout << "Error (idx=" << i << ")!"
<< "\n quat_regular=" << quat_regular.t()
<< "\n quat_tricked=" << quat_tricked.t()
<< "\n quat_flipped=" << quat_flipped.t() << std::endl;
++n_errors;
}
}
std::cout << n_errors << " errors on " << n_tests << " tests." << std::endl;
system("pause");
return 0;
}

关于math - 在四元数旋转上应用镜像效果的有效方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32438252/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com