-
目标函数的定义
-
迷宫导航实验
-
- NEAT-Python超参数选择
-
迷宫环境配置文件
-
实验启动程序
-
基因组适应度评估
-
运行迷宫导航实验
-
智能体记录可视化
-
完整代码
迷宫导航问题是经典的计算机问题,与创建可以在模糊环境中找到路径的自主导航智能体密切相关。迷宫环境中,面向目标的适应度函数在迷宫中接近最终目标点的死角中可以具有较大的适应度得分梯度。迷宫的这些区域成为基于目标搜索算法的局部最优解,这些目标算法可能会收敛于这些区域。当搜索算法收敛于这种欺骗性的局部最优时,它找不到合适的迷宫求解器。
在以下示例中,可以看到带有局部最优死角的二维迷宫,如图中阴影所示:
使用基于目标的搜索算法从起点导航到出口的迷宫导航智能体很容易陷入局部最优死角,阻碍基于目标的搜索算法找到成功的迷宫求解器。
假设迷宫中的智能体是配备了一组传感器的机器人,它可以检测附近的障碍物并判断通往迷宫出口的方向。机器人的运动由两个决策器控制,分别用于控制智能体的行驶运动和角运动。机器人的决策器由ANN控制,该ANN接收来自传感器的输入并为决策器产生两个控制信号。
迷宫模拟环境由三个主要组件构成,这些组件由单独的Python类实现:
-
Agent
:包含与模拟所使用的迷宫导航器智能体相关信息的类(agent.py)。 -
AgentRecordStore
:用于管理演化过程中所有智能体的评估记录的存储类。收集的记录可用于在完成进化过程后对其进行分析(agent.py)。 -
MazeEnvironment
:包含有关迷宫模拟环境信息的类。此类还提供了管理模拟环境、控制求解器智能体的位置、执行碰撞检测以及生成智能体传感器的输入人工神经网络(Artificial Neural Network, ANN)中数据的方法(maze_environment.py)。
接下来,将详细地介绍迷宫模拟环境的每个部分。
迷宫导航智能体
二维迷宫导航任务中,机器人的主要目标是在限定的时间内穿越迷宫到达定义的目标点。控制机器人的ANN是由神经进化算法获得的。
神经进化算法从基本的初始ANN配置开始——该配置只有传感器的输入节点和决策器的输出节点,算法运行过程中ANN配置逐渐变得复杂,直到找到成功的迷宫求解器为止。
下图显示了迷宫导航模拟中使用的迷宫智能体的示意图:
在上图中,实心圆表示机器人。实心圆中的箭头显示了机器人的前进方向。实心圆周围的六个箭头表示六个测距传感器,这些传感器指示在给定方向上到最近障碍物的距离。四个外圆线段表示四个扇形雷达传感器,它们充当着指向迷宫出口的指南针。
当从目标点到机器人中心的线落入其视场(FOV)内时,特定的雷达传感器将被激活。雷达传感器的检测范围受到落入其FOV的迷宫区域的限制。因此,在任何给定时间,四个雷达传感器之一被激活,指示迷宫出口方向。
雷达传感器相对于机器人的方向具有以下FOV区域:
| Sensor | FOV, degrees |
| — | — |
| Front | 315.0~405.0 |
| Left | 45.0~135.0 |
| Back | 135.0~225.0 |
| Right | 225.0~315.0 |
测距传感器是从机器人中心沿特定方向的射线。当与任何障碍物相交时,它将激活,并返回与障碍物的距离。该传感器的检测范围由特定的配置参数定义。
机器人的测距传感器监视与智能体前进方向相关的以下方向:
| Sensor | Direction, degrees |
| — | — |
| Right | -90.0 |
| Front-right | -45.0 |
| Front | 0.0 |
| Front-left | 45.0 |
| Left | 90.0 |
| Back | -180.0 |
机器人的运动由两个决策器控制,这两个决策可以更改其速度和角速度。
迷宫导航智能体的Python实现具有多个字段来保存其当前状态并维护其传感器的激活状态:
class Agent:
def init(
self,location,heading=0,speed=0,
angular_vel=0,radius=8.0,range_finder_range=100.0):
self.heading=heading
self.speed=speed
self.angular_vel=angular_vel
self.radius=radius
self.range_finder_range=range_finder_range
self.location=location
defining the range finder sensors
self.range_finder_range = [-90.0,-45.0,0.0,45.0,90.0,-180.0]
defining the radar sensors
self.radar_angles = [(315.0,405.0),(45.0,135.0),(135.0,225.0),(225.0,315.0)]
the list to hold range finders activations
self.range_finders = [None] * len(self.range_finder_angles)
the list to hold pie-slice radar acivations
self.radar = [None] * len(self.radar_angles)
上面的代码显示了Agent类的默认构造函数,其中Agent的所有字段都已初始化。迷宫环境模拟将使用这些字段在每个模拟步骤中存储智能体的当前状态。
迷宫环境实现
为了模拟在迷宫中导航的求解器智能体程序,需要定义一个环境,该环境管理迷宫的配置,跟踪迷宫求解器的位置,并为导航机器人的传感器数据阵列提供输入。
所有这些特性都适合封装到MazeEnvironment Python类中的一个逻辑块,该类包含以下字段:
class MazeEnvironment:
def init(self,agent,walls,exit_point,exit_range=5.0):
self.walls = walls
self.exit_point=exit_point
self.exit_range=exit_range
The maze navigating agent
self.agent = agent
The flag to indicate if exit was found
self.exit_found = False
The initial distance of agent from exit
self.initial_distance = self.agent_distance_to_exit()
上面的代码显示了MazeEnvironment类的默认构造函数及其所有字段的初始化:
-
迷宫的配置由walls和exit_point确定。墙是线段的列表,每个线段代表迷宫中的一堵墙,而exit_point是迷宫出口的位置。
-
exit_range字段定义出口区域(exit_point周围的距离范围)的值。当智能体位于出口区域时,意味着成功解决了迷宫问题。
-
agent字段包含对上一节中描述的已初始化智能体类的引用。
-
initial_distance字段存储从智能体的起始位置到迷宫出口的距离。此值将用于智能体适应度得分计算。
智能体由ANN控制,该ANN需要接收传感器数据作为输入,以产生相应的控制信号作为输出。导航智能体配备了两种传感器的阵列:
-
六个测距传感器,用于检测与迷宫壁的距离,指示在特定方向上到最近障碍物的距离。
-
四个扇形雷达传感器,指示迷宫出口点的方向。
传感器值需要在每个模拟步骤中进行更新,并且MazeEnvironment类提供了更新两类传感器的方法。
测距传感器的阵列更新如下:
for i,angle in enumerate(self.agent.range_finder_angles):
rad = genometry.deg_to_rad(angle)
projection_point = genometry.Point(
x = self.agent.location.x + math.cos(rad) * \
self.agent.range_finder_range,
y = self.agent.location.y + math.sin(rad) * \
self.agent.range_finder_range
)
projection_point.rotate(
self.agent.heading,self.agent.location
)
projection_line = genometry.Line(
a = self.agent.location,
b = self.projection_point
)
min_range = self.agent.range_finder_range
for wall in self.walls:
found,intersection = wall.intersection(projection_line)
if found:
found_range = intersection.distance(self.agent.location)
if found_range < min_range:
min_range = found_range
store the distance to the closest obstacle
self.agent.range_finders[i] = min_range
代码枚举了测距传感器的所有检测方向,这些方向由方向角确定(Agent构造函数中的range_finder_angles字段)。然后针对每个方向创建一条投影线,该投影线从智能体的当前位置开始,其长度等于测距仪的检测范围。之后,测试投影线是否与任何迷宫壁相交。如果检测到多个交叉点,则到最近墙的距离将作为值存储到特定的测距传感器。否则,最大检测范围将保存为测距仪传感器的值。
扇形雷达传感器阵列使用MazeEnvironment类中的以下代码进行更新:
def update_radar(self):
target = genometry.Point(self.exit_point.x,self.exit_point.y)
target.rotate(self.agent.heading,self.agent.location)
target.x -= self.agent.location.x
target.y -= self.agent.location.y
angle = target.angle()
for i,r_angles in enumerate(self.agent.radar_angles):
self.agent.radar[i] = 0.0 # reset specific radar
if (angle >= r_angles[0] and angle <= r_angles[1]) or
(angle + 360 >= r_angles[0] and angle + 360 < r_rangles[1]):
fire the radar
self.agent.radar[i] = 1.0
创建迷宫出口点的副本,并相对于智能体的航向和全局坐标系内的位置旋转迷宫出口点。然后平移目标点以使其与迷宫求解器的局部坐标系对齐;智能体被放置在坐标的原点。之后,计算从坐标的原点到局部坐标系内目标点的矢量角度。该角度是从当前智能体位置到迷宫出口点的方位角。找到方位角后,枚举扇形雷达传感器,以找到在其视场中包含方位角的传感器。通过将其值设置为1来激活相应的雷达传感器,而通过将其值清零来停用其他雷达传感器。
智能体位置更新
从控制器ANN接收到相应的控制信号后,在每个模拟步骤中都需要更新迷宫中智能体的位置。执行以下代码以更新智能体的位置:
def update(self,control_signals):
if self.exit_found:
return True # Maze exit already found
self.apply_control_signals(control_signals)
vx = math.cos(genometry.deg_to_rad(self.agent.heading)) * \
self.agent.speed
vy = math.sin(genometry.deg_to_rad(self.agent.heading)) * \
self.agent.speed
self.agent.heading += self.agent.angular_vel
if self.agent.heading > 360:
self.agent.heading -=