| 1 | ||
|
Editor: antont
Time: 2006/11/21 12:47:43 GMT+0 |
||
| Note: | ||
changed: - Below is the source code of the BrevE example (in it's own language, Steve) which is an EvolutionProgram (or GeneticAlgorithm) that tries to learn to walk (this version also modifies the body). PyGP might help in BlendeR port. <HR> <PRE> @use Link. @use Genome. @use PhysicalControl. @use Shape. @use Stationary. @use MultiBody. @define SPEED_K 20. # # This demo is an enhanced version of the Walker demo in which # 1) the limbs are not bounded to move symmetrically and 2) limb # lengths are evolved along with walking behaviors. # # Both of these changes make the search space much larger meaning # that evolution goes much more slowly than for the Walker. It is # best to let this simulation over night in order to observe real # walking behaviors. # Controller Walker. PhysicalControl : Walker { + variables: seats, monkeys (list). currentSeat (int). wigglyThing (object). # the following are flags that can be controlled by the user # via the simulation menu locked (int). lockMenu, symMenu (object). cloudTexture (int). + to init: floorShape (object). floor (object). number (int). item (object). locked = 0. self enable-lighting. self enable-smooth-drawing. # self set-gravity to (0, 0, 0). # Create the floor for the critter to walk on. floorShape = (new Shape init-with-cube size (1000, 2, 1000)). floor = new Stationary. floor register with-shape floorShape at-location (0, 0, 0). floor catch-shadows. cloudTexture = (self load-image from "clouds.sgi"). self set-background-texture to cloudTexture. self enable-shadows. self enable-reflections. self set-background-color to (.4, .6, .9). # The "Monkeys" are the individuals that will control the Creature. # Create the Monkeys and assign them numbers. monkeys = 15 new Monkeys. foreach item in monkeys: { (item set-number to number). number += 1. } self pick-drivers. # set up the menus... lockMenu = (self add-menu named "Lock Driver" for-method "toggle-driver-lock"). self add-menu-separator. self add-menu named "Save Current Genome" for-method "save-current-genome". self add-menu named "Load Into Current Genome" for-method "load-into-current-genome". # schedule the first driver change and we're ready to go. self schedule method-call "change-drivers" at-time (self get-time) + 20.0. self display-current-driver. # Create the Creature. wigglyThing = new Creature. wigglyThing init-with genome (seats{0} get-genome). wigglyThing move to (0, 3, 0). self offset-camera by (-3, 3, 13). self watch item wigglyThing. + to display-current-driver: current (object). current = seats{currentSeat}. self set-display-text to "Driver #$currentSeat" at-x -.95 at-y -.9. + to iterate: seats{currentSeat} control robot wigglyThing at-time (self get-time). super iterate. + to pick-drivers: # pick 4 new drivers at random. we do this by sorting the # list randomly and picking the first 4 items. sort monkeys with random-sort. seats{0} = monkeys{0}. seats{1} = monkeys{1}. seats{2} = monkeys{2}. seats{3} = monkeys{3}. currentSeat = 0. + to random-sort objectA a (object) objectB b (object): return random[2] - 1. + to save-drivers: driver (object). n (int). foreach driver in monkeys: { monkeys{n} = ((driver get-genome) save-as-xml file "driver$n.xml"). n++. } + to load-drivers: n (int). for n=0,n<15,n++: { ((monkeys{n} get-genome) load-from-xml file "driver$n.xml"). } self pick-drivers. print monkeys{currentSeat}. + to change-drivers: newDriver (int). newOffset (vector). # if they have locked the current driver, do nothing. if locked: return. # pick a new camera angle and pan... newOffset = random[(30, 6, 30)] + (-15, 1, -15). if |newOffset| < 9: newOffset = 9 * newOffset/|newOffset|. self pan-camera-offset by newOffset steps 30. # we change the drivers every time a monkey is finished it's # turn. if we have seen the last monkey, breed them together. seats{currentSeat} set-distance to |(wigglyThing get-location)|. currentSeat += 1. if currentSeat > 3: { self breed-new-monkeys. self pick-drivers. } newDriver = (seats{currentSeat} get-number). if wigglyThing: free wigglyThing. wigglyThing = new Creature. wigglyThing init-with genome (seats{currentSeat} get-genome). wigglyThing move to (0, 3, 0). self watch item wigglyThing. # schedule a new driver change in 20 seconds. self schedule method-call "change-drivers" at-time (self get-time) + 20.0. self display-current-driver. + to breed-new-monkeys: print "breeding monkeys...". sort seats with compare-distance. # print out the distance for each individual tested print "driver ", (seats{0} get-number), (seats{0} get-distance). print "driver ", (seats{1} get-number), (seats{1} get-distance). print "driver ", (seats{2} get-number), (seats{2} get-distance). print "driver ", (seats{3} get-number), (seats{3} get-distance). # breed the two best twice, replacing the two worst. seats{0} breed with seats{1} to-child seats{2}. seats{1} breed with seats{0} to-child seats{3}. # give each individual a mutation (seats{2} get-genome) mutate. (seats{3} get-genome) mutate. + to compare-distance of a (object) with b (object): result (float). result = (b get-distance) - (a get-distance). return result. # the following methods are accessed from the simulation menu. + to toggle-driver-lock: if locked == 1: { locked = 0. wigglyThing center. self schedule method-call "change-drivers" at-time (self get-time) + 20.0. lockMenu uncheck. } else { locked = 1. lockMenu check. } + to save-current-genome: (seats{currentSeat} get-genome) save-with-dialog. + to load-into-current-genome: (seats{currentSeat} get-genome) load-with-dialog. } Object : Monkeys { + variables: distanceTraveled (float). genome (object). number (int). + to set-number to n (int): number = n. + to get-number: return number. + to init: genome = new MonkeyGenome. genome randomize. self add-dependency on genome. + to get-genome: return genome. + to breed with otherMonkey (object) to-child child (object): (child get-genome) crossover from-parent-1 (otherMonkey get-genome) from-parent-2 (self get-genome). + to control robot theRobot (object) at-time t (float): n (int). while n < 8: { theRobot set-joint-velocity number n to SPEED_K * (genome calculate-torque number n at t). n+=1. } + to set-distance to value (float): distanceTraveled = value. + to get-distance: return distanceTraveled. } Genome : MonkeyGenome { + variables: waveCompression (float). phaseShifts (8 floats). ampShifts (8 floats). limbLengths (8 floats). + to randomize: n (int). for n=0, n<8, n++: { phaseShifts[n] = random[6.3] - 3.15. ampShifts[n] = random[1.0] - .5. limbLengths[n] = random[2.0] + .5. } waveCompression = random[5.0] - 2.5. + to calculate-torque number jointNum (int) at time (float): # calculates the torque for a certain joint number. return .5 * sin(waveCompression * (time + phaseShifts[jointNum]) - (ampShifts[jointNum])). + to get-limb-length number n (int): return limbLengths[n]. + to mutate: n (int). # we need to decide which of the elements in this genome to mutate n = random[25]. if n < 8: ampShifts[n] = random[2.0] - 1.0. else if n < 16: phaseShifts[n - 8] = random[6.3] - 3.15. else if n < 24: limbLengths[n - 16] = random[2.0] + .5. else waveCompression = random[5.0] - 2.5. print "mutated item $n of $self". } MultiBody : Creature { + variables: bodyLink (object). links (list). + to get-root: return bodyLink. + to init-with genome g (object): x, y (float). lowerLimbSize, upperLimbSize (vector). lowerLimbLinkPoint, upperLimbLinkPoint (vector). counter (int). linkShape, lowerLinkShape, bodyShape (object). self add-menu named "Send to Center" for-method "center". # we want the limb volume to be constant at 16, but the dimensions can change. y = (g get-limb-length number 0). x = sqrt(.16/y). x = sqrt(.16/y). lowerLimbSize = (x, y, x). lowerLimbLinkPoint = (0, y/2, 0). y = (g get-limb-length number 1). x = sqrt(.16/y). x = sqrt(.16/y). upperLimbSize = (x, y, x). upperLimbLinkPoint = (0, y/2, 0). lowerLinkShape = new Shape. lowerLinkShape init-with-cube size lowerLimbSize. linkShape = new Shape. linkShape init-with-cube size upperLimbSize. bodyShape = new Shape. bodyShape init-with-cube size (4, 3, .4). counter = 0. while counter < 8: { links{counter} = new Link. counter+=1. } links{0} set shape linkShape. links{2} set shape linkShape. links{4} set shape linkShape. links{6} set shape linkShape. links{1} set shape lowerLinkShape. links{3} set shape lowerLinkShape. links{5} set shape lowerLinkShape. links{7} set shape lowerLinkShape. links set-color to random[(1.0, 1.0, 1.0)]. bodyLink = new Link. bodyLink set shape bodyShape. linkShape unretain. lowerLinkShape unretain. bodyShape unretain. self register with-link bodyLink. links{0} link-revolute to-link bodyLink with-normal (0, 0, 1) with-parent-point (2.0, -1.5, 0) with-child-point upperLimbLinkPoint. links{1} link-revolute to-link links{0} with-normal (1, 0, 0) with-parent-point -upperLimbLinkPoint with-child-point lowerLimbLinkPoint. links{4} link-revolute to-link bodyLink with-normal (0, 0, 1) with-parent-point (-2.0, -1.5, 0) with-child-point upperLimbLinkPoint. links{5} link-revolute to-link links{4} with-normal (1, 0, 0) with-parent-point -upperLimbLinkPoint with-child-point lowerLimbLinkPoint. links{2} link-revolute to-link bodyLink with-normal (0, 0, 1) with-parent-point (2.0, 1.5, 0) with-child-point -upperLimbLinkPoint. links{3} link-revolute to-link links{2} with-normal (1, 0, 0) with-parent-point upperLimbLinkPoint with-child-point -lowerLimbLinkPoint. links{6} link-revolute to-link bodyLink with-normal (0, 0, 1) with-parent-point (-2.0, 1.5, 0) with-child-point -upperLimbLinkPoint. links{7} link-revolute to-link links{6} with-normal (1, 0, 0) with-parent-point upperLimbLinkPoint with-child-point -lowerLimbLinkPoint. # rotate the creature and move it to above the origin. self rotate around-axis (1, 0, 0) by 1.57. links set-double-spring with-strength 200 with-max .6 with-min -.6. links set-strength-limit to 250. # to center the object, we set the X and Z components to 0, but not # the Y, otherwise we would push the walker into the ground + to center: currentLocation (vector). currentLocation = (self get-location). self move to (0, currentLocation::y, 0). # The following four method allow external objects to manipulate # the torque values of the links. + to set-joint-velocity number jointNum (int) to value (float): links{jointNum} set-joint-velocity to value. + to destroy: free bodyLink. free links. }
Below is the source code of the BrevE example (in it's own language, Steve) which is an EvolutionProgram? (or GeneticAlgorithm?) that tries to learn to walk (this version also modifies the body). PyGP might help in BlendeR port.
@use Link. @use Genome.
@use PhysicalControl. @use Shape. @use Stationary. @use MultiBody.
@define SPEED_K 20.
# # This demo is an enhanced version of the Walker demo in which # 1) the limbs are not bounded to move symmetrically and 2) limb # lengths are evolved along with walking behaviors. # # Both of these changes make the search space much larger meaning # that evolution goes much more slowly than for the Walker. It is # best to let this simulation over night in order to observe real # walking behaviors. #
Controller Walker.
PhysicalControl : Walker { + variables: seats, monkeys (list).
currentSeat (int).
wigglyThing (object).
# the following are flags that can be controlled by the user # via the simulation menu
locked (int).
lockMenu, symMenu (object).
cloudTexture (int).
+ to init: floorShape (object). floor (object). number (int). item (object).
locked = 0.
self enable-lighting. self enable-smooth-drawing.
# self set-gravity to (0, 0, 0).
# Create the floor for the critter to walk on.
floorShape = (new Shape init-with-cube size (1000, 2, 1000)). floor = new Stationary.
floor register with-shape floorShape at-location (0, 0, 0). floor catch-shadows.
cloudTexture = (self load-image from "clouds.sgi"). self set-background-texture to cloudTexture.
self enable-shadows. self enable-reflections.
self set-background-color to (.4, .6, .9).
# The "Monkeys" are the individuals that will control the Creature. # Create the Monkeys and assign them numbers.
monkeys = 15 new Monkeys.
foreach item in monkeys: { (item set-number to number). number += 1. }
self pick-drivers.
# set up the menus...
lockMenu = (self add-menu named "Lock Driver" for-method "toggle-driver-lock").
self add-menu-separator.
self add-menu named "Save Current Genome" for-method "save-current-genome". self add-menu named "Load Into Current Genome" for-method "load-into-current-genome".
# schedule the first driver change and we're ready to go.
self schedule method-call "change-drivers" at-time (self get-time) + 20.0.
self display-current-driver.
# Create the Creature.
wigglyThing = new Creature. wigglyThing init-with genome (seats{0} get-genome). wigglyThing move to (0, 3, 0). self offset-camera by (-3, 3, 13). self watch item wigglyThing.
+ to display-current-driver: current (object).
current = seats{currentSeat}.
self set-display-text to "Driver #$currentSeat" at-x -.95 at-y -.9.
+ to iterate: seats{currentSeat} control robot wigglyThing at-time (self get-time).
super iterate.
+ to pick-drivers: # pick 4 new drivers at random. we do this by sorting the # list randomly and picking the first 4 items.
sort monkeys with random-sort.
seats{0} = monkeys{0}. seats{1} = monkeys{1}. seats{2} = monkeys{2}. seats{3} = monkeys{3}.
currentSeat = 0.
+ to random-sort objectA a (object) objectB b (object): return random[2] - 1.
+ to save-drivers: driver (object). n (int).
foreach driver in monkeys: { monkeys{n} = ((driver get-genome) save-as-xml file "driver$n.xml"). n++. }
+ to load-drivers: n (int).
for n=0,n<15,n++: { ((monkeys{n} get-genome) load-from-xml file "driver$n.xml"). }
self pick-drivers. print monkeys{currentSeat}.
+ to change-drivers: newDriver (int). newOffset (vector).
# if they have locked the current driver, do nothing.
if locked: return.
# pick a new camera angle and pan...
newOffset = random[(30, 6, 30)] + (-15, 1, -15). if |newOffset| < 9: newOffset = 9 * newOffset/|newOffset|. self pan-camera-offset by newOffset steps 30.
# we change the drivers every time a monkey is finished it's # turn. if we have seen the last monkey, breed them together.
seats{currentSeat} set-distance to |(wigglyThing get-location)|.
currentSeat += 1.
if currentSeat > 3: { self breed-new-monkeys. self pick-drivers. }
newDriver = (seats{currentSeat} get-number).
if wigglyThing: free wigglyThing.
wigglyThing = new Creature. wigglyThing init-with genome (seats{currentSeat} get-genome). wigglyThing move to (0, 3, 0). self watch item wigglyThing.
# schedule a new driver change in 20 seconds.
self schedule method-call "change-drivers" at-time (self get-time) + 20.0. self display-current-driver.
+ to breed-new-monkeys: print "breeding monkeys...".
sort seats with compare-distance.
# print out the distance for each individual tested
print "driver ", (seats{0} get-number), (seats{0} get-distance). print "driver ", (seats{1} get-number), (seats{1} get-distance). print "driver ", (seats{2} get-number), (seats{2} get-distance). print "driver ", (seats{3} get-number), (seats{3} get-distance).
# breed the two best twice, replacing the two worst.
seats{0} breed with seats{1} to-child seats{2}. seats{1} breed with seats{0} to-child seats{3}.
# give each individual a mutation
(seats{2} get-genome) mutate. (seats{3} get-genome) mutate.
+ to compare-distance of a (object) with b (object): result (float).
result = (b get-distance) - (a get-distance). return result.
# the following methods are accessed from the simulation menu.
+ to toggle-driver-lock: if locked == 1: { locked = 0. wigglyThing center. self schedule method-call "change-drivers" at-time (self get-time) + 20.0. lockMenu uncheck. } else { locked = 1. lockMenu check. }
+ to save-current-genome: (seats{currentSeat} get-genome) save-with-dialog.
+ to load-into-current-genome: (seats{currentSeat} get-genome) load-with-dialog. }
Object : Monkeys { + variables: distanceTraveled (float). genome (object).
number (int).
+ to set-number to n (int): number = n.
+ to get-number: return number.
+ to init: genome = new MonkeyGenome. genome randomize.
self add-dependency on genome.
+ to get-genome: return genome.
+ to breed with otherMonkey (object) to-child child (object): (child get-genome) crossover from-parent-1 (otherMonkey get-genome) from-parent-2 (self get-genome).
+ to control robot theRobot (object) at-time t (float): n (int).
while n < 8: { theRobot set-joint-velocity number n to SPEED_K * (genome calculate-torque number n at t). n+=1. }
+ to set-distance to value (float): distanceTraveled = value.
+ to get-distance: return distanceTraveled. }
Genome : MonkeyGenome { + variables: waveCompression (float). phaseShifts (8 floats). ampShifts (8 floats). limbLengths (8 floats).
+ to randomize: n (int).
for n=0, n<8, n++: { phaseShifts[n] = random[6.3] - 3.15. ampShifts[n] = random[1.0] - .5. limbLengths[n] = random[2.0] + .5.
}
waveCompression = random[5.0] - 2.5.
+ to calculate-torque number jointNum (int) at time (float): # calculates the torque for a certain joint number.
return .5 sin(waveCompression (time + phaseShifts[jointNum]) - (ampShifts[jointNum])).
+ to get-limb-length number n (int): return limbLengths[n].
+ to mutate: n (int).
# we need to decide which of the elements in this genome to mutate
n = random[25].
if n < 8: ampShifts[n] = random[2.0] - 1.0. else if n < 16: phaseShifts[n - 8] = random[6.3] - 3.15. else if n < 24: limbLengths[n - 16] = random[2.0] + .5. else waveCompression = random[5.0] - 2.5.
print "mutated item $n of $self". }
MultiBody : Creature { + variables: bodyLink (object).
links (list).
+ to get-root: return bodyLink.
+ to init-with genome g (object): x, y (float). lowerLimbSize, upperLimbSize (vector). lowerLimbLinkPoint, upperLimbLinkPoint (vector). counter (int). linkShape, lowerLinkShape, bodyShape (object).
self add-menu named "Send to Center" for-method "center".
# we want the limb volume to be constant at 16, but the dimensions can change.
y = (g get-limb-length number 0). x = sqrt(.16/y). x = sqrt(.16/y).
lowerLimbSize = (x, y, x). lowerLimbLinkPoint = (0, y/2, 0).
y = (g get-limb-length number 1). x = sqrt(.16/y). x = sqrt(.16/y).
upperLimbSize = (x, y, x). upperLimbLinkPoint = (0, y/2, 0).
lowerLinkShape = new Shape. lowerLinkShape init-with-cube size lowerLimbSize.
linkShape = new Shape. linkShape init-with-cube size upperLimbSize.
bodyShape = new Shape. bodyShape init-with-cube size (4, 3, .4).
counter = 0. while counter < 8: { links{counter} = new Link. counter+=1. }
links{0} set shape linkShape. links{2} set shape linkShape. links{4} set shape linkShape. links{6} set shape linkShape. links{1} set shape lowerLinkShape. links{3} set shape lowerLinkShape. links{5} set shape lowerLinkShape. links{7} set shape lowerLinkShape.
links set-color to random[(1.0, 1.0, 1.0)].
bodyLink = new Link. bodyLink set shape bodyShape.
linkShape unretain. lowerLinkShape unretain. bodyShape unretain.
self register with-link bodyLink.
links{0} link-revolute to-link bodyLink with-normal (0, 0, 1) with-parent-point (2.0, -1.5, 0) with-child-point upperLimbLinkPoint. links{1} link-revolute to-link links{0} with-normal (1, 0, 0) with-parent-point -upperLimbLinkPoint with-child-point lowerLimbLinkPoint.
links{4} link-revolute to-link bodyLink with-normal (0, 0, 1) with-parent-point (-2.0, -1.5, 0) with-child-point upperLimbLinkPoint. links{5} link-revolute to-link links{4} with-normal (1, 0, 0) with-parent-point -upperLimbLinkPoint with-child-point lowerLimbLinkPoint.
links{2} link-revolute to-link bodyLink with-normal (0, 0, 1) with-parent-point (2.0, 1.5, 0) with-child-point -upperLimbLinkPoint. links{3} link-revolute to-link links{2} with-normal (1, 0, 0) with-parent-point upperLimbLinkPoint with-child-point -lowerLimbLinkPoint.
links{6} link-revolute to-link bodyLink with-normal (0, 0, 1) with-parent-point (-2.0, 1.5, 0) with-child-point -upperLimbLinkPoint. links{7} link-revolute to-link links{6} with-normal (1, 0, 0) with-parent-point upperLimbLinkPoint with-child-point -lowerLimbLinkPoint.
# rotate the creature and move it to above the origin.
self rotate around-axis (1, 0, 0) by 1.57.
links set-double-spring with-strength 200 with-max .6 with-min -.6. links set-strength-limit to 250.
# to center the object, we set the X and Z components to 0, but not # the Y, otherwise we would push the walker into the ground
+ to center: currentLocation (vector).
currentLocation = (self get-location). self move to (0, currentLocation::y, 0).
# The following four method allow external objects to manipulate # the torque values of the links.
+ to set-joint-velocity number jointNum (int) to value (float): links{jointNum} set-joint-velocity to value.
+ to destroy: free bodyLink. free links. }