赞
踩
2021SC@SDUSC
在上一章,我们大致把visual_odometry
的main
函数过了一遍,把节点在生成回调函数之前的准备工作介绍了一遍。我们已经大致了解了节点的输入和输出。其实,通过上一章的分析,可以发现,这个节点和我之前分析的visual_feature
的关联还是比较紧密,需要用到visual_feature
发布的视觉特征点。
下面,我们开始分析这个节点最重要的回调函数部分,以及主线程process
。一共有4个,分别是:imu_callback
,feature_callback
,odom_callback
,restart_callback
。
其中,imu_callback
,主线程process
是主要部分。
imu_callback
IMU回调函数,将imu_msg
保存到imu_buf
,IMU状态递推并发布[P,Q,V,header, failureCount]
.
这里判断每个IMU数据帧到达的时间顺序是否正确,如果不正确则会提示,并且不处理。如果正确,继续往下执行,并保存这一帧的时间,给下一帧的时间做判断。
// 判断这一帧的时间是否小于等于上一帧的时间,如果结果为true,则说明乱序,时间顺序错误
if (imu_msg->header.stamp.toSec() <= last_imu_t)
{
ROS_WARN("imu message in disorder!");
return;
}
last_imu_t = imu_msg->header.stamp.toSec();
通过加锁,防止多线程访问IMU数据缓存队列出现问题。并在取出数据之后,通知主线程process,从阻塞状态唤醒。唤醒的具体功能,见下面注释。
m_buf.lock();
imu_buf.push(imu_msg);
m_buf.unlock();
con.notify_one(); // 唤醒作用于process线程中的获取观测值数据的函数
last_imu_t = imu_msg->header.stamp.toSec(); // 不知道为啥又加了一次赋值操作
首先,所有代码都被一个大括号包围住,表示这是一个作用域。那为什么要这么做呢?其实是因为里面的第一行的互斥锁lock_guard
。当第一行代码执行的时候,相当于加了锁,当离开这个作用域的时候,临时变量会释放,而lock_guard
析构的时候就会解锁。这相当于一个编程的小技巧。
接着执行predict函数,这个函数的作用是从IMU测量值imu_msg
和上一个PVQ
递推得到下一个tmp_Q
,tmp_P
,tmp_V
,使用中值积分。
最后,发布最新的由IMU直接递推得到的PQV
。
{
// 构造互斥锁m_state,析构时解锁
std::lock_guard<std::mutex> lg(m_state);
predict(imu_msg); // 递推得到IMU的PQV
std_msgs::Header header = imu_msg->header;
// 发布最新的由IMU直接递推得到的PQV
if (estimator.solver_flag == Estimator::SolverFlag::NON_LINEAR)
pubLatestOdometry(tmp_P, tmp_Q, tmp_V, header, estimator.failureCount);
}
从IMU测量值imu_msg
和上一个PQV
递推得到下一个tmp_Q
,tmp_P
,tmp_V
。下面的代码就是使用积分中值定理得到tmp_Q
,tmp_P
,tmp_V
. 不过这里我也不是特别了解。
void predict(const sensor_msgs::ImuConstPtr &imu_msg) { double t = imu_msg->header.stamp.toSec(); // 如果是第一个IMU的数据,啧init_imu为1,不处理,直接返回 if (init_imu) { latest_time = t; init_imu = 0; return; } double dt = t - latest_time; latest_time = t; double dx = imu_msg->linear_acceleration.x; double dy = imu_msg->linear_acceleration.y; double dz = imu_msg->linear_acceleration.z; Eigen::Vector3d linear_acceleration{dx, dy, dz}; double rx = imu_msg->angular_velocity.x; double ry = imu_msg->angular_velocity.y; double rz = imu_msg->angular_velocity.z; Eigen::Vector3d angular_velocity{rx, ry, rz}; Eigen::Vector3d un_acc_0 = tmp_Q * (acc_0 - tmp_Ba) - estimator.g; Eigen::Vector3d un_gyr = 0.5 * (gyr_0 + angular_velocity) - tmp_Bg; tmp_Q = tmp_Q * Utility::deltaQ(un_gyr * dt); Eigen::Vector3d un_acc_1 = tmp_Q * (linear_acceleration - tmp_Ba) - estimator.g; Eigen::Vector3d un_acc = 0.5 * (un_acc_0 + un_acc_1); tmp_P = tmp_P + dt * tmp_V + 0.5 * dt * dt * un_acc; tmp_V = tmp_V + dt * un_acc; acc_0 = linear_acceleration; gyr_0 = angular_velocity; }
void imu_callback(const sensor_msgs::ImuConstPtr &imu_msg) { if (imu_msg->header.stamp.toSec() <= last_imu_t) { ROS_WARN("imu message in disorder!"); return; } last_imu_t = imu_msg->header.stamp.toSec(); m_buf.lock(); imu_buf.push(imu_msg); m_buf.unlock(); con.notify_one(); last_imu_t = imu_msg->header.stamp.toSec(); { std::lock_guard<std::mutex> lg(m_state); predict(imu_msg); std_msgs::Header header = imu_msg->header; if (estimator.solver_flag == Estimator::SolverFlag::NON_LINEAR) pubLatestOdometry(tmp_P, tmp_Q, tmp_V, header, estimator.failureCount); } }
odom_callback
这个函数就是简单的把重定位后的里程计信息放入缓存队列中。
void odom_callback(const nav_msgs::Odometry::ConstPtr& odom_msg)
{
m_odom.lock();
odomQueue.push_back(*odom_msg);
m_odom.unlock();
}
feature_callback
这个函数也较为简单,先验证是否是第一帧,如果是的话,不处理。因为我前面的blog有分析到,我们对于第一帧图像是认为没有光流速度的,所以,没有办法追踪并找到特征点。后面的话,也是把消息放到缓冲区内,然后,也需要和imu_callback
一样,通知主线程。
void feature_callback(const sensor_msgs::PointCloudConstPtr &feature_msg)
{
if (!init_feature)
{
//skip the first detected feature, which doesn't contain optical flow speed
init_feature = 1;
return;
}
m_buf.lock();
feature_buf.push(feature_msg);
m_buf.unlock();
con.notify_one();
}
restart_callback
这里的话,是会从visual_feature
节点可能会接受到重启的消息,我之前的blog也有分析到,如果相机的数据流不稳定,是需要发布restart消息。这里可以看我的第11篇blog。然后再重启之前,我们需要清空缓冲区,并且重置所有数据和状态。
void restart_callback(const std_msgs::BoolConstPtr &restart_msg) { if (restart_msg->data == true) { ROS_WARN("restart the estimator!"); m_buf.lock(); while(!feature_buf.empty()) feature_buf.pop(); while(!imu_buf.empty()) imu_buf.pop(); m_buf.unlock(); m_estimator.lock(); estimator.clearState(); estimator.setParameter(); m_estimator.unlock(); current_time = -1; last_imu_t = 0; } return; }
这个线程是整个节点最重要的部分。
process
里面只有一个while循环,运行条件为ros::ok()
。因此,如果当前节点正在运行,ros::ok()
为true
,为无限循环。
接下来是while循环里面的功能。首先是等待并获取measurements:(IMUs, img_msg)s
,计算dt。接着使用,estimator.processIMU()
进行IMU预积分。然后,通过estimator.setReloFrame()
设置重定位帧。最后,estimator.processImage()
处理图像帧:初始化,紧耦合的非线性优化。
一开始,先创建临时变量,用来保存提取到的measurements。然后,对m_buf
创建唯一锁,防止访问共享内存时出现问题。然后进入陷入等待。如果唤醒了,会给m_buf
加上锁,然后执行getMeasurements
函数,获得相关的值,然后判断取出来的measurement
的size
值是不是不等于0,如果不等于0,则继续往下执行。如果等于0,则继续挂起。
std::vector<std::pair<std::vector<sensor_msgs::ImuConstPtr>, sensor_msgs::PointCloudConstPtr>> measurements;
std::unique_lock<std::mutex> lk(m_buf);
con.wait(lk, [&]
{
return (measurements = getMeasurements()).size() != 0;
});
lk.unlock();
getMeasurements()
函数 这里的返回值是数据对数组。其中数组对中的第一个数据为IMU数组,第二个数据为img_msg。这里将缓冲区imu_buf
和feature_buf
的数据都取出来。除此之外,还让IMU数据和图像进行对齐并组合。其中对齐的方式是要让所有数据帧的时间一定要顺序。具体的对齐方法,见下面的代码注释。
std::vector<std::pair<std::vector<sensor_msgs::ImuConstPtr>, sensor_msgs::PointCloudConstPtr>> getMeasurements() { // 返回结果 std::vector<std::pair<std::vector<sensor_msgs::ImuConstPtr>, sensor_msgs::PointCloudConstPtr>> measurements; while (ros::ok()) { if (imu_buf.empty() || feature_buf.empty()) return measurements; // 对齐标准:IMU最后一个数据的时间要大于第一个图像特征数据的时间 if (!(imu_buf.back()->header.stamp.toSec() > feature_buf.front()->header.stamp.toSec() + estimator.td)) { return measurements; } // 对齐标准:IMU第一个数据的时间要小于第一个图像特征数据的时间 if (!(imu_buf.front()->header.stamp.toSec() < feature_buf.front()->header.stamp.toSec() + estimator.td)) { ROS_WARN("throw img, only should happen at the beginning"); feature_buf.pop(); continue; } sensor_msgs::PointCloudConstPtr img_msg = feature_buf.front(); feature_buf.pop(); std::vector<sensor_msgs::ImuConstPtr> IMUs; // 图像数据(img_msg),对应多组在时间戳内的imu数据,然后塞入measurements while (imu_buf.front()->header.stamp.toSec() < img_msg->header.stamp.toSec() + estimator.td) { IMUs.emplace_back(imu_buf.front()); imu_buf.pop(); } // 这里把下一个imu_msg也放进去了,但没有pop,因此当前图像帧和下一图像帧会共用这个imu_msg IMUs.emplace_back(imu_buf.front()); if (IMUs.empty()) ROS_WARN("no imu between two image"); measurements.emplace_back(IMUs, img_msg); } return measurements; }
下面开始正式的处理。首先会加上m_estimator.lock()
,这里是为了防止正在执行主线程的时候,突然visual_feature
节点突然来了一个重启信号,导致同时访问接下来操作要访问到的共享缓冲区以及共享变量。然后,接着为一个for-each
操作,把measurements数组的每一个元素取出来,然后循环执行for里面的内容。这一部分的代码就不单独显示,会放在后面的完整代码里面。
接着就是对每一个measurement
元素进行操作。首先IMU的预积分。首先就是把数据取出来,然后接着就预积分,详细见下面代码注释。
auto img_msg = measurement.second; // IMU的预积分 double dx = 0, dy = 0, dz = 0, rx = 0, ry = 0, rz = 0; for (auto &imu_msg : measurement.first) { double t = imu_msg->header.stamp.toSec(); double img_t = img_msg->header.stamp.toSec() + estimator.td; if (t <= img_t) { if (current_time < 0) current_time = t; double dt = t - current_time; ROS_ASSERT(dt >= 0); current_time = t; dx = imu_msg->linear_acceleration.x; dy = imu_msg->linear_acceleration.y; dz = imu_msg->linear_acceleration.z; rx = imu_msg->angular_velocity.x; ry = imu_msg->angular_velocity.y; rz = imu_msg->angular_velocity.z; // 这里就是执行IMU预积分的地方 estimator.processIMU(dt, Vector3d(dx, dy, dz), Vector3d(rx, ry, rz)); //printf("imu: dt:%f a: %f %f %f w: %f %f %f\n",dt, dx, dy, dz, rx, ry, rz); } else { double dt_1 = img_t - current_time; double dt_2 = t - img_t; current_time = img_t; ROS_ASSERT(dt_1 >= 0); ROS_ASSERT(dt_2 >= 0); ROS_ASSERT(dt_1 + dt_2 > 0); double w1 = dt_2 / (dt_1 + dt_2); double w2 = dt_1 / (dt_1 + dt_2); dx = w1 * dx + w2 * imu_msg->linear_acceleration.x; dy = w1 * dy + w2 * imu_msg->linear_acceleration.y; dz = w1 * dz + w2 * imu_msg->linear_acceleration.z; rx = w1 * rx + w2 * imu_msg->angular_velocity.x; ry = w1 * ry + w2 * imu_msg->angular_velocity.y; rz = w1 * rz + w2 * imu_msg->angular_velocity.z; // 这里就是执行IMU预积分的地方 estimator.processIMU(dt_1, Vector3d(dx, dy, dz), Vector3d(rx, ry, rz)); //printf("dimu: dt:%f a: %f %f %f w: %f %f %f\n",dt_1, dx, dy, dz, rx, ry, rz); } }
processIMU()
函数 这里主要处理IMU数据。让IMU预积分,中值积分得到当前PQV作为优化初值。
oid Estimator::processIMU(double dt, const Vector3d &linear_acceleration, const Vector3d &angular_velocity) { if (!first_imu) { first_imu = true; acc_0 = linear_acceleration; gyr_0 = angular_velocity; } if (!pre_integrations[frame_count]) { pre_integrations[frame_count] = new IntegrationBase{acc_0, gyr_0, Bas[frame_count], Bgs[frame_count]}; } if (frame_count != 0) { pre_integrations[frame_count]->push_back(dt, linear_acceleration, angular_velocity); tmp_pre_integration->push_back(dt, linear_acceleration, angular_velocity); dt_buf[frame_count].push_back(dt); linear_acceleration_buf[frame_count].push_back(linear_acceleration); angular_velocity_buf[frame_count].push_back(angular_velocity); // 采用的是中值积分的传播方式 int j = frame_count; Vector3d un_acc_0 = Rs[j] * (acc_0 - Bas[j]) - g; Vector3d un_gyr = 0.5 * (gyr_0 + angular_velocity) - Bgs[j]; Rs[j] *= Utility::deltaQ(un_gyr * dt).toRotationMatrix(); Vector3d un_acc_1 = Rs[j] * (linear_acceleration - Bas[j]) - g; Vector3d un_acc = 0.5 * (un_acc_0 + un_acc_1); Ps[j] += dt * Vs[j] + 0.5 * dt * dt * un_acc; Vs[j] += dt * un_acc; } acc_0 = linear_acceleration; gyr_0 = angular_velocity; }
这里建立每个特征点的(camera_id,[x,y,z,u,v,vx,vy])s的map,索引为feature_id。
map<int, vector<pair<int, Eigen::Matrix<double, 8, 1>>>> image; for (unsigned int i = 0; i < img_msg->points.size(); i++) { int v = img_msg->channels[0].values[i] + 0.5; int feature_id = v / NUM_OF_CAM; int camera_id = v % NUM_OF_CAM; double x = img_msg->points[i].x; double y = img_msg->points[i].y; double z = img_msg->points[i].z; double p_u = img_msg->channels[1].values[i]; double p_v = img_msg->channels[2].values[i]; double velocity_x = img_msg->channels[3].values[i]; double velocity_y = img_msg->channels[4].values[i]; double depth = img_msg->channels[5].values[i]; ROS_ASSERT(z == 1); Eigen::Matrix<double, 8, 1> xyz_uv_velocity_depth; xyz_uv_velocity_depth << x, y, z, p_u, p_v, velocity_x, velocity_y, depth; image[feature_id].emplace_back(camera_id, xyz_uv_velocity_depth); }
vector<float> initialization_info;
m_odom.lock();
initialization_info = odomRegister->getOdometry(odomQueue, img_msg->header.stamp.toSec() + estimator.td);
m_odom.unlock();
getOdometry()
函数 这里属于初始化的内容,可以看我另外一个同学的分析。我这里只是简单贴一下代码。简单的来说,这个函数的作用是将里程计信息,从激光雷达帧转到VINS的图像帧中。下面是我简单的代码分析。
vector<float> getOdometry(deque<nav_msgs::Odometry>& odomQueue, double img_time) { vector<float> odometry_channel; odometry_channel.resize(18, -1); // 重新设置 id(1), P(3), Q(4), V(3), Ba(3), Bg(3), gravity(1) nav_msgs::Odometry odomCur; // 把旧的里程计信息扔掉,类似我前面的blog中的分析。 while (!odomQueue.empty()) { if (odomQueue.front().header.stamp.toSec() < img_time - 0.05) odomQueue.pop_front(); else break; } if (odomQueue.empty()) { return odometry_channel; } // 找到最接近图像时间的里程计时间 for (int i = 0; i < (int)odomQueue.size(); ++i) { odomCur = odomQueue[i]; if (odomCur.header.stamp.toSec() < img_time - 0.002) // 500Hz imu continue; else break; } // 时间戳差异仍然太大,进行处理 if (abs(odomCur.header.stamp.toSec() - img_time) > 0.05) { return odometry_channel; } // 将里程计旋转从激光雷达 ROS 框架转换为 VINS 相机框架(仅旋转,假设激光雷达、相机和 IMU 足够接近) tf::Quaternion q_odom_lidar; tf::quaternionMsgToTF(odomCur.pose.pose.orientation, q_odom_lidar); tf::Quaternion q_odom_cam = tf::createQuaternionFromRPY(0, 0, M_PI) * (q_odom_lidar * q_lidar_to_cam); // 全局旋转 pi // 注意:相机 - 激光雷达 tf::quaternionTFToMsg(q_odom_cam, odomCur.pose.pose.orientation); // 将里程计位置从激光雷达 ROS 框架转换为 VINS 相机框架 Eigen::Vector3d p_eigen(odomCur.pose.pose.position.x, odomCur.pose.pose.position.y, odomCur.pose.pose.position.z); Eigen::Vector3d v_eigen(odomCur.twist.twist.linear.x, odomCur.twist.twist.linear.y, odomCur.twist.twist.linear.z); Eigen::Vector3d p_eigen_new = q_lidar_to_cam_eigen * p_eigen; Eigen::Vector3d v_eigen_new = q_lidar_to_cam_eigen * v_eigen; odomCur.pose.pose.position.x = p_eigen_new.x(); odomCur.pose.pose.position.y = p_eigen_new.y(); odomCur.pose.pose.position.z = p_eigen_new.z(); odomCur.twist.twist.linear.x = v_eigen_new.x(); odomCur.twist.twist.linear.y = v_eigen_new.y(); odomCur.twist.twist.linear.z = v_eigen_new.z(); // odomCur.header.stamp = ros::Time().fromSec(img_time); // odomCur.header.frame_id = "vins_world"; // odomCur.child_frame_id = "vins_body"; // pub_latest_odometry.publish(odomCur); odometry_channel[0] = odomCur.pose.covariance[0]; odometry_channel[1] = odomCur.pose.pose.position.x; odometry_channel[2] = odomCur.pose.pose.position.y; odometry_channel[3] = odomCur.pose.pose.position.z; odometry_channel[4] = odomCur.pose.pose.orientation.x; odometry_channel[5] = odomCur.pose.pose.orientation.y; odometry_channel[6] = odomCur.pose.pose.orientation.z; odometry_channel[7] = odomCur.pose.pose.orientation.w; odometry_channel[8] = odomCur.twist.twist.linear.x; odometry_channel[9] = odomCur.twist.twist.linear.y; odometry_channel[10] = odomCur.twist.twist.linear.z; odometry_channel[11] = odomCur.pose.covariance[1]; odometry_channel[12] = odomCur.pose.covariance[2]; odometry_channel[13] = odomCur.pose.covariance[3]; odometry_channel[14] = odomCur.pose.covariance[4]; odometry_channel[15] = odomCur.pose.covariance[5]; odometry_channel[16] = odomCur.pose.covariance[6]; odometry_channel[17] = odomCur.pose.covariance[7]; return odometry_channel; }
接下来就是把数据通过话题,传给RVIZ了。传输的内容,见下面的注释。至于发布信息的函数,就不详细分析了,就是简单的把数据取出,然后封装起来,发送到话题上。到这里,for-each
循环已经结束了,然后就是解锁:m_estimator.unlock();
。
std_msgs::Header header = img_msg->header;
pubOdometry(estimator, header); // "odometry" 里程计信息PQV
pubKeyPoses(estimator, header); // "key_poses" 关键点三维坐标
pubCameraPose(estimator, header); // "camera_pose" 相机位姿
pubPointCloud(estimator, header); // "history_cloud" 点云信息
pubTF(estimator, header); // "extrinsic" 相机到IMU的外参
pubKeyframe(estimator); // "keyframe_point"、"keyframe_pose" 关键帧位姿和点云
m_buf.lock();
m_state.lock();
if (estimator.solver_flag == Estimator::SolverFlag::NON_LINEAR)
update(); // 更新操作
m_state.unlock();
m_buf.unlock();
update()
函数 从estimator
中得到滑动窗口当前图像帧的imu更新项[P,Q,V,ba,bg,a,g]
。然后对imu_buf
中剩余的imu_msg
进行PVQ递推。predict函数在上面有分析过。至此,主线程函数分析完成!
void update() { TicToc t_predict; latest_time = current_time; tmp_P = estimator.Ps[WINDOW_SIZE]; tmp_Q = estimator.Rs[WINDOW_SIZE]; tmp_V = estimator.Vs[WINDOW_SIZE]; tmp_Ba = estimator.Bas[WINDOW_SIZE]; tmp_Bg = estimator.Bgs[WINDOW_SIZE]; acc_0 = estimator.acc_0; gyr_0 = estimator.gyr_0; queue<sensor_msgs::ImuConstPtr> tmp_imu_buf = imu_buf; for (sensor_msgs::ImuConstPtr tmp_imu_msg; !tmp_imu_buf.empty(); tmp_imu_buf.pop()) predict(tmp_imu_buf.front()); }
void process() { while (ros::ok()) { std::vector<std::pair<std::vector<sensor_msgs::ImuConstPtr>, sensor_msgs::PointCloudConstPtr>> measurements; std::unique_lock<std::mutex> lk(m_buf); con.wait(lk, [&] { return (measurements = getMeasurements()).size() != 0; }); lk.unlock(); m_estimator.lock(); for (auto &measurement : measurements) { auto img_msg = measurement.second; double dx = 0, dy = 0, dz = 0, rx = 0, ry = 0, rz = 0; for (auto &imu_msg : measurement.first) { double t = imu_msg->header.stamp.toSec(); double img_t = img_msg->header.stamp.toSec() + estimator.td; if (t <= img_t) { if (current_time < 0) current_time = t; double dt = t - current_time; ROS_ASSERT(dt >= 0); current_time = t; dx = imu_msg->linear_acceleration.x; dy = imu_msg->linear_acceleration.y; dz = imu_msg->linear_acceleration.z; rx = imu_msg->angular_velocity.x; ry = imu_msg->angular_velocity.y; rz = imu_msg->angular_velocity.z; estimator.processIMU(dt, Vector3d(dx, dy, dz), Vector3d(rx, ry, rz)); //printf("imu: dt:%f a: %f %f %f w: %f %f %f\n",dt, dx, dy, dz, rx, ry, rz); } else { double dt_1 = img_t - current_time; double dt_2 = t - img_t; current_time = img_t; ROS_ASSERT(dt_1 >= 0); ROS_ASSERT(dt_2 >= 0); ROS_ASSERT(dt_1 + dt_2 > 0); double w1 = dt_2 / (dt_1 + dt_2); double w2 = dt_1 / (dt_1 + dt_2); dx = w1 * dx + w2 * imu_msg->linear_acceleration.x; dy = w1 * dy + w2 * imu_msg->linear_acceleration.y; dz = w1 * dz + w2 * imu_msg->linear_acceleration.z; rx = w1 * rx + w2 * imu_msg->angular_velocity.x; ry = w1 * ry + w2 * imu_msg->angular_velocity.y; rz = w1 * rz + w2 * imu_msg->angular_velocity.z; estimator.processIMU(dt_1, Vector3d(dx, dy, dz), Vector3d(rx, ry, rz)); //printf("dimu: dt:%f a: %f %f %f w: %f %f %f\n",dt_1, dx, dy, dz, rx, ry, rz); } } // TicToc t_s; map<int, vector<pair<int, Eigen::Matrix<double, 8, 1>>>> image; for (unsigned int i = 0; i < img_msg->points.size(); i++) { int v = img_msg->channels[0].values[i] + 0.5; int feature_id = v / NUM_OF_CAM; int camera_id = v % NUM_OF_CAM; double x = img_msg->points[i].x; double y = img_msg->points[i].y; double z = img_msg->points[i].z; double p_u = img_msg->channels[1].values[i]; double p_v = img_msg->channels[2].values[i]; double velocity_x = img_msg->channels[3].values[i]; double velocity_y = img_msg->channels[4].values[i]; double depth = img_msg->channels[5].values[i]; ROS_ASSERT(z == 1); Eigen::Matrix<double, 8, 1> xyz_uv_velocity_depth; xyz_uv_velocity_depth << x, y, z, p_u, p_v, velocity_x, velocity_y, depth; image[feature_id].emplace_back(camera_id, xyz_uv_velocity_depth); } vector<float> initialization_info; m_odom.lock(); initialization_info = odomRegister->getOdometry(odomQueue, img_msg->header.stamp.toSec() + estimator.td); m_odom.unlock(); estimator.processImage(image, initialization_info, img_msg->header); // double whole_t = t_s.toc(); // printStatistics(estimator, whole_t); std_msgs::Header header = img_msg->header; pubOdometry(estimator, header); pubKeyPoses(estimator, header); pubCameraPose(estimator, header); pubPointCloud(estimator, header); pubTF(estimator, header); pubKeyframe(estimator); } m_estimator.unlock(); m_buf.lock(); m_state.lock(); if (estimator.solver_flag == Estimator::SolverFlag::NON_LINEAR) update(); m_state.unlock(); m_buf.unlock(); } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。