package AutoPilot; use strict; use warnings; # use diagnostics; =head1 Lunar Lander Autopilot The autopilot is called on every step of the lunar lander simulation. It is passed state information as an argument and returns a set of course correction commands. The lander world takes the surface of the moon (a circle!) and maps it onto a rectangular region. On the x-axis,the lander will wrap around when it hits either the left or right edge of the region. If the lander goes above the maximum height of the world,it escapes into the space and thus fails. Similarly,if the lander's position goes below 0 without ever landing on some solid surface,it "sinks" and thus fails again. The simulation is simple in the respect that if the langer goes at a high speed it may pass through the terrain boundary. The y-axis has normal gravitational physics. The goal of the autopilot is to land the craft at (or near) the landing zone without crashing it (or failing by leaving the world). =head2 Interface in a nutshell When the simulation is initialized,AutoPilot::Initialize() is called. Every clock tick,AutoPilot::ComputeLanding() is called by the simulator. For more explanation,see below. =cut # if you want to keep data between invocations of ComputeLanding,put # the data in this part of the code. Use Initialize() to handle simulation # resets. my $call_count = 0; my $gravity; my ($x_min,$y_min,$x_max,$y_max); my ($lander_width,$lander_height,$center_x,$center_y); my $target_x; my ($thrust,$left_right_thrust); my ($max_rotation,$max_left_right_thrusters,$max_main_thruster); my $ascend_height = 980; =head1 AutoPilot::Initialize() This method is called when a new simulation is started. The following parameters are passed to initialize: $gravity,a number,describing the gravity in the world $space_boundaries,a reference to an array with 4 numerical elements,($x_min,$y_max),describing the world boundaries $target_x,a number representing the target landing position $lander_capabilities,a reference to an array with 5 elements,($thrust,$left_right_thrust,$max_rotation,$max_main_thruster),describing the capabilities of the lander. $lander_dimensions,a reference to an array with 4 elements,($lander_width,$center_y),describing the dimensions of the lander. =head2 Details =head3 Dimensions The dimensions are given in 'units' (you can think of 'units' as meters). The actual numbers can take any real value,not only integers. =head4 World dimensions The lander world is a square region with a lower left corner at ($x_min,$y_min) and an upper right corner at ($x_max,$y_max). The measurement units of these dimensions will just be called units (think about units as meters). By definition,$x_max>$x_min and $y_max>$y_min. The default values for the lower left and upper right corners are (-800,0),and (800,1600),respectively. =head4 Lander dimensions The lander is $lander_width units wide and $lander_height high. The coordinates of the lander are always specified with respect to its center. The center of the lander relative to the lower left corner of the lander bounding Box is given by $center_x,$center_y. Thus,if ($x,$y) are the coordinates of the lander,($x-$center_x,$y-$center_y) and ($x-$center_x+$lander_width,$y-$center_y+$lander_height) specify the corners of the bounding Box of the lander. (Think of the lander as completely filling this Box.) The significance of the bounding Box of the lander is that a collision occurs if the bounding Box intersects with the terrain or the upper/lower edges of the world. If a collision occurs,as described earlier,the lander might have just landed,crashed or 'escaped' (and thus the lander Failed). The constraints on these values are: $lander_width>0,$lander_height>0,$center_x>0,$center_y>0. The default value for the width is 60 units,for the height it is 50,for $center_x it is 30,for $center_y it is 25. =head4 Forces The gravitational force is: $g The thrust exerted by the engine when fired is: $thrust The thrust exerted by the left/right thrusters when fired is: $left_right_thrust =head4 Limits to the controls Within a single timestep there are limits to how many degrees the lander may rotate in a timestep,and how many times the side thrusters,and main thruster,can fire. These are stored in: $max_rotation,$max_main_thruster =head4 Target The target landing zone that the lander is supposed to land at: $target_x which returns the string "any" if any safe landing site will do,or a number giving the x-coordinate of the desired landing site. Note: there is no guarantee that this is actually a safe spot to land! For more details about how the lander is controlled,see AutoPilot::ComputeLanding. =cut sub Initialize { my ($space_boundaries,$lander_capabilities,$lander_dimensions); ($gravity,$space_boundaries,$target_x,$lander_dimensions) = @_; ($x_min,$y_max) = @{$space_boundaries}; ( $thrust,$max_main_thruster) = @{$lander_capabilities}; ($lander_width,$center_y) = @{$lander_dimensions}; $call_count = 0; } =head1 AutoPilot::ComputeLanding() This method is called for every clock tick of the simulation. It is passed the necessary information about the current state and it must return an array with elements,describing the actions that the lander should execute in the current tick. The parameters passed to the method describe the actual state of the lander,the current terrain below the lander and some extra information. In particular,the parameters are: $fuel,a nonnegative integer describing the remaining amount of fuel. When the fuel runs out,the lander becomes uncontrolled. $terrain_info,an array describing the terrain below the lander (see below). $lander_state,an array which contains information about the lander's state. For more information,see below. $debug,an integer encoding whether the autopilot should output any debug information. Effectively,the value supplied on the command line after "-D",or if this value is not supplied,the value of the variable $autopilot_debug in the main program. $time,the time elapsed from the beginning of the simulation. If the simulation is reset,time is also reset to 0. =head2 Details of the parameters =head3 The terrain information The array referred to by $terrain_info is either empty,or it describes the terrain element which is just (vertically) below the lander. It is empty,when there is no terrain element below the lander. When it is non-empty,it has the following elements: ($x0,$y0,$x1,$y1,$slope,$crashSpeed,$crashSlope) where ($x0,$y0) is the left coordinate of the terrain segment,($x1,$y1) is the right coordinate of the terrain segment,$slope is the left to right slope of the segment (rise/run),$crashSpeed is the maximum landing speed to avoid a crash,$crashSlope is the maximum ground slope to avoid a crash. =head3 The state of the lander The array referred to by $lander_state contains the current position,attitude,and velocity of the lander: ($px,$py,$attitude,$vx,$vy,$speed) where $px is its x position in the world,in the range [-800,800],$py is its y position in the world,in the range [0,1600],$attitude is its current attitude angle in unit degrees,from y axis,where 0 is vertical,> 0 is to the left (counter clockwise),< 0 is to the right (clockwise),$vx is the x velocity in m/s (< 0 is to left,> 0 is to right),$vy is the y velocity in m/s (< 0 is down,> 0 is up),$speed is the speed in m/s,where $speed == sqrt($vx*$vx + $vy*$vy) =head2 The array to be returned To control the lander you must return an array with 3 values: ($rotation,$left_right_thruster,$main_thruster) $rotation instructs the lander to rotate the given number of degrees. A value of 5 will cause the lander to rotate 5 degrees counter clockwise,-5 will rotate 5 degrees clockwise. $left_right_thruster instructs the lander to fire either the left or right thruster. Negative value fire the right thruster,pushing the lander to the left,positive fire the left thruster,pushing to the right. The absolute value of the value given is the number of pushes,so a value of -5 will fire the right thruster 5 times. $main_thruster instructs the lander to fire the main engine,a value of 5 will fire the main engine 5 times. Each firing of either the main engine or a side engine consumes one unit of fuel. When the fuel runs out,the lander becomes uncontrolled. Note that your instructions will only be executed up until the limits denoted in $max_rotation,$max_side_thrusters,and $max_main_thruster. If you return a value larger than one of these maximums than the lander will only execute the value of the maximum. =cut sub ComputeLanding { my ($fuel,$terrain_info,$lander_state,$debug,$time) = @_; my $rotation = 0; my $left_right_thruster = 0; my $main_thruster = 0; # fetch and update historical information $call_count++; if ( ! $terrain_info ) { # hmm,we are not above any terrain! So do nothing. return; } my ($x0,$crashSlope) = @{$terrain_info}; my ($px,$speed) = @{$lander_state}; if ( $debug ) { printf "%5d ",$call_count; printf "%5s ",$target_x; printf "%4d,(%6.1f,%6.1f),%4d,",$fuel,$px,$attitude; printf "(%5.2f,%5.2f),%5.2f,$speed; printf "(%d %d %d %d,%5.2f\n",$x0,$crashSlope; } # reduce horizontal velocity if ( $vx < -1 && $attitude > -90 ) { # going to the left,rotate clockwise,but not past -90! $rotation = -1; } elsif ( 1 < $vx && $attitude < 90 ) { # going to the right,rotate counterclockwise,but not past 90 $rotation = +1; } else { # we're stable horizontally so make sure we are vertical $rotation = -$attitude; } # reduce vertical velocity if ($target_x eq "any"){ if (abs($slope) < $crashSlope){ if ($vy < -$crashSpeed + 6){ $main_thruster = 1; if (int($vx) < 1 && int ($vx) > -1){ $left_right_thruster = 0; } if (int($vx) < -1){ $left_right_thruster = 1; } if (int($vx) > 1){ $left_right_thruster = -1; } } } else{ if ( $py < $ascend_height) { if ($vy < 5){ $main_thruster=2; } } if ($py > $ascend_height){ $left_right_thruster = 1; if ($vx > 18){ $left_right_thruster = 0; } } } } if ($target_x ne "any"){ if ($target_x < $px + 5 && $target_x > $px - 5){ print "I made it here"; if (abs($slope) < $crashSlope){ if ($vy < -$crashSpeed + 1){ $main_thruster = 1; if (int($vx) < 1 && int ($vx) > -1){ $left_right_thruster = 0; } if (int($vx) < -1){ $left_right_thruster = 1; } if (int($vx) > 1){ $left_right_thruster = -1; } } } } if ($target_x != $px){ if ( $py < $ascend_height) { if ($vy < 5){ $main_thruster=2; } } if ($py > $ascend_height){ $left_right_thruster = 1; if ($vx > 10){ $left_right_thruster = 0; } } } } return ($rotation,$main_thruster); } 1; # package ends
对于代码长度很抱歉
所以有几件我想要这个自动驾驶仪程序.为了他们是:
>稳定着陆器(如果非零,将姿态和水平漂移降至零).一旦稳定:
>如果在目标之上,目标的段可以安全登陆,然后下降.
>否则上升至1200单位以上的安全高度.你可以放心地假设在这个高度或更高的高度上没有物体,而且在从最初的位置直线上升时,着陆器也不会发生任何事情.
>一旦达到安全的高度,登陆者就可以开始向水平方向前进,如果有目标,否则应该瞄准通过一个方向扫描地形可以感觉到的第一个安全着陆点.重要的是,着陆器在水平移动时保持其高度,因为它不能感测到其旁边的物体,并且可能存在任何低于该高度的物体.
>一旦达到了目标x坐标并被发现是安全的登陆,开始下降.
>如果达到目标x坐标,但是地形是不安全的,如果在向目标移动的过程中看到一个好点,则返回到目标位置,否则继续寻找一个好点.
>一旦看到一个好地方,只要把它放在好的安全上.
好的,我更新了代码.我的代码现在可以在所有测试中着陆(除了一个,已经厌倦了,代码工作足够近),没有目标.但是,我有很大的麻烦,弄清楚如何让登陆者登陆目标.有什么想法与我的代码到目前为止? (实际使用的代码在ComputeLanding子程序中找到)
解决方法
>着陆几乎相当于起飞时间扭转.唯一不能逆转的是燃料消耗. (这是否重要取决于模拟是否将燃料作为着陆器质量的一部分,在现实的模拟中,它应该是一目了然的,看起来你的可能不会.)
>在火箭中起飞的最佳方式(燃油效率)是以最大功率发射发动机,直到你足够快,然后将其关闭.你爬的越慢,你浪费的燃油就越多.
因此,降落火箭的最佳方式是自由下落(在可能的初始燃烧之后,直到最后的可能时刻),然后以全功率的方式对发动机进行起火,以便在着陆垫上方停下(或以您认为可接受的任何速度击打垫,如果大于零).
你可以计算什么时候打开引擎呢? (即使你不能直接做到这一点,你可以通过二进制搜索来总是近似,只需选择一个点并模拟发生的情况:如果你崩溃,早点开始刻录;如果在击球之前停止,稍后再开始. )
(Ps.这对Perl编程课程来说似乎是一个相当愚蠢的练习,是的,你一定可以在Perl中解决这个问题,但Perl没有什么可以特别适合这个练习,事实上,这根本不是一个编程问题,但是一个数学问题,唯一的编程方面就是将数学解决方案(一旦找到)翻译成一个工作程序.)