├── BehaviorTracker.h ├── ExplainableBT.h ├── README.md ├── kitting-tree.xml ├── main_service ├── CMakeLists.txt ├── README.md ├── package.xml └── srv │ └── Explain.srv └── taxi-domain-tree.xml /BehaviorTracker.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | class BehaviorTracker : public BT::StdCoutLogger { 6 | public: 7 | BehaviorTracker(const BT::Tree &tree) : tree(tree), StdCoutLogger(tree) { 8 | 9 | } 10 | 11 | void callback(BT::Duration timestamp, const BT::TreeNode &node, BT::NodeStatus prev_status, BT::NodeStatus status) override { 12 | ticking_node_uid_ = node.UID(); 13 | 14 | StdCoutLogger::callback(timestamp, node, prev_status, status); 15 | } 16 | 17 | BT::TreeNode* get_running_node() { 18 | BT::TreeNode *running_node = nullptr; 19 | BT::applyRecursiveVisitor(tree.root_node, [this, &running_node](BT::TreeNode* node_visiting) { 20 | if (running_node != nullptr) { 21 | return; 22 | } 23 | 24 | if (node_visiting->UID() == ticking_node_uid_) { 25 | running_node = node_visiting; 26 | } 27 | }); 28 | return running_node; 29 | } 30 | 31 | BT::TreeNode* get_running_node_different_control_parent() { 32 | BT::TreeNode *running_node = this->get_running_node(); 33 | 34 | BT::TreeNode *p = running_node->getParent(); 35 | while (p->name().empty() || p->short_description() == running_node->short_description()) { 36 | p = p->getParent(); 37 | } 38 | 39 | return p; 40 | } 41 | 42 | BT::TreeNode* get_overall_goal_node() { 43 | return tree.root_node; 44 | } 45 | 46 | BT::TreeNode* get_tree_parent() { 47 | BT::TreeNode *running_node = this->get_running_node(); 48 | 49 | BT::TreeNode *p = running_node->getParent(); 50 | while (p != nullptr && p->type() != BT::NodeType::SUBTREE) { 51 | p = p->getParent(); 52 | } 53 | 54 | return p; 55 | } 56 | 57 | private: 58 | const BT::Tree &tree; 59 | uint16_t ticking_node_uid_; 60 | }; 61 | -------------------------------------------------------------------------------- /ExplainableBT.h: -------------------------------------------------------------------------------- 1 | // This file contains Ports and ExplainableBT classes. 2 | // ExplainableBT class contains all the proposed algorithms in the paper. 3 | // The only input needed is a behavior tree. Please refer to 4 | // https://www.behaviortree.dev/ to get familiar with it. 5 | // Please use our fork https://github.com/uml-robotics/BehaviorTree.CPP because 6 | // it contains modifications that make the algorithms easy to reason about. 7 | // You should also be familiar with ROS. 8 | 9 | #include 10 | #include 11 | #include "main_service/Explain.h" 12 | #include "BehaviorTracker.h" 13 | #include // starts_with 14 | 15 | class Ports { 16 | public: 17 | Ports(const std::unordered_map& ports) : ports(ports) {} 18 | 19 | std::set get_keyed_value_set() { 20 | std::set set; 21 | for (const auto &kv : ports) { 22 | std::string value = kv.second; 23 | if (is_keyed(value)) { 24 | set.emplace(value); 25 | } 26 | } 27 | return set; 28 | } 29 | 30 | bool has_keyed_value(const std::string &port) { 31 | std::set set; 32 | for (const auto &kv : ports) { 33 | std::string value = kv.second; 34 | if (is_keyed(value)) { 35 | if (value == port) { 36 | return true; 37 | } 38 | } 39 | } 40 | return false; 41 | } 42 | private: 43 | static bool is_keyed(const std::string &value) { 44 | return boost::starts_with(value, "{"); 45 | } 46 | 47 | const std::unordered_map& ports; 48 | }; 49 | 50 | class ExplainableBT { 51 | public: 52 | explicit ExplainableBT(const BT::Tree & tree) : tree(tree), behavior_tracker(tree) { 53 | printTreeRecursively(tree.root_node); 54 | } 55 | 56 | BT::NodeStatus execute() { 57 | return tree.root_node->executeTick(); 58 | } 59 | 60 | bool explain_callback(main_service::Explain::Request &req, main_service::Explain::Response &res) { 61 | const std::string q = req.what; // question 62 | std::string a; // answer 63 | 64 | ROS_INFO_STREAM("Q: " << q); 65 | BT::TreeNode* n = behavior_tracker.get_running_node(); 66 | if (q == "What are you doing?") { 67 | a = "I " + n->short_description() + "."; 68 | } 69 | else if (q == "Why are you doing this?") { 70 | std::string goal = behavior_tracker.get_running_node_different_control_parent()->name(); 71 | 72 | a = "I " + (n->short_description()) + " in order to " + goal + "."; 73 | } 74 | else if (q == "What is your subgoal?") { 75 | BT::TreeNode* tree_parent = behavior_tracker.get_tree_parent(); 76 | if (tree_parent == nullptr) 77 | a = "Sorry. I don't have a subgoal."; 78 | else { 79 | std::string subgoal = tree_parent->name(); 80 | a = "My subgoal is to " + subgoal + "."; 81 | } 82 | } 83 | else if (q == "How do you achieve your subgoal?" || q== "What are the steps for your subgoal?") { 84 | BT::TreeNode* tree_parent = behavior_tracker.get_tree_parent(); 85 | if (tree_parent == nullptr) 86 | a = "Sorry. I don't have a subgoal."; 87 | else { 88 | std::string goal = tree_parent->name(); 89 | std::vector steps = find_steps(tree_parent); 90 | a = "To achieve the subgoal \"" + goal + "\", I need to do " + std::to_string(steps.size()) + 91 | " steps. "; 92 | for (int i = 0; i < steps.size(); ++i) { 93 | a += std::to_string(i + 1) + ". " + steps.at(i)->name() + ". "; 94 | } 95 | } 96 | } 97 | else if (q == "What is your goal?") { 98 | std::string goal = behavior_tracker.get_overall_goal_node()->name(); 99 | 100 | a = "My goal is to " + goal + "."; 101 | } 102 | else if (q == "How do you achieve your goal?" || q == "What are the steps for your goal") { 103 | std::string goal = behavior_tracker.get_overall_goal_node()->name(); 104 | std::vector steps = find_steps(tree.root_node); 105 | a = "To achieve the goal \"" + goal + "\", I need to do " + std::to_string(steps.size()) + " steps. "; 106 | for (int i = 0; i < steps.size(); ++i) { 107 | a += std::to_string(i+1) + ". " + steps.at(i)->name() + ". "; 108 | } 109 | } 110 | else if (q == "What went wrong?") { 111 | BT::TreeNode *running_node = behavior_tracker.get_running_node(); 112 | 113 | bool is_wrong = false; 114 | bool is_fell_back = false; 115 | 116 | BT::FallbackNode* fallback_node = nullptr; 117 | 118 | BT::TreeNode *p = running_node->getParent(); 119 | while (p != nullptr && p->type() != BT::NodeType::SUBTREE) { 120 | ROS_INFO_STREAM(p->short_description()); 121 | 122 | bool is_fallback_node = (dynamic_cast(p) != nullptr); 123 | if (is_fallback_node) { 124 | // 125 | // Fallback node 126 | // 127 | fallback_node = dynamic_cast(p); 128 | ROS_INFO_STREAM("Fallback node found: " << fallback_node->short_description()); 129 | if (fallback_node->child(0)->status() == BT::NodeStatus::FAILURE) { 130 | is_wrong = true; 131 | is_fell_back = true; 132 | 133 | a = "I could not " + fallback_node->short_description() + " because "; 134 | 135 | // find the failed child 136 | const BT::TreeNode* failed_child; 137 | BT::applyRecursiveVisitorSelectively(fallback_node, [&failed_child](const BT::TreeNode* node) -> bool { 138 | if (node->has_failed() && (node->type() == BT::NodeType::CONDITION || node->type() == BT::NodeType::ACTION)) { 139 | failed_child = node; 140 | return true; 141 | } 142 | return false; 143 | }); 144 | 145 | if (failed_child->getParent() != nullptr) { 146 | if (failed_child->getParent()->short_description() != fallback_node->short_description()) { 147 | a += "I was unable to " + failed_child->getParent()->short_description() + " as "; 148 | } 149 | } 150 | 151 | a += failed_child->short_description() + " failed."; 152 | 153 | break; 154 | } 155 | } 156 | 157 | bool is_retry_node = (dynamic_cast(p) != nullptr); 158 | if (is_retry_node) { 159 | auto retry_node = dynamic_cast(p); 160 | ROS_INFO_STREAM("Retry node found: " << retry_node->short_description()); 161 | 162 | if (retry_node->is_retrying()) { 163 | is_wrong = true; 164 | 165 | // check if have non-null parent 166 | BT::TreeNode *rp = retry_node->getParent(); 167 | while (rp == nullptr) { 168 | rp = rp->getParent(); 169 | } 170 | 171 | if (rp != nullptr) { 172 | a = "I am retrying for attempt " + std::to_string(retry_node->n_th_retry()) + " to " + rp->short_description() + ". "; 173 | 174 | // find the failed child 175 | const BT::TreeNode* failed_child; 176 | BT::applyRecursiveVisitorSelectively(retry_node, [&failed_child](const BT::TreeNode* node) -> bool { 177 | if (node->has_failed() && (node->type() == BT::NodeType::CONDITION || node->type() == BT::NodeType::ACTION)) { 178 | failed_child = node; 179 | return true; 180 | } 181 | return false; 182 | }); 183 | 184 | auto fp = failed_child->getParent(); 185 | while (fp->name().empty()) { 186 | fp = fp->getParent(); 187 | } 188 | 189 | a += "I could not " + fp->short_description() + " because " + failed_child->short_description() + " failed.";; 190 | 191 | break; 192 | } 193 | } 194 | } 195 | p = p->getParent(); 196 | } 197 | 198 | if (is_fell_back) { 199 | // find if there is a parent Retry node retrying. If so, say that 200 | 201 | p = fallback_node->getParent(); 202 | while (p != nullptr && p->type() != BT::NodeType::SUBTREE) { 203 | 204 | bool is_retry_node = (dynamic_cast(p) != nullptr); 205 | if (is_retry_node) { 206 | auto retry_node = dynamic_cast(p); 207 | ROS_INFO_STREAM("Retry node found: " << retry_node->short_description()); 208 | 209 | if (retry_node->is_retrying()) { 210 | 211 | // check if have non-null parent 212 | BT::TreeNode *rp = retry_node->getParent(); 213 | while (rp == nullptr) { 214 | rp = rp->getParent(); 215 | } 216 | 217 | if (rp != nullptr) { 218 | a += " I am retrying for attempt " + std::to_string(retry_node->n_th_retry()) + " to " + rp->short_description() + "."; 219 | } 220 | } 221 | } 222 | p = p->getParent(); 223 | } 224 | } 225 | 226 | if ( ! is_wrong) 227 | a = "Nothing went wrong."; 228 | } 229 | else if (boost::starts_with(q, "Can you")) { 230 | std::string asked = q.substr(8, q.size() - 8 - 1); 231 | ROS_INFO_STREAM(asked); 232 | a = asked; 233 | 234 | // get tree parent 235 | BT::TreeNode* tree_parent = behavior_tracker.get_tree_parent(); 236 | if (tree_parent == nullptr) 237 | tree_parent = tree.root_node; 238 | ROS_INFO_STREAM("tree parent: " + tree_parent->short_description()); 239 | 240 | // build supported nodes 241 | std::vector supported_nodes; 242 | auto visitor = [&supported_nodes](BT::TreeNode* node) { 243 | bool is_sequence_node = (dynamic_cast(node) != nullptr); 244 | if ((is_sequence_node || node->type() == BT::NodeType::SUBTREE) && (! node->name().empty())) { 245 | 246 | // if a node with the same name is added, don't add this node 247 | bool has_same_name_node_added = false; 248 | for (auto sn : supported_nodes) { 249 | if (sn->name() == node->name()) { 250 | has_same_name_node_added = true; 251 | break; 252 | } 253 | } 254 | 255 | if ( ! has_same_name_node_added) { 256 | ROS_INFO_STREAM("V " << node->short_description()); 257 | supported_nodes.emplace_back(node); 258 | } 259 | } 260 | }; 261 | applyRecursiveVisitor(tree_parent, visitor); 262 | 263 | // find the node, reply if not supported 264 | BT::TreeNode* supported_node; 265 | bool is_supported = false; 266 | for (auto sn : supported_nodes) { 267 | if (sn->name() == asked) { 268 | is_supported = true; 269 | supported_node = sn; 270 | break; 271 | } 272 | } 273 | if ( ! is_supported) { 274 | a = "Sorry. I cannot redo \"" + asked + "\"."; 275 | } 276 | else { 277 | 278 | ROS_INFO_STREAM("\"" + asked + "\" is supported"); 279 | 280 | auto ns = find_self_contained_behavior_node(supported_node); 281 | auto goal = behavior_tracker.get_overall_goal_node(); 282 | auto subgoal = behavior_tracker.get_tree_parent(); 283 | ROS_INFO_STREAM("Checking " << goal->UID() << " vs. " << subgoal->UID()); 284 | if (goal->UID() == subgoal->UID()) { 285 | ROS_INFO_STREAM("must have a subtree..."); 286 | } else { 287 | auto goal_sequence = dynamic_cast(goal); 288 | std::cout << "Before: " << goal_sequence->childrenCount(); 289 | goal_sequence->insertChildAfter(ns, subgoal); 290 | std::cout << "After: " << goal_sequence->childrenCount(); 291 | BT::printTreeRecursively(goal); 292 | 293 | a = "Yes. I will " + asked + " after " + subgoal->name() + "."; 294 | } 295 | } 296 | } 297 | else { 298 | a = "Sorry. I don't understand that \"" + req.what + "\""; 299 | } 300 | 301 | ROS_INFO_STREAM("A: " << a); 302 | res.reply = a; 303 | return true; 304 | } 305 | 306 | private: 307 | 308 | BT::TreeNode* find_self_contained_behavior_node(BT::TreeNode* supported_node) { 309 | 310 | // build unique, keyed input ports 311 | std::set unique_keyed_input_ports; 312 | applyRecursiveVisitor(supported_node, [&unique_keyed_input_ports](BT::TreeNode *node) { 313 | if (node->type() == BT::NodeType::ACTION) { // action node only 314 | auto set = Ports(node->config().input_ports).get_keyed_value_set(); 315 | unique_keyed_input_ports.insert(set.begin(), set.end()); 316 | } 317 | }); 318 | 319 | // find an ancestor node providing all dynamic input ports 320 | auto ns = supported_node; 321 | for (auto &dynamic_input : unique_keyed_input_ports) { 322 | ROS_INFO_STREAM("looking for " << dynamic_input); 323 | bool found = false; 324 | 325 | while ( ! found) { 326 | applyRecursiveVisitor(ns, [&found, &dynamic_input](BT::TreeNode *node) { 327 | if (found) { 328 | return; 329 | } 330 | 331 | if (node->type() == BT::NodeType::ACTION) { // action node only 332 | if (Ports(node->config().output_ports).has_keyed_value(dynamic_input)) { 333 | ROS_INFO_STREAM("found " << dynamic_input << "from node: " << node->short_description()); 334 | found = true; 335 | } 336 | } 337 | }); 338 | 339 | if (!found) { // go up a level 340 | ns = ns->getParent(); 341 | ROS_INFO_STREAM("went up to: " << ns->short_description()); 342 | } 343 | } 344 | } 345 | 346 | return ns; 347 | } 348 | 349 | std::vector find_steps(BT::TreeNode* parent_node) { 350 | std::vector steps; 351 | 352 | auto visitor = [this, &parent_node, &steps](BT::TreeNode* node) -> bool { 353 | if ( node->name().empty() || node->name() == parent_node->name() || node->type() == BT::NodeType::DECORATOR) { 354 | ROS_INFO_STREAM("X " << node->short_description()); 355 | return false; 356 | } 357 | else { 358 | ROS_INFO_STREAM("V " << node->short_description()); 359 | steps.emplace_back(node); 360 | return true; 361 | } 362 | }; 363 | 364 | applyRecursiveVisitorSelectively(parent_node, visitor); 365 | 366 | return steps; 367 | } 368 | 369 | const BT::Tree &tree; 370 | BehaviorTracker behavior_tracker; 371 | }; 372 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Robot Explanation Generation Using Behavior Trees (BTs) 2 | 3 | This reposotory contains code and the behavior trees in XML for the following paper accepted to ACM Transactions on Human-Robot Interaction (THRI). 4 | 5 | - Zhao Han, Daniel Giger, Jordan Allspaw, Michael S. Lee, Henny Admoni, and Holly A. Yanco. __Building The Foundation of Robot Explanation Generation Using Behavior Trees__. 6 | 7 | Everything other than the code is available at https://inside.mines.edu/~zhaohan/#re-bt. 8 | 9 | ## Code 10 | 11 | `ExplainableBT.h` contains all the proposed algorithms, implemented in C++ using the [BehaviorTree.CPP](https://www.behaviortree.dev/) library. Please read the header comment for its usage. 12 | 13 | - It also depends on `BehaviorTracker.h`, which tracks the node currently ticking in order to generate explanations related to it. 14 | 15 | The `main_service` folder contains a ROS service package to answer the 5 questions mentioned in the paper as well as dynamic behavior insertion as subgoal. 16 | 17 | ```bash 18 | rosservice call /explain "what: 'What are you doing?'" 19 | rosservice call /explain "what: 'Why are you doing this?'" 20 | rosservice call /explain "what: 'What is your goal?'" 21 | rosservice call /explain "what: 'How do you achieve your goal?'" 22 | rosservice call /explain "what: 'What is your subgoal?'" 23 | rosservice call /explain "what: 'How do you achieve your subgoal?'" 24 | 25 | rosservice call /explain "what: 'Can you place screw into caddy?'" 26 | rosservice call /explain "what: 'Can you pick screw?'" 27 | ``` 28 | 29 | ## Behavior Trees in XML 30 | 31 | `kitting-tree.xml` contains the tree of the meta kitting task, including the 3 tasks mentioned in the paper: 32 | 33 | 1. go pick screw 34 | 2. go place screw 35 | 3. go insert large gear 36 | 37 | `taxi-domain-tree.xml` contains the tree for the optimal policy of the Taxi domain. 38 | -------------------------------------------------------------------------------- /kitting-tree.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | -------------------------------------------------------------------------------- /main_service/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8.3) 2 | project(main_service) 3 | 4 | ## Compile as C++11, supported in ROS Kinetic and newer 5 | # add_compile_options(-std=c++11) 6 | 7 | ## Find catkin macros and libraries 8 | ## if COMPONENTS list like find_package(catkin REQUIRED COMPONENTS xyz) 9 | ## is used, also find other catkin packages 10 | find_package(catkin REQUIRED COMPONENTS 11 | roscpp message_generation 12 | ) 13 | 14 | ## System dependencies are found with CMake's conventions 15 | # find_package(Boost REQUIRED COMPONENTS system) 16 | 17 | 18 | ## Uncomment this if the package has a setup.py. This macro ensures 19 | ## modules and global scripts declared therein get installed 20 | ## See http://ros.org/doc/api/catkin/html/user_guide/setup_dot_py.html 21 | # catkin_python_setup() 22 | 23 | ################################################ 24 | ## Declare ROS messages, services and actions ## 25 | ################################################ 26 | 27 | ## To declare and build messages, services or actions from within this 28 | ## package, follow these steps: 29 | ## * Let MSG_DEP_SET be the set of packages whose message types you use in 30 | ## your messages/services/actions (e.g. std_msgs, actionlib_msgs, ...). 31 | ## * In the file package.xml: 32 | ## * add a build_depend tag for "message_generation" 33 | ## * add a build_depend and a exec_depend tag for each package in MSG_DEP_SET 34 | ## * If MSG_DEP_SET isn't empty the following dependency has been pulled in 35 | ## but can be declared for certainty nonetheless: 36 | ## * add a exec_depend tag for "message_runtime" 37 | ## * In this file (CMakeLists.txt): 38 | ## * add "message_generation" and every package in MSG_DEP_SET to 39 | ## find_package(catkin REQUIRED COMPONENTS ...) 40 | ## * add "message_runtime" and every package in MSG_DEP_SET to 41 | ## catkin_package(CATKIN_DEPENDS ...) 42 | ## * uncomment the add_*_files sections below as needed 43 | ## and list every .msg/.srv/.action file to be processed 44 | ## * uncomment the generate_messages entry below 45 | ## * add every package in MSG_DEP_SET to generate_messages(DEPENDENCIES ...) 46 | 47 | ## Generate messages in the 'msg' folder 48 | # add_message_files( 49 | # FILES 50 | # Message1.msg 51 | # Message2.msg 52 | # ) 53 | 54 | ## Generate services in the 'srv' folder 55 | add_service_files( 56 | FILES 57 | Explain.srv 58 | ) 59 | 60 | ## Generate actions in the 'action' folder 61 | # add_action_files( 62 | # FILES 63 | # Action1.action 64 | # Action2.action 65 | # ) 66 | 67 | ## Generate added messages and services with any dependencies listed here 68 | generate_messages( 69 | # DEPENDENCIES 70 | # std_msgs # Or other packages containing msgs 71 | ) 72 | 73 | ################################################ 74 | ## Declare ROS dynamic reconfigure parameters ## 75 | ################################################ 76 | 77 | ## To declare and build dynamic reconfigure parameters within this 78 | ## package, follow these steps: 79 | ## * In the file package.xml: 80 | ## * add a build_depend and a exec_depend tag for "dynamic_reconfigure" 81 | ## * In this file (CMakeLists.txt): 82 | ## * add "dynamic_reconfigure" to 83 | ## find_package(catkin REQUIRED COMPONENTS ...) 84 | ## * uncomment the "generate_dynamic_reconfigure_options" section below 85 | ## and list every .cfg file to be processed 86 | 87 | ## Generate dynamic reconfigure parameters in the 'cfg' folder 88 | # generate_dynamic_reconfigure_options( 89 | # cfg/DynReconf1.cfg 90 | # cfg/DynReconf2.cfg 91 | # ) 92 | 93 | ################################### 94 | ## catkin specific configuration ## 95 | ################################### 96 | ## The catkin_package macro generates cmake config files for your package 97 | ## Declare things to be passed to dependent projects 98 | ## INCLUDE_DIRS: uncomment this if your package contains header files 99 | ## LIBRARIES: libraries you create in this project that dependent projects also need 100 | ## CATKIN_DEPENDS: catkin_packages dependent projects also need 101 | ## DEPENDS: system dependencies of this project that dependent projects also need 102 | catkin_package( 103 | # INCLUDE_DIRS include 104 | # LIBRARIES main_service 105 | # CATKIN_DEPENDS roscpp 106 | # DEPENDS system_lib 107 | ) 108 | 109 | ########### 110 | ## Build ## 111 | ########### 112 | 113 | ## Specify additional locations of header files 114 | ## Your package locations should be listed before other locations 115 | include_directories( 116 | # include 117 | ${catkin_INCLUDE_DIRS} 118 | ) 119 | 120 | ## Declare a C++ library 121 | # add_library(${PROJECT_NAME} 122 | # src/${PROJECT_NAME}/main_service.cpp 123 | # ) 124 | 125 | ## Add cmake target dependencies of the library 126 | ## as an example, code may need to be generated before libraries 127 | ## either from message generation or dynamic reconfigure 128 | # add_dependencies(${PROJECT_NAME} ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS}) 129 | 130 | ## Declare a C++ executable 131 | ## With catkin_make all packages are built within a single CMake context 132 | ## The recommended prefix ensures that target names across packages don't collide 133 | # add_executable(${PROJECT_NAME}_node src/main_service_node.cpp) 134 | 135 | ## Rename C++ executable without prefix 136 | ## The above recommended prefix causes long target names, the following renames the 137 | ## target back to the shorter version for ease of user use 138 | ## e.g. "rosrun someones_pkg node" instead of "rosrun someones_pkg someones_pkg_node" 139 | # set_target_properties(${PROJECT_NAME}_node PROPERTIES OUTPUT_NAME node PREFIX "") 140 | 141 | ## Add cmake target dependencies of the executable 142 | ## same as for the library above 143 | # add_dependencies(${PROJECT_NAME}_node ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS}) 144 | 145 | ## Specify libraries to link a library or executable target against 146 | # target_link_libraries(${PROJECT_NAME}_node 147 | # ${catkin_LIBRARIES} 148 | # ) 149 | 150 | ############# 151 | ## Install ## 152 | ############# 153 | 154 | # all install targets should use catkin DESTINATION variables 155 | # See http://ros.org/doc/api/catkin/html/adv_user_guide/variables.html 156 | 157 | ## Mark executable scripts (Python etc.) for installation 158 | ## in contrast to setup.py, you can choose the destination 159 | # install(PROGRAMS 160 | # scripts/my_python_script 161 | # DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION} 162 | # ) 163 | 164 | ## Mark executables for installation 165 | ## See http://docs.ros.org/melodic/api/catkin/html/howto/format1/building_executables.html 166 | # install(TARGETS ${PROJECT_NAME}_node 167 | # RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION} 168 | # ) 169 | 170 | ## Mark libraries for installation 171 | ## See http://docs.ros.org/melodic/api/catkin/html/howto/format1/building_libraries.html 172 | # install(TARGETS ${PROJECT_NAME} 173 | # ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION} 174 | # LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION} 175 | # RUNTIME DESTINATION ${CATKIN_GLOBAL_BIN_DESTINATION} 176 | # ) 177 | 178 | ## Mark cpp header files for installation 179 | # install(DIRECTORY include/${PROJECT_NAME}/ 180 | # DESTINATION ${CATKIN_PACKAGE_INCLUDE_DESTINATION} 181 | # FILES_MATCHING PATTERN "*.h" 182 | # PATTERN ".svn" EXCLUDE 183 | # ) 184 | 185 | ## Mark other files for installation (e.g. launch and bag files, etc.) 186 | # install(FILES 187 | # # myfile1 188 | # # myfile2 189 | # DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION} 190 | # ) 191 | 192 | ############# 193 | ## Testing ## 194 | ############# 195 | 196 | ## Add gtest based cpp test target and link libraries 197 | # catkin_add_gtest(${PROJECT_NAME}-test test/test_main_service.cpp) 198 | # if(TARGET ${PROJECT_NAME}-test) 199 | # target_link_libraries(${PROJECT_NAME}-test ${PROJECT_NAME}) 200 | # endif() 201 | 202 | ## Add folders to be run by python nosetests 203 | # catkin_add_nosetests(test) 204 | -------------------------------------------------------------------------------- /main_service/README.md: -------------------------------------------------------------------------------- 1 | # main_service 2 | 3 | Everything in this package should be in the main package, but the generated msg code conflicts with the `main` keyword reserved in C++. -------------------------------------------------------------------------------- /main_service/package.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | main_service 4 | 0.0.0 5 | The main_service package 6 | 7 | 8 | 9 | 10 | zhao 11 | 12 | 13 | 14 | 15 | 16 | TODO 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | catkin 52 | roscpp 53 | message_generation 54 | roscpp 55 | roscpp 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /main_service/srv/Explain.srv: -------------------------------------------------------------------------------- 1 | string what 2 | --- 3 | string reply -------------------------------------------------------------------------------- /taxi-domain-tree.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | --------------------------------------------------------------------------------