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