如何寫出三體的MATLAB程序-代碼篇 如何寫出三體的MATLAB程序-代碼篇

如何寫出三體的MATLAB程序-代碼篇

寫在前面

在上文當中我們已經對三個物體之間的受力進行了分析,也說明了在時間t下的加速度、速度和位移的計算方式。

本篇中將根據上一篇的公式來寫出對應的代碼,並且詳細說明一下如何去構建一個程序的框架。

本文所有代碼均在我的Github中存有備份,可下載後直接運行,點擊Github: HanpuLiang/Three-Body-by-MATLAB即可進入。

構建框架

基本變量

我們首先要確定,物體本身具有哪些物理量?

質量、加速度、速度、座標。

其中加速度和座標爲矢量,當在計算過程當中可以將其正交分解爲xy軸的標量。

其餘的量我們還可以設置一下,諸如:物體運動的時間長度、我們計算所需要的時間間隔、萬有引力常數等。

對於我們要做出的圖像大小也需要設置一下,比如說x軸範圍,y軸範圍等。

程序流程

初始化

首先需要初始化,確定三個物體在初始狀態的情況:初始座標、初始速度(大小與方向)。所以一共需要三個量:座標、速度大小、速度相對x軸角度。

物體的加速度可以直接由萬有引力公式計算出來,爲了計算方便,需要將其正交分解與疊加。

隨着時間演變

物體開始運動了,但是因爲我們無法給出一段連續的時間,只能計算在極小的時間間隔\Delta t之後物體所在的位置。

所以在t\to t+Delta t時間,首先計算出當前位置的加速度,然後根據這個加速度算出當前的速度,再根據這個速度算出經歷過\Delta t時間後的位移變化量,再將這個位移變化量疊加到t的位置上。

這樣子就完成了一個循環。

作圖

之前按道理,我們應該將每一個時間點的值放在一個矩陣內,這樣子我們就可以得到隨之間變化的所有物理量。

這樣子我們就可以直接做出隨着時間變化的各個物理量的圖,如t-vt-a以及t-\theta等。

如果我們想要做出小球的運動圖,那就需要t時刻及其之前(做出尾跡)的數據進行作圖。

實際代碼

基本變量-代碼

首先是初始化

%% 初始條件
% 初始條件爲以圓心爲(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;

初始化-代碼

然後是將我們的初始值放入矩陣中,因爲我們初始值設定的是角度與速度大小,所以首先要把v給分解爲xy軸上的。

%% 初始設置
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中,地址在文章開頭有。

如果你學到了一些東西的話,麻煩點個贊,加個收藏來個關注噢。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章