Усовершенствования скрипта для оптимизации траектории конечного прожига с помощью MATLAB

Я написал сценарий MATLAB, чтобы использовать решатель двухточечного граничного уравнения bvp4c для реализации оптимизации траектории с помощью вариационного исчисления проблемы одиночного конечного горения со свободным выбегом для выбора оптимальной точки воспламенения. Я хотел бы получить информацию об ошибках, которые я сделал, или о способах улучшения кода. Что он делает, так это определяет оптимальное конечное горение от орбиты 185x185 км до орбиты 185x1000 км с необязательным изменением наклонения. Он устанавливает многоточечную краевую задачу для решения задачи ожога-побережья с использованием векторного уравнения праймера.

Код основан на коде MATLAB в приложениях к книге « Оптимальное управление с аэрокосмическими приложениями » Лонгуски, Гусмана и Пруссинга. Время выбега и время горения были реализованы как свободные параметры, а в краевой задаче используется нормализованное время (тау), так что [0,1] — это период выбега, а [1,2] — период горения.

ODE реализует уравнения движения и векторное уравнение праймера для движения центральной силы в трехмерных декартовых координатах. Думаю, я все правильно понял, но мог бы кто-нибудь перепроверить мою работу. Умножение на время выбега или время горения в возвращаемом значении ODE необходимо для преобразования производной от г г т к г г т .

Граничные условия - это то, о чем у меня есть вопрос, правильно ли я все сделал. Начальное положение, скорость и масса — тривиальные условия. Есть также 14 условий, которые обеспечивают непрерывность всех переменных слева и справа между побережьем и горением (tau = 1).

Остальные условия - это 5 ограничений на конечные орбитальные условия, а затем 3 условия трансверсальности, учитывающие истинную аномалию свободного места точки присоединения, а также свободное время выбега и ожога. Для конечных орбитальных условий я использую вектор углового момента, а первые две компоненты вектора эксцентриситета равны целевой орбите. Что я использую для условий трансверальности, так это то, что если гамильтониан разбит на ЧАС знак равно ЧАС 0 + Т С где Т — тяга, а S — функция переключения, то устанавливаю ЧАС 0 ( т 2 ) знак равно 0 , ЧАС 0 ( т 0 ) знак равно 0 а потом с С знак равно ( | п в | 1 ) поставил | п в ( т 1 ) | знак равно 1 . Мне не хватает уверенности в том, что я все понял правильно.

Результаты кажутся довольно приличными, но я немного удивлен, что не вижу | п в ( т 2 ) | знак равно 1 . Я также вижу проблемы с конвергенцией для любых проблем, связанных с наклоном выше примерно 25 градусов (но это приводит к сжиганию 300 секунд / 3000 dV).

Я еще не нормализовал единицы измерения положения и скорости, поэтому они все еще м и м / с .

Для более умеренных прожигов он, кажется, работает довольно хорошо и дает время прожига примерно в секунду по сравнению с моделью импульсного прожига и выглядит так, как будто он правильно ведет прожиг с отрицательным выбегом примерно в половину времени горения.

Я был бы признателен за любую помощь в поиске ошибок, которые я сделал, или способов улучшить этот код, чтобы сделать его более надежным.

close all; clear all; clc;

global r0 v0 m0 rT vT g0
global MU Thrust Mdot

MU = 3.9860044189e+14;  % earth
Thrust = 232.7 * 1000;  % N; LR-91 232.7 kN
m0 = 32.74 * 1000;      % kg; 32.74t - 1g start accel
isp = 316;              % sec; LR-91
g0 = 9.80665;           % m/s; standard gravity
ve = isp * g0;          % m/s
a0 = Thrust / m0        % m/s^2; initial accel
tau = ve / a0           % s; time to burn rocket to zero
Mdot = Thrust / ve;     % kg/sec

rearth = 6.371e+6;      % m; earth radius
r185 = rearth + 0.185e+6;
r1000 = rearth + 1.000e+6;

% initial position (185x185)
r0 = [ r185, 0, 0 ];
v185 = sqrt(MU/r185);
v0 = [ 0, v185, 0 ];

% target orbital parameters (185x1000)
rT = [ r185, 0, 0 ];
smaT = ( r185 + r1000 ) / 2;
inc = 10;  % degrees
vTm = sqrt(MU * (2/r185 - 1/smaT) );
vT = [ 0, vTm * cosd(inc), vTm * sind(inc)];

vBurn = vT - v0;
dV = norm(vBurn)

% list initial conds
yinit = [r0 v0 vBurn/norm(vBurn) 0 0 0 m0 ];  % initial state and costate
tb_guess = tau * (1 - exp(-dV/ve) )
tc_guess = - tb_guess / 2

% sanity checks on the impulsive burn model
mf_impulsive = m0 - tb_guess * Mdot
af_impulsive = Thrust / mf_impulsive / g0

% parameters
parameters = [ tc_guess, tb_guess ];

% number of timeslices
Nt = 41;
tau = [
  linspace(0,1,Nt)'
  linspace(1,2,Nt)'
];

% initial guess
solinit = bvpinit(tau, yinit, parameters);

% bump up the mesh by a bit
option=bvpset('Nmax',50000);

% solve
sol = bvp4c(@Burn_odes, @Burn_bcs, solinit, option);

% extract times
tc = sol.parameters(1)
tb = sol.parameters(2)

% pull out values for times
Z = deval(sol, tau);

% convert taus to times
for i=1:length(tau)
  if tau(i) <= 1
    time(i) = tau(i) * tc;
  else
    time(i) = tc + ( tau(i) - 1 ) * tb;
  end
end

% extract solution vars
x_sol = Z(1,:);
y_sol = Z(2,:);
z_sol = Z(3,:);
r_sol = sqrt( x_sol.^2 + y_sol.^2 + z_sol.^2 );
vx_sol = Z(4,:);
vy_sol = Z(5,:);
vz_sol = Z(6,:);
v_sol = sqrt( vx_sol.^2 + vy_sol.^2 + vz_sol.^2 );
pvx_sol = Z(7,:);
pvy_sol = Z(8,:);
pvz_sol = Z(9,:);
pv_sol = sqrt( pvx_sol.^2 + pvy_sol.^2 + pvz_sol.^2 );
m_sol = Z(13,:);

for i = 1:length(tau)
  r = Z(1:3, i);
  v = Z(4:6, i);
  h(i) = norm(cross(r,v));
end

% plots

figure;
subplot(3,2,1);
plot(time,m_sol/1000);
ylabel('mass, tons', 'fontsize', 14);
xlabel('Time, sec', 'fontsize', 14);
hold on;
grid on;

subplot(3,2,2);
plot(time,r_sol/1000);
ylabel('radius, km', 'fontsize', 14);
xlabel('Time, sec', 'fontsize', 14);
hold on;
grid on;

subplot(3,2,3);
plot(time,v_sol/1000);
ylabel('velocity, km/s', 'fontsize', 14);
xlabel('Time, sec', 'fontsize', 14);
hold on;
grid on;

subplot(3,2,4);
plot(time,h);
ylabel('h', 'fontsize', 14);
xlabel('Time, sec', 'fontsize', 14);
hold on;
grid on;

subplot(3,2,5);
plot(time,pv_sol);
ylabel('pv magnitude', 'fontsize', 14);
xlabel('Time, sec', 'fontsize', 14);
hold on;
grid on;

figure;
plot(x_sol/1000, y_sol/1000);
grid on;
axis equal
xlabel('x, km', 'fontsize', 14);
ylabel('y, km', 'fontsize', 14);


%
% Boundary conditions
%

function PSI = Burn_bcs(yleft, yright, parameters)

Y0 = yleft(:,1);
Y1 = yleft(:,2);  % == yright(:,1)
Y2 = yright(:,2);

global r0 v0 m0 rT vT MU g0 Thrust

ri = Y0(1:3);
vi = Y0(4:6);
pvi = Y0(7:9);
pri = Y0(10:12);
mi = Y0(13);
ri3 = norm(ri)^3;

rf = Y2(1:3);
vf = Y2(4:6);
pvf = Y2(7:9);
prf = Y2(10:12);
mf = Y2(13);
rf3 = norm(rf)^3;

r1 = Y1(1:3);
v1 = Y1(4:6);
pv1 = Y1(7:9);
pr1 = Y1(10:12);
r13 = norm(r1)^3;

hT = cross(rT, vT);
hf = cross(rf, vf);

eT = - (rT/norm(rT) + cross(hT,vT)/MU);
ef = - (rf/norm(rf) + cross(hf,vf)/MU);

emiss = ef - eT';

uf = pvf / norm(pvf);

H0ti = dot(pri, vi) - MU * dot(pvi, ri) / ri3;
H0t1 = dot(pr1, v1) - MU * dot(pv1, r1) / r13;
H0tf = dot(prf, vf) - MU * dot(pvf, rf) / rf3;
Htf = H0tf - Thrust * ( norm(pvf) - 1 );

PSI = [
    Y0(1:3) - r0'                       % 3 - initial position
    Y0(4:6) - v0'                       % 3 - initial velocity
    Y0(13) - m0                         % 1 - initial mass
    norm(Y1(7:9)) - 1                   % 1 - norm(pv1) = 1 (so H(t1) = 0 on both sides of t1)
    hf - hT'                            % 3 - terminal constraint on angular momentum
    emiss(1:2)                          % 2 - terminal constraint on ecc vector
    H0ti                                % 1 - H0(t0) = 0
    H0tf                                % 1 - H0(tf) = 0
    yleft(:,2) - yright(:,1)            % 14 - values at t1 are continuous
    ];

end

%
% Equations of motion
%

function dX_dtau = Burn_odes(tau, X, k, parameters)

global MU Thrust Mdot

if k == 1
  thr = 0;
  md = 0;
  tinterval = parameters(1);  % tc
else
  thr = Thrust;
  md = Mdot;
  tinterval = parameters(2);  % tb
end

r = X(1:3);
v = X(4:6);
pv = X(7:9);
pr = X(10:12);
m = X(13);

u = pv / norm(pv);

Fm = thr / m;
r3 = norm(r)^3;
r2 = dot(r,r);
r5 = norm(r)^5;

rdot  = v;
vdot  = - MU * r / r3 + Fm * u;
pvdot = pr;
prdot = - MU / r5 * ( 3 * r' * r - r2 * eye(3,3) ) * pv;

dX_dtau = tinterval*[rdot', vdot', pvdot', prdot', -md ];

end
Я признаю, что не очень хорошо знаком с работой Лонгуски (хотя должен был бы, так как работаю над конечной оптимизацией записи и неоптимальными решениями), поэтому, пожалуйста, извините за неосведомленные вопросы. Не могли бы вы объяснить нам, почему первые три элемента сосостояния являются нормой вектора скорости (если я правильно понял)? Кроме того, есть ли какая-то конкретная причина, по которой вы закодировали все в метрах, а не в километрах? Кроме того, вы уверены, что этот алгоритм TPBVP работает при больших изменениях наклона? Многие алгоритмы оптимизации перестают работать из-за больших изменений OE или при включении некоторых возмущений.
Если вы имеете в виду yinit, это всего лишь начальное предположение о стоимости вектора праймера, которое является единичным вектором в направлении продвижения. Уравнение вектора праймера для костата опущено почти в нижней части сценария.
Нет причин в метрах, а не в км, и я считаю, что его следует нормализовать к чему-то более близкому к радиусу начальной или целевой орбиты. В книге есть дополнительный пример, посвященный этому, и это просто TODO, до которого я еще не добрался.
И это моя первая попытка правильно решить задачи оптимизации (любого рода), поэтому я понятия не имею, должен ли алгоритм TPBVP работать с большими изменениями наклона или нет. Если ожидается, что у оптимизаторов возникнут трудности с этой проблемой, тогда это имеет смысл (я понятия не имею, насколько оптимизатор bvp4c подходит для такого рода проблем, я использую его в основном для того, чтобы научиться ставить эти проблемы в первое место).
Я недостаточно разбираюсь в вариационном исчислении, чтобы знать, ломаются ли они: я спрошу у некоторых коллег, которые, надеюсь, найдут для вас ответ. Однако я могу утверждать, что решатель Ламберта — это один из способов решения TPBVP, и он определенно работает для небольших изменений наклона. И я также не понимаю, почему он не работает даже при очень больших изменениях наклона... Это озадачивает! Отличный вопрос! :-D
Сегодня утром я сделал крошечную поправку, чтобы изменить предположение о том, что начальная костата должна быть в направлении решения импульсного сжигания, а не только в направлении проградации, но, похоже, это мало что меняет.
Возможно, я смогу значительно реорганизовать это, чтобы использовать функцию ode45() в Matlab вместе с парой популярных и надежных подпрограмм jacobianest() и newtonraphson() при обмене файлами mathworks (и последняя, ​​кажется, квазиньютоновская). подход, который ухудшается до градиентного подъема, поэтому может подойти для такого рода приложений). Результат должен быть в целом эквивалентен всем подходам CoV в литературе (аналитическая обработка периода побережья и нормализация единиц завершили бы его).
Кроме того, читая о многоточечных граничных задачах, я не знал, что вы не просто интегрируете через граничные точки, но запускаете (например,) Ньютона-Рафсона в своих граничных точках, что повышает стабильность (очевидно, получая некоторые из преимущества методов конечных элементов при меньшей стоимости?). Так что я думаю, что увеличение времени горения на 200-300 секунд повысит стабильность. Что согласуется с тем, что я читал в литературе по оптимизации траектории восхождений.
Добавление фиксированной 200-секундной дополнительной точки в прожиге позволило мне добиться стабильного изменения наклона на 30 градусов (что увеличивает время движения до 200 секунд). Я попробую динамически разбить проблему на 100-секундные интервалы на обоих побережьях и записать и посмотреть, как это пойдет.
Наткнулся на этот документ researchgate.net/publication/… который, я думаю, охватывает все вопросы, которые у меня есть. Похоже, мне действительно нужно сосредоточиться на обусловливании проблемы, чтобы состояние и стоимость были нормализованы около нуля, и я думаю, что напортачил, по крайней мере, ЧАС 0 ( т ф ) знак равно 0 условие, которое должно быть ЧАС ( т ф ) знак равно 0 и мне нужно решить проблемы масштабирования с тем условием, которое рассматривается в статье.
"...нормировано около единицы "
большая ошибка, которую я только что обнаружил в уравнении вектора костата / праймера, заключается в том, что r' * rэто просто внутренний скалярный продукт, в то время как вам нужен r * r'внешний продукт (я знал, что мне нужно, что у меня просто была опечатка, и я никогда не проверял это).
Я голосую за то, чтобы закрыть этот вопрос как не по теме, потому что речь идет о решении уравнений с помощью Matlab. Уравнения сами по себе важны, но вопрос не в них или в том, как они применялись.
Вопросы о граничных условиях применимы здесь как вопросы орбитальной механики, я бы сказал, они просто недостаточно выделены ни в вопросе, ни в ответе на него. Учитывая положительные отзывы и комментарии до того, как несколько месяцев спустя появились закрытые голоса, я голосую за то, чтобы оставить его открытым.
@ErinAnne, вопрос о граничных условиях может быть применим, но вопрос должен быть сильно отредактирован, чтобы сделать его о них, иначе вопрос следует закрыть как слишком широкий.
Вероятно, сейчас я смогу лучше написать вопрос, хотя не думаю, что сейчас у меня есть время его редактировать. Однако цель вопроса такова: «Я пытаюсь написать оптимизатор траектории ракеты (в Matlab), и я не знаю, что делаю, помогите».
И это выходит за рамки вопросов граничного значения/трансверсальности. Я начал с попытки использовать решатель Matlab bpv4c TPBPV, когда метод CoV / косвенный, который я выбрал для использования, уже превращает TPBPV в проблему интеграции + поиска корня, поэтому простое использование ode45 + fsolve() работало намного лучше. Я не знаю, что я делал с bpv4c, но это точно не сработало. Также могут быть лучшие способы решения исходной проблемы оптимизации траектории с использованием bpv4c от Matlab, и я все равно был бы признателен, если бы кто-нибудь показал мне, как это сделать.
(также я специально искал информацию от людей, которые могли бы быть аспирантами или выше, занимающимися оптимизацией траектории ракеты, а не от обычных пользователей Matlab - проблемная область намного важнее, чем инструмент)

Ответы (1)

Я, наконец, понял, что, пытаясь использовать bvp4cэто, я усложнял всю проблему, чем мне было нужно. Используя CoV, я уже преобразовал проблему из TPBVP в проблему поиска корней. Средство поиска корней по умолчанию fsolveв Matlab является квазиньютоновским с областью доверия изгиба и поддерживает вычисление числовых якобианов. Это ставит все галочки.

Поэтому я обновил скрипт выше, чтобы использовать fsolve. Я также применил числовую обработку, которую использует Пинг Лу .

Кажется, что он отлично справляется со случаем перехода с экваториальной орбиты 185x185 км на орбиту 185x1000 с изменением наклонения на 89 градусов и дает правдоподобные результаты. На моем рабочем столе требуется всего 0,4 секунды при подаче начального предположения, соответствующего импульсивному сжиганию. Это сжигание 11 089 dV, что становится довольно глупо с точки зрения реальности, но решатель справляется с этим.

Этот сценарий имеет особенность в граничных условиях при нацеливании на полярные орбиты (ссылка, из которой я получил граничные условия, предлагает изменить векторы через изменение системы координат, чтобы решить эту проблему). Вот почему я использовал изменение на 89 градусов вместо 90 градусов.

Я считаю, что самый большой численный недостаток здесь заключается в том, что это подход с одним выстрелом, а для нескольких фаз горения-выбега задача должна использовать подход с несколькими выстрелами для дополнительной стабильности (или для длинных дуг горения или выбега).

Я также просто использую ODE45 для интеграции решения. Для береговых дуг лучше использовать аналитическое решение Goodyear. У Пинга Лу есть серия статей о различных подходах к аналитическим приближениям к дугам горения.

Я также до сих пор не уверен, что у меня есть 3 граничных условия для свободного подключения и оптимизации побережья и времени горения - мне просто нужно вернуться к этим условиям теперь, когда у меня есть более стабильное решение для оптимизации.

Мне также нужно немного посмотреть на очистку переменных в коде для нормализации - я почти уверен, что my mu_barвсегда алгебраически идентичен 1.000..и может быть полностью удален и т. д.

Для запуска этого кода требуется Matlab R2017b (для vecnorm), а функция rv2orb() преобразует векторы состояния в кеплеровы элементы и находится здесь .

close all; clear all; clc;
format shortG;

mu = 3.9860044189e+14;  % m^3/s^2; earth
thrust = 232.7 * 1000;  % N; kg m / s^2; LR-91 232.7 kN
m0 = 32.74 * 1000;      % kg; 32.74t - 1g start accel
isp = 316;              % sec; LR-91
g0 = 9.80665;           % m/s; standard gravity
ve = isp * g0;          % m/s
a0 = thrust / m0;       % m/s^2; initial accel
tau = ve / a0;          % s; time to burn rocket to zero
mdot = thrust / ve;     % kg/sec

rearth = 6.371e+6;      % m; earth radius
r185   = rearth + 0.185e+6;
r1000  = rearth + 1.000e+6;
v185   = sqrt(mu/r185); % 185x185 velocity

% initial conditions
r0 = [ r185, 0, 0 ];
v0 = [ 0, v185, 0 ];

% target conditions
rT = [ r185, 0, 0 ];
smaT = ( r185 + r1000 ) / 2;
inc = 89; % degrees
vTm = sqrt(mu * (2/r185 - 1/smaT) );
vT = [ 0, vTm * cosd(inc), vTm * sind(inc)];

% impulsive burn approximation
dV = vT - v0;
dVm = norm(dV)
burnTime = tau * (1 - exp(-dVm/ve) )

% scaling factors
g_bar = mu / norm(r0)^2;
r_scale = norm(r0);
v_scale = sqrt( norm(r0) * g_bar );
t_scale = sqrt( norm(r0) / g_bar );

% applying scaling
tb_bar     = burnTime / t_scale;
tc_bar     = - burnTime / t_scale / 2;
r0_bar     = r0 / r_scale;
rT_bar     = rT / r_scale;
v0_bar     = v0 / v_scale;
vT_bar     = vT / v_scale;
mu_bar     = mu / r_scale^3 * t_scale^2;    % this is just always == 1.000 tho right?
thrust_bar = thrust / r_scale * t_scale^2;  % can these two...
mdot_bar   = mdot * t_scale;                % ...get simplified?

% solve the problem
fun = @(x) coastBurn5constraintFun(r0_bar, v0_bar, x(1:3), x(4:6), m0, x(7), x(8), mu_bar, thrust_bar, mdot_bar, rT_bar, vT_bar);

tic
[x, z, exitflag, output, jacobian] = fsolve(fun, [ dV/norm(dV) zeros(1,3) tc_bar tb_bar ]);
toc

disp(z');

disp(output);

% use the solution to pull data out of the IVP again
xinit = [ r0_bar v0_bar x(1:3) x(4:6) m0 ];
tc_bar = x(7);
tb_bar = x(8);
[x, tfull, xfull] = coastBurnIVP(xinit, tc_bar, tb_bar, mu_bar, thrust_bar, mdot_bar);

% reverse scaling
tfull = tfull * t_scale;
xfull(:,1:3) = xfull(:,1:3) * r_scale;
xfull(:,4:6) = xfull(:,4:6) * v_scale;

% print some output
rf = xfull(end, 1:3)
vf = xfull(end, 4:6)

tc = tc_bar * t_scale
tb = tb_bar * t_scale

[a,eMag,i,O,o,nu,truLon,argLat,lonPer,p] = rv2orb(rf', vf', mu);

inc = rad2deg(i)

PeR = (1 - eMag) * a
ApR = (1 + eMag) * a

PeA = PeR - rearth
ApA = ApR - rearth

% plots

figure;
subplot(3,2,1);
plot(tfull,xfull(:,13)/1000);
ylabel('mass, tons', 'fontsize', 14);
xlabel('Time, sec', 'fontsize', 14);
hold on;
grid on;

subplot(3,2,2);
plot(tfull,vecnorm(xfull(:,1:3)')'/1000);
ylabel('radius, km', 'fontsize', 14);
xlabel('Time, sec', 'fontsize', 14);
hold on;
grid on;

subplot(3,2,3);
plot(tfull,vecnorm(xfull(:,4:6)')'/1000);
ylabel('velocity, km/s', 'fontsize', 14);
xlabel('Time, sec', 'fontsize', 14);
hold on;
grid on;

%subplot(3,2,4);
%plot(time,h);
%ylabel('h', 'fontsize', 14);
%xlabel('Time, sec', 'fontsize', 14);
%hold on;
%grid on;

subplot(3,2,5);
plot(tfull,vecnorm(xfull(:,7:9)')');
ylabel('pv magnitude', 'fontsize', 14);
xlabel('Time, sec', 'fontsize', 14);
hold on;
grid on;

%figure;
%plot(x_sol/1000, y_sol/1000);
%grid on;
%axis equal
%xlabel('x, km', 'fontsize', 14);
%ylabel('y, km', 'fontsize', 14);


% IVP function returning the 5-constraint BC miss
%
function z = coastBurn5constraintFun(r0_bar, v0_bar, pv, pr, m0, tc_bar, tb_bar, mu_bar, thrust_bar, mdot_bar, rT_bar, vT_bar)
  xinit = [ r0_bar v0_bar pv pr m0 ];
  [x, tfull, xfull] = coastBurnIVP(xinit, tc_bar, tb_bar, mu_bar, thrust_bar, mdot_bar);
  z = coastBurn5constraintBC(x, mu_bar, rT_bar, vT_bar);
end

% 5 orbital constraint Boundary Conditions for Coast-Burn with free times
%
function z = coastBurn5constraintBC(x, mu_bar, rT, vT)
  x0 = x(1,:);
  x1 = x(2,:);
  x2 = x(3,:);

  r0 = x0(1:3);
  v0 = x0(4:6);
  pv0 = x0(7:9);
  pr0 = x0(10:12);

  r1 = x1(1:3);
  v1 = x1(4:6);
  pv1 = x1(7:9);
  pr1 = x1(10:12);

  r2 = x2(1:3);
  v2 = x2(4:6);
  pv2 = x2(7:9);
  pr2 = x2(10:12);

  hT = cross(rT, vT);
  h2 = cross(r2, v2);

  hmiss = h2 - hT;

  eT = - (rT/norm(rT) + cross(hT, vT)/mu_bar);
  e2 = - (r2/norm(r2) + cross(h2, v2)/mu_bar);

  emiss = e2 - eT;

  H0t1 = dot(pr1, v1) - mu_bar * dot(pv1, r1) / norm(r1)^3;
  H0t2 = dot(pr2, v2) - mu_bar * dot(pv2, r2) / norm(r2)^3;

  z = [
    hmiss'          % 3 constraints
    emiss(1:2)'     % 2 constraints
    H0t1
    H0t2
    norm(pv0) - 1
  ];
end

% Multi step integration of the IVP
%
function [x, tfull, xfull] = coastBurnIVP(x0, tc, tb, mu, thrust, mdot)
  coastfun = @(t,x) centralForceThrustEOM(t, x, mu, 0, 0);  % FIXME: Use Goodyear instead of ODE45
  [t1, x1] = ode45(coastfun, [0 tc], x0);

  burnfun = @(t,x) centralForceThrustEOM(t, x, mu, thrust, mdot);
  [t2, x2] = ode45(burnfun, [tc tc+tb], x1(end,:));

  % x values at the boundaries
  x = [
    x1(1,:)
    x2(1,:)
    x2(end,:)
  ];

  % all the t's for graphing
  tfull = [
    t1
    t2
  ];

  % all the x's for graphing
  xfull = [
    x1
    x2
  ];
end

% Equations of Motion
%
function dX_dt = centralForceThrustEOM(t, X, mu, thrust, mdot)
  r  = X(1:3);
  v  = X(4:6);
  pv = X(7:9);
  pr = X(10:12);
  m  = X(13);

  u = pv/norm(pv);
  Fm = thrust / m;

  r3 = norm(r)^3;
  r2 = dot(r,r);
  r5 = r2 * r3;

  rdot  = v;
  vdot  = - mu * r / r3 + Fm * u;
  pvdot = pr;
  prdot = - mu / r5 * ( 3 * r * r' - r2 * eye(3,3) ) * pv;

  dX_dt = [ rdot' , vdot', pvdot', prdot', -mdot ]';
end
пометка @ChrisR
Я преобразовал приведенный выше скрипт для поиска многоточечного корня, который оказался концептуально проще, чем я ожидал. Несмотря на то, что я убрал фиксированные переменные (r0, v0 и m), это по-прежнему увеличивает количество уравнений с 8 до 20, а время решения увеличивается с 0,4 с до 1,4 с.
Удаление переменных кажется ненужной микрооптимизацией. Расширение его до 28-мерной задачи решения корня, похоже, не требует больших затрат (поскольку его начальные условия, якобиан тривиален, а начальное предположение точно). Это начинает упрощать код. Реализация Levenburg-Marquardt fsolve() также кажется подходящей для решения такого рода проблем.