00001
00002 #include <dynamic_obs_msgs/DynamicObstacles.h>
00003 #include <dynamic_obs_msgs/DynamicObstacle.h>
00004 #include <dynamic_obs_msgs/DynObsTrajectory.h>
00005 #include <geometry_msgs/PoseWithCovarianceStamped.h>
00006 #include <ros/ros.h>
00007 #include <stdio.h>
00008 #include <lidar_tracking/lidarTracking.h>
00009 #include <geometry_msgs/Pose.h>
00010 #include <visualization_msgs/Marker.h>
00011 #include <visualization_msgs/MarkerArray.h>
00012
00013 using namespace std;
00014 using namespace ros;
00015 using namespace dynamic_obs_msgs;
00016
00017 #define VISUALIZE_DYNAMIC_OBSTACLES 1
00018 #define VISUALIZE_OBSTACLE_TRAILS 1
00019
00020 #define MAX_COUNT 1600
00021 #define MIN_COUNT 0
00022 #define DYN_OBS_COUNT_THRESH 40
00023
00024 #define CLUSTER_TIMEOUT 15.0
00025 #define CLUSTER_TOO_FAR 0.4
00026 #define CLUSTER_MIN_VELOCITY 0.2
00027 #define CLUSTER_MAX_ANGLE_DIFF (90*M_PI/180)
00028 #define MAX_RADIUS_CHANGE 0.1
00029
00030 #define MAX_POINTS 100
00031 #define MAP_PADDING 0.1
00032
00033 #define MAX_TURNING 0.1
00034
00035 #define MAX(a,b) (a > b ? a : b)
00036
00037 LidarTracking::LidarTracking()
00038 : tf_(ros::NodeHandle(), ros::Duration(10), true){
00039 dynObs_pub = n.advertise<dynamic_obs_msgs::DynamicObstacles>("dynamic_obstacles", 1);
00040
00041 #if VISUALIZE_DYNAMIC_OBSTACLES
00042 marker_pub = n.advertise<visualization_msgs::MarkerArray>("/visualization_marker_array", 1);
00043 #endif
00044
00045 n.param("laser_link",laser_link,std::string("base_laser_link"));
00046 n.param("map_frame",map_frame,std::string("map"));
00047
00048 n.param("use_costmap",useCostmap,true);
00049 if(useCostmap){
00050 printf("initialize costmap\n");
00051 costmap_ros = new costmap_2d::Costmap2DROS("tracking_costmap", tf_);
00052 costmap_ros->getCostmapCopy(cost_map);
00053 printf("initialized!\n");
00054 }
00055
00056 turning = false;
00057 bool track_when_turning;
00058 n.param("track_when_turning",track_when_turning,true);
00059 if(!track_when_turning)
00060 vel_sub = n.subscribe("cmd_vel", 1, &LidarTracking::velocityCallback,this);
00061
00062 scan_sub = n.subscribe("scan", 1, &LidarTracking::lidarCallback,this);
00063 }
00064
00065 void LidarTracking::velocityCallback(const geometry_msgs::TwistConstPtr& msg){
00066 turning = fabs(msg->angular.z) > MAX_TURNING || (msg->angular.z != 0 && msg->linear.x == 0);
00067 }
00068
00069 void LidarTracking::lidarCallback(const sensor_msgs::LaserScanConstPtr& msg){
00070 bool useScan = !turning;
00071
00072 vector<float> angles;
00073 vector<float> ranges;
00074 int num_rays = msg->ranges.size();
00075 angles.reserve(num_rays);
00076 ranges.reserve(num_rays);
00077 float ang = msg->angle_min;
00078 for(int i=0; i<num_rays; i++){
00079 ranges.push_back(msg->ranges[i]);
00080 angles.push_back(ang);
00081 ang += msg->angle_increment;
00082 }
00083
00084 double t = msg->header.stamp.toSec();
00085 vector<Cluster> temp;
00086 if(useScan)
00087 getClusters(ranges, angles, &temp);
00088 forwardPropogateClusters(t);
00089 if(useScan)
00090 matchClusters(temp, t);
00091 linearExtrapolate(msg->header.stamp);
00092 last_t = t;
00093 }
00094
00095 void LidarTracking::getClusters(vector<float> ranges, vector<float> angles, vector<Cluster>* raw_clusters){
00096 float minDist = 0.3;
00097 float maxDist = 25;
00098 float clusterThreshold = 0.4;
00099 unsigned int clusterNMin = 4;
00100 float minWidth = 0.1;
00101 float maxWidth = 0.7;
00102
00103 geometry_msgs::PointStamped msg_out;
00104 geometry_msgs::PointStamped msg_in;
00105 msg_in.header.stamp = ros::Time();
00106 msg_in.header.frame_id = laser_link;
00107 msg_in.point.z = 0;
00108
00109 for(unsigned int i=0; i<ranges.size(); i++){
00110 unsigned int j;
00111 for(j=i; j<ranges.size()-1; j++){
00112 if(fabs(ranges[j+1]-ranges[j]) > clusterThreshold)
00113 break;
00114 }
00115
00116 if(j-i >= clusterNMin && i!=0 && j!=ranges.size()-1){
00117 int upperMid = ceil(((float)(j+i))/2.);
00118 int lowerMid = (j+i)/2;
00119 float dist = (ranges[upperMid]+ranges[lowerMid])/2;
00120 float width = dist*(angles[j]-angles[i]);
00121 if(width <= maxWidth && width >= minWidth && dist <= maxDist && dist >= minDist){
00122 Cluster c;
00123 float theta = (angles[j]+angles[i])/2;
00124
00125 msg_in.point.x = dist*cos(theta);
00126 msg_in.point.y = dist*sin(theta);
00127 try{
00128 tf_.transformPoint(map_frame, msg_in, msg_out);
00129 }
00130 catch (tf::TransformException ex){
00131 ROS_ERROR("%s",ex.what());
00132 }
00133
00134 c.x = msg_out.point.x;
00135 c.y = msg_out.point.y;
00136 c.r = width/2;
00137
00138 if(!useCostmap || !hasMapCollision(c))
00139 raw_clusters->push_back(c);
00140 }
00141 }
00142 i=j;
00143 }
00144 printf("%d raw clusters found\n",(int)raw_clusters->size());
00145 }
00146
00147 bool LidarTracking::hasMapCollision(Cluster c){
00148 int CenterX = (int)(c.x/costmap_ros->getResolution());
00149 int CenterY = (int)(c.y/costmap_ros->getResolution());
00150 int rad = (int)(c.r/costmap_ros->getResolution())+MAP_PADDING;
00151
00152 int d = 3 - 2*rad;
00153 int x = 0;
00154 int y = rad;
00155
00156 while(x <= y){
00157
00158 if(!onMap(CenterX-y,CenterY+x) ||
00159 !onMap(CenterX-x,CenterY-x) ||
00160 !onMap(CenterX+x,CenterY+y) ||
00161 !onMap(CenterX+y,CenterY-y))
00162 return true;
00163 if(cost_map.getCost(CenterX-y, CenterY+x) >= costmap_2d::LETHAL_OBSTACLE ||
00164 cost_map.getCost(CenterX-y, CenterY-x) >= costmap_2d::LETHAL_OBSTACLE ||
00165 cost_map.getCost(CenterX-x, CenterY+y) >= costmap_2d::LETHAL_OBSTACLE ||
00166 cost_map.getCost(CenterX-x, CenterY-y) >= costmap_2d::LETHAL_OBSTACLE ||
00167 cost_map.getCost(CenterX+x, CenterY+y) >= costmap_2d::LETHAL_OBSTACLE ||
00168 cost_map.getCost(CenterX+x, CenterY-y) >= costmap_2d::LETHAL_OBSTACLE ||
00169 cost_map.getCost(CenterX+y, CenterY+x) >= costmap_2d::LETHAL_OBSTACLE ||
00170 cost_map.getCost(CenterX+y, CenterY-x) >= costmap_2d::LETHAL_OBSTACLE)
00171 return true;
00172
00173 if(d <= 0)
00174 d += 4*x + 6;
00175 else{
00176 d += 4*(x-y) + 10;
00177 y--;
00178 }
00179 x++;
00180 }
00181 return false;
00182 }
00183
00184 bool LidarTracking::onMap(int x, int y){
00185 return x>=0 && (unsigned int)x<costmap_ros->getSizeInCellsX() && y>=0 && (unsigned int)y<costmap_ros->getSizeInCellsY();
00186 }
00187
00188
00189 void LidarTracking::forwardPropogateClusters(double t){
00190 double dt = t-last_t;
00191 for(unsigned int i=0; i<clusters.size(); i++){
00192 clusters[i].x += clusters[i].x_dot*dt;
00193 clusters[i].y += clusters[i].y_dot*dt;
00194 if(clusters[i].count > MIN_COUNT)
00195 clusters[i].count--;
00196 if(t-clusters[i].last_seen > CLUSTER_TIMEOUT){
00197 clusters.erase(clusters.begin()+i);
00198
00199
00200 i--;
00201 }
00202 }
00203 }
00204
00205 void LidarTracking::matchClusters(vector<Cluster> raw_clusters, double t){
00206
00207
00208 for(unsigned int i=0; i<raw_clusters.size(); i++){
00209 raw_clusters[i].x_dot = 0;
00210 raw_clusters[i].y_dot = 0;
00211
00212 int closest_idx = -1;
00213 double closest_dist = 100;
00214 for(unsigned int j=0; j<clusters.size(); j++){
00215 double dx = raw_clusters[i].x - clusters[j].x;
00216 double dy = raw_clusters[i].y - clusters[j].y;
00217 double dist = sqrt(dx*dx+dy*dy);
00218 if(dist < closest_dist){
00219 closest_dist = dist;
00220 closest_idx = j;
00221 }
00222 }
00223 if(closest_idx == -1 || closest_dist > CLUSTER_TOO_FAR){
00224
00225 printf("no match...\n");
00226 raw_clusters[i].x_dot = 0;
00227 raw_clusters[i].y_dot = 0;
00228 raw_clusters[i].last_seen = t;
00229 raw_clusters[i].count = 0;
00230 clusters.push_back(raw_clusters[i]);
00231 clusters.back().addPoint(raw_clusters[i].x,raw_clusters[i].y,t);
00232 }
00233 else{
00234
00235 double dt = t-last_t;
00236 double old_x = clusters[closest_idx].x-clusters[closest_idx].x_dot*dt;
00237 double old_y = clusters[closest_idx].y-clusters[closest_idx].y_dot*dt;
00238 double new_x_dot = (raw_clusters[i].x - old_x)/dt;
00239 double new_y_dot = (raw_clusters[i].y - old_y)/dt;
00240 double x_dot = (clusters[closest_idx].x_dot + new_x_dot)/2;
00241 double y_dot = (clusters[closest_idx].y_dot + new_y_dot)/2;
00242
00243 double mag_old = sqrt(clusters[closest_idx].x_dot*clusters[closest_idx].x_dot + clusters[closest_idx].y_dot*clusters[closest_idx].y_dot);
00244 double mag_new = sqrt(new_x_dot*new_x_dot + new_y_dot*new_y_dot);
00245 double dir_diff = acos(clusters[closest_idx].x_dot/mag_old*new_x_dot/mag_new + clusters[closest_idx].y_dot/mag_old*new_y_dot/mag_new);
00246 double v = sqrt(x_dot*x_dot + y_dot*y_dot);
00247 printf("v_old=(%f,%f) v_new(%f,%f)\n",clusters[closest_idx].x_dot,clusters[closest_idx].y_dot,new_x_dot,new_y_dot);
00248
00249 clusters[closest_idx].addPoint(raw_clusters[i].x,raw_clusters[i].y,t);
00250 clusters[closest_idx].getVelocity(&x_dot, &y_dot);
00251
00252 if(v < CLUSTER_MIN_VELOCITY || (!isnan(dir_diff) && dir_diff > CLUSTER_MAX_ANGLE_DIFF)){
00253
00254
00255 printf("bad match (%f,%f)\n",v,dir_diff*180/M_PI);
00256 clusters[closest_idx].last_seen = t;
00257 clusters[closest_idx].x = (clusters[closest_idx].x + raw_clusters[i].x)/2;
00258 clusters[closest_idx].y = (clusters[closest_idx].y + raw_clusters[i].y)/2;
00259 if(clusters[closest_idx].count > MIN_COUNT)
00260 clusters[closest_idx].count--;
00261 clusters[closest_idx].x_dot = x_dot;
00262 clusters[closest_idx].y_dot = y_dot;
00263 }
00264 else{
00265
00266
00267 printf("good match!\n");
00268 clusters[closest_idx].last_seen = t;
00269 clusters[closest_idx].x = (clusters[closest_idx].x + raw_clusters[i].x)/2;
00270 clusters[closest_idx].y = (clusters[closest_idx].y + raw_clusters[i].y)/2;
00271 clusters[closest_idx].x_dot = x_dot;
00272 clusters[closest_idx].y_dot = y_dot;
00273 clusters[closest_idx].r = (clusters[closest_idx].r + raw_clusters[i].r)/2;
00274 if(clusters[closest_idx].count < MAX_COUNT)
00275 clusters[closest_idx].count += 8;
00276 }
00277 }
00278
00279 }
00280 printf("%d clusters\n",(int)clusters.size());
00281 }
00282
00283 void LidarTracking::linearExtrapolate(ros::Time stamp){
00284 double pred_sec = 20;
00285 double t_gran = 0.1;
00286 dynamic_obs_msgs::DynamicObstacles msg;
00287 msg.header.stamp = stamp;
00288 msg.header.frame_id = map_frame;
00289
00290 int max_count = 0;
00291 for(unsigned int c=0; c<clusters.size(); c++){
00292 if(clusters[c].count > max_count)
00293 max_count = clusters[c].count;
00294 if(clusters[c].count < DYN_OBS_COUNT_THRESH)
00295 continue;
00296 double x = clusters[c].x;
00297 double y = clusters[c].y;
00298 double x_dot, y_dot;
00299
00300
00301 clusters[c].getVelocity(&x_dot, &y_dot);
00302 double t = stamp.toSec();
00303
00304 DynObsTrajectory traj;
00305 traj.exists_after = false;
00306 traj.probability = 1;
00307 for(int i=0; i<pred_sec/t_gran+1; i++){
00308 geometry_msgs::PoseWithCovarianceStamped p;
00309 double timestep = i*t_gran;
00310
00311 p.pose.pose.position.x = x + x_dot*timestep;
00312 p.pose.pose.position.y = y + y_dot*timestep;
00313 p.header.stamp = ros::Time(t + timestep);
00314 p.header.frame_id = map_frame;
00315 for(int i=0; i<36; i++)
00316 p.pose.covariance[i] = 0;
00317
00318 p.pose.covariance[0] = 0.01;
00319
00320 p.pose.covariance[7] = 0.01;
00321 p.pose.pose.orientation.w = 1;
00322 traj.points.push_back(p);
00323 }
00324
00325 DynamicObstacle o;
00326 o.radius = clusters[c].r;
00327 o.trajectories.push_back(traj);
00328 msg.dyn_obs.push_back(o);
00329 }
00330
00331 dynObs_pub.publish(msg);
00332
00333 printf("max_count=%d\n",max_count);
00334 printf("%d dynamic obstacles\n",(int)msg.dyn_obs.size());
00335
00336 #if VISUALIZE_DYNAMIC_OBSTACLES
00337 int obs_idx = 0;
00338 int trail_idx = 0;
00339 int cluster_idx = 0;
00340 int dynObs_idx = 0;
00341 visualization_msgs::MarkerArray ma;
00342 for(unsigned int i=0; i<clusters.size(); i++){
00343
00344
00345
00346 geometry_msgs::Pose pose;
00347 pose.position.x = clusters[i].x;
00348 pose.position.y = clusters[i].y;
00349 pose.position.z = 0;
00350
00351 visualization_msgs::Marker marker;
00352 marker.header = msg.header;
00353 marker.type = visualization_msgs::Marker::CYLINDER;
00354 marker.action = visualization_msgs::Marker::ADD;
00355 marker.pose = pose;
00356 marker.scale.x = clusters[i].r*2;
00357 marker.scale.y = clusters[i].r*2;
00358 marker.scale.z = 0.1;
00359 if(clusters[i].count < DYN_OBS_COUNT_THRESH){
00360 marker.color.r = 0.0f;
00361 marker.color.g = 0.0f;
00362 marker.color.b = 1.0f;
00363 marker.ns = "cluster";
00364 marker.id = cluster_idx;
00365 cluster_idx++;
00366 }
00367 else{
00368 marker.color.r = 1.0f;
00369 marker.color.g = 0.0f;
00370 marker.color.b = 0.0f;
00371 marker.ns = "dynamic_obstacle";
00372 marker.id = dynObs_idx;
00373 dynObs_idx++;
00374 }
00375 marker.color.a = 1.0;
00376 marker.lifetime = ros::Duration(CLUSTER_TIMEOUT-(stamp.toSec()-clusters[i].last_seen));
00377 ma.markers.push_back(marker);
00378
00379 #if VISUALIZE_OBSTACLE_TRAILS
00380 if(clusters[i].count >= DYN_OBS_COUNT_THRESH){
00381 vector<geometry_msgs::PoseWithCovarianceStamped> trail = msg.dyn_obs[obs_idx].trajectories[0].points;
00382 obs_idx++;
00383 for(unsigned int j=0; j<trail.size(); j+=20){
00384 geometry_msgs::Pose pose;
00385 pose.position.x = trail[j].pose.pose.position.x;
00386 pose.position.y = trail[j].pose.pose.position.y;
00387 pose.position.z = 0;
00388
00389 visualization_msgs::Marker marker;
00390 marker.header = msg.header;
00391 marker.ns = "obstacle_trail";
00392 marker.id = trail_idx;
00393 trail_idx++;
00394 marker.type = visualization_msgs::Marker::CYLINDER;
00395 marker.action = visualization_msgs::Marker::ADD;
00396 marker.pose = pose;
00397 marker.scale.x = clusters[i].r*2;
00398 marker.scale.y = clusters[i].r*2;
00399 marker.scale.z = 0.1;
00400 marker.color.r = 1.0f;
00401 marker.color.g = 0.0f;
00402 marker.color.b = 0.0f;
00403 marker.color.a = MAX(1.0-((double)j)/trail.size(),0.1);
00404 marker.lifetime = ros::Duration(0.5);
00405 ma.markers.push_back(marker);
00406 }
00407 }
00408 #endif
00409
00410
00411 }
00412 marker_pub.publish(ma);
00413 #endif
00414
00415 }
00416
00417 Cluster::Cluster(){
00418 n = 0;
00419 sumX = 0;
00420 sumY = 0;
00421 sumXX = 0;
00422 sumXY = 0;
00423 curr_x_dot = 0;
00424 curr_y_dot = 0;
00425 points_changed = false;
00426 }
00427
00428 void Cluster::addPoint(double x, double y, double t){
00429 if(n == MAX_POINTS){
00430 sumX -= points.front().x;
00431 sumY -= points.front().y;
00432 sumXX -= points.front().xx;
00433 sumXY -= points.front().xy;
00434 points.pop_front();
00435 }
00436 else
00437 n++;
00438
00439 Point p;
00440 p.x = x;
00441 p.y = y;
00442 p.xx = x*x;
00443 p.xy = x*y;
00444 p.t = t;
00445 points.push_back(p);
00446 sumX += p.x;
00447 sumY += p.y;
00448 sumXX += p.xx;
00449 sumXY += p.xy;
00450
00451 points_changed = true;
00452 }
00453
00454 void Cluster::getVelocity(double* x_dot, double* y_dot){
00455 if(points_changed){
00456 if(n < 2){
00457 curr_x_dot = 0;
00458 curr_y_dot = 0;
00459 }
00460 else{
00461 double slope, intercept, ang1;
00462 if(fitLine(&slope, &intercept))
00463 ang1 = atan2(slope,1);
00464 else
00465 ang1 = M_PI/2;
00466 double dy = points.back().y-points.front().y;
00467 double dx = points.back().x-points.front().x;
00468 double ang2 = atan2(dy, dx);
00469
00470 double ang_diff = ang2 - ang1;
00471 if(ang_diff < -M_PI)
00472 ang_diff += 2*M_PI;
00473 if(ang_diff > M_PI)
00474 ang_diff -= 2*M_PI;
00475
00476 if(fabs(ang_diff) > M_PI/2)
00477 ang1 += M_PI;
00478
00479 double mag = sqrt(dx*dx+dy*dy)/(points.back().t-points.front().t);
00480 curr_x_dot = mag*cos(ang1);
00481 curr_y_dot = mag*sin(ang1);
00482 }
00483 points_changed = false;
00484 }
00485
00486 *x_dot = curr_x_dot;
00487 *y_dot = curr_y_dot;
00488 }
00489
00490 bool Cluster::fitLine(double* slope, double* intercept){
00491 double Sx = n*sumXX - sumX*sumX;
00492 if(Sx == 0)
00493 return false;
00494 double Sxy = n*sumXY - sumX*sumY;
00495 double m = Sxy/Sx;
00496 double b = (sumY - m*sumX)/n;
00497 *slope = m;
00498 *intercept = b;
00499 return true;
00500 }
00501
00502 int main(int argc, char** argv){
00503 ros::init(argc, argv, "lidar_tracking");
00504 LidarTracking track;
00505 ros::spin();
00506 }
00507