MIT6_0002F16_ProblemSet3

MIT6_0002F16_ProblemSet3

实验内容:

本次实验主要围绕模拟一个机器人打扫房间的场景进行,通过随机模拟以及利用各种数学公式等,完成模拟。因此对随机类中的许多函数需要掌握。

这个实验非常有趣,因为提供的test函数将会使自己的函数可视化!

实验流程:

阅读实验pdf,可知实验具体要求,按照要求一步一步解决。

Problem 1: Implementing the RectangularRoom and Robot classes

这一部分主要是填写两个类:RectangularRoom类以及Robot类,分别表示一个矩形房间和一个机器人,在此处二者均为抽象类,不做实例化。在进行代码之前先看一下Position类,该类表示二维空间的一个坐标,并掌握该类的函数意义和使用方法。两个类中均有部分函数标注有not change,表示不需要在抽象类中填写。

完成Rectangular类时:成员变量tiles表示将大矩形分成的许多小方格对应的脏乱程度,因此在这里用字典表示,且字典映射关系为 position->dirt_amount 。需要注意的是小方格用它左下角的点表示即可,因此需要用math.floor等函数判断一个任意位置的点所在的小方格位置。还有就是要注意dirt_amount不能为负数。

完成Robot类时:机器人最初的位置、角度是随机确定的,用类Position实例化即可表示位置,因此需要用到Position类中的get_random_position,以及random类中的random.uniform。

具体代码:(为避免冗余,报告中将自带的英文注释删去)

class RectangularRoom(object):
   
    def __init__(self, width, height, dirt_amount):
        #根据要求将RectangularRoom的成员变量赋值
        self.width=width
        self.height=height
        #这里需要注意的是灰尘量指的是每个小格的灰尘量,故定义小格并赋值,用字典表示
        if width > 0 and height > 0 and dirt_amount >= 0:
            #tiles表示字典,映射关系为 position:(x,y)->dirt_amount
            self.tiles = {}
            #用循环赋值
            for x in range(width):
                for y in range(height):
                    self.tiles[(x, y)] = dirt_amount
        else:
            raise ValueError
    
    #将对应位置的灰尘量减去capacity,当然需要注意不能是负值
    def clean_tile_at_position(self, pos, capacity):
        #这里需要注意的是,pos代表的是一个点的坐标,所以这里需要将其对应的方格表示出来
        #用math.floor求出不大于x,y的正整数
        pos_x, pos_y = math.floor(pos.get_x()), math.floor(pos.get_y())
        #如果capacity小于对应的方格的灰尘值,则说明不会产生负值,减去即可
        if capacity <= self.tiles[(pos_x, pos_y)]:
            self.tiles[(pos_x, pos_y)] -= capacity
        #否则为了避免负数,将其直接设为0
        else:
            self.tiles[(pos_x, pos_y)] = 0
            
    #判断某一方格是否清扫干净       
    def is_tile_cleaned(self, m, n):
        #判断(m,n)代表的灰尘值是否为0,为0说明清除干净
        return self.tiles[(m,n)]==0
    
    #返回总的干净的格子数目
    def get_num_cleaned_tiles(self):
        #直接遍历tiles,如果出现0,则加1
        num_cleaned_tiles = 0
        for the_tile ,tile_dirt in self.tiles.items():
            if tile_dirt == 0:
                num_cleaned_tiles += 1

        return num_cleaned_tiles
                    
    #判断某一点是否在该房间内    
    def is_position_in_room(self, pos):
        #得到pos对应的正整数点,然后查看其是否在tiles中
        x, y = math.floor(pos.get_x()), math.floor(pos.get_y())
        return (x, y) in self.tiles
    
    #返回某一tile的dirt_amount值    
    def get_dirt_amount(self, m, n):
        return self.tiles[(m,n)]
    
    #返回总的tiles数量  ,这里标注了不要改变   
    def get_num_tiles(self):
        # do not change -- implement in subclasses.
        raise NotImplementedError 
    #判断某一pos是否合法,这里标注了不要改变     
    def is_position_valid(self, pos):
        # do not change -- implement in subclasses
        raise NotImplementedError         
    #得到一个随机的位置,  这里标注了不要改变
    def get_random_position(self):
        # do not change -- implement in subclasses
        raise NotImplementedError        
class Robot(object):
    def __init__(self, room, speed, capacity):
        #根据参数赋值即可
        self.room = room
        self.speed = speed
        self.capacity = capacity
        #机器人的位置和方向均为随机的
        self.position = room.get_random_position()
        self.direction = random.uniform(0, 360)

    #返回机器人的位置
    def get_robot_position(self):
        return self.position
    #返回机器人的方向
    def get_robot_direction(self):
        return self.direction
    #设置机器人的位置
    def set_robot_position(self, position):
        self.position=position
    #设置机器人的方向
    def set_robot_direction(self, direction):
        self.direction=direction
    #移动位置并打扫  在此处标注了不要修改
    def update_position_and_clean(self):
        # do not change -- implement in subclasses
        raise NotImplementedError

Problem 2: Implementing EmptyRoom and FurnishedRoom

该部分主要是完成两个类:EmptyRoom类 以及 FurnishedRoom 类。二者的区别主要就在于FurnishedRoom在Room的基础上,增添了家具,家具将占据一块区域,该区域机器人将不可进入。因此,FurnishedRoom在实现某点是否可用、某小方格是否可用、随机产生一个合法位置等函数时,不仅需要判断是否在房间内,还要判断是否没有被家具占据,FurnishedRoom 类中成员变量 furniture_tiles存取被家具占据的小方格坐标。需要注意的是加入家具时是随机大小、随机位置。

代码如下:(为避免冗余,报告中将自带的英文注释删去)

#不带家具的空房间
class EmptyRoom(RectangularRoom):
    #返回小方格数目
    def get_num_tiles(self):
        return len(self.tiles)
    
    #判断一个点是否在房间中,调用父类函数is_position_in_room即可
    def is_position_valid(self, pos):
        return self.is_position_in_room(pos)
    
    #得到一个随机的合法位置 ,利用随机random.uniform,再判断随机的位置是否合法
    def get_random_position(self):
        while True:
            rand_x = random.uniform(0.0, self.width)
            rand_y = random.uniform(0.0, self.height)
            rand_Pos =  Position(rand_x, rand_y)
            if self.is_position_valid(rand_Pos):
                return rand_Pos
#FurnishedRoom表示一个长房间包括一个家具,机器人不能通过家具所在的区域              
class FurnishedRoom(RectangularRoom):
    def __init__(self, width, height, dirt_amount):
        RectangularRoom.__init__(self, width, height, dirt_amount)
        self.furniture_tiles = []
     
    #将家具占据的小方格均加入到     furniture_tiles  中
    def add_furniture_to_room(self):
        furniture_width = random.randint(1, self.width - 1)
        furniture_height = random.randint(1, self.height - 1)
    
        f_bottom_left_x = random.randint(0, self.width - furniture_width)
        f_bottom_left_y = random.randint(0, self.height - furniture_height)

        for i in range(f_bottom_left_x, f_bottom_left_x + furniture_width):
            for j in range(f_bottom_left_y, f_bottom_left_y + furniture_height):
                self.furniture_tiles.append((i,j))     
                
    #判断一个方格是否被家具占据
    def is_tile_furnished(self, m, n):
        return (m, n) in self.furniture_tiles
    
    #判断一个位置是否被家具占据
    def is_position_furnished(self, pos):
        return self.is_tile_furnished(math.floor(pos.get_x()), math.floor(pos.get_y()))
    
    #如果一个位置在房间里且没有被家具占据,返回True    
    def is_position_valid(self, pos):
        return (not self.is_position_furnished(pos)) and self.is_position_in_room(pos)
    
    #求房间中总的不被家具占据的小方格数量   
    def get_num_tiles(self):
        answer = 0
        for tile in self.tiles:
            m,n = tile
            if not self.is_tile_furnished(m, n):
                answer += 1
        return answer
    
     #返回一个可用的位置,该位置在房间里且不被家具占据   
    def get_random_position(self):
        while True:
            rand_x = random.uniform(0.0, self.width)
            rand_y = random.uniform(0.0, self.height)
            rand_Pos =  Position(rand_x, rand_y)
            if self.is_position_valid(rand_Pos):
                return rand_Pos

Problem 3: StandardRobot and Simulating a Timestep

该部分让写一个StandardRobot 类,该类需要写一个标准的 update_position_and_clean 函数,主要就是将通过路径上的小方格进行clean。移动位置可以通过position中的get_new_position实现,每次触壁后将获得一个随机方向。实现较为简单:

  #移动位置并清扫所路过的小方格 
    def update_position_and_clean(self):
        new_pos = self.get_robot_position().get_new_position(self.get_robot_direction(), self.speed)
        if self.room.is_position_valid(new_pos):
            self.set_robot_position(new_pos)
            self.room.clean_tile_at_position(self.get_robot_position(), self.capacity)
        else: #表示触壁,此时需要将方向改变,改变为一个随机值
            self.set_robot_direction(random.uniform(0, 360))

将测试的注释删去,运行程序:
MIT6_0002F16_ProblemSet3
MIT6_0002F16_ProblemSet3
通过运动轨迹知道运行正确。

Problem 4: Implementing FaultyRobot

模拟一个出错的机器人轨迹,发生错误时不扫地且随机更改方向。

    def update_position_and_clean(self):
        #没发生错误的话就正常运行,将stardrobot复制下来即可:
        if not self.gets_faulty():
            new_pos = self.get_robot_position().get_new_position(self.get_robot_direction(), self.speed)
            if self.room.is_position_valid(new_pos):
                self.set_robot_position(new_pos)
                self.room.clean_tile_at_position(self.get_robot_position(), self.capacity)
            else:
                self.set_robot_direction(random.uniform(0, 360))
        #如果发生错误,只更改方向
        else:
            self.set_robot_direction(random.uniform(0, 360))

运行结果:
MIT6_0002F16_ProblemSet3
查看动画可知轨迹符合预期,但是总时间较标准机器人有所增长。

Problem 5: Creating the Simulator

模拟指定机器人打扫特定的房间,通过多遍模拟得出打扫房间的平均时间。每次打扫房间直至房间的整洁程度达到指定要求。每一次使用指定的机器人类型,这样可以使得函数具有更强的延展性。

def run_simulation(num_robots, speed, capacity, width, height, dirt_amount, min_coverage, num_trials, robot_type):
    trial_times = []    
    #模拟每一次循环
    for trial_no in range(num_trials):
        #创建指定的空房间,脏度为dirt_amounnt
        room = EmptyRoom(width, height, dirt_amount)
        #创建指定的机器人
        robot_crate = {}
        for robot in range(num_robots):
            robot_crate[robot] = robot_type(room, speed, capacity)

        # 机器人应该一直工作直至房间的干净程度达到要求
        time_step = 0
        current_coverage = room.get_num_cleaned_tiles() / room.get_num_tiles()
        #当未达到干净要求时:
        while current_coverage < min_coverage:
            # 每一个机器人执行一步
            for num, robot in robot_crate.items():
                robot.update_position_and_clean()
            # step+1
            time_step += 1
            #检查已经走了多少步
            current_coverage = room.get_num_cleaned_tiles() / room.get_num_tiles()
        #记录数据
        trial_times.append(time_step)
    #返回平均时间
    return sum(trial_times)/len(trial_times)

运行结果:

runfile('C:/Users/MI/Desktop/ps3/ps3.py', wdir='C:/Users/MI/Desktop/ps3')
Reloaded modules: ps3_visualize, ps3_verify_movement27
avg time steps: 285.28
avg time steps: 574.6
avg time steps: 706.62
avg time steps: 1257.14
avg time steps: 412.74

Problem 6: Running the Simulator

1)How does the performance of the two robot types compare when cleaning 80% of a 20x20 room?
MIT6_0002F16_ProblemSet3
很显然看出StandardRobot的性能更好,但是随着数量的增加,两种机器人性能非常接近。清理一个房间的时间基本上与机器人数量成反比。

2) How does the performance of the two robot types compare when two of each robot cleans 80% of rooms with dimensions 10x30, 20x15, 25x12, and 50x6?
MIT6_0002F16_ProblemSet3
我们能够看出FautyRobot在这些形状中性能较差,并且我们能够看出长宽比越大,FaultyRobot受影响越大。而StandardRobot受影响较小,FaultyRobot性能受形状影响非常明显。

上一篇:MIT6.S081 Lec06翻译


下一篇:Java中的构造函数引用和方法引用