navigation_test.cpp
Go to the documentation of this file.
3 #include <gtest/gtest.h>
4 
5 using namespace BT;
6 
7 // clang-format off
8 static const char* xml_text = R"(
9 
10 <root BTCPP_format="4" main_tree_to_execute="BehaviorTree">
11  <BehaviorTree ID="BehaviorTree">
12  <Fallback name="root">
13 
14  <ReactiveSequence name="navigation_subtree">
15  <Inverter>
16  <Condition ID="IsStuck"/>
17  </Inverter>
18  <SequenceWithMemory name="navigate">
19  <Action ID="ComputePathToPose"/>
20  <Action ID="FollowPath"/>
21  </SequenceWithMemory>
22  </ReactiveSequence>
23 
24  <SequenceWithMemory name="stuck_recovery">
25  <Condition ID="IsStuck"/>
26  <Action ID="BackUpAndSpin"/>
27  </SequenceWithMemory>
28 
29  </Fallback>
30  </BehaviorTree>
31 </root>
32  )";
33 
34 // clang-format on
35 
36 using Milliseconds = std::chrono::milliseconds;
37 
38 inline std::chrono::high_resolution_clock::time_point Now()
39 {
40  return std::chrono::high_resolution_clock::now();
41 }
42 
43 //--------------------------------------------
44 
45 class TestNode
46 {
47 public:
48  TestNode(const std::string& name) : _expected_result(true), _tick_count(0), _name(name)
49  {}
50 
51  void setExpectedResult(bool will_succeed)
52  {
53  _expected_result = will_succeed;
54  }
56  {
57  return _expected_result ? NodeStatus::SUCCESS : NodeStatus::FAILURE;
58  }
60  {
61  _tick_count = 0;
62  }
63  int tickCount() const
64  {
65  return _tick_count;
66  }
67 
69  {
70  std::cout << _name << ": " << (_expected_result ? "true" : "false") << std::endl;
71  _tick_count++;
72  return expectedResult();
73  }
74 
75 private:
78  std::string _name;
79 };
80 
81 class IsStuck : public ConditionNode, public TestNode
82 {
83 public:
84  IsStuck(const std::string& name) : ConditionNode(name, {}), TestNode(name)
85  {}
86 
87  NodeStatus tick() override
88  {
89  return tickImpl();
90  }
91 };
92 
93 class BackUpAndSpin : public SyncActionNode, public TestNode
94 {
95 public:
96  BackUpAndSpin(const std::string& name) : SyncActionNode(name, {}), TestNode(name)
97  {}
98 
99  NodeStatus tick() override
100  {
101  return tickImpl();
102  }
103 };
104 
106 {
107 public:
108  ComputePathToPose(const std::string& name) : SyncActionNode(name, {}), TestNode(name)
109  {}
110 
111  NodeStatus tick() override
112  {
113  return tickImpl();
114  }
115 };
116 
117 class FollowPath : public ActionNodeBase, public TestNode
118 {
119  std::chrono::high_resolution_clock::time_point _initial_time;
120 
121 public:
122  FollowPath(const std::string& name)
123  : ActionNodeBase(name, {}), TestNode(name), _halted(false)
124  {}
125 
126  NodeStatus tick() override
127  {
128  if(status() == NodeStatus::IDLE)
129  {
130  setStatus(NodeStatus::RUNNING);
131  _halted = false;
132  std::cout << "FollowPath::started" << std::endl;
133  _initial_time = Now();
134  }
135 
136  // Yield for 1 second
137  while(Now() < _initial_time + Milliseconds(600) || _halted)
138  {
139  return NodeStatus::RUNNING;
140  }
141  if(_halted)
142  {
143  return NodeStatus::IDLE;
144  }
145  return tickImpl();
146  }
147  void halt() override
148  {
149  std::cout << "FollowPath::halt" << std::endl;
150  _halted = true;
151  }
152 
153  bool wasHalted() const
154  {
155  return _halted;
156  }
157 
158 private:
159  bool _halted;
160 };
161 
162 //-------------------------------------
163 
164 template <typename Original, typename Casted>
165 void TryDynamicCastPtr(Original* ptr, Casted*& destination)
166 {
167  if(dynamic_cast<Casted*>(ptr))
168  {
169  destination = dynamic_cast<Casted*>(ptr);
170  }
171 }
172 
173 /****************TESTS START HERE***************************/
174 
175 TEST(Navigationtest, MoveBaseRecovery)
176 {
177  BehaviorTreeFactory factory;
178 
179  factory.registerNodeType<IsStuck>("IsStuck");
180  factory.registerNodeType<BackUpAndSpin>("BackUpAndSpin");
181  factory.registerNodeType<ComputePathToPose>("ComputePathToPose");
182  factory.registerNodeType<FollowPath>("FollowPath");
183 
184  auto tree = factory.createTreeFromText(xml_text);
185 
186  // Need to retrieve the node pointers with dynamic cast
187  // In a normal application you would NEVER want to do such a thing.
188  IsStuck* first_stuck_node = nullptr;
189  IsStuck* second_stuck_node = nullptr;
190  BackUpAndSpin* back_spin_node = nullptr;
191  ComputePathToPose* compute_node = nullptr;
192  FollowPath* follow_node = nullptr;
193 
194  for(auto& subtree : tree.subtrees)
195  {
196  for(auto& node : subtree->nodes)
197  {
198  auto ptr = node.get();
199 
200  if(!first_stuck_node)
201  {
202  TryDynamicCastPtr(ptr, first_stuck_node);
203  }
204  else
205  {
206  TryDynamicCastPtr(ptr, second_stuck_node);
207  }
208  TryDynamicCastPtr(ptr, back_spin_node);
209  TryDynamicCastPtr(ptr, follow_node);
210  TryDynamicCastPtr(ptr, compute_node);
211  }
212  }
213 
214  ASSERT_TRUE(first_stuck_node);
215  ASSERT_TRUE(second_stuck_node);
216  ASSERT_TRUE(back_spin_node);
217  ASSERT_TRUE(compute_node);
218  ASSERT_TRUE(follow_node);
219 
220  std::cout << "-----------------------" << std::endl;
221 
222  // First case: not stuck, everything fine.
223  NodeStatus status = NodeStatus::IDLE;
224 
225  first_stuck_node->setExpectedResult(false);
226 
227  while(status == NodeStatus::IDLE || status == NodeStatus::RUNNING)
228  {
229  status = tree.tickWhileRunning();
230  std::this_thread::sleep_for(Milliseconds(100));
231  }
232 
233  // SUCCESS expected
234  ASSERT_EQ(status, NodeStatus::SUCCESS);
235  // IsStuck on the left branch must run several times
236  ASSERT_GE(first_stuck_node->tickCount(), 6);
237  // Never take the right branch (recovery)
238  ASSERT_EQ(second_stuck_node->tickCount(), 0);
239  ASSERT_EQ(back_spin_node->tickCount(), 0);
240 
241  ASSERT_EQ(compute_node->tickCount(), 1);
242  ASSERT_EQ(follow_node->tickCount(), 1);
243  ASSERT_FALSE(follow_node->wasHalted());
244 
245  std::cout << "-----------------------" << std::endl;
246 
247  // Second case: get stuck after a while
248 
249  // Initialize everything first
250  first_stuck_node->resetTickCount();
251  second_stuck_node->resetTickCount();
252  compute_node->resetTickCount();
253  follow_node->resetTickCount();
254  back_spin_node->resetTickCount();
255  status = NodeStatus::IDLE;
256  int cycle = 0;
257 
258  while(status == NodeStatus::IDLE || status == NodeStatus::RUNNING)
259  {
260  // At the fifth cycle get stuck
261  if(++cycle == 2)
262  {
263  first_stuck_node->setExpectedResult(true);
264  second_stuck_node->setExpectedResult(true);
265  }
266  status = tree.tickWhileRunning();
267  std::this_thread::sleep_for(Milliseconds(100));
268  }
269 
270  // SUCCESS expected
271  ASSERT_EQ(status, NodeStatus::SUCCESS);
272 
273  // First IsStuck must run several times
274  ASSERT_GE(first_stuck_node->tickCount(), 2);
275  // Second IsStuck probably only once
276  ASSERT_EQ(second_stuck_node->tickCount(), 1);
277  ASSERT_EQ(back_spin_node->tickCount(), 1);
278 
279  // compute done once and follow started but halted
280  ASSERT_EQ(compute_node->tickCount(), 1);
281 
282  ASSERT_EQ(follow_node->tickCount(), 0); // started but never completed
283  ASSERT_TRUE(follow_node->wasHalted());
284 
285  ASSERT_EQ(compute_node->status(), NodeStatus::IDLE);
286  ASSERT_EQ(follow_node->status(), NodeStatus::IDLE);
287  ASSERT_EQ(back_spin_node->status(), NodeStatus::IDLE);
288 
289  std::cout << "-----------------------" << std::endl;
290 
291  // Third case: execute again
292 
293  // Initialize everything first
294  first_stuck_node->resetTickCount();
295  second_stuck_node->resetTickCount();
296  compute_node->resetTickCount();
297  follow_node->resetTickCount();
298  back_spin_node->resetTickCount();
299  status = NodeStatus::IDLE;
300  first_stuck_node->setExpectedResult(false);
301  second_stuck_node->setExpectedResult(false);
302 
303  while(status == NodeStatus::IDLE || status == NodeStatus::RUNNING)
304  {
305  status = tree.tickWhileRunning();
306  std::this_thread::sleep_for(Milliseconds(100));
307  }
308 
309  // SUCCESS expected
310  ASSERT_EQ(status, NodeStatus::SUCCESS);
311 
312  ASSERT_GE(first_stuck_node->tickCount(), 6);
313  ASSERT_EQ(second_stuck_node->tickCount(), 0);
314  ASSERT_EQ(back_spin_node->tickCount(), 0);
315 
316  ASSERT_EQ(compute_node->status(), NodeStatus::IDLE);
317  ASSERT_EQ(follow_node->status(), NodeStatus::IDLE);
318  ASSERT_EQ(back_spin_node->status(), NodeStatus::IDLE);
319  ASSERT_FALSE(follow_node->wasHalted());
320 }
BT
Definition: ex01_wrap_legacy.cpp:29
IsStuck::tick
NodeStatus tick() override
Method to be implemented by the user.
Definition: navigation_test.cpp:87
FollowPath::_halted
bool _halted
Definition: navigation_test.cpp:159
BT::ConditionNode
Definition: condition_node.h:21
TestNode::resetTickCount
void resetTickCount()
Definition: navigation_test.cpp:59
FollowPath::_initial_time
std::chrono::high_resolution_clock::time_point _initial_time
Definition: navigation_test.cpp:119
FollowPath::FollowPath
FollowPath(const std::string &name)
Definition: navigation_test.cpp:122
FollowPath::halt
void halt() override
Definition: navigation_test.cpp:147
TestNode::tickImpl
NodeStatus tickImpl()
Definition: navigation_test.cpp:68
ComputePathToPose
Definition: navigation_test.cpp:105
TestNode::expectedResult
NodeStatus expectedResult() const
Definition: navigation_test.cpp:55
TEST
TEST(Navigationtest, MoveBaseRecovery)
Definition: navigation_test.cpp:175
TestNode::_tick_count
int _tick_count
Definition: navigation_test.cpp:77
TestNode::_expected_result
bool _expected_result
Definition: navigation_test.cpp:76
FollowPath
Definition: navigation_test.cpp:117
ComputePathToPose::ComputePathToPose
ComputePathToPose(const std::string &name)
Definition: navigation_test.cpp:108
BT::TreeNode::status
NodeStatus status() const
Definition: tree_node.cpp:279
BT::NodeStatus::FAILURE
@ FAILURE
BT::BehaviorTreeFactory::registerNodeType
void registerNodeType(const std::string &ID, const PortsList &ports, ExtraArgs... args)
Definition: bt_factory.h:322
BT::BehaviorTreeFactory::createTreeFromText
Tree createTreeFromText(const std::string &text, Blackboard::Ptr blackboard=Blackboard::create())
createTreeFromText will parse the XML directly from string. The XML needs to contain either a single ...
Definition: bt_factory.cpp:395
Now
std::chrono::high_resolution_clock::time_point Now()
Definition: navigation_test.cpp:38
BackUpAndSpin::BackUpAndSpin
BackUpAndSpin(const std::string &name)
Definition: navigation_test.cpp:96
FollowPath::wasHalted
bool wasHalted() const
Definition: navigation_test.cpp:153
BackUpAndSpin
Definition: navigation_test.cpp:93
ComputePathToPose::tick
NodeStatus tick() override
Method to be implemented by the user.
Definition: navigation_test.cpp:111
xml_parsing.h
BT::BehaviorTreeFactory
The BehaviorTreeFactory is used to create instances of a TreeNode at run-time.
Definition: bt_factory.h:205
BT::TreeNode::name
const std::string & name() const
Name of the instance, not the type.
Definition: tree_node.cpp:296
BT::NodeStatus::SUCCESS
@ SUCCESS
BackUpAndSpin::tick
NodeStatus tick() override
Method to be implemented by the user.
Definition: navigation_test.cpp:99
BT::ActionNodeBase
The ActionNodeBase is the base class to use to create any kind of action. A particular derived class ...
Definition: action_node.h:35
xml_text
static const char * xml_text
Definition: navigation_test.cpp:8
BT::TestNode
The TestNode is a Node that can be configure to:
Definition: test_node.h:64
BT::NodeStatus::RUNNING
@ RUNNING
TestNode::_name
std::string _name
Definition: navigation_test.cpp:78
TestNode::tickCount
int tickCount() const
Definition: navigation_test.cpp:63
TryDynamicCastPtr
void TryDynamicCastPtr(Original *ptr, Casted *&destination)
Definition: navigation_test.cpp:165
BT::NodeStatus::IDLE
@ IDLE
IsStuck
Definition: navigation_test.cpp:81
IsStuck::IsStuck
IsStuck(const std::string &name)
Definition: navigation_test.cpp:84
blackboard.h
TestNode::TestNode
TestNode(const std::string &name)
Definition: navigation_test.cpp:48
TestNode::setExpectedResult
void setExpectedResult(bool will_succeed)
Definition: navigation_test.cpp:51
BT::SyncActionNode
The SyncActionNode is an ActionNode that explicitly prevents the status RUNNING and doesn't require a...
Definition: action_node.h:52
BT::NodeStatus
NodeStatus
Definition: basic_types.h:33
FollowPath::tick
NodeStatus tick() override
Method to be implemented by the user.
Definition: navigation_test.cpp:126
Milliseconds
std::chrono::milliseconds Milliseconds
Definition: navigation_test.cpp:36


behaviortree_cpp_v4
Author(s): Davide Faconti
autogenerated on Fri Jun 28 2024 02:20:07