splintered-reality/py_trees_ros

Infrequent topic to blackboard

XDGFX opened this issue · 5 comments

XDGFX commented

Hi,

I'm having trouble setting up topic and variable gathering at the start of my tree, as suggested in the docs.

I'm using py_trees_ros.subscribers.ToBlackboard to save the value of a ROS topic to a blackboard variable.

mode_request_to_blackboard = py_trees_ros.subscribers.ToBlackboard(
    name="Mode request to blackboard",
    topic_name="/io/mode_request",
    topic_type=std_msgs.msg.Int16,
    blackboard_variables={"mode_request": None},
    qos_profile=mode_request_qos_profile,
)

The issue is that this topic only updates on changes (e.g. could be minutes or hours between changes). The ToBlackboard behaviour is blocking, and so does not work if no new /io/mode_request has been published since the last tick.

As suggested in a previous issue #28 I've tried adding a RunningIsSuccess decorator to allow the root sequence to continue if no new topic message is received. However, this seems to always just return without the ToBlackboard behaviour running (as the blackboard variable never updates).

mode_request_to_blackboard_running_is_success = (
    py_trees.decorators.RunningIsSuccess(
        mode_request_to_blackboard,
        name="Mode request to blackboard running is success",
    )
)

Something to note is that py-trees-tree-watcher doesn't actually show the return as success, however the sequence does move onto the next behaviour. This might just be a bug?

{-} root [*]
    -^- Mode request to blackboard running is success
        --> Mode request to blackboard
    --> doing some running [*] -- running

How can I achieve the functionality I'm after? I've tried conbinations of EventToBlackboard and CheckData but I'm fairly new to py_trees and haven't had any luck.

XDGFX commented

Alternatively, is it more ROS-like to edit the node publishing /io/mode_request and get it to continually publish, regardless of whether it changes?

So, getting that you'd like a non-blocking version of ToBlackboard. Odd that the decorator is giving you trouble - it should still run the behaviour underneath. I'll dig into that.

Could use a non-blocking version of ToBlackboard in this library. However, our usual practice is to front-load these behaviours under a parallel at the start of the tree. Collect all the ROS information on the blackboard and then the rest of your tree just checks blackboard variables (py_trees.behaviours.CheckBlackboardVariableValue, py_trees.behaviours.CheckBlackboardVariableExists, py_trees.behaviours.WaitForBlackboardVariable. Makes debugging easier AND lets you use the results of a ToBlackboard in multiple places across a tree.

See also: https://py-trees-ros-tutorials.readthedocs.io/en/stable/tutorials.html#module-py_trees_ros_tutorials.one_data_gathering


Alternatively, is it more ROS-like to edit the node publishing /io/mode_request and get it to continually publish, regardless of whether it changes?

I wouldn't change your system to make your tree work as desired. The rest of your system probably has it's own design requirements and redundantly publishing a stream might be consuming precious communications bandwidth.

XDGFX commented

Thanks for the help. I've got it working in the same manner as the tutorial.

The thing that swayed me from that initially is that topic gathering should happen right at the start. However 'parallel' indicates it might be happening at the same time as the other branch of the tree. This might cause issues if e.g. gathering data has a small delay for some reason, and so another branch of the tree could execute before the blackboard has been properly updated.

Or, it could result in the blackboard variables changing during a single tick.

Is this a real concern? Or maybe at a lower level they do still execute sequentially?

Aha, I can understand the confusion. Sometimes you work with something so long, you filter out the confusing elements and that surprises others.

The 'Parallel' method for doing this works, not because it is parallel, but because of the underlying implementation Obviously not ideal - the implementation could change, e.g. if it became a multi-threaded implementation (I have no plan on making this a multi-threaded library). The underlying implementation goes from left-right and the paralellism is not thread-style, but rather a promise that over the course of an entire tree tick, it will tick every subtree under that parallel. The implementation goes from left to right as a single-threaded sequence of activities, so you're always guaranteed that anything happening on the left, will be available to the right.

Now that we're getting sequences without memory, I might be able to implement this data gathering pattern right in a way that doesn't pragmatically rely on the underlying implementation. Once I do a final iteration, I'll look at the tutorial and let you know back here. In the meantime, feel free to abuse the parallel :)

XDGFX commented

Ah ok I understand! :) I don't think I helped by trying to equate the composites to logic blocks... Selector is OR, Sequence is AND, ... Parallel is AND... It's probably not worth trying to compare them ;)

Sure, sounds good to me! Thanks!