這篇文章隨便水一下,寫一個簡單的3體問題,權當娛樂
1地月系統模擬
萬有引力的公式爲:
查詢地月系統的參數,可以得到:
參數 | 數值 |
---|---|
引力常數 G | 6.67e-11 |
地球質量 M | 5.965e+24 Kg |
近地點距離 r | 363300000 m |
遠地點距離 R | 405493000 m |
近地點月球速度 Vnear | 1074.82 m/s |
軌道離心率 | 0.0549 |
其中近地點月球的速度通過下面公式求得:
在已知初始條件和參數後,我們開始建立模型,對月球的運行軌道建立方程,可以得到在任意時刻月球的加速度a爲:
其中ax爲x軸方向上的加速度,ay爲y軸方向上的加速度。該方程屬於典型的常微分方程ODE,整理爲一階微分方程的形式:
其中上式的uv爲x方向上的速度和y方向上的速度。爲了提高精度,將方程帶入到4階RK求解器中,即可求解得到月球的軌跡。
matlab代碼如下:
clear
clc
close all
%初始化
x0=363300*10^3;%初始近地位置
y0=0;
u0=0;
v0=1074.82;%初始近地速度
%設置計算時間
h=0.5*60*60;%步長爲0.5個小時
t=0:h:27.5*24*60*60;%計算總時間維27.5天
y=ODE_RK4_hyh(t,h,[x0;y0;u0;v0]);
plot(y(1,:),y(2,:))%繪圖
a=(max(y(1,:))-min(y(1,:)))/2;%橢圓半長軸
b=(max(y(2,:))-min(y(2,:)))/2;%橢圓半短軸
e=sqrt(a^2-b^2)/a;%橢圓軌道離心率
function y=ODE_RK4_hyh(x,h,y0)
%4階RK方法
%h間隔爲常數的算法
y=zeros(size(y0,1),size(x,2));
y(:,1)=y0;
for ii=1:length(x)-1
yn=y(:,ii);
xn=x(ii);
K1=Fdydx(xn,yn);
K2=Fdydx(xn+h/2,yn+h/2*K1);
K3=Fdydx(xn+h/2,yn+h/2*K2);
K4=Fdydx(xn+h,yn+h*K3);
y(:,ii+1)=yn+h/6*(K1+2*K2+2*K3+K4);
end
end
function dydx=Fdydx(x,y)
%將原方程整理爲dy/dx=F(y,x)的形式
G=6.67E-11;%引力常數
M=5.965E24;%地球質量
ax=-G*M*y(1)/(y(1).^2+y(2).^2)^1.5;
ay=-G*M*y(2)/(y(1).^2+y(2).^2)^1.5/1;
dydx=[y(3);y(4);ax;ay];
end
驗證得到的週期大概小於27.5天(這個我沒細算)。驗證得到的偏心率爲0.0549,和百度百科上完全一致,證明該方法完全可以用在定量計算上。下圖爲平平無奇的月球軌道,我沒有用axis equal調整x軸和y軸比例,所以看上去離心率有點大。
我之後爲了效果,也爲了計算省事,不再設置真實的G、M等參數。
2雙星問題模型
上一章由於地球質量遠遠大於月球質量,所以假設地球不動,只建立月球的模型。
但是當兩個星球質量比較接近時,就不能忽略兩個星球間的相互作用了。
所以對於雙星問題,原先的常微分方程需要更改爲:
每一個行星有5個信息,分別爲質量、位置xy、速度uv。其中質量不發生改變,位置的導數爲速度,速度的導數爲加速度。
在matlab編程中,由於在計算引力過程中,兩個行星還可以窮舉,但是多個行星之後,就要考慮循環,第一層循環遍歷每一個行星,第二層循環計算每一個行星與其它行星之間的引力之和。這種雙循環在matlab中可能會導致計算速度太慢,所以我利用matlab裏的meshgrid方法,避免了使用循環,這也相當於向量加速。
代碼如下:
clear
clc
close all
%計算雙星系統,定性計算,沒有代入實際物理參數
mxyuv1=rand(5,1);%行星1,所有參數都隨機
mxyuv2=rand(5,1);%行星2,所有參數都隨機
h=1e-5;%步長
t=0:h:1;%時間
y=ODE_RK4_hyh(t,h,[mxyuv1;mxyuv2]);
plot(y(2,:),y(3,:),y(7,:),y(8,:))%繪軌跡圖
function y=ODE_RK4_hyh(x,h,y0)
%4階RK方法
%h間隔爲常數的算法
y=zeros(size(y0,1),size(x,2));
y(:,1)=y0;
for ii=1:length(x)-1
yn=y(:,ii);
xn=x(ii);
K1=Fdydx(xn,yn);
K2=Fdydx(xn+h/2,yn+h/2*K1);
K3=Fdydx(xn+h/2,yn+h/2*K2);
K4=Fdydx(xn+h,yn+h*K3);
y(:,ii+1)=yn+h/6*(K1+2*K2+2*K3+K4);
end
end
function dydx=Fdydx(x,y)
%將原方程整理爲dy/dx=F(y,x)的形式
N=numel(y)/5;%N個球體
G=1;%引力系數,這裏只做展示,不做定量計算
%計算兩個星球之間的引力
m=y(1:5:end);
x0=y(2:5:end);
y0=y(3:5:end);
[x1,x2]=meshgrid(x0,x0);
[y1,y2]=meshgrid(y0,y0);
[m1,m2]=meshgrid(m,m);
dx=x1-x2;
dy=y1-y2;
ax=-G.*dx./(dx.^2+dy.^2).^(1.5).*m2;
ay=-G.*dy./(dx.^2+dy.^2).^(1.5).*m2;
for k=1:N
ax(k,k)=0;
ay(k,k)=0;
end
%建立dydx
dydx=zeros(size(y));
%1 質量不變
dydx(1:5:N*5-4)=0;
%2 x導數
dydx(2:5:N*5-3)=y(4:5:N*5-1);
%3 y導數
dydx(3:5:N*5-2)=y(5:5:N*5-0);
%4 x加速度
dydx(4:5:N*5-1)=sum(ax,1);
%3 y加速度
dydx(5:5:N*5-0)=sum(ay,1);
end
雙星的軌跡如下(參數時隨機的,所以不一樣很正常):
可以看到,兩個行星在相互吸引後在不到一個週期內又快速互相甩出,當然之後可能還會再次吸引再次重複這個週期,但是我這裏時間設置比較少。
3多星問題模擬
2維平面
代碼還是以上一章的代碼爲主,星球的增加參考上一章節的即可,然後對最終演示做了億點優化。
clear
clc
close all
%計算雙星系統,不停循環,只顯示一部分點
%初始條件
Mn=3;%更改星星數量
h=5e-5;%步長
Nn=5000;%線段長度
%t=0:h:1;
% y0=rand(Mn*5,1);%隨機
% y0=[100,0 ,0 ,0 ,0 ,...
% 1 ,1.1,0.05 ,2.1,9 ,...
% 1 ,1.1,-0.05 ,-2.1 ,9 ]';%3星,雙星加恆星
% y0=[100,0 ,0 ,0 ,0 ,...
% 0.1,0.2 ,0 ,0 ,22 ,...
% 0.5,0.35,0 ,0 ,17 ,...
% 1 ,0.6 ,0 ,0 ,13 ,...
% 0.4 ,0.9 ,0 ,0 ,10]';%5星,恆星加4個行星
y0=[11,0 ,0 ,4.5 ,4 ,...
11,0.5 ,-0.132,-2 ,-1.8 ,...
11,-0.5,0.132 ,-2 ,-1.8]';%3星,8字形運動,沒調試好
y1=zeros(size(y0,1),Nn);
T=1;t=T*h;
y1(:,T)=y0;
while true
T=T+1;
t=T*h;
%計算下一步
if T<=Nn
y=ODE_RK4_hyh2(t,h,y1(:,T-1));
y1(:,T)=y;
else
y2=y1;
y=ODE_RK4_hyh2(t,h,y1(:,end));
y1=[y2(:,2:end),y];
end
%繪圖
if mod(T,100)==1
ax=figure(1);
ax.Color=[0 0 0.1];
clf
x_medium=0;y_medium=0;
if T<=Nn
%初始還沒有超過線條點數
hold on
for k=1:Mn
%計算顏色
color_hsv=colormap(hsv);
color_N=length(color_hsv);
F_color=color_hsv(round(color_N/Mn*k),:);
F_color=F_color*0.6+[1,1,1]*0.4;
cdata=[linspace(0,F_color(1),T+1)',linspace(0,F_color(2),T+1)',linspace(0.1,F_color(3),T+1)'];
cdata=reshape(cdata,T+1,1,3);
%繪製線條
patch([y1(k*5-5+2,1:T),NaN],[y1(k*5-5+3,1:T),NaN],1:T+1,'EdgeColor','interp','Marker','none',...
'MarkerFaceColor','flat','CData',cdata,'LineWidth',1);
x_medium=x_medium+y1(k*5-5+2,T);
y_medium=y_medium+y1(k*5-5+3,T);
end
hold off
x_medium=x_medium/Mn;
y_medium=y_medium/Mn;
else
%超過線條點數後
hold on
for k=1:Mn
%計算顏色
color_hsv=colormap(hsv);
color_N=length(color_hsv);
F_color=color_hsv(round(color_N/Mn*k),:);
F_color=F_color*0.6+[1,1,1]*0.4;
cdata=[linspace(0,F_color(1),Nn+1)',linspace(0,F_color(2),Nn+1)',linspace(0.1,F_color(3),Nn+1)'];
cdata=reshape(cdata,Nn+1,1,3);
%繪製線條
patch([y1(k*5-5+2,:),NaN],[y1(k*5-5+3,:),NaN],1:Nn+1,'EdgeColor','interp','Marker','none',...
'MarkerFaceColor','flat','CData',cdata,'LineWidth',1);
x_medium=x_medium+y1(k*5-5+2,end);
y_medium=y_medium+y1(k*5-5+3,end);
end
hold off
x_medium=x_medium/Mn;
y_medium=y_medium/Mn;
end
xlim([x_medium-1,x_medium+1]);
ylim([y_medium-1,y_medium+1]);
%W_deta=Gravity_Energy(y)/W
axis off
pause(0.1)
end
end
function y1=ODE_RK4_hyh2(x0,h,y0)
%4階RK方法
%只向後計算1步h
%y1=zeros(size(y0,1),1);
K1=Fdydx(x0,y0);
K2=Fdydx(x0+h/2,y0+h/2*K1);
K3=Fdydx(x0+h/2,y0+h/2*K2);
K4=Fdydx(x0+h,y0+h*K3);
y1=y0+h/6*(K1+2*K2+2*K3+K4);
end
function dydx=Fdydx(x,y)
%將原方程整理爲dy/dx=F(y,x)的形式
N=numel(y)/5;%N個球體
G=1;%引力系數,這裏只做展示,不做定量計算
%計算兩個星球之間的引力
m=y(1:5:end);
x0=y(2:5:end);
y0=y(3:5:end);
[x1,x2]=meshgrid(x0,x0);
[y1,y2]=meshgrid(y0,y0);
[m1,m2]=meshgrid(m,m);
dx=x1-x2;
dy=y1-y2;
ax=-G.*dx./(dx.^2+dy.^2).^(1.5).*m2;
ay=-G.*dy./(dx.^2+dy.^2).^(1.5).*m2;
for k=1:N
ax(k,k)=0;
ay(k,k)=0;
end
%建立dydx
dydx=zeros(size(y));
%1 質量不變
dydx(1:5:N*5-4)=0;
%2 x導數
dydx(2:5:N*5-3)=y(4:5:N*5-1);
%3 y導數
dydx(3:5:N*5-2)=y(5:5:N*5-0);
%4 x加速度
dydx(4:5:N*5-1)=sum(ax,1);
%3 y加速度
dydx(5:5:N*5-0)=sum(ay,1);
end
在文章最開頭,我已經演示了8字形雙星的運動(感覺軌道有誤差,結果越修正越歪)。下面演示一個5星運動(恆星加行星)
代碼本身還是有很多bug的,比如
1遇到兩個行星特別近的時候,就會出現差分步長太大導致的數值誤差,表現爲兩個行星突然朝相反的方向快速飛出。如果設計爲變步長的差分算法,或者設定一個碰撞機制,可以避免這個問題。
2視角問題,遇到多個行星的移動、拉近、遠離等視角自動跟蹤的方法還不是太滿意,有時候表現爲晃來晃去的。如果能夠做到交互式視角的話,可以避免這個問題,但是交互界面需要GUI或者appdesigner,比較複雜。
3維空間
在方程上,增加了z方向的位置,以及對應的速度w。每一個行星的信息爲7個。計算量上有所增加,但是具體算法沒有什麼變化。但是3維的視角問題更爲嚴重,所以我暫時還沒想好怎麼演示,如果之後寫關於交互的話,可能會把這一塊補上。