为方便查阅,此文是原网站文档翻译,如有侵权,请与本人联系。
坐标变换
此文档组件介绍了整个Chrono API中用于管理坐标、点、转换和旋转的重要概念。
向量
表示空间中三维点的矢量是通过chrono::ChVector类定义的。在数学符号中:
P
=
{
p
x
,
p
y
,
p
z
}
\mathbf{P}=\{p_x,p_y,p_z\}
P={px,py,pz}
ChVector<>类已模板化。可以有单精度的向量,如ChVector<float>
,双精度的向量如ChVector<double>
等。默认数据类型为double
;即ChVector<>默认为ChVector<double>
。
比如,创建一个向量:
ChVector<> mvect1(2,3,4);
使用+、-、*运算符对矢量进行加法、减法、乘法运算。
ChVector<double> mvect1(2,3,4); // Create a vector with given the 2,3,4 ‘double’ components
ChVector<float> mvect2(4,1,2); // Create a vector with given the 4,1,2 ‘float’ components
ChVector<> mvect3(); // Create a 0,0,0, vector. The <> defaults to ‘double’
ChVector<> mvect4(mvect1 + mvect2); // Create a vector by copying another (a result from +)
mvect3 = mvect1 + mvect2; // Vector operators: +, -
mvect3 += mvect1; // In-place operators
mvect3 = mvect2 * 0.003; // Vector product by scalar
mvect3.Normalize(); // One of many member functions, normalizes a vector
mvect3 = mvect1 % mvect2; // Operator for cross product: A%B means vector cross-product AxB
double val = mvect1 ^ mvect2; // Operator for inner product (scalar product)
Chrono还提供了一组有用的常量(双精度类型)向量-VNULL
、VECT_X
、VECT.Y
、VECT_W
,它们分别表示零和坐标轴上的单位向量。
四元数
四元数提供了一个扩展复数的数字系统,它被Chrono用来表示三维空间中的旋转。Chrono实现四元数的封装在Chrono::ChQuaternion
类中。在数学符号中,四元数表示为四个实数的集合:
q
=
{
q
0
,
q
1
,
q
2
,
q
3
}
\mathbf{q}=\{q_0,q_1,q_2,q_3\}
q={q0,q1,q2,q3}
四元数代数性质:
- 实单位四元数 q = { 1 , 0 , 0 , 0 } \mathbf{q}=\{1,0,0,0\} q={1,0,0,0}表示没有旋转
- 给定关于一般单位向量 u \mathbf{u} u的旋转 θ u \theta_{u} θu,相应的四元数为
q = { cos ( α u / 2 ) u x sin ( θ u / 2 ) u y sin ( θ u / 2 ) u z sin ( θ u / 2 ) } \mathbf{q}=\left\{ \begin{array}{c} \cos(\alpha_u / 2)\\ {u}_x \sin(\theta_u / 2)\\ {u}_y \sin(\theta_u / 2)\\ {u}_z \sin(\theta_u / 2) \end{array} \right\} q=⎩ ⎨ ⎧cos(αu/2)uxsin(θu/2)uysin(θu/2)uzsin(θu/2)⎭ ⎬ ⎫
只有具有单位范数的四元数表示有效的旋转。四元数可以用不同的方式构建。
构建表示没有旋转的等效四元数的示例:
ChQuaternion<> qA(1,0,0,0);
ChQuaternion<> qB(QUNIT);
ChQuaternion<> qC = QUNIT;
四元数可以作为关于单位向量 u \mathbf{u} u旋转 θ u \theta{u} θu的函数,用以下公式构建:
ChQuaternion<> qA = Q_from_AngAxis(theta, u);
通过使用四元数的.Rotate()成员函数,可以很容易地通过四元数旋转点。下面的示例显示了如何将点绕y轴旋转20度:
ChVector vA(1,2,3);
ChVector vB();
ChQuaternion qA = Q_from_AngAxis(20 * CH_C_DEG_TO_RAD, VECT_Y);
vB = qA.Rotate(vA);
*运算符用于执行四元数乘积。从运动学角度来看,这表示旋转的串联。
旋转矩阵
在Chrono中使用旋转矩阵
A
\mathbf{A}
A来表征参考系相对于不同参考系的3D定向。与旋转矩阵概念相关的大多数实现支持都封装在chrono::ChMatrix33
中。以下是给出四元数的Chrono函数,可生成3x3方向矩阵
q
↦
A
\mathbf{q} \mapsto \mathbf{A}
q↦A,反之亦然
A
↦
q
\mathbf{A} \mapsto \mathbf{q}
A↦q,注意,旋转矩阵是正交矩阵,
A
∈
S
O
(
3
)
\mathbf{A} \in \mathsf{SO}(3)
A∈SO(3),因此
A
−
1
=
A
t
\mathbf{A}^{-1} = \mathbf{A}^{t}
A−1=At.
创建一个旋转矩阵
ChMatrix33<> mB(1); // Unit matrix, this is a rotation matrix, meaning no rotation
ChMatrix33<> mC(quat); // Build a rotation matrix from a given quaternion
*运算符可以将两个旋转矩阵相乘,例如,旋转mA之后旋转mB变为:
ChMatrix33<> mC = mB * mA;
*运算符可以乘以ChVector<>,这对应于旋转矢量:
ChVector<> vB = m * vA;
逆旋转是矩阵的逆,也是转置。
坐标
chrono::ChCoordsys
表示三维空间中的坐标系。它嵌入向量(坐标平移
d
\mathbf{d}
d)和四元数(坐标旋转
q
\mathbf{q}
q):
c
=
{
d
,
q
}
\mathbf{c}=\{\mathbf{d},\mathbf{q}\}
c={d,q}
ChCoordys是ChFrame的轻量级版本,下面将对此进行讨论。
ChFrame 坐标架
chrono::ChFrame
表示三维空间中的坐标系,如ChCoordsys,但包含更高级的功能。
如上图所示,ChFrame对象表示坐标系
b
\mathbf{b}
b相对于另一坐标系
a
\mathbf{a}
a的“旋转”和“移位”程度。很多时候
a
\mathbf{a}
a是绝对参考系。
d
a
,
b
(
c
)
\mathbf{d}_{a,b(c)}
da,b(c)
用于定义,以基
c
\mathbf{c}
c表示从点
b
\mathbf{b}
b开始,到点
a
\mathbf{a}
a结束的矢量
d
\mathbf{d}
d(沿坐标系
c
\mathbf{c}
c的x、y、z轴测量),如果省略
b
\mathbf{b}
b,则假定它是绝对参考系的原点。作为ChCoordsys,ChFrame对象具有用于平移的矢量和用于旋转的四元数
c
=
{
d
,
q
}
\mathbf{c}=\{\mathbf{d},\mathbf{q}\}
c={d,q}
作为四元数的替代,ChFrame对象还存储辅助3x3旋转矩阵,该矩阵可用于在四元数效率较低的情况下加快计算速度。
ChCoordsys可以被视为ChFrame的轻量级版本。
为了节省内存或如果不需要高级功能,ChCoordsys<>可以是更好的选择。
有几种方法可以构建ChFrame。例如:
ChFrame<> Xa; // build default frame: zero translation, no rotation
ChFrame<> Xb(va, qa); // build from given translation va and rotation quaternion qa
ChFrame<> Xc(csys); // build from a given ChCoordys<>
ChFrame<> Xd(va, tetha, u); // build from translation va and rotation theta about axis u
ChFrame对象最重要的特征之一是能够变换点和其他ChFrame对象。
示例:将点从局部坐标系
b
\mathbf{b}
b转换为绝对坐标系
a
\mathbf{a}
a:
通常一个仿射变换 使用涉及矩阵a旋转和平移的:
这可以在Chrono中完成,如下所示:
ChVector<> d_Paa, d_baa, d_Pbb;
ChMatrix33<> A_ba;
...
d_Paa = d_baa + A_ba * d_Pbb;
然而,ChFrame<>类使其更简单:
ChVector<> d_Paa, d_Pbb;
ChFrame<> X_ba;
...
d_Paa = X_ba * d_Pbb;
同样的概念可以用于链坐标变换。例如如果从坐标架
c
\mathbf{c}
c到
b
\mathbf{b}
b和从
b
\mathbf{b}
b到
a
\mathbf{a}
a的变换是已知的,则可以找到总的框架旋转和位移。
ChFrame<> X_ba, X_cb, X_ca;
...
X_ca = X_ba * X_cb;
这意味着使用不那么复杂的表示。符号被循环使用,以强调后一种形式在使用位移和四元数时具有相同的含义:
上面的转换可以用替换运算符>>表示,其操作数相对于*运算符交换:
ChFrame<> X_ba, X_cb, X_ca;
...
X_ca = X_cb >> X_ba; // equivalent to X_ca = X_ba * X_cb;
在Chrono中,ChCorrdsys<>与ChFrame和ChFrameMoving(定义如下)之间的大部分转换可以用两种等效的方式表示:
…使用*运算符从右到左转换,如:X_ca = X_ba * X_cb
…使用>>运算符从左到右转换,如:``X_ca = X_cb >> X_ba
后者有一些优点:
它更“直观”(参见下标cb-ba如何遵循“链”),
第一个操作数是矢量,如vnew = v >> X_dc >> X_cb >> X_ba
,编译器的默认行为是从左侧执行操作,从而生成一系列返回临时向量的矩阵乘ChFrame操作。否则,*运算符将创建多个ChFrame<>临时对象,这会比较慢。
还可以对其他对象使用*或>>运算符,例如在ChFrame Xa和ChVector vb之间使用>>。此操作的结果是一个新的ChFrame对象,该对象通过矢量vb转换旧对象而获得:
ChVector<> vb;
ChFrame<> Xa, Xt; ...
Xt = Xa >> vb; // also Xt = vb * Xa;
对于可以用于平移或旋转ChFrame的在位运算符*=或>>=,或者完全转换,这取决于它的使用方式:使用ChVector或ChQuaternion或另一个ChFrame。例如,要通过矢量vb转换Xa,可以写下:
Xa >>= vb;
请注意,虽然运算符 * 和>>创建临时对象,但*=或>>=并非如此,这会提高效率。在这种情况下,经验法则是避免:
- 操作符*和>>
- 使用低级函数,如TransformLocalToParent、TransformParentToLocal等。
*和>>操作都支持逆变换。示例:在关系式X_ca=X_cb>>X_ba中;假设X_ca和X_cb是已知的,并且人们对计算X_ba感兴趣。将方程两边乘以X_cb的倒数,得到
X_ba = X_cb.GetInverse() >> X_ca;
注意,GetInverse操作可能比可读性较差的低级方法(在本例中为TransformParentToLocal())效率低,即:
X_ba = X_cb.TransformParentToLocal(X_ca);
有关API详细信息,请参阅chrono::ChFrame
ChFrameMoving
chrono::ChFrameMoving
对象表示三维空间中的坐标系,像ChFrame一样,但它也存储有关帧的速度和加速度的信息:
c
=
{
p
,
q
,
p
˙
,
q
˙
,
p
¨
,
q
¨
}
\mathbf{c}=\{\mathbf{p},\mathbf{q},\dot{\mathbf{p}}, \dot{\mathbf{q}}, \ddot{\mathbf{p}}, \ddot{\mathbf{q}} \}
c={p,q,p˙,q˙,p¨,q¨}
注意,使用四元数导数来表示角速度和角加速度可能很麻烦。因此,该类还可以设置和获取角速度
ω
\mathbf{\omega}
ω和角加速度
α
\mathbf{\alpha}
α方面的数据:
c
=
{
p
,
q
,
p
˙
,
ω
,
p
¨
,
α
}
\mathbf{c}=\{\mathbf{p},\mathbf{q},\dot{\mathbf{p}}, \mathbf{\omega}, \ddot{\mathbf{p}}, \mathbf{\alpha}\}
c={p,q,p˙,ω,p¨,α}
这可以通过下图直观地显示出来:
注意,“角”速度和加速度可以在移动z坐标架或绝对坐标架的基础上设置或者获得。
示例:创建ChFrameMoving,并为其指定非零角速度和线速度,同时还指定了线加速度和角加速度。
ChFrameMoving<> X_ba;
X_ba.SetPos(ChVector<>(2,3,5));
X_ba.SetRot(myquaternion);
// set velocity
X_ba.SetPos_dt(ChVector<>(100,20,53));
X_ba.SetWvel_loc(ChVector<>(0,40,0)); // W in local frame, or..
X_ba.SetWvel_par(ChVector<>(0,40,0)); // W in parent frame
// set acceleration
X_ba.SetPos_dtdt(ChVector<>(13,16,22));
X_ba.SetWacc_loc(ChVector<>(80,50,0)); // a in local frame, or..
X_ba.SetWacc_par(ChVector<>(80,50,0)); // a in parent frame
ChFrameMoving可用于变换ChVector(空间中的点)、ChFrame或ChFrameMoving 对象。速度也可以被计算和转换。
示例:如果已知从
b
\mathbf{b}
b到
a
\mathbf{a}
a和从
c
\mathbf{c}
c到
b
\mathbf{b}
b的变换,则可以计算
c
\mathbf{c}
c相对于
a
\mathbf{a}
a的绝对速度和角速度:
使用两种替代形式(基于>>运算符的从左到右,或基于*运算符的从右到左),上述情况可转换为以下等效表达式:
X_ca = X_cb >> X_ba;
X_ca = X_ba * X_cb;
这与ChFrame和ChCoordsys中使用的代数完全相同,只是这一次速度和加速度也进行了转换。
注意,转换自动考虑了复杂项的贡献,如向心加速度、相对加速度、科里奥利加速度等。
以下是另一个较长的转换串联示例:
注意,还可以使用GetInverse()使用坐标架的逆变换,如ChFrame所示。
示例:计算移动目标8相对于夹具6的位置、速度和加速度,以框架6为基础表示。
在已知其他所有变换时如何计算X_86?从X_80的两个等价表达式开始:
X_86>>X_65>>X_54>>X_43>>X_32>>X_21>>X_10 = X_87>>X_70;
或者
X_86>>(X_65>>X_54>>X_43>>X_32>>X_21>>X_10) = X_87>>X_70;
将两边乘以(…)的倒数,记住一般情况下
- X >> X.GetInverse() = I
- X.GetInverse() >> X = I,
其中I是可以移除的身份转换,最终得到:
X_86 = X_87 >> X_70 >> (X_65 >> X_54 >> X_43 >> X_32 >> X_21 >> X_10).GetInverse();
基于同一图的示例:计算夹具相对于移动目标8的速度和加速度,以参考系8为基础表示。
X_68 = X_12 >> X_23 >> X_34 >> X_45 >> X_56 >> (X_87 >> X_70).GetInverse();