diff --git a/include/publisher_laser_map.h b/include/publisher_laser_map.h index 98d26ea8ddd083d4467013bec89864af8ed4d067..c0919820f4372e195f305e2568c3d7366c5457c8 100644 --- a/include/publisher_laser_map.h +++ b/include/publisher_laser_map.h @@ -24,6 +24,14 @@ using namespace wolf; +struct ScanData +{ + CaptureLaser2dPtr capture_; + laserscanutils::LaserScanParams params_; + Eigen::MatrixXf local_points_; + pcl::PointCloud<pcl::PointXYZRGB> local_pc_; +}; + class PublisherLaserMap: public Publisher { protected: @@ -34,8 +42,7 @@ class PublisherLaserMap: public Publisher double Lfree_, Lobst_, Lobst_th_, Lfree_th_; int max_n_cells_; Eigen::Vector2i n_cells_; - double grid_size_, laser_ray_incr_; - int n_obstacle_samples_; + double grid_size_; bool resized_; bool discard_max_range_; Eigen::Vector2d map_origin_; @@ -49,7 +56,7 @@ class PublisherLaserMap: public Publisher int pc_r_, pc_g_, pc_b_; // std::maps - std::map<FrameBasePtr,std::list<CaptureLaser2dPtr>> scans_; + std::map<FrameBasePtr,std::list<ScanData>> scans_; std::map<FrameBasePtr,Eigen::Vector3d> current_trajectory_, last_trajectory_occ_, last_trajectory_pc_; @@ -67,21 +74,36 @@ class PublisherLaserMap: public Publisher void updateTrajectory(); bool trajectoryChanged(const std::map<FrameBasePtr,Eigen::Vector3d>& _last_trajectory, const std::map<FrameBasePtr,Eigen::Vector3d>& _current_trajectory) const; + void storeScan(FrameBasePtr frame, CaptureLaser2dPtr capture); // occmap void resetOccMap(); bool updateOccMap(); - void addScanToOccMap(const CaptureLaser2dPtr& capture_laser, const Eigen::Vector3d& pose); - void addRayToOccMap(const double& theta, const double& range, const Eigen::Vector3d& laser_pose, bool _obstacle); - Eigen::Vector2i vectorToCell(const Eigen::Vector2d& p); - void resizeOccMap(const int& dim, const unsigned int& oversize, const bool& back); + void addScanToOccMap(const ScanData& scan_data, const Eigen::Vector3d& pose); + template<typename T> + Eigen::Vector2i vectorToCell(const Eigen::DenseBase<T>& p); + void drawFreeLine(const Eigen::Vector2i& cell_0, const Eigen::Vector2i& cell_1); + void resizeOccMap(const Eigen::Vector4i& oversize); void updateOccMsg(); void resizeOccMsg(); // pointcloud void resetPcMap(); bool updatePcMap(); - void addScanToPcMap(const CaptureLaser2dPtr& capture_laser, const Eigen::Vector3d& pose); + void addScanToPcMap(const ScanData& scan_data, const Eigen::Vector3d& pose); void updatePcMsg(); }; + + +template<typename T> +inline Eigen::Vector2i PublisherLaserMap::vectorToCell(const Eigen::DenseBase<T>& p) +{ + MatrixSizeCheck<2, 1>::check(p); + Eigen::Vector2i cell; + cell(0) = int( std::floor((p(0) - map_origin_(0)) / grid_size_ )); + cell(1) = int( std::floor((p(1) - map_origin_(1)) / grid_size_ )); + + return cell; +} + #endif diff --git a/src/publisher_laser_map.cpp b/src/publisher_laser_map.cpp index d173c05977e6d61bf70be59acf3eab177e99657b..288ba6c586af48a8b3864000ca0b792002e0d150 100644 --- a/src/publisher_laser_map.cpp +++ b/src/publisher_laser_map.cpp @@ -20,36 +20,24 @@ PublisherLaserMap::PublisherLaserMap(const std::string& _unique_name, agg_pc_(0,0) { // common params - update_dist_th_ = _server.getParam<double> (prefix_ + "/update_dist_th"); - update_angle_th_ = _server.getParam<double> (prefix_ + "/update_angle_th"); + update_dist_th_ = _server.getParam<double> (prefix_ + "/update_dist_th"); + update_angle_th_ = _server.getParam<double> (prefix_ + "/update_angle_th"); // occmap params - occ_msg_.header.frame_id = _server.getParam<std::string> (prefix_ + "/map_frame_id"); - max_n_cells_ = _server.getParam<int> (prefix_ + "/max_n_cells"); - grid_size_ = _server.getParam<double> (prefix_ + "/grid_size"); - laser_ray_incr_ = _server.getParam<double> (prefix_ + "/laser_ray_incr"); - n_obstacle_samples_ = _server.getParam<int> (prefix_ + "/n_obstacle_samples"); - discard_max_range_ = _server.getParam<bool> (prefix_ + "/discard_max_range"); - double p_free = _server.getParam<double> (prefix_ + "/p_free"); - double p_obst = _server.getParam<double> (prefix_ + "/p_obst"); - double p_free_th = _server.getParam<double> (prefix_ + "/p_free_th"); - double p_obst_th = _server.getParam<double> (prefix_ + "/p_obst_th"); + occ_msg_.header.frame_id = _server.getParam<std::string> (prefix_ + "/map_frame_id"); + max_n_cells_ = _server.getParam<int> (prefix_ + "/max_n_cells"); + grid_size_ = _server.getParam<double> (prefix_ + "/grid_size"); + discard_max_range_ = _server.getParam<bool> (prefix_ + "/discard_max_range"); + double p_free = _server.getParam<double> (prefix_ + "/p_free"); + double p_obst = _server.getParam<double> (prefix_ + "/p_obst"); + double p_free_th = _server.getParam<double> (prefix_ + "/p_free_th"); + double p_obst_th = _server.getParam<double> (prefix_ + "/p_obst_th"); // pointcloud params - pc_msg_.header.frame_id = _server.getParam<std::string> (prefix_ + "/map_frame_id"); - try - { - pc_r_ = (int)(_server.getParam<double> (prefix_ + "/pointcloud_color_r") * 255.0); - pc_g_ = (int)(_server.getParam<double> (prefix_ + "/pointcloud_color_g") * 255.0); - pc_b_ = (int)(_server.getParam<double> (prefix_ + "/pointcloud_color_b") * 255.0); - } - catch(...) - { - WOLF_INFO("PublisherLaserMap: taking default pointcloud colors..."); - pc_r_ = 100; - pc_g_ = 200; - pc_b_ = 50; - } + pc_msg_.header.frame_id = _server.getParam<std::string> (prefix_ + "/map_frame_id"); + pc_r_ = (int)(getParamWithDefault<double> (_server, prefix_ + "/pointcloud_color_r", 100) * 255.0); + pc_g_ = (int)(getParamWithDefault<double> (_server, prefix_ + "/pointcloud_color_g", 200) * 255.0); + pc_b_ = (int)(getParamWithDefault<double> (_server, prefix_ + "/pointcloud_color_b", 50) * 255.0); // preprocessing Lfree_ = log(p_free / (1 - p_free)); @@ -124,7 +112,7 @@ void PublisherLaserMap::updateTrajectory() // insert frame to maps current_trajectory_[frame_pair.second] = frame_pair.second->getState("PO").vector("PO"); - scans_[frame_pair.second] = std::list<CaptureLaser2dPtr>(); + scans_[frame_pair.second] = std::list<ScanData>(); // search all CaptureLaser2d auto cap_list = frame_pair.second->getCaptureList(); @@ -132,9 +120,7 @@ void PublisherLaserMap::updateTrajectory() { auto cap_laser = std::dynamic_pointer_cast<CaptureLaser2d>(cap); if (cap_laser and not cap_laser->isRemoving()) - { - scans_.at(frame_pair.second).push_back(cap_laser); - } + storeScan(frame_pair.second, cap_laser); } // remove frames without scans @@ -145,6 +131,55 @@ void PublisherLaserMap::updateTrajectory() } } } + +void PublisherLaserMap::storeScan(FrameBasePtr frame, CaptureLaser2dPtr capture) +{ + ScanData scan_data; + scan_data.capture_ = capture; + scan_data.params_ = std::static_pointer_cast<SensorLaser2d>(capture->getSensor())->getScanParams(); + + // local points + Eigen::Vector2d laser_extrinsics_p = capture->getSensor()->getP()->getState(); + double laser_extrinsics_o = capture->getSensor()->getO()->getState()(0); + + Eigen::Map<const Eigen::ArrayXf> ranges(capture->getScan().ranges_raw_.data(), capture->getScan().ranges_raw_.size()); + + Eigen::ArrayXf orientations = Eigen::ArrayXf::LinSpaced(ranges.size(), + scan_data.params_.angle_min_ + laser_extrinsics_o, + scan_data.params_.angle_max_ + laser_extrinsics_o); + + scan_data.local_points_.resize(2,ranges.size()); + if (discard_max_range_) + { + scan_data.local_points_.row(0) = orientations.cos() * (ranges < scan_data.params_.range_max_ -0.1).select(ranges, 0) + laser_extrinsics_p(0); + scan_data.local_points_.row(1) = orientations.sin() * (ranges < scan_data.params_.range_max_ -0.1).select(ranges, 0) + laser_extrinsics_p(1); + } + else + { + scan_data.local_points_.row(0) = orientations.cos() * ranges + laser_extrinsics_p(0); + scan_data.local_points_.row(1) = orientations.sin() * ranges + laser_extrinsics_p(1); + } + + // local pointcloud + scan_data.local_pc_ = pcl::PointCloud<pcl::PointXYZRGB>(ranges.size(), + 1, + pcl::PointXYZRGB(pc_r_,pc_g_,pc_b_)); + for (auto i=0; i < ranges.size(); i++) + { + if (ranges[i] > scan_data.params_.range_max_ - 0.1) + scan_data.local_pc_.at(i).x = scan_data.local_pc_.at(i).y = scan_data.local_pc_.at(i).z = std::numeric_limits<float>::quiet_NaN(); + else + { + scan_data.local_pc_.at(i).x = scan_data.local_points_(0,i); + scan_data.local_pc_.at(i).y = scan_data.local_points_(1,i); + scan_data.local_pc_.at(i).z = 0; + } + } + + // store data + scans_.at(frame).push_back(scan_data); +} + bool PublisherLaserMap::trajectoryChanged(const std::map<FrameBasePtr,Eigen::Vector3d>& _last_trajectory, const std::map<FrameBasePtr,Eigen::Vector3d>& _current_trajectory) const { @@ -202,8 +237,8 @@ bool PublisherLaserMap::updateOccMap() last_trajectory_occ_[frame_pose.first] = frame_pose.second; // add all scans of the frame the map - for (auto cap_laser : scans_.at(frame_pose.first)) - addScanToOccMap(cap_laser, frame_pose.second); + for (auto scan_data : scans_.at(frame_pose.first)) + addScanToOccMap(scan_data, frame_pose.second); updated = true; } @@ -212,141 +247,151 @@ bool PublisherLaserMap::updateOccMap() return updated; } -void PublisherLaserMap::addScanToOccMap(const CaptureLaser2dPtr& capture_laser, const Eigen::Vector3d& pose) +void PublisherLaserMap::addScanToOccMap(const ScanData& scan_data, const Eigen::Vector3d& pose) { - // scan pose (considering extrinsics) - Eigen::Vector3d laser_pose = pose; - laser_pose.head<2>() += Eigen::Rotation2Dd(pose(2)) * capture_laser->getSensor()->getP()->getState(); - laser_pose(2) += capture_laser->getSensor()->getO()->getState()(0); - - // Apply log odds for each beam - const std::vector<float>& ranges = capture_laser->getScan().ranges_raw_; - auto scan_params = std::static_pointer_cast<SensorLaser2d>(capture_laser->getSensor())->getScanParams(); - double theta = scan_params.angle_min_; - for (unsigned int i=0; i < ranges.size(); i++, theta+=scan_params.angle_step_) + // Compute global points + Eigen::MatrixXf points = Eigen::Rotation2Df(pose(2)).matrix() * scan_data.local_points_ + pose.cast<float>().head(2).replicate(1,scan_data.local_points_.cols()); + + // Bounds + Eigen::Vector2i min_cell = vectorToCell(Eigen::Vector2d(points.row(0).minCoeff(), + points.row(1).minCoeff())); + Eigen::Vector2i max_cell = vectorToCell(Eigen::Vector2d(points.row(0).maxCoeff(), + points.row(1).maxCoeff())); + + // Resize + Eigen::Vector4i oversize; + oversize << std::max(0, -min_cell(0)), + std::max(0, -min_cell(1)), + std::max(0, max_cell(0) - n_cells_(0) + 1), + std::max(0, max_cell(1) - n_cells_(1) + 1); + + if (oversize.maxCoeff() != 0) + { + WOLF_DEBUG("PublisherLaserMap::addScanToOccMap: resizing... " + "\nmin_cell: ", min_cell.transpose() , + "\nmax_cell: ", max_cell.transpose() , + "\noversize: ", oversize.transpose()); + resizeOccMap(oversize); + } + + // Apply log odds for each ray + Eigen::Vector2i origin_cell = vectorToCell(pose.head<2>()); + Eigen::Vector2i point_cell; + for (auto i=0; i < points.cols(); i++) { - assert(theta <= scan_params.angle_max_ + 1e-3 && "PublisherLaserMap::addScanToOccMap: Ray orientation higher than angle_max "); - if (ranges[i] > scan_params.range_min_ and - (ranges[i] < scan_params.range_max_ - 0.1 or not discard_max_range_)) - // Add the logodds ray - addRayToOccMap(theta, ranges[i], laser_pose, ranges[i] < scan_params.range_max_-0.1); + point_cell = vectorToCell(points.col(i)); + if (point_cell == origin_cell) + continue; + assert(point_cell(0) >= 0 and point_cell(0) < n_cells_(0)); + assert(point_cell(1) >= 0 and point_cell(1) < n_cells_(1)); + + // Free cells + drawFreeLine(origin_cell, point_cell); + + // Obstacle cell + logodds_array_(point_cell(0), point_cell(1)) += Lobst_ - Lfree_; // and undo last free } } -void PublisherLaserMap::addRayToOccMap(const double& theta, const double& range, const Eigen::Vector3d& laser_pose, bool _obstacle) +void PublisherLaserMap::drawFreeLine(const Eigen::Vector2i& cell_1, const Eigen::Vector2i& cell_2) { - double d = 0; - Eigen::Vector2d origin, point, displ; - - double orientation = theta + laser_pose(2); - origin = laser_pose.head<2>(); + // Bresenham's line algorithm inspired in https://github.com/JesusHF/bresenham/blob/master/src/main.cpp - displ(0) = cos(orientation) * laser_ray_incr_; - displ(1) = sin(orientation) * laser_ray_incr_; - point = origin;// + displ; + int dx, dy, x, y, d, sx, sy; + bool swap = false; - assert(std::isfinite(point(0)) && std::isfinite(point(1)) && "PublisherLaserMap::addRayToOccMap: Not finite point"); + dx = abs(cell_2(0) - cell_1(0)); + dy = abs(cell_2(1) - cell_1(1)); + sx = (cell_2(0) - cell_1(0)) > 0 ? 1 : -1; + sy = (cell_2(1) - cell_1(1)) > 0 ? 1 : -1; - // free cells log odds - Eigen::Vector2i cell_prev(-1,-1); + x = cell_1(0); + y = cell_1(1); - while (d < range) + /* Check if dx or dy has a greater range */ + /* if dy has a greater range than dx swap */ + if (dy > dx) { - Eigen::Vector2i cell = vectorToCell(point); + int temp = dx; + dx = dy; + dy = temp; - if (cell != cell_prev) - logodds_array_(cell(0), cell(1)) += Lfree_; + temp = sx; + sx = sy; + sy = temp; - cell_prev = cell; + temp = x; + x = y; + y = temp; - // next point - point += displ; - d += laser_ray_incr_; + swap = true; } - // obstacle cell log odd - if (_obstacle) + /* Set the initial decision parameter and the initial point */ + d = 2 * dy - dx; + + for (int i = 1; i <= dx; i++) { - double range_boundary = range; - for (auto j = 0; j < n_obstacle_samples_; j++, range_boundary += laser_ray_incr_) - { - point(0) = origin(0) + cos(orientation) * range_boundary; - point(1) = origin(1) + sin(orientation) * range_boundary; + if (swap) + logodds_array_(y, x) += Lfree_; + else + logodds_array_(x, y) += Lfree_; - Eigen::Vector2i cell = vectorToCell(point); - logodds_array_(cell(0), cell(1)) += Lobst_; + while (d >= 0) + { + y = y + sy; + d = d - 2 * dx; } + x = x + sx; + d = d + 2 * dy; } } -Eigen::Vector2i PublisherLaserMap::vectorToCell(const Eigen::Vector2d& p) +void PublisherLaserMap::resizeOccMap(const Eigen::Vector4i& oversize) { - Eigen::Vector2i cell; - cell(0) = int( std::floor((p(0) - map_origin_(0)) / grid_size_ )); - cell(1) = int( std::floor((p(1) - map_origin_(1)) / grid_size_ )); + WOLF_DEBUG("PublisherLaserMap::resizeOccMap: oversize = ", oversize.transpose(), + "\nCurrent size: ", n_cells_.transpose() , " cells | ", (grid_size_ * n_cells_.cast<double>()).transpose(), " m ", + "\nCurrent origin: ", map_origin_.transpose()); - for (unsigned int i = 0; i<2; i++) + assert(oversize.minCoeff() >= 0); + if (oversize.maxCoeff() == 0) { - int oversize = std::max( 0 - cell(i), cell(i) - n_cells_(i) + 1 ); - while ( oversize > 0 && n_cells_(i) < max_n_cells_ ) - { - bool back = !(cell(i) < 0); - ROS_DEBUG("PublisherLaserMap: Point out of the map in dimension %i: \n\tpoint = %f, %f\n\tlower = %f, %f\n\tupper = %f, %f\n\tcell = %i, %i\n\tn cells = %i, %i", i, - p(0), p(1), map_origin_(0), map_origin_(1), map_origin_(0) + grid_size_ * n_cells_(1), map_origin_(1) + grid_size_ * n_cells_(0), - cell(1), cell(0), n_cells_(1), n_cells_(0)); - resizeOccMap(i, oversize, back); - ROS_DEBUG("PublisherLaserMap: Map resized in dimension %i: new limits:\n\tlower = %f, %f\n\tupper = %f, %f\n\tn cells = %i, %i", i, - map_origin_(0), map_origin_(1), map_origin_(0) + grid_size_ * n_cells_(1), map_origin_(1) + grid_size_ * n_cells_(0), n_cells_(1), n_cells_(0)); - - // Recompute current cell and oversize - cell(i) = int( (p(i) - map_origin_(i)) / grid_size_ ); - oversize = std::max( 0 - cell(i), cell(i) - n_cells_(i) + 1 ); - } + ROS_WARN("called resizeOccMap with zero oversize!"); + return; } - assert(cell(0) >= 0 && cell(1) >= 0 && cell(0) < n_cells_(0) && cell(1) < n_cells_(1) && "Resultinc cell out of the map. Resizing whent wrong somehow..."); - return cell; -} + // 0 and 1 oversize (x, y) at the beginning + // 2 and 3 oversize (x, y) at the end -void PublisherLaserMap::resizeOccMap(const int& dim, const unsigned int& oversize, const bool& back) -{ - Eigen::Vector2i new_n_cells = n_cells_; - new_n_cells(dim) = n_cells_(dim) + oversize; + Eigen::Vector2i new_n_cells = n_cells_ + oversize.head<2>() + oversize.tail<2>(); + WOLF_DEBUG("Resizing to ", new_n_cells.transpose(), " cells"); // store old logodds array Eigen::ArrayXXd old_logodds_array = logodds_array_; + // resize with zeros logodds_array_ = Eigen::ArrayXXd::Zero(new_n_cells(0), new_n_cells(1)); - // Adding at the end of the array - if (back) - { - // Origin doesn't change - // Copy old values - logodds_array_.block(0, 0, n_cells_(0), n_cells_(1)) = old_logodds_array; - } - // Adding at the beggining of the array - else - { - // Change origin - map_origin_(dim) -= oversize * grid_size_; - // Copy old values - logodds_array_.block((dim == 0 ? oversize : 0), - (dim == 1 ? oversize : 0), - n_cells_(0), - n_cells_(1)) = old_logodds_array; - } + // Copy back the old array + logodds_array_.block(oversize(0), oversize(1), n_cells_(0), n_cells_(1)) = old_logodds_array; + + // Change origin + map_origin_(0) -= oversize(0) * grid_size_; + map_origin_(1) -= oversize(1) * grid_size_; + // Change in the occupancy grid msg n_cells_ = new_n_cells; resized_= true; + WOLF_INFO("PublisherLaserMap::resizeOccMap: resized occupancy map!", + "\nCurrent size: ", n_cells_.transpose() , " cells | ", (grid_size_ * n_cells_.cast<double>()).transpose(), " m ", + "\nCurrent origin: ", map_origin_.transpose()); + } void PublisherLaserMap::updateOccMsg() { Eigen::ArrayXXd occupancy_probability(Eigen::ArrayXXd::Ones(n_cells_(0), n_cells_(1))); - //std::cout << logodds_array_<< std::endl; - occupancy_probability = (logodds_array_ >= Lobst_th_).select(100,occupancy_probability); occupancy_probability = (logodds_array_ <= Lfree_th_).select(0,occupancy_probability); occupancy_probability = (logodds_array_ < Lobst_th_ && logodds_array_ > Lfree_th_).select(-1,occupancy_probability); @@ -421,31 +466,17 @@ bool PublisherLaserMap::updatePcMap() } -void PublisherLaserMap::addScanToPcMap(const CaptureLaser2dPtr& capture_laser, const Eigen::Vector3d& pose) +void PublisherLaserMap::addScanToPcMap(const ScanData& scan_data, const Eigen::Vector3d& pose) { - // scan global pose (considering extrinsics) - Eigen::Vector3d laser_pose = pose; - laser_pose.head<2>() += Eigen::Rotation2Dd(pose(2)) * capture_laser->getSensor()->getP()->getState(); - laser_pose(2) += capture_laser->getSensor()->getO()->getState()(0); - - // get scan necessary data - const std::vector<float>& ranges = capture_laser->getScan().ranges_raw_; - auto scan_params = std::static_pointer_cast<SensorLaser2d>(capture_laser->getSensor())->getScanParams(); - - // Generate scan pointcloud - pcl::PointCloud<pcl::PointXYZRGB> scan_pc(ranges.size(), 1, pcl::PointXYZRGB(pc_r_,pc_g_,pc_b_)); - double theta = laser_pose(2) + scan_params.angle_min_; - for (auto i=0; i < ranges.size(); i++, theta+=scan_params.angle_step_) - { - if (ranges[i] > scan_params.range_max_ - 0.1) - scan_pc.at(i).x = scan_pc.at(i).y = scan_pc.at(i).z = std::numeric_limits<float>::quiet_NaN(); - else - { - scan_pc.at(i).x = laser_pose(0) + cos(theta) * ranges[i]; - scan_pc.at(i).y = laser_pose(1) + sin(theta) * ranges[i]; - scan_pc.at(i).z = 0; - } - } + // transformation + Eigen::Matrix4d T = Eigen::Matrix4d::Identity(); + T.topLeftCorner<2,2>() = Eigen::Rotation2Dd(pose(2)).matrix(); + T.topRightCorner<2,1>() = pose.head<2>(); + WOLF_DEBUG("addScanToPcMap T:\n", T); + + // global scan pointcloud + pcl::PointCloud<pcl::PointXYZRGB> scan_pc(scan_data.local_pc_); + pcl::transformPointCloud (scan_data.local_pc_, scan_pc, T); // Aggregate scan agg_pc_ += scan_pc;