赞
踩
2021SC@SDUSC
本次分析主要是对imuHandler函数的分析。
/* 然后是里程计回调函数。 每隔100帧激光里程计,重置ISAM2优化器,添加里程计、速度、偏置先验因子,执行优化。 计算前一帧激光里程计与当前帧激光里程计之间的imu预积分量,用前一帧状态施加预积分量得到当前帧初始状态估计,添加来自mapOptimization的当前帧位姿,进行因子图优化,更新当前帧状态。 优化之后,执行重传播。获得 imu 真实的 bias,用来计算当前时刻之后的 imu 预积分。 */ void odometryHandler(const nav_msgs::Odometry::ConstPtr& odomMsg) { // 当前帧激光里程计时间戳 double currentCorrectionTime = ROS_TIME(odomMsg); // make sure we have imu data to integrate // 确保imu优化队列中有imu数据进行预积分 //在imuHander函数中,会将imu数据push到队列中;数据是imu原始数据经过 旋转但没平移 到雷达坐标系下的数据 if (imuQueOpt.empty()) return; 当前帧激光位姿,来自scan-to-map匹配、因子图优化后的位姿 //位置 和 方向 float p_x = odomMsg->pose.pose.position.x; float p_y = odomMsg->pose.pose.position.y; float p_z = odomMsg->pose.pose.position.z; float r_x = odomMsg->pose.pose.orientation.x; float r_y = odomMsg->pose.pose.orientation.y; float r_z = odomMsg->pose.pose.orientation.z; float r_w = odomMsg->pose.pose.orientation.w; //四舍五入 // int currentResetId = round(odomMsg->pose.covariance[0]); //用订阅的激光雷达里程计消息 初始化一个lidarPose 包括一个四元数和points gtsam::Pose3 lidarPose = gtsam::Pose3(gtsam::Rot3::Quaternion(r_w, r_x, r_y, r_z), gtsam::Point3(p_x, p_y, p_z)); // correction pose jumped, reset imu pre-integration 当前里程计位姿 id 保存在协方差的第一项,如果和当前 imu 位姿 id 不同,说明有跳变,重置优化 //跟那个图有关系(imu和lidar的发布的频率对比图 id相同代表处在同一个时间段内???????好像不是 根据之后的 暂时判断:有若干个雷达里程计帧属于同一个id) //id不同 可能是因为误差太大了 //暂时根据imuHandler中,发布odometry来看,两针激光之间的很多帧imu数据,对每一帧imu,做预积分处理,这些imu帧属于同一个imu预积分项 // imuPreintegrationResetId 代表的是预积分的id(即id相同的里程计中的imu的数据属于同一个预积分器) if (currentResetId != imuPreintegrationResetId) { //如果当前的里程计已经属于 另一个时间段了,就重置参数 //这个可能不是什么正常现象?????????????? resetParams(); imuPreintegrationResetId = currentResetId; return; } // 0. initialize system // 系统初始化,第一帧 if (systemInitialized == false) { // 重置优化参数,也就是重置 ISAM2 优化器 // 将 relinearizeThreshold 赋值为 0.1,将relinearizeSkip 赋值为 1 // new 分配出新的 graphFactors 和 graphValues resetOptimization(); // pop old IMU message // 从imu优化队列中删除当前帧激光里程计时刻之前的imu数据(将过期,即时间戳<当前激光里程计的时间戳的数据都弹出) while (!imuQueOpt.empty()) { //delta_t是个常量 = 0 //if 当前opt队首的元素的时间戳 < 当前激光里程计时间戳 if (ROS_TIME(&imuQueOpt.front()) < currentCorrectionTime - delta_t) { //将队首元素的时间戳记录下来,更新lastImuT_opt ,将元素弹出 lastImuT_opt = ROS_TIME(&imuQueOpt.front()); imuQueOpt.pop_front(); } //直到时间戳> else break; } // initial pose // 添加 里程计位姿先验因子 //lidarPose.compose(lidar2Imu); 将雷达位置转换到Imu坐标系下 prevPose_ = lidarPose.compose(lidar2Imu); /* PriorFactor(Key key, const VALUE& prior, const SharedNoiseModel& model = nullptr) : Base(model, key), prior_(prior) { } */ gtsam::PriorFactor<gtsam::Pose3> priorPose(X(0), prevPose_, priorPoseNoise); //先验因子中的参数:位姿信息和 噪声:在构造函数中初始化过,是一个常量(自己测量出来的) graphFactors.add(priorPose); // initial velocity // 添加里程计速度先验因子 prevVel_ = gtsam::Vector3(0, 0, 0); gtsam::PriorFactor<gtsam::Vector3> priorVel(V(0), prevVel_, priorVelNoise); //类似于位姿先验因子的添加 graphFactors.add(priorVel); // initial bias // 添加imu偏置先验因子 prevBias_ = gtsam::imuBias::ConstantBias(); /* ConstantBias() : biasAcc_(0.0, 0.0, 0.0), biasGyro_(0.0, 0.0, 0.0) { } */ gtsam::PriorFactor<gtsam::imuBias::ConstantBias> priorBias(B(0), prevBias_, priorBiasNoise); graphFactors.add(priorBias); // add values // 变量节点赋初值 graphValues.insert(X(0), prevPose_); graphValues.insert(V(0), prevVel_); graphValues.insert(B(0), prevBias_); // optimize once // 假定起始为 0 速度,进行一次优化,用于刚开始的 scan-matching // 对于起始速度小于 10 m/s,角速度小于 180°/s,效果都非常好 // 但是这总归是基于 0 起始速度估计的,起始估计并不是实际情况,因此清除图优化中内容 optimizer.update(graphFactors, graphValues); //我的理解:暂时理解成,用一组虚假的数据做一次优化,进行初始化;但是毕竟是假的,所以要在因子图中删掉这些数据 graphFactors.resize(0); graphValues.clear(); //重置预积分器(这个代码块内是初始化) imuIntegratorImu_->resetIntegrationAndSetBias(prevBias_); imuIntegratorOpt_->resetIntegrationAndSetBias(prevBias_); //key=1 有一帧 //系统初始化成功 key = 1; systemInitialized = true; return; } /* 暂时认为: 根据imuhandler的那个图来看, 激光雷达里程计帧后面跟着很多imu帧,这是一个连续的过程 在imuhandler函数中,会将每一帧imu数据添加到队列imu和opt中 在这个函数中,会将当前的激光雷达帧的之前的imu数据帧(上一帧激光后,在opt队列中)都进行预积分,然后进行预测(imuhandler中是 进行一次预积分,就做一次预测) resetParam可能是因为过程中发现误差过大,需要重新进行优化了 100帧后重置就纯粹是因为内存不够? */ // reset graph for speed // 之后会有一个++key // 每隔100帧激光里程计,重置ISAM2优化器,保证优化效率,防止内存溢出 if (key == 100) { // get updated noise before reset // 根据最近的下标即 99,获得前一帧的位姿、速度、偏置噪声模型 作为初始化 //marginalCovariance 返回值是一个矩阵 gtsam::noiseModel::Gaussian::shared_ptr updatedPoseNoise = gtsam::noiseModel::Gaussian::Covariance(optimizer.marginalCovariance(X(key-1))); gtsam::noiseModel::Gaussian::shared_ptr updatedVelNoise = gtsam::noiseModel::Gaussian::Covariance(optimizer.marginalCovariance(V(key-1))); gtsam::noiseModel::Gaussian::shared_ptr updatedBiasNoise = gtsam::noiseModel::Gaussian::Covariance(optimizer.marginalCovariance(B(key-1))); // reset graph //优化系统开始前,也要进行重置操作 //这里的重置是被迫的, //在wiki中没有找到key类,暂时理解成x(0)类似于键值对的中的key对应后面的噪声 //内部源代码瞅着贼复杂,暂时先不看的(里面一堆typedef重命名看着乱七八糟的) resetOptimization(); // add pose gtsam::PriorFactor<gtsam::Pose3> priorPose(X(0), prevPose_, updatedPoseNoise); graphFactors.add(priorPose); // add velocity gtsam::PriorFactor<gtsam::Vector3> priorVel(V(0), prevVel_, updatedVelNoise); graphFactors.add(priorVel); // add bias gtsam::PriorFactor<gtsam::imuBias::ConstantBias> priorBias(B(0), prevBias_, updatedBiasNoise); graphFactors.add(priorBias); // add values graphValues.insert(X(0), prevPose_); graphValues.insert(V(0), prevVel_); graphValues.insert(B(0), prevBias_); // optimize once optimizer.update(graphFactors, graphValues); graphFactors.resize(0); graphValues.clear(); key = 1; //楼上的步骤系统初始化时候的步骤相同 } // 正常步骤开始 // 如论文中所述,初始化完成后就可以估计 IMU 偏差,机器人位姿,速度 // 计算前一帧与当前帧之间的imu预积分量,用前一帧状态施加预积分量得到当前帧初始状态估计, // 添加来自mapOptimization的当前帧位姿,进行因子图优化,更新当前帧状态 // 1. integrate imu data and optimize while (!imuQueOpt.empty()) { // pop and integrate imu data that is between two optimizations // 提取前一帧与当前帧之间的imu数据,计算预积分 sensor_msgs::Imu *thisImu = &imuQueOpt.front(); //这一帧imu的时间戳 double imuTime = ROS_TIME(thisImu); // 判断这一帧是否已经超过了当前激光雷达帧的时间戳 if (imuTime < currentCorrectionTime - delta_t) { //没超过 // 时间差初始值为 1/500 s,否则为与上一帧的时间差 double dt = (lastImuT_opt < 0) ? (1.0 / 500.0) : (imuTime - lastImuT_opt); // 此处积分只用到了线加速度和角速度 // 加入的是这个用来 因子图优化 的预积分器imuIntegratorOpt_,注意加入了上一步算出的dt //作者要求的9轴imu数据中欧拉角在本程序文件中没有任何用到,全在地图优化里用到的 imuIntegratorOpt_->integrateMeasurement( gtsam::Vector3(thisImu->linear_acceleration.x, thisImu->linear_acceleration.y, thisImu->linear_acceleration.z), gtsam::Vector3(thisImu->angular_velocity.x, thisImu->angular_velocity.y, thisImu->angular_velocity.z), dt); lastImuT_opt = imuTime; //从队列中删除已经处理的imu数据 imuQueOpt.pop_front(); } else break; } // add imu factor to graph // 加imu预积分因子 //利用两帧之间的IMU数据完成了预积分后增加imu因子到因子图中, //注意后面容易被遮挡,imuIntegratorOpt_的值经过格式转换被传入preint_imu, // 因此可以推测imuIntegratorOpt_中的integrateMeasurement函数应该就是一个简单的积分轮子,???? //传入数据和dt,得到一个积分量,数据会被存放在imuIntegratorOpt_中 /* 用法:dynamic_cast < type-id > ( expression ) 运算符把expression转换成type-id类型的对象。Type-id必须是类的指针、类的引用或者void *; 如果type-id是类指针类型,那么expression也必须是一个指针,如果type-id是一个引用,那么expression也必须是一个引用。 dynamic_cast主要用于类层次间的上行转换和下行转换,还可以用于类之间的交叉转换。 */ //楼下这些 参考imu的预积分公式中的一系列参数 const gtsam::PreintegratedImuMeasurements& preint_imu = dynamic_cast<const gtsam::PreintegratedImuMeasurements&>(*imuIntegratorOpt_); // 参数:前一帧位姿,前一帧速度,当前帧位姿,当前帧速度,前一帧偏置,预积分量 gtsam::ImuFactor imu_factor(X(key - 1), V(key - 1), X(key), V(key), B(key - 1), preint_imu); graphFactors.add(imu_factor); // add imu bias between factor // 添加imu偏置因子,前一帧偏置,当前帧偏置,观测值,噪声协方差;deltaTij()是积分段的时间 graphFactors.add(gtsam::BetweenFactor<gtsam::imuBias::ConstantBias>(B(key - 1), B(key), gtsam::imuBias::ConstantBias(), gtsam::noiseModel::Diagonal::Sigmas(sqrt(imuIntegratorOpt_->deltaTij()) * noiseModelBetweenBias))); // add pose factor gtsam::Pose3 curPose = lidarPose.compose(lidar2Imu); gtsam::PriorFactor<gtsam::Pose3> pose_factor(X(key), curPose, correctionNoise); //添加位姿因子 graphFactors.add(pose_factor); // insert predicted values //imuHandler中的imuInteratorImu_中的predict是每一帧imu数据加入预积分器后都会预测一次,而这里是将两帧激光雷达帧之间的imu帧全部加入后才预测(上面的while循环) // 用前一帧的状态、偏置,施加 imu 预计分量,得到当前帧的状态 gtsam::NavState propState_ = imuIntegratorOpt_->predict(prevState_, prevBias_); // graphValues.insert(X(key), propState_.pose()); graphValues.insert(V(key), propState_.v()); graphValues.insert(B(key), prevBias_); // optimize optimizer.update(graphFactors, graphValues); optimizer.update(); graphFactors.resize(0); graphValues.clear(); // Overwrite the beginning of the preintegration for the next step. // 优化结果 gtsam::Values result = optimizer.calculateEstimate(); // 更新当前帧位姿、速度 --> 上一帧 prevPose_ = result.at<gtsam::Pose3>(X(key)); prevVel_ = result.at<gtsam::Vector3>(V(key)); // 更新当前帧状态 --> 上一帧 prevState_ = gtsam::NavState(prevPose_, prevVel_); // 更新当前帧imu偏置 prevBias_ = result.at<gtsam::imuBias::ConstantBias>(B(key)); // Reset the optimization preintegration object. // 重置预积分器,设置新的偏置,这样下一帧激光里程计进来的时候,预积分量就是两帧之间的增量 imuIntegratorOpt_->resetIntegrationAndSetBias(prevBias_); // check optimization // check optimization // 检测是否有失败: // 1. imu 的速度大于 30,则速度过大 // 2. imu 的偏差大于 0.1,则偏差 // 如果有上述失败,则重置参数 if (failureDetection(prevVel_, prevBias_)) { resetParams(); return; } // 2. after optiization, re-propagate imu odometry preintegration // 为了维持实时性 imuIntegrateImu 就得在每次 odom 触发优化后立刻获取最新的 bias // 同时对imu测量值 imuQueImu 执行 bias 改变的状态重传播处理, 这样可以最大限度的保证实时性和准确性 //prevStateOdom和prevBiasOdom是imuHandler里面用过的参数 //感觉地位等同于prevState在这里面 prevStateOdom = prevState_; prevBiasOdom = prevBias_; // first pop imu message older than current correction data double lastImuQT = -1; // 从 imu 队列中删除当前激光里程计时刻之前的 imu 数据 while (!imuQueImu.empty() && ROS_TIME(&imuQueImu.front()) < currentCorrectionTime - delta_t) { //弹出 lastImuQT = ROS_TIME(&imuQueImu.front()); imuQueImu.pop_front(); } // repropogate // 对剩余的imu数据计算预积分 // 因为bias改变了 if (!imuQueImu.empty()) { // reset bias use the newly optimized bias // 重置预积分器和最新的偏置,使用最新的偏差更新 bias 值 imuIntegratorImu_->resetIntegrationAndSetBias(prevBiasOdom); // integrate imu message from the beginning of this optimization for (int i = 0; i < (int)imuQueImu.size(); ++i) { //重新进行预积分 //与imuHandler中的预积分过程相同 sensor_msgs::Imu *thisImu = &imuQueImu[i]; double imuTime = ROS_TIME(thisImu); double dt = (lastImuQT < 0) ? (1.0 / 500.0) :(imuTime - lastImuQT); imuIntegratorImu_->integrateMeasurement(gtsam::Vector3(thisImu->linear_acceleration.x, thisImu->linear_acceleration.y, thisImu->linear_acceleration.z), gtsam::Vector3(thisImu->angular_velocity.x, thisImu->angular_velocity.y, thisImu->angular_velocity.z), dt); lastImuQT = imuTime; } } //记录帧数 ++key; //优化器已经开启 doneFirstOpt = true; }
odometryHandler中调用的函数:
重置优化器:resetOptimization.cpp
void resetOptimization() { //ISAM2的参数类 gtsam::ISAM2Params optParameters; optParameters.relinearizeThreshold = 0.1; optParameters.relinearizeSkip = 1; //优化器 optimizer = gtsam::ISAM2(optParameters); gtsam::NonlinearFactorGraph newGraphFactors; graphFactors = newGraphFactors; gtsam::Values NewGraphValues; graphValues = NewGraphValues; }
重置系统参数:resetParam.cpp
void resetParams()
{
//将上一帧imu数据的时间戳更新成-1(代表还没有imu数据进来)
lastImuT_imu = -1;
//false 代表 需要重新进行一次odo优化(跟imuHandler联系起来)
doneFirstOpt = false;
//系统关闭
systemInitialized = false;
}
检查优化器过程是否失败:failure Detection.cpp
bool failureDetection(const gtsam::Vector3& velCur, const gtsam::imuBias::ConstantBias& biasCur) { Eigen::Vector3f vel(velCur.x(), velCur.y(), velCur.z()); if (vel.norm() > 30) { ROS_WARN("Large velocity, reset IMU-preintegration!"); return true; } Eigen::Vector3f ba(biasCur.accelerometer().x(), biasCur.accelerometer().y(), biasCur.accelerometer().z()); Eigen::Vector3f bg(biasCur.gyroscope().x(), biasCur.gyroscope().y(), biasCur.gyroscope().z()); if (ba.norm() > 0.1 || bg.norm() > 0.1) { ROS_WARN("Large bias, reset IMU-preintegration!"); return true; } return false; }
Scan to map ,即激光雷达扫描数据直接与地图进行匹配,得到实际位置坐标[x,y,theta]。这种方式一边计算位置,一边把新扫描到的数据及时加入到先前地图中。
https://blog.csdn.net/fuxingyin/article/details/51854415?locationNum=1&fps=1
iSAM2 将图优化问题转换成 **Bayes tree 的建立、更新和推理问题。**原始待优化的问题用 factor graph 表示,factor graph -> Bayes net 建立变量的条件概率,Bayes net -> Bayes tree 建立变量间的更新关系,Bayes tree 从 leaves 到 root 建立的过程,定义了变量逐级更新的 functions,Bayes tree 从 root 到 leaves 可以逐级更新变量值。iSAM2 整套架构建立在概率推理的基础上,和 iSAM 利用 QR 矩阵分解,当有 new factor 时,更新 R 矩阵差别蛮大。
小例子:
一般激光雷达驱动封装数据时,默认一帧激光雷达数据的所有激光束是在同一位姿下、瞬时获得的,也就是所有激光束都是从橙色点获得的数据,这样实际使用的数据和真实数据有明显差距,如10cm。所以,对于低帧率的激光雷达,运动畸变明显是个需要被解决的问题。
畸变去除原理:在一帧激光雷达数据中,为每个激光束的原点都找到一个近似的里程计位姿,并且认为该激光束是在该里程计位姿上进行数据采集的,得到这些基础数据之后,进行系列的坐标变换,把所有激光点的数据都变换到第一束激光原点的坐标系下,然后再封装数据,这样就能极大的减小畸变。
https://bitbucket.org/gtborg/gtsam/src/develop/
ISAM2Params.h
//类前定义了两个结构体:ISAM2GaussNewtonParams ISAM2DoglegParams /** * @addtogroup ISAM2 * Parameters for ISAM2 using Gauss-Newton optimization. Either this class or * ISAM2DoglegParams should be specified as the optimizationParams in * ISAM2Params, which should in turn be passed to ISAM2(const ISAM2Params&). */ //使用高斯-牛顿优化的 ISAM2 参数。 应将此类或 ISAM2DoglegParams 指定为 ISAM2Params 中的 optimizationParams,然后将其传递给 ISAM2(const ISAM2Params&) //另一个是Dogleg,描述基本相同 //ISAM2Params类 //文件中使用的两个参数 RelinearizationThreshold relinearizeThreshold; //重线性化阈值,仅重新线性化线性增量幅度大于此阈值的变量(默认值:0.1) int relinearizeSkip; ///< Only relinearize any variables every ///< relinearizeSkip calls to ISAM2::update (default: ///< 10) //一个下界 一个上界 //重新线性化处理???
ISAM2.h
//BayesTree 的增量更新功能 (ISAM2),具有流体重新线性化。
//优化器
NonLinearFactorGraph.h
//Factor Graph Constsiting of non-linear factors
values.h
/*
* @brief A non-templated config holding any types of Manifold-group elements
*
* Detailed story:
* A values structure is a map from keys to values. It is used to specify the value of a bunch
* of variables in a factor graph. A Values is a values structure which can hold variables that
* are elements on manifolds, not just vectors. It then, as a whole, implements a aggregate type
* which is also a manifold element, and hence supports operations dim, retract, and localCoordinates.
* @brief 一个非模板化的配置,包含任何类型的 Manifold-group 元素
* 值结构是从键到值的映射。 它用于指定因子图中一组变量的值。 Values 是一个值结构,它可以保存作为流形元素的变量,而不仅仅是向量。 然后,作为一个整体,它实现了一个聚合类型,该类型也是一个流形元素,因此支持操作 dim、retract 和 localCoordinates。
*/
priorFactor.h
/** Constructor */
PriorFactor(Key key, const VALUE& prior, const SharedNoiseModel& model = nullptr) :
Base(model, key), prior_(prior) {
}
//先验因子
ImuFactor.h
//ImuFactor imu因子 /** * ImuFactor is a 5-ways factor involving previous state (pose and velocity of * the vehicle at previous time step), current state (pose and velocity at * current time step), and the bias estimate. Following the preintegration * scheme proposed in [2], the ImuFactor includes many IMU measurements, which * are "summarized" using the PreintegratedIMUMeasurements class. * Note that this factor does not model "temporal consistency" of the biases * (which are usually slowly varying quantities), which is up to the caller. * See also CombinedImuFactor for a class that does this for you. * * @addtogroup SLAM * ImuFactor 是一个 5 向因子,涉及先前状态(车辆在前一时间步的姿态和速度)、当前状态(当前时间步的姿态和速度)和偏差估计。 遵循 [2] 中提出的预积分方案,ImuFactor 包括许多 IMU 测量值,这些测量值使用 PreintegratedIMUMeasurements 类进行“汇总”。 * 请注意,此因素不会对偏差(通常是缓慢变化的数量)的“时间一致性”进行建模,这取决于调用者。 * 另请参阅 CombinedImuFactor 以了解为您执行此操作的类。 */ ImuFactor(Key pose_i, Key vel_i, Key pose_j, Key vel_j, Key bias, const PreintegratedImuMeasurements& preintegratedMeasurements);
imuFactor.cpp
//------------------------------------------------------------------------------ // ImuFactor methods //------------------------------------------------------------------------------ ImuFactor::ImuFactor(Key pose_i, Key vel_i, Key pose_j, Key vel_j, Key bias, const PreintegratedImuMeasurements& pim) : Base(noiseModel::Gaussian::Covariance(pim.preintMeasCov_), pose_i, vel_i, pose_j, vel_j, bias), _PIM_(pim) { } //这里的Base,根据ImuFactor.h中的重定义为:typedef NoiseModelFactor2<POSE, POSE> Base; //找到对应的函数: gtsam/gtsam/nonlinear/NonlinearFactor.h : /** * Constructor * @param noiseModel shared pointer to noise model * @param j1 key of the first variable * @param j2 key of the second variable */ NoiseModelFactor2(const SharedNoiseModel& noiseModel, Key j1, Key j2) : Base(noiseModel, cref_list_of<2>(j1)(j2)) {} //但是跟项目里的参数列表对不起来,但是大体意思一样了 //这里的Base,根据NonlinearFactor.h中的重定义为:typedef NoiseModelFactor Base; template<typename CONTAINER> NoiseModelFactor(const SharedNoiseModel& noiseModel, const CONTAINER& keys) ://这里的key变成keys了 Base(keys), noiseModel_(noiseModel) {} //这里的Base,根据NonlinearFactor.h中的重定义为:typedef NonlinearFactor Base; template<typename CONTAINER> NonlinearFactor(const CONTAINER& keys) : Base(keys) {} //这里的Base,根据NonlinearFactor.h中的重定义为:typedef Factor Base; gtsam/interface/factor.h /** Construct factor from container of keys. This constructor is used internally from derived factor * constructors, either from a container of keys or from a boost::assign::list_of. */ template<typename CONTAINER> explicit Factor(const CONTAINER& keys) : keys_(keys.begin(), keys.end()) {} //注意这个keys.begin和keys.end
在上篇博客中没有找到该函数的原型,这次发现了一个相似度很高的函数:
gtsam/gtsam/navigation/PreintegrationBase.cpp
//------------------------------------------------------------------------------ NavState PreintegrationBase::predict(const NavState& state_i, const imuBias::ConstantBias& bias_i, OptionalJacobian<9, 9> H1, OptionalJacobian<9, 6> H2) const { // TODO(frank): make sure this stuff is still correct Matrix96 D_biasCorrected_bias; Vector9 biasCorrected = biasCorrectedDelta(bias_i, H2 ? &D_biasCorrected_bias : 0); // Correct for initial velocity and gravity Matrix9 D_delta_state, D_delta_biasCorrected; Vector9 xi = state_i.correctPIM(biasCorrected, deltaTij_, p().n_gravity, p().omegaCoriolis, p().use2ndOrderCoriolis, H1 ? &D_delta_state : 0, H2 ? &D_delta_biasCorrected : 0); // Use retract to get back to NavState manifold Matrix9 D_predict_state, D_predict_delta; NavState state_j = state_i.retract(xi, D_predict_state, D_predict_delta); if (H1) *H1 = D_predict_state + D_predict_delta * D_delta_state; if (H2) *H2 = D_predict_delta * D_delta_biasCorrected * D_biasCorrected_bias; return state_j; }
函数中出现的X(0) B(0)…
gtsam/inference/Symbol.h
/** * 用于引用变量的字符和索引键。 将简单地转换为一个 Key,即一个大整数。 键用于从值中检索值,指定因素依赖的变量等。 */ /** Create a symbol key from a character and index, i.e. x5. */ inline Key symbol(unsigned char c, std::uint64_t j) { return (Key)Symbol(c,j); } /** Return the character portion of a symbol key. */ inline unsigned char symbolChr(Key key) { return Symbol(key).chr(); } /** Return the index portion of a symbol key. */ inline std::uint64_t symbolIndex(Key key) { return Symbol(key).index(); } namespace symbol_shorthand { inline Key A(std::uint64_t j) { return Symbol('a', j); } inline Key B(std::uint64_t j) { return Symbol('b', j); } inline Key C(std::uint64_t j) { return Symbol('c', j); } inline Key D(std::uint64_t j) { return Symbol('d', j); } inline Key E(std::uint64_t j) { return Symbol('e', j); } inline Key F(std::uint64_t j) { return Symbol('f', j); } inline Key G(std::uint64_t j) { return Symbol('g', j); } . . . inline Key Z(std::uint64_t j) { return Symbol('z', j); } }
通过综合分析后,认为X B V应该作为一个通用的键,同时与后面的信息,噪声之类的相关联。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。