00001 #include <cmath>
00002 #include <limits>
00003
00004 #include <Eigen/Core>
00005 #include <Eigen/Geometry>
00006 #include <Eigen/Dense>
00007
00008 #include <pcl/point_cloud.h>
00009 #include <pcl_conversions/pcl_conversions.h>
00010 #include <pcl_ros/transforms.h>
00011
00012 #include <sensor_msgs/PointCloud2.h>
00013 #include <hector_worldmodel_msgs/PosePercept.h>
00014
00015 #include <hector_soft_obstacle_detection/soft_obstacle_detection.h>
00016
00017 SoftObstacleDetection::SoftObstacleDetection()
00018 {
00019 ros::NodeHandle nh;
00020
00021 unit_scale = 100.0;
00022 border_size = 10.0;
00023
00024
00025 laser_scan_sub_ = nh.subscribe("/laser1/scan", 1, &SoftObstacleDetection::laserScanCallback, this);
00026
00027
00028 veil_percept_pub_ = nh.advertise<hector_worldmodel_msgs::PosePercept>("veil_percept", 20);
00029 veil_percept_pose_pub_ = nh.advertise<geometry_msgs::PoseStamped>("veil_percept_pose", 20);
00030 veil_point_cloud_pub_ = nh.advertise<sensor_msgs::PointCloud2>("veil_point_cloud", 5);
00031
00032
00033 dyn_rec_server_.setCallback(boost::bind(&SoftObstacleDetection::dynRecParamCallback, this, _1, _2));
00034
00035 update_timer = nh.createTimer(ros::Duration(0.01), &SoftObstacleDetection::update, this);
00036 }
00037
00038 SoftObstacleDetection::~SoftObstacleDetection()
00039 {
00040 }
00041
00042 void SoftObstacleDetection::transformScanToImage(const sensor_msgs::LaserScanConstPtr scan, cv::Mat& img, cv::Point2i& scan_center) const
00043 {
00044 float min_x = std::numeric_limits<float>::max();
00045 float min_y = std::numeric_limits<float>::max();
00046 float max_x = std::numeric_limits<float>::min();
00047 float max_y = std::numeric_limits<float>::min();
00048
00049 std::vector<cv::Point2f> points;
00050
00051
00052 for (unsigned int i = 0; i < scan->ranges.size(); i++)
00053 {
00054 if (!finite(scan->ranges[i]))
00055 continue;
00056
00057 cv::Point2f p;
00058 float angle = scan->angle_max - scan->angle_increment*i;
00059 p.x = cos(angle) * scan->ranges[i]*unit_scale;
00060 p.y = sin(angle) * scan->ranges[i]*unit_scale;
00061
00062 points.push_back(p);
00063
00064 min_x = std::min(min_x, p.x);
00065 min_y = std::min(min_y, p.y);
00066 max_x = std::max(max_x, p.x);
00067 max_y = std::max(max_y, p.y);
00068 }
00069
00070 if (points.empty())
00071 return;
00072
00073
00074 cv::Size img_size(max_x-min_x + 2*border_size, max_y-min_y + 2*border_size);
00075
00076 if (img_size.width <= 2*border_size || img_size.height <= 2*border_size)
00077 return;
00078
00079 scan_center.x = border_size - min_x;
00080 scan_center.y = border_size - min_y;
00081
00082 img = cv::Mat(img_size, CV_8UC1);
00083 img = cv::Scalar::all(0);
00084
00085 for (std::vector<cv::Point2f>::const_iterator itr = points.begin(); itr != points.end(); itr++)
00086 img.at<uchar>(std::floor(itr->y + scan_center.y), std::floor(itr->x + scan_center.x)) = 255;
00087
00088
00089
00090 #ifdef DEBUG
00091 cv::imshow("scan", img);
00092 #endif
00093 }
00094
00095 void SoftObstacleDetection::houghTransform(const cv::Mat& img, std::vector<cv::Vec4i>& lines) const
00096 {
00097 lines.clear();
00098
00099
00100 cv::HoughLinesP(img, lines, 1, CV_PI/180, 35, 80, 30);
00101
00102 #ifdef DEBUG
00103
00104 cv::Mat img_lines(img.size(), CV_8UC3);
00105 img_lines = cv::Scalar::all(0);
00106 for (size_t i = 0; i < lines.size(); i++)
00107 {
00108 cv::Vec4i line = lines[i];
00109 cv::line(img_lines, cv::Point(line[0], line[1]), cv::Point(line[2], line[3]), cv::Scalar(0, 0, 255), 2, CV_AA);
00110 }
00111
00112 cv::imshow("detected_lines", img_lines);
00113 #endif
00114 }
00115
00116 uchar SoftObstacleDetection::getMaxAtLine(const cv::Mat& img, const cv::Point& p1, const cv::Point& p2) const
00117 {
00118 uchar result = 0;
00119
00120 cv::LineIterator it(img, p1, p2, 8);
00121 for(int i = 0; i < it.count; i++, ++it)
00122 {
00123 result = std::max(result, img.at<uchar>(it.pos()));
00124
00125 if (result == 255)
00126 break;
00127 }
00128
00129 return result;
00130 }
00131
00132 void SoftObstacleDetection::getLine(const cv::Mat& img, const cv::Vec4i& line, cv::Mat& out) const
00133 {
00134 cv::Point p1(line[0], line[1]);
00135 cv::Point p2(line[2], line[3]);
00136
00137 std::vector<uchar> signal;
00138 double r = 3.0;
00139
00140 cv::Vec2d orth_dir(static_cast<double>(p2.y-p1.y), static_cast<double>(-(p2.x-p1.x)));
00141 orth_dir = cv::normalize(orth_dir) * r;
00142 cv::Point o(std::floor(orth_dir(2)+0.5), std::floor(orth_dir(1)+0.5));
00143
00144 cv::LineIterator it(img, p1, p2, 8);
00145 for(int i = 0; i < it.count; i++, ++it)
00146 {
00147 uchar val1 = getMaxAtLine(img, it.pos()-o, it.pos()+o);
00148 uchar val2 = getMaxAtLine(img, it.pos()-o-cv::Point(1, 1), it.pos()+o-cv::Point(1, 1));
00149 signal.push_back(std::max(val1, val2));
00150 }
00151
00152 out = cv::Mat(cv::Size(signal.size(), 1), CV_8UC1);
00153 for (size_t i = 0; i < signal.size(); i++)
00154 out.at<uchar>(i) = signal[i];
00155 }
00156
00157 void SoftObstacleDetection::edgeDetection(const cv::Mat& signal, std::vector<double>& edges, std::vector<double>& centers) const
00158 {
00159
00160
00161
00162 cv::Mat sig(signal.size(), CV_8UC1);
00163 signal.copyTo(sig);
00164
00165
00166 unsigned int edge_dist = 0;
00167 std::vector<unsigned int> edge_distances;
00168
00169
00170 unsigned int last_val = signal.at<uchar>(0);
00171 for (int i = 1; i < signal.cols; i++)
00172 {
00173 edge_dist++;
00174
00175 unsigned int current_val = static_cast<int>(signal.at<uchar>(i));
00176 int d = current_val - last_val;
00177
00178 if (std::abs(d) > 150)
00179 {
00180 int sign = d < 0 ? -1 : 1;
00181
00182 edges.push_back(static_cast<double>(sign * (i-0.5)));
00183
00184 if (sign < 0 || edge_dist > min_hole_size_)
00185 {
00186 edge_distances.push_back(edge_dist);
00187 centers.push_back(static_cast<double>(-sign) * (static_cast<double>(i-0.5) - 0.5*static_cast<double>(edge_dist)));
00188
00189 edge_dist = 0;
00190 }
00191 else
00192 {
00193 for (int j = i - edge_dist; j < i; j++)
00194 sig.at<uchar>(j) = 255;
00195
00196 if (!edge_distances.empty())
00197 {
00198 edge_dist += edge_distances[edge_distances.size()-1];
00199 edge_distances.pop_back();
00200 edges.pop_back();
00201 centers.pop_back();
00202 }
00203 edges.pop_back();
00204 }
00205 }
00206
00207 last_val = current_val;
00208 }
00209
00210 edge_distances.push_back(edge_dist);
00211 centers.push_back((last_val == 0 ? -1.0 : 1.0) * (static_cast<double>(signal.cols-0.5) - 0.5*static_cast<double>(edge_dist)));
00212
00213 cv::resize(sig, sig, cv::Size(sig.cols*5, 30));
00214 cv::imshow("test1", sig);
00215 }
00216
00217 bool SoftObstacleDetection::checkSegmentsMatching(const std::vector<double>& edges, const std::vector<double>& centers, double veil_segment_size, double min_segments, double max_segments) const
00218 {
00219 if (edges.empty() || centers.empty())
00220 return false;
00221
00222 std::vector<double> seg_sizes;
00223 std::vector<double> seg_dists;
00224
00225 double mse = 0.0;
00226 double size_mean = 0.0;
00227 double size_var = 0.0;
00228 double dist_mean = 0.0;
00229 double dist_var = 0.0;
00230
00231 double last_edge = std::abs(edges[0]);
00232 for (size_t i = 1; i < edges.size()-1; i++)
00233 {
00234 double size = std::abs(edges[i]) - last_edge;
00235 if (edges[i] < 0)
00236 {
00237 double e = veil_segment_size - size;
00238
00239 mse += e*e;
00240 size_mean += size;
00241
00242 seg_sizes.push_back(size);
00243 }
00244 else
00245 {
00246 dist_mean += size;
00247 seg_dists.push_back(size);
00248 }
00249 last_edge = std::abs(edges[i]);
00250 }
00251
00252 if (min_segments > seg_sizes.size() || seg_sizes.size() > max_segments)
00253 return false;
00254
00255 mse /= static_cast<double>(seg_sizes.size());
00256 size_mean /= static_cast<double>(seg_sizes.size());
00257 dist_mean /= static_cast<double>(seg_dists.size());
00258
00259 if (size_mean/dist_mean < size_dist_ratio_*0.9 || size_mean/dist_mean > size_dist_ratio_*1.1)
00260 return false;
00261
00262 for (std::vector<double>::const_iterator itr = seg_sizes.begin(); itr != seg_sizes.end(); itr++)
00263 size_var += (*itr - size_mean)*(*itr - size_mean);
00264 size_var /= static_cast<double>(seg_sizes.size());
00265
00266 for (std::vector<double>::const_iterator itr = seg_dists.begin(); itr != seg_dists.end(); itr++)
00267 dist_var += (*itr - dist_mean)*(*itr - dist_mean);
00268 dist_var /= static_cast<double>(seg_dists.size());
00269
00270 #ifdef DEBUG
00271 ROS_INFO("MSE: %f, size_mean: %f, size_var: %f, dist_mean: %f, dist_var: %f", mse, size_mean, size_var, dist_mean, dist_var);
00272 #endif
00273
00274 return mse < max_segment_size_mse_ && size_var < max_segment_size_var_ && dist_var < max_segment_dist_var_;
00275 }
00276
00277 double SoftObstacleDetection::evalModel(const std::vector<double>& edges, const std::vector<double>& centers, double lambda) const
00278 {
00279 if (centers.size() < 2)
00280 return -1.0;
00281
00282 double f = (2.0*M_PI)/lambda;
00283
00284 double mse = 0.0;
00285
00286 double bias = std::abs(edges[0]) - (edges[0] > 0.0 ? 0.0 : 0.5*lambda);
00287
00288 double y = centers[0] < 0.0 ? 1.0 : -1.0;
00289 for (size_t i = 0; i < centers.size(); i++)
00290 {
00291 y *= -1.0;
00292 double x = f*(std::abs(centers[i])-bias);
00293 double e = y - sin(x);
00294 mse += e*e;
00295 }
00296
00297 y = edges[0] < 0.0 ? 1.0 : -1.0;
00298 for (size_t i = 0; i < edges.size(); i++)
00299 {
00300 y *= -1.0;
00301 double x = f*(std::abs(edges[i])-bias);
00302 double e = y - cos(x);
00303 mse += e*e;
00304 }
00305
00306 return mse/static_cast<double>(centers.size()+edges.size());
00307 }
00308
00309 bool SoftObstacleDetection::checkFrequencyMatching(const std::vector<double>& edges, const std::vector<double>& centers, double lambda, double min_segments, double max_segments) const
00310 {
00311 if (edges.empty() || centers.empty())
00312 return false;
00313
00314 if (min_segments > centers.size()/2 || centers.size()/2 > max_segments)
00315 return false;
00316
00317
00318
00319
00320
00321
00322
00323
00324
00325
00326
00327
00328
00329
00330
00331
00332 double mse = evalModel(edges, centers, lambda);
00333
00334
00335
00336
00337
00338
00339
00340
00341
00342
00343
00344
00345
00346
00347
00348
00349
00350
00351
00352 #ifdef DEBUG
00353 std::string out("MSE: ");
00354 out += boost::lexical_cast<std::string>(mse);
00355 out += "\nCenters: ";
00356 for (size_t i = 0; i < centers.size(); i++)
00357 out += boost::lexical_cast<std::string>(centers[i]) + std::string(" ");
00358 out += "\nEdges: ";
00359 for (size_t i = 0; i < edges.size(); i++)
00360 out += boost::lexical_cast<std::string>(edges[i]) + std::string(" ");
00361
00362 ROS_INFO("%s", out.c_str());
00363
00364 #endif
00365
00366 return mse < max_frequency_mse_;
00367
00368
00369
00370
00371
00372
00373
00374
00375
00376
00377
00378
00379
00380
00381
00382
00383
00384
00385
00386
00387
00388
00389
00390
00391
00392
00393
00396
00401
00406
00410
00411
00412
00413
00414
00415
00416
00417
00418
00419
00420
00421
00422
00423
00424
00425
00426
00427
00428 }
00429
00430 void SoftObstacleDetection::lineToPointCloud(const cv::Vec4i& line, const cv::Point2i& scan_center, pcl::PointCloud<pcl::PointXYZ>& out) const
00431 {
00432 out.clear();
00433
00434 int x1 = line[0];
00435 int y1 = line[1];
00436 int x2 = line[2];
00437 int y2 = line[3];
00438
00439
00440 const bool steep = (std::abs(y2 - y1) > std::abs(x2 - x1));
00441 if (steep)
00442 {
00443 std::swap(x1, y1);
00444 std::swap(x2, y2);
00445 }
00446
00447 if (x1 > x2)
00448 {
00449 std::swap(x1, x2);
00450 std::swap(y1, y2);
00451 }
00452
00453 const int dx = x2 - x1;
00454 const int dy = std::abs(y2 - y1);
00455
00456 int error = dx / 2;
00457 const int ystep = (y1 < y2) ? 1 : -1;
00458
00459 int y = y1;
00460 for (int x = x1; x < x2; x++)
00461 {
00462 if (steep)
00463 out.push_back(pcl::PointXYZ(static_cast<double>(y), static_cast<double>(x), 0.0));
00464 else
00465 out.push_back(pcl::PointXYZ(static_cast<double>(x), static_cast<double>(y), 0.0));
00466
00467 error -= dy;
00468 if (error < 0)
00469 {
00470 y += ystep;
00471 error += dx;
00472 }
00473 }
00474 }
00475
00476 void SoftObstacleDetection::update(const ros::TimerEvent& )
00477 {
00478 if (!last_scan)
00479 return;
00480
00481 std_msgs::Header header = last_scan->header;
00482
00483 cv::Mat img_scan;
00484 cv::Point2i scan_center;
00485 transformScanToImage(last_scan, img_scan, scan_center);
00486
00487 #ifdef DEBUG
00488 cv::Mat result;
00489 cv::cvtColor(img_scan, result, CV_GRAY2RGB);
00490 #endif
00491
00492 last_scan.reset();
00493
00494 cv::Mat img_scan_filtered;
00495 cv::Mat kernel_dil = getStructuringElement(cv::MORPH_ELLIPSE, cv::Size(3.0, 3.0));
00496 cv::dilate(img_scan, img_scan, kernel_dil);
00497
00498 std::vector<cv::Vec4i> lines;
00499 houghTransform(img_scan, lines);
00500
00501
00502
00503
00504
00505
00506
00507 if (lines.empty())
00508 return;
00509
00510
00511 std::vector<cv::Mat> signals_;
00512 for (std::vector<cv::Vec4i>::const_iterator itr = lines.begin(); itr != lines.end(); itr++)
00513 {
00514 const cv::Vec4i& line = *itr;
00515 cv::Point p1(line[0], line[1]);
00516 cv::Point p2(line[2], line[3]);
00517
00518 double length_sq = (p1.x-p2.x)*(p1.x-p2.x) + (p1.y-p2.y)*(p1.y-p2.y);
00519
00520 if (length_sq > max_curtain_length_sq_)
00521 continue;
00522
00523 cv::Mat signal;
00524 getLine(img_scan, line, signal);
00525 signals_.push_back(signal);
00526
00527 std::vector<double> edges;
00528 std::vector<double> centers;
00529
00530 edgeDetection(signal, edges, centers);
00531
00532
00533
00534
00535 if (checkSegmentsMatching(edges, centers, veil_segment_size_, min_segments_, max_segments_))
00536 {
00537 #ifdef DEBUG
00538 ROS_INFO("Detected");
00539 #endif
00540
00541 if (tf_listener.canTransform("/map", "/laser1_frame", header.stamp))
00542 {
00543
00544 geometry_msgs::PoseStamped veil_pose;
00545 veil_pose.header = header;
00546 veil_pose.pose.position.x = static_cast<float>((p1.x + p2.x)/2 - scan_center.x) / unit_scale;
00547 veil_pose.pose.position.y = -static_cast<float>((p1.y + p2.y)/2 - scan_center.y) / unit_scale;
00548 veil_pose.pose.position.z = 0.0;
00549
00550 double dx = ((p2.x - p1.x));
00551 double dy = -((p2.y - p1.y));
00552 veil_pose.pose.orientation = tf::createQuaternionMsgFromYaw(atan2(dy, dx) + M_PI_2);
00553
00554 tf_listener.transformPose("/map", veil_pose, veil_pose);
00555
00556
00557 hector_worldmodel_msgs::PosePercept veil_percept;
00558 veil_percept.header = veil_pose.header;
00559 veil_percept.pose.pose = veil_pose.pose;
00560 veil_percept.info.class_id = "soft_obstacle";
00561 veil_percept.info.class_support = 1.0;
00562
00563 veil_percept_pub_.publish(veil_percept);
00564 veil_percept_pose_pub_.publish(veil_pose);
00565
00566
00567 pcl::PointCloud<pcl::PointXYZ> pc;
00568 lineToPointCloud(line, scan_center, pc);
00569
00570 for (size_t i = 0; i < pc.size(); i++)
00571 {
00572 pcl::PointXYZ& p = pc.at(i);
00573 p.x = (p.x-static_cast<double>(scan_center.x)) / unit_scale;
00574 p.y = -(p.y-static_cast<double>(scan_center.y)) / unit_scale;
00575 p.z = 0.0;
00576 }
00577
00578
00579 sensor_msgs::PointCloud2 pc_msg;
00580 pcl::toROSMsg(pc, pc_msg);
00581 pc_msg.header = header;
00582 pcl_ros::transformPointCloud("/map", pc_msg, pc_msg, tf_listener);
00583 veil_point_cloud_pub_.publish(pc_msg);
00584 }
00585
00586 #ifdef DEBUG
00587 cv::line(result, p1, p2, cv::Scalar(0, 255, 0), 2, CV_AA);
00588 cv::imshow("result", result);
00589
00590 #endif
00591 }
00592 }
00593
00594 #ifdef DEBUG
00595 cv::imshow("result", result);
00596
00597 #endif
00598 }
00599
00600 void SoftObstacleDetection::laserScanCallback(const sensor_msgs::LaserScanConstPtr& scan)
00601 {
00602 last_scan = scan;
00603 }
00604
00605 void SoftObstacleDetection::dynRecParamCallback(SoftObstacleDetectionConfig& config, uint32_t )
00606 {
00607 min_hole_size_ = config.min_hole_size;
00608 max_curtain_length_sq_ = config.max_curtain_length*config.max_curtain_length*unit_scale*unit_scale;
00609 min_frequency_ = config.min_frequency;
00610 max_frequency_ = config.max_frequency;
00611 veil_segment_size_ = config.veil_segment_size*unit_scale;
00612 min_segments_ = config.min_segments;
00613 max_segments_ = config.max_segments;
00614 max_segment_size_mse_ = config.max_segment_size_mse;
00615 max_segment_size_var_ = config.max_segment_size_std*config.max_segment_size_std;
00616 max_segment_dist_var_ = config.max_segment_dist_std*config.max_segment_dist_std;
00617 size_dist_ratio_ = config.size_dist_ratio;
00618 max_frequency_mse_ = config.max_frequency_mse;
00619 percept_class_id_ = config.percept_class_id;
00620 }
00621
00622 int main(int argc, char **argv)
00623 {
00624 #ifdef DEBUG
00625 cv::namedWindow("scan");
00626 cv::namedWindow("detected_lines");
00627 cv::namedWindow("result");
00628 cv::namedWindow("test1");
00629 cv::namedWindow("test2");
00630 cv::startWindowThread();
00631 #endif
00632
00633 ros::init(argc, argv, "soft_obstacle_detection");
00634 SoftObstacleDetection sod;
00635 ros::spin();
00636
00637 #ifdef DEBUG
00638 cv::destroyWindow("scan");
00639 cv::destroyWindow("detected_lines");
00640 cv::destroyWindow("result");
00641 cv::destroyWindow("test1");
00642 cv::destroyWindow("test2");
00643 #endif
00644
00645 return 0;
00646 }
00647