00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036 #define BOOST_PARAMETER_MAX_ARITY 7
00037
00038 #include "jsk_pcl_ros/heightmap_time_accumulation.h"
00039 #include "jsk_pcl_ros/tf_listener_singleton.h"
00040 #include <cv_bridge/cv_bridge.h>
00041 #include <sensor_msgs/image_encodings.h>
00042 #include <pcl/common/transforms.h>
00043
00044 namespace jsk_pcl_ros
00045 {
00046 void HeightmapTimeAccumulation::onInit()
00047 {
00048 ConnectionBasedNodelet::onInit();
00049 pub_config_ = pnh_->advertise<jsk_recognition_msgs::HeightmapConfig>(
00050 "output/config", 1, true);
00051 sub_config_ = pnh_->subscribe(
00052 getHeightmapConfigTopic(pnh_->resolveName("input")), 1,
00053 &HeightmapTimeAccumulation::configCallback, this);
00054 if (!pnh_->getParam("center_frame_id", center_frame_id_)) {
00055 JSK_NODELET_FATAL("no ~center_frame_id is specified");
00056 return;
00057 }
00058 if (!pnh_->getParam("fixed_frame_id", fixed_frame_id_)) {
00059 JSK_NODELET_FATAL("no ~fixed_frame_id is specified");
00060 return;
00061 }
00062 int tf_queue_size;
00063 pnh_->param("tf_queue_size", tf_queue_size, 10);
00064 prev_from_center_to_fixed_ = Eigen::Affine3f::Identity();
00065 tf_ = TfListenerSingleton::getInstance();
00066 pub_output_ = pnh_->advertise<sensor_msgs::Image>("output", 1, true);
00067 sub_previous_pointcloud_ = pnh_->subscribe<sensor_msgs::PointCloud2>(
00068 "input/prev_pointcloud", 5,
00069 &HeightmapTimeAccumulation::prevPointCloud, this);
00070 sub_heightmap_.subscribe(*pnh_, "input", 10);
00071 tf_filter_.reset(new tf::MessageFilter<sensor_msgs::Image>(
00072 sub_heightmap_,
00073 *tf_,
00074 fixed_frame_id_,
00075 tf_queue_size));
00076 tf_filter_->registerCallback(
00077 boost::bind(
00078 &HeightmapTimeAccumulation::accumulate, this, _1));
00079 srv_reset_ = pnh_->advertiseService("reset", &HeightmapTimeAccumulation::resetCallback, this);
00080 }
00081
00082 void HeightmapTimeAccumulation::subscribe()
00083 {
00084
00085 }
00086 void HeightmapTimeAccumulation::unsubscribe()
00087 {
00088
00089 }
00090
00091
00092 void HeightmapTimeAccumulation::publishHeightmap(
00093 const cv::Mat& heightmap, const std_msgs::Header& header)
00094 {
00095 pub_output_.publish(cv_bridge::CvImage(
00096 header,
00097 sensor_msgs::image_encodings::TYPE_32FC1,
00098 heightmap).toImageMsg());
00099 }
00100
00101 cv::Point HeightmapTimeAccumulation::toIndex(
00102 const pcl::PointXYZ& p, const cv::Mat& map)
00103 {
00104 if (p.x > max_x_ || p.x < min_x_ ||
00105 p.y > max_y_ || p.y < min_y_) {
00106 return cv::Point(-1, -1);
00107 }
00108 const float offsetted_x = p.x - min_x_;
00109 const float offsetted_y = p.y - min_y_;
00110 const float dx = (max_x_ - min_x_) / map.cols;
00111 const float dy = (max_y_ - min_y_) / map.rows;
00112 return cv::Point(std::floor(offsetted_x / dx),
00113 std::floor(offsetted_y / dy));
00114 }
00115
00116 bool HeightmapTimeAccumulation::isValidIndex(const cv::Point& index, const cv::Mat& map)
00117 {
00118 return (index.x >= 0 && index.x < map.cols &&
00119 index.y >= 0 && index.y < map.rows);
00120 }
00121
00122 bool HeightmapTimeAccumulation::isValidCell(const cv::Point& index, const cv::Mat& map)
00123 {
00124 float v = map.at<float>(index.y, index.x);
00125 return !isnan(v) && v != -FLT_MAX;
00126 }
00127
00128 void HeightmapTimeAccumulation::accumulate(
00129 const sensor_msgs::Image::ConstPtr& msg)
00130 {
00131 boost::mutex::scoped_lock lock(mutex_);
00132 if (!config_) {
00133 JSK_NODELET_ERROR("no ~input/config is yet available");
00134 return;
00135 }
00136 tf::StampedTransform tf_transform;
00137 tf_->lookupTransform(fixed_frame_id_, center_frame_id_,
00138 msg->header.stamp,
00139 tf_transform);
00140 Eigen::Affine3f from_center_to_fixed;
00141 tf::transformTFToEigen(tf_transform, from_center_to_fixed);
00142 cv::Mat new_heightmap = cv_bridge::toCvShare(
00143 msg, sensor_msgs::image_encodings::TYPE_32FC1)->image;
00144
00145 Eigen::Affine3f from_prev_to_current
00146 = prev_from_center_to_fixed_.inverse() * from_center_to_fixed;
00147 pcl::PointCloud<pcl::PointXYZ> transformed_pointcloud;
00148 pcl::transformPointCloud(prev_cloud_, transformed_pointcloud, from_prev_to_current.inverse());
00149 for (size_t i = 0; i < transformed_pointcloud.points.size(); i++) {
00150 pcl::PointXYZ p = transformed_pointcloud.points[i];
00151 if (isValidPoint(p)) {
00152 cv::Point index = toIndex(p, new_heightmap);
00153 if (isValidIndex(index, new_heightmap)) {
00154 if (!isValidCell(index, new_heightmap)) {
00155 new_heightmap.at<float>(index.y, index.x) = p.z;
00156 }
00157 }
00158 }
00159 }
00160 publishHeightmap(new_heightmap, msg->header);
00161 prev_from_center_to_fixed_ = from_center_to_fixed;
00162 }
00163
00164 void HeightmapTimeAccumulation::prevPointCloud(
00165 const sensor_msgs::PointCloud2::ConstPtr& msg)
00166 {
00167 boost::mutex::scoped_lock lock(mutex_);
00168 pcl::fromROSMsg(*msg, prev_cloud_);
00169
00170 }
00171
00172 void HeightmapTimeAccumulation::configCallback(
00173 const jsk_recognition_msgs::HeightmapConfig::ConstPtr& msg)
00174 {
00175 boost::mutex::scoped_lock lock(mutex_);
00176 config_ = msg;
00177 min_x_ = msg->min_x;
00178 max_x_ = msg->max_x;
00179 min_y_ = msg->min_y;
00180 max_y_ = msg->max_y;
00181 pub_config_.publish(msg);
00182 }
00183
00184 bool HeightmapTimeAccumulation::resetCallback(std_srvs::Empty::Request& req,
00185 std_srvs::Empty::Response& res)
00186 {
00187 boost::mutex::scoped_lock lock(mutex_);
00188 prev_from_center_to_fixed_ = Eigen::Affine3f::Identity();
00189 prev_cloud_.points.clear();
00190 return true;
00191 }
00192 }
00193
00194 #include <pluginlib/class_list_macros.h>
00195 PLUGINLIB_EXPORT_CLASS (jsk_pcl_ros::HeightmapTimeAccumulation, nodelet::Nodelet);