Skip to content

Commit 14b0a37

Browse files
committed
BehaviourTree.CPP BT Tutorial extendend
1 parent 344a6b3 commit 14b0a37

File tree

7 files changed

+224
-33
lines changed

7 files changed

+224
-33
lines changed

advanced/CMakeLists.txt

+4-4
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,10 @@ catkin_install_python(
2121
DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
2222
)
2323

24-
add_executable(mbf_cpp_client_node src/mbf_cpp_client_node.cpp)
25-
target_include_directories(mbf_cpp_client_node PRIVATE include ${catkin_INCLUDE_DIRS})
26-
target_link_libraries(mbf_cpp_client_node ${catkin_LIBRARIES})
27-
target_compile_definitions(mbf_cpp_client_node
24+
add_executable(mbf_client_node src/mbf_client_node.cpp)
25+
target_include_directories(mbf_client_node PRIVATE include ${catkin_INCLUDE_DIRS})
26+
target_link_libraries(mbf_client_node ${catkin_LIBRARIES})
27+
target_compile_definitions(mbf_client_node
2828
PRIVATE
2929
POSE_PATH="${PROJECT_SOURCE_DIR}/param/poses.txt"
3030
)

advanced/include/mbf_advanced/mbf_cpp_client.h renamed to advanced/include/mbf_advanced/mbf_circle_client.h

+27-11
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,22 @@
1010
namespace mbf_advanced
1111
{
1212

13-
struct MBFClient
13+
enum MBFCircleClientState
1414
{
15-
explicit MBFClient(std::vector<mbf_msgs::MoveBaseGoal> pose_goals)
15+
AT_END = 0,
16+
MOVING,
17+
FAILED
18+
};
19+
20+
struct MBFCircleClient
21+
{
22+
explicit MBFCircleClient(std::vector<mbf_msgs::MoveBaseGoal> pose_goals)
1623
: pose_goals_(std::move(pose_goals))
1724
, it_(pose_goals_.begin())
1825
, prev_move_(pose_goals_.begin())
1926
, home_(pose_goals_.back())
2027
, ac_("move_base_flex/move_base", true)
28+
, terminate_(false)
2129
{
2230
ac_.waitForServer();
2331
ROS_INFO("Connected to MBF action server");
@@ -49,36 +57,43 @@ struct MBFClient
4957
return true;
5058
}
5159

52-
bool next_move()
60+
MBFCircleClientState next_move()
5361
{
5462
if (at_end())
5563
{
56-
throw std::runtime_error("Reached end! No more next poses. Exiting BT.");
64+
ROS_INFO_STREAM("Reached end of circle");
65+
terminate_ = true;
66+
return MBFCircleClientState::AT_END;
5767
}
5868

69+
prev_move_ = it_-1;
5970
auto result = log_move(*it_);
60-
prev_move_ = it_;
6171
++it_;
62-
return result;
72+
return result ? MBFCircleClientState::MOVING : MBFCircleClientState::FAILED;
6373
}
6474

6575
bool at_end()
6676
{
6777
return it_ == pose_goals_.end();
6878
}
6979

70-
bool prev_move()
71-
{
80+
MBFCircleClientState prev_move()
81+
{
82+
if (terminate_)
83+
{
84+
return MBFCircleClientState::AT_END;
85+
}
86+
7287
if (prev_move_ != pose_goals_.begin())
7388
{
7489
auto result = log_move(*prev_move_);
7590
it_ = prev_move_+1;
76-
prev_move_ = it_-1;
77-
return result;
91+
--prev_move_;
92+
return result ? MBFCircleClientState::MOVING : MBFCircleClientState::FAILED;
7893
}
7994
else
8095
{
81-
return driveHome();
96+
return driveHome() ? MBFCircleClientState::MOVING : MBFCircleClientState::FAILED;
8297
}
8398
}
8499

@@ -93,6 +108,7 @@ struct MBFClient
93108
std::vector<mbf_msgs::MoveBaseGoal>::const_iterator prev_move_;
94109
const mbf_msgs::MoveBaseGoal& home_;
95110
actionlib::SimpleActionClient<mbf_msgs::MoveBaseAction> ac_;
111+
bool terminate_;
96112
};
97113

98114
} // end

advanced/param/mbf_behavior_tree.xml

+11-7
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,17 @@
22
<BehaviorTree ID="MainTree">
33
<Sequence name="endless_circle">
44
<DriveHomeStart name="drive_home_start"/>
5-
<Repeat num_cycles="-1">
6-
<Fallback>
7-
<AttemptNext name="attempt_next"/>
8-
<AttemptPrevious name="attempt_previous"/>
9-
<DriveHomeEnd name="drive_home_end"/>
10-
</Fallback>
11-
</Repeat>
5+
<ForceSuccess>
6+
<Repeat num_cycles="10">
7+
<Fallback>
8+
<AttemptNext name="attempt_next"/>
9+
<AttemptSkip name="attempt_skip"/>
10+
<AttemptPrevious name="attempt_previous"/>
11+
<AttemptSkipPrevious name="attempt_skip_previous"/>
12+
</Fallback>
13+
</Repeat>
14+
</ForceSuccess>
15+
<DriveHomeEnd name="drive_home_end"/>
1216
</Sequence>
1317
</BehaviorTree>
1418
</root>

advanced/scripts/circle_smach.py

100644100755
File mode changed.
+132
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
import rospy
2+
import smach
3+
import smach_ros
4+
import threading
5+
6+
from geometry_msgs.msg import PoseStamped
7+
from nav_msgs.msg import Path
8+
9+
from mbf_msgs.msg import ExePathAction
10+
from mbf_msgs.msg import GetPathAction
11+
from mbf_msgs.msg import RecoveryAction
12+
13+
14+
def create_pose(x, y, z, xx, yy, zz, ww):
15+
pose = PoseStamped()
16+
pose.header.frame_id = "map"
17+
pose.header.stamp = rospy.Time.now()
18+
pose.pose.position.x = x
19+
pose.pose.position.y = y
20+
pose.pose.position.z = z
21+
pose.pose.orientation.x = xx
22+
pose.pose.orientation.y = yy
23+
pose.pose.orientation.z = zz
24+
pose.pose.orientation.w = ww
25+
return pose
26+
27+
28+
def iterate_target_poses():
29+
target_poses = [
30+
create_pose(-1.75, 0.74, 0, 0, 0, 0.539, 0.843),
31+
create_pose(-0.36, 1.92, 0, 0, 0, -0.020, 0.999),
32+
create_pose(0.957, 1.60, 0, 0, 0, -0.163, 0.987),
33+
create_pose(1.8741, 0.3830, 0, 0, 0, -0.70, 0.711),
34+
create_pose(1.752, -0.928, 0, 0, 0, -0.856, 0.517),
35+
create_pose(0.418, -2.116, 0, 0, 0, 0.998, 0.0619),
36+
create_pose(-0.775, -1.80, 0, 0, 0, 0.954, 0.300),
37+
create_pose(-1.990, -0.508, 0, 0, 0, -0.112, 0.999)
38+
]
39+
40+
for target_pose in target_poses:
41+
yield target_pose
42+
43+
def create_path_goal(path, tolerance_from_action, dist_tolerance, angle_tolerance):
44+
goal = mbf_msgs.ExePathGoal()
45+
goal.path = path
46+
goal.tolerance_from_action = tolerance_from_action
47+
goal.dist_tolerance = dist_tolerance
48+
goal.angle_tolerance = angle_tolerance
49+
return goal
50+
51+
def main():
52+
rospy.init_node('mbf_state_machine')
53+
54+
target_poses = iterate_target_poses()
55+
56+
# Create SMACH state machine
57+
sm = smach.StateMachine(outcomes=['succeeded', 'aborted', 'preempted'])
58+
59+
# Define userdata
60+
sm.userdata.target_pose = None
61+
sm.userdata.path = None
62+
sm.userdata.error = None
63+
sm.userdata.clear_costmap_flag = False
64+
sm.userdata.error_status = None
65+
66+
with sm:
67+
# path callback
68+
def get_path_callback(userdata, goal):
69+
try:
70+
goal.target_pose = next(target_poses)
71+
except StopIteration:
72+
rospy.logwarn("Reached last target pose")
73+
rospy.signal_shutdown("Last goal reached. Shutting down")
74+
75+
# Get path
76+
smach.StateMachine.add(
77+
'GET_PATH',
78+
smach_ros.SimpleActionState(
79+
'/move_base_flex/get_path',
80+
GetPathAction,
81+
goal_cb=get_path_callback,
82+
goal_slots=['target_pose'],
83+
result_slots=['path']
84+
),
85+
transitions={
86+
'succeeded': 'EXE_PATH',
87+
'aborted': 'FAILED',
88+
'preempted': 'preempted'
89+
}
90+
)
91+
92+
def path_callback(userdata, goal):
93+
target_pose = goal.path.poses[-1].pose
94+
rospy.loginfo("Attempting to reach (%1.3f, %1.3f)", target_pose.position.x, target_pose.position.y)
95+
96+
# Execute path
97+
smach.StateMachine.add(
98+
'EXE_PATH',
99+
smach_ros.SimpleActionState(
100+
'/move_base_flex/exe_path',
101+
ExePathAction,
102+
goal_cb=path_callback,
103+
goal_slots=['path']
104+
),
105+
transitions={
106+
'succeeded': 'GET_PATH',
107+
'aborted': 'FAILED',
108+
'preempted': 'preempted'
109+
}
110+
)
111+
112+
smach.StateMachine.add(
113+
'FAILED',
114+
115+
)
116+
117+
# Execute SMACH plan
118+
# Create a thread to execute the smach container
119+
smach_thread = threading.Thread(target=sm.execute)
120+
smach_thread.start()
121+
122+
# Wait for ctrl-c
123+
rospy.spin()
124+
125+
# Request the container to preempt
126+
sm.request_preempt()
127+
128+
# Block until everything is preempted
129+
smach_thread.join()
130+
131+
if __name__=="__main__":
132+
main()

advanced/src/mbf_behavior_tree.cpp

+48-9
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
#include <fstream>
22
#include <ros/ros.h>
33
#include <behaviortree_cpp_v3/bt_factory.h>
4-
#include <mbf_advanced/mbf_cpp_client.h>
4+
#include <mbf_advanced/mbf_circle_client.h>
55

6+
using State = mbf_advanced::MBFCircleClientState;
67

7-
BT::NodeStatus DriveHome(std::shared_ptr<mbf_advanced::MBFClient>& mbfclient)
8+
BT::NodeStatus DriveHome(std::shared_ptr<mbf_advanced::MBFCircleClient>& mbfclient)
89
{
910
ROS_INFO_STREAM("BT: driving home");
1011
return mbfclient->driveHome() ? BT::NodeStatus::SUCCESS : BT::NodeStatus::FAILURE;
@@ -18,7 +19,7 @@ class AttemptNext : public BT::SyncActionNode
1819
, mbfclient_{}
1920
{ }
2021

21-
void attachMBFClient(std::shared_ptr<mbf_advanced::MBFClient> mbfclient)
22+
void attachMBFClient(std::shared_ptr<mbf_advanced::MBFCircleClient> mbfclient)
2223
{
2324
mbfclient_ = mbfclient;
2425
}
@@ -28,14 +29,45 @@ class AttemptNext : public BT::SyncActionNode
2829
if (mbfclient_)
2930
{
3031
ROS_INFO_STREAM("BT: " << this->name());
31-
return mbfclient_->next_move() ? BT::NodeStatus::SUCCESS : BT::NodeStatus::FAILURE;
32+
33+
while (mbfclient_->next_move() == State::MOVING) {}
34+
35+
return BT::NodeStatus::FAILURE;
36+
}
37+
38+
return BT::NodeStatus::FAILURE;
39+
}
40+
41+
private:
42+
std::shared_ptr<mbf_advanced::MBFCircleClient> mbfclient_;
43+
};
44+
45+
class AttemptSkip : public BT::SyncActionNode
46+
{
47+
public:
48+
explicit AttemptSkip(const std::string& name)
49+
: BT::SyncActionNode(name, {})
50+
, mbfclient_{}
51+
{ }
52+
53+
void attachMBFClient(std::shared_ptr<mbf_advanced::MBFCircleClient> mbfclient)
54+
{
55+
mbfclient_ = mbfclient;
56+
}
57+
58+
BT::NodeStatus tick() override
59+
{
60+
if (mbfclient_)
61+
{
62+
ROS_INFO_STREAM("BT: " << this->name());
63+
return (mbfclient_->next_move() == State::MOVING) ? BT::NodeStatus::SUCCESS : BT::NodeStatus::FAILURE;
3264
}
3365

3466
return BT::NodeStatus::FAILURE;
3567
}
3668

3769
private:
38-
std::shared_ptr<mbf_advanced::MBFClient> mbfclient_;
70+
std::shared_ptr<mbf_advanced::MBFCircleClient> mbfclient_;
3971
};
4072

4173
class AttemptPrevious : public BT::SyncActionNode
@@ -46,7 +78,7 @@ class AttemptPrevious : public BT::SyncActionNode
4678
, mbfclient_{}
4779
{ }
4880

49-
void attachMBFClient(std::shared_ptr<mbf_advanced::MBFClient> mbfclient)
81+
void attachMBFClient(std::shared_ptr<mbf_advanced::MBFCircleClient> mbfclient)
5082
{
5183
mbfclient_ = mbfclient;
5284
}
@@ -56,27 +88,29 @@ class AttemptPrevious : public BT::SyncActionNode
5688
if (mbfclient_)
5789
{
5890
ROS_INFO_STREAM("BT: " << this->name());
59-
return mbfclient_->prev_move() ? BT::NodeStatus::SUCCESS : BT::NodeStatus::FAILURE;
91+
return (mbfclient_->prev_move() == State::MOVING) ? BT::NodeStatus::SUCCESS : BT::NodeStatus::FAILURE;
6092
}
6193

6294
return BT::NodeStatus::FAILURE;
6395
}
6496

6597
private:
66-
std::shared_ptr<mbf_advanced::MBFClient> mbfclient_;
98+
std::shared_ptr<mbf_advanced::MBFCircleClient> mbfclient_;
6799
};
68100

69101
int main(int argc, char** argv)
70102
{
71103
ros::init(argc, argv, "behavior_tree");
72104
ros::NodeHandle n;
73105

74-
auto mbfclient = std::make_shared<mbf_advanced::MBFClient>(std::move(mbf_advanced::loadPoseGoals(POSE_PATH)));
106+
auto mbfclient = std::make_shared<mbf_advanced::MBFCircleClient>(std::move(mbf_advanced::loadPoseGoals(POSE_PATH)));
75107

76108
BT::BehaviorTreeFactory factory;
77109
factory.registerSimpleCondition("DriveHomeStart", std::bind(DriveHome, std::ref(mbfclient)));
78110
factory.registerNodeType<AttemptNext>("AttemptNext");
111+
factory.registerNodeType<AttemptSkip>("AttemptSkip");
79112
factory.registerNodeType<AttemptPrevious>("AttemptPrevious");
113+
factory.registerNodeType<AttemptPrevious>("AttemptSkipPrevious");
80114
factory.registerSimpleCondition("DriveHomeEnd", std::bind(DriveHome, std::ref(mbfclient)));
81115

82116
auto tree = factory.createTreeFromFile(BT_XML_PATH);
@@ -88,6 +122,11 @@ int main(int argc, char** argv)
88122
attempt_next->attachMBFClient(mbfclient);
89123
}
90124

125+
if( auto attempt_skip = dynamic_cast<AttemptSkip*>( node.get() ))
126+
{
127+
attempt_skip->attachMBFClient(mbfclient);
128+
}
129+
91130
if( auto attempt_prev = dynamic_cast<AttemptPrevious*>( node.get() ))
92131
{
93132
attempt_prev->attachMBFClient(mbfclient);

0 commit comments

Comments
 (0)