├── CMakeLists.txt
├── README.md
├── costmap_plugins.xml
├── include
└── multibot_layers
│ └── multibot_layer.h
├── package.xml
└── src
└── multibot_layer.cpp
/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 2.8.3)
2 | project(multibot_layers)
3 |
4 | find_package(catkin REQUIRED COMPONENTS
5 | costmap_2d
6 | dynamic_reconfigure
7 | roscpp
8 | )
9 |
10 | catkin_package(
11 | INCLUDE_DIRS include
12 | LIBRARIES multibot_layers
13 | CATKIN_DEPENDS costmap_2d dynamic_reconfigure roscpp
14 | )
15 |
16 | include_directories(
17 | include
18 | ${catkin_INCLUDE_DIRS}
19 | )
20 |
21 | add_library(multibot_layer src/multibot_layer.cpp)
22 |
23 |
24 | install(FILES costmap_plugins.xml
25 | DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION})
26 |
27 | install(TARGETS multibot_layer
28 | LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION})
29 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## Multibot_layers
2 | A costmap layer allows robot to communicate and share location to each others. Through the ROS parameter server, robots publish their pose to the corresponding rosparam under its own name prefix, for example, /omni_1/pose_x and /omni_1/pose_y, when the costmap updates.
3 |
4 | ## Implementation
5 | Before diving into this repo, read this [wonderful tutorial](http://wiki.ros.org/costmap_2d/Tutorials/Creating%20a%20New%20Layer). The differences between the multibot_layers and the simple_layers represented in the ROS Wiki tutorial are changes in updateBounds() and updateCosts() functions.
6 | * ***updateBounds()*** does not change the costmap. It just defines the area that will need to be updated. It first publish its own pose to the parameter server under the specific prefix. And then We calculate the point we want to change in mark[][] by checking the poses of other robots on parameter server. Finally, we expand the min/max bounds to be sure it includes the new point.
7 | * ***updateCosts()*** calculates which grid cell our point is in using worldToMap. Then we set the cost of that cell.
8 |
9 | ### pseudocode
10 | ```
11 | # in updateBounds
12 |
13 | set param -- /omni_num/pose_x -- to its pose_x relative to "map" frame
14 | set param -- /omni_num/pose_x -- to its pose_x relative to "map" frame
15 | find its own prefix by "searchParam"
16 | for i (0 to 4) do
17 | save the poses of omni_i, except itself, relative to "map" frame map[i]
18 | end for
19 | ```
20 | p s. [searchParam](http://wiki.ros.org/roscpp/Overview/Parameter%20Server) helps you to get a parameter from the closest namespace.
21 | ```
22 | # in updateCosts
23 |
24 | for i (0 to 4) do
25 | set Lethal_obstacle to the poses occupied by other robots.
26 | end for
27 | ```
28 |
29 | ## Usage
30 |
31 | ### Clone the pkg
32 | ```
33 | cd ~/catkin_ws/src
34 | git clone https://github.com/airuchen/multibot_layer.git
35 | cd ~/catkin_ws
36 | rosdep install --from-paths src --ignore-src -r -y
37 | catkin_make
38 | source devel/setup.bash
39 | ```
40 | ### Add the layer into the costmap yaml file
41 | ```
42 | plugins:
43 | - {name: multibot_layer, type: "multibot_layer_namespace::MultibotLayer"}
44 |
45 | ```
46 |
47 | ## Try it yourself
48 | Follow the tutorial [here](https://github.com/airuchen/turtlebot3) to try out this plugin with multiple turtlebot3.
--------------------------------------------------------------------------------
/costmap_plugins.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/include/multibot_layers/multibot_layer.h:
--------------------------------------------------------------------------------
1 | #ifndef MULTIBOT_LAYER_H_
2 | #define MULTIBOT_LAYER_H_
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 |
9 | namespace multibot_layer_namespace
10 | {
11 |
12 | class MultibotLayer : public costmap_2d::Layer
13 | {
14 | public:
15 | MultibotLayer();
16 |
17 | virtual void onInitialize();
18 | virtual void updateBounds(double origin_x, double origin_y, double origin_yaw, double* min_x, double* min_y, double* max_x,
19 | double* max_y);
20 | virtual void updateCosts(costmap_2d::Costmap2D& master_grid, int min_i, int min_j, int max_i, int max_j);
21 |
22 | private:
23 | void reconfigureCB(costmap_2d::GenericPluginConfig &config, uint32_t level);
24 |
25 |
26 | double mark[10][2];
27 | ros::NodeHandle nh;
28 | dynamic_reconfigure::Server *dsrv_;
29 | };
30 | }
31 | #endif
32 |
--------------------------------------------------------------------------------
/package.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | multibot_layers
4 | 1.0.0
5 | The multibot_layers package
6 |
7 | airuchen
8 |
9 |
10 | Apache 2.0
11 |
12 | catkin
13 | costmap_2d
14 | dynamic_reconfigure
15 | roscpp
16 | costmap_2d
17 | dynamic_reconfigure
18 | roscpp
19 | costmap_2d
20 | dynamic_reconfigure
21 | roscpp
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/src/multibot_layer.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 |
4 | PLUGINLIB_EXPORT_CLASS(multibot_layer_namespace::MultibotLayer, costmap_2d::Layer)
5 |
6 | using costmap_2d::LETHAL_OBSTACLE;
7 |
8 | namespace multibot_layer_namespace
9 | {
10 | MultibotLayer::MultibotLayer() {}
11 |
12 | void MultibotLayer::onInitialize()
13 | {
14 | ros::NodeHandle nh("~/" + name_);
15 | current_ = true;
16 |
17 | dsrv_ = new dynamic_reconfigure::Server(nh);
18 | dynamic_reconfigure::Server::CallbackType cb = boost::bind(
19 | &MultibotLayer::reconfigureCB, this, _1, _2);
20 | dsrv_->setCallback(cb);
21 | }
22 |
23 |
24 | void MultibotLayer::reconfigureCB(costmap_2d::GenericPluginConfig &config, uint32_t level)
25 | {
26 | enabled_ = config.enabled;
27 | }
28 |
29 | void MultibotLayer::updateBounds(double origin_x, double origin_y, double origin_yaw, double* min_x,
30 | double* min_y, double* max_x, double* max_y)
31 | {
32 | if (!enabled_)
33 | return;
34 |
35 |
36 | nh.setParam("pose_x", origin_x);
37 | nh.setParam("pose_y", origin_y);
38 |
39 | // dummy search for five robots. TODO: set dynamic robot array
40 | for (int i = 0; i < 5; i++) {
41 | std::string robot_pose = "/nb2_";
42 | std::string robot_x;
43 | std::string robot_y;
44 | std::string this_bot;
45 | // search for the full posename of the current robot. Eg, this_bot = "nb2_/pose_x".
46 | nh.searchParam("pose_x", this_bot);
47 |
48 |
49 | robot_pose.append(std::to_string(i)); // set robot_pose to "/nb2_"
50 | robot_x = robot_y = robot_pose; // give robot_x and robot_y the same prefix
51 | robot_x.append("/pose_x"); // set robot_x to "/nb2_/pose_x"
52 | robot_y.append("/pose_y"); // set robot_y to "/nb2_/pose_y"
53 |
54 | // Save poses of every robot, except itself, in mark[][] array
55 | if (nh.searchParam(robot_x, robot_x) && robot_x != this_bot) {
56 | nh.getParam(robot_x, mark[i][0]);
57 | nh.getParam(robot_y, mark[i][1]);
58 | } else {
59 | mark[i][0] = 100; // Set an unaffected pose for unused robots
60 | mark[i][1] = 100; // Set an unaffected pose for unused robots
61 | }
62 |
63 | *min_x = std::min(*min_x, mark[i][0]);
64 | *min_y = std::min(*min_y, mark[i][1]);
65 | *max_x = std::max(*max_x, mark[i][0]);
66 | *max_y = std::max(*max_y, mark[i][1]);
67 | }
68 | }
69 |
70 | void MultibotLayer::updateCosts(costmap_2d::Costmap2D& master_grid, int min_i, int min_j, int max_i,
71 | int max_j)
72 | {
73 | if (!enabled_)
74 | return;
75 | unsigned int mx;
76 | unsigned int my;
77 | for (int i = 0; i < 5; i++) {
78 | if(master_grid.worldToMap(mark[i][0], mark[i][1], mx, my)){
79 | master_grid.setCost(mx, my, LETHAL_OBSTACLE);
80 | }
81 | }
82 | }
83 |
84 | } // end namespace
85 |
--------------------------------------------------------------------------------