Skip to content

Commit 6d93448

Browse files
reintroduced robot collision analyzer
there is an example script that can be run from the param revolve.py --test-robot-collision path/to/robot_phenotype.yaml
1 parent 1c0d0ef commit 6d93448

File tree

7 files changed

+139
-82
lines changed

7 files changed

+139
-82
lines changed

cpprevolve/revolve/gazebo/plugin/BodyAnalyzer.cpp

+3-1
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,9 @@ void BodyAnalyzer::OnContacts(ConstContactsPtr &msg)
202202

203203
void BodyAnalyzer::AnalyzeRequest(ConstRequestPtr &request)
204204
{
205-
if (request->request() not_eq " ")
205+
auto request_title = request->request();
206+
207+
if (request_title not_eq "analyze_body")
206208
{
207209
// Request is not meant for us
208210
return;

pyrevolve/config.py

+7-1
Original file line numberDiff line numberDiff line change
@@ -393,7 +393,13 @@ def str_to_address(v):
393393
parser.add_argument(
394394
'--test-robot',
395395
default=None, type=str,
396-
help="Start a simulation with a single robot instead of running evolution. Loads yaml robots."
396+
help="Start a simulation with a single robot instead of running evolution. Loads a yaml robot."
397+
)
398+
399+
parser.add_argument(
400+
'--test-robot-collision',
401+
default=None, type=str,
402+
help="Tests the collision of a single robot. Loads a yaml robot."
397403
)
398404

399405
parser.add_argument(
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import asyncio
2+
import logging
3+
import os
4+
5+
import sys
6+
import time
7+
8+
from pyrevolve import parser
9+
from pyrevolve.custom_logging import logger
10+
from pyrevolve.gazebo.analyze import BodyAnalyzer
11+
from pyrevolve.revolve_bot import RevolveBot
12+
from pyrevolve.util.supervisor.supervisor_collision import CollisionSimSupervisor
13+
14+
15+
async def test_collision_robot(robot_file_path: str):
16+
log = logger.create_logger('experiment', handlers=[
17+
logging.StreamHandler(sys.stdout),
18+
])
19+
20+
# Set debug level to DEBUG
21+
log.setLevel(logging.DEBUG)
22+
23+
# Parse command line / file input arguments
24+
settings = parser.parse_args()
25+
26+
assert (settings.test_robot_collision is not None)
27+
robot = RevolveBot(_id=settings.test_robot_collision)
28+
robot.load_file(robot_file_path, conf_type='yaml')
29+
robot.save_file(f'{robot_file_path}.sdf', conf_type='sdf')
30+
31+
def simulator_died_callback(_process, _ret_code):
32+
pass
33+
34+
# Start Simulator
35+
if settings.simulator_cmd != 'debug':
36+
simulator_supervisor = CollisionSimSupervisor(
37+
world_file=os.path.join('tools', 'analyzer', 'analyzer-world.world'),
38+
simulator_cmd=settings.simulator_cmd,
39+
simulator_args=["--verbose"],
40+
plugins_dir_path=os.path.join('.', 'build', 'lib'),
41+
models_dir_path=os.path.join('.', 'models'),
42+
simulator_name='gazebo',
43+
process_terminated_callback=simulator_died_callback,
44+
)
45+
await simulator_supervisor.launch_simulator(port=settings.port_start)
46+
await asyncio.sleep(0.1)
47+
48+
log.debug("simulator ready")
49+
50+
# Connect to the simulator and pause
51+
analyzer = await BodyAnalyzer.create('127.0.0.1', settings.port_start)
52+
log.debug("connection ready")
53+
await asyncio.sleep(1)
54+
55+
log.info("Sending robot to the analyzer simulator")
56+
start = time.time()
57+
result = await analyzer.analyze_robot(robot)
58+
end = time.time()
59+
60+
log.debug(f'Analyzer finished in {end-start}')
61+
log.debug('result:')
62+
log.debug(result)
63+
64+
await analyzer.disconnect()
65+
log.debug("disconnected")
66+
67+
if settings.simulator_cmd != 'debug':
68+
await simulator_supervisor.stop()
69+
70+
log.debug("simulator killed")

pyrevolve/data_analisys/visualize_robot.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,8 @@ async def test_robot_run(robot_file_path: str):
4646
robot.load_file(robot_file_path, conf_type='yaml')
4747
robot.save_file(f'{robot_file_path}.sdf', conf_type='sdf')
4848

49+
await connection.pause(True)
4950
robot_manager = await connection.insert_robot(robot, Vector3(0, 0, 0.25), life_timeout=None)
50-
await connection.pause(False)
5151
await asyncio.sleep(1.0)
5252

5353
# Start the main life loop

pyrevolve/gazebo/analyze.py

+19-79
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,6 @@
1-
from __future__ import absolute_import
2-
from __future__ import print_function
3-
4-
import asyncio
5-
import logging
6-
import sys
7-
8-
from pyrevolve.sdfbuilder import SDF
9-
from pyrevolve.sdfbuilder.sensor import Sensor
10-
11-
from ..spec import BodyAnalysisResponse
1+
from pyrevolve.custom_logging.logger import logger
2+
from pyrevolve.SDF.math import Vector3
3+
from pyrevolve.spec import BodyAnalysisResponse
124
from .connect import connect, RequestHandler
135

146
# Message ID sequencer, start at some high value to prevent ID clashes
@@ -23,68 +15,13 @@ def _msg_id():
2315
return r
2416

2517

26-
# Prevent the trollius logging warning
27-
kl = logging.getLogger("trollius")
28-
kl.addHandler(logging.StreamHandler(sys.stdout))
29-
30-
31-
def get_analysis_robot(robot, builder):
32-
"""
33-
Creates an SDF model suitable for analysis from a robot object
34-
and a builder.
35-
:param robot:
36-
:type robot: Robot
37-
:param builder:
38-
:type builder: BodyBuilder
39-
:return:
40-
"""
41-
model = builder.sdf_robot(
42-
robot=robot,
43-
controller_plugin=None,
44-
name="analyze_bot",
45-
analyzer_mode=True)
46-
model.remove_elements_of_type(Sensor, recursive=True)
47-
sdf = SDF()
48-
sdf.add_element(model)
49-
return sdf
50-
51-
52-
def analyze_body(sdf, address=("127.0.0.1", 11346)):
53-
"""
54-
Single body analyzer. Opens a new connection, analyzes the
55-
body, and returns the result. If you already have a manager
56-
running doing other things, create an instance of `BodyAnalyzer`
57-
instead.
58-
59-
:param sdf: SDF object consisting of BodyPart
60-
instances.
61-
:type sdf: SDF
62-
:param address: Tuple of the hostname and port where the analyzer
63-
resides. Note that the default is one up from the default
64-
Gazebo port, since it is meant to be used with the
65-
`run-analyzer.sh` tool.
66-
:type address: (str, int)
67-
:return:
68-
:rtype: (bool, (float, float, float))
69-
"""
70-
response_obj = [None]
71-
72-
async def internal_analyze():
73-
analyzer = await (BodyAnalyzer.create(address))
74-
response_obj[0] = await (analyzer.analyze_sdf(sdf))
75-
76-
loop = asyncio.get_event_loop()
77-
loop.run_until_complete(internal_analyze())
78-
return response_obj[0]
79-
80-
8118
class BodyAnalyzer(object):
8219
"""
8320
Class used to make body analysis requests.
8421
"""
8522
_PRIVATE = object()
8623

87-
def __init__(self, _private, address):
24+
def __init__(self, _private, address, port):
8825
"""
8926
Private constructor - use the `create` coroutine instead.
9027
@@ -97,43 +34,46 @@ def __init__(self, _private, address):
9734
"the `create` coroutine.")
9835

9936
self.address = address
37+
self.port = port
10038
self.manager = None
10139
self.request_handler = None
10240

10341
@classmethod
104-
async def create(cls, address=("127.0.0.1", 11346)):
42+
async def create(cls, address, port):
10543
"""
10644
Instantiates a new body analyzer at the given address.
10745
108-
:param address: host, port tuple.
109-
:type address: (str, int)
46+
:param address: hostname.
47+
:param port: host port.
11048
:return:
11149
"""
112-
self = cls(cls._PRIVATE, address)
113-
await (self._init())
50+
self = cls(cls._PRIVATE, address, port)
51+
await self._init()
11452
return self
11553

11654
async def _init(self):
11755
"""
11856
BodyAnalyzer initialization coroutine
11957
:return:
12058
"""
121-
self.manager = await (connect(self.address))
59+
self.manager = await connect(self.address)
12260
self.request_handler = await (
12361
RequestHandler.create(self.manager, msg_id_base=_msg_id()))
12462

125-
async def analyze_robot(self, robot, builder, max_attempts=5):
63+
async def disconnect(self):
64+
await self.manager.stop()
65+
await self.request_handler.stop()
66+
67+
async def analyze_robot(self, robot, max_attempts=5):
12668
"""
12769
Performs body analysis of a given Robot object.
12870
:param robot:
12971
:type robot: Robot
130-
:param builder:
131-
:type builder: Builder
13272
:param max_attempts:
13373
:return:
13474
"""
135-
sdf = get_analysis_robot(robot, builder)
136-
ret = await (self.analyze_sdf(sdf, max_attempts=max_attempts))
75+
sdf = robot.to_sdf(pose=Vector3(0, 0, 0))
76+
ret = await self.analyze_sdf(sdf, max_attempts=max_attempts)
13777
return ret
13878

13979
async def analyze_sdf(self, sdf, max_attempts=5):
@@ -156,7 +96,7 @@ async def analyze_sdf(self, sdf, max_attempts=5):
15696
break
15797

15898
if not msg:
159-
# Error return
99+
logger.error("analyze_sdf returned not valid message")
160100
return None
161101

162102
if msg.HasField("boundingBox"):
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
from .supervisor_multi import DynamicSimSupervisor
2+
3+
4+
class CollisionSimSupervisor(DynamicSimSupervisor):
5+
def __init__(self,
6+
world_file,
7+
output_directory=None,
8+
simulator_cmd="gzserver",
9+
simulator_args=None,
10+
restore_arg="--restore-directory",
11+
snapshot_world_file="snapshot.world",
12+
restore_directory=None,
13+
plugins_dir_path=None,
14+
models_dir_path=None,
15+
simulator_name='collision',
16+
process_terminated_callback=None,
17+
):
18+
super().__init__(world_file,
19+
output_directory,
20+
simulator_cmd,
21+
simulator_args,
22+
restore_arg,
23+
snapshot_world_file,
24+
restore_directory,
25+
plugins_dir_path,
26+
models_dir_path,
27+
simulator_name,
28+
process_terminated_callback)
29+
30+
async def _launch_simulator(self,
31+
ready_str="Body analyzer ready",
32+
output_tag="simulator",
33+
address='localhost',
34+
port=11345):
35+
return await super()._launch_simulator(ready_str, output_tag, address, port)

revolve.py

+4
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import importlib
66

77
from pyrevolve.data_analisys.visualize_robot import test_robot_run
8+
from pyrevolve.data_analisys.check_robot_collision import test_collision_robot
89
from pyrevolve import parser
910
from experiments.examples import only_gazebo
1011

@@ -17,6 +18,9 @@ def run(loop, arguments):
1718
if arguments.test_robot is not None:
1819
return loop.run_until_complete(test_robot_run(arguments.test_robot))
1920

21+
if arguments.test_robot_collision is not None:
22+
return loop.run_until_complete(test_collision_robot(arguments.test_robot_collision))
23+
2024
if arguments.manager is not None:
2125
# this split will give errors on windows
2226
manager_lib = os.path.splitext(arguments.manager)[0]

0 commit comments

Comments
 (0)