火星着陆器,第二次尝试:坠毁在表面
在我的上一篇文章中,我几乎花了整篇文章探讨引力物理学,这是一个多么不太可能的话题。重点是编写经典街机游戏《月球着陆器》的一个版本,但这一次,它将在红色星球火星而不是围绕地球运行的那个麻点岩石上着陆。
然而,作为一个 shell 脚本,它完全是关于物理学的,而不是关于用户界面的,因为在 Bourne Shell 中实现矢量图形有点棘手——至少可以这么说!
为了使解决方案只有几十行而不是几千行,我将问题简化为二维,并假设安全的、平坦的着陆空间。然后,问题就变成了前向速度(这很容易计算)和向下速度(这很棘手,因为它具有恒定的引力拉力,当你发射反向火箭来补偿并因此避免坠毁到行星表面时)。
如果与 Space X 或 NASA 合作,那么真正的火星着陆器将有很多因素需要考虑,尤其是航天器的质量:随着燃料的燃烧,质量会减少,这是一个引力计算不能忽略的细微差别。
然而,这超出了本项目的范围,因此我将使用一些高度简化的数学方法来代替,从一维下降问题开始
speed = speed + gravity
altitude = altitude - speed
令人惊讶的是,这效果非常好,尤其是在大气层可以忽略不计的情况下。在地球表面着陆具有更多的大气阻力和天气效应的复杂性,但看看火星,而不是它作为巴索姆的辉煌时代,它是没有大气层的。
在我的上一篇文章中,我使用了英尺作为测量单位,但现在是时候切换到公制了,因此对于模拟游戏,我使用火星重力 = 3.722 米/秒/秒。宇宙飞船将在 500 米(约 1/3 英里)的高度进入大气层,玩家只有 15 秒多一点的时间来避免坠毁到火星表面,终端速度为 59 米/秒。
由于我正在制作游戏,因此计算以一秒为增量执行,这意味着你实际上可以在任何时候使用反向火箭来补偿重力的拉力,并希望能够着陆,而不是坠毁到火星上!
方程只发生了一点点变化
speed = speed + gravity + thrust
同样,有很多复杂的天体机械公式可以计算出反向火箭燃烧产生的力和燃料消耗,但为了简化,我假设燃料是以输出力来衡量的:每秒的反向推力米数。
也就是说,如果你以每秒 25 米的速度下降,施加 25 个单位的推力将完全补偿并使你达到零下降速度,基本上悬停在表面之上——直到不可避免的引力开始把你拉回行星表面,至少!
重力随着距离的减小而减小,因此过多的推力可能会让你完全摆脱行星的引力。那可不好。为了包含这种可能性,我将设置一个上限高度。飞到那个高度以上,你就已经挣脱束缚,注定要漂浮到太空中。
浮点数学Shell 脚本使处理整数数学非常容易,但任何实际计算都需要使用浮点数来完成,这在 shell 中可能很棘手。因此,我不使用 $(( ))
表示法或 expr
,而是要利用 bc
,即二进制计算器程序的力量。
由于是在 shell 脚本中,这有点笨拙,所以我将使用一种相当古怪的符号约定,将每个计算限制为一行
speed=$( $bc <<< "scale=3; $speed + $gravity + $thrust" )
默认情况下,由于我不理解的原因,bc
也希望仅使用整数值,因此要求它求解方程 1/4,它将返回 0。但是,指示在小数点后要跟踪多少位数字,它会工作得更好。这就是我上面使用 scale=3
所做的事情。这在小数点后提供了三位精度,足以使游戏正常运行。
考虑到这种表示法,我终于可以编写火星着陆器的基本代码了
while [ $altitude -gt 0 ]
do
speed=$( $bc <<< "scale=3; $speed + $gravity + $thrust" )
altitude=$( $bc <<< "scale=3; $altitude + $speed" )
time=$(( $time + 1 ))
done
显然,有很多变量需要用正确的值来实例化,包括重力(-3.722)、高度(500 米)、推力(反向火箭启动时功率降低,因此初始值为 0),速度和时间也应都设置为 0。
然而,即使使用这个小片段,也存在一些问题。例如,控制 while
循环的条件测试是高度是否大于零。但是高度是一个浮点数,因此测试失败。简单的解决方案是第二个变量,它只是高度的整数部分
alt=$( echo $altitude | cut -d\. -f1 )
一个问题解决了。
推力是火箭发射时施加的力,因此这必须是用户在每秒(游戏的“游戏”部分)之后可以输入的内容。但是一旦发射,它应该再次关闭,因此每次计算完成后,都需要将推力设置回零。
这里还有一个关于正值和负值的棘手挑战。重力应该是一个负值,因为它不可避免地将飞船拉向行星的中心。因此,推力应该是正值,因为它正在对抗重力。这意味着速度在朝向表面下降时将为负值,而在向上射击时将为正值,有可能完全逃脱行星的引力场。
这是核心程序循环的改进版本
while [ $alt -gt 0 ]
do
speed=$( $bc <<< "scale=3; $speed + $gravity + $thrust" )
thrust=0 # rocket fires on a per-second basis
altitude=$( $bc <<< "scale=3; $altitude + $speed" )
alt=$( echo "$altitude" | cut -d\. -f1 )
time=$(( $time + 1 ))
echo "$time seconds: speed: $speed m/s
altitude: $altitude meters."
done
如果你只想在没有任何火箭发射的情况下坠落到行星上,这就可以了。它看起来像这样
1 seconds: speed: -3.722 m/s altitude: 496.278 meters.
2 seconds: speed: -7.444 m/s altitude: 488.834 meters.
3 seconds: speed: -11.166 m/s altitude: 477.668 meters.
4 seconds: speed: -14.888 m/s altitude: 462.780 meters.
5 seconds: speed: -18.610 m/s altitude: 444.170 meters.
6 seconds: speed: -22.332 m/s altitude: 421.838 meters.
7 seconds: speed: -26.054 m/s altitude: 395.784 meters.
8 seconds: speed: -29.776 m/s altitude: 366.008 meters.
9 seconds: speed: -33.498 m/s altitude: 332.510 meters.
10 seconds: speed: -37.220 m/s altitude: 295.290 meters.
11 seconds: speed: -40.942 m/s altitude: 254.348 meters.
12 seconds: speed: -44.664 m/s altitude: 209.684 meters.
13 seconds: speed: -48.386 m/s altitude: 161.298 meters.
14 seconds: speed: -52.108 m/s altitude: 109.190 meters.
15 seconds: speed: -55.830 m/s altitude: 53.360 meters.
此时,飞船正以 55 米/秒的速度下降,并且仅在行星表面上方 53 米处,因此你可以指望会发生一场巨大而丑陋的撞击。砰!
在第 15 秒,你可以施加 55 个单位的推力来将飞船猛拉回零速,但是如果你没有 55 个单位的燃料,或者由于火箭设计(和乘客生存)的限制,你在任何给定单位时间内可以施加的最大推力是 25,那该怎么办?
这就是有趣的地方。
在我的下一篇文章中,我将深入探讨这些限制,并最终为程序添加一些交互性。现在,小心驾驶这艘特殊的宇宙飞船。毕竟,更换零件的费用要从你的预算中出!
感谢 Joel Garcia 和 Chris York 在所有引力公式方面的持续帮助。任何错误和故障都完全归咎于我生疏的物理知识。