TigerJython | xx für Gymnasien |
Schwarmverhalten ist ein in der Natur häufig auftretendes Phänomen. Einzelne Individuen einer Population verhalten sich so, als wären sie Teil eines grösseren Ganzen. Für die Individuen entstehen Vorteile bei der Flucht vor Fressfeinden durch kollektive Wachsamkeit und beim Energieverbrauch der Bewegung. In unserem Beispiel wird ein Vogelschwarm simuliert.
Schwarmverhalten:Ein Vogel im Schwarm möchte einerseits seinen Nachbaren nahe sein, andererseits möchte er Kollisionen vermeiden. Sein Verhalten lässt sich mit folgenden Regeln beschreiben:
|
1. Separation Diese Bewegung entsteht durch den Drang einen gewissen Abstand zu den Nachbaren zu halten, um mögliche Kollisionen zu vermeiden. Der Beschleunigungsvektor des Akteurs ist entgegengesetzt zum Vektor der Summe der Positionen der Nachbarn.
|
||
2. Aligment Das Aligment hat zum Ziel, die Geschwindigkeit und Richtung an die Bewegung der Nachbarn anzugleichen. Die Richtung des Beschleunigungsvektors des Akteurs wird gebildet aus der Differenz des Geschwindigkeitsvektors des Akteurs und des Mittelwert-Vektors der Geschwindigkeitsvektoren der Nachbarn. |
||
3. Cohesion Die Cohesion hat zum Ziel, den Schwarm zusammenzuhalten und lässt den Akteur möglichst nahe beim Nachbarn bleiben. Die Richtung des Beschleunigungsvektors des Akteurs zeigt in Richtung des gemeinsamen Schwerpunktes der Punktmassen der Nachbarn. |
||
4. Ausseneinflüsse Im ersten Beispiel (Swarm.py) versuchen die Vögel versuchen den Bäumen auszuweichen. Im zweiten Beispiel (Swarm2.py) verfolgen 3 Raubvögel das Swarm und eliminieren die Vögel. |
# Swarm.py from gamegrid import * import math import random class Tree(Actor): def __init__(self): Actor.__init__(self, "sprites/tree1.png") class Bird(Actor): def __init__(self): Actor.__init__(self, True, "sprites/arrow1.png") self.r = GGVector(0, 0) # Position self.v = GGVector(0, 0) # Velocity self.a = GGVector(0, 0) # Acceleration # Called when actor is added to gamegrid def reset(self): self.r.x = self.getX() self.r.y = self.getY() self.v.x = startVelocity * math.cos(math.radians(self.getDirection())) self.v.y = startVelocity * math.sin(math.radians(self.getDirection())) # ----------- cohesion --------------- def cohesion(self, distance): return self.getCenterOfMass(distance).sub(self.r) # ----------- alignment -------------- def alignment(self, distance): align = self.getAverageVelocity(distance) align = align.sub(self.v) return align # ----------- separation ------------- def separation(self, distance): repulse = GGVector() # ------ from birds ------ for p in birdPositions: dist = p.sub(self.r) d = dist.magnitude() if d < distance and d != 0: repulse = repulse.add(dist.mult((d - distance) / d)) # ------ from trees ------ trees = self.gameGrid.getActors(Tree) for actor in trees: p = GGVector(actor.getX(), actor.getY()) dist = p.sub(self.r) d = dist.magnitude() if d < distance and d != 0: repulse = repulse.add(dist.mult((d - distance) / d)) return repulse # ----------- wall interaction ------- def wallInteraction(self): width = self.gameGrid.getWidth() height = self.gameGrid.getHeight() acc = GGVector() if self.r.x < wallDist: distFactor = (wallDist - self.r.x) / wallDist acc = GGVector(wallWeight * distFactor, 0) if width - self.r.x < wallDist: distFactor = ((width - self.r.x) - wallDist) / wallDist acc = GGVector(wallWeight * distFactor, 0) if self.r.y < wallDist: distFactor = (wallDist - self.r.y) / wallDist acc = GGVector(0, wallWeight * distFactor) if height - self.r.y < wallDist: distFactor = ((height - self.r.y) - wallDist) / wallDist acc = GGVector(0, wallWeight * distFactor) return acc def getPosition(self): return self.r def getVelocity(self): return self.v def getCenterOfMass(self, distance): center = GGVector() sum = 0 for p in birdPositions: dist = p.sub(self.r) d = dist.magnitude() if d < distance: center = center.add(p) sum += 1 if sum != 0: return center.mult(1.0/sum) else: return center def getAverageVelocity(self, distance): avg = GGVector() sum = 0 for i in range(len(birdPositions)): p = birdPositions[i] if (self.r.x - p.x) * (self.r.x - p.x) + \ (self.r.y - p.y) * (self.r.y - p.y) < distance * distance: avg = avg.add(birdVelocities[i]); sum += 1 return avg.mult(1.0/sum) def limitSpeed(self): m = self.v.magnitude() if m < minSpeed: self.v = self.v.mult(minSpeed / m) if m > maxSpeed: self.v = self.v.mult(minSpeed / m) def setAcceleration(self): self.a = self.cohesion(cohesionDist).mult(cohesionWeight) self.a = self.a.add(self.separation(separationDist).mult(separationWeight)) self.a = self.a.add(self.alignment(alignmentDist).mult(alignmentWeight)) self.a = self.a.add(self.wallInteraction()) def act(self): self.setAcceleration() self.v = self.v.add(self.a) # new velocity self.limitSpeed() self.r = self.r.add(self.v) # new position self.setDirection(int(math.degrees(self.v.getDirection()))) self.setLocation(Location(int(self.r.x), int(self.r.y))) # global section def populateTrees(number): blockSize = 70 treesPerBlock = 10 for block in range(number // treesPerBlock): x = getRandomNumber(800 // blockSize) * blockSize y = getRandomNumber(600 // blockSize) * blockSize for t in range(treesPerBlock): dx = getRandomNumber(blockSize) dy = getRandomNumber(blockSize) addActor(Tree(), Location(x + dx, y + dy)) def generateBirds(number): for i in range(number): addActorNoRefresh(Bird(), getRandomLocation(), getRandomDirection()) onAct() # Initialize birdPositions, birdVelocities def onAct(): global birdPositions, birdVelocities # Update bird positions and velocities birdPositions = [] birdVelocities = [] for b in getActors(Bird): birdPositions.append(b.getPosition()) birdVelocities.append(b.getVelocity()) def getRandomNumber(limit): return random.randint(0, limit-1) # coupling constants cohesionDist = 100 cohesionWeight = 0.01 alignmentDist = 30 alignmentWeight = 1 separationDist = 30 separationWeight = 0.2 wallDist = 20 wallWeight = 2 maxSpeed = 20 minSpeed = 10 startVelocity = 10 numberTrees = 100 numberBirds = 50 birdPositions = [] birdVelocities = [] makeGameGrid(800, 600, 1, False) registerAct(onAct) setSimulationPeriod(10) setBgColor(Color.blue) setTitle("Swarm Simulation") show() populateTrees(numberTrees) generateBirds(numberBirds) doRun() |
Schwarmsimulation mit 3 Raubvögel als Störungsfaktor
# Swarm2.py from gamegrid import * import math import random # --------- class Tree ---------- class Tree(Actor): def __init__(self): Actor.__init__(self, "sprites/tree1.png") # --------- class Flyer ---------- class Flyer(Actor): def __init__(self, sprite): Actor.__init__(self, True, sprite) self.r = GGVector(0, 0) # Position self.v = GGVector(0, 0) # Velocity self.a = GGVector(0, 0) # Acceleration # Called when actor is added to gamegrid def reset(self): self.r.x = self.getX() self.r.y = self.getY() self.v.x = startVelocity * math.cos(math.radians(self.getDirection())) self.v.y = startVelocity * math.sin(math.radians(self.getDirection())) # ----------- wall interaction ------- def wallInteraction(self): width = self.gameGrid.getWidth() height = self.gameGrid.getHeight() acc = GGVector() if self.r.x < wallDist: distFactor = (wallDist - self.r.x) / wallDist acc = GGVector(wallWeight * distFactor, 0) if width - self.r.x < wallDist: distFactor = ((width - self.r.x) - wallDist) / wallDist acc = GGVector(wallWeight * distFactor, 0) if self.r.y < wallDist: distFactor = (wallDist - self.r.y) / wallDist acc = GGVector(0, wallWeight * distFactor) if height - self.r.y < wallDist: distFactor = ((height - self.r.y) - wallDist) / wallDist acc = GGVector(0, wallWeight * distFactor) return acc def limitSpeed(self): m = self.v.magnitude() if m < minSpeed: self.v = self.v.mult(minSpeed / m) if m > maxSpeed: self.v = self.v.mult(minSpeed / m) def getCenterOfMass(self, distance): center = GGVector() sum = 0 for p in birdPositions: dist = p.sub(self.r) d = dist.magnitude() if d < distance: center = center.add(p) sum += 1 if sum != 0: return center.mult(1.0/sum) else: return center def getPosition(self): return self.r def getVelocity(self): return self.v # ------------- class Bird --------------- class Bird(Flyer): def __init__(self): Flyer.__init__(self, "sprites/arrow1.png") # ----------- cohesion --------------- def cohesion(self, distance): return self.getCenterOfMass(distance).sub(self.r) # ----------- alignment -------------- def alignment(self, distance): align = self.getAverageVelocity(distance) align = align.sub(self.v) return align # ----------- separation ------------- def separation(self, distance): repulse = GGVector() # ------ from birds ------ for p in birdPositions: dist = p.sub(self.r) d = dist.magnitude() if d < distance and d != 0: repulse = repulse.add(dist.mult((d - distance) / d)) # ------ from trees ------ trees = self.gameGrid.getActors(Tree) for actor in trees: p = GGVector(actor.getX(), actor.getY()) dist = p.sub(self.r) d = dist.magnitude() if d < distance and d != 0: repulse = repulse.add(dist.mult((d - distance) / d)) # ------ from raptors ------ for raptor in raptors: p = GGVector(raptor.getX(), raptor.getY()) dist = p.sub(self.r) d = dist.magnitude() if d < distance and d != 0: repulse = repulse.add(dist.mult(raptorRepulse * (d - distance) / d)) return repulse def getAverageVelocity(self, distance): avg = GGVector() sum = 0 for i in range(len(birdPositions)): p = birdPositions[i] if (self.r.x - p.x) * (self.r.x - p.x) + \ (self.r.y - p.y) * (self.r.y - p.y) < distance * distance: avg = avg.add(birdVelocities[i]); sum += 1 return avg.mult(1.0/sum) def setAcceleration(self): self.a = self.cohesion(cohesionDist).mult(cohesionWeight) self.a = self.a.add(self.separation(separationDist).mult(separationWeight)) self.a = self.a.add(self.alignment(alignmentDist).mult(alignmentWeight)) self.a = self.a.add(self.wallInteraction()) def act(self): self.setAcceleration() self.v = self.v.add(self.a) # new velocity self.limitSpeed() self.r = self.r.add(self.v) # new position self.setDirection(int(math.degrees(self.v.getDirection()))) self.setLocation(Location(int(self.r.x), int(self.r.y))) # ---------- class Raptor -------------- class Raptor(Flyer): def __init__(self): Flyer.__init__(self, "sprites/arrow2.png") # ----------- cohesion to birds ------ def cohesion(self, distance): return self.getCenterOfMass(distance).sub(self.r) def setAcceleration(self): self.a = self.cohesion(raptorCohesionDist).mult(cohesionWeight) self.a = self.a.add(self.wallInteraction()) def act(self): self.setAcceleration() self.v = self.v.add(self.a) # new velocity self.limitSpeed() self.r = self.r.add(self.v) # new position self.setDirection(int(math.degrees(self.v.getDirection()))) self.setLocation(Location(int(self.r.x), int(self.r.y))) # ------------- class Sky ------------------- class Sky(GameGrid, GGActorCollisionListener): def __init__(self): GameGrid.__init__(self, 800, 600, 1, False) self.setSimulationPeriod(10) self.setBgColor(makeColor(25, 121, 212)) self.setTitle("Swarm Simulation") self.show() self.populateTrees(numberTrees) self.generateBirds(numberBirds) self.generateRaptors(numberRaptors) self.doRun() self.addStatusBar(20) def populateTrees(self, number): blockSize = 70 treesPerBlock = 10 for block in range(number // treesPerBlock): x = getRandomNumber(self.getWidth() // blockSize) * blockSize y = getRandomNumber(self.getHeight() // blockSize) * blockSize for t in range(treesPerBlock): dx = getRandomNumber(blockSize) dy = getRandomNumber(blockSize) self.addActor(Tree(), Location(x + dx, y + dy)) def generateBirds(self, number): for i in range(number): self.addActorNoRefresh(Bird(), self.getRandomLocation(), self.getRandomDirection()) self.act() # Initialize birdPositions, birdVelocities def generateRaptors(self, number): for i in range(number): raptor = Raptor() self.addActorNoRefresh(raptor, self.getRandomLocation(), self.getRandomDirection()) raptors.append(raptor) raptor.addActorCollisionListener(self) for bird in self.getActors(Bird): raptor.addCollisionActor(bird) self.act() # Automatically called at the end of each simulation cycle def act(self): global birdPositions, birdVelocities # Update bird positions and velocities birdPositions = [] birdVelocities = [] for b in self.getActors(Bird): birdPositions.append(b.getPosition()) birdVelocities.append(b.getVelocity()) def collide(self, actor1, actor2): global n self.removeActor(actor2) # actor2.hide() n = n - 1 return 10 # ------------- global section-------------------- def getRandomNumber(limit): return random.randint(0, limit-1) # coupling constants raptorCohesionDist = 500 raptorRepulse = 5 cohesionDist = 100 cohesionWeight = 0.01 alignmentDist = 30 alignmentWeight = 1 separationDist = 30 separationWeight = 0.2 wallDist = 20 wallWeight = 2 maxSpeed = 20 minSpeed = 10 startVelocity = 10 numberTrees = 100 numberBirds = 50 numberRaptors = 3 birdPositions = [] birdVelocities = [] raptors = [] n = numberBirds s = Sky() while not s.isDisposed() and n > 0: s.setStatusText("# birds: " + str(n)) s.delay(200) s.doPause() |