目录
kt_bool Mapper::Process(LocalizedRangeScan* pScan)
kt_double ScanMatcher::MatchScan()
kt_double ScanMatcher::CorrelateScan()
kt_double ScanMatcher::GetResponse()
void ScanMatcher::ComputePositionalCovariance() ComputeAngularCovariance()
slam_karto的addScan()函数调用了Process()函数。
slam_karto的代码请看上一篇博文。
1 数据类型与类 简介
1.1 抽象类 Sensor
这个类为抽象类,无具体实现,只有一个成员变量
Parameter<Pose2>* m_pOffsetPose; 这个变量定义了雷达坐标系与base_link间的偏差
1.2 class LaserRangeFinder : public Sensor
这个类继承Sensor,定义了激光雷达数据的各个参数,如 最大最小距离,最大最小角度,角度分辨率,一帧雷达的点的个数,以及Sensor中定义的雷达坐标系与base_link间的偏差
1.3 class SensorData : public Object
SensorData是所有传感器数据的基类,定义了如下变量
/**
* ID unique to individual sensor
*/
kt_int32s m_StateId;
/**
* ID unique across all sensor data
*/
kt_int32s m_UniqueId;
/**
* Sensor that created this sensor data
*/
Name m_SensorName;
/**
* Time the sensor data was created
*/
kt_double m_Time;
CustomDataVector m_CustomData;
1.4 class LaserRangeScan : public SensorData
存储了最原始的扫描深度数据
kt_double* m_pRangeReadings; // 一帧scan的所有距离值,指向数组的指针
kt_int32u m_NumberOfRangeReadings; // 一帧scan的点数,也就是数组的个数
1.5 class LocalizedRangeScan : public LaserRangeScan
2 kt_bool Mapper::Process(LocalizedRangeScan* pScan)
kt_bool Mapper::Process(LocalizedRangeScan* pScan)
{
if (pScan != NULL)
{
karto::LaserRangeFinder* pLaserRangeFinder = pScan->GetLaserRangeFinder();
// validate scan
if (pLaserRangeFinder == NULL || pScan == NULL || pLaserRangeFinder->Validate(pScan) == false)
{
return false;
}
if (m_Initialized == false)
{
// 初始化Lidar的相关参数(如范围等)
// initialize mapper with range threshold from device
Initialize(pLaserRangeFinder->GetRangeThreshold());
}
// get last scan 获取上一帧激光数据帧
LocalizedRangeScan* pLastScan = m_pMapperSensorManager->GetLastScan(pScan->GetSensorName());
// update scans corrected pose based on last correction
if (pLastScan != NULL)
{
Transform lastTransform(pLastScan->GetOdometricPose(), pLastScan->GetCorrectedPose());
pScan->SetCorrectedPose(lastTransform.TransformPose(pScan->GetOdometricPose()));
}
// test if scan is outside minimum boundary or if heading is larger then minimum heading
// 通过和上一帧数据比较判断当前帧是否能作为关键帧,如果不能,该函数直接返回,不作处理;
// 能作为关键帧有两种情况:1 是当前帧是第一帧 2是当前帧和上一帧相比在时间上或者角度与距离上相距较远
if (!HasMovedEnough(pScan, pLastScan))
{
return false;
}
Matrix3 covariance;
covariance.SetToIdentity();
// 如果当前帧是第一帧,则不执行MatchScan操作,否则执行。MatchScan是用来将当前帧和前面的帧(多个)进行比较,根据odometry给出的初始位姿,在初始位姿附近找到更合适的位姿作为机器人移动位姿,同时返回该位姿下的response(衡量标准)以及covariance(可信度),具体内容在后面介绍
// correct scan (if not first scan)
if (m_pUseScanMatching->GetValue() && pLastScan != NULL)
{
Pose2 bestPose;
m_pSequentialScanMatcher->MatchScan(pScan,
m_pMapperSensorManager->
GetRunningScans(pScan->GetSensorName()),
bestPose, // 求出的最优位姿
covariance);
pScan->SetSensorPose(bestPose);
}
// add scan to buffer and assign id 将当前帧加入数据库中
m_pMapperSensorManager->AddScan(pScan);
if (m_pUseScanMatching->GetValue())
{
// add to graph
m_pGraph->AddVertex(pScan); // 将当前帧设置为图优化的顶点
m_pGraph->AddEdges(pScan, covariance); // 将当前帧执行 添加边的操作
// 将当前帧添加到runningScan中,并进行维护. runningScan即是 扫描匹配 中被比较的对象,也即是“前面的帧”
m_pMapperSensorManager->AddRunningScan(pScan);
if (m_pDoLoopClosing->GetValue())
{
std::vector<Name> deviceNames = m_pMapperSensorManager->GetSensorNames();
const_forEach(std::vector<Name>, &deviceNames)
{
m_pGraph->TryCloseLoop(pScan, *iter); // 进行闭环检测
}
}
}
m_pMapperSensorManager->SetLastScan(pScan);
return true;
}
return false;
}
kt_double ScanMatcher::MatchScan()
1次粗匹配(搜索步长为两倍的分辨率), 如果没匹配到的话改变角度再进行一次粗匹配, 之后进行1次精细匹配(步长为分辨率)
/**
* Match given scan against set of scans
* @param pScan scan being scan-matched
* @param rBaseScans set of scans whose points will mark cells in grid as being occupied
* @param rMean output parameter of mean (best pose) of match
* @param rCovariance output parameter of covariance of match
* @param doPenalize default = true
whether to penalize matches further from the search center
* @param doRefineMatch default= true
whether to do finer-grained matching if coarse match is good (default is true)
* @return strength of response
*/
kt_double ScanMatcher::MatchScan(LocalizedRangeScan* pScan,
const LocalizedRangeScanVector& rBaseScans,
Pose2& rMean,
Matrix3& rCovariance,
kt_bool doPenalize,
kt_bool doRefineMatch)
{
///////////////////////////////////////
// set scan pose to be center of grid
//pScan还没有更新父类LaserRangeScan中原始数据,扫描深度数据都在父类中,通过 pScan->GetRangeReadings()[iterNum]这种形式来访问。
// 1. get scan position
Pose2 scanPose = pScan->GetSensorPose(); // 第二个数据进来时,scanPose的位置是(1,1) angle是 90度
// scan has no readings; cannot do scan matching
// best guess of pose is based off of adjusted odometer reading
if (pScan->GetNumberOfRangeReadings() == 0)
{
rMean = scanPose;
// maximum covariance
rCovariance(0, 0) = MAX_VARIANCE; // XX
rCovariance(1, 1) = MAX_VARIANCE; // YY
rCovariance(2, 2) = 4 * math::Square(m_pMapper->m_pCoarseAngleResolution->GetValue()); // TH*TH
return 0.0;
}
// 2. get size of grid
Rectangle2<kt_int32s> roi = m_pCorrelationGrid->GetROI(); // 这个是矩形栅格地图,12(雷达范围,之前设置过的 SetRangeThreshold)+0.3m(滑窗范围)
std::cout << "width : " <<roi.GetWidth() << " height : " << roi.GetHeight() << " center : " << roi.GetCenter().GetX() << " " << roi.GetCenter().GetY() << std::endl;
// 3. compute offset (in meters - lower left corner)
Vector2<kt_double> offset; //offset是栅格左下角在世界坐标系中的位置
// m_pCorrelationGrid->GetResolution() 栅格地图一个格子在实际中的长度,单位:默认是 m
offset.SetX(scanPose.GetX() - (0.5 * (roi.GetWidth() - 1) * m_pCorrelationGrid->GetResolution()));
offset.SetY(scanPose.GetY() - (0.5 * (roi.GetHeight() - 1) * m_pCorrelationGrid->GetResolution()));
// 4. set offset
m_pCorrelationGrid->GetCoordinateConverter()->SetOffset(offset);
///////////////////////////////////////
// set up correlation grid
// 这里的scanPose.GetPosition()是 Lidar的位置,不是Lidar point的位置
//将rBaseScans的点所对应的栅格置为“occupied”,这就相当于存储好了原始的数据,之后只要拿现在的和原始的栅格相比较就行了
AddScans(rBaseScans, scanPose.GetPosition()); //获得了Roi里栅格状态 //这个会对扫描点进行处理
// compute how far to search in each direction //coarseSearchOffset 是一个坐标
Vector2<kt_double> searchDimensions(m_pSearchSpaceProbs->GetWidth(), m_pSearchSpaceProbs->GetHeight());
Vector2<kt_double> coarseSearchOffset(0.5 * (searchDimensions.GetX() - 1) * m_pCorrelationGrid->GetResolution(),
0.5 * (searchDimensions.GetY() - 1) * m_pCorrelationGrid->GetResolution());
// a coarse search only checks half the cells in each dimension
//因为每两个格子扫描一下,所以认为是粗略的
Vector2<kt_double> coarseSearchResolution(2 * m_pCorrelationGrid->GetResolution(),
2 * m_pCorrelationGrid->GetResolution());
// actual scan-matching //找到了 最好的位姿使得response最大 //这里面进行了三重for循环来判断是否有合适的旋转
kt_double bestResponse = CorrelateScan(pScan, scanPose, coarseSearchOffset, coarseSearchResolution,
m_pMapper->m_pCoarseSearchAngleOffset->GetValue(),
m_pMapper->m_pCoarseAngleResolution->GetValue(),
doPenalize, rMean, rCovariance, false);
if (m_pMapper->m_pUseResponseExpansion->GetValue() == true)
{
if (math::DoubleEqual(bestResponse, 0.0))
{
#ifdef KARTO_DEBUG
std::cout << "Mapper Info: Expanding response search space!" << std::endl;
#endif
// try and increase search angle offset with 20 degrees and do another match
kt_double newSearchAngleOffset = m_pMapper->m_pCoarseSearchAngleOffset->GetValue();
for (kt_int32u i = 0; i < 3; i++)
{
newSearchAngleOffset += math::DegreesToRadians(20);
bestResponse = CorrelateScan(pScan, scanPose, coarseSearchOffset, coarseSearchResolution,
newSearchAngleOffset, m_pMapper->m_pCoarseAngleResolution->GetValue(),
doPenalize, rMean, rCovariance, false);
if (math::DoubleEqual(bestResponse, 0.0) == false)
{
break;
}
}
#ifdef KARTO_DEBUG
if (math::DoubleEqual(bestResponse, 0.0))
{
std::cout << "Mapper Warning: Unable to calculate response!" << std::endl;
}
#endif
}
}
if (doRefineMatch)
{
Vector2<kt_double> fineSearchOffset(coarseSearchResolution * 0.5);
Vector2<kt_double> fineSearchResolution(m_pCorrelationGrid->GetResolution(), m_pCorrelationGrid->GetResolution());
bestResponse = CorrelateScan(pScan, rMean, fineSearchOffset, fineSearchResolution,
0.5 * m_pMapper->m_pCoarseAngleResolution->GetValue(),
m_pMapper->m_pFineSearchAngleOffset->GetValue(),
doPenalize, rMean, rCovariance, true); //这里的true会让程序计算角度方差
}
#ifdef KARTO_DEBUG
std::cout << " BEST POSE = " << rMean << " BEST RESPONSE = " << bestResponse << ", VARIANCE = "
<< rCovariance(0, 0) << ", " << rCovariance(1, 1) << std::endl;
#endif
assert(math::InRange(rMean.GetHeading(), -KT_PI, KT_PI));
return bestResponse;
}
/**
* Marks cells where scans' points hit as being occupied
* @param rScans scans whose points will mark cells in grid as being occupied
* @param viewPoint do not add points that belong to scans "opposite" the view point
*/
void ScanMatcher::AddScans(const LocalizedRangeScanVector& rScans, Vector2<kt_double> viewPoint)
{
m_pCorrelationGrid->Clear(); //把栅格地图的所有值(0,100,255)置为0
// add all scans to grid
const_forEach(LocalizedRangeScanVector, &rScans) // rScans 可能是 running scans 或者 相邻的scans或者 闭环产生的scans
{
AddScan(*iter, viewPoint); //viewPoint 是当前帧的 在世界坐标系中的位置,但是不包含laser朝向信息,而 *iter都是存储的之前的帧的扫描点在世界坐标系中的位置
}
}
kt_double ScanMatcher::CorrelateScan()
扫描匹配的实际处理函数
/**
* Finds the best pose for the scan centering the search in the correlation grid
* at the given pose and search in the space by the vector and angular offsets
* in increments of the given resolutions
* @param rScan scan to match against correlation grid
* @param rSearchCenter the center of the search space
* @param rSearchSpaceOffset searches poses in the area offset by this vector around search center
* @param rSearchSpaceResolution how fine a granularity to search in the search space
* @param searchAngleOffset searches poses in the angles offset by this angle around search center
* @param searchAngleResolution how fine a granularity to search in the angular search space
* @param doPenalize whether to penalize matches further from the search center
* @param rMean output parameter of mean (best pose) of match
* @param rCovariance output parameter of covariance of match
* @param doingFineMatch whether to do a finer search after coarse search
* @return strength of response
*/
kt_double ScanMatcher::CorrelateScan(LocalizedRangeScan* pScan, const Pose2& rSearchCenter,
const Vector2<kt_double>& rSearchSpaceOffset,
const Vector2<kt_double>& rSearchSpaceResolution,
kt_double searchAngleOffset, kt_double searchAngleResolution,
kt_bool doPenalize, Pose2& rMean, Matrix3& rCovariance, kt_bool doingFineMatch)
{
assert(searchAngleResolution != 0.0);
// setup lookup arrays
m_pGridLookup->ComputeOffsets(pScan, rSearchCenter.GetHeading(), searchAngleOffset, searchAngleResolution);
// only initialize probability grid if computing positional covariance (during coarse match)
if (!doingFineMatch)
{
m_pSearchSpaceProbs->Clear();
// position search grid - finds lower left corner of search grid
Vector2<kt_double> offset(rSearchCenter.GetPosition() - rSearchSpaceOffset);
m_pSearchSpaceProbs->GetCoordinateConverter()->SetOffset(offset);
}
// calculate position arrays
std::vector<kt_double> xPoses;
kt_int32u nX = static_cast<kt_int32u>(math::Round(rSearchSpaceOffset.GetX() *
2.0 / rSearchSpaceResolution.GetX()) + 1);
kt_double startX = -rSearchSpaceOffset.GetX();
for (kt_int32u xIndex = 0; xIndex < nX; xIndex++)
{
xPoses.push_back(startX + xIndex * rSearchSpaceResolution.GetX());
}
assert(math::DoubleEqual(xPoses.back(), -startX));
std::vector<kt_double> yPoses;
kt_int32u nY = static_cast<kt_int32u>(math::Round(rSearchSpaceOffset.GetY() *
2.0 / rSearchSpaceResolution.GetY()) + 1);
kt_double startY = -rSearchSpaceOffset.GetY();
for (kt_int32u yIndex = 0; yIndex < nY; yIndex++)
{
yPoses.push_back(startY + yIndex * rSearchSpaceResolution.GetY());
}
assert(math::DoubleEqual(yPoses.back(), -startY));
// calculate pose response array size
kt_int32u nAngles = static_cast<kt_int32u>(math::Round(searchAngleOffset * 2.0 / searchAngleResolution) + 1);
kt_int32u poseResponseSize = static_cast<kt_int32u>(xPoses.size() * yPoses.size() * nAngles);
// allocate array
std::pair<kt_double, Pose2>* pPoseResponse = new std::pair<kt_double, Pose2>[poseResponseSize];
Vector2<kt_int32s> startGridPoint = m_pCorrelationGrid->WorldToGrid(Vector2<kt_double>(rSearchCenter.GetX()
+ startX, rSearchCenter.GetY() + startY));
kt_int32u poseResponseCounter = 0;
forEachAs(std::vector<kt_double>, &yPoses, yIter)
{
kt_double y = *yIter;
kt_double newPositionY = rSearchCenter.GetY() + y;
kt_double squareY = math::Square(y);
forEachAs(std::vector<kt_double>, &xPoses, xIter)
{
kt_double x = *xIter;
kt_double newPositionX = rSearchCenter.GetX() + x;
kt_double squareX = math::Square(x);
Vector2<kt_int32s> gridPoint = m_pCorrelationGrid->WorldToGrid(Vector2<kt_double>(newPositionX, newPositionY));
kt_int32s gridIndex = m_pCorrelationGrid->GridIndex(gridPoint);
assert(gridIndex >= 0);
kt_double angle = 0.0;
kt_double startAngle = rSearchCenter.GetHeading() - searchAngleOffset;
for (kt_int32u angleIndex = 0; angleIndex < nAngles; angleIndex++)
{
angle = startAngle + angleIndex * searchAngleResolution;
kt_double response = GetResponse(angleIndex, gridIndex);
if (doPenalize && (math::DoubleEqual(response, 0.0) == false))
{
// simple model (approximate Gaussian) to take odometry into account
kt_double squaredDistance = squareX + squareY;
kt_double distancePenalty = 1.0 - (DISTANCE_PENALTY_GAIN *
squaredDistance / m_pMapper->m_pDistanceVariancePenalty->GetValue());
distancePenalty = math::Maximum(distancePenalty, m_pMapper->m_pMinimumDistancePenalty->GetValue());
kt_double squaredAngleDistance = math::Square(angle - rSearchCenter.GetHeading());
kt_double anglePenalty = 1.0 - (ANGLE_PENALTY_GAIN *
squaredAngleDistance / m_pMapper->m_pAngleVariancePenalty->GetValue());
anglePenalty = math::Maximum(anglePenalty, m_pMapper->m_pMinimumAnglePenalty->GetValue());
response *= (distancePenalty * anglePenalty);
}
// store response and pose
pPoseResponse[poseResponseCounter] = std::pair<kt_double, Pose2>(response, Pose2(newPositionX, newPositionY,
math::NormalizeAngle(angle)));
poseResponseCounter++;
}
assert(math::DoubleEqual(angle, rSearchCenter.GetHeading() + searchAngleOffset));
}
}
assert(poseResponseSize == poseResponseCounter);
// find value of best response (in [0; 1])
kt_double bestResponse = -1;
for (kt_int32u i = 0; i < poseResponseSize; i++)
{
bestResponse = math::Maximum(bestResponse, pPoseResponse[i].first);
// will compute positional covariance, save best relative probability for each cell
if (!doingFineMatch)
{
const Pose2& rPose = pPoseResponse[i].second;
Vector2<kt_int32s> grid = m_pSearchSpaceProbs->WorldToGrid(rPose.GetPosition());
// Changed (kt_double*) to the reinterpret_cast - Luc
kt_double* ptr = reinterpret_cast<kt_double*>(m_pSearchSpaceProbs->GetDataPointer(grid));
if (ptr == NULL)
{
throw std::runtime_error("Mapper FATAL ERROR - Index out of range in probability search!");
}
*ptr = math::Maximum(pPoseResponse[i].first, *ptr);
}
}
// average all poses with same highest response
Vector2<kt_double> averagePosition;
kt_double thetaX = 0.0;
kt_double thetaY = 0.0;
kt_int32s averagePoseCount = 0;
for (kt_int32u i = 0; i < poseResponseSize; i++)
{
if (math::DoubleEqual(pPoseResponse[i].first, bestResponse))
{
averagePosition += pPoseResponse[i].second.GetPosition();
kt_double heading = pPoseResponse[i].second.GetHeading();
thetaX += cos(heading);
thetaY += sin(heading);
averagePoseCount++;
}
}
Pose2 averagePose;
if (averagePoseCount > 0)
{
averagePosition /= averagePoseCount;
thetaX /= averagePoseCount;
thetaY /= averagePoseCount;
averagePose = Pose2(averagePosition, atan2(thetaY, thetaX));
}
else
{
throw std::runtime_error("Mapper FATAL ERROR - Unable to find best position");
}
// delete pose response array
delete [] pPoseResponse;
#ifdef KARTO_DEBUG
std::cout << "bestPose: " << averagePose << std::endl;
std::cout << "bestResponse: " << bestResponse << std::endl;
#endif
if (!doingFineMatch)
{
ComputePositionalCovariance(averagePose, bestResponse, rSearchCenter, rSearchSpaceOffset,
rSearchSpaceResolution, searchAngleResolution, rCovariance);
}
else
{
ComputeAngularCovariance(averagePose, bestResponse, rSearchCenter,
searchAngleOffset, searchAngleResolution, rCovariance);
}
rMean = averagePose;
#ifdef KARTO_DEBUG
std::cout << "bestPose: " << averagePose << std::endl;
#endif
if (bestResponse > 1.0)
{
bestResponse = 1.0;
}
assert(math::InRange(bestResponse, 0.0, 1.0));
assert(math::InRange(rMean.GetHeading(), -KT_PI, KT_PI));
return bestResponse;
}
kt_double ScanMatcher::GetResponse()
/**
* Get response at given position for given rotation (only look up valid points)
* @param angleIndex
* @param gridPositionIndex
* @return response
*/
kt_double ScanMatcher::GetResponse(kt_int32u angleIndex, kt_int32s gridPositionIndex) const
{
kt_double response = 0.0;
// add up value for each point
kt_int8u* pByte = m_pCorrelationGrid->GetDataPointer() + gridPositionIndex;
const LookupArray* pOffsets = m_pGridLookup->GetLookupArray(angleIndex);
assert(pOffsets != NULL);
// get number of points in offset list
kt_int32u nPoints = pOffsets->GetSize();
if (nPoints == 0)
{
return response;
}
// calculate response
kt_int32s* pAngleIndexPointer = pOffsets->GetArrayPointer();
for (kt_int32u i = 0; i < nPoints; i++)
{
// ignore points that fall off the grid
kt_int32s pointGridIndex = gridPositionIndex + pAngleIndexPointer[i];
if (!math::IsUpTo(pointGridIndex, m_pCorrelationGrid->GetDataSize()) || pAngleIndexPointer[i] == INVALID_SCAN)
{
continue;
}
// uses index offsets to efficiently find location of point in the grid
response += pByte[pAngleIndexPointer[i]];
}
// normalize response
response /= (nPoints * GridStates_Occupied);
assert(fabs(response) <= 1.0);
return response;
}
void ScanMatcher::ComputePositionalCovariance() ComputeAngularCovariance()
/**
* Computes the positional covariance of the best pose
* @param rBestPose
* @param bestResponse
* @param rSearchCenter
* @param rSearchSpaceOffset
* @param rSearchSpaceResolution
* @param searchAngleResolution
* @param rCovariance
*/
void ScanMatcher::ComputePositionalCovariance(const Pose2& rBestPose, kt_double bestResponse,
const Pose2& rSearchCenter,
const Vector2<kt_double>& rSearchSpaceOffset,
const Vector2<kt_double>& rSearchSpaceResolution,
kt_double searchAngleResolution, Matrix3& rCovariance)
{
// reset covariance to identity matrix
rCovariance.SetToIdentity();
// if best response is vary small return max variance
if (bestResponse < KT_TOLERANCE)
{
rCovariance(0, 0) = MAX_VARIANCE; // XX
rCovariance(1, 1) = MAX_VARIANCE; // YY
rCovariance(2, 2) = 4 * math::Square(searchAngleResolution); // TH*TH
return;
}
kt_double accumulatedVarianceXX = 0;
kt_double accumulatedVarianceXY = 0;
kt_double accumulatedVarianceYY = 0;
kt_double norm = 0;
kt_double dx = rBestPose.GetX() - rSearchCenter.GetX();
kt_double dy = rBestPose.GetY() - rSearchCenter.GetY();
kt_double offsetX = rSearchSpaceOffset.GetX();
kt_double offsetY = rSearchSpaceOffset.GetY();
kt_int32u nX = static_cast<kt_int32u>(math::Round(offsetX * 2.0 / rSearchSpaceResolution.GetX()) + 1);
kt_double startX = -offsetX;
assert(math::DoubleEqual(startX + (nX - 1) * rSearchSpaceResolution.GetX(), -startX));
kt_int32u nY = static_cast<kt_int32u>(math::Round(offsetY * 2.0 / rSearchSpaceResolution.GetY()) + 1);
kt_double startY = -offsetY;
assert(math::DoubleEqual(startY + (nY - 1) * rSearchSpaceResolution.GetY(), -startY));
for (kt_int32u yIndex = 0; yIndex < nY; yIndex++)
{
kt_double y = startY + yIndex * rSearchSpaceResolution.GetY();
for (kt_int32u xIndex = 0; xIndex < nX; xIndex++)
{
kt_double x = startX + xIndex * rSearchSpaceResolution.GetX();
Vector2<kt_int32s> gridPoint = m_pSearchSpaceProbs->WorldToGrid(Vector2<kt_double>(rSearchCenter.GetX() + x,
rSearchCenter.GetY() + y));
kt_double response = *(m_pSearchSpaceProbs->GetDataPointer(gridPoint));
// response is not a low response
if (response >= (bestResponse - 0.1))
{
norm += response;
accumulatedVarianceXX += (math::Square(x - dx) * response);
accumulatedVarianceXY += ((x - dx) * (y - dy) * response);
accumulatedVarianceYY += (math::Square(y - dy) * response);
}
}
}
if (norm > KT_TOLERANCE)
{
kt_double varianceXX = accumulatedVarianceXX / norm;
kt_double varianceXY = accumulatedVarianceXY / norm;
kt_double varianceYY = accumulatedVarianceYY / norm;
kt_double varianceTHTH = 4 * math::Square(searchAngleResolution);
// lower-bound variances so that they are not too small;
// ensures that links are not too tight
kt_double minVarianceXX = 0.1 * math::Square(rSearchSpaceResolution.GetX());
kt_double minVarianceYY = 0.1 * math::Square(rSearchSpaceResolution.GetY());
varianceXX = math::Maximum(varianceXX, minVarianceXX);
varianceYY = math::Maximum(varianceYY, minVarianceYY);
// increase variance for poorer responses
kt_double multiplier = 1.0 / bestResponse;
rCovariance(0, 0) = varianceXX * multiplier;
rCovariance(0, 1) = varianceXY * multiplier;
rCovariance(1, 0) = varianceXY * multiplier;
rCovariance(1, 1) = varianceYY * multiplier;
rCovariance(2, 2) = varianceTHTH; // this value will be set in ComputeAngularCovariance
}
// if values are 0, set to MAX_VARIANCE
// values might be 0 if points are too sparse and thus don't hit other points
if (math::DoubleEqual(rCovariance(0, 0), 0.0))
{
rCovariance(0, 0) = MAX_VARIANCE;
}
if (math::DoubleEqual(rCovariance(1, 1), 0.0))
{
rCovariance(1, 1) = MAX_VARIANCE;
}
}
/**
* Computes the angular covariance of the best pose
* @param rBestPose
* @param bestResponse
* @param rSearchCenter
* @param rSearchAngleOffset
* @param searchAngleResolution
* @param rCovariance
*/
void ScanMatcher::ComputeAngularCovariance(const Pose2& rBestPose,
kt_double bestResponse,
const Pose2& rSearchCenter,
kt_double searchAngleOffset,
kt_double searchAngleResolution,
Matrix3& rCovariance)
{
// NOTE: do not reset covariance matrix
// normalize angle difference
kt_double bestAngle = math::NormalizeAngleDifference(rBestPose.GetHeading(), rSearchCenter.GetHeading());
Vector2<kt_int32s> gridPoint = m_pCorrelationGrid->WorldToGrid(rBestPose.GetPosition());
kt_int32s gridIndex = m_pCorrelationGrid->GridIndex(gridPoint);
kt_int32u nAngles = static_cast<kt_int32u>(math::Round(searchAngleOffset * 2 / searchAngleResolution) + 1);
kt_double angle = 0.0;
kt_double startAngle = rSearchCenter.GetHeading() - searchAngleOffset;
kt_double norm = 0.0;
kt_double accumulatedVarianceThTh = 0.0;
for (kt_int32u angleIndex = 0; angleIndex < nAngles; angleIndex++)
{
angle = startAngle + angleIndex * searchAngleResolution;
kt_double response = GetResponse(angleIndex, gridIndex);
// response is not a low response
if (response >= (bestResponse - 0.1))
{
norm += response;
accumulatedVarianceThTh += (math::Square(angle - bestAngle) * response);
}
}
assert(math::DoubleEqual(angle, rSearchCenter.GetHeading() + searchAngleOffset));
if (norm > KT_TOLERANCE)
{
if (accumulatedVarianceThTh < KT_TOLERANCE)
{
accumulatedVarianceThTh = math::Square(searchAngleResolution);
}
accumulatedVarianceThTh /= norm;
}
else
{
accumulatedVarianceThTh = 1000 * math::Square(searchAngleResolution);
}
rCovariance(2, 2) = accumulatedVarianceThTh;
}
REFERENCES
Karto SLAM之open_karto代码学习笔记(一) https://blog.csdn.net/wphkadn/article/details/85144186
Karto SLAM之open_karto代码学习笔记(二) https://blog.csdn.net/wphkadn/article/details/90247146
Karto_slam框架与代码解析 https://blog.csdn.net/qq_24893115/article/details/52965410
加入了详细中文解释的open_karto地址: https://github.com/kadn/open_karto