总结火星着陆器

作者: Dave Taylor

在我之前的几篇文章 (“让我们用火星着陆器去火星”“火星着陆器,第二次尝试:坠毁在表面”) 中,我一直在构建经典视频游戏《月球着陆器》的变体,进行了一些简化和一个重大改变:火星重力而不是月球重力。月球的重力是地球的 1/6;而火星的重力约为地球的 1/3,这使得驾驶着陆器进行软着陆更加刺激。

棘手的问题可能是模拟黑洞,但这很容易通过设置一个非常非常大的引力值来实现,但安全着陆就不那么容易了。实际上,除非您正在编写《星际穿越 2》的剧本,否则它并没有太大的吸引力。

游戏的起始参数将火星重力设置为 3.722 米/秒/秒,宇宙飞船在大气层中的高度为 500 米(约 1/3 英里)。算一下,这意味着玩家只有 15 秒多的时间来避免坠毁在火星表面。

创建界面

我的上一篇文章(在 2016 年 10 月的 LJ 期刊中)以演示代码结束,该代码提供了基本上是自由落体穿过火星大气层的每秒数据


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.

除非你躺在一个非常非常软的沙发上,否则这不是一个很好的着陆方式。我对添加有趣界面的第一次尝试是每秒停止一次,并让用户有机会指定他们是否要启动反向火箭以及在接下来的秒内燃烧多少燃料。

过早地燃烧燃料,你最终可能会射入太空,或者只是水平飞行然后坠落到地面。然而,在这个第一个版本中,用户将拥有无限的燃料(尽管在现实生活中燃料是有限的,并且随着燃料的燃烧,飞船会变轻,从而减少引力)。

以下是代码的核心


echo "$time seconds into flight: speed: $speed m/s \
      and altitude: $altitude meters."
echo -n "Fire retro rockets? (burn rate: 0-100): "
read thrust
if [ -z "$thrust" ] ; then
  thrust=0
fi

最后几行允许玩家只需按 Enter 键,就相当于输入零——非常简单。现在让我们尝试着陆该死的宇宙飞船


Time: 1: Speed: -3.722 m/s and altitude: 496.278 meters.
Fire retro rockets? (burn rate: 0-100):

Time: 2: Speed: -7.444 m/s and altitude: 488.834 meters.
Fire retro rockets? (burn rate: 0-100):

Time: 3: Speed: -11.166 m/s and altitude: 477.668 meters.
Fire retro rockets? (burn rate: 0-100):

Time: 4: Speed: -14.888 m/s and altitude: 462.780 meters.
Fire retro rockets? (burn rate: 0-100): 15

Time: 5: Speed: -3.610 m/s and altitude: 459.170 meters.
Fire retro rockets? (burn rate: 0-100):

Time: 6: Speed: -7.332 m/s and altitude: 451.838 meters.
Fire retro rockets? (burn rate: 0-100):

Time: 7: Speed: -11.054 m/s and altitude: 440.784 meters.
Fire retro rockets? (burn rate: 0-100):

Time: 8: Speed: -14.776 m/s and altitude: 426.008 meters.
Fire retro rockets? (burn rate: 0-100): 15

Time: 9: Speed: -3.498 m/s and altitude: 422.510 meters.
Fire retro rockets? (burn rate: 0-100):

Time: 10: Speed: -7.220 m/s and altitude: 415.290 meters.
Fire retro rockets? (burn rate: 0-100):

Time: 11: Speed: -10.942 m/s and altitude: 404.348 meters.
Fire retro rockets? (burn rate: 0-100):

Time: 12: Speed: -14.664 m/s and altitude: 389.684 meters.
Fire retro rockets? (burn rate: 0-100): 15

Time: 13: Speed: -3.386 m/s and altitude: 386.298 meters.
Fire retro rockets? (burn rate: 0-100):

请注意,这里我在燃料方面很保守,在第 4、8 和 12 秒启动推进器。这使我能够在下降 13 秒后速度仅为 3.386 米/秒,同时从 500 米下降到 386 米。如果燃料充足,这并不是一个糟糕的着陆策略!

添加一些限制和约束

现在,如果我添加一些基本约束会怎样?如果燃料有限呢?如果对最大可能的推力进行限制,这样你就不会只是决定像石头一样掉到地面,然后在最后一秒施加巨大的推力来平稳着陆呢?

当然,在现实生活中,这可能会产生比人类能够承受的更大的重力加速度,但我不会担心人们在玩火星着陆器时会失去知觉。无论如何,这可能会对评论不利!

尽管如此,从 100 燃料和 30 的最大推力开始应该会很有趣。这可以在脚本顶部的几个变量中捕获(然后可以根据需要使用起始参数进行更改)。

脚本的主要数学运算在五行代码中捕获,使其非常容易理解


speed=$( $bc <<< "scale=3; $speed + $gravity + $thrust" )
thrust=0           # rocket fires second by second
altitude=$( $bc <<< "scale=3; $altitude + $speed" )
alt=$( echo "$altitude" | cut -d\. -f1 )
time=$(( $time + 1 ))

请注意 $alt 变量。那是用于显示的高度的整数部分。脚本实际上将更精确的值保留为 $altitude

提醒一下,我正在使用 bc 二进制计算器,在 Linux 世界中,您需要指定小数点后要保留多少位精度。默认值为零,使 bc 的工作方式类似于 expr

一旦充分简化(这是为数不多的不适合在 NASA 工作的时候之一!),数学运算就很简单了,因此大部分代码都处理用户交互。

但是在开始之前,让我们先列出脚本的所有起始参数


speed=0            # > 0 is climbing, < 0 is falling
gravity="-3.722"   # gravity pulls ya down
accel=0            # start with zero acceleration
height=500         # Note that 1609 meters = 1 mile
fuel=100           # limited fuel
maxthrust=30       # the ship, she canna handle greater!
maxheight=$(( 2 * $height )) # above you shoot into space
altitude=$height   # current altitude above the surface
alt=$altitude      # integer value of altitude
thrust=0
outoffuel=0        # not yet true

指定所有这些值后,游戏的主要输入循环将在大约 20 行代码中捕获


if [ $alt -gt $maxheight ] ; then
  echo "Well heck. You used too much thrust and have \
    escaped the gravitational pull of Mars. You're \
    doomed to float off into space and die. Bummer."
  exit 1
elif [ $alt -gt 0 ] ; then
  echo "Time: ${time}: Speed: $speed m/s and \
        altitude: $altitude meters."
  if [ $fuel -gt 0 ] ; then
    echo -n "Fire retro rockets? (burn: 0-${maxthrust}): "
    read thrust
    echo ""
    if [ -z "$thrust" ] ; then
      thrust=0
   elif [ $thrust -gt $maxthrust ] ; then
      echo "** Ya canna handle that much thrust! \
         Reset to $maxthrust" ; echo ""
      thrust=$maxthrust
    fi
    fuel=$(( $fuel - $thrust ))       # burn, baby
  else
    if [ $outoffuel -eq 0 ] ; then
      echo "( out of fuel )"
      outoffuel=1
    fi
  fi
fi

请注意,如果用户指定的推力过大,程序只会将其重置为 $maxthrust——安全第一,您懂的。否则,上面的代码应该很容易理解(并且还要注意,长而有助记符的变量名总是使代码更具可读性!)

最后,让我们试一试


Time: 1: Speed: -3.722 m/s and altitude: 496.278 meters.
Fire retro rockets? (burn rate: 0-30): 50

** Ya canna handle that much thrust! Reset to 30

Time: 2: Speed: 22.556 m/s and altitude: 518.834 meters.
Fire retro rockets? (burn rate: 0-30): 30

Time: 3: Speed: 48.834 m/s and altitude: 567.668 meters.
Fire retro rockets? (burn rate: 0-30): 30

Time: 4: Speed: 75.112 m/s and altitude: 642.780 meters.
Fire retro rockets? (burn rate: 0-30): 30

Time: 5: Speed: 101.390 m/s and altitude: 744.170 meters.
( out of fuel )
Time: 6: Speed: 97.668 m/s and altitude: 841.838 meters.
Time: 7: Speed: 93.946 m/s and altitude: 935.784 meters.
Well heck. You used too much thrust and have escaped the
gravitational pull of Mars. You're doomed to float off
into space and die. Bummer.

糟糕——反向火箭推力太大了,我在下降仅五秒后就耗尽了燃料!真是倒霉。

修改和改进

我鼓励您自己研究这个脚本,看看您能用它做些什么有趣的事情。一种可能性是简单的输入脚本,允许用户指定时间和燃烧量,并自动应用它们(这可以像 time:burn time:burn 一样简单作为起始参数)。

为了追求真实性,您还可以返回并计算引力,使其成为飞船重量 + 燃料的函数,这样随着燃料的燃烧,火星表面的引力就会减小。或者,这可能物理学成分太高了!

另一种可能性:使重力成为可选的起始参数,这样您也可以创建金星着陆器、海王星着陆器等等。在您进行操作时,您还可以让玩家从命令行指定最大推力和燃料。

无论如何,祝您火星着陆器好运。在我的下一篇文章中,我将转向一个全新的方向——这可能仍然与月球有关。

Dave Taylor 长期以来一直在 UNIX 和 Linux 系统上编写 shell 脚本。他是 Learning Unix for Mac OS XWicked Cool Shell Scripts 的作者。您可以在 Twitter 上通过 @DaveTaylor 找到他,也可以通过他的技术问答网站联系他:Ask Dave Taylor

加载 Disqus 评论