Steering path following
Verfasst: Sonntag 24. März 2019, 20:44
Hallo ThomasL u.a
habe mal wieder ein Problen:
versuche die Ausführungen von Fernando Bevilaqcua Understanding Steering Behavior: Path Following in pygame und python umzusetzen.
Ein Vehikel folgt den Punkten eines Pfades.
Ein Pfad ist zusammengesetzt aus Linien und Punkten. Jeder Punkt des Pfades wird als target angesehen, sodass der seek-Algorithmus von Reynolds verwendet werden kann. Wenn ein bestimmter Abstand zu einem Punkt erreicht ist, wird der nächste Punkt gesucht, der Listenindex um 1 erhöht.
Problem: Wenn der letzte Punkt des Pfades gesucht wird, soll das Vehikel dort zur Ruhe kommen, der seek_with_approach - Algorithmus verwendet werden.
Frage: wie kann ich das machen, ohne eine Fehlermeldung wegen des Listenindex zu erhalten.
Für Tips wäre ich dankbar.
Code:
import pygame
from random import randint, uniform
import math
import copy
from vec2d import vec2d
WIDTH = 1000
HEIGHT = 640
FPS = 60
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
YELLOW = (255, 255, 0)
DARKGRAY = (40, 40, 40)
CYAN = (0, 255, 255)
MAGENTA = (255, 0, 255)
BG_COLOR = (122, 150, 134)
#BG_COLOR = (150, 150, 80)
MAX_SPEED = 2
MAX_FORCE = 0.05
APPROACH_RADIUS = 50
AGENT_FILENAME = '../images/dreieck0.png'
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.init()
class Vehicle(pygame.sprite.Sprite):
def __init__(self, screen, img_filename, init_position):
pygame.sprite.Sprite.__init__(self)
self.screen = screen
self.screen_rect = screen.get_rect()
self.base_image = pygame.image.load(img_filename).convert_alpha()
self.image = self.base_image
self.image_w, self.image_h = self.image.get_size()
self.location = vec2d(init_position)
self.velocity = vec2d(MAX_SPEED, 0)
self.direction = vec2d(0,0)
self.acceleration = vec2d(MAX_SPEED, 0)
self.desired = vec2d(0, 0)
self.current_node = 0 # Listenindex der Liste path.nodes
def apply_force(self, force):
self.acceleration += force
def follow_path(self, path):
target = vec2d(0,0)
if path != None:
path.nodes = path.get_nodes()
target = path.nodes[self.current_node]
distance = self.location.get_distance(target)
if distance <= 10:
self.current_node += 1
if self.current_node >= len(path.nodes):
self.seek_with_approach(target)
self.current_node = len(path.nodes) - 1
self.seek(target)
def seek(self, target):
self.desired = (target - self.location).normalized()* MAX_SPEED
steer = (self.desired - self.velocity)
if steer.get_length() > MAX_FORCE:
steer *= (MAX_FORCE)
self.apply_force(steer)
def seek_with_approach(self, target):
""" wenn sich Vehikel innerhalb der Entfernung
APPROACH_RADIUS befindet bremse ab """
self.desired = (target - self.location)
dist = self.desired.get_length()
self.desired.normalized()
if dist < APPROACH_RADIUS:
self.desired *= dist / APPROACH_RADIUS * MAX_SPEED
else:
self.desired *= MAX_SPEED
steer = (self.desired - self.velocity)
if steer.get_length() > MAX_FORCE:
steer *= MAX_FORCE
self.apply_force(steer)
def update(self):
if self.velocity.get_length() > 0.00001:
self.direction = self.velocity.normalized()
# Make the agent point in the correct direction.
# Since our direction vector is in screen coordinates
# (i.e. right bottom is 1, 1), and rotate() rotates
# counter-clockwise, the angle must be inverted to
# work correctly.
# self.image = pygame.transform.rotate(self.base_image,
# -180 * math.atan2(self.direction.y , self.direction.x)
# / math.pi)
self.image = pygame.transform.rotate(self.base_image, -self.direction.angle)
self.image_w, self.image_h = self.image.get_size()
# equations of motion
self.velocity += self.acceleration
# begrenze velocity auf MAX_SPEED
if self.velocity.get_length() > MAX_SPEED:
self.velocity * MAX_SPEED
self.location += self.velocity
self.acceleration *= 0.0 # resette nach jedem update
self.rect = self.image.get_rect()
self.rect.topleft = (self.location.x, self.location.y)
# Zeichne Sprite
def blitme(self):
draw_pos = self.rect.move(
[-self.image_w / 2, -self.image_h / 2])
self.screen.blit(self.image, draw_pos)
class Path(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.screen = screen
self.screen_rect = screen.get_rect()
self.nodes = []
# Add a point to the path
def add_node(self, x, y):
node = vec2d(x, y)
self.nodes.append(node);
def get_start(self):
return self.nodes[0]
def get_end(self):
return self.nodes[-1]
def get_nodes(self):
return self.nodes
def display(self):
node_list = []
for node in self.nodes:
node_list.append(node)
pygame.draw.lines(screen, BLACK, False, node_list, 3)
# generiere Pfad aus Liniensegmenten
def new_path():
path = Path()
path.add_node(20, HEIGHT/2)
path.add_node(randint(0, WIDTH/2), randint(20, HEIGHT))
path.add_node(randint(WIDTH/2, WIDTH), randint(20, HEIGHT))
path.add_node(WIDTH-20, HEIGHT/2)
return path
###### main ######
def main() :
pygame.init()
clock = pygame.time.Clock()
# Generiere Pfad
path = new_path()
# einen Agent erzeugen
agent = pygame.image.load(AGENT_FILENAME).convert_alpha()
agent = Vehicle(screen, AGENT_FILENAME,
(WIDTH/2, HEIGHT/3))
running = True
while running:
clock.tick(FPS)
screen.fill(BG_COLOR)
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
running = False
path.display()
agent.follow_path(path)
agent.update()
agent.blitme()
pygame.display.set_caption('Path Following')
pygame.display.flip()
if __name__ == "__main__":
main()
habe mal wieder ein Problen:
versuche die Ausführungen von Fernando Bevilaqcua Understanding Steering Behavior: Path Following in pygame und python umzusetzen.
Ein Vehikel folgt den Punkten eines Pfades.
Ein Pfad ist zusammengesetzt aus Linien und Punkten. Jeder Punkt des Pfades wird als target angesehen, sodass der seek-Algorithmus von Reynolds verwendet werden kann. Wenn ein bestimmter Abstand zu einem Punkt erreicht ist, wird der nächste Punkt gesucht, der Listenindex um 1 erhöht.
Problem: Wenn der letzte Punkt des Pfades gesucht wird, soll das Vehikel dort zur Ruhe kommen, der seek_with_approach - Algorithmus verwendet werden.
Frage: wie kann ich das machen, ohne eine Fehlermeldung wegen des Listenindex zu erhalten.
Für Tips wäre ich dankbar.
Code:
import pygame
from random import randint, uniform
import math
import copy
from vec2d import vec2d
WIDTH = 1000
HEIGHT = 640
FPS = 60
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
YELLOW = (255, 255, 0)
DARKGRAY = (40, 40, 40)
CYAN = (0, 255, 255)
MAGENTA = (255, 0, 255)
BG_COLOR = (122, 150, 134)
#BG_COLOR = (150, 150, 80)
MAX_SPEED = 2
MAX_FORCE = 0.05
APPROACH_RADIUS = 50
AGENT_FILENAME = '../images/dreieck0.png'
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.init()
class Vehicle(pygame.sprite.Sprite):
def __init__(self, screen, img_filename, init_position):
pygame.sprite.Sprite.__init__(self)
self.screen = screen
self.screen_rect = screen.get_rect()
self.base_image = pygame.image.load(img_filename).convert_alpha()
self.image = self.base_image
self.image_w, self.image_h = self.image.get_size()
self.location = vec2d(init_position)
self.velocity = vec2d(MAX_SPEED, 0)
self.direction = vec2d(0,0)
self.acceleration = vec2d(MAX_SPEED, 0)
self.desired = vec2d(0, 0)
self.current_node = 0 # Listenindex der Liste path.nodes
def apply_force(self, force):
self.acceleration += force
def follow_path(self, path):
target = vec2d(0,0)
if path != None:
path.nodes = path.get_nodes()
target = path.nodes[self.current_node]
distance = self.location.get_distance(target)
if distance <= 10:
self.current_node += 1
if self.current_node >= len(path.nodes):
self.seek_with_approach(target)
self.current_node = len(path.nodes) - 1
self.seek(target)
def seek(self, target):
self.desired = (target - self.location).normalized()* MAX_SPEED
steer = (self.desired - self.velocity)
if steer.get_length() > MAX_FORCE:
steer *= (MAX_FORCE)
self.apply_force(steer)
def seek_with_approach(self, target):
""" wenn sich Vehikel innerhalb der Entfernung
APPROACH_RADIUS befindet bremse ab """
self.desired = (target - self.location)
dist = self.desired.get_length()
self.desired.normalized()
if dist < APPROACH_RADIUS:
self.desired *= dist / APPROACH_RADIUS * MAX_SPEED
else:
self.desired *= MAX_SPEED
steer = (self.desired - self.velocity)
if steer.get_length() > MAX_FORCE:
steer *= MAX_FORCE
self.apply_force(steer)
def update(self):
if self.velocity.get_length() > 0.00001:
self.direction = self.velocity.normalized()
# Make the agent point in the correct direction.
# Since our direction vector is in screen coordinates
# (i.e. right bottom is 1, 1), and rotate() rotates
# counter-clockwise, the angle must be inverted to
# work correctly.
# self.image = pygame.transform.rotate(self.base_image,
# -180 * math.atan2(self.direction.y , self.direction.x)
# / math.pi)
self.image = pygame.transform.rotate(self.base_image, -self.direction.angle)
self.image_w, self.image_h = self.image.get_size()
# equations of motion
self.velocity += self.acceleration
# begrenze velocity auf MAX_SPEED
if self.velocity.get_length() > MAX_SPEED:
self.velocity * MAX_SPEED
self.location += self.velocity
self.acceleration *= 0.0 # resette nach jedem update
self.rect = self.image.get_rect()
self.rect.topleft = (self.location.x, self.location.y)
# Zeichne Sprite
def blitme(self):
draw_pos = self.rect.move(
[-self.image_w / 2, -self.image_h / 2])
self.screen.blit(self.image, draw_pos)
class Path(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.screen = screen
self.screen_rect = screen.get_rect()
self.nodes = []
# Add a point to the path
def add_node(self, x, y):
node = vec2d(x, y)
self.nodes.append(node);
def get_start(self):
return self.nodes[0]
def get_end(self):
return self.nodes[-1]
def get_nodes(self):
return self.nodes
def display(self):
node_list = []
for node in self.nodes:
node_list.append(node)
pygame.draw.lines(screen, BLACK, False, node_list, 3)
# generiere Pfad aus Liniensegmenten
def new_path():
path = Path()
path.add_node(20, HEIGHT/2)
path.add_node(randint(0, WIDTH/2), randint(20, HEIGHT))
path.add_node(randint(WIDTH/2, WIDTH), randint(20, HEIGHT))
path.add_node(WIDTH-20, HEIGHT/2)
return path
###### main ######
def main() :
pygame.init()
clock = pygame.time.Clock()
# Generiere Pfad
path = new_path()
# einen Agent erzeugen
agent = pygame.image.load(AGENT_FILENAME).convert_alpha()
agent = Vehicle(screen, AGENT_FILENAME,
(WIDTH/2, HEIGHT/3))
running = True
while running:
clock.tick(FPS)
screen.fill(BG_COLOR)
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
running = False
path.display()
agent.follow_path(path)
agent.update()
agent.blitme()
pygame.display.set_caption('Path Following')
pygame.display.flip()
if __name__ == "__main__":
main()