Пример решения системы дифференциальных уравнений (ДУ) в MATLAB адаптивным и не адаптивным методами.
В MATLAB встроено множество численных решателей с адаптивным шагом для решения жестких, нежестких и полностью неявных систем. С помощью Symbolic Math Toolbox можно сначала выводить системы ДУ, а затем тут же решать их численными методами.
Описание модели
Для примера решим систему ДУ, которая описывает систему из двух масс m1 и m2, которые жестко соединены невесомым стержнем длинной L.
Дубинка подбрасывается в воздух и двигается в вертикальной плоскости XY в поле тяжести. Угол θ показан на рисунке, координаты центра масс примем за (x,y).
Пусть параметры системы будут следующими:
Найдем систему ДУ относительно центра масс в терминах Лагранжевой механики
Запишем энергию вращения системы:
Энергию движения:
Потенциальную энергию:
J - момент инерции, M - масса дубинки, ω - угловая скорость, g - ускорение свободного падения, h - высота.
syms Erot Emov J omega V M U g h T x(t) y(t) theta(t) l % зададим символьные переменные eqnErot = Erot == 1/2*J*omega^2; % энергия вращения eqnEmov = Emov == 1/2*M*V^2; % энергия движения eqnU = U == M*g*h; % потенциальная энергия eqnV = V == sqrt((diff(x,t))^2+(diff(y,t))^2); % скорость
Из закона сохранения энергии найдем выражение L(t,q,q')
eqnT = T == rhs(eqnErot) + rhs(eqnEmov); eqnT = subs(eqnT,[omega V],[diff(theta,t) rhs(eqnV)]); % кинетическая энергия eqnU = subs(eqnU,h,y); % потенциальная энергия syms L eqnL = L == T - U; eqnL = subs(eqnL,[T U],[rhs(eqnT) rhs(eqnU)]) % найдем функцию Лагранжа
Найдем уравнения движения дубинки подставив функцию Лагранжа в выражение (1):
eqns = functionalDerivative(-rhs(eqnL),[x y theta]) == [0;0;0] % минимизируем функционал vars = [x(t); y(t); theta(t)]; % переменные
Решим систему уравнений
Для этого уменьшаем порядок системы до первого, переписываем систему в виде M(t,x(t))*x(t)'==F(t,x(t)) составляем пользовательскую функцию и решаем ее с помощью ode45.
[newEqs,newVars] = reduceDifferentialOrder(eqns,vars); % найдем эквивалентную систему первого порядка [Mass,f] = massMatrixForm(newEqs,newVars); % найдем массовую матрицу для системы вида M(t,x(t))*x(t)'==F(t,x(t)) mass = odeFunction(Mass, newVars,J,M); % конвертируем массовую матрицу �� пользовательскую функцию func = odeFunction(f,newVars,l,g,M); % запишем пользовательскую функцию для F(t,x(t)) % зададим параметры init_cond % инициализация начальных условий и параметров y0est = [x0; y0; theta0; vx0; vy0; omega]; % вектор начальных состояний tend = 3; % конец промежутка времени opts = odeset('Mass',@(t,q) mass(t,q,Ixx,Msum)); % настройки решателя Sol = ode45(@(t,q) func(t,q,l,g,Msum),[0 tend],y0est,opts); % решаем систему численно step = 0.1; % шаг по времени t = 0:step:tend; % промежуток времени X = deval(Sol,t,1); % решение для x(t) Y = deval(Sol,t,2); % решение для y(t) Th = deval(Sol,t,3);% решение для theta(t)
Результаты
В результате построим графики движения центра масс (ЦМ) дубинки в пространстве и изменение угла θ от времени.

Построим анимацию

Решение системы ДУ с помощью не адаптивного решателя
Иногда при решении систем ДУ решателями с адаптивным шагом возникают ошибки, связанные с достижением решателем слишком маленького шага интегрирования. В такие моменты следует поменять решатель, либо уменьшить точность интегрирования, а если это не помогает, остается только выбрать другой промежуток интегрирования, что приводит к кусочному решению. К сожалению, в пакете MATLAB нет функций численного решения ДУ с фиксированным шагом (есть только в Simulink), по типу метода Руге-Кутты.
Для решения ДУ с фиксированным шагом придется самому написать функцию, либо скачать уже готовый скрипт с форума MATLAB, например тут.
Продемонстрируем решение системы ДУ в MATLAB с помощью метода Дормана-Принса.
Составим и решим систему ДУ относительно координат массы 1 (x1,y1) и массы 2 (x2,y2).
Найдем систему ДУ в формулировках Лагранжевой механики:
Запишем выражение для кинетической энергии первой и второй массы:
и выражения для потенциальной энергии первой и второй массы:
Запишем выражения для скоростей первой и второй массы:
Ограничивающее выражение:
где l – длинна дубинки.
syms E1 E2 U1 U2 M V1 V2 g h1 h2 m1 m2 eqnE1 = E1 == 1/2*m1*V1^2; % кинетическая энергия массы 1 eqnE2 = E2 == 1/2*m2*V2^2; % кинетическая энергия массы 2 eqnU1 = U1 == m1*g*h1; % потенциальная энергия массы 1 eqnU2 = U2 == m2*g*h2; % потенциальная энергия массы 2 syms T U C x1(t) y1(t) x2(t) y2(t) u(t) l eqnV1 = V1 == sqrt((diff(x1,t))^2+(diff(y1,t))^2); % скорость массы 1 eqnV2 = V2 == sqrt((diff(x2,t))^2+(diff(y2,t))^2); % скорость массы 2 eqnC = C == (m1+m2)*u*((x2-x1)^2+(y2-y1)^2-l^2); % выражение для функции Лагранжа eqnT = T == rhs(eqnE1) + rhs(eqnE2); eqnT = subs(eqnT,[V1 V2],[rhs(eqnV1) rhs(eqnV2)]); % итоговая кинетическая энергия системы eqnU = U == rhs(eqnU1) + rhs(eqnU2); eqnU = subs(eqnU,[h1 h2],[y1 y2]); % итоговая потенциальная энергия системы
Найдем выражение L(t,q,q')
syms L eqnL = L == T - U + C; eqnL = subs(eqnL,[T U C],[rhs(eqnT) rhs(eqnU) rhs(eqnC)]) % функция Лагранжа
Найдем уравнения движения дубинки подставив функцию Лагранжа в выражение (1):
eqns = functionalDerivative(-rhs(eqnL),[x1 y1 x2 y2]); % минимизируем функционал eqn5 = (x2-x1)^2 + (y2-y1)^2; % выражение ограничивающее расстояние между массами eqns = [eqns; eqn5] == [0; 0; 0; 0; l^2] % итоговая система vars = [x1(t); y1(t); x2(t); y2(t); u(t)]; % переменные
Решим систему уравнений
Для решения ДУ очень важно найти правильную пользовательскую функцию, пригодную для выбранного решателя, а также начальные условия.
Чтобы составить пользовательскую функцию, произведем следующий алгоритм:
[eqnsR,varsR] = reduceDifferentialOrder(eqns,vars); % найдем эквивалентную систему первого порядка [ODEs,constraints] = reduceDAEToODE(eqnsR,varsR); % конвертируем систему первого порядка в такую систему, чтобы Якобиан был обратным [massM,f] = massMatrixForm(ODEs,varsR); % найдем массовую матрицу M и систему F, такие чтобы выполнялось условие: M(t,x(t))*x(t)'==F(t,x(t)) Lvars = length(varsR); % сосчитаем количество неизвестных F = massM\f; % итоговая функция вида x(t)'== M(t,x(t))/F(t,x(t)) Mode = odeFunction(massM, varsR, m1, m2); % пользовательская функция для массовой матрицы Fode = odeFunction(F, varsR, g, m1, m2); % пользовательская функция системы
Подставим числен��ые значения в пользовательские функции:
init_cond % загрузим начальные условия ODEsNumeric = subs(ODEs); % подставим численные значения в систему первого порядка constraintsNumeric = subs(constraints); % подставим численные значения в ограничения Mode = @(t,Y) Mode(t,Y,m1,m2); % пользовательская функция для массовой матрицы с численными значениями Fode = @(t,Y) Fode(t,Y,g,m1,m2); % пользовательская функция системы с численными значениями
Найдем начальные условия для системы первого порядка:
L_CM = (m1*0+m2*l)/Msum; % центр масс дубинки x01 = 1; y01 = 1; % начальное положение массы 1 x02 = x01+l; y02 = y01; % начальное положение массы 2 vx01 = vx0; vy01 = -omega*L_CM+vy0; % начальная скорость массы 1 vx02 = vx0; vy02 = omega*(l-L_CM)+vy0; % начальная скорость массы 2 y0est = [x01; y01; x02; y02; 0; vx01; vy01; vx02; vy02]; % вектор начальных значений yp0est = zeros(Lvars,1); % предположение о начальных значениях производных t0 = 0; opt1 = odeset('Mass', Mode); % настройки поиска начальных условий [y0, yp0] = decic(ODEsNumeric, varsR, constraintsNumeric, t0,... y0est, [1,1,1,0,0,1,1,0,1], yp0est, opt1); % поиск начальных условий и соответствующих производных
Зададим промежуток времени и решим систему решателем с фиксированным шагом ode5 (метод Дормана-Принса)
t = t0:0.01:tend; % промежуток времени Sol = ode5(Fode,t,y0); % решение X1 = Sol(:,1); % координаты X массы 1 Y1 = Sol(:,2); % координаты Y массы 1 X2 = Sol(:,3); % координаты X массы 2 Y2 = Sol(:,4); % координаты Y массы 2 Th = unwrap(atan2((Y2-Y1),(X2-X1))); % угол вращения дубинки X = X1+L_CM*cos(Th); % координата X ЦМ Y = Y1+L_CM*sin(Th); % координата Y ЦМ
Результаты
Построим такие же графики движения центра масс (ЦМ) дубинки в пространстве и изменение угла θ от времени.

Построим анимацию

Как видно, решения первой системы ДУ адаптивным решателем ode45 и второй системы ДУ фиксированным ode5, довольно похожи. Значит мы скорее всего не ошиблись в выводе уравнений.
Для сравнения решим эту же систему решателем для жестких уравнений ode15s:
Fode = odeFunction(f, varsR, g, m1, m2); % пользовательская функция системы opt2 = odeset(opt1,'InitialSlope',yp0); % настройки решателя Sol = ode15s(Fode, [t0 tend], y0, opt1); % решение
Сравним результаты решения адаптивного ode15s и не адаптивного ode5 решателей, найдем абсолютное значение ошибки между решениями:

Как можно видеть на графиках, квадрат ошибки порядка 10^-5, что немного, но особенно выводов тут не сделать. Плюсы и минусы адаптивных и не адаптивных решателей известны.
Если ни один адаптивный решатель не справляется с решением задачи, всегда поможет старый добрый метод Рунге-Кутты, главное верно задать пользовательскую функцию, пригодную для решателя.
Список источников: Solve Equations of Motion for Baton Thrown into Air
