Skip to content

Commit 3ea85d8

Browse files
committed
Adding left, right side errors
Signed-off-by: silanus23 <[email protected]>
1 parent 7a02bf6 commit 3ea85d8

File tree

4 files changed

+155
-78
lines changed

4 files changed

+155
-78
lines changed

nav2_behavior_tree/include/nav2_behavior_tree/plugins/condition/is_within_path_tracking_bounds_condition.hpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,8 @@ class IsWithinPathTrackingBoundsCondition : public BT::ConditionNode
5252
static BT::PortsList providedPorts()
5353
{
5454
return {
55-
BT::InputPort<double>("max_error", "Maximum allowed tracking error")
55+
BT::InputPort<double>("max_error_right", "Maximum allowed tracking error on the right side"),
56+
BT::InputPort<double>("max_error_left", "Maximum allowed tracking error left side")
5657
};
5758
}
5859

@@ -62,7 +63,8 @@ class IsWithinPathTrackingBoundsCondition : public BT::ConditionNode
6263
rclcpp::executors::SingleThreadedExecutor callback_group_executor_;
6364
rclcpp::Subscription<nav2_msgs::msg::TrackingFeedback>::SharedPtr tracking_feedback_sub_;
6465
double last_error_{0.0};
65-
double max_error_{1.5};
66+
double max_error_right_{1.5};
67+
double max_error_left_{1.5};
6668
std::chrono::milliseconds bt_loop_duration_;
6769

6870
void trackingFeedbackCallback(const nav2_msgs::msg::TrackingFeedback::SharedPtr msg);

nav2_behavior_tree/nav2_tree_nodes.xml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -368,7 +368,8 @@
368368
</Condition>
369369

370370
<Decorator ID="IsWithinPathTrackingBounds">
371-
<input_port name="max_error">Maximum distance robot can go out in meters</input_port>
371+
<input_port name="max_error_left">Maximum distance robot can go out from path in meters. On left side</input_port>
372+
<input_port name="max_error_right">Maximum distance robot can go out from path in meters. On right side</input_port>
372373
</Decorator>
373374

374375
<Condition ID="DistanceTraveled">

nav2_behavior_tree/plugins/condition/is_within_path_tracking_bounds_condition.cpp

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,8 @@ void IsWithinPathTrackingBoundsCondition::trackingFeedbackCallback(
5252

5353
void IsWithinPathTrackingBoundsCondition::initialize()
5454
{
55-
getInput("max_error", max_error_);
55+
getInput("max_error_left", max_error_left_);
56+
getInput("max_error_right", max_error_right_);
5657
}
5758

5859
BT::NodeStatus IsWithinPathTrackingBoundsCondition::tick()
@@ -63,24 +64,44 @@ BT::NodeStatus IsWithinPathTrackingBoundsCondition::tick()
6364

6465
callback_group_executor_.spin_all(bt_loop_duration_);
6566

66-
if (!getInput("max_error", max_error_)) {
67+
if (!getInput("max_error_left", max_error_left_)) {
68+
RCLCPP_ERROR(logger_, "max_error_left parameter not provided");
69+
return BT::NodeStatus::FAILURE;
70+
}
71+
72+
if (max_error_left_ < 0.0) {
73+
RCLCPP_WARN(logger_, "max_error_left should be positive, using absolute value");
74+
max_error_left_ = std::abs(max_error_left_);
75+
}
76+
77+
if (!getInput("max_error_right", max_error_right_)) {
6778
RCLCPP_ERROR(logger_, "max_error parameter not provided");
6879
return BT::NodeStatus::FAILURE;
6980
}
7081

71-
if (max_error_ < 0.0) {
72-
RCLCPP_WARN(logger_, "max_error should be positive, using absolute value");
73-
max_error_ = std::abs(max_error_);
82+
if (max_error_right_ < 0.0) {
83+
RCLCPP_WARN(logger_, "max_error_right should be positive, using absolute value");
84+
max_error_right_ = std::abs(max_error_right_);
7485
}
7586

7687
if (last_error_ == std::numeric_limits<double>::max()) {
7788
RCLCPP_WARN(logger_, "No tracking feedback received yet.");
89+
return BT::NodeStatus::FAILURE;
7890
}
7991

80-
if (last_error_ <= max_error_) {
81-
return BT::NodeStatus::SUCCESS;
92+
if (last_error_ > 0.0) { // Positive = left side
93+
if (last_error_ > max_error_left_) {
94+
return BT::NodeStatus::FAILURE;
95+
} else {
96+
return BT::NodeStatus::SUCCESS;
97+
}
98+
} else { // Negative = right side
99+
if (std::abs(last_error_) > max_error_right_) {
100+
return BT::NodeStatus::FAILURE;
101+
} else {
102+
return BT::NodeStatus::SUCCESS;
103+
}
82104
}
83-
return BT::NodeStatus::FAILURE;
84105
}
85106

86107
} // namespace nav2_behavior_tree

nav2_behavior_tree/test/plugins/condition/test_is_within_path_tracking_bounds.cpp

Lines changed: 120 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,7 @@ class IsWithinPathTrackingBoundsConditionTestFixture : public ::testing::Test
3838
// Create the blackboard that will be shared by all of the nodes in the tree
3939
config_->blackboard = BT::Blackboard::create();
4040
// Put items on the blackboard
41-
config_->blackboard->set(
42-
"node",
43-
node_);
41+
config_->blackboard->set("node", node_);
4442
config_->blackboard->set<std::chrono::milliseconds>(
4543
"bt_loop_duration",
4644
std::chrono::milliseconds(10));
@@ -49,7 +47,7 @@ class IsWithinPathTrackingBoundsConditionTestFixture : public ::testing::Test
4947
"IsWithinPathTrackingBounds");
5048

5149
tracking_feedback_pub_ = node_->create_publisher<nav2_msgs::msg::TrackingFeedback>(
52-
"/tracking_feedback",
50+
"tracking_feedback",
5351
rclcpp::SystemDefaultsQoS());
5452
tracking_feedback_pub_->on_activate();
5553
}
@@ -64,6 +62,15 @@ class IsWithinPathTrackingBoundsConditionTestFixture : public ::testing::Test
6462
executor_.reset();
6563
}
6664

65+
void publishAndSpin(float error_value)
66+
{
67+
nav2_msgs::msg::TrackingFeedback msg;
68+
msg.tracking_error = error_value;
69+
tracking_feedback_pub_->publish(msg);
70+
std::this_thread::sleep_for(std::chrono::milliseconds(100));
71+
executor_->spin_some();
72+
}
73+
6774
protected:
6875
static nav2::LifecycleNode::SharedPtr node_;
6976
static rclcpp::executors::SingleThreadedExecutor::SharedPtr executor_;
@@ -81,129 +88,175 @@ std::shared_ptr<BT::BehaviorTreeFactory> IsWithinPathTrackingBoundsConditionTest
8188
nav2::Publisher<nav2_msgs::msg::TrackingFeedback>::SharedPtr
8289
IsWithinPathTrackingBoundsConditionTestFixture::tracking_feedback_pub_ = nullptr;
8390

84-
TEST_F(IsWithinPathTrackingBoundsConditionTestFixture, test_behavior_within_bounds)
91+
TEST_F(IsWithinPathTrackingBoundsConditionTestFixture, test_symmetric_bounds)
8592
{
93+
// Test with equal bounds for left and right sides
8694
std::string xml_txt =
8795
R"(
8896
<root BTCPP_format="4">
8997
<BehaviorTree ID="MainTree">
90-
<IsWithinPathTrackingBounds max_error="1.0"/>
98+
<IsWithinPathTrackingBounds max_error_left="1.0" max_error_right="1.0"/>
9199
</BehaviorTree>
92100
</root>)";
93101

94102
auto tree = factory_->createTreeFromText(xml_txt, config_->blackboard);
95103

96-
// Test case 1: Error is within bounds (should return SUCCESS)
97-
nav2_msgs::msg::TrackingFeedback tracking_feedback_msg;
98-
tracking_feedback_msg.tracking_error = 0.5;
99-
tracking_feedback_pub_->publish(tracking_feedback_msg);
100-
std::this_thread::sleep_for(std::chrono::milliseconds(100));
101-
executor_->spin_some();
104+
publishAndSpin(0.5);
102105
EXPECT_EQ(tree.tickOnce(), BT::NodeStatus::SUCCESS);
103-
104-
// Test case 2: Error is exactly at the boundary (should return SUCCESS)
105-
tracking_feedback_msg.tracking_error = 1.0;
106-
tracking_feedback_pub_->publish(tracking_feedback_msg);
107-
std::this_thread::sleep_for(std::chrono::milliseconds(100));
108-
executor_->spin_some();
106+
publishAndSpin(-0.5);
109107
EXPECT_EQ(tree.tickOnce(), BT::NodeStatus::SUCCESS);
110-
111-
// Test case 3: Error exceeds bounds (should return FAILURE)
112-
tracking_feedback_msg.tracking_error = 1.5;
113-
tracking_feedback_pub_->publish(tracking_feedback_msg);
114-
std::this_thread::sleep_for(std::chrono::milliseconds(100));
115-
executor_->spin_some();
108+
publishAndSpin(1.0);
109+
EXPECT_EQ(tree.tickOnce(), BT::NodeStatus::SUCCESS);
110+
publishAndSpin(-1.0);
111+
EXPECT_EQ(tree.tickOnce(), BT::NodeStatus::SUCCESS);
112+
publishAndSpin(1.5);
116113
EXPECT_EQ(tree.tickOnce(), BT::NodeStatus::FAILURE);
114+
publishAndSpin(-1.5);
115+
EXPECT_EQ(tree.tickOnce(), BT::NodeStatus::FAILURE);
116+
publishAndSpin(0.0);
117+
EXPECT_EQ(tree.tickOnce(), BT::NodeStatus::SUCCESS);
118+
}
117119

118-
// Test case 4: Zero error (should return SUCCESS)
119-
tracking_feedback_msg.tracking_error = 0.0;
120-
tracking_feedback_pub_->publish(tracking_feedback_msg);
121-
std::this_thread::sleep_for(std::chrono::milliseconds(100));
122-
executor_->spin_some();
120+
TEST_F(IsWithinPathTrackingBoundsConditionTestFixture, test_asymmetric_bounds)
121+
{
122+
// Test with different bounds for left and right sides
123+
std::string xml_txt =
124+
R"(
125+
<root BTCPP_format="4">
126+
<BehaviorTree ID="MainTree">
127+
<IsWithinPathTrackingBounds max_error_left="2.0" max_error_right="0.5"/>
128+
</BehaviorTree>
129+
</root>)";
130+
131+
auto tree = factory_->createTreeFromText(xml_txt, config_->blackboard);
132+
publishAndSpin(1.5);
133+
EXPECT_EQ(tree.tickOnce(), BT::NodeStatus::SUCCESS);
134+
publishAndSpin(-0.3);
135+
EXPECT_EQ(tree.tickOnce(), BT::NodeStatus::SUCCESS);
136+
publishAndSpin(2.0);
137+
EXPECT_EQ(tree.tickOnce(), BT::NodeStatus::SUCCESS);
138+
publishAndSpin(-0.5);
123139
EXPECT_EQ(tree.tickOnce(), BT::NodeStatus::SUCCESS);
140+
publishAndSpin(2.5);
141+
EXPECT_EQ(tree.tickOnce(), BT::NodeStatus::FAILURE);
142+
publishAndSpin(-0.8);
143+
EXPECT_EQ(tree.tickOnce(), BT::NodeStatus::FAILURE);
124144
}
125145

126-
TEST_F(IsWithinPathTrackingBoundsConditionTestFixture, test_behavior_different_max_error)
146+
TEST_F(IsWithinPathTrackingBoundsConditionTestFixture, test_left_side_only)
127147
{
148+
// Test with very restrictive right bound
128149
std::string xml_txt =
129150
R"(
130151
<root BTCPP_format="4">
131152
<BehaviorTree ID="MainTree">
132-
<IsWithinPathTrackingBounds max_error="0.2"/>
153+
<IsWithinPathTrackingBounds max_error_left="5.0" max_error_right="0.0"/>
133154
</BehaviorTree>
134155
</root>)";
135156

136157
auto tree = factory_->createTreeFromText(xml_txt, config_->blackboard);
137158

138-
// Test case 1: Error is within smaller bounds (should return SUCCESS)
139-
nav2_msgs::msg::TrackingFeedback tracking_feedback_msg;
140-
tracking_feedback_msg.tracking_error = 0.1;
141-
tracking_feedback_pub_->publish(tracking_feedback_msg);
142-
std::this_thread::sleep_for(std::chrono::milliseconds(100));
143-
executor_->spin_some();
159+
publishAndSpin(4.9);
160+
EXPECT_EQ(tree.tickOnce(), BT::NodeStatus::SUCCESS);
161+
publishAndSpin(5.0);
144162
EXPECT_EQ(tree.tickOnce(), BT::NodeStatus::SUCCESS);
163+
publishAndSpin(5.1);
164+
EXPECT_EQ(tree.tickOnce(), BT::NodeStatus::FAILURE);
165+
publishAndSpin(-0.001);
166+
EXPECT_EQ(tree.tickOnce(), BT::NodeStatus::FAILURE);
167+
publishAndSpin(0.0);
168+
EXPECT_EQ(tree.tickOnce(), BT::NodeStatus::SUCCESS);
169+
}
145170

146-
// Test case 2: Error exceeds smaller bounds (should return FAILURE)
147-
tracking_feedback_msg.tracking_error = 0.3;
148-
tracking_feedback_pub_->publish(tracking_feedback_msg);
149-
std::this_thread::sleep_for(std::chrono::milliseconds(100));
150-
executor_->spin_some();
171+
TEST_F(IsWithinPathTrackingBoundsConditionTestFixture, test_right_side_only)
172+
{
173+
// Test with very restrictive left bound
174+
std::string xml_txt =
175+
R"(
176+
<root BTCPP_format="4">
177+
<BehaviorTree ID="MainTree">
178+
<IsWithinPathTrackingBounds max_error_left="0.0" max_error_right="5.0"/>
179+
</BehaviorTree>
180+
</root>)";
181+
182+
auto tree = factory_->createTreeFromText(xml_txt, config_->blackboard);
183+
184+
publishAndSpin(-4.9);
185+
EXPECT_EQ(tree.tickOnce(), BT::NodeStatus::SUCCESS);
186+
publishAndSpin(-5.0);
187+
EXPECT_EQ(tree.tickOnce(), BT::NodeStatus::SUCCESS);
188+
publishAndSpin(-5.1);
189+
EXPECT_EQ(tree.tickOnce(), BT::NodeStatus::FAILURE);
190+
publishAndSpin(0.001);
151191
EXPECT_EQ(tree.tickOnce(), BT::NodeStatus::FAILURE);
192+
publishAndSpin(0.0);
193+
EXPECT_EQ(tree.tickOnce(), BT::NodeStatus::SUCCESS);
152194
}
153195

154-
TEST_F(IsWithinPathTrackingBoundsConditionTestFixture, test_behavior_large_error_values)
196+
TEST_F(IsWithinPathTrackingBoundsConditionTestFixture, test_very_tight_bounds)
155197
{
198+
// Test with very small bounds
156199
std::string xml_txt =
157200
R"(
158201
<root BTCPP_format="4">
159202
<BehaviorTree ID="MainTree">
160-
<IsWithinPathTrackingBounds max_error="5.0"/>
203+
<IsWithinPathTrackingBounds max_error_left="0.1" max_error_right="0.1"/>
161204
</BehaviorTree>
162205
</root>)";
163206

164207
auto tree = factory_->createTreeFromText(xml_txt, config_->blackboard);
165208

166-
// Test case 1: Large error within bounds (should return SUCCESS)
167-
nav2_msgs::msg::TrackingFeedback tracking_feedback_msg;
168-
tracking_feedback_msg.tracking_error = 4.9;
169-
tracking_feedback_pub_->publish(tracking_feedback_msg);
170-
std::this_thread::sleep_for(std::chrono::milliseconds(100));
171-
executor_->spin_some();
209+
publishAndSpin(0.05);
172210
EXPECT_EQ(tree.tickOnce(), BT::NodeStatus::SUCCESS);
211+
publishAndSpin(-0.05);
212+
EXPECT_EQ(tree.tickOnce(), BT::NodeStatus::SUCCESS);
213+
publishAndSpin(0.15);
214+
EXPECT_EQ(tree.tickOnce(), BT::NodeStatus::FAILURE);
215+
publishAndSpin(-0.15);
216+
EXPECT_EQ(tree.tickOnce(), BT::NodeStatus::FAILURE);
217+
}
218+
219+
TEST_F(IsWithinPathTrackingBoundsConditionTestFixture, test_no_feedback_received)
220+
{
221+
// Test behavior when no feedback has been received yet
222+
std::string xml_txt =
223+
R"(
224+
<root BTCPP_format="4">
225+
<BehaviorTree ID="MainTree">
226+
<IsWithinPathTrackingBounds max_error_left="1.0" max_error_right="1.0"/>
227+
</BehaviorTree>
228+
</root>)";
173229

174-
// Test case 2: Very large error exceeding bounds (should return FAILURE)
175-
tracking_feedback_msg.tracking_error = 10.0;
176-
tracking_feedback_pub_->publish(tracking_feedback_msg);
230+
// Create a fresh tree without publishing any feedback
231+
auto tree = factory_->createTreeFromText(xml_txt, config_->blackboard);
232+
233+
// Should return FAILURE when no feedback received
234+
// Note: This test creates a new condition node, so last_error_ will be at max()
177235
std::this_thread::sleep_for(std::chrono::milliseconds(100));
178236
executor_->spin_some();
179237
EXPECT_EQ(tree.tickOnce(), BT::NodeStatus::FAILURE);
180238
}
181239

182-
TEST_F(IsWithinPathTrackingBoundsConditionTestFixture, test_behavior_edge_cases)
240+
TEST_F(IsWithinPathTrackingBoundsConditionTestFixture, test_sign_convention)
183241
{
242+
// Verify sign convention: positive = left, negative = right
184243
std::string xml_txt =
185244
R"(
186245
<root BTCPP_format="4">
187246
<BehaviorTree ID="MainTree">
188-
<IsWithinPathTrackingBounds max_error="0.0"/>
247+
<IsWithinPathTrackingBounds max_error_left="1.0" max_error_right="2.0"/>
189248
</BehaviorTree>
190249
</root>)";
191250

192251
auto tree = factory_->createTreeFromText(xml_txt, config_->blackboard);
193252

194-
// Test case 1: Zero max_error with zero tracking error (should return SUCCESS)
195-
nav2_msgs::msg::TrackingFeedback tracking_feedback_msg;
196-
tracking_feedback_msg.tracking_error = 0.0;
197-
tracking_feedback_pub_->publish(tracking_feedback_msg);
198-
std::this_thread::sleep_for(std::chrono::milliseconds(100));
199-
executor_->spin_some();
253+
publishAndSpin(0.9);
200254
EXPECT_EQ(tree.tickOnce(), BT::NodeStatus::SUCCESS);
201-
202-
// Test case 2: Zero max_error with any positive tracking error (should return FAILURE)
203-
tracking_feedback_msg.tracking_error = 0.001;
204-
tracking_feedback_pub_->publish(tracking_feedback_msg);
205-
std::this_thread::sleep_for(std::chrono::milliseconds(100));
206-
executor_->spin_some();
255+
publishAndSpin(1.5);
256+
EXPECT_EQ(tree.tickOnce(), BT::NodeStatus::FAILURE);
257+
publishAndSpin(-1.5);
258+
EXPECT_EQ(tree.tickOnce(), BT::NodeStatus::SUCCESS);
259+
publishAndSpin(-2.5);
207260
EXPECT_EQ(tree.tickOnce(), BT::NodeStatus::FAILURE);
208261
}
209262

0 commit comments

Comments
 (0)