Skip to content

Commit 173fcd4

Browse files
Merge pull request #39 from safsaf150/brain_drawings_and_measurements
Brain drawings
2 parents ef1261c + 231c436 commit 173fcd4

File tree

7 files changed

+113
-28
lines changed

7 files changed

+113
-28
lines changed

pyrevolve/revolve_bot/brain_nn.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ def FromYaml(yaml_object):
4444
params.generate_params(yaml_object['params'][k_node])
4545
brain.params[k_node] = params
4646

47+
return brain
48+
4749
def to_yaml(self):
4850
yaml_dict_brain = OrderedDict()
4951

pyrevolve/revolve_bot/measure.py

Lines changed: 29 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,8 @@ def count_branching_bricks(self, module=None):
4141
for core_slot, child_module in module.iter_children():
4242
if child_module is None:
4343
continue
44-
children_count += 1
44+
if not isinstance(child_module, TouchSensorModule) and not isinstance(child_module, BrickSensorModule):
45+
children_count += 1
4546
self.count_branching_bricks(child_module)
4647
if (isinstance(module, BrickModule) and children_count == 3) or (isinstance(module, CoreModule) and children_count == 4):
4748
self.branching_modules_count += 1
@@ -70,25 +71,27 @@ def measure_branching(self):
7071
def calculate_extremities_extensiveness(self, module=None, extremities=False, extensiveness=False):
7172
"""
7273
Calculate extremities or extensiveness in body
74+
@param extremities: calculate extremities in body if true
75+
@param extensiveness: calculate extensiveness in body if true
7376
"""
7477
try:
7578
if module is None:
7679
module = self.body
7780

78-
if module.has_children():
79-
children_count = 0
80-
for core_slot, child_module in module.iter_children():
81-
if child_module is None:
82-
continue
81+
children_count = 0
82+
for core_slot, child_module in module.iter_children():
83+
if child_module is None:
84+
continue
85+
if not isinstance(child_module, TouchSensorModule):
8386
children_count += 1
84-
if extremities:
85-
self.calculate_extremities_extensiveness(child_module, True, False)
86-
if extensiveness:
87-
self.calculate_extremities_extensiveness(child_module, False, True)
88-
if children_count == 1 and not isinstance(module, CoreModule) and extremities:
89-
self.extremities += 1
90-
if children_count == 2 and not isinstance(module, CoreModule) and extensiveness:
91-
self.extensiveness += 1
87+
if extremities:
88+
self.calculate_extremities_extensiveness(child_module, True, False)
89+
if extensiveness:
90+
self.calculate_extremities_extensiveness(child_module, False, True)
91+
if children_count == 0 and not (isinstance(module, CoreModule) or isinstance(module, TouchSensorModule)) and extremities:
92+
self.extremities += 1
93+
if children_count == 1 and not (isinstance(module, CoreModule) or isinstance(module, TouchSensorModule)) and extensiveness:
94+
self.extensiveness += 1
9295
except Exception as e:
9396
print('Failed calculating extremities or extensiveness')
9497
print('Exception: {}'.format(e))
@@ -104,7 +107,7 @@ def measure_limbs(self):
104107
if self.absolute_size < 6:
105108
practical_limit_limbs = self.absolute_size - 1
106109
else:
107-
practical_limit_limbs = 2 * math.floor((self.absolute_size-6)/3) + (self.absolute_size - 6) % 3 + 4
110+
practical_limit_limbs = 2 * math.floor((self.absolute_size - 6) / 3) + ((self.absolute_size - 6) % 3) + 4
108111
self.calculate_extremities_extensiveness(None, True, False)
109112
if self.extremities == 0:
110113
self.limbs = 0
@@ -181,7 +184,6 @@ def count_active_hinges(self, module=None):
181184
try:
182185
if module is None:
183186
module = self.body
184-
185187
if module.has_children():
186188
if isinstance(module, ActiveHingeModule):
187189
self.active_hinges_count += 1
@@ -204,7 +206,10 @@ def measure_joints(self):
204206
self.joints = 0
205207
return 0
206208
self.count_active_hinges()
207-
practical_limit_active_hinges = math.floor((self.absolute_size-1)/2)
209+
practical_limit_active_hinges = self.absolute_size - 2
210+
if self.active_hinges_count == 0:
211+
self.joints = 0
212+
return 0
208213
self.joints = self.active_hinges_count / practical_limit_active_hinges
209214
return self.joints
210215

@@ -227,8 +232,9 @@ def measure_absolute_size(self, module=None):
227232
:return:
228233
"""
229234
try:
230-
self.calculate_count()
231-
self.absolute_size = self.brick_count + self.hinge_count + 1
235+
if self.absolute_size is None:
236+
self.calculate_count()
237+
self.absolute_size = self.brick_count + self.hinge_count + 1
232238
return self.absolute_size
233239
except Exception as e:
234240
print('Failed measuring absolute size')
@@ -298,9 +304,9 @@ def measure_all(self):
298304
self.measure_coverage()
299305
self.measure_symmetry()
300306
self.measure_branching()
301-
return self.get_all_measurements()
307+
return self.measurement_to_dict()
302308

303-
def get_all_measurements(self):
309+
def measurement_to_dict(self):
304310
"""
305311
Return dict of all measurements
306312
:return:
@@ -309,7 +315,9 @@ def get_all_measurements(self):
309315
'branching': self.branching,
310316
'branching_modules_count': self.branching_modules_count,
311317
'limbs': self.limbs,
318+
'extremeties': self.extremities,
312319
'length_of_limbs': self.length_of_limbs,
320+
'extensiveness': self.extensiveness,
313321
'coverage': self.coverage,
314322
'joints': self.joints,
315323
'hinge_count': self.hinge_count,
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
from graphviz import Digraph, render
2+
# belong to TODO
3+
import fnmatch
4+
5+
6+
class BrainGraph:
7+
def __init__(self, brain, name='brain', typename='brain'):
8+
self.graph = Digraph(typename, filename=name, format='png')
9+
self.brain = brain
10+
11+
def add_node(self, node_id, node_type, text):
12+
"""
13+
Add node to graph
14+
@param node_id: id of node
15+
@param node_type: type of node
16+
@param text: text to show inside node
17+
"""
18+
if node_type == 'Input':
19+
self.graph.attr('node', shape='circle')
20+
elif node_type == 'Oscillator':
21+
self.graph.attr('node', shape='square')
22+
self.graph.node(node_id, label=text)
23+
24+
def add_edge(self, source_id, desitnation_id, label):
25+
"""
26+
Add edge to graph
27+
@param source_id: id of source node
28+
@param destination_id: id of destination node
29+
@param label: label of edge
30+
"""
31+
self.graph.edge(source_id, desitnation_id, label)
32+
33+
def save_graph(self):
34+
"""
35+
Save graph
36+
"""
37+
self.graph.render()
38+
39+
def brain_to_graph(self):
40+
"""
41+
Export complete brain to graph
42+
"""
43+
44+
nodes = self.brain.nodes
45+
params = self.brain.params
46+
# belongs to TODO
47+
duplicates = fnmatch.filter(nodes, 'node*-*')
48+
for node in nodes:
49+
# TODO REMOVE condition WHEN duplicated nodes bug is fixed -- duplicated nodes end in '-[0-9]+' or '-core[0-9]+' (node2-2, node2-core1)
50+
if node not in duplicates:
51+
node_id = nodes[node].id
52+
text = node_id
53+
if node_id in params:
54+
param = params[node_id]
55+
text += '\n Oscillator {0} \n period: {1} \n phase_offset: {2} \n amplitude: {3}'.format(
56+
nodes[node].part_id, params[node_id].period, params[node_id].phase_offset, params[node_id].amplitude)
57+
self.add_node(node_id, nodes[node].type, text)
58+
59+
for connection in self.brain.connections:
60+
self.add_edge(str(connection.src), str(connection.dst), str(connection.weight))

pyrevolve/revolve_bot/render/grid.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -130,10 +130,11 @@ def move_back(self):
130130
Grid.y_pos = last_movement[1]
131131
Grid.orientation = last_movement[2]
132132

133-
def add_to_visited(self):
133+
def add_to_visited(self, include_sensors=True, is_sensor=False):
134134
"""Add current position to visited coordinates list"""
135135
self.calculate_orientation()
136-
self.visited_coordinates.append([Grid.x_pos, Grid.y_pos])
136+
if (include_sensors and is_sensor) or not is_sensor:
137+
self.visited_coordinates.append([Grid.x_pos, Grid.y_pos])
137138
Grid.movement_stack.append([Grid.x_pos, Grid.y_pos, Grid.orientation])
138139

139140
def calculate_grid_dimensions(self):

pyrevolve/revolve_bot/render/render.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,7 @@ def traverse_path_of_robot(self, module, slot, include_sensors=True):
5353
"""
5454
if isinstance(module, ActiveHingeModule) or isinstance(module, BrickModule) or isinstance(module, TouchSensorModule) or isinstance(module, BrickSensorModule):
5555
self.grid.move_by_slot(slot)
56-
if include_sensors or (isinstance(module, ActiveHingeModule) or isinstance(module, BrickModule)):
57-
self.grid.add_to_visited()
58-
56+
self.grid.add_to_visited(include_sensors, isinstance(module, TouchSensorModule))
5957
if module.has_children():
6058
# Traverse path of children of module
6159
for core_slot, child_module in module.iter_children():

pyrevolve/revolve_bot/revolve_bot.py

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
from .brain_nn import BrainNN
1717

1818
from .render.render import Render
19+
from .render.brain_graph import BrainGraph
1920
from .measure import Measure
2021

2122
import xml.etree.ElementTree
@@ -118,8 +119,7 @@ def load_yaml(self, text):
118119
brain_type = yaml_brain['type']
119120

120121
if brain_type == 'neural-network':
121-
self._brain = BrainNN()
122-
self._brain.FromYaml(yaml_brain)
122+
self._brain = BrainNN.FromYaml(yaml_brain)
123123

124124
else:
125125
self._brain = None
@@ -252,6 +252,21 @@ def _update_substrate(self,
252252
new_direction,
253253
substrate_coordinates_map)
254254

255+
def render_brain(self, img_path):
256+
"""
257+
Render image of brain
258+
@param img_path: path to where to store image
259+
"""
260+
if self._brain == None:
261+
raise RuntimeError('Brain not initialized')
262+
else:
263+
try:
264+
brain_graph = BrainGraph(self._brain, img_path)
265+
brain_graph.brain_to_graph()
266+
brain_graph.save_graph()
267+
except:
268+
print('Failed rendering brain')
269+
255270
def render2d(self, img_path):
256271
"""
257272
Render 2d representation of robot and store as png

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,6 @@ PyYAML>=3.11
66
protobuf>=3.0.0
77
psutil==3.4.2
88
pycairo>=1.18.0
9+
graphviz>=0.10.1
910

1011
-e git+https://github.com/ci-group/pygazebo.git@py2to3#egg=pygazebo

0 commit comments

Comments
 (0)