Week 4 - Ports and new approach
Summary
In the previous month, I achieved a working ROS 2 package with almost all the nodes in BT.cpp supported. It still lacked support for a key feature in BT.cpp: the usage of ports and blackboard entries.
Development
Remaining nodes
This week, I added support for both the remaining nodes. I accepted a slight change in the API of RunOnce as explained in the previous post, but Delay was implemented by hand to have the exact same functionality.
Ports support
Now, I focused my efforts on implementing port support. This required some experimentation and design decisions, as BT.cpp has two types of ports:
<Forward name="move_forward" speed="0.2" obs_port="{n_obs}"/>
As you can see in this example, the library supports both ports with a given fixed value (always an input), and references to a blackboard entry. To adapt the concept of ports to the blackboard of py_trees
, I wrote some auxiliary functions:
def get_port_content(port_value):
if port_value.startswith("{") and port_value.endswith("}"):
bb_key = port_value.strip("{}")
blackboard = GlobalBlackboard.get_instance()
# Return the value of the blackboard entry if it exists, otherwise None
return getattr(blackboard, bb_key, "")
else:
return port_value
def set_port_content(port_value, value):
if not (port_value.startswith("{") and port_value.endswith("}")):
raise ValueError(f"'{port_value}' is a read-only port. Only ports connected to the blackboard are writable")
# Extract the key from the port_value
key = port_value.strip("{}")
blackboard = GlobalBlackboard.get_instance()
# Lazy creation: if key doesn't exist, register it
if not hasattr(blackboard, key):
blackboard.register_key(key=key, access=py_trees.common.Access.WRITE, required=True)
# Set the value for the key in the blackboard
setattr(blackboard, key, value)
This works because when the different actions are instantiated, they are passed a dictionary with a name-value list of all the ports. For that reason, these functions can be used from the actions in the following way:
tree_tools.set_port_content(self.ports["obs_port"], self.n_obs)
Where “obs_port” is just the name of the port, independently of the subjacent value. As this is a Python implementation, I’ve decided that all ports which reference a blackboard entry, can be used as input and output ports. This provides a simpler approach than BT.cpp