如何寫出三體的MATLAB程序-代碼篇
寫在前面
在上文當中我們已經對三個物體之間的受力進行了分析,也說明了在時間下的加速度、速度和位移的計算方式。
本篇中將根據上一篇的公式來寫出對應的代碼,並且詳細說明一下如何去構建一個程序的框架。
本文所有代碼均在我的Github中存有備份,可下載後直接運行,點擊Github: HanpuLiang/Three-Body-by-MATLAB即可進入。
構建框架
基本變量
我們首先要確定,物體本身具有哪些物理量?
質量、加速度、速度、座標。
其中加速度和座標爲矢量,當在計算過程當中可以將其正交分解爲與軸的標量。
其餘的量我們還可以設置一下,諸如:物體運動的時間長度、我們計算所需要的時間間隔、萬有引力常數等。
對於我們要做出的圖像大小也需要設置一下,比如說軸範圍,軸範圍等。
程序流程
初始化
首先需要初始化,確定三個物體在初始狀態的情況:初始座標、初始速度(大小與方向)。所以一共需要三個量:座標、速度大小、速度相對軸角度。
物體的加速度可以直接由萬有引力公式計算出來,爲了計算方便,需要將其正交分解與疊加。
隨着時間演變
物體開始運動了,但是因爲我們無法給出一段連續的時間,只能計算在極小的時間間隔之後物體所在的位置。
所以在時間,首先計算出當前位置的加速度,然後根據這個加速度算出當前的速度,再根據這個速度算出經歷過時間後的位移變化量,再將這個位移變化量疊加到的位置上。
這樣子就完成了一個循環。
作圖
之前按道理,我們應該將每一個時間點的值放在一個矩陣內,這樣子我們就可以得到隨之間變化的所有物理量。
這樣子我們就可以直接做出隨着時間變化的各個物理量的圖,如和以及等。
如果我們想要做出小球的運動圖,那就需要時刻及其之前(做出尾跡)的數據進行作圖。
實際代碼
基本變量-代碼
首先是初始化
%% 初始條件
% 初始條件爲以圓心爲(0, 0)半徑r的圓上有三個等質量的點
r = 10;
% 座標(等邊三角形)
pos0 = [0, r; r/2*sqrt(3), -r/2; -r/2*sqrt(3), -r/2];
% 速度大小
v0 = [6, 6, 6];
% 速度方向(x軸正方向爲參考)
theta0 = [0, 4*pi/3, 2*pi/3];
%% 參數設置
global G dt m x_min x_max y_min y_max time_end isOutVideo;
% 結束時間
time_end = 2;
% 時間間隔
dt = 0.05;
% 萬有引力係數,隨便設置的
G = 1;
% 質量
m = [1000, 1000, 1000];
% 小球個數
N = length(v0);
% 圖像邊界
x_min = -25;
x_max = -x_min;
y_min = x_min;
y_max = -y_min;
% 是否輸出視頻圖像
isOutVideo = true;
初始化-代碼
然後是將我們的初始值放入矩陣中,因爲我們初始值設定的是角度與速度大小,所以首先要把給分解爲軸上的。
%% 初始設置
time = 0:dt:time_end;
% 座標
pos = zeros(N, 2, length(time));
pos(:,:,1) = pos0;
% 速度
vx = zeros(length(time), N);
vx(1,:) = v0.*cos(theta0);
vy = zeros(length(time), N);
vy(1,:) = v0.*sin(theta0);
% 加速度大小
a = zeros(length(time), N);
迭代開始
迭代的話這一步其實就和我們的邏輯很像了,不過之所以主代碼這麼簡介,是因爲我把一大堆複雜的內容全部放到了函數裏面,只留個接口調用,這樣子可以讓主代碼更加簡潔明瞭。
%% 迭代開始
for t = 2:length(time)
% 得到分加速度
da = calAcceleration(pos(:,:,t-1));
% 計算速度
[vx(t,:), vy(t,:)] = calVelocity(vx(t-1,:), vy(t-1,:), da);
% 計算位移
pos(:,:,t) = calDisplacement(vx(t-1:t,:), vy(t-1:t,:), pos(:,:,t-1));
end
對於計算加速度的函數,主要的原理還是和上一篇講的一樣,通過萬有引力公式求解,然後正交分解併疊加。
% 計算x與y軸加速度變化量da(3x2)
function da = calAcceleration(pos)
global m;
% 小球數量
[n, ~] = size(pos);
da = zeros(n, 2);
for i = 1:n
dax = 0;
day = 0;
for j = 1:n
if i ~= j
% i小球和j小球相對角度與距離
[theta, r] = calRelatively(pos(i,:), pos(j,:));
% 兩個小球的引力大小
F = calGravitation(r, i, j);
% 第i個小球收到來自j的加速度分量
dax = dax + F/m(i)*cos(theta);
day = day + F/m(i)*sin(theta);
end
end
da(i,:) = [dax, day];
end
end
% 計算兩個小球的相對角度與距離
function [theta, r] = calRelatively(pos1, pos2)
dx = pos2(1) - pos1(1);
dy = pos2(2) - pos1(2);
r = sqrt(dx^2 + dy^2);
theta = acos(dx/r);
% 因爲cos值的兩個象限需要區分,所以這裏要變換
if dy < 0 && dx >0
theta = -theta;
end
if dx < 0 && dy < 0
theta = (pi-theta)+pi;
end
end
% 計算兩個小球引力大小
function F = calGravitation(r, i, j)
global m G;
F = G*m(i)*m(j)/r^2;
end
計算速度的函數,這個就很簡單了,簡單的速度與加速度公式。
% 計算小球的速度變化
function [vx, vy] = calVelocity(vx_p, vy_p, da)
global dt;
vx = vx_p + dt*da(:,1)';
vy = vy_p + dt*da(:,2)';
end
計算當前座標,方法同公式。
% 計算小球的位移變化
function pos = calDisplacement(vx, vy, pos_p)
global dt;
vx = vx';
vy = vy';
% 計算下一時刻的座標
pos(:,1) = pos_p(:,1) + (vx(:,1)+vx(:,2))/2*dt;
pos(:,2) = pos_p(:,2) + (vy(:,1)+vy(:,2))/2*dt;
end
作圖-代碼
作圖的話就沒有這麼簡單了,因爲還需要設置一大堆比較麻煩的圖像參數。
對於做軌跡圖,可以通過以下代碼實現
% 做軌跡圖像
plotPosition(pos, vx, vy, time)
% 做運動圖像並保存視頻
function plotPosition(pos, vx, vy, time)
global isOutVideo;
figure(1)
if isOutVideo == true
% 創建視頻句柄,視頻名稱three_body.avi
writerObj = VideoWriter('three_body.avi');
open(writerObj);
myMovie(1:length(time)) = struct('cdata', [], 'colormap', []);
end
% 迭代計算得到圖像
for t = 1:length(time)
plotPosVec(pos(:,:,t), vx(t,:), vy(t,:), t, pos)
if isOutVideo == true
frame = getframe;
% 修改幀參數
frame.cdata = imresize(frame.cdata, [685, 685]);
writeVideo(writerObj, frame);
end
end
% 關閉視頻句柄
if isOutVideo == true
close(writerObj);
end
end
% 作圖位置+速度矢量
function plotPosVec(pos, vx, vy, t, pos_all)
% 小球
global x_min x_max y_min y_max;
figure(1)
scatter(pos(:,1), pos(:,2), 'ok', 'filled')
% 圖像細節調整
axis equal
box on
grid on
set(gca, 'linewidth', 1.5, 'xtick', floor(linspace(x_min, x_max, 11)), 'ytick', floor(linspace(y_min, y_max, 11)))
hold on
% 三條速度線
for i = 1:length(vx)
line([pos(i,1) pos(i,1)+vx(i)/2], [pos(i,2), pos(i,2)+vy(i)/2], 'linewidth', 1.2)
end
% 添加軌道線
plotCurrentTrace(pos_all, t)
% 添加文本
text(x_max*13/25, y_min*20/25, 'Made By Liang Hanpu', 'horiz', 'center', 'color', 'r')
axis([x_min x_max y_min y_max])
hold off
end
% 輸出軌跡線
function plotCurrentTrace(pos, t)
global x_min x_max y_min y_max;
if t ~= 0
[a, ~, ~] = size(pos);
hold on
axis equal
box on
grid on
set(gca, 'linewidth', 1.5)
axis([x_min x_max y_min y_max])
for i = 1:a
x = zeros(1, t);
y = zeros(1, t);
for j = 1:t
x(j) = pos(i, 1, j);
y(j) = pos(i, 2, j);
end
plot(x, y, 'linewidth', 1.5)
end
end
end
對於做時間隨速度大小與角度的圖像,可以由以下函數實現,這個就比較簡單了。
% 做速度隨時間圖像
plotVelocity(vx, vy)
% 輸出三個小球速度變化圖與角度變化圖
function plotVelocity(vx, vy)
global dt time_end;
% 速度
v = sqrt(vx.^2 + vy.^2);
t = (0:dt:time_end)'*ones(1,3);
% 角度
theta = acos(vx./v);
theta(vx>0&vy<0) = 2*pi-theta(vx>0&vy<0);
theta(vx<0&vy<0) = (pi-theta(vx<0&vy<0))+pi;
figure
plot(t, v, 'linewidth', 1.2)
box on
xlabel('Time', 'fontsize', 16)
ylabel('Velocity' , 'fontsize', 16)
set(gca, 'linewidth', 1.2)
figure
plot(t,theta, 'linewidth', 1.2)
xlabel('Time', 'fontsize', 16)
ylabel('Angle \theta' , 'fontsize', 16)
set(gca, 'linewidth', 1.2)
end
做出來的圖形大概可以看成這樣子,可以看出來還是有比較有趣的趨勢的。
最後
以上就是全部內容,我將會全部放在我的Github中,地址在文章開頭有。
如果你學到了一些東西的話,麻煩點個贊,加個收藏來個關注噢。