본문 바로가기
항공우주/우주역학

섭동력을 받는 램버트 문제의 보정 해

by 깊은대학 2024. 4. 12.

램버트 문제의 해(https://pasus.tistory.com/316)는 두 위치 r1r2 사이를 비행하는 데 걸리는 시간 Δt 가 주어졌을 때, 두 위치를 연결하는 이체문제 (two-body problem) 궤적(trajectory)을 계산한다. 하지만 램버트 문제에서 고려하지 않았던 섭동력(perturbation)으로 인하여 궤적이 목표로 한 위치 r2 에 도달하지 못할 때는 어떻게 해야 할까.

 

 

일반적인 섭동력 (J2 섭동력, 태양복사압력, 항력, 달 또는 태양 등의 제3의 중력 등)의 경우 이러한 오차 거리(miss distance)가 크지 않기 때문에, 출발 위치 r1 에서 램버트 솔버가 산출한 속도벡터 v1 을 보정하여 최종 위치 r2 에서 오차 거리를 줄일 수 있다. 이와 같은 방법을 슈팅방법(shooting method)이라고 한다(https://pasus.tistory.com/276).

슈팅방법은 기본적으로 경계값 문제를 초기값 문제로 바꾸어 해를 구하는 방법으로서, 초기값으로부터 산출된 최종값과 목표값 간의 차이를 이용하여 원래 초기값을 수정한다.

최종 위치 오차 Δr2 는 출발 위치에서의 속도벡터 섭동 Δv1 을 이용하여 다음과 같이 근사적으로 표현할 수 있다.

 

(1)Δr2=dr2dv1Δv1

 

여기서 미분 dr2dv1 는 자코비안(Jacobian)으로서 3×3 행렬인데, 출발 위치에서의 초기 속도변화에 대한 최종 위치의 변화를 나타내므로 '민감도' 행렬이라고도 한다.

식 (1)에 의하면 최종 위치벡터 오차에 대한 초기 속도벡터 오차 Δv1 은 다음과 같이 추정할 수 있다.

 

(2)Δv1=(dr2dv1)1Δr2

 

추정된 속도벡터 오차를 속도벡터 v1 에서 빼주면 v1 을 새로운 값으로 업데이트할 수 있다.

 

(3)v1v1Δv1=v1(dr2dv1)1Δr2

 

식 (2)의 자코비안 또는 민감도 행렬 dr2dv1 의 구성 성분을 쓰면 다음과 같다.

 

(5)dr2dv1=J=[x2u1x2v1x2w1y2u1y2v1y2w1z2u1z2v1z2w1]

 

여기서 r2=[x2y2z2]T, v1=[u1v1w1]T 로서 각 벡터를 ECI 좌표계로 표현한 것이다.

이제 J2 섭동력 하에서도 궤적이 시간 Δt 후에 목표 위치 r2 로 정확히 도달할 수 있도록, 램버트가 산출한 초기 속도벡터 v1 을 슈팅방법을 이용하여 보정해 보도록 하겠다.

J2 섭동력을 포함한 램버트 문제는 다음과 같다.

 

(5)d2rdt2+μr3r=apap=32J2μr5Re2[x(5(zr)21)y(5(zr)21)z(5(zr)23)]r(t1)=r1,   r(t2)=r2,   Δt=t2t1

 

여기서 r=[xyz]T, r=|r|, J2=0.0010826269 이며 ap 는 J2 섭동 가속도이다.

식 (4)에 의하면 자코비안 J 는 유한 차분을 사용하여 한 번에 한 열씩 수치적으로 계산할 수 있다. 방법은 다음과 같다.

J 의 첫번째 열을 계산하기 위하여 v1 의 첫번째 성분에만 +Δ 만큼의 조그마한 (예를 들면 0.01km/s) 섭동을 준다.

 

(6)v1+=v1+[+Δ00]

 

그리고 r1v1+ 을 초기 조건으로 삼아 식 (5)를 수치적분하여 시간 Δt 후의 위치벡터 r2+ 를 계산한다. 이 단계를 슈팅 단계라고 한다.

그런 후 이번에는 v1 의 첫번째 성분에 반대 방향으로 Δ 만큼의 섭동을 준다.

 

(7)v1+=v1+[Δ00]

 

마찬가지로 r1v1 을 초기 조건으로 삼아 식 (4)를 수치적분하여 시간 Δt 후의 위치벡터 r2 를 계산한다. 그러면 자코비안 j 의 첫번째 열을 다음과 같이 계산할 수 있다.

 

(8)J1=r2+r22Δ

 

동일한 방법으로 v1 의 두번째 성분과 세번째 성분에만 각각 동일한 섭동을 주어서 자코비안 J 의 두번째 열과 세번째 열을 계산하면 자코비안 J 의 전체 행렬을 구할 수 있다.

 

 

자코비안을 구한 후에는 식 (3)으로 초기 속도벡터를 보정하면 된다. 그리고 다시 새로운 속도벡터를 이용하여 자코비안을 다시 계산하고 속도벡터를 보정하는 프로세스를 오차 거리가 일정 수준 이하로 작아질 때 까지 계속 반복한다.

 

 

수치 예를 들어보자. 램버트 문제의 경계조건은 다음과 같다.

 

(9)r(t1)=r1=[5,00010,0002,100]T  kmr(t2)=r2=[14,6002,5007,000]T  kmΔt=t2t1=3,600 sec  (1 hr)

 

위 문제를 범용변수 기반 램버트 알고리즘(https://pasus.tistory.com/316)으로 푼 초기 속도는 다음과 같다.

 

(10)v(t1)=v1=[5.9924946396663941.9253634152808933.245636528490488]  km/s

 

식 (5)에서 ap=0 (no J2) 으로 하고 r1v1 을 초기 조건으로 하여 최종 위치벡터를 계산하고 오차 거리를 구하면 5.954239×109 m 로서 램버트 솔버가 매우 정확한 해를 구한 것을 알 수 있다. 하지만 J2를 고려했을 때, 동일한 초기 조건으로 최종 위치벡터를 계산하고 오차 거리를 구하면 538.5933 m 로서 오차 거리가 상당히 커진 것을 볼 수 있다.

이 오차 거리를 감소시키기 위하여 초기 속도벡터를 슈팅 방법으로 보정하면 결과는 다음과 같다. 우선 3회의 반복횟수 만에 초기 속도벡터가 다음 값으로 수정되었다.

 

(11)v1corrected=[5.9923595266703241.9242155307238043.246086959795341]  km/s

 

보정된 속도와 원래 속도의 크기 차이는 0.1240477 m/s 에 불과할 만큼 매우 작다. 하지만 이러한 속도 차이가 상당한 크기의 오차 거리를 유발한 것이다.

이제 보정된 속도벡터를 초기값으로 하여 최종 위치벡터를 계산하고 오차 거리를 구하면 2.746990×109 m 로서 슈팅 방법으로 수행한 보정이 매우 정확하다는 것을 알 수 있다.

다음은 자코비안 행렬 (또는 민감도 행렬)을 계산하는 알고리즘을 매트랩 코드로 구현한 것이다.

 

jacobian_shooting.m

 

function J = jacobian_shooting(odefun, y0ini, tof, dv)

% J = jacobian_shooting(odefun, y0ini, tof, dv)
%        given odefun, y0in, tof, dv
%        compute 3x3 Jacobian for shooting method
%       
% Input  odefun: ODE function
%        y0ini: initial position and velocity vector in km, km/s
%             = [x y z vx vy vz]'
%        tof: time of flight in sec
%        dv: small velocity amoint used for finite differences in km/s
% Output J: Jacobian
%
% all in inertial frame (ECI)
%
% coded by st.watermelon

J = zeros(3, 3);

tspan = [0:tof/100:tof]';
OPTIONS = odeset('RelTol',3e-14,'AbsTol',1e-15);

% finite differences with +/- vx
y0 = y0ini + [0 0 0 +dv 0 0]';
[t,y] = ode113(odefun, tspan, y0, OPTIONS);
rplus = y(end,1:3)';
y0 = y0ini + [0 0 0 -dv 0 0]';
[t,y] = ode113(odefun, tspan, y0, OPTIONS);
rminus = y(end,1:3)';
J(:, 1) = (rplus-rminus)/(2*dv);

% finite differences with +/- vy
y0 = y0ini + [0 0 0 0 +dv 0]';
[t,y] = ode113(odefun, tspan, y0, OPTIONS);
rplus = y(end,1:3)';
y0 = y0ini + [0 0 0 0 -dv 0]';
[t,y] = ode113(odefun, tspan, y0, OPTIONS);
rminus = y(end,1:3)';
J(:, 2) = (rplus-rminus)/(2*dv);

% finite differences with +/- vz
y0 = y0ini + [0 0 0 0 0 +dv]';
[t,y] = ode113(odefun, tspan, y0, OPTIONS);
rplus = y(end,1:3)';
y0 = y0ini + [0 0 0 0 0 -dv]';
[t,y] = ode113(odefun, tspan, y0, OPTIONS);
rminus = y(end,1:3)';
J(:, 3) = (rplus-rminus)/(2*dv);

 

다음은 식 (5)의 운동 방정식을 구현한 코드다.

 

orbitbody_J2.m

 

function ydot=orbitbody_J2(t,y)

% two-body orbital equation with J2
% coded by st.watermelon

mu=398600;
Rearth=6378;
J2=1.0826269e-3;

r=y(1:3);
v=y(4:6);

normr=norm(r);
rhat=r/normr;

acc=-(mu/(normr*normr))*rhat;

% J2
tmp = 5*r(3)^2/normr^2;
acc=acc + 3/2*J2*mu/normr^5*Rearth^2* ...
          [r(1)*(tmp-1); r(2)*tmp-1; r(3)*(tmp-1)];

%acc=acc - (mu*J2*Rearth*Rearth/2)* ...
%    ((6*z/(normr^5))*khat+((3/(normr^4))-(15*z*z/(normr^6)))*rhat);
ydot=zeros(6,1);
ydot(1:3)=v;
ydot(4:6)=acc;

 

다음은 수치 예제를 실행한 파일이다.

 

orbit_simul.m

 

% 
% J2-perturbed Lambert correction with shooting method
% coded by st.watermelon

clear all


% data
r1=[5000 10000  2100]';
r2=[-14600  2500  7000]';
tof=60*60; % 1 hour

OPTIONS = odeset('RelTol',3e-14,'AbsTol',1e-15);

% 1. Lambert solution
[v1, v2] = lambert(r1, r2, tof);

% 2. check solution accuracy  
y0=[r1;v1];
[t,y]=ode113(@orbitbody, [0:60:tof]',y0,OPTIONS); 

r2_computed = y(end,1:3)';
err_is = r2_computed-r2;

fprintf('Lambert solver accuracy \n');
fprintf('final error distance = %e m \n\n', norm(err_is)*100);


% 3. check again with J2 perturbation
y0=[r1;v1];
[t,yJ2]=ode113(@orbitbody_J2, [0:60:tof]',y0,OPTIONS);

r2_J2computed = yJ2(end,1:3)';
errJ2_is = r2_J2computed-r2;

fprintf('J2-perturbed Lambert solver accuracy \n');
fprintf('final error distance = %e m \n\n', norm(errJ2_is)*100);

% 4. correction with shooting method
v1_J2 = v1;
for iter=1:10
    y0=[r1;v1_J2];
    [t,y]=ode113(@orbitbody_J2, [0:60:tof]',y0,OPTIONS);
    J = jacobian_shooting(@orbitbody_J2, [r1; v1_J2], tof, 0.01);
    correc = inv(J)*(y(end,1:3)'-r2);
    v1_J2 = v1_J2 - correc;
    if norm(correc)<1e-8
        break
    end
end
v1_J2
fprintf('shooting iteration = %d \n', iter);
fprintf('initial velocity differences = %e m/s \n\n', norm(v1-v1_J2)*100);


% 5. check corrected accuracy
y0=[r1;v1_J2];
[t,yJ2_c]=ode113(@orbitbody_J2, [0:60:tof]',y0,OPTIONS);

r2_J2c = yJ2_c(end,1:3)';
errJ2c_is = r2_J2c-r2;

fprintf('corrected solution accuracy \n');
fprintf('final error distance = %e m \n', norm(errJ2c_is)*100);

댓글