11 void analyze_area (vector<geometry_msgs::Point> coords, geometry_msgs::Point& origin,
double& rotation,
double& width,
double& height)
14 geometry_msgs::Point pl, pb, pr, pt;
15 pl.x = numeric_limits<double>::max();
16 pb.y = numeric_limits<double>::max();
17 pr.x = numeric_limits<double>::min();
18 pt.y = numeric_limits<double>::min();
19 for (
auto p : coords) {
21 if (p.x < pl.x || (p.x == pl.x && p.y < pl.y))
29 if (p.x > pr.x || (p.x == pr.x && p.y < pr.y))
38 if ((pl.x == pb.x && pl.y == pb.y) || (pr.x == pb.x && pr.y == pb.y)) {
46 else if (pr.y < pl.y) {
48 rotation = -atan2(pr.y - pb.y, pr.x - pb.x);
49 width = hypot(pr.x - pb.x, pr.y - pb.y);
50 height = hypot(pl.x - pb.x, pl.y - pb.y);
56 rotation = -atan2(pb.y - pl.y, pb.x - pl.x);
57 width = hypot(pl.x - pb.x, pl.y - pb.y);
58 height = hypot(pr.x - pb.x, pr.y - pb.y);
61 ROS_DEBUG(
"Origin (%.2f,%.2f)", origin.x, origin.y);
63 ROS_DEBUG(
"Size %.2fx%.2f", width, height);
71 void rotate (nav_msgs::OccupancyGrid& map,
double angle)
73 ROS_DEBUG(
"Rotate map by %.2f...", angle);
76 geometry_msgs::Pose origin_new;
77 origin_new.position.x = map.info.origin.position.x*cos(angle) - map.info.origin.position.y*sin(angle);
78 origin_new.position.y = map.info.origin.position.x*sin(angle) + map.info.origin.position.y*cos(angle);
81 vector<vector<signed char>> rt;
82 for (
int i=0; i<2*map.info.height; ++i) {
83 vector<signed char> row(2*map.info.width, 100);
88 int i_new, j_new, width_new=0, height_new=0;
89 double x, y, x_new, y_new;
90 for (
int i=0; i<map.info.height; ++i) {
91 for (
int j=0; j<map.info.width; ++j) {
93 x = double(j) * map.info.resolution + map.info.origin.position.x;
94 y = double(i) * map.info.resolution + map.info.origin.position.y;
95 x_new = x*cos(angle) - y*sin(angle);
96 y_new = x*sin(angle) + y*cos(angle);
97 j_new = int(round((x_new - origin_new.position.x) / map.info.resolution));
98 i_new = int(round((y_new - origin_new.position.y) / map.info.resolution));
101 if (i_new >= rt.size()) {
104 if (j_new >= rt[i_new].size()) {
109 rt[i_new][j_new] = map.data[i*map.info.width + j];
112 if (rt[i_new][j_new] == 0) {
113 if (i_new > height_new)
115 if (j_new > width_new)
122 rt.resize(height_new);
123 for (
int i=0; i<rt.size(); ++i)
124 rt[i].resize(width_new);
127 vector<signed char> rt_col;
128 for (
int i=0; i<rt.size(); ++i) {
129 for (
int j=0; j<rt[i].size(); ++j) {
130 rt_col.push_back(rt[i][j]);
138 map.info.map_load_time = Time::now();
139 map.info.width = width_new;
140 map.info.height= height_new;
141 map.info.origin = origin_new;
155 int f = int(round(
resolution / map.info.resolution));
160 vector<signed char> lr;
161 for (
int i=0; i+f<map.info.height; i+=f) {
162 for (
int j=0; j+f<map.info.width; j+=f) {
164 vector<unsigned int>
values(256, 0);
165 for (
int m=i; m<i+f; ++m) {
166 for (
int n=j; n<j+f; ++n) {
167 values[map.data[m*map.info.width + n]]++;
172 unsigned char value = 0;
173 unsigned int freq = 0;
174 for (
int k=0; k<values.size(); ++k) {
175 if (values[k] > freq) {
188 map.info.map_load_time = Time::now();
190 map.info.width = int(floor(
double(map.info.width) /
double(f)));
191 map.info.height = int(floor(
double(map.info.height) /
double(f)));
194 for (
int i=0; i<map.info.height; ++i) {
197 for (
int j=0; j<map.info.width; ++j) {
198 if (map.data[i*map.info.width + j] == 100) {
204 if (obst == map.info.width) {
206 map.data.erase(map.data.begin() + i*map.info.width, map.data.begin() + (i+1)*map.info.width);
209 map.info.map_load_time = Time::now();
211 map.info.origin.position.y += map.info.resolution;
226 ROS_DEBUG(
"Starting at (%.2f,%.2f)", start.x, start.y);
229 vector<geometry_msgs::Point> coords;
230 cpswarm_msgs::GetPoints get_coords;
232 coords = get_coords.response.points;
234 ROS_ERROR(
"Failed to get area coordinates!");
239 geometry_msgs::Point origin;
240 double rotation, width, height;
254 ROS_INFO(
"Generate new coverage path...");
257 ROS_DEBUG(
"Construct minimum-spanning-tree...");
290 bool get_path (nav_msgs::GetPlan::Request &req, nav_msgs::GetPlan::Response &res)
310 bool get_waypoint (cpswarm_msgs::GetWaypoint::Request &req, cpswarm_msgs::GetWaypoint::Response &res)
324 geometry_msgs::PointStamped wp;
325 wp.header.stamp = Time::now();
326 wp.header.frame_id =
"local_origin_ned";
327 wp.point = res.point;
352 for (
auto cps : msg->states) {
358 auto idx =
swarm.find(cps.swarmio.node);
361 if (idx ==
swarm.end()) {
362 swarm.emplace(cps.swarmio.node, Time::now());
364 ROS_DEBUG(
"New CPS %s", cps.swarmio.node.c_str());
372 idx->second = Time::now();
377 for (
auto cps=
swarm.cbegin(); cps!=
swarm.cend();) {
379 ROS_DEBUG(
"Remove CPS %s", cps->first.c_str());
397 int main (
int argc,
char **argv)
400 init(argc, argv,
"coverage_path");
408 nh.
param(this_node::getName() +
"/loop_rate", loop_rate, 1.5);
410 nh.
param(this_node::getName() +
"/queue_size", queue_size, 1);
414 nh.
param(this_node::getName() +
"/vertical",
vertical,
false);
421 wp_publisher = nh.
advertise<geometry_msgs::PointStamped>(
"coverage_path/waypoint", queue_size,
true);
437 Rate rate(loop_rate);
bool get_path(nav_msgs::GetPlan::Request &req, nav_msgs::GetPlan::Response &res)
Callback function to get the coverage path.
ServiceClient serviceClient(const std::string &service_name, bool persistent=false, const M_string &header_values=M_string())
void initialize_map(geometry_msgs::Point origin, double rotation, double width, double height)
Initialize properties of the area to be covered.
bool get_waypoint(cpswarm_msgs::GetWaypoint::Request &req, cpswarm_msgs::GetWaypoint::Response &res)
Callback function to get the current waypoint of the path.
nav_msgs::OccupancyGrid area
The grid map representing the environment to be covered.
void publish(const boost::shared_ptr< M > &message) const
void construct()
Generate the MST using Kruskal's algorithm.
Subscriber swarm_sub
Subscriber to get information about other CPSs in the swarm.
std::vector< double > values
bool generate_path(geometry_msgs::Point start)
Generate an optimal coverage path for a given area.
Subscriber subscribe(const std::string &topic, uint32_t queue_size, void(T::*fp)(M), T *obj, const TransportHints &transport_hints=TransportHints())
void init(const M_string &remappings)
void reduce()
Remove waypoints that are within straight line segments of the path. Only keep turning points of the ...
Publisher path_publisher
Publisher to visualize the coverage path.
bool call(MReq &req, MRes &res)
bool turning_points
Whether there are only waypoints at turning points of the path or also waypoints regularly spaced on ...
#define ROS_DEBUG_ONCE(...)
bool swarm_path
Whether to regnerate the path when new CPSs join or leave the swarm.
vector< edge > get_mst_edges()
Get the edges of the MST.
ServiceServer advertiseService(const std::string &service, bool(T::*srv_func)(MReq &, MRes &), T *obj)
Publisher mst_publisher
Publisher to visualize the minimum spanning tree.
void rotate(nav_msgs::OccupancyGrid &map, double angle)
Rotate an occupancy grid map so the lower boundary is horizontal.
double resolution
The grid map underlying the path planning will be downsampled to this resolution in meter / cell...
geometry_msgs::Point get_waypoint(geometry_msgs::Point position, double tolerance)
Get the current waypoint and possibly select next waypoint, if close enough.
ServiceClient area_getter
Service client to get coordinates of the area to cover.
bool waitForExistence(ros::Duration timeout=ros::Duration(-1))
double swarm_timeout
The time in seconds communication in the swarm can be delayed at most. Used to wait after an area div...
bool param(const std::string ¶m_name, T ¶m_val, const T &default_val) const
Publisher wp_publisher
Publisher to visualize the current waypoint.
void downsample(nav_msgs::OccupancyGrid &map)
Decrease the resolution of a occupancy grid map.
void swarm_state_callback(const cpswarm_msgs::ArrayOfStates::ConstPtr &msg)
Callback function to receive the states of the other CPSs.
Publisher advertise(const std::string &topic, uint32_t queue_size, bool latch=false)
Subscriber map_subscriber
Service client to get the assigned area.
bool vertical
Whether the sweeping pattern is vertical or horizontal.
void analyze_area(vector< geometry_msgs::Point > coords, geometry_msgs::Point &origin, double &rotation, double &width, double &height)
Determine properties of an area.
vector< string > behaviors
The behavior states in which CPSs are considered part of the swarm.
void initialize_graph(nav_msgs::OccupancyGrid gridmap, bool vertical=false, bool connect4=true)
Initialize the internal tree structure from a given grid map.
bool visualize
Whether to publish the coverage path on a topic for visualization.
spanning_tree tree
The minimum-spanning-tree (MST) that defines the coverage path.
mst_path path
The coverage path.
geometry_msgs::PoseArray get_tree()
Get the generated MST for visualization.
bool reconfigure
Whether the swarm configuration has changed which requires a replanning of the path.
bool getParam(const std::string &key, std::string &s) const
map< string, Time > swarm
The UUIDs of the other swarm members.
void initialize_graph(nav_msgs::OccupancyGrid gridmap, bool vertical=false, bool connect4=true)
Initialize the internal graph structure that represents the area division.
bool map_valid
Whether a valid grid map has been received.
void area_callback(const nav_msgs::OccupancyGrid::ConstPtr &msg)
Callback function to receive the grid map.
bool valid()
Check whether the current waypoint index is valid.
void initialize_tree(vector< edge > mst)
Remove edges of the graph that overlap with the tree.
ROSCPP_DECL void spinOnce()
bool divide_area
Whether to divide the area among the CPSs before generating the path or to generate the path on the c...
int main(int argc, char **argv)
A ROS node that computes the optimal paths for area coverage with a swarm of CPSs.
nav_msgs::Path get_path()
Get the complete path.
bool generate_path(geometry_msgs::Point start)
Generate all way points for the path.