2. 坐标系与姿态表示:地心惯性坐标系、本体坐标系、欧拉角、四元数、方向余弦矩阵
做卫星仿真,第一件事不是写代码,而是把坐标系搞明白。我见过太多新手,代码写得飞起,结果姿态算出来全是错的——最后发现是坐标系搞反了。说白了,坐标系就是卫星的「世界观」,你用什么尺子量,结果就长什么样。
这一节,我们聊聊卫星仿真里最常用的几个坐标系,以及怎么描述卫星「转了多少、朝向哪」。嗯,内容有点干,但都是硬通货。
2.1 地心惯性坐标系(ECI)
地心惯性坐标系,简称 ECI。你可以把它想象成宇宙里的一个「绝对参考系」——它不跟着地球转。
它的定义是这样的:
- 原点:地球质心
- X 轴:指向春分点(就是太阳从南往北穿过赤道那一点)
- Z 轴:指向北极(地球自转轴方向)
- Y 轴:右手定则,X 叉乘 Z 得到
为什么需要它?因为卫星的轨道运动,在惯性系里描述最方便。牛顿定律只适用于惯性系,你想想看,如果坐标系跟着地球转,那卫星的受力分析就多了一堆科里奥利力,多麻烦。
我个人的习惯:在仿真里,所有轨道积分、太阳位置计算,一律在 ECI 下做。等算完了,再转到其他坐标系去用。
小提示:常用的 ECI 系有 J2000 和 TEME 两种。J2000 是标准版本,TEME 是两行轨道根数(TLE)用的。如果你用 TLE 数据,记得用 TEME,别混了。我曾经在这上面吃过亏,算出来的星下点偏了十几公里。
2.2 本体坐标系(Body Frame)
本体坐标系,就是「长在卫星身上的坐标系」。它跟着卫星一起转。
定义很简单:
- 原点:卫星质心
- X 轴:通常指向卫星飞行方向(前向)
- Y 轴:指向卫星右侧
- Z 轴:指向地面(或天顶,看具体定义)
为什么需要它?因为卫星上的传感器、推力器、太阳帆板,都是固定在卫星本体上的。你要知道「太阳帆板朝哪」,就得用本体坐标系。
我做过一个项目,卫星上装了 4 个反作用飞轮,每个飞轮的安装方向都不一样。如果不把飞轮的力矩从本体坐标系转到惯性系,控制律根本没法写。
2.3 方向余弦矩阵(DCM)
方向余弦矩阵,说白了就是一个 3x3 的旋转矩阵。它能把一个向量从一个坐标系转到另一个坐标系。
假设你有一个向量 v 在坐标系 A 里表示为 v_A,你想知道它在坐标系 B 里长什么样:
v_B = C_AB * v_A
其中 C_AB 就是方向余弦矩阵。它的每一列,其实就是坐标系 A 的基向量在坐标系 B 里的投影。
举个例子:
import numpy as np
# 绕 Z 轴转 30 度
theta = np.radians(30)
C = np.array([
[np.cos(theta), np.sin(theta), 0],
[-np.sin(theta), np.cos(theta), 0],
[0, 0, 1]
])
v_A = np.array([1, 0, 0])
v_B = C @ v_A
print(v_B) # 输出:[0.866, -0.5, 0]
注意:方向余弦矩阵是正交矩阵,它的逆等于它的转置。也就是说,C_BA = C_AB^T。这个性质在仿真里非常有用——你只需要存一个矩阵,来回转就行了。
2.4 欧拉角
欧拉角,可能是最直观的姿态表示法。它用三个角度来描述旋转:
- 滚转角(Roll):绕 X 轴转
- 俯仰角(Pitch):绕 Y 轴转
- 偏航角(Yaw):绕 Z 轴转
但这里有个坑——旋转顺序。同样是 (30°, 20°, 10°),按 ZYX 顺序转和按 XYZ 顺序转,结果完全不同。
我建议你统一用 ZYX 顺序(先偏航、再俯仰、最后滚转),这是航空航天领域的惯例。
from scipy.spatial.transform import Rotation as R
# 欧拉角转方向余弦矩阵
euler = [30, 20, 10] # 单位:度
r = R.from_euler('zyx', euler, degrees=True)
C = r.as_matrix()
print(C)
避坑指南:欧拉角有个著名的「万向锁」问题。当俯仰角接近 ±90° 时,滚转和偏航会变得无法区分。我当年做半物理仿真时,就因为这个 bug 查了整整两天。所以,如果你要做大角度机动,别用欧拉角,用四元数。
2.5 四元数
四元数,是姿态仿真的「瑞士军刀」。它没有万向锁,计算效率高,插值平滑。
一个四元数长这样:
q = [q0, q1, q2, q3]
其中 q0 是标量部分,[q1, q2, q3] 是向量部分。它满足:
q0^2 + q1^2 + q2^2 + q3^2 = 1
四元数乘法的规则有点绕,但别怕,直接用库:
from scipy.spatial.transform import Rotation as R
import numpy as np
# 定义两个旋转
q1 = R.from_euler('z', 30, degrees=True).as_quat()
q2 = R.from_euler('y', 45, degrees=True).as_quat()
# 四元数乘法:先转 q1,再转 q2
q_total = R.from_quat(q2) * R.from_quat(q1)
print(q_total.as_quat())
我的经验:在仿真里,我通常用四元数做姿态积分,用方向余弦矩阵做坐标变换,用欧拉角做可视化。各取所长,别死磕一种。
2.6 知识体系总览
下面这张图,是我自己总结的坐标系与姿态表示的核心逻辑。你照着这个思路走,基本不会乱。
2.7 总结与建议
这一节内容不少,我帮你捋一下重点:
- ECI 系:做轨道计算用,别偷懒
- 本体系:做姿态控制用,传感器数据都在这里
- DCM:做坐标变换用,简单直接
- 欧拉角:做可视化用,但小心万向锁
- 四元数:做姿态积分用,稳定可靠
我个人建议,刚开始做仿真时,先把 ECI 和本体系之间的 DCM 写对。这一步对了,后面所有姿态计算都不会跑偏。嗯,就这些,动手试试吧。