splintered-reality / py_trees_ros

ROS extensions and implementations for py_trees
Other
158 stars 40 forks source link

Get blackboard variable from a Subscriber defined in a separate file #222

Open SyZbidi opened 3 months ago

SyZbidi commented 3 months ago

Hello,

I created an Image subscriber that writes the image it receives on a blackboard, with the intent to add it at the top of the tree so that every behavior that would need it has access to its blackboard.

I defined a small root tree with the image_subscriber in it at first

import sys

import py_trees
import py_trees.console as console
import py_trees_ros.trees
import rclpy

from .behaviors import ImageSubscriber

def root_create_root() -> py_trees.behaviour.Behaviour:
    """
    Create a tree for a 'Scan' work that moves the robot along waypoints and takes images.

    Returns
    -------
        the root of the tree

    """
    root = py_trees.composites.Parallel(
        name="Damage Inspection Process",
        policy=py_trees.common.ParallelPolicy.SuccessOnAll(synchronise=False),
    )
    image_subscriber = ImageSubscriber(
        name="Image Subscriber",
        topic_name="img_topic",
        qos_profile=py_trees_ros.utilities.qos_profile_unlatched(),
    )

    workpiece_info = py_trees.composites.Sequence(name="workpiece_info", memory=True)
    tb_placeholder = py_trees.timers.Timer("TB_info_placeholder", duration=1.0)

    workpiece_info.add_child(tb_placeholder)
    root.add_children([image_subscriber, workpiece_info])

    return root

def main():
    """Entry point for the root script."""
    rclpy.init(args=None)
    root = root_create_root()
    tree = py_trees_ros.trees.BehaviourTree(root=root, unicode_tree_debug=True)
    try:
        tree.setup(node_name="root_tree", timeout=15.0)
    except py_trees_ros.exceptions.TimedOutError as e:
        console.logerror(
            console.red + "failed to setup the tree, aborting [{}]".format(str(e)) + console.reset
        )
        tree.shutdown()
        rclpy.try_shutdown()
        sys.exit(1)
    except KeyboardInterrupt:
        # not a warning, nor error, usually a user-initiated shutdown
        console.logerror("tree setup interrupted")
        tree.shutdown()
        rclpy.try_shutdown()
        sys.exit(1)

    tree.tick_tock(period_ms=1000.0)

    try:
        rclpy.spin(tree.node)
    except (KeyboardInterrupt, rclpy.executors.ExternalShutdownException):
        pass
    finally:
        tree.shutdown()
        rclpy.try_shutdown()

if __name__ == "__main__":
    main()

then I import that root in my subtree and try to get the image_subscriber blackboard variable but it fails there with KeyError: '/camera_feed_image'. Here's how I'm trying to get the value of BB variable in the subtree

current_image = py_trees.blackboard.Blackboard.get(variable_name="camera_feed_image")

To be used in an action client

    get_image = py_trees_ros.actions.ActionClient(
        name="get_image",
        action_type=SaveImage,
        action_name="capture_image",
        action_goal=SaveImage.Goal(block_id=block_id, images_folder=folder, image=current_image),
    )

So I'm guessing maybe I haven't properly created the ImageSubscriber? are the keyboard variables not global in this case?

This is my ImageSubscriber behavior

import py_trees
import py_trees_ros
import rclpy
from sensor_msgs.msg import Image

class ImageSubscriber(py_trees_ros.subscribers.ToBlackboard):
    """
    Subscribe to the Image message and write an Image to the blackboard.

    Blackboard Variables:
    --------------------
        * image (:class:`sensor_msgs.msg.Image`)[w]: the raw image message

    Args:
    ----
        name: name of the behaviour
        topic_name: name of the Image topic
        qos_profile: qos profile for the subscriber

    """

    def __init__(self, name: str, topic_name: str, qos_profile: rclpy.qos.QoSProfile):
        super().__init__(
            name=name,
            topic_name=topic_name,
            topic_type=Image,
            qos_profile=qos_profile,
            blackboard_variables={"camera_feed_image": None},
            clearing_policy=py_trees.common.ClearingPolicy.NEVER,
        )

    def update(self) -> py_trees.common.Status:
        """
        Call the parent to write the Image to the blackboard.

        Returns
        -------
            py_trees.common.Status: SUCCESS if a message is written, RUNNING otherwise.

        """
        self.logger.debug("%s.update()" % self.__class__.__name__)
        status = super(ImageSubscriber, self).update()
        if status != py_trees.common.Status.RUNNING:
            self.feedback_message = "Saved Image to blackboard"
        return status

Thank you in advance for your help

slyandsmart commented 3 months ago

i have the same problem.

i do not know how to use a blackboard variables.

I have used the subscribers.ToBlackboard Class

    jointstates_to_blackboard = py_trees_ros.subscribers.ToBlackboard(
                                name="jointstates_to_blackboard",
                                topic_type=JointState,
                                topic_name="/joint_states",
                                qos_profile=py_trees_ros.utilities.qos_profile_unlatched(),
                                #blackboard_variables={'jointstate': None}
                                blackboard_variables={'JointStateName': 'name'}
                                )

And i see in the watcher that the variable is there but when i use

        myblackboardvar = py_trees.blackboard.Blackboard.get(variable_name='/JointStateName')

i also get the KeyError

slyandsmart commented 3 months ago

Here a additional picture of my usage.

image

It can be seen that the key /JointStateName is existing. By the key method i also can get it from the blackboard but with get i get a keyError