-
Notifications
You must be signed in to change notification settings - Fork 49
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Initial commit with basic version of EA loop (#2) Contains the basic loop, but very rough. Everything needs cleanup and API improvements. Isaac Gym simulation environment. Modular robot framework that can be used to create robots for the simulation environment. An optimization framework for evolutionary processes. A database system to store results and provide recovery in case of failure. * Expose sim params in isaac gym local runner * For isaac runner, add a bunch of todos. Make initial pos and rot configurable. * Review apis (#10) * For modular robots: remove slot, rotation is now directly part of Module * Remove back from BRICK module, because that's where it's attached so it can't be filled * Add brain 'cpg_random', which randomly initialized cpg weights * Remove old file * Rework an error message in ea selection multiple_unique * Added TODOs to database that transactions still need to be implmeented * Remove some more old BACK from BRICK Co-authored-by: Aart Stuurman <[email protected]> * Add some missing packages to setup.py * Correct values in to_urdf * Fix bug in ea were population and new individuals were swapped in a function call * Add headless for isaac runner * Store individuals seperate from generation (#19) * Added individual class to ea * Individuals stored seperately from generations. generations now use references * Individual members now individually serialized * Create Serializable class. Use it to serialize Genotype and Evaluation nicer * Add isaac as a prereq for isaac env * Individuals now also list parents Co-authored-by: Aart Stuurman <[email protected]> * Improve cpg performance (#20) Co-authored-by: Aart Stuurman <[email protected]> * In EA, rename evaluation to fitness. (#24) Co-authored-by: Aart Stuurman <[email protected]> * 22 simulation history (#25) * Can set update frequency. * Implement sampling frequency * Add getting path from AnyView * Fixed bug in DictView where dict 'contains' was not properly programmed. * EA optimizer now makes space in the database were the evaluator can do its work. * Save individual rng objects for each generation * Make physics State serializable Co-authored-by: Aart Stuurman <[email protected]> * Emergency fix: fix incorrect assertion in EA * 21 analysis - intermediate merge (#29) * Add analyzer to ea optimization * Fix bug in ea analyzer where Generations did not always create Generation correctly. * Improve serialization of cppnneat genotypes * Add plot script for ea fitness * Add modular robot rerunner. Update ea plotter with max min avg * add prepare_sim to isaac runner so gpu can be used Co-authored-by: Aart Stuurman <[email protected]> * Fix py.typed. Fix many mypy errors - intermediate merge for #3 (#30) Co-authored-by: Aart Stuurman <[email protected]> * 28 improve database performance (#32) * Completely reworked db interface and inner workings. Db implementations can now be significantly faster. * Removed all existing db implementations * Added SQLite implementation * Updated all existing code to use the new db interface Co-authored-by: Aart Stuurman <[email protected]> * 15 isaac leak (#34) * Isaac now runs in a subprocess every batch Co-authored-by: Aart Stuurman <[email protected]> * Removed asyncinit dependency (#35) Co-authored-by: Aart Stuurman <[email protected]> * Add aabb calculation for actor. Can be used to know how far to put robots above ground. (#37) Co-authored-by: Aart Stuurman <[email protected]> * 27 robot friction (#39) * Add static friction to robot rigid bodies * Set friction paremeters to be arbitrarily chosen values that look good * Remove some old imports Co-authored-by: Aart Stuurman <[email protected]> * for cppnneat brain genotype set output func to signed sign (#41) Co-authored-by: Aart Stuurman <[email protected]> * Add modular robot size measurement and number of bricks to modular robot (#42) Co-authored-by: Aart Stuurman <[email protected]> * Add support for multiple runs to plt_ea_fitness script. Co-authored-by: Aart Stuurman <[email protected]>
- Loading branch information
Showing
91 changed files
with
4,270 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
""" | ||
Rerun(watch) a modular robot in isaac gym. | ||
""" | ||
|
||
from pyrr import Quaternion, Vector3 | ||
from revolve2.core.modular_robot import ModularRobot | ||
from revolve2.core.physics.control import ActorController | ||
from revolve2.core.physics.env import ActorControl, Batch, Environment, PosedActor | ||
from revolve2.envs.isaacgym import LocalRunner | ||
|
||
|
||
class ModularRobotRerunner: | ||
_controller: ActorController | ||
|
||
async def rerun(self, robot: ModularRobot, control_frequency: float) -> None: | ||
batch = Batch( | ||
simulation_time=1000000, | ||
sampling_frequency=0.0001, | ||
control_frequency=control_frequency, | ||
control=self._control, | ||
) | ||
|
||
actor, self._controller = robot.make_actor_and_controller() | ||
|
||
env = Environment() | ||
env.actors.append(PosedActor(actor, Vector3([0.0, 0.0, 0.1]), Quaternion())) | ||
batch.environments.append(env) | ||
|
||
runner = LocalRunner(LocalRunner.SimParams()) | ||
await runner.run_batch(batch) | ||
|
||
def _control(self, dt: float, control: ActorControl) -> None: | ||
self._controller.step(dt) | ||
control.set_dof_targets(0, 0, self._controller.get_dof_targets()) | ||
|
||
|
||
if __name__ == "__main__": | ||
print( | ||
"This file cannot be ran as a script. Import it and use the contained classes instead." | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
""" | ||
Plot average, min, and max fitness over generations, using the results of the evolutionary optimizer. | ||
Assumes fitness is a float and database is files. | ||
""" | ||
|
||
import argparse | ||
from statistics import mean | ||
|
||
import matplotlib.pyplot as plt | ||
from revolve2.core.database.sqlite import Database | ||
from revolve2.core.optimization.ea import Analyzer as EaAnalyzer | ||
from revolve2.core.optimization.ea.analyzer import Generation | ||
|
||
|
||
async def main() -> None: | ||
parser = argparse.ArgumentParser() | ||
parser.add_argument( | ||
"databases", | ||
nargs="+", | ||
help="The databases to make plots for. If more then one database(multiple runs of the same experiment) is provided, their respective plots are averaged into one single plot.", | ||
) | ||
args = parser.parse_args() | ||
|
||
max_fitnesses = [] | ||
min_fitnesses = [] | ||
mean_fitnesses = [] | ||
|
||
for db_file in args.databases: | ||
db = await Database.create(db_file) | ||
with db.begin_transaction() as txn: | ||
analyzer = EaAnalyzer(txn, db.root) | ||
|
||
max_fitness = [ | ||
max( | ||
[ | ||
analyzer.individuals[individual].fitness | ||
for individual in generation | ||
], | ||
) | ||
for generation in analyzer.generations | ||
] | ||
|
||
min_fitness = [ | ||
min( | ||
[ | ||
analyzer.individuals[individual].fitness | ||
for individual in generation | ||
], | ||
) | ||
for generation in analyzer.generations | ||
] | ||
|
||
mean_fitness = [ | ||
mean( | ||
[ | ||
analyzer.individuals[individual].fitness | ||
for individual in generation | ||
], | ||
) | ||
for generation in analyzer.generations | ||
] | ||
max_fitnesses.append(max_fitness) | ||
min_fitnesses.append(min_fitness) | ||
mean_fitnesses.append(mean_fitness) | ||
|
||
assert all( | ||
len(max_fitnesses[0]) == len(x) for x in max_fitnesses | ||
), "Not all databases have an equal amount of generations." | ||
|
||
mean_max_fitness = [mean(x) for x in list(map(list, zip(*max_fitnesses)))] | ||
mean_min_fitness = [mean(x) for x in list(map(list, zip(*min_fitnesses)))] | ||
mean_avg_fitness = [mean(x) for x in list(map(list, zip(*mean_fitnesses)))] | ||
|
||
x = [i for i in range(len(mean_max_fitness))] | ||
|
||
fig, ax = plt.subplots() | ||
ax.plot( | ||
x, | ||
mean_max_fitness, | ||
) | ||
ax.plot( | ||
x, | ||
mean_min_fitness, | ||
) | ||
ax.plot( | ||
x, | ||
mean_avg_fitness, | ||
) | ||
plt.show() | ||
|
||
|
||
if __name__ == "__main__": | ||
import asyncio | ||
|
||
asyncio.run(main()) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
from .database import Database | ||
from .database_error import DatabaseError | ||
from .list import List | ||
from .node import Node | ||
from .object import Object | ||
from .static_data import StaticData, is_static_data | ||
from .transaction import Transaction | ||
from .uninitialized import Uninitialized |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
from abc import ABC, abstractmethod | ||
|
||
from .node import Node | ||
from .transaction import Transaction | ||
|
||
|
||
class Database(ABC): | ||
@abstractmethod | ||
def begin_transaction(self) -> Transaction: | ||
""" | ||
Begin a transaction context. | ||
""" | ||
pass | ||
|
||
@property | ||
@abstractmethod | ||
def root(self) -> Node: | ||
""" | ||
Get the root node of the database. | ||
""" | ||
pass |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
class DatabaseError(Exception): | ||
pass |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
from typing import Optional | ||
|
||
from .database_error import DatabaseError | ||
from .list_impl import ListImpl | ||
from .node import Node | ||
from .transaction import Transaction | ||
|
||
|
||
class List: | ||
_impl: Optional[ListImpl] | ||
|
||
def __init__(self, impl: ListImpl = None): | ||
self._impl = impl | ||
|
||
@property | ||
def is_stub(self) -> bool: | ||
""" | ||
If list is not yet linked to the database but a stub created by the user. | ||
""" | ||
return self._impl is None | ||
|
||
def get_or_append(self, txn: Transaction, index: int) -> Node: | ||
""" | ||
Get the item at the given index, or if the list is one short, | ||
append to the list and return the new node. | ||
If the list is more than one too short or the index is not the last index in the list, | ||
DatabaseError is thrown. | ||
""" | ||
if self.is_stub: | ||
raise DatabaseError( | ||
"List not usable yet. It is a stub created by the user that has not yet been linked with the database." | ||
) | ||
|
||
return self._impl.get_or_append(txn, index) | ||
|
||
def append(self, txn: Transaction) -> Node: | ||
""" | ||
Append a new node to the list and return it. | ||
""" | ||
if self.is_stub: | ||
raise DatabaseError( | ||
"List not usable yet. It is a stub created by the user that has not yet been linked with the database." | ||
) | ||
|
||
return self._impl.append(txn) | ||
|
||
def get(self, txn: Transaction, index: int) -> Node: | ||
""" | ||
Get the item at the given index. | ||
:raises: DatabaseError if out of bounds | ||
""" | ||
if self.is_stub: | ||
raise DatabaseError( | ||
"List not usable yet. It is a stub created by the user that has not yet been linked with the database." | ||
) | ||
|
||
return self._impl.get(txn, index) | ||
|
||
def len(self, txn: Transaction) -> int: | ||
""" | ||
Get the length of the list. | ||
""" | ||
if self.is_stub: | ||
raise DatabaseError( | ||
"List not usable yet. It is a stub created by the user that has not yet been linked with the database." | ||
) | ||
|
||
return self._impl.len(txn) | ||
|
||
def _set_impl(self, impl: ListImpl) -> None: | ||
self._impl = impl |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
from abc import ABC, abstractmethod | ||
|
||
from .node import Node | ||
from .transaction import Transaction | ||
|
||
|
||
class ListImpl(ABC): | ||
@abstractmethod | ||
def get_or_append(self, txn: Transaction, index: int) -> Node: | ||
pass | ||
|
||
@abstractmethod | ||
def append(self, txn: Transaction) -> Node: | ||
pass | ||
|
||
@abstractmethod | ||
def get(self, txn: Transaction, index: int) -> Node: | ||
pass | ||
|
||
@abstractmethod | ||
def len(self, txn: Transaction) -> int: | ||
pass |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
from typing import Optional, Union | ||
|
||
from .database_error import DatabaseError | ||
from .node_impl import NodeImpl | ||
from .object import Object | ||
from .transaction import Transaction | ||
from .uninitialized import Uninitialized | ||
|
||
|
||
class Node: | ||
""" | ||
Represents a node in a database. | ||
Can be either an object or unitialized. | ||
""" | ||
|
||
_impl: Optional[NodeImpl] | ||
|
||
def __init__(self, impl: NodeImpl = None): | ||
self._impl = impl | ||
|
||
@property | ||
def is_stub(self) -> bool: | ||
""" | ||
If node is not yet linked to the database but a stub created by the user. | ||
""" | ||
return self._impl is None | ||
|
||
def get_object(self, txn: Transaction) -> Union[Object, Uninitialized]: | ||
""" | ||
Read the underlying object from the database. | ||
""" | ||
if self.is_stub: | ||
raise DatabaseError() | ||
|
||
return self._impl.get_object(txn) | ||
|
||
def set_object(self, txn: Transaction, object: Object) -> None: | ||
""" | ||
Set the underlying object in the database. | ||
If object is not uninitialized, raises DatabaseError. | ||
""" | ||
if self.is_stub: | ||
raise DatabaseError( | ||
"Node not usable yet. It is a stub created by the user that has not yet been linked with the database." | ||
) | ||
|
||
return self._impl.set_object(txn, object) | ||
|
||
def _set_impl(self, impl: NodeImpl) -> None: | ||
self._impl = impl |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
from abc import ABC, abstractmethod | ||
from typing import Union | ||
|
||
from .object import Object | ||
from .transaction import Transaction | ||
from .uninitialized import Uninitialized | ||
|
||
|
||
class NodeImpl(ABC): | ||
@abstractmethod | ||
def get_object(self, txn: Transaction) -> Union[Object, Uninitialized]: | ||
pass | ||
|
||
@abstractmethod | ||
def set_object(self, txn: Transaction, object: Object) -> None: | ||
pass |
Oops, something went wrong.