Program Listing for File mode_switch.cpp
↰ Return to documentation for file (hateb_local_planner/src/mode_switch.cpp)
/*********************************************************************
*
* Software License Agreement (BSD License)
*
* Copyright (c) 2025 LAAS/CNRS
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
* * Neither the name of the institute nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* Author: Phani Teja Singamaneni (email:ptsingaman@laas.fr)
*********************************************************************/
#include <hateb_local_planner/mode_switch.h>
#include <string>
#define AGENTS_INFO_SUB "/move_base/agentsInfo"
#define GOAL_SUB "/move_base/goal"
#define RESULT_SUB "/move_base/result"
#define PASSAGE_SUB "/map_scanner/passage"
namespace hateb_local_planner {
ModeSwitch::ModeSwitch() {
name_ = "ModeSwitch";
initialized_ = false;
};
void ModeSwitch::initialize(ros::NodeHandle& nh, std::string& xml_path, std::shared_ptr<agents::Agents>& agents_ptr, std::shared_ptr<Backoff>& backoff_ptr) {
if (!initialized_) {
// Initialize the ROS components
// TODO(sphanit): Check if you need to make them configurable
nh_ = nh;
// Get the namespace from the parameter server (different from the cfg server)
if (!ros::param::get("~ns", ns_)) {
ns_ = std::string("");
}
// Map the subcsriptions properly
agents_info_sub_topic_ = std::string(AGENTS_INFO_SUB);
goal_sub_topic_ = std::string(GOAL_SUB);
result_sub_topic_ = std::string(RESULT_SUB);
passage_sub_topic_ = std::string(PASSAGE_SUB);
if (!ns_.empty()) {
agents_info_sub_topic_ = "/" + ns_ + agents_info_sub_topic_;
goal_sub_topic_ = "/" + ns_ + goal_sub_topic_;
result_sub_topic_ = "/" + ns_ + result_sub_topic_;
passage_sub_topic_ = "/" + ns_ + passage_sub_topic_;
}
agents_info_sub_ = nh_.subscribe(agents_info_sub_topic_, 1, &ModeSwitch::agentsInfoCB, this);
goal_sub_ = nh_.subscribe(goal_sub_topic_, 1, &ModeSwitch::goalMoveBaseCB, this);
result_sub_ = nh_.subscribe(result_sub_topic_, 1, &ModeSwitch::resultMoveBaseCB, this);
passage_detect_sub_ = nh_.subscribe(passage_sub_topic_, 1, &ModeSwitch::passageCB, this);
planning_mode_pub_ = nh_.advertise<hateb_local_planner::PlanningMode>("planning_mode", 10);
// Initialize the parameters
goal_reached_ = true;
goal_update_ = false;
backoff_ptr_ = backoff_ptr;
// Register the BT nodes
registerNodes();
// Build the Behavior Tree from the XML
if (xml_path == "") {
BT_ERROR("ModeSwitch", "Please provide the correct xml to create the tree!")
exit(0);
}
bhv_tree_ = bhv_factory_.createTreeFromFile(xml_path);
int8_t psg_type = cohan_msgs::PassageType::OPEN;
// Set the initial Blackboard entries
ModeInfo init_mode;
init_mode.plan = PLAN::SINGLE_BAND;
init_mode.predict = PREDICTION::CONST_VEL;
bhv_tree_.rootBlackboard()->set("planning_mode", init_mode);
bhv_tree_.rootBlackboard()->set("goal_update", false);
bhv_tree_.rootBlackboard()->set("backoff_ptr", backoff_ptr);
bhv_tree_.rootBlackboard()->set("agents_ptr", agents_ptr);
bhv_tree_.rootBlackboard()->set("passage_type", psg_type);
bhv_tree_.rootBlackboard()->set("reset", false);
bhv_tree_.rootBlackboard()->set("recovery", false);
BT_INFO(name_, "Behavior Tree initialized.")
initialized_ = true;
} else {
BT_WARN(name_, "The tree is already initialized!")
}
}
void ModeSwitch::passageCB(const cohan_msgs::PassageType& passage_msg) {
// Set the passage type on the blackboard
bhv_tree_.rootBlackboard()->set("passage_type", passage_msg.type);
}
void ModeSwitch::agentsInfoCB(const agent_path_prediction::AgentsInfo& info_msg) {
agents_info_ = info_msg;
// Set the agents_info on the blackboard
bhv_tree_.rootBlackboard()->set("agents_info", agents_info_);
}
void ModeSwitch::goalMoveBaseCB(const move_base_msgs::MoveBaseActionGoal& goal_msg) {
// Set the goal status
BT_INFO(name_, "Goal is set!")
if (!goal_reached_) {
bhv_tree_.rootBlackboard()->set("goal_update", true);
goal_update_ = true;
}
goal_ = goal_msg.goal.target_pose;
bhv_tree_.rootBlackboard()->set("nav_goal", goal_);
goal_reached_ = false;
}
void ModeSwitch::resultMoveBaseCB(const move_base_msgs::MoveBaseActionResult& result_msg) {
// Set the goal status
if (static_cast<int>(result_msg.status.status) == 3) {
goal_reached_ = true;
}
}
BT::NodeStatus ModeSwitch::tickBT() {
// Tick the tree from the start
auto status = bhv_tree_.tickOnce();
updateMode();
if (goal_update_) {
bhv_tree_.rootBlackboard()->set("goal_update", false);
goal_update_ = false;
}
return status;
}
void ModeSwitch::updateMode(int duration) {
std::scoped_lock lock(pub_mutex_);
// Get the PlanningMode msg from the blackboard
mode_info_ = bhv_tree_.rootBlackboard()->get<ModeInfo>("planning_mode");
BT_INFO(name_, (int)mode_info_.plan);
BT_INFO(name_, (int)mode_info_.predict);
plan_mode_.plan_mode = mode_info_.plan;
plan_mode_.predict_mode = mode_info_.predict;
plan_mode_.moving_humans = agents_info_.moving;
plan_mode_.still_humans = agents_info_.still;
// TODO(sphanit): Make the duration configurable. will this be of any advantage?
// Publish the mode on the give ROS Topic
if (duration == 0) {
planning_mode_pub_.publish(plan_mode_);
} else {
auto start = ros::Time::now();
auto end = ros::Time::now();
while (((end - start).toSec() != duration)) {
end = ros::Time::now();
planning_mode_pub_.publish(plan_mode_);
}
}
}
hateb_local_planner::PlanningMode ModeSwitch::tickAndGetMode() {
// Tick the tree once and return the updated planning mode
tickBT();
return plan_mode_;
}
void ModeSwitch::resetBT() {
// Halt the tree and set goal reached to true
goal_reached_ = true;
bhv_tree_.haltTree();
// printTreeStatus(bhv_tree_.rootNode()); //<! Use this for debugging Tree status
}
void ModeSwitch::registerNodes() {
// The only node that handles ROS connections is the "setMode"
RegisterStatefulActionNodeROS<hateb_local_planner::SetMode>(bhv_factory_, "setMode", nh_);
// Register all other nodes needed for the behavior tree
bhv_factory_.registerNodeType<hateb_local_planner::IsGoalReached>("goalCheck");
bhv_factory_.registerNodeType<hateb_local_planner::IsGoalUpdated>("isGoalUpdated");
bhv_factory_.registerNodeType<hateb_local_planner::SingleBandExitCondition>("singleBandExitCond");
bhv_factory_.registerNodeType<hateb_local_planner::DualBandExitCondition>("dualBandExitCond");
bhv_factory_.registerNodeType<hateb_local_planner::VelObsExitCondition>("velobsExitCond");
bhv_factory_.registerNodeType<hateb_local_planner::BackoffExitCondition>("backoffExitCond");
bhv_factory_.registerNodeType<hateb_local_planner::PassThroughCondition>("passThroughCond");
}
}; // namespace hateb_local_planner