<think>我们使用libsdp4库(一个用于卫星轨道计算的C++库)来计算卫星的位置和速度,但该库本身并不直接提供姿态参数(如偏移角、姿态偏离角、侧摆角、俯仰角、偏航角)的计算。这些姿态参数通常需要卫星的姿态控制系统数据或预设的成像模式来确定。然而,我们可以通过轨道参数和卫星的指向模型来推导一些相关的角度。
基本思路:
1. 使用TLE数据初始化卫星轨道模型(SGP4/SDP4),计算特定时刻的卫星位置(ECI坐标系)和速度。
2. 根据卫星的位置和速度,计算卫星的轨道坐标系(通常是RTN:径向、横向、法向)。
3. 定义卫星本体坐标系(通常x为飞行方向,z指向地心,y构成右手系)和相机指向(通常为z轴负方向或根据任务定义)。
4. 根据卫星的姿态控制模式(例如对地定向)确定卫星本体坐标系相对于轨道坐标系的旋转(即姿态角:偏航、俯仰、滚动)。
5. 计算姿态偏离角(卫星实际指向与理想天底方向的夹角)等。
注意:libsdp4库通常只提供位置和速度,姿态参数需要额外的模型。如果卫星是三轴稳定的对地定向卫星,那么其理想姿态是本体z轴指向地心,此时姿态偏离角为0。但实际中由于控制误差或成像需要,会有一定的姿态角。
步骤详解:
步骤1:使用libsdp4计算卫星位置和速度
- 初始化:使用TLE的两行字符串初始化卫星轨道模型。
- 计算:给定一个UTC时间,计算卫星的位置(km)和速度(km/s)在ECI坐标系(地心惯性坐标系)中。
步骤2:计算轨道坐标系(RTN)
- R (径向): 从地心指向卫星的单位向量。
- T (横向): 轨道平面内垂直于R并指向速度方向(在圆轨道中与速度方向一致)。
- N (法向): 轨道平面的法向,与R和T构成右手系,即 N = R × T / |R × T|,然后 T = N × R 确保单位向量。
步骤3:定义卫星本体坐标系
- 对于对地定向卫星,理想情况下:
x轴(滚动轴,Roll): 沿飞行方向(即速度方向,但注意速度方向不一定是横向,在椭圆轨道中速度方向有径向分量)。
z轴(偏航轴,Yaw): 指向地心(即-R方向)。
y轴(俯仰轴,Pitch): 构成右手系,即 y = z × x。
但实际上,由于轨道是椭圆的,速度方向并不完全与横向重合。因此,我们通常这样定义:
- 首先,定义速度方向单位矢量:V_unit = V / |V|
- 然后,z轴理想指向地心:z_body_desired = -R (单位矢量)
- 然后,y轴方向为轨道法向(即角动量方向):H = R × V,然后单位化:N = H / |H|
- 最后,x轴由y和z叉乘得到:x_body_desired = y_body × z_body_desired (注意叉乘顺序)
但是,卫星的实际本体坐标系可能由于姿态机动而偏离理想状态。通常,我们通过姿态四元数或欧拉角来描述。
步骤4:计算姿态角(偏航、俯仰、滚动)
- 假设我们已知卫星的实际本体坐标系相对于轨道坐标系(RTN)的旋转。这个旋转通常由卫星的姿态确定系统提供(如星敏感器、陀螺仪等)。如果我们没有实际数据,可以假设为0(即理想对地定向)。
然而,问题中要求根据TLE计算姿态角,这通常是不可能的,因为TLE只提供轨道信息,不提供姿态信息。但如果我们假设卫星是理想对地定向(即三轴稳定,且本体坐标系与轨道坐标系对齐),那么姿态角(偏航、俯仰、滚动)都为0。
但有时,我们可能根据任务需求设置一个侧摆角(例如为了扩大成像范围),这时卫星绕滚动轴(x轴)旋转一个角度(侧摆角)。那么,我们可以通过预设的侧摆角、俯仰角、偏航角来构建旋转矩阵,从而计算相机指向。
步骤5:计算姿态偏离角
- 姿态偏离角是卫星实际指向(相机光轴)与理想天底方向(即地心方向)的夹角。
- 理想天底方向:-R(即地心到卫星的向量取反,从卫星指向地心)。
- 实际相机指向:假设相机固定在本体坐标系上,通常为z轴负方向(即 -z_body)。
- 因此,实际指向矢量:d = -z_body
- 理想指向矢量:n = -R
- 姿态偏离角:θ = arccos( (d · n) / (|d||n|) )
由于我们假设卫星是理想对地定向,则z_body = R(注意:这里z_body指向卫星,而地心方向是-R,所以相机指向地心应该是-z_body,即-R)。所以此时d = -z_body = -R,n = -R,则θ=0。
如果我们设置了一个侧摆角(绕x轴旋转),那么z_body会发生变化,从而θ不为0。
因此,我们需要一个从轨道坐标系(RTN)到本体坐标系的旋转矩阵(由三个姿态角确定)。
旋转顺序:通常为偏航(Yaw)-俯仰(Pitch)-滚动(Roll),即:
R = R_roll(φ) * R_pitch(θ) * R_yaw(ψ)
在轨道坐标系中:
- 偏航(Yaw): 绕z轴(R轴)旋转
- 俯仰(Pitch): 绕y轴(T轴)旋转
- 滚动(Roll): 绕x轴(N轴)旋转
但注意:不同的领域定义可能不同。在卫星中,通常定义:
- 滚动(Roll): 绕x轴(飞行方向)旋转
- 俯仰(Pitch): 绕y轴(轨道法向)旋转
- 偏航(Yaw): 绕z轴(径向向外)旋转
然而,轨道坐标系(RTN)的x轴是R(径向),y轴是T(横向),z轴是N(法向)。而卫星本体坐标系通常定义x为飞行方向(T方向),y为负法向(或法向,根据定义)?这里需要统一。
常见的对地定向卫星的本体坐标系定义:
- x: 飞行方向(速度方向,即T方向)
- y: 负法向(-N方向)
- z: 径向向外(R方向)?但是这样z就背离地球了,所以相机指向应该是负z(-z)方向,即指向地球。
但实际上,更常见的定义是:
- 卫星本体坐标系:
z轴:指向地心(即-R方向)
x轴:沿速度方向(即T方向)
y轴:构成右手系(即N方向,轨道法向)
因此,理想情况下,卫星本体坐标系与轨道坐标系的关系为:
[x_body, y_body, z_body] = [T, N, -R]
那么,从轨道坐标系(R, T, N)到卫星本体坐标系(x_body, y_body, z_body)的变换矩阵为:
| 0 1 0 |
| 0 0 1 |
|-1 0 0 | (注意:这里需要验证)
但更一般地,我们可以通过三个欧拉角(偏航、俯仰、滚动)来构建旋转矩阵。如果我们有姿态角(ψ, θ, φ),则旋转矩阵为:
R = R_z(ψ) * R_y(θ) * R_x(φ)
注意:这个旋转矩阵是将轨道坐标系的矢量转换到本体坐标系。
然而,由于标准不同,我们可能需要调整旋转顺序和轴的定义。
由于libsdp4库不直接提供姿态计算,我们需要自己实现姿态角的设定(通常来自任务需求或预设值)。因此,要计算姿态偏离角等,我们需要额外的姿态角输入。
因此,问题可以拆解为:
1. 使用libsdp4计算卫星在某一时刻的位置和速度(ECI坐标系)。
2. 计算轨道坐标系的三个轴(R, T, N)。
3. 根据给定的姿态角(用户输入或预设)构建从轨道坐标系到本体坐标系的旋转矩阵。
4. 计算相机光轴在本体坐标系中的方向(通常为[0,0,-1]),然后通过旋转矩阵转换到ECI坐标系(或直接计算与天底方向的夹角)。
具体步骤:
1. 计算位置矢量r和速度矢量v(ECI坐标系)。
2. 计算单位矢量:
R = r / |r|
W = r × v (角动量方向)
N = W / |W|
T = N × R (注意:这里T是单位矢量)
3. 轨道坐标系到ECI坐标系的变换矩阵(3×3):
M_orbit_to_eci = [R, T, N] (列向量)
4. 给定姿态角(滚动φ, 俯仰θ, 偏航ψ),构建从轨道坐标系到本体坐标系的旋转矩阵R_orbit_to_body(3×3)。
旋转顺序:通常采用3-2-1(偏航-俯仰-滚动)顺序,但注意轴的定义。假设:
- 偏航ψ绕轨道坐标系的z轴(N)旋转
- 俯仰θ绕旋转后的y轴(T')旋转
- 滚动φ绕旋转后的x轴(R'')旋转
旋转矩阵:
R_orbit_to_body = R_x(φ) * R_y(θ) * R_z(ψ)
其中,绕z轴旋转ψ的矩阵:
| cosψ -sinψ 0 |
| sinψ cosψ 0 |
| 0 0 1 |
绕y轴旋转θ的矩阵:
| cosθ 0 sinθ |
| 0 1 0 |
|-sinθ 0 cosθ |
绕x轴旋转φ的矩阵:
|1 0 0 |
|0 cosφ -sinφ|
|0 sinφ cosφ|
注意:这个旋转矩阵是将轨道坐标系中的矢量转换到本体坐标系。
5. 将轨道坐标系中的矢量(如天底方向在轨道坐标系中为[0,0,-1]?)转换到本体坐标系:
在轨道坐标系中,天底方向(指向地心)是 -R,即矢量在轨道坐标系中表示为[ -1, 0, 0 ](因为R轴是轨道坐标系的第一个轴)?不对,轨道坐标系中,R轴是径向,所以地心方向是-R,即矢量表示为[-1,0,0](在轨道坐标系中)。
但实际上,在轨道坐标系中,任意矢量可以表示为(R分量, T分量, N分量)。所以天底方向矢量在轨道坐标系中为:[-1, 0, 0](即-R方向)。
6. 将天底方向矢量从轨道坐标系转换到本体坐标系:
v_body = R_orbit_to_body * v_orbit
其中 v_orbit = [-1, 0, 0]^T
7. 相机光轴在本体坐标系中的方向是固定的,假设为[0,0,-1](即z轴负方向)。
8. 计算姿态偏离角θ(即相机光轴与天底方向的夹角):
cosθ = (v_camera_body · v_nadir_body) / (|v_camera_body| * |v_nadir_body|)
其中,v_camera_body = [0,0,-1](单位矢量),v_nadir_body是上一步计算得到的矢量(也是单位矢量?需要归一化吗?旋转矩阵是正交的,所以模长不变,因此v_nadir_body的模长还是1,因为v_orbit是单位矢量?)
但是注意:我们计算的是相机光轴与天底方向在本体坐标系中的夹角?实际上,这两个矢量都在本体坐标系中,所以可以直接点积。
9. 另外,侧摆角、俯仰角、偏航角就是我们给定的φ, θ, ψ。
因此,整个流程需要用户提供这三个角度。如果用户没有提供,那么默认都是0(理想对地定向),则v_nadir_body = R_orbit_to_body * [-1,0,0]^T = [1,0,0](因为旋转矩阵是单位阵)?不对,当旋转矩阵为单位阵时:
v_nadir_body = [-1,0,0] (即本体坐标系的x轴负方向)
而相机光轴是[0,0,-1](z轴负方向),所以点积为0,夹角90度?这显然不对。
问题出在哪里?我们期望在理想对地定向时,相机光轴指向天底方向,而天底方向在本体坐标系中应该是[0,0,-1](即与相机光轴重合)。所以,在理想情况下,我们期望旋转矩阵R_orbit_to_body将轨道坐标系中的天底方向[-1,0,0]变换为本体坐标系中的[0,0,-1]。
因此,我们需要一个固定的旋转矩阵将轨道坐标系转换到本体坐标系(理想情况):
轨道坐标系的R轴([1,0,0])应该对应本体坐标系的负z轴([0,0,-1])
轨道坐标系的T轴([0,1,0])应该对应本体坐标系的x轴([1,0,0])
轨道坐标系的N轴([0,0,1])应该对应本体坐标系的y轴([0,1,0])
所以,变换矩阵为:
| 0 1 0 | // T轴(轨道坐标系的y轴)变为本体坐标系的x轴(第一列)
| 0 0 1 | // N轴(轨道坐标系的z轴)变为本体坐标系的y轴(第二列)
|-1 0 0 | // R轴(轨道坐标系的x轴)变为本体坐标系的z轴,但取反(因为R轴向外,而本体z轴向内)
验证:轨道坐标系中的矢量[-1,0,0](天底方向)变换到本体坐标系:
[0,1,0; 0,0,1; -1,0,0] * [-1,0,0]^T = [0,0,1]^T (注意:矩阵乘法,列向量)
但是,我们期望的是[0,0,-1](因为相机指向负z),所以这里我们得到的是[0,0,1](即z轴正方向),与期望相反。
因此,我们需要将轨道坐标系中的天底方向[ -1, 0, 0 ]变换为[0,0,-1]。所以,我们可以调整变换矩阵:
| 0 1 0 |
| 0 0 1 |
| 1 0 0 | // 这样变换:[-1,0,0] -> [0,0,-1] (因为第三行是[1,0,0]·[-1,0,0]^T = -1,但我们需要-1在z轴负方向,所以取负?)
或者,我们保持之前的变换矩阵,然后对z轴取反?但这样会破坏坐标系的右手性。
实际上,更常见的做法是定义本体坐标系的z轴指向地心(即-R),那么轨道坐标系中的R轴(径向向外)变换到本体坐标系就是z轴的负方向?不对,应该是z轴的正方向(如果我们定义z轴指向地心,那么轨道坐标系的R轴(径向向外)与本体坐标系的z轴方向相反,所以变换关系为:z_body = -R)。
因此,变换矩阵可以这样构建:
本体坐标系的x轴(飞行方向) = T(轨道坐标系的T轴)
本体坐标系的y轴(轨道法向) = N(轨道坐标系的N轴)
本体坐标系的z轴(指向地心) = -R
所以,从轨道坐标系到本体坐标系的变换矩阵为:
[ T, N, -R ] (每一列是轨道坐标系中的矢量在本体坐标系中的表示?不对,变换矩阵的列向量是轨道坐标系各轴在本体坐标系中的分量)
实际上,变换矩阵的每一列是轨道坐标系基矢量在本体坐标系中的分量:
轨道坐标系的R轴([1,0,0])在本体坐标系中表示为[0,0,-1]^T(因为z_body=-R)
轨道坐标系的T轴([0,1,0])在本体坐标系中表示为[1,0,0]^T(x_body=T)
轨道坐标系的N轴([0,0,1])在本体坐标系中表示为[0,1,0]^T(y_body=N)
因此,变换矩阵(将轨道坐标系的矢量转换到本体坐标系):
M = [ [0,1,0], // R轴在本体坐标系中的分量(x,y,z)-> (0,1,0)? 不对,上面分析R轴对应z轴负方向,即(0,0,-1)
[0,0,1], // T轴对应x轴(1,0,0)?不对
[ -1,0,0 ] ] // N轴对应y轴(0,1,0)
这个矩阵不对。正确的做法是:设有一个轨道坐标系中的矢量v_orbit = (vR, vT, vN),那么它在本体坐标系中的表示为:
v_body = vT * x_body + vN * y_body + (-vR) * z_body
= vT * [1,0,0] + vN * [0,1,0] + (-vR) * [0,0,1]
= [vT, vN, -vR]
所以,变换矩阵为:
| 0 0 -1 |
| 1 0 0 | // 这样写不对,因为矩阵乘法是行乘以列
实际上,变换矩阵M满足:v_body = M * v_orbit
v_body_x = vT -> M的第一行: [0, 1, 0] (因为vR的系数为0,vT的系数为1,vN的系数为0)
v_body_y = vN -> M的第二行: [0, 0, 1]
v_body_z = -vR -> M的第三行: [-1,0,0]
所以,M = [ [0, 1, 0],
[0, 0, 1],
[-1,0,0] ]
验证:轨道坐标系中的矢量[1,0,0](R轴)变换后:[0,0,1]^T? 不对,应该是[0,0,-1]^T?按矩阵乘法:
v_body_x = 0*1 + 1*0 + 0*0 = 0
v_body_y = 0*1 + 0*0 + 1*0 = 0
v_body_z = -1*1 +0*0+0*0 = -1 -> [0,0,-1]^T 正确(即本体坐标系的z轴负方向,但注意:我们定义本体坐标系的z轴指向地心,所以z轴正方向指向地心,而相机通常指向z轴负方向?不对,相机指向应该与z轴正方向一致?)
这里出现混淆:我们定义本体坐标系的z轴指向地心(正方向指向地心),那么相机光轴方向就是z轴正方向(指向地心)。那么,在理想情况下,相机光轴方向就是z_body = [0,0,1](单位矢量)。
所以,天底方向就是[0,0,1](本体坐标系),而轨道坐标系中的天底方向矢量(-R)经过变换后得到[0,0,-1](本体坐标系)?这不对,我们期望变换后是[0,0,1]。
因此,我们需要调整:在轨道坐标系中,天底方向矢量是 -R,即矢量[-1,0,0](在轨道坐标系中表示)。用变换矩阵M作用:
v_body = M * [-1,0,0]^T = [0,0,1]^T -> [0,0,1] 这正是我们期望的(指向地心)。
所以,变换矩阵M将轨道坐标系的矢量转换到本体坐标系,并且有:
M = [ [0, 1, 0],
[0, 0, 1],
[1, 0, 0] ] // 第三行改为[1,0,0]? 不对,上面计算第三行是[-1,0,0]得到[0,0,1](即[-1,0,0]变换后为[0,0,-(-1)]=[0,0,1])?不对,矩阵乘法:
重新用矩阵乘法计算:
M = |0 1 0|
|0 0 1|
|-1 0 0|
M * [ -1, 0, 0 ]^T = [0* (-1) +1*0+0*0, 0*(-1)+0*0+1*0, -1*(-1)+0*0+0*0]^T = [0,0,1]^T 正确。
所以,这个变换矩阵是正确的。
因此,在理想情况下(无姿态角),从轨道坐标系到本体坐标系的旋转矩阵为:
R0 = |0 1 0|
|0 0 1|
|-1 0 0|
现在,如果我们有姿态角(ψ, θ, φ),那么我们在轨道坐标系到本体坐标系的变换中再加入一个旋转(即姿态调整),这个旋转发生在轨道坐标系中,然后我们再用R0变换到本体坐标系?或者,我们先将姿态旋转作用在轨道坐标系上,然后再用R0变换。
但更常见的做法是:姿态角定义的旋转是相对于轨道坐标系的,所以先做姿态旋转(得到一个新的中间坐标系),然后用R0变换到本体坐标系。但这样就需要两个旋转矩阵。
另一种做法:将姿态旋转直接与R0结合,即总的旋转矩阵为 R_total = R0 * R_attitude,其中R_attitude是将轨道坐标系旋转到中间坐标系的矩阵(由ψ, θ, φ确定),而R0将中间坐标系变换到本体坐标系?但这里我们并没有中间坐标系。
实际上,我们可以将姿态角理解为卫星本体坐标系相对于轨道坐标系的旋转。所以,总的旋转矩阵就是 R_orbit_to_body = R0 * R_attitude?但R0已经是一个固定的旋转矩阵,而R_attitude是相对于轨道坐标系的旋转,所以R_attitude作用后得到中间坐标系,然后R0将中间坐标系变换到本体坐标系。然而,我们通常将R0视为本体坐标系的定义(即理想情况下的变换),而姿态角是相对于理想情况的偏离。
因此,我们可以这样定义:
R_orbit_to_body = R_attitude * R0
其中,R0是理想情况下的变换矩阵(3×3),R_attitude是姿态旋转矩阵(相对于轨道坐标系,按照给定的欧拉角旋转)。
注意:这里的R_attitude是相对于轨道坐标系的旋转,所以它作用在轨道坐标系的矢量上,然后R0再作用,得到本体坐标系。
或者,我们可以将欧拉角旋转直接作用在轨道坐标系上,然后通过R0变换到本体坐标系,即:
v_body = R0 * (R_attitude * v_orbit)
由于矩阵乘法结合律,也可以写成 v_body = (R0 * R_attitude) * v_orbit
所以,总的旋转矩阵为 R_total = R0 * R_attitude
其中,R_attitude由欧拉角(ψ, θ, φ)按照旋转顺序(如ZYX,即先偏航,再俯仰,再滚动)构建的旋转矩阵。
因此,步骤为:
a. 根据欧拉角(ψ, θ, φ)和旋转顺序(ZYX)构建旋转矩阵R_attitude(3×3)。
b. 计算R0 = [[0,1,0],[0,0,1],[-1,0,0]]
c. R_total = R0 * R_attitude (注意:矩阵乘法顺序)
d. 将天底方向矢量(在轨道坐标系中为[-1,0,0]^T)通过R_total变换到本体坐标系:
v_nadir_body = R_total * [-1,0,0]^T
e. 相机光轴方向在本体坐标系中为[0,0,1]^T(指向地心,注意:这里我们定义相机光轴与z轴正方向一致)。
f. 计算姿态偏离角θ = arccos( v_nadir_body · [0,0,1]^T ) (注意:v_nadir_body和[0,0,1]都是单位矢量)
但是,这里有一个问题:在理想情况下(无姿态角),R_attitude是单位阵,那么:
v_nadir_body = R0 * [-1,0,0]^T = [0,0,1]^T
点积: [0,0,1] · [0,0,1] = 1,θ=0,正确。
如果有一个滚动角φ(绕x轴旋转),那么R_attitude = R_x(φ)(注意:这里旋转顺序,我们假设旋转顺序是XYZ,但之前说ZYX,这里需要统一)。
由于我们采用旋转顺序:偏航(ψ绕Z)、俯仰(θ绕Y)、滚动(φ绕X),那么旋转矩阵R_attitude = R_x(φ) * R_y(θ) * R_z(ψ) (注意:从右到左,先旋转的写在右边)。
因此,R_attitude = R_z(ψ) * R_y(θ) * R_x(φ) // 还是 R_attitude = R_x(φ) * R_y(θ) * R_z(ψ) ?
通常,按照从本体坐标系到轨道坐标系的旋转顺序(或者反之),我们使用相反的乘法顺序。对于从轨道坐标系旋转到中间坐标系,我们按照给定的欧拉角顺序,先进行偏航旋转(绕Z),然后俯仰(绕Y),然后滚动(绕X)。那么,旋转矩阵为:
R_attitude = R_x(φ) * R_y(θ) * R_z(ψ)
注意:这个矩阵是将轨道坐标系的矢量先绕Z轴旋转ψ,然后绕新的Y轴旋转θ,然后绕新的X轴旋转φ。
因此,总的旋转矩阵:R_total = R0 * R_attitude = R0 * (R_x(φ) * R_y(θ) * R_z(ψ))
现在,我们可以在C++中实现:
1. 使用libsdp4计算卫星的位置和速度(ECI坐标系)。
2. 计算轨道坐标系的三个单位向量(R, T, N)。
3. 构建R0矩阵(固定)。
4. 根据给定的欧拉角(ψ, θ, φ)构建旋转矩阵R_attitude(注意:角度单位,通常为弧度)。
5. 计算R_total = R0 * R_attitude
6. 计算天底方向在轨道坐标系中的矢量:[-1,0,0]^T,然后乘以R_total得到v_nadir_body。
7. 计算相机光轴方向矢量[0,0,1]^T与v_nadir_body的点积,然后求反余弦得到姿态偏离角。
另外,侧摆角、俯仰角、偏航角就是给定的φ, θ, ψ。
注意:欧拉角有万向节死锁问题,这里不展开。
由于libsdp4库的具体使用需要包含头文件和链接库,下面给出一个示例框架:
步骤1:安装libsdp4
- 可以从https://www.danrw.com/satellites/sgp4/ 或 https://github.com/cbassa/libsgp4 获取(注意:libsdp4是SDP4模型的实现,而SGP4是用于近地卫星的,SDP4用于深空卫星)。但注意,这里我们使用C++版本,我推荐使用libsgp4(它同时实现了SGP4和SDP4)。
步骤2:编写代码
示例代码框架:
```cpp
#include <iostream>
#include <iomanip>
#include <cmath>
#include "sgp4/CoordTopocentric.h"
#include "sgp4/CoordGeodetic.h"
#include "sgp4/Observer.h"
#include "sgp4/DateTime.h"
#include "sgp4/Eci.h"
#include "sgp4/Tle.h"
#include "sgp4/SGP4.h"
using namespace std;
// 构建绕X轴旋转的矩阵
Matrix3x3 rotationX(double angle) {
double c = cos(angle);
double s = sin(angle);
return Matrix3x3(1, 0, 0,
0, c, -s,
0, s, c);
}
// 绕Y轴旋转
Matrix3x3 rotationY(double angle) {
double c = cos(angle);
double s = sin(angle);
return Matrix3x3(c, 0, s,
0, 1, 0,
-s, 0, c);
}
// 绕Z轴旋转
Matrix3x3 rotationZ(double angle) {
double c = cos(angle);
double s = sin(angle);
return Matrix3x3(c, -s, 0,
s, c, 0,
0, 0, 1);
}
int main() {
// 示例TLE
string line1 = "1 25544U 98067A 20232.51013889 .00000000 00000-0 00000-0 0 9999";
string line2 = "2 25544 51.6440 322.0970 0003980 52.6685 63.1486 15.49507818 10001";
// 创建TLE对象
Tle tle("ISS (ZARYA)", line1, line2);
SGP4 sgp4(tle);
// 设置时间
DateTime time(2020, 8, 20, 12, 0, 0); // 示例时间
// 计算卫星位置和速度
Eci eci = sgp4.FindPosition(time);
// 获取位置矢量(km)和速度矢量(km/s)
Vector r = eci.Position(); // 位置矢量(km)
Vector v = eci.Velocity(); // 速度矢量(km/s)
// 1. 计算轨道坐标系 (RTN)
Vector R = r.Normalize(); // 径向单位矢量
Vector W = r.Cross(v); // 角动量方向
Vector N = W.Normalize(); // 法向单位矢量
Vector T = N.Cross(R); // 横向单位矢量(注意:已经单位化)
// 2. 构建R0矩阵(轨道坐标系到本体坐标系的理想变换)
Matrix3x3 R0(0, 1, 0,
0, 0, 1,
-1, 0, 0);
// 3. 设定姿态角(弧度)
double yaw = 0.0; // 偏航角(绕Z轴)
double pitch = 0.0; // 俯仰角(绕Y轴)
double roll = 0.0; // 滚动角(绕X轴)
// 4. 构建姿态旋转矩阵(按照Z-Y-X顺序,即R_attitude = R_x(roll) * R_y(pitch) * R_z(yaw))
Matrix3x3 R_att = rotationX(roll) * rotationY(pitch) * rotationZ(yaw);
// 5. 总旋转矩阵
Matrix3x3 R_total = R0 * R_att;
// 6. 天底方向在轨道坐标系中的表示
Vector nadir_orbit(-1, 0, 0); // 单位矢量
// 变换到本体坐标系
Vector nadir_body = R_total * nadir_orbit;
// 7. 相机光轴方向在本体坐标系中的表示(指向地心)
Vector camera_body(0, 0, 1); // 单位矢量
// 8. 计算夹角(姿态偏离角)
double dot = nadir_body.Dot(camera_body);
double theta = acos(dot) * 180.0 / M_PI; // 转换为度
cout << "姿态偏离角: " << theta << " 度" << endl;
cout << "侧摆角: " << roll * 180.0 / M_PI << " 度" << endl;
cout << "俯仰角: " << pitch * 180.0 / M_PI << " 度" << endl;
cout << "偏航角: " << yaw * 180.0 / M_PI << " 度" << endl;
return 0;
}
```
注意:以上代码中,Matrix3x3和Vector类需要定义。libsgp4库中可能没有提供矩阵类,需要自己实现或使用其他线性代数库。
由于libsgp4库中可能没有提供矩阵类,我们可以自己实现一个简单的3x3矩阵和3D向量类,并重载乘法运算符。
这里提供一个简单的实现:
```cpp
class Vector {
public:
double x, y, z;
Vector() : x(0), y(0), z(0) {}
Vector(double x_, double y_, double z_) : x(x_), y(y_), z(z_) {}
double Dot(const Vector& v) const {
return x*v.x + y*v.y + z*v.z;
}
Vector Cross(const Vector& v) const {
return Vector(y*v.z - z*v.y, z*v.x - x*v.z, x*v.y - y*v.x);
}
double Magnitude() const {
return sqrt(x*x + y*y + z*z);
}
Vector Normalize() const {
double mag = Magnitude();
return Vector(x/mag, y/mag, z/mag);
}
};
class Matrix3x3 {
public:
double m[3][3];
Matrix3x3() {
for (int i=0; i<3; i++)
for (int j=0; j<3; j++)
m[i][j] = (i==j) ? 1.0 : 0.0;
}
Matrix3x3(double a11, double a12, double a13,
double a21, double a22, double a23,
double a31, double a32, double a33) {
m[0][0]=a11; m[0][1]=a12; m[0][2]=a13;
m[1][0]=a21; m[1][1]=a22; m[1][2]=a23;
m[2][0]=a31; m[2][1]=a32; m[2][2]=a33;
}
Vector operator*(const Vector& v) const {
return Vector(m[0][0]*v.x + m[0][1]*v.y + m[0][2]*v.z,
m[1][0]*v.x + m[1][1]*v.y + m[1][2]*v.z,
m[2][0]*v.x + m[2][1]*v.y + m[2][2]*v.z);
}
Matrix3x3 operator*(const Matrix3x3& mat) const {
Matrix3x3 res;
for (int i=0; i<3; i++) {
for (int j=0; j<3; j++) {
res.m[i][j] = 0;
for (int k=0; k<3; k++) {
res.m[i][j] += m[i][k] * mat.m[k][j];
}
}
}
return res;
}
};
```
注意:以上代码仅作为示例,实际使用时需要与libsgp4库的Vector类兼容。libsgp4库中可能已经提供了Vector类,但可能没有矩阵类,所以需要自己实现矩阵类。
总结:使用libsdp4(或libsgp4)计算卫星位置和速度,然后根据轨道坐标系和给定的姿态角(侧摆、俯仰、偏航)计算姿态偏离角。其中,姿态角需要作为输入参数,因为TLE不提供姿态信息。
如果卫星的姿态角未知,那么无法计算姿态偏离角。在实际应用中,卫星的姿态角由卫星的姿态确定与控制系统提供,通常通过遥测下传。
因此,本方法适用于已知卫星姿态角(例如预设的成像侧摆角)的情况。
相关问题:
1. 如何从卫星的星敏感器数据计算姿态角?
2. 卫星轨道坐标系(RTN)的详细定义是什么?
3. 在卫星成像任务中,姿态角(滚动、俯仰、偏航)如何影响成像质量?
4. 如何利用卫星轨道参数和姿态角计算地面目标的成像时间?</think>### 使用libsdp4库计算卫星姿态参数
在C++中使用libsdp4库(或兼容的SGP4/SDP4实现)计算卫星姿态参数的核心步骤和关键公式如下。需要特别注意:**TLE数据仅提供轨道信息,姿态角(侧摆、俯仰、偏航)需要额外输入或预设模型**,因为TLE不包含姿态数据。
---
### 核心计算步骤
#### 1. **初始化轨道模型**
```cpp
#include <sgp4/SGP4.h>
#include <sgp4/Vector.h>
#include <sgp4/Matrix3x3.h>
// 解析TLE数据
Tle tle("ISS",
"1 25544U 98067A 20232.51013889 .00000000 00000-0 00000-0 0 9999",
"2 25544 51.6440 322.0970 0003980 52.6685 63.1486 15.49507818 10001");
SGP4 sgp4(tle);
// 设置计算时间(UTC)
DateTime time(2023, 8, 20, 12, 0, 0);
```
#### 2. **计算位置和速度(ECI坐标系)**
```cpp
Eci eci = sgp4.FindPosition(time);
Vector position = eci.Position(); // 位置矢量 (km)
Vector velocity = eci.Velocity(); // 速度矢量 (km/s)
```
#### 3. **计算轨道坐标系(RTN)**
```cpp
Vector R = position.Normalize(); // 径向 (指向地心)
Vector W = position.Cross(velocity).Normalize(); // 角动量方向
Vector N = W; // 法向 (轨道面法向量)
Vector T = N.Cross(R).Normalize();// 横向 (运动方向)
```
#### 4. **定义姿态角(需预设或输入)**
```cpp
double roll_deg = 10.0; // 侧摆角(绕T轴旋转)
double pitch_deg = 0.0; // 俯仰角(绕N轴旋转)
double yaw_deg = 0.0; // 偏航角(绕R轴旋转)
```
#### 5. **构建姿态旋转矩阵**
```cpp
#include <cmath>
// 欧拉角转旋转矩阵 (Z-Y-X顺序)
auto rotMat = [](double roll, double pitch, double yaw) -> Matrix3x3 {
double cr = cos(roll), sr = sin(roll);
double cp = cos(pitch), sp = sin(pitch);
double cy = cos(yaw), sy = sin(yaw);
return Matrix3x3(
cy*cp, cy*sp*sr - sy*cr, cy*sp*cr + sy*sr,
sy*cp, sy*sp*sr + cy*cr, sy*sp*cr - cy*sr,
-sp, cp*sr, cp*cr
);
};
Matrix3x3 R_att = rotMat(
roll_deg * M_PI/180.0,
pitch_deg * M_PI/180.0,
yaw_deg * M_PI/180.0
);
```
#### 6. **计算姿态偏离角**
$$ \theta = \arccos\left( \frac{\vec{d} \cdot \vec{n}}{|\vec{d}| \cdot |\vec{n}|} \right) $$
```cpp
// 理想天底方向 (地心方向)
Vector nadir_ideal = -R; // 单位矢量
// 实际相机指向 (本体坐标系Z轴)
Vector camera_body(0, 0, 1);
Vector camera_eci = R_att * camera_body;
// 计算夹角
double dot = camera_eci.Dot(nadir_ideal);
double theta_rad = acos(dot);
double theta_deg = theta_rad * 180.0 / M_PI;
```
#### 7. **完整示例输出**
```cpp
std::cout << "轨道高度: " << position.Magnitude() << " km\n";
std::cout << "侧摆角: " << roll_deg << "°\n";
std::cout << "俯仰角: " << pitch_deg << "°\n";
std::cout << "偏航角: " << yaw_deg << "°\n";
std::cout << "姿态偏离角: " << theta_deg << "°\n";
```
---
### 关键公式说明
1. **轨道坐标系定义**
- $ \vec{R} = \frac{\vec{r}}{|\vec{r}|} $(径向:指向地心)
- $ \vec{N} = \frac{\vec{r} \times \vec{v}}{|\vec{r} \times \vec{v}|} $(法向:轨道面法线)
- $ \vec{T} = \vec{N} \times \vec{R} $(横向:速度方向)
2. **姿态旋转矩阵**
$$ R_{\text{att}} = R_z(\psi) R_y(\theta) R_x(\phi) $$
其中 $\phi, \theta, \psi$ 分别为滚动、俯仰、偏航角。
3. **姿态偏离角计算**
- 理想天底方向:$ \vec{n} = -\vec{R} $
- 实际相机指向:$ \vec{d} = R_{\text{att}} \cdot [0,0,1]^T $
- 偏离角:$ \theta = \arccos(\vec{d} \cdot \vec{n}) $
---
### 注意事项
1. **姿态角来源**:
实际工程中需通过星敏感器或陀螺仪获取姿态角[^1]。若仅做仿真,可预设典型值:
- 侧摆角:±30°(用于扩大成像范围)
- 俯仰/偏航角:通常<0.1°(高精度对地定向)
2. **精度影响因素**:
- TLE轨道误差:100-1000米
- 时间同步误差:1ms时间误差 → 7米位置误差[^2]
- 姿态测量误差:0.01°姿态角 → 10米地面误差(500km轨道)
3. **地球自转校正**:
需将ECI坐标转到ECEF坐标系:
$$ \theta_G = \theta_{G0} + \omega_E \cdot \Delta t $$
其中 $\omega_E = 7.292115 \times 10^{-5}$ rad/s 为地球自转角速度。
---
### 扩展实现建议
1. **使用专业库**:推荐[CelesTrak SGP4](https://celestrak.org/software/sgp4/)或[Orekit](https://www.orekit.org/)(Java)提高精度
2. **实时校正**:融合GNSS数据减少TLE误差
3. **可视化**:用VTK或OpenGL渲染卫星指向和地面轨迹