1 minute read

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

References

BehaviorTrees BT.cpp