Python 正确实施墙跟随转向行为 介绍

Python 正确实施墙跟随转向行为 介绍,python,python-2.7,boids,Python,Python 2.7,Boids,一段时间以来,我一直在尝试实施Craig Reynold所描述的“墙跟随”转向行为。这种行为基本上可以归结为网站上的描述: 为了实现墙跟踪,我们首先预测车辆的 根据当前速度在未来短时间内定位 (黑点)。此未来位置投影到上最近的点 “墙”(红点)。从该墙点沿墙向外移动 通过所需偏移距离的法线(红线)生成目标 点(红色圆圈) 链接页面上有一个Java小程序显示了上述内容,但对于那些(大多数人)在查看时遇到困难的人,请参见下面的.gif 我已经非常接近了,但由于某些原因,我的版本仍然有点偏离。我的

一段时间以来,我一直在尝试实施Craig Reynold所描述的“墙跟随”转向行为。这种行为基本上可以归结为网站上的描述:

为了实现墙跟踪,我们首先预测车辆的 根据当前速度在未来短时间内定位 (黑点)。此未来位置投影到上最近的点 “墙”(红点)。从该墙点沿墙向外移动 通过所需偏移距离的法线(红线)生成目标 点(红色圆圈)

链接页面上有一个Java小程序显示了上述内容,但对于那些(大多数人)在查看时遇到困难的人,请参见下面的.gif


我已经非常接近了,但由于某些原因,我的版本仍然有点偏离。我的boid似乎无法选择下一个最近的墙像素,这会导致投影目标点的抖动移动。这个问题非常严重,以至于boid有时几乎可以赶上目标点,而不需要预测新的目标点。请参阅下面的.gif进行比较

要简要描述boid在我的实现中如何操作,请执行以下操作:

  • 每一帧,boid都调用
    followWall()
  • followWall()
    中,如果boid的成员变量
    hasHitWall
    为False,它将检查boid前面的点是否落在墙像素上(boid在其中移动的环境是黑白像素的网格,其中黑=墙)
  • 如果检测到墙像素,它会将
    hasHitWall
    设置为True,并收集所有连接的墙“边缘像素”,并将其存储在boid中名为
    currentEdgePixels
    的列表中
  • 下一帧,如果
    hasHitWall
    为真,boid将通过迭代
    currentEdgePixels
    并找到距离其
    位置最短的一个来找到最近的边缘像素。然后,它将拉动该墙像素的法线,并沿法线向量投影一个设定距离的点。这就成为了“指导点”
  • 然后,boid将使用它的
    seek()
    行为转向点(在Craig Reynold的描述中称为目标点)
  • 只要
    hasHitWall
    保持为真,boid将继续在
    currentEdgePixels
    中找到最近的边缘像素并向其搜索
  • 简要披露:我完全理解,将墙的所有边缘像素存储在boid对象中,并在每一帧中检查每一个像素都不够理想,而且效率低下。这是一个临时设计,以便于调试,一旦一切正常,我可能会重新设计

    代码
    我的上述行为代码可以在下面找到。但是,首先对一些常见元素进行一些简要说明:

    pixelGrid
    :此参数是表示图像行的列表列表。表示行的列表中的每个元素都包含1或0,具体取决于该行/列位置的像素颜色是白色还是黑色

    getNormalizedVectorFromOrigin()
    :此函数返回一个元组,表示[-1,1]范围内单位圆上的向量。如果将向量(或点)传递到未规范化向量超出[-1,1]范围的函数中,则在返回之前,生成的向量将减小到适合的范围内。例:(1.7675,-1.7675)的向量将变成(0.707,-0.707)


    跟随墙()

    def followWall(self, pixelGrid, screenSize): 
            if (not self.hasHitWall):
                wallCollisionPixel = self.detectWall(pixelGrid, screenSize)
                if (wallCollisionPixel):
                    self.hasHitWall = True
                    self.currentEdgePixels = utils.findAllEdgePixels(wallCollisionPixel, pixelGrid, screenSize)
                    
            elif (self.hasHitWall):
                wallPixel = self.findClosestEdgePixel(pixelGrid, screenSize)       
                wallPixelNormal = utils.getEdgePixelNormal(wallPixel, pixelGrid, screenSize)
                steeringPointDistance = 30
                steeringPoint = (int(round(wallPixel[0] + (wallPixelNormal[0] * steeringPointDistance), 0)), int(round(wallPixel[1] + (wallPixelNormal[1] * steeringPointDistance), 0)))
                self.seekPoint = steeringPoint
                self.seek(steeringPoint, 1)
    
        def findClosestEdgePixel(self, pixelGrid, screenSize):
            futureDistance = 20
            normalizedVelocity = utils.getNormalizedVectorFromOrigin(self.velocity.get())
            futurePosition = (int(round(self.position.x + (normalizedVelocity[0] * futureDistance), 0)), int(round(self.position.y + (normalizedVelocity[1] * futureDistance), 0)))
            
            closestEdgePixel = self.currentEdgePixels[0]
            for edgePixel in self.currentEdgePixels:
                if (utils.getDistance(futurePosition, edgePixel) < utils.getDistance(futurePosition, closestEdgePixel)):
                    closestEdgePixel = edgePixel
            return closestEdgePixel
    
    def getDistance(point1, point2):
        return math.sqrt(((point2[0] - point1[0]) ** 2) + ((point2[1] - point1[1]) ** 2))
    
    findClosestEdgePixel()

    def followWall(self, pixelGrid, screenSize): 
            if (not self.hasHitWall):
                wallCollisionPixel = self.detectWall(pixelGrid, screenSize)
                if (wallCollisionPixel):
                    self.hasHitWall = True
                    self.currentEdgePixels = utils.findAllEdgePixels(wallCollisionPixel, pixelGrid, screenSize)
                    
            elif (self.hasHitWall):
                wallPixel = self.findClosestEdgePixel(pixelGrid, screenSize)       
                wallPixelNormal = utils.getEdgePixelNormal(wallPixel, pixelGrid, screenSize)
                steeringPointDistance = 30
                steeringPoint = (int(round(wallPixel[0] + (wallPixelNormal[0] * steeringPointDistance), 0)), int(round(wallPixel[1] + (wallPixelNormal[1] * steeringPointDistance), 0)))
                self.seekPoint = steeringPoint
                self.seek(steeringPoint, 1)
    
        def findClosestEdgePixel(self, pixelGrid, screenSize):
            futureDistance = 20
            normalizedVelocity = utils.getNormalizedVectorFromOrigin(self.velocity.get())
            futurePosition = (int(round(self.position.x + (normalizedVelocity[0] * futureDistance), 0)), int(round(self.position.y + (normalizedVelocity[1] * futureDistance), 0)))
            
            closestEdgePixel = self.currentEdgePixels[0]
            for edgePixel in self.currentEdgePixels:
                if (utils.getDistance(futurePosition, edgePixel) < utils.getDistance(futurePosition, closestEdgePixel)):
                    closestEdgePixel = edgePixel
            return closestEdgePixel
    
    def getDistance(point1, point2):
        return math.sqrt(((point2[0] - point1[0]) ** 2) + ((point2[1] - point1[1]) ** 2))
    

    结束 如果有人想知道如何让我的实现更好地工作,或者指出我的代码中可能导致问题的缺陷,我将不胜感激。如果您认为有更好的方法来实现这种特定的转向行为,我也非常乐意听到您的建议

    请让我知道,如果我可以提供任何更多的信息或代码位。事先非常感谢