Mappoint类
MapPoint
类的核心定位:场景中的三维点
简单来说,MapPoint
类代表了在三维真实世界中的一个静态点。在 SLAM 系统运行过程中,相机会从不同的视角观测到场景中的特征点(比如墙角、纹理清晰的物体表面等)。当系统通过三角化等方法确定了这些二维图像特征点对应的三维空间位置后,就会创建一个 MapPoint
对象来存储这个三维点的信息。
MapPoint
类的主要职责和包含的信息:00
MapPoint
类不仅仅存储一个三维点的坐标,它还包含了大量与这个三维点相关的观测信息、几何属性以及在 SLAM 系统不同模块中用于追踪和优化的状态信息。
以下是 MapPoint
类的主要成员变量和功能的概览:
1. 核心属性 (Core Properties):
mWorldPos
(类型:cv::Mat
):含义: 地图点在世界坐标系下的三维坐标 (通常是一个 3x1 的列向量)。这是
MapPoint
最基本也是最重要的信息。获取/设置: 通过
GetWorldPos()
和SetWorldPos()
方法。
mnId
(类型:long unsigned int
):含义: 每个
MapPoint
对象都有一个全局唯一的 ID。通过静态成员变量nNextId
自增生成,确保了 ID 的唯一性。
mpRefKF
(类型:KeyFrame*
):含义: 指向该地图点的参考关键帧 (Reference KeyFrame) 的指针。通常是第一个创建该地图点的关键帧,或者与该地图点关联最紧密的关键帧。参考关键帧对于计算地图点的观测距离范围等属性非常重要。
获取: 通过
GetReferenceKeyFrame()
方法。
mDescriptor
(类型:cv::Mat
):含义: 该地图点的代表性描述子 (Descriptor)。一个三维点可能被多个关键帧观测到,每个观测都会对应一个二维特征点的描述子(例如 ORB 描述子)。
mDescriptor
存储的是从这些观测到的描述子中挑选出来的一个“最具代表性”的描述子。这个描述子用于后续的特征匹配,例如在重定位或闭环检测中快速识别该地图点。计算: 通过
ComputeDistinctiveDescriptors()
方法计算。该方法会比较所有观测到该点的描述子,选择与其他描述子平均距离最小的那个。获取: 通过
GetDescriptor()
方法。
2. 观测信息 (Observation Information):
mObservations
(类型:std::map<KeyFrame*, size_t>
):含义: 一个
std::map
容器,记录了所有观测到这个地图点的关键帧 (KeyFrame*
) 以及该地图点在对应关键帧中特征点的索引 (size_t
)。这是维护地图点与关键帧之间关联的核心数据结构,也是构建共视图的基础。操作:
AddObservation()
: 添加一个新的观测关系。EraseObservation()
: 移除一个观测关系。GetObservations()
: 获取所有观测关系。
nObs
(类型:int
):含义: 观测到该地图点的相机数目。对于单目相机,每次观测
nObs
加1;对于双目或 RGB-D 相机(可以提供深度信息,相当于两个独立的观测),每次观测nObs
加2。这个值用于判断地图点的可靠性。获取: 通过
Observations()
方法。
3. 几何与尺度属性 (Geometric and Scale Properties):
mNormalVector
(类型:cv::Mat
):含义: 地图点的平均观测方向。计算方法是:将所有观测到该点的关键帧相机光心指向该点的方向向量进行归一化,然后求平均。这个向量指示了该点主要从哪个方向被观测。
用途: 用于判断地图点是否在当前相机视野内(可见性剔除)。
更新: 通过
UpdateNormalAndDepth()
方法。获取: 通过
GetNormal()
方法。
mfMinDistance
,mfMaxDistance
(类型:float
):含义: 地图点的最小和最大有效观测距离。这两个值是基于该地图点在其参考关键帧中的观测情况(特征点所在的金字塔层级和到相机的距离)来估计的。它们定义了一个深度范围,在这个范围内,该地图点可以被认为是可靠的观测。
用途: 用于
PredictScale()
函数,预测当地图点投影到新图像时,对应的特征点应该在图像金字塔的哪个尺度层级。更新: 通过
UpdateNormalAndDepth()
方法。获取: 通过
GetMinDistanceInvariance()
和GetMaxDistanceInvariance()
(这两个方法会返回0.8f*mfMinDistance
和1.2f*mfMaxDistance
,增加了一些容错范围)。
4. 状态与追踪计数器 (Status and Tracking Counters):
mbBad
(类型:bool
):含义: 标记该地图点是否为坏点 (Bad Point)。如果一个地图点的观测过少,或者在优化过程中被认为是外点,就可能被标记为坏点。坏点将不再参与后续的追踪和优化。
设置: 通过
SetBadFlag()
方法。该方法不仅设置mbBad
为true
,还会清除其所有观测关系,并通知所属的地图 (mpMap
) 将其从地图中移除。检查: 通过
isBad()
方法。
mpReplaced
(类型:MapPoint*
):含义: 指向替换当前地图点的另一个地图点的指针。在地图点融合(MapPoint fusion)的过程中,如果发现两个地图点实际上是同一个三维点,质量较差的那个会被质量较好的那个替换掉。
mpReplaced
就指向那个质量更好的地图点。设置/获取: 通过
Replace()
和GetReplaced()
方法。
mnVisible
(类型:int
):含义: 该地图点在帧视野中出现的次数。只要一个地图点通过了
Frame::isInFrustum()
的判断(即理论上在当前相机视野内),即使没有成功匹配上特征点,mnVisible
也会增加。操作: 通过
IncreaseVisible()
方法增加计数。
mnFound
(类型:int
):含义: 该地图点成功匹配到帧中特征点的次数。这个计数比
mnVisible
更严格,要求地图点不仅在视野内,而且成功地与某一帧的特征点建立了对应关系。操作: 通过
IncreaseFound()
方法增加计数。获取: 通过
GetFound()
方法。GetFoundRatio()
: 返回mnFound / mnVisible
的比例,可以用来评估地图点的追踪稳定性。
5. 用于不同线程的状态标记 (Status Flags for Different Threads):
ORB-SLAM2 是一个多线程系统,MapPoint
类中包含了一些用于在不同线程(Tracking, Local Mapping, Loop Closing)中同步状态或避免重复操作的标记性成员变量。这些变量通常存储的是关键帧的 ID 或帧的 ID。
用于 Tracking 线程:
mTrackProjX
,mTrackProjY
,mTrackProjXR
: 存储地图点投影到当前帧的像素坐标(XR
为右目坐标)。mnTrackScaleLevel
: 预测该地图点在当前帧中对应的特征点的金字塔层级。mTrackViewCos
: 地图点的平均观测方向与当前相机到该点方向的夹角的余弦值,用于判断视角是否合适。mbTrackInView
: 标记该地图点在当前帧的追踪过程中是否已经被处理过或是否在视野内。mnTrackReferenceForFrame
: 在TrackLocalMap
的UpdateLocalPoints
步骤中,用于防止将同一个地图点重复添加到局部地图点列表mvpLocalMapPoints
。mnLastFrameSeen
: 记录上一次“看到”(即使只是在视野内)该地图点的帧的 ID。
用于 Local Mapping 线程:
mnBALocalForKF
: 记录该地图点上一次参与局部 BA (Bundle Adjustment) 时对应的关键帧 ID。mnFuseCandidateForKF
: 在地图点融合过程中,标记该地图点是哪个关键帧的融合候选者。
用于 Loop Closing 线程:
mnLoopPointForKF
: 标记该地图点在闭环检测中是作为哪个“当前关键帧”的闭环候选地图点。mnCorrectedByKF
: 记录上一次通过闭环校正更新该地图点位置时,是基于哪个关键帧的校正。mnCorrectedReference
: 类似mnCorrectedByKF
,用于更细致的校正跟踪。mPosGBA
: 存储在全局 BA (Global Bundle Adjustment) 优化后的地图点位置。mnBAGlobalForKF
: 记录该地图点上一次参与全局 BA 时,触发全局 BA 的关键帧 ID。
6. 构造函数 (Constructors):
MapPoint
类有两个主要的构造函数:
MapPoint(const cv::Mat &Pos, KeyFrame* pRefKF, Map* pMap)
:最常用的构造函数,用于根据一个已知的三维坐标
Pos
和一个参考关键帧pRefKF
来创建一个新的地图点。通常在三角化新的地图点时(例如单目初始化、双目/RGB-D 初始化、局部建图线程创建新点)被调用。
MapPoint(const cv::Mat &Pos, Map* pMap, Frame* pFrame, const int &idxF)
:用于根据一个已知的三维坐标
Pos
和一个普通帧pFrame
以及该点在帧中对应的特征点索引idxF
来创建地图点。这种构造方式通常用于双目或 RGB-D 相机,当可以直接从一帧图像中获取到某些特征点的三维信息时(例如,在
Tracking::UpdateLastFrame()
中为双目/RGB-D 创建临时地图点以增强追踪鲁棒性)。这种方式创建的点,其初始参考关键帧mpRefKF
会被设为NULL
。
7. 核心方法 (Key Methods):
除了上面提到的获取/设置方法和计数器增加方法外,还有一些核心的功能性方法:
UpdateNormalAndDepth()
: 更新平均观测方向和观测距离范围,这在之前已经详细讲过。ComputeDistinctiveDescriptors()
: 计算并更新最具代表性的描述子。SetBadFlag()
: 将地图点标记为坏点,并进行清理。Replace(MapPoint* pMP)
: 用另一个地图点pMP
替换当前地图点,主要发生在地图点融合时。此操作会将被替换点的观测关系等信息转移到替换它的点上,然后将被替换点标记为坏点。AddObservation(KeyFrame* pKF, size_t idx)
: 添加一个关键帧对该地图点的观测。EraseObservation(KeyFrame* pKF)
: 移除一个关键帧对该地图点的观测。PredictScale(const float ¤tDist, KeyFrame* pKF / Frame* pF)
: 根据地图点到当前相机光心的距离,以及其参考观测距离范围,预测该地图点在当前帧/关键帧中应该位于图像金字塔的哪个层级。
8. 线程安全 (Thread Safety):
由于 MapPoint
的成员变量可能被多个线程(Tracking, Local Mapping, Loop Closing)同时访问和修改,类中使用了一些互斥锁 (std::mutex
) 来保护关键数据的读写,以保证线程安全:
mMutexPos
: 保护与位置相关的成员变量,如mWorldPos
,mNormalVector
,mfMinDistance
,mfMaxDistance
。mMutexFeatures
: 保护与特征和观测相关的成员变量,如mObservations
,mDescriptor
,mpRefKF
,mnVisible
,mnFound
,mbBad
,mpReplaced
。mGlobalMutex
(静态成员): 一个全局互斥锁,在SetWorldPos
中被使用,可能用于某些更全局的同步操作,但其具体必要性需要结合上下文分析(在 ORB-SLAM2 中,一些静态全局锁的使用有时是为了简化设计,但也可能引入不必要的串行化)。mpMap->mMutexPointCreation
: 在构造函数中,当分配新的mnId
时,会使用所属地图mpMap
的一个互斥锁mMutexPointCreation
,以确保 ID 分配的原子性。
总结:
MapPoint
类是 ORB-SLAM2 中对三维空间点的抽象表示。它不仅仅是一个三维坐标,更是一个包含了丰富几何信息、观测历史、追踪状态和优化标记的复杂对象。理解 MapPoint
的这些属性和方法,对于深入理解 ORB-SLAM2 如何进行地图构建、追踪定位、闭环优化等核心流程至关重要。它就像是连接不同图像观测之间的桥梁,构成了整个 SLAM 系统所依赖的稀疏三维地图的基石。