void BallPickerLayer::updateBounds(double origin_x, double origin_y, double origin_yaw, double* min_x, double* min_y, double* max_x, double* max_y) { if (!enabled_) return; if (layered_costmap_->isRolling()) updateOrigin(origin_x - getSizeInMetersX() / 2, origin_y - getSizeInMetersY() / 2); double mark_x, mark_y; unsigned int mx, my; unsigned int index; if (clear_flag) { *min_x = *min_x - getSizeInMetersX()/2; *min_y = *min_y - getSizeInMetersY()/2; *max_x = *max_x + getSizeInMetersX()/2; *max_y = *max_y + getSizeInMetersY()/2; } for (std::vector<ball_picker::PointOfInterest>::iterator iter = obstacle_buffer.begin() ; iter != obstacle_buffer.end(); iter++) { mark_x = iter->x; mark_y = iter->y; if(worldToMap(mark_x, mark_y, mx, my)) { index = getIndex(mx, my); costmap_[index] = LETHAL_OBSTACLE; } *min_x = std::min(*min_x, mark_x); *min_y = std::min(*min_y, mark_y); *max_x = std::max(*max_x, mark_x); *max_y = std::max(*max_y, mark_y); } }
void VoxelLayer::clearNonLethal(double wx, double wy, double w_size_x, double w_size_y, bool clear_no_info) { //get the cell coordinates of the center point of the window unsigned int mx, my; if (!worldToMap(wx, wy, mx, my)) return; //compute the bounds of the window double start_x = wx - w_size_x / 2; double start_y = wy - w_size_y / 2; double end_x = start_x + w_size_x; double end_y = start_y + w_size_y; //scale the window based on the bounds of the costmap start_x = std::max(origin_x_, start_x); start_y = std::max(origin_y_, start_y); end_x = std::min(origin_x_ + getSizeInMetersX(), end_x); end_y = std::min(origin_y_ + getSizeInMetersY(), end_y); //get the map coordinates of the bounds of the window unsigned int map_sx, map_sy, map_ex, map_ey; //check for legality just in case if (!worldToMap(start_x, start_y, map_sx, map_sy) || !worldToMap(end_x, end_y, map_ex, map_ey)) return; //we know that we want to clear all non-lethal obstacles in this window to get it ready for inflation unsigned int index = getIndex(map_sx, map_sy); unsigned char* current = &costmap_[index]; for (unsigned int j = map_sy; j <= map_ey; ++j) { for (unsigned int i = map_sx; i <= map_ex; ++i) { //if the cell is a lethal obstacle... we'll keep it and queue it, otherwise... we'll clear it if (*current != LETHAL_OBSTACLE) { if (clear_no_info || *current != NO_INFORMATION) { *current = FREE_SPACE; voxel_grid_.clearVoxelColumn(index); } } current++; index++; } current += size_x_ - (map_ex - map_sx) - 1; index += size_x_ - (map_ex - map_sx) - 1; } }
void Costmap2D::resetInflationWindow(double wx, double wy, double w_size_x, double w_size_y, priority_queue<CellData>& inflation_queue, bool clear){ //get the cell coordinates of the center point of the window unsigned int mx, my; if(!worldToMap(wx, wy, mx, my)) return; //compute the bounds of the window double start_x = wx - w_size_x / 2; double start_y = wy - w_size_y / 2; double end_x = start_x + w_size_x; double end_y = start_y + w_size_y; //scale the window based on the bounds of the costmap start_x = max(origin_x_, start_x); start_y = max(origin_y_, start_y); end_x = min(origin_x_ + getSizeInMetersX(), end_x); end_y = min(origin_y_ + getSizeInMetersY(), end_y); //get the map coordinates of the bounds of the window unsigned int map_sx, map_sy, map_ex, map_ey; //check for legality just in case if(!worldToMap(start_x, start_y, map_sx, map_sy) || !worldToMap(end_x, end_y, map_ex, map_ey)){ ROS_ERROR("Bounds not legal for reset window. Doing nothing."); return; } //we know that we want to clear all non-lethal obstacles in this window to get it ready for inflation unsigned int index = getIndex(map_sx, map_sy); unsigned char* current = &costmap_[index]; for(unsigned int j = map_sy; j <= map_ey; ++j){ for(unsigned int i = map_sx; i <= map_ex; ++i){ //if the cell is a lethal obstacle... we'll keep it and queue it, otherwise... we'll clear it if(*current == LETHAL_OBSTACLE) enqueue(index, i, j, i, j, inflation_queue); else if(clear && *current != NO_INFORMATION) *current = FREE_SPACE; current++; index++; } current += size_x_ - (map_ex - map_sx) - 1; index += size_x_ - (map_ex - map_sx) - 1; } }
void Costmap2D::resetMapOutsideWindow(double wx, double wy, double w_size_x, double w_size_y){ ROS_ASSERT_MSG(w_size_x >= 0 && w_size_y >= 0, "You cannot specify a negative size window"); double start_point_x = wx - w_size_x / 2; double start_point_y = wy - w_size_y / 2; double end_point_x = start_point_x + w_size_x; double end_point_y = start_point_y + w_size_y; //check start bounds start_point_x = max(origin_x_, start_point_x); start_point_y = max(origin_y_, start_point_y); //check end bounds end_point_x = min(origin_x_ + getSizeInMetersX(), end_point_x); end_point_y = min(origin_y_ + getSizeInMetersY(), end_point_y); unsigned int start_x, start_y, end_x, end_y; //check for legality just in case if(!worldToMap(start_point_x, start_point_y, start_x, start_y) || !worldToMap(end_point_x, end_point_y, end_x, end_y)) return; ROS_ASSERT(end_x >= start_x && end_y >= start_y); unsigned int cell_size_x = end_x - start_x; unsigned int cell_size_y = end_y - start_y; //we need a map to store the obstacles in the window temporarily unsigned char* local_map = new unsigned char[cell_size_x * cell_size_y]; //copy the local window in the costmap to the local map copyMapRegion(costmap_, start_x, start_y, size_x_, local_map, 0, 0, cell_size_x, cell_size_x, cell_size_y); //now we'll reset the costmap to the static map memcpy(costmap_, static_map_, size_x_ * size_y_ * sizeof(unsigned char)); //now we want to copy the local map back into the costmap copyMapRegion(local_map, 0, 0, cell_size_x, costmap_, start_x, start_y, size_x_, cell_size_x, cell_size_y); //clean up delete[] local_map; }
void Costmap2D::raytraceFreespace(const Observation& clearing_observation){ //create the functor that we'll use to clear cells from the costmap ClearCell clearer(costmap_); double ox = clearing_observation.origin_.x; double oy = clearing_observation.origin_.y; sensor_msgs::PointCloud cloud = clearing_observation.cloud_; //get the map coordinates of the origin of the sensor unsigned int x0, y0; if(!worldToMap(ox, oy, x0, y0)){ ROS_WARN("The origin for the sensor at (%.2f, %.2f) is out of map bounds. So, the costmap cannot raytrace for it.", ox, oy); return; } //we can pre-compute the enpoints of the map outside of the inner loop... we'll need these later double map_end_x = origin_x_ + getSizeInMetersX(); double map_end_y = origin_y_ + getSizeInMetersY(); //for each point in the cloud, we want to trace a line from the origin and clear obstacles along it for(unsigned int i = 0; i < cloud.points.size(); ++i){ double wx = cloud.points[i].x; double wy = cloud.points[i].y; //now we also need to make sure that the enpoint we're raytracing //to isn't off the costmap and scale if necessary double a = wx - ox; double b = wy - oy; //the minimum value to raytrace from is the origin if(wx < origin_x_){ double t = (origin_x_ - ox) / a; wx = origin_x_; wy = oy + b * t; } if(wy < origin_y_){ double t = (origin_y_ - oy) / b; wx = ox + a * t; wy = origin_y_; } //the maximum value to raytrace to is the end of the map if(wx > map_end_x){ double t = (map_end_x - ox) / a; wx = map_end_x; wy = oy + b * t; } if(wy > map_end_y){ double t = (map_end_y - oy) / b; wx = ox + a * t; wy = map_end_y; } //now that the vector is scaled correctly... we'll get the map coordinates of its endpoint unsigned int x1, y1; //check for legality just in case if(!worldToMap(wx, wy, x1, y1)) continue; unsigned int cell_raytrace_range = cellDistance(clearing_observation.raytrace_range_); //and finally... we can execute our trace to clear obstacles along that line raytraceLine(clearer, x0, y0, x1, y1, cell_raytrace_range); } }
void VoxelLayer::raytraceFreespace(const Observation& clearing_observation, double* min_x, double* min_y, double* max_x, double* max_y) { if (clearing_observation.cloud_->points.size() == 0) return; double sensor_x, sensor_y, sensor_z; double ox = clearing_observation.origin_.x; double oy = clearing_observation.origin_.y; double oz = clearing_observation.origin_.z; if (!worldToMap3DFloat(ox, oy, oz, sensor_x, sensor_y, sensor_z)) { ROS_WARN_THROTTLE( 1.0, "The origin for the sensor at (%.2f, %.2f, %.2f) is out of map bounds. So, the costmap cannot raytrace for it.", ox, oy, oz); return; } bool publish_clearing_points = (clearing_endpoints_pub_.getNumSubscribers() > 0); if( publish_clearing_points ) { clearing_endpoints_.points.clear(); clearing_endpoints_.points.reserve( clearing_observation.cloud_->points.size() ); } //we can pre-compute the enpoints of the map outside of the inner loop... we'll need these later double map_end_x = origin_x_ + getSizeInMetersX(); double map_end_y = origin_y_ + getSizeInMetersY(); for (unsigned int i = 0; i < clearing_observation.cloud_->points.size(); ++i) { double wpx = clearing_observation.cloud_->points[i].x; double wpy = clearing_observation.cloud_->points[i].y; double wpz = clearing_observation.cloud_->points[i].z; double distance = dist(ox, oy, oz, wpx, wpy, wpz); double scaling_fact = 1.0; scaling_fact = std::max(std::min(scaling_fact, (distance - 2 * resolution_) / distance), 0.0); wpx = scaling_fact * (wpx - ox) + ox; wpy = scaling_fact * (wpy - oy) + oy; wpz = scaling_fact * (wpz - oz) + oz; double a = wpx - ox; double b = wpy - oy; double c = wpz - oz; double t = 1.0; //we can only raytrace to a maximum z height if (wpz > max_obstacle_height_) { //we know we want the vector's z value to be max_z t = std::max(0.0, std::min(t, (max_obstacle_height_ - 0.01 - oz) / c)); } //and we can only raytrace down to the floor else if (wpz < origin_z_) { //we know we want the vector's z value to be 0.0 t = std::min(t, (origin_z_ - oz) / c); } //the minimum value to raytrace from is the origin if (wpx < origin_x_) { t = std::min(t, (origin_x_ - ox) / a); } if (wpy < origin_y_) { t = std::min(t, (origin_y_ - oy) / b); } //the maximum value to raytrace to is the end of the map if (wpx > map_end_x) { t = std::min(t, (map_end_x - ox) / a); } if (wpy > map_end_y) { t = std::min(t, (map_end_y - oy) / b); } wpx = ox + a * t; wpy = oy + b * t; wpz = oz + c * t; double point_x, point_y, point_z; if (worldToMap3DFloat(wpx, wpy, wpz, point_x, point_y, point_z)) { unsigned int cell_raytrace_range = cellDistance(clearing_observation.raytrace_range_); //voxel_grid_.markVoxelLine(sensor_x, sensor_y, sensor_z, point_x, point_y, point_z); voxel_grid_.clearVoxelLineInMap(sensor_x, sensor_y, sensor_z, point_x, point_y, point_z, costmap_, unknown_threshold_, mark_threshold_, FREE_SPACE, NO_INFORMATION, cell_raytrace_range); updateRaytraceBounds(ox, oy, wpx, wpy, clearing_observation.raytrace_range_, min_x, min_y, max_x, max_y); if( publish_clearing_points ) { geometry_msgs::Point32 point; point.x = wpx; point.y = wpy; point.z = wpz; clearing_endpoints_.points.push_back( point ); } } } if( publish_clearing_points ) { clearing_endpoints_.header.frame_id = global_frame_; clearing_endpoints_.header.stamp = pcl_conversions::fromPCL(clearing_observation.cloud_->header).stamp; clearing_endpoints_.header.seq = clearing_observation.cloud_->header.seq; clearing_endpoints_pub_.publish( clearing_endpoints_ ); } }
void VoxelLayer::updateBounds(double robot_x, double robot_y, double robot_yaw, double* min_x, double* min_y, double* max_x, double* max_y) { if (rolling_window_) updateOrigin(robot_x - getSizeInMetersX() / 2, robot_y - getSizeInMetersY() / 2); if (!enabled_) return; useExtraBounds(min_x, min_y, max_x, max_y); bool current = true; std::vector<ObservationSet> observations; std::vector<Observation> clearing_observations; if(clear_old_){ resetOldCosts(min_x, min_y, max_x, max_y); } //get the marking observations current = current && getMarkingObservations(observations); //get the clearing observations current = current && getClearingObservations(clearing_observations); //update the global current status current_ = current; //raytrace freespace for (unsigned int i = 0; i < clearing_observations.size(); ++i) { raytraceFreespace(clearing_observations[i], min_x, min_y, max_x, max_y); } //place the new obstacles into a priority queue... each with a priority of zero to begin with for (std::vector<ObservationSet>::const_iterator it = observations.begin(); it != observations.end(); ++it) { const ObservationSet& obs_set = *it; //check if this is in the max_timeout list bool max_timeout = false; if(observation_timeout.find(obs_set.topic)!= observation_timeout.end()){ max_timeout = true; ROS_DEBUG("Observation set for topic %s - clearing\n", obs_set.topic.c_str()); } for(std::vector<Observation>::const_iterator it_o = obs_set.marking_observations.begin(); it_o != obs_set.marking_observations.end(); it_o++){ const Observation& obs = *it_o; //we should throw out stale observations also CostMapList cm_list; cm_list.topic = obs_set.topic; cm_list.obs_timestamp = obs.cloud_->header.stamp; double obs_ts = cm_list.obs_timestamp / 1.0e6; const pcl::PointCloud<pcl::PointXYZ>& cloud = *(obs.cloud_); double sq_obstacle_range = obs.obstacle_range_ * obs.obstacle_range_; int count = 0; for (unsigned int i = 0; i < cloud.points.size(); ++i) { //if the obstacle is too high or too far away from the robot we won't add it if (cloud.points[i].z > max_obstacle_height_) continue; //compute the squared distance from the hitpoint to the pointcloud's origin double sq_dist = (cloud.points[i].x - obs.origin_.x) * (cloud.points[i].x - obs.origin_.x) + (cloud.points[i].y - obs.origin_.y) * (cloud.points[i].y - obs.origin_.y) + (cloud.points[i].z - obs.origin_.z) * (cloud.points[i].z - obs.origin_.z); //if the point is far enough away... we won't consider it if (sq_dist >= sq_obstacle_range) continue; //now we need to compute the map coordinates for the observation unsigned int mx, my, mz; if (cloud.points[i].z < origin_z_) { if (!worldToMap3D(cloud.points[i].x, cloud.points[i].y, origin_z_, mx, my, mz)) continue; } else if (!worldToMap3D(cloud.points[i].x, cloud.points[i].y, cloud.points[i].z, mx, my, mz)) { continue; } //mark the cell in the voxel grid and check if we should also mark it in the costmap if (voxel_grid_.markVoxelInMap(mx, my, mz, mark_threshold_)) { unsigned int index = getIndex(mx, my); //these are the ones set as occupied costmap_[index] = LETHAL_OBSTACLE; touch((double)cloud.points[i].x, (double)cloud.points[i].y, min_x, min_y, max_x, max_y); //keep track of which indices we updated if(max_timeout){ cm_list.indices.push_back(ObstaclePoint(index, (double)cloud.points[i].x, (double)cloud.points[i].y)); count++; } } } if(max_timeout && count > 0){ locations_utime.updateObstacleTime(cm_list); new_obs_list.push_back(cm_list); } } } if(clear_old_){ //inflate the bounds - otherwise the inflation layer wont reset the inflated cost *min_x -= inflation_radius_; *min_y -= inflation_radius_; *max_x += inflation_radius_; *max_y += inflation_radius_; } if (publish_voxel_) { costmap_2d::VoxelGrid grid_msg; unsigned int size = voxel_grid_.sizeX() * voxel_grid_.sizeY(); grid_msg.size_x = voxel_grid_.sizeX(); grid_msg.size_y = voxel_grid_.sizeY(); grid_msg.size_z = voxel_grid_.sizeZ(); grid_msg.data.resize(size); memcpy(&grid_msg.data[0], voxel_grid_.getData(), size * sizeof(unsigned int)); grid_msg.origin.x = origin_x_; grid_msg.origin.y = origin_y_; grid_msg.origin.z = origin_z_; grid_msg.resolutions.x = resolution_; grid_msg.resolutions.y = resolution_; grid_msg.resolutions.z = z_resolution_; grid_msg.header.frame_id = global_frame_; grid_msg.header.stamp = ros::Time::now(); voxel_pub_.publish(grid_msg); } footprint_layer_.updateBounds(robot_x, robot_y, robot_yaw, min_x, min_y, max_x, max_y); }