У меня два вектора в 3D-пространстве, S
(Пуск) и T
(Target), и я хочу найти матрицу вращения (RM), которая допускает такое преобразование.
Я знаю, что, вычисляя поперечное произведение S × T
я получаю ось вращения. Угол между S
и T
задается tan⁻¹(||S × T||, S·T)
, где S·T
- точечное произведение между S и T.
Это дает мне вектор вращения rotvec = [S x T; angle]
rotvec = [S x T; angle]
(поперечное произведение нормировано). Затем, используя функцию vrrotvec2mat (в MATLAB) или transforms3d.axangles.axangle2mat (в Python), я могу получить матрицу вращения, соответствующую преобразованию из S
в T
В моем приложении T
задается точечным произведением RM·D
, где D
- вектор 3x1
. Моя цель - найти RM. Я знаю S
, T
и D
, но мне трудно понять математику за этим.
На практике я хочу найти RM между S
и T'
, где T'
- целевой вектор до D
(направление).
Еще немного контекста: я хочу получить углы сустава тела из трехмерных точек в системе координат камеры.
Для выполнения этой работы вам также нужен центр вращения (точка, которая остается неизменной после вращения). Теперь нам нужны две матрицы преобразования, представляющие систему координат до и одну после преобразования.
Чтобы построить вашу матрицу 3D-преобразования, вам понадобятся 3 перпендикулярных базисных вектора и исходная позиция:
теперь ось вращения будет одним из базовых векторов, и мы можем использовать S,T
как вторую, так что третье мы можем вычислить с помощью поперечного произведения, а начало координат будет центром вращения:
X = cross(S,T); // rotation axis
X /= |X|; // unit vector
Y = S; // start vector
Y /= |Y|; // unit vector
Z = cross(X,Y); // Z perpendicular to X,Y
Z /= |Z|; // unit vector
O = center_of_rotation;
Так постройте матрицу преобразования 4x4 A
из них. И сделайте то же самое для B
но используйте T
вместо S
Теперь мы хотим вычислить разностное преобразование, так что если p=(x,y,z,1)
- любая точка преобразования, то:
p' = Inverse(A)*p
p' = B*p'
Итак, ваша матрица преобразования M
:
M = Inverse(A)*B;
Помните, что это будет работать со стандартными соглашениями OpenGL, если вы используете разные (порядок умножения, ориентацию матрицы и т.д.), Уравнение может измениться.
Вы также можете использовать полную псевдо-обратную матрицу для более эффективного и точного вычисления инверсии Inverse(A)
.
Как вы можете видеть, вам не нужны никакие гониометрия или угол, чтобы сделать это (векторная математика хороша в этом)
Пример [Edit1] C++
Его использование VCL (AnsiString и mm_log, которые вы можете игнорировать) и моя векторная математика (используемые функции находятся в первой ссылке).
//---------------------------------------------------------------------------
AnsiString matrix_prn(double *a)
{
int i; AnsiString s;
for (s ="(",i=0;i<16;i+=4) { if (a[i]>=0.0) s+=" "; s+=AnsiString().sprintf("%2.3lf,",a[i]); } s[s.Length()]=')'; s+="\r\n";
for (s+="(",i=1;i<16;i+=4) { if (a[i]>=0.0) s+=" "; s+=AnsiString().sprintf("%2.3lf,",a[i]); } s[s.Length()]=')'; s+="\r\n";
for (s+="(",i=2;i<16;i+=4) { if (a[i]>=0.0) s+=" "; s+=AnsiString().sprintf("%2.3lf,",a[i]); } s[s.Length()]=')'; s+="\r\n";
for (s+="(",i=3;i<16;i+=4) { if (a[i]>=0.0) s+=" "; s+=AnsiString().sprintf("%2.3lf,",a[i]); } s[s.Length()]=')';
return s;
}
//---------------------------------------------------------------------------
AnsiString vector_prn(double *a)
{
int i; AnsiString s;
for (s ="(",i=0;i<3;i++) { if (a[i]>=0.0) s+=" "; s+=AnsiString().sprintf("%2.3lf,",a[i]); } s[s.Length()]=')';
return s;
}
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner):TForm(Owner)
{
int i;
double O[3]={0.00, 0.00,0.00}; // center ofrotation
double S[3]={4.10,-9.44,0.54}; // start vector
double T[3]={1.40,-9.08,4.10}; // end vector
double A[16],_A[16],B[16],M[16],X[3],Y[3],Z[3];
// A
vector_mul(X,S,T); // rotation axis
vector_one(X,X); // unit vector
vector_one(Y,S); // unit start vector
vector_mul(Z,X,Y); // Z perpendicular to X,Y
vector_one(Z,Z); // unit vector
for (i=0;i<3;i++)
{
A[ 0+i]=X[i];
A[ 4+i]=Y[i];
A[ 8+i]=Z[i];
A[12+i]=O[i];
A[(i<<2)+3]=0.0;
} A[15]=1.0;
// B
vector_one(Y,T); // unit end vector
vector_mul(Z,X,Y); // Z perpendicular to X,Y
vector_one(Z,Z); // unit vector
for (i=0;i<3;i++)
{
B[ 0+i]=X[i];
B[ 4+i]=Y[i];
B[ 8+i]=Z[i];
B[12+i]=O[i];
B[(i<<2)+3]=0.0;
} B[15]=1.0;
// M = B*Inverse(A)
matrix_inv(_A,A);
matrix_mul(M,_A,B);
mm_log->Lines->Add("A");
mm_log->Lines->Add(matrix_prn(A));
mm_log->Lines->Add("B");
mm_log->Lines->Add(matrix_prn(B));
mm_log->Lines->Add("M");
mm_log->Lines->Add(matrix_prn(M));
mm_log->Lines->Add("");
vector_one(S,S); // unit start vector
vector_one(T,T); // unit end vector
mm_log->Lines->Add("S = "+vector_prn(S));
matrix_mul_vector(X,M,S);
mm_log->Lines->Add("X = "+vector_prn(X));
mm_log->Lines->Add("T = "+vector_prn(T));
}
//-------------------------------------------------------------------------
Вот результат:
A
(-0.760, 0.398,-0.514, 0.000)
(-0.361,-0.916,-0.175, 0.000)
(-0.540, 0.052, 0.840, 0.000)
( 0.000, 0.000, 0.000, 1.000)
B
(-0.760, 0.139,-0.635, 0.000)
(-0.361,-0.903, 0.235, 0.000)
(-0.540, 0.408, 0.736, 0.000)
( 0.000, 0.000, 0.000, 1.000)
M
( 0.959, 0.258,-0.115, 0.000)
(-0.205, 0.916, 0.345, 0.000)
( 0.194,-0.307, 0.932, 0.000)
( 0.000, 0.000, 0.000, 1.000)
S = ( 0.398,-0.916, 0.052)
X = ( 0.139,-0.903, 0.408) // X = M * S
T = ( 0.139,-0.903, 0.408)
Как вы можете видеть, если я преобразую единицу S
на M
я получил единицу T
вектора. PS. my matrix_mul_vector
и vector_mul
предполагает w=1.0
но как O=(0.0,0.0,0.0)
векторы и точки одинаковы.
p=(x,y,z,0)
чем p,p'
обрабатываются как векторы, и если p=(x,y,z,1)
они обрабатываются как точки ... ,