Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: Added TX branching measurement #106

Open
wants to merge 1 commit into
base: task_morphology
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pyrevolve/data_analisys/visualize_robot.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ def panoramic_rotation(robot_manager, robot: RevolveBot, vertical_angle_limit: f
if z < min_z:
chosen_orientation = orientation
min_z = z
print(f"Chosen orientation for robot {robot.id} is {chosen_orientation}")
#print(f"Chosen orientation for robot {robot.id} is {chosen_orientation}")

vec_list = [vecs[chosen_orientation] for vecs in robot_manager._orientation_vecs]

Expand Down
18 changes: 16 additions & 2 deletions pyrevolve/revolve_bot/measure/measure_body_3d.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ def __init__(self, body):

# Absolute branching
self.branching_modules_count = None
# Absolute T/X branching
self.tx_branching_modules_count = None
# Relative branching
self.branching = None
# Absolute number of limbs
Expand Down Expand Up @@ -70,6 +72,7 @@ def count_branching_bricks(self, module=None, init=True):
try:
if init:
self.branching_modules_count = 0
self.tx_branching_modules_count = 0
if module is None:
module = self.body

Expand All @@ -82,12 +85,22 @@ def count_branching_bricks(self, module=None, init=True):
and not isinstance(child_module, BrickSensorModule):
children_count += 1
self.count_branching_bricks(child_module, False)
if (isinstance(module, BrickModule) and children_count == 3) or \
(isinstance(module, CoreModule) and children_count == 4):
module_connections = module.count_module_connections()
if module_connections >= 3:
self.tx_branching_modules_count += 1
if module_connections == 4:
self.branching_modules_count += 1
except Exception as e:
logger.exception(f'Exception: {e}. \nFailed counting branching bricks')

def folding_no_branching(self):
pass
#TODO

def folding_or_tbranching(self):
pass
#TODO

def measure_branching(self):
"""
Measure branching by dividing fully branching by possible branching modules
Expand Down Expand Up @@ -516,6 +529,7 @@ def measurements_to_dict(self):
return {
'branching': self.branching,
'branching_modules_count': self.branching_modules_count,
'tx_branching_modules_count': self.tx_branching_modules_count,
'limbs': self.limbs,
'extremities': self.extremities,
'length_of_limbs': self.length_of_limbs,
Expand Down
106 changes: 98 additions & 8 deletions pyrevolve/revolve_bot/revolve_module.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,23 @@ def grams(x):

# Module Orientation
class Orientation(Enum):
BACK = 0
BACK = 0 # This is the parent attachment point (if any)
FORWARD = 1
RIGHT = 2
LEFT = 3

def opposite(self):
if self == self.BACK:
return self.FORWARD
elif self == self.FORWARD:
return self.BACK
elif self == self.RIGHT:
return self.LEFT
elif self == self.LEFT:
return self.RIGHT
else:
assert False

def short_repr(self):
if self == self.BACK:
return 'B'
Expand Down Expand Up @@ -222,20 +234,66 @@ def possible_slots(self):
(box_geometry[2] / -2.0, box_geometry[2] / 2.0), # Z
)

def has_children(self):
def has_children(self) -> bool:
"""
Check wheter module has children
Check whether module has children
:return: True if module has children
"""
has_children = False
for i, child in self.iter_children():
if child is not None:
return True
return False

if self.children == {1: None}: return False
def count_module_connections(self) -> int:
"""
Count how many connections the module has.
Connected TouchSensor and BrickSensor Modules are ignored
:return: number of connections
"""
if not self.has_children():
return 1

for i, child in enumerate(self.children):
children_count = 0
for core_slot, child in self.iter_children():
if child is not None:
has_children = True
continue
if not isinstance(child, TouchSensorModule) and \
not isinstance(child, BrickSensorModule):
children_count += 1

return has_children
return children_count + 1

def is_folding(self) -> bool:
"""
Checks if the module is a folding point.
The module is a folding point if and only if it has one child and that child is not
attached opposite to the parent.
:return: True if the module is a folding point
"""
if not self.has_children():
return False

# Default parent slot is BACK
parent_slot = Orientation.BACK

target_slot = None
for slot, child in self.iter_children():
if slot == parent_slot:
continue
# is the child slot occupied?
if child is None:
continue
# ignore TouchSensor and BrickSensor modules
if isinstance(child, TouchSensorModule) or \
isinstance(child, BrickSensorModule):
continue
# second slot found, not a fold
if target_slot is not None:
return False
target_slot = slot

target_slot = Orientation(target_slot)
return parent_slot.opposite() != target_slot


class CoreModule(RevolveModule):
Expand Down Expand Up @@ -265,6 +323,38 @@ def to_sdf(self, tree_depth='', parent_link=None, child_link=None):
parent_link.append(imu_sensor)
return visual, collision, imu_sensor

def count_module_connections(self) -> int:
"""
Count how many connections the module has.
Connected TouchSensor and BrickSensor Modules are ignored
:return: number of connections
"""
# The CoreModule does not have a parent module
return super(CoreModule, self).count_module_connections() - 1

def is_folding(self) -> bool:
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does is_folding need to be overwritten by CoreModule?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because in the other function I assume you always have one connection already, which is the connection to the parent. In this case the assumption is broken.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there anything we can reuse?

# parent slot is not supposed to be set
parent_slot = None
target_slot = None
for slot, child in self.iter_children():
# is the child slot occupied?
if child is None:
continue
# ignore TouchSensor and BrickSensor modules
if isinstance(child, TouchSensorModule) or \
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could inherit TouchSensorModule from SensorModule (create new class) and inherit BrickSensorModule from SensorModule so you only have to do one check instead of two.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, good point. But I'm not going to touch that right now. It's out of scope and I'm afraid I'm going to break stuff

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could literaly do

class SensorModule:
pass

class TouchSensorModule(SensorModule):
....

class BrickSensorModule(SensorModule):
...

But yeah, that is up to you.

isinstance(child, BrickSensorModule):
continue
if parent_slot is None:
parent_slot = slot
elif target_slot is None:
target_slot = slot
else:
# More than 2 children, this is not a fold
return False
if parent_slot is None or target_slot is None:
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will always trigger when only the parent_slot is filled in the first iteration that a viable child is set as the parent_slot at line 348.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you mean? This if will trigger if I've found both a first and a second child, at least that was my intention.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's say we are in the first iteration of this for loop and it found a viable child and we are saving the slot of this child at line 348. Then now we come to 354 and we say that this statement is true, since the target slot is still unfilled, then we return false, I do not think this is expected behavior.

return False
return parent_slot.opposite() != target_slot
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will only trigger after executing line 350 is executed, but this does not necessarily need to be opposite right? After that it cannot be changed since you already return that it is False.
Overall very confused by this entire function.



class ActiveHingeModule(RevolveModule):
"""
Expand Down
2 changes: 2 additions & 0 deletions test_py/plasticonding/test_development.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ def test_measurements_body(self):
connectivity2_abs = 4
connectivity3 = 1
connectivity4 = 1
connectivity5 = 1
coverage = 0.44
effective_joints = 0.444
joints_abs = 6
Expand All @@ -101,6 +102,7 @@ def test_measurements_body(self):
self.assertAlmostEqual(connectivity2_abs, m.extensiveness, 3)
# self.assertAlmostEqual(connectivity3, m., 3)
self.assertAlmostEqual(connectivity4, m.branching_modules_count, 3)
self.assertAlmostEqual(connectivity5, m.tx_branching_modules_count, 3)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not assertEqual?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

because these are floating point values

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is a counter a floating point value, can you count half branching modules?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good point, this is an integer counter :/

self.assertAlmostEqual(coverage, m.coverage, 3)
self.assertAlmostEqual(effective_joints, m.joints, 3)
self.assertAlmostEqual(joints_abs, m.hinge_count, 3)
Expand Down