-
Notifications
You must be signed in to change notification settings - Fork 4
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
Add BlackboardBehavior and initial AcquireFood tree #102
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, comments should be addressed, but nothing that requires a re-review.
Re. your idea of writing goals in local namespace as opposed to the tree root namespace, I think the only scenario in which that matters is if we use a tree within another tree, and I'm not sure how we would percolate the goal down into every subtree's blackboard. We should discuss more asynch (e.g., on a GitHub issue). For Feedback and Result, I agree that we can create a visitor for those.
|
||
class BlackboardBehavior(py_trees.behaviour.Behaviour): | ||
""" | ||
TODO |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Flagging
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add to the class docstring the following:
self.remap
,self.inputs
, andself.outputs
should not be overridden. I was hoping name mangling could allow us to prevent them from being overridden, but unfortunately that won't work (see example).- blackboard values should only be accessed via
blackboard_exists
,blackboard_get
, andblackboard_write
, not by accessingself.blackboard
directly.
|
||
def blackboard_inputs(self, **kwargs) -> None: | ||
""" | ||
Define and register all blackboard input keys. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add an English explanation of what this does, in addition to the pseudocode explanation before? e.g.,
Each key of the kwargs is a str specifying the location on the blackboard for that variable to be stored. Each value of kwargs is either `BlackboardKey`, in which case it specifies a blackboard location to remap the key to, or another type, in which case it specifies a constant value to store at that key. Note that as opposed to setting constants on the blackboard, this behavior stores it in a local dict.
|
||
return self.blackboard.get(self.remap[key]) | ||
|
||
def blackboard_write(self, key: str, output: Any) -> None: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why blackboard_write
instead of blackboard_set
(which would seem to match better with blackboard_get
)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or blackboard_read
instead of blackboard_get
. Basically I think we should either use read/write or get/set, not both.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Switch to blackboard_set
, using get/set to match the underlying API
|
||
def blackboard_exists(self, key: str) -> bool: | ||
""" | ||
Check if a key is set in the blackboard or available locally. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add to blackboard_exists
, blackboard_get
, blackboard_write
docstrings: Raises KeyError if the key has not been defined in blackboard_inputs/outputs
<key>: BlackboardKey = <default> | ||
|
||
Call from the subclass using: | ||
super().blackboard_inputs(**{key: value for key, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
outputs, not inputs
""" | ||
TODO | ||
Computes the food reference frame. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please define the food reference frame here, or point to the file where it is defined (I believe in one of the message definitions).
raise KeyError(f"{key} is not a registered output: {self.outputs.keys()}") | ||
|
||
if self.outputs[key] is not None: | ||
self.blackboard.set(self.outputs[key], output) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should log an error/warning if blackboard_write
is called when the default value was None. A programmer may have just forgotten to change the default output value, and a warning can help surface that issue.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's expected behavior, so I'll instead write it to debug()
request.food_context = mask | ||
self.blackboard_write("action_select_request", request) | ||
|
||
return py_trees.common.Status.SUCCESS |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In practice, once we have the transform how long does it take to do the computations? Because the tree is blocked until the computations are done. If it takes a while, we should instead make it an asynch function that does the computation, and return RUNNING until that asynch function succeeds.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Timed: 0.0005796703s (i.e. 580us)
I think we're fine
@@ -35,6 +54,42 @@ | |||
MOVE_TO_NAMESPACE_PREFIX = "move_to" | |||
|
|||
|
|||
def quat_between_vectors(vec_from: Vector3, vec_to: Vector3) -> Quaternion: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Trusting that this is correct, I didn't check the math on this. In the future we can consider writing a unit test for this to check it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added issue #110
@@ -73,9 +55,69 @@ def create_move_to_tree( | |||
------- | |||
tree: The behavior tree that acquires food | |||
""" | |||
# TODO: remove name and tree_root_name | |||
# We have access to them implicitly via self.blackboard / self.blackboard_tree_root |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point, feel free to create an issue for this.
Currently, self.blackboard.namespace
and self.blackboard_tree_root.namespace
are the ones that have the unchanged name. We should consider whether it should instead be .name
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Made issue #111
We can discuss offline, though I want to flag that we shouldn't conceive of the input of the tree being the action goal specifically. Instead, we can consider a tree to have a set of inputs, like a function, that are implemented as expected variables written to the local blackboard prior to tree execution. For the root tree, For sub-trees, it is up to the parent tree to write to those variables (by having the keys be the outputs of other nodes or written directly). |
Description
behaviors.BlackboardBehavior
, which exposes a concise way to map blackboard keys to local strings.AcquireFood
tree to theAcquireFood
action serverComputeFoodFrame
which, given the goal, outputs the food reference frame. It also (optionally) publishes this frame to the TF tree for debugging purposes.ComputeFoodFrame
to theAcquireFoodTree
send_goal
to write specific goal arguments to the local blackboard.Proposal:
In general, I think goal and result variables should be read from / written to the current tree blackboard namespace. This better enables subtree inclusion, where previous nodes explicitly write subtree inputs to that subtree's local blackboard and read subtree outputs from that subtree's local blackboard namespace.
For right now, I think it's okay for feedback to utilize the tree root blackboard. However, I think it would be better going forward for us to attach a FeedbackVisitor to construct it every tick instead. Feedback in theory should ideally only be dependent on:
Note that, for subtrees,
get_feedback
will never be called, soget_feedback
,get_result
,send_goal
should be free to assume that they are running on the main tree and act accordingly.The upshot of all of this is that
tree_root_name
becomes deprecated, and all trees can treat their ownself.blackboard
as the root scope.Testing procedure
ros2 run ada_feeding dummy_ft_sensor.py
ros2 launch ada_feeding ada_feeding_launch.xml use_estop:=false
ros2 launch ada_moveit demo.launch.py sim:=mock
ros2 launch feeding_web_app_ros2_test feeding_dummy_acquirefood_launch.py
ros2 launch feeding_web_app_ros2_test feeding_dummy_acquirefood_launch.py request:=above_plate_2_carrot_request.pkl
Before opening a pull request
python3 -m black .
ada_feeding
directory, run:pylint --recursive=y --rcfile=.pylintrc .
.Before Merging
Squash & Merge