上次,兩次前我們對拋物線運動進行了物理模擬。正如承諾的那樣,這次我想討論天體的運動。
眾所周知,當有三個或更多天體(三體問題)時,受引力束縛天體的運動無法得到解析求解。基於計算機的數值模擬對於解決這些類型的問題很有用。
但是處理多個引力比較麻煩,所以我將首先嘗試模擬一個繞著單個天體運行的小物體……具體來說,是繞地球運行的太空船或人造衛星。與地球相比,衛星的質量可以忽略不計,因此我們可以認為唯一的引力就是地球的引力,這大大簡化了問題。
準備畫布
上次到目前為止,座標系的原點位於左下角。這次我們使用具有中心原點的常見笛卡爾座標系。而且,由於它是圓週運動,所以正方形比長方形更好。
</畫布>
const canvas = document.getElementById('canvas'), ctx = canvas.getContext('2d'); const dpr = window.devicePixelRatio || 1, // 裝置像素比 width = canvas.width, Canvas 畫布尺寸*= dpr; canvas.height *= dpr; // 使用 CSS 恢復原始大小 canvas.style.width = width + 'px'; canvas.style.height = height + 'px'; // 放大 Canvas 繪製本身 1/scale(dprx.); ,使原點位於中心 ctx.translate(width / 2, -height / 2);
規模
和 翻譯
如果你把所有內容寫在一行上,它看起來像這樣。
ctx.transform(dpr, 0, 0, -dpr, 寬度* dpr / 2, 高度* dpr / 2);
思維方式
上次和以前一樣,我們測量時間併計算位置和速度的變化,但這一次,加速度(重力)會根據位置而變化。
作用於兩個物體之間的引力可以用以下公式計算。
(F 是引力的大小,格 萬有引力常數,毫米 是每個物體的質量,r 是物體間的距離)
將其代入運動方程,我們得到
米 可以被擦除。因此我們可以看出,物體在單受重力作用時運動的加速度只取決於重力源的質量,與物體本身的質量無關(這也是為什麼重物體與輕物體下落速度相同的原因)。
換句話說,我們不需要考慮衛星的質量,因為假設它足夠小,不會施加任何引力。您所需要的只是地球的質量及其與地心的距離。此外,地球中心固定在座標原點[0,0]。
單位和常數
距離的單位是公里,質量的單位是公斤。應使用。
萬有引力常數為 6.674 × 10-20公里3s-2公斤-1 被使用。此外,地球的質量為 5.972 × 1024公斤,半徑為 6.378 × 103假設為 km。這些常量應該提前定義。
const G = 6.674 * 1e-20, // 萬有引力常數 Me = 5.972 * 1e24, // 地球質量 Re = 6.378 * 1e3; // 地球半徑
由於我們這次只考慮地球引力,所以我們也將準備一個地球引力係數常數,它是地球質量和引力常數的乘積。我們還需要準備一個比例常數,並確定 1px 代表多少公里。
// 地球重力係數 const K = G * Me; // 比例 const scale = 30; // [km/px]
天文課
建立一個類別來處理天體。此位置在 XY 座標中內部處理,但初始化時可以在極座標(角度和距離)中指定。
三角函數 數學.cos
, 數學
由於以弧度作為參數,因此建立一個可以使用直覺的度數方法執行計算的輔助函數很有用。
/** sin */ const sin = deg => { deg %= 360; if (deg === 180) { return 0; // Math.sin 有錯誤,因此傳回正確的值。 90 || deg === 270) { return 0; // Math.cos 有錯誤,因此回傳正確的值。
距中心距離 r ,它與 x 軸的夾角 程度 我們假設一下。
let objectList = []; // 管理陣列/**天體類別 */ class AstroObject { const (attr) { // 位置 this.x = attr.r * cos(attr.deg); this.y = attr.r * sin(attr.deg); } } }
類似地,速度可以透過大小和角度來指定。參考角度可以是任何位置,但這裡我們將使用 r 軸(徑向)作為參考。極座標系的旋轉方向恆定為90度。這個角度加上位置角就是速度相對於 x 軸的角度。
速度的大小v,相對於 r 軸的角度為電壓水平我們假設一下。
/** 天體類別 */ class AstroObject { 建構子(attr) { ... // 速度 this.vx = attr.v * cos(attr.vdeg + attr.deg);this.vy = attr.v * sin(attr.vdeg + attr.deg);this.vy = attr.v * sin(attr.vdeg + attr.deg);
如上所述,品質不是必需的。為了容納多顆衛星,我們將陣列本身儲存在建構函數中。
距中心距離r和角度拉德, 程度我們也來設定一個可以進行向後計算的 getter。
... // 取得與中心的距離 r() { return Math.hypot(this.x, this.y); } // 取得角度 rad() { // x = 0 if (!this.x) { if (!this.y) { return 0; // 如果 [0, 0] 則回傳 0 if ( 90° } } let rad = Math.atan(this.y / this.x); // arctan(y/x) : -π/2 到 π/2 if (this.x < 0) { // 第二和第三象限 rad += Math.PI; } else if (this.x > 0 & 象限數: rad; } 取得 deg() { 回傳 this.rad * 180 / Math.PI; } ...
新增繪圖和更新方法。
... draw() { // 繪製一個 4px x 4px 的正方形 ctx.beginPath(); ctx.rect(this.x / scale - 2, this.y / scale - 2, 4, 4); ctx.fill(); } update(this. x * dt; this.y += this.vy * dt; this.vx += grave * cos(this.rad) * dt; this.vy += grave * sin(this.rad) * dt; } ...
集會
在畫布上繪圖和更新上次和以前基本上一樣。我們只需添加一點點來融入繪製地球的過程。
const fps = 30, // FPS dt = 1 / fps; // 每幀的秒數 let startTime, // 開始時間 frameCount = 0; // 已過去的幀數 / ** 繪製地球 */ const drawEarth = () => { ctx.save(); ctx.fillStybe 'ctx. x.arc(0, 0, Re / scale, 0, Math.PI * 2); // 在中心畫一個圓圈 ctx.fill(); ctx.restore(); } /** tick */ const tick = (time) => { if (!startTime) startTime = 折扣; // 迄今為止已過去的幀數 let df = currentFrame - frameCount // 從上次增加// 以訊框增量重複更新程序 while (df) { // 更新物件 objectList.forEach((obj, i) => { if (obj) { obj.update(dt); // 碰撞檢查 if (obj.r <= Re) { objectList[i] = null + 15;陣列中移除碰撞的物件。
現在您就可以開始了。
實踐
讓我們實際發射一顆衛星看看。在這裡我們將把它放置在高度400公里的圓形軌道上,與國際太空站相同。
圓形軌道的軌道速度可用以下公式計算:
這約為7.67公里/秒。使用該值對物件進行分類。 天文物體
創建 的一個實例。
const satellite = new AstroObject({ r: Re + 400, // 地球半徑 + 高度 deg: 90, v: 7.67, vdeg: 90 });
進而 打鉤()
啟動程式以開始模擬。
看
筆
SHIN Inc.(@shin-inc)的 Orbital Motion
在 CodePen 上。
你怎麼認為? 它可能看起來就像您看到的一個圓圈和一個小正方形。 30公里顯示為1px,因此以每秒7.67公里的速度移動1px大約需要4秒。由於速度相對於尺度來說太慢,所以看起來好像它們幾乎沒有移動。在400公里的高度,它完成一次旋轉大約需要一個半小時。
如果一直這樣觀看一個半小時會比較困難,所以我要進行改進,讓它能夠以兩倍的速度播放。方法很簡單,以雙倍的速度重複一幀內的更新過程。
const speed = 200; // 播放速度... /** tick */ const tick = (time) => { ... let df = currentFrame - frameCount // 與上一次相比的幀增量 df *= speed; // 按速度增加進程數 // 按幀增量重複更新過程 while (df) { ... } ... }
這將以更快的速度播放。
看
筆
SHIN Inc.(@shin-inc)的 Orbital Motion x200
在 CodePen 上。
你可以清楚地看到它沿著地球的圓形軌道運行。嘗試不同的速度和高度。
同時操作多顆衛星是可能的,但隨著衛星數量的增加,負載自然也會增加。另外,請注意,速度越高,負載越大,處理速度越慢。
任務
這次我們創造了一個地球軌道的模擬。最後,以下是一些未來的改進:
改變中心恆星
如果把中心恆星變成一個物體,以便可以自由調整它的參數,而不是僅僅把它固定在地球上,應用範圍將大大擴展。您將能夠自由模擬圍繞虛構行星(包括火星和金星)的軌道。
多重重力源
我希望能夠處理多個引力源,例如地球和月球,而不是像人造衛星這樣的小天體。對於繞地球運行的軌道,可以忽略月球的引力,但如果你想處理靠近月球的軌道,你必須考慮兩者的引力。
當處理多個重力源時,必須分別處理重力加速度和位置更新的計算,這使得處理更加複雜。
數值改進
現在的積分計算方法叫歐拉法,這種方法不太準確。因此,可以考慮用跳蛙法等更精確的方法來取代它。如果計算不精確,歐拉方法也可以,因此優先順序較低。
獎金
我們將介紹物體在軌道上如何以違反地面直覺的方式運作。
前方紅色物體飛行高度400公里,速度為7.67公里/秒。有一藍色物體以相同的高度和8公里/秒的速度追趕。
看
筆
SHIN Inc.(@shin-inc)的 Orbital Chaser Motion
在 CodePen 上。
可以看到,一開始好像距離越來越近,但是逐漸地距離增加,最後藍色物體就追不上紅色物體了。如果您試圖在賽道上追逐某人,僅僅提高速度是行不通的。
那你該怎麼做呢?