Go to the documentation of this file.00001 #include "behaviortree_cpp/xml_parsing.h"
00002 #include "behaviortree_cpp/blackboard/blackboard_local.h"
00003 #include <gtest/gtest.h>
00004
00005 using namespace BT;
00006
00007
00008 const std::string xml_text = R"(
00009
00010 <root main_tree_to_execute="BehaviorTree">
00011 <BehaviorTree ID="BehaviorTree">
00012 <FallbackStar name="root">
00013 <Sequence name="navigation_subtree">
00014 <Inverter>
00015 <Condition ID="IsStuck"/>
00016 </Inverter>
00017 <SequenceStar name="navigate">
00018 <Action ID="ComputePathToPose"/>
00019 <Action ID="FollowPath"/>
00020 </SequenceStar>
00021 </Sequence>
00022 <SequenceStar name="stuck_recovery">
00023 <Condition ID="IsStuck"/>
00024 <Action ID="BackUpAndSpin"/>
00025 </SequenceStar>
00026 </FallbackStar>
00027 </BehaviorTree>
00028 </root>
00029 )";
00030
00031
00032
00033 using Milliseconds = std::chrono::milliseconds;
00034
00035 inline std::chrono::high_resolution_clock::time_point Now()
00036 {
00037 return std::chrono::high_resolution_clock::now();
00038 }
00039
00040
00041
00042
00043
00044 class TestNode
00045 {
00046 public:
00047 TestNode(const std::string& name):
00048 _expected_result(true),
00049 _tick_count(0),
00050 _name(name)
00051 { }
00052
00053 void setExpectedResult(bool will_succeed)
00054 {
00055 _expected_result = will_succeed;
00056 }
00057 NodeStatus expectedResult() const
00058 {
00059 return _expected_result ? NodeStatus::SUCCESS : NodeStatus::FAILURE;
00060 }
00061 void resetTickCount() { _tick_count = 0; }
00062 int tickCount() const { return _tick_count;}
00063
00064 NodeStatus tickImpl()
00065 {
00066 std::cout << _name << ": "<< (_expected_result? "true" : "false") << std::endl;
00067 _tick_count++;
00068 return expectedResult();
00069 }
00070
00071 private:
00072 bool _expected_result;
00073 int _tick_count;
00074 std::string _name;
00075 };
00076
00077 class IsStuck: public ConditionNode, public TestNode
00078 {
00079 public:
00080 IsStuck(const std::string& name): ConditionNode(name), TestNode(name) {}
00081
00082 NodeStatus tick() override
00083 {
00084 return tickImpl();
00085 }
00086 };
00087
00088 class BackUpAndSpin: public SyncActionNode, public TestNode
00089 {
00090 public:
00091 BackUpAndSpin(const std::string& name): SyncActionNode(name), TestNode(name){}
00092
00093 NodeStatus tick() override
00094 {
00095 return tickImpl();
00096 }
00097 };
00098
00099 class ComputePathToPose: public SyncActionNode, public TestNode
00100 {
00101 public:
00102 ComputePathToPose(const std::string& name): SyncActionNode(name), TestNode(name){}
00103
00104 NodeStatus tick() override
00105 {
00106 return tickImpl();
00107 }
00108 };
00109
00110 class FollowPath: public CoroActionNode, public TestNode
00111 {
00112 public:
00113 FollowPath(const std::string& name): CoroActionNode(name), TestNode(name), _halted(false){}
00114
00115 NodeStatus tick() override
00116 {
00117 _halted = false;
00118 std::cout << "FollowPath::started" << std::endl;
00119 auto initial_time = Now();
00120
00121
00122 while( Now() < initial_time + Milliseconds(600) )
00123 {
00124 setStatusRunningAndYield();
00125
00126 }
00127 return tickImpl();
00128 }
00129 void halt() override {
00130 std::cout << "FollowPath::halt" << std::endl;
00131 _halted = true;
00132 CoroActionNode::halt();
00133 }
00134
00135 bool wasHalted() const { return _halted; }
00136
00137 private:
00138 bool _halted;
00139 };
00140
00141
00142
00143 template <typename Original, typename Casted>
00144 void TryDynamicCastPtr(Original* ptr, Casted*& destination)
00145 {
00146 if( dynamic_cast<Casted*>(ptr) )
00147 {
00148 destination = dynamic_cast<Casted*>(ptr);
00149 }
00150 }
00151
00152
00153
00154 TEST(Navigationtest, MoveBaseRecovery)
00155 {
00156 BehaviorTreeFactory factory;
00157
00158 factory.registerNodeType<IsStuck>("IsStuck");
00159 factory.registerNodeType<BackUpAndSpin>("BackUpAndSpin");
00160 factory.registerNodeType<ComputePathToPose>("ComputePathToPose");
00161 factory.registerNodeType<FollowPath>("FollowPath");
00162
00163 auto tree = buildTreeFromText(factory, xml_text);
00164
00165
00166
00167 IsStuck *first_stuck_node = nullptr;
00168 IsStuck *second_stuck_node = nullptr;
00169 BackUpAndSpin* back_spin_node = nullptr;
00170 ComputePathToPose* compute_node = nullptr;
00171 FollowPath* follow_node = nullptr;
00172
00173 for (auto& node: tree.nodes)
00174 {
00175 auto ptr = node.get();
00176
00177 if( !first_stuck_node )
00178 {
00179 TryDynamicCastPtr(ptr, first_stuck_node);
00180 }
00181 else{
00182 TryDynamicCastPtr(ptr, second_stuck_node);
00183 }
00184 TryDynamicCastPtr(ptr, back_spin_node);
00185 TryDynamicCastPtr(ptr, follow_node);
00186 TryDynamicCastPtr(ptr, compute_node);
00187 }
00188
00189 ASSERT_TRUE( first_stuck_node );
00190 ASSERT_TRUE( second_stuck_node );
00191 ASSERT_TRUE( back_spin_node );
00192 ASSERT_TRUE( compute_node );
00193 ASSERT_TRUE( follow_node );
00194
00195 std::cout << "-----------------------" << std::endl;
00196
00197
00198 NodeStatus status = NodeStatus::IDLE;
00199
00200 first_stuck_node->setExpectedResult(false);
00201
00202 while( status == NodeStatus::IDLE || status == NodeStatus::RUNNING )
00203 {
00204 status = tree.root_node->executeTick();
00205 std::this_thread::sleep_for(Milliseconds(100));
00206 }
00207
00208
00209 ASSERT_EQ( status, NodeStatus::SUCCESS );
00210
00211 ASSERT_GE( first_stuck_node->tickCount(), 6);
00212
00213 ASSERT_EQ( second_stuck_node->tickCount(), 0 );
00214 ASSERT_EQ( back_spin_node->tickCount(), 0 );
00215
00216 ASSERT_EQ( compute_node->tickCount(), 1 );
00217 ASSERT_EQ( follow_node->tickCount(), 1 );
00218 ASSERT_FALSE( follow_node->wasHalted() );
00219
00220 std::cout << "-----------------------" << std::endl;
00221
00222
00223
00224
00225 first_stuck_node->resetTickCount();
00226 second_stuck_node->resetTickCount();
00227 compute_node->resetTickCount();
00228 follow_node->resetTickCount();
00229 back_spin_node->resetTickCount();
00230 status = NodeStatus::IDLE;
00231 int cycle = 0;
00232
00233 while( status == NodeStatus::IDLE || status == NodeStatus::RUNNING )
00234 {
00235
00236 if( ++cycle == 2 )
00237 {
00238 first_stuck_node->setExpectedResult(true);
00239 second_stuck_node->setExpectedResult(true);
00240 }
00241 status = tree.root_node->executeTick();
00242 std::this_thread::sleep_for(Milliseconds(100));
00243 }
00244
00245
00246 ASSERT_EQ( status, NodeStatus::SUCCESS );
00247
00248
00249 ASSERT_GE( first_stuck_node->tickCount(), 2);
00250
00251 ASSERT_EQ( second_stuck_node->tickCount(), 1 );
00252 ASSERT_EQ( back_spin_node->tickCount(), 1 );
00253
00254
00255 ASSERT_EQ( compute_node->tickCount(), 1 );
00256
00257 ASSERT_EQ( follow_node->tickCount(), 0 );
00258 ASSERT_TRUE( follow_node->wasHalted() );
00259
00260 ASSERT_EQ( compute_node->status(), NodeStatus::IDLE );
00261 ASSERT_EQ( follow_node->status(), NodeStatus::IDLE );
00262 ASSERT_EQ( back_spin_node->status(), NodeStatus::IDLE );
00263
00264
00265 std::cout << "-----------------------" << std::endl;
00266
00267
00268
00269
00270 first_stuck_node->resetTickCount();
00271 second_stuck_node->resetTickCount();
00272 compute_node->resetTickCount();
00273 follow_node->resetTickCount();
00274 back_spin_node->resetTickCount();
00275 status = NodeStatus::IDLE;
00276 first_stuck_node->setExpectedResult(false);
00277 second_stuck_node->setExpectedResult(false);
00278
00279 while( status == NodeStatus::IDLE || status == NodeStatus::RUNNING )
00280 {
00281 status = tree.root_node->executeTick();
00282 std::this_thread::sleep_for(Milliseconds(100));
00283 }
00284
00285
00286 ASSERT_EQ( status, NodeStatus::SUCCESS );
00287
00288 ASSERT_GE( first_stuck_node->tickCount(), 6);
00289 ASSERT_EQ( second_stuck_node->tickCount(), 0 );
00290 ASSERT_EQ( back_spin_node->tickCount(), 0 );
00291
00292 ASSERT_EQ( compute_node->status(), NodeStatus::IDLE );
00293 ASSERT_EQ( follow_node->status(), NodeStatus::IDLE );
00294 ASSERT_EQ( back_spin_node->status(), NodeStatus::IDLE );
00295 ASSERT_FALSE( follow_node->wasHalted() );
00296
00297 }
00298
00299