VINS-Mono代码学习记录(二)---feature_tracker

一、feature_tracker总体流程图解

先给出一张feature_tracker的总体流程图,让自己有一个全局意识。这张图真好,感谢原作者。基本这就是feature_tracker这个node搞的事情啦!重点关注它,对! 就是它!
在这里插入图片描述图片转自:https://blog.csdn.net/qq\_41839222/article/details/85797156

二、main()函数分析

开始进入feature_tracker–>src文件夹可以看到,里面的文件共6个,先看feature_tracker_node.cpp中的主函数main()

  1. int main(int argc, char **argv)
  2. {
  3. //【1】初始化,设置句柄,设置logger级别
  4. ros::init(argc, argv, "feature_tracker");
  5. ros::NodeHandle n("~");
  6. ros::console::set_logger_level(ROSCONSOLE_DEFAULT_NAME, ros::console::levels::Info);
  7. //【2】从config-->euroc-->euroc_config.yaml文件中读取参数,包括话题,图像宽高,追踪的特征点数量等等
  8. readParameters(n);
  9. //【3】循环读取每个相机的内参
  10. for (int i = 0; i < NUM_OF_CAM; i++) //NUM_OF_CAM=1
  11. trackerData[i].readIntrinsicParameter(CAM_NAMES[i]);
  12. //【4】如果是鱼眼相机,添加mask去除边缘噪声
  13. if(FISHEYE)
  14. {
  15. for (int i = 0; i < NUM_OF_CAM; i++)
  16. {
  17. trackerData[i].fisheye_mask = cv::imread(FISHEYE_MASK, 0);
  18. if(!trackerData[i].fisheye_mask.data)
  19. {
  20. ROS_INFO("load mask fail");
  21. ROS_BREAK();
  22. }
  23. else
  24. ROS_INFO("load mask success");
  25. }
  26. }
  27. //【5】订阅话题IMAGE_TOPIC(/cam0/image_raw),执行回调函数img_callback
  28. ros::Subscriber sub_img = n.subscribe(IMAGE_TOPIC, 100, img_callback);//当订阅的topic有新的信息时,激活回调函数
  29. //【6】发布feature,实例feature_points,跟踪的特征点,给后端优化用
  30. //发布feature_img,实例ptr,跟踪的特征点图,给RVIZ用和调试用
  31. //发布restart
  32. pub_img = n.advertise<sensor_msgs::PointCloud>("feature", 1000);
  33. pub_match = n.advertise<sensor_msgs::Image>("feature_img",1000);
  34. pub_restart = n.advertise<std_msgs::Bool>("restart",1000);
  35. /*
  36. if (SHOW_TRACK)
  37. cv::namedWindow("vis", cv::WINDOW_NORMAL);
  38. */
  39. ros::spin();//ros::spin() 在调用后不会再返回,也就是主程序到这儿就不往下执行了
  40. return 0;
  41. }

上面每一步都给出了详细的注释,其中步骤【2】读取参数的函数readParameters()如下:

  1. void readParameters(ros::NodeHandle &n)
  2. {
  3. std::string config_file;
  4. config_file = readParam<std::string>(n, "config_file");//ans-->config_file
  5. std::cout<<"config_file"<<config_file<<endl;//自己加的,为了查看config_file的具体值
  6. cv::FileStorage fsSettings(config_file, cv::FileStorage::READ);
  7. if(!fsSettings.isOpened())
  8. {
  9. std::cerr << "ERROR: Wrong path to settings" << std::endl;
  10. }
  11. std::string VINS_FOLDER_PATH = readParam<std::string>(n, "vins_folder");
  12. fsSettings["image_topic"] >> IMAGE_TOPIC; //image_topic: "/cam0/image_raw"
  13. fsSettings["imu_topic"] >> IMU_TOPIC; //imu_topic: "/imu0"
  14. MAX_CNT = fsSettings["max_cnt"];//tracking最大特征点数量150
  15. MIN_DIST = fsSettings["min_dist"];//两个特征点之间的最小距离30
  16. ROW = fsSettings["image_height"];//480
  17. COL = fsSettings["image_width"];//752
  18. FREQ = fsSettings["freq"];//publish tracking result 的频率,默认设置为10hz
  19. F_THRESHOLD = fsSettings["F_threshold"];//ransac threshold (pixel)值为1
  20. SHOW_TRACK = fsSettings["show_track"];//发布image topic 值为1
  21. EQUALIZE = fsSettings["equalize"];//if image is too dark or light, trun on equalize to find enough features,默认为1
  22. FISHEYE = fsSettings["fisheye"];//默认不用fisheye 默认值为0
  23. if (FISHEYE == 1)
  24. FISHEYE_MASK = VINS_FOLDER_PATH + "config/fisheye_mask.jpg";
  25. CAM_NAMES.push_back(config_file);//string类型的config_file push到CAM_NAMES vector中
  26. WINDOW_SIZE = 20;
  27. STEREO_TRACK = false;//如果双目 把这个参数改为true
  28. FOCAL_LENGTH = 460;
  29. PUB_THIS_FRAME = false;
  30. if (FREQ == 0)
  31. FREQ = 100;
  32. fsSettings.release();
  33. }

代码中涉及到的变量基本根据变量名理解即可。
步骤【3】读取相机内参,根据config_file文件中model_type的值决定采用何种相机模型,并创建相应模型的对象指针,读取文件在camera_models–>CameraFctory.cc文件中。
步骤【5】和【6】发布和订阅的topic图形表示如下:
在这里插入图片描述在上一节给出的是整个算法中的所有nodes和topics,这里就单独把feature_tracker这个node和相关的topic拿出来了。

三、img_callback()回调函数

接下来看img_callback()回调函数:

  1. void img_callback(const sensor_msgs::ImageConstPtr &img_msg)
  2. {
  3. //[1]判断是否是第一帧
  4. if(first_image_flag)//首先是true
  5. {
  6. first_image_flag = false;
  7. first_image_time = img_msg->header.stamp.toSec();
  8. last_image_time = img_msg->header.stamp.toSec();
  9. return;
  10. }
  11. //[2] 判断时间间隔是否正确,有问题则restart
  12. if (img_msg->header.stamp.toSec() - last_image_time > 1.0 || img_msg->header.stamp.toSec() < last_image_time)
  13. {
  14. ROS_WARN("image discontinue! reset the feature tracker!");
  15. first_image_flag = true;
  16. last_image_time = 0;
  17. pub_count = 1;
  18. std_msgs::Bool restart_flag;
  19. restart_flag.data = true;
  20. pub_restart.publish(restart_flag);
  21. return;
  22. }
  23. last_image_time = img_msg->header.stamp.toSec();
  24. //[3] 发布频率控制,并不是每读入一帧图像,就要发布特征点,通过判断间隔时间内的发布次数
  25. if (round(1.0 * pub_count / (img_msg->header.stamp.toSec() - first_image_time)) <= FREQ)//FREQ=10hz
  26. {
  27. PUB_THIS_FRAME = true;
  28. // reset the frequency control,pub_count=1 时间间隔内的发布频率十分接近设定频率时,更新时间间隔起始时刻,并将数据发布次数置0
  29. if (abs(1.0 * pub_count / (img_msg->header.stamp.toSec() - first_image_time) - FREQ) < 0.01 * FREQ)
  30. {
  31. first_image_time = img_msg->header.stamp.toSec();
  32. pub_count = 0;
  33. }
  34. }
  35. else
  36. PUB_THIS_FRAME = false;
  37. //[4]将图像编码8UC1转换为mono8
  38. cv_bridge::CvImageConstPtr ptr;
  39. if (img_msg->encoding == "8UC1")
  40. {
  41. sensor_msgs::Image img;
  42. img.header = img_msg->header;
  43. img.height = img_msg->height;
  44. img.width = img_msg->width;
  45. img.is_bigendian = img_msg->is_bigendian;
  46. img.step = img_msg->step;
  47. img.data = img_msg->data;
  48. img.encoding = "mono8";
  49. ptr = cv_bridge::toCvCopy(img, sensor_msgs::image_encodings::MONO8);
  50. }
  51. else
  52. ptr = cv_bridge::toCvCopy(img_msg, sensor_msgs::image_encodings::MONO8);
  53. cv::Mat show_img = ptr->image;
  54. TicToc t_r;
  55. for (int i = 0; i < NUM_OF_CAM; i++) //NUM_OF_CAM默认为1
  56. {
  57. ROS_DEBUG("processing camera %d", i);
  58. //[5]单目时:FeatureTracker::readImage() 函数读取图像数据进行处理
  59. if (i != 1 || !STEREO_TRACK) //STEREO_TRACK默认为false
  60. trackerData[i].readImage(ptr->image.rowRange(ROW * i, ROW * (i + 1)), img_msg->header.stamp.toSec());//ROW为图像的height
  61. else
  62. {
  63. if (EQUALIZE)//默认为1,均衡化
  64. {
  65. cv::Ptr<cv::CLAHE> clahe = cv::createCLAHE();//直方图均衡化
  66. clahe->apply(ptr->image.rowRange(ROW * i, ROW * (i + 1)), trackerData[i].cur_img);//cur_img是光流跟踪的前一帧的图像数据
  67. }
  68. else
  69. trackerData[i].cur_img = ptr->image.rowRange(ROW * i, ROW * (i + 1));
  70. }
  71. #if SHOW_UNDISTORTION
  72. trackerData[i].showUndistortion("undistrotion_" + std::to_string(i));
  73. #endif
  74. }
  75. //【6】更新全局ID
  76. for (unsigned int i = 0;; i++)
  77. {
  78. bool completed = false;
  79. for (int j = 0; j < NUM_OF_CAM; j++)
  80. if (j != 1 || !STEREO_TRACK)
  81. completed |= trackerData[j].updateID(i);
  82. if (!completed)
  83. break;
  84. }
  85. //[7]如果PUB_THIS_FRAME=1则进行发布
  86. if (PUB_THIS_FRAME)
  87. {
  88. //将特征点id,矫正后归一化平面的3D点(x,y,z=1),像素2D点(u,v),像素的速度(vx,vy),封装成sensor_msgs::PointCloudPtr类型的feature_points实例中,发布到pub_img;
  89. pub_count++;
  90. sensor_msgs::PointCloudPtr feature_points(new sensor_msgs::PointCloud);
  91. sensor_msgs::ChannelFloat32 id_of_point;
  92. sensor_msgs::ChannelFloat32 u_of_point;
  93. sensor_msgs::ChannelFloat32 v_of_point;
  94. sensor_msgs::ChannelFloat32 velocity_x_of_point;
  95. sensor_msgs::ChannelFloat32 velocity_y_of_point;
  96. feature_points->header = img_msg->header;
  97. feature_points->header.frame_id = "world";
  98. vector<set<int>> hash_ids(NUM_OF_CAM);
  99. for (int i = 0; i < NUM_OF_CAM; i++)
  100. {
  101. auto &un_pts = trackerData[i].cur_un_pts;
  102. auto &cur_pts = trackerData[i].cur_pts;
  103. auto &ids = trackerData[i].ids;
  104. auto &pts_velocity = trackerData[i].pts_velocity;
  105. for (unsigned int j = 0; j < ids.size(); j++)
  106. {
  107. if (trackerData[i].track_cnt[j] > 1)
  108. {
  109. int p_id = ids[j];
  110. hash_ids[i].insert(p_id);
  111. geometry_msgs::Point32 p;
  112. p.x = un_pts[j].x;
  113. p.y = un_pts[j].y;
  114. p.z = 1;
  115. feature_points->points.push_back(p);
  116. id_of_point.values.push_back(p_id * NUM_OF_CAM + i);
  117. u_of_point.values.push_back(cur_pts[j].x);
  118. v_of_point.values.push_back(cur_pts[j].y);
  119. velocity_x_of_point.values.push_back(pts_velocity[j].x);
  120. velocity_y_of_point.values.push_back(pts_velocity[j].y);
  121. }
  122. }
  123. }
  124. feature_points->channels.push_back(id_of_point);
  125. feature_points->channels.push_back(u_of_point);
  126. feature_points->channels.push_back(v_of_point);
  127. feature_points->channels.push_back(velocity_x_of_point);
  128. feature_points->channels.push_back(velocity_y_of_point);
  129. ROS_DEBUG("publish %f, at %f", feature_points->header.stamp.toSec(), ros::Time::now().toSec());
  130. // skip the first image; since no optical speed on frist image
  131. if (!init_pub)
  132. {
  133. init_pub = 1;
  134. }
  135. else
  136. pub_img.publish(feature_points);
  137. //将图像封装到cv_bridge::cvtColor类型的ptr实例中发布到pub_match;
  138. if (SHOW_TRACK)
  139. {
  140. ptr = cv_bridge::cvtColor(ptr, sensor_msgs::image_encodings::BGR8);
  141. //cv::Mat stereo_img(ROW * NUM_OF_CAM, COL, CV_8UC3);
  142. cv::Mat stereo_img = ptr->image;
  143. for (int i = 0; i < NUM_OF_CAM; i++)
  144. {
  145. cv::Mat tmp_img = stereo_img.rowRange(i * ROW, (i + 1) * ROW);//Mat的rowRange和colRange函数可以获取某些范围内行或列的指针
  146. cv::cvtColor(show_img, tmp_img, CV_GRAY2RGB);
  147. for (unsigned int j = 0; j < trackerData[i].cur_pts.size(); j++)
  148. {
  149. double len = std::min(1.0, 1.0 * trackerData[i].track_cnt[j] / WINDOW_SIZE);
  150. cv::circle(tmp_img, trackerData[i].cur_pts[j], 2, cv::Scalar(255 * (1 - len), 0, 255 * len), 2);
  151. //draw speed line
  152. /*
  153. Vector2d tmp_cur_un_pts (trackerData[i].cur_un_pts[j].x, trackerData[i].cur_un_pts[j].y);
  154. Vector2d tmp_pts_velocity (trackerData[i].pts_velocity[j].x, trackerData[i].pts_velocity[j].y);
  155. Vector3d tmp_prev_un_pts;
  156. tmp_prev_un_pts.head(2) = tmp_cur_un_pts - 0.10 * tmp_pts_velocity;
  157. tmp_prev_un_pts.z() = 1;
  158. Vector2d tmp_prev_uv;
  159. trackerData[i].m_camera->spaceToPlane(tmp_prev_un_pts, tmp_prev_uv);
  160. cv::line(tmp_img, trackerData[i].cur_pts[j], cv::Point2f(tmp_prev_uv.x(), tmp_prev_uv.y()), cv::Scalar(255 , 0, 0), 1 , 8, 0);
  161. */
  162. //char name[10];
  163. //sprintf(name, "%d", trackerData[i].ids[j]);
  164. //cv::putText(tmp_img, name, trackerData[i].cur_pts[j], cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(0, 0, 0));
  165. }
  166. }
  167. //cv::imshow("vis", stereo_img);
  168. //cv::waitKey(5);
  169. pub_match.publish(ptr->toImageMsg());
  170. }
  171. }
  172. ROS_INFO("whole feature tracker processing costs: %f", t_r.toc());
  173. }

接下来剩下的内容就是feature_track.h变量的认识以及feature_track.cpp中几个主要函数的介绍,包括
void readImage(const cv::Mat &_img,double _cur_time),void rejectWithF()等函数。下一节再见吧,朋友们!!!

发表评论

表情:
评论列表 (有 0 条评论,73人围观)

还没有评论,来说两句吧...

相关阅读

    相关 暑假学习记录

    转眼已经回家一个礼拜了,我开始学习安装hadoop,放假之前听说hadoopp安装挺难的,但我还是鼓足了勇气去安装。 最开始也想按着教程来装,但是也遇到了许多的疑惑,于是我就

    相关 VINS-Mono代码学习记录(一)

    写在前面的话 看代码真是一个令人头疼的过程,由于代码量很大,而脑子又不够使,看了总是忘,忘了又接着看,所以还是决定写博客来记录一下这个过程,也方便自己日后回忆是咋学的,哈