Skip to content

Commit aa1dc09

Browse files
committed
Merge branch 'advanced-smach'
2 parents b620831 + 3dbb3f8 commit aa1dc09

File tree

1 file changed

+128
-0
lines changed

1 file changed

+128
-0
lines changed

advanced/scripts/pytrees.py

+128
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
"""
2+
MBF BT Demo: Behavior tree implementing a really basic navigation strategy,
3+
even simpler than the move_base hardcoded FSM, as it lacks:
4+
5+
* continuous replanning
6+
* oscillation detection
7+
8+
We create on the first place action client behaviors for MBF's planner, controller and recovery action servers
9+
On this simple demo we need to add pretty little additional code to the base ActionClient class
10+
"""
11+
12+
##############################################################################
13+
# Imports
14+
##############################################################################
15+
16+
import functools
17+
import py_trees
18+
import py_trees_ros
19+
import py_trees.console as console
20+
import rospy
21+
import sys
22+
23+
import geometry_msgs.msg as geometry_msgs
24+
import mbf_msgs.msg as mbf_msgs
25+
26+
27+
##############################################################################
28+
# Actions
29+
##############################################################################
30+
31+
class GetPath(py_trees_ros.actions.ActionClient):
32+
33+
def initialise(self):
34+
"""
35+
Get target pose from the blackboard to create an action goal
36+
"""
37+
self.action_goal = mbf_msgs.GetPathGoal(target_pose=py_trees.blackboard.Blackboard().get("target_pose"))
38+
super(GetPath, self).initialise()
39+
40+
def update(self):
41+
"""
42+
On success, set the resulting path on the blackboard, so ExePath can use it
43+
"""
44+
status = super(GetPath, self).update()
45+
if status == py_trees.Status.SUCCESS:
46+
py_trees.blackboard.Blackboard().set("path", self.action_client.get_result().path)
47+
return status
48+
49+
class ExePath(py_trees_ros.actions.ActionClient):
50+
51+
def initialise(self):
52+
"""
53+
Get path from the blackboard to create an action goal
54+
"""
55+
self.action_goal = mbf_msgs.ExePathGoal(path=py_trees.blackboard.Blackboard().get("path"))
56+
super(ExePath, self).initialise()
57+
58+
class Recovery(py_trees_ros.actions.ActionClient):
59+
def setup(self, timeout):
60+
"""
61+
Read the list of available recovery behaviors so we can try them in sequence
62+
"""
63+
self._behaviors = rospy.get_param("/move_base_flex/recovery_behaviors")
64+
return super(Recovery, self).setup(timeout)
65+
66+
def update(self):
67+
"""
68+
Try the next recovery behavior, dropping it from the list
69+
"""
70+
try:
71+
self.action_goal = mbf_msgs.RecoveryGoal(behavior=self._behaviors.pop(0)["name"])
72+
return super(Recovery, self).update()
73+
except IndexError:
74+
# recovery behaviors exhausted; fail to abort navigation but restore the list for the next goal
75+
# TODO: this means that we won't reset the list after a successful recovery, so the list keeps shrinking
76+
# until fully exhausted; that's clearly not the expected operation, so I need to find a better solution
77+
self._behaviors = rospy.get_param("/move_base_flex/recovery_behaviors")
78+
return py_trees.Status.FAILURE
79+
80+
81+
##############################################################################
82+
# Behaviours
83+
##############################################################################
84+
85+
def create_root():
86+
# Create all behaviours
87+
bt_root = py_trees.composites.Sequence("MBF BT Demo")
88+
get_goal = py_trees.composites.Selector("GetGoal")
89+
fallback = py_trees.composites.Selector("Fallback")
90+
navigate = py_trees.composites.Sequence("Navigate")
91+
new_goal = py_trees_ros.subscribers.ToBlackboard(name="NewGoal",
92+
topic_name="/move_base_simple/goal",
93+
topic_type=geometry_msgs.PoseStamped,
94+
blackboard_variables = {'target_pose': None})
95+
have_goal = py_trees.blackboard.CheckBlackboardVariable(name="HaveGoal", variable_name="target_pose")
96+
clr_goal1 = py_trees.blackboard.ClearBlackboardVariable(name="ClearGoal", variable_name="target_pose")
97+
clr_goal2 = py_trees.blackboard.ClearBlackboardVariable(name="ClearGoal", variable_name="target_pose")
98+
get_path = GetPath(name="GetPath",
99+
action_namespace="/move_base_flex/get_path",
100+
action_spec=mbf_msgs.GetPathAction)
101+
exe_path = ExePath(name="ExePath",
102+
action_namespace="/move_base_flex/exe_path",
103+
action_spec=mbf_msgs.ExePathAction)
104+
recovery = Recovery(name="Recovery",
105+
action_namespace="/move_base_flex/recovery",
106+
action_spec=mbf_msgs.RecoveryAction)
107+
108+
# Compose tree
109+
bt_root.add_children([get_goal, fallback])
110+
get_goal.add_children([have_goal, new_goal])
111+
navigate.add_children([get_path, exe_path, clr_goal1])
112+
fallback.add_children([navigate, recovery, clr_goal2])
113+
return bt_root
114+
115+
116+
def shutdown(behaviour_tree):
117+
behaviour_tree.interrupt()
118+
119+
if __name__ == '__main__':
120+
rospy.init_node("mbf_bt_demo")
121+
root = create_root()
122+
behaviour_tree = py_trees_ros.trees.BehaviourTree(root)
123+
rospy.on_shutdown(functools.partial(shutdown, behaviour_tree))
124+
if not behaviour_tree.setup(timeout=15):
125+
console.logerror("failed to setup the tree, aborting.")
126+
sys.exit(1)
127+
128+
behaviour_tree.tick_tock(500)

0 commit comments

Comments
 (0)