news 2026/10/22 16:57:19

【SLAM】ORB-SLAM2地图加载

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【SLAM】ORB-SLAM2地图加载

前一篇讲了地图保存,经测试是可以使用的,现在进行之前保存地图的读取模式,但是这个代码会有几个问题,后续还得慢慢解决。

首先在Map.h加入函数的定义,Map.cc中用到SystemSetting,Map.h中需要加入SystemSetting.h,也需要添加“class SystemSetting;"后面会涉及到。

void Load( const string &filename, SystemSetting* mySystemSetting ); MapPoint* LoadMapPoint( ifstream &f ); KeyFrame* LoadKeyFrame( ifstream &f, SystemSetting* mySystemSetting );

下面是加载主函数Load的构成:

void Map::Load ( const string &filename, SystemSetting* mySystemSetting ) { cerr << "Map reading from:"<<filename<<endl; ifstream f; f.open( filename.c_str() ); //按照保存的顺序,先读取MapPoints的数目; unsigned long int nMapPoints; f.read((char*)&nMapPoints, sizeof(nMapPoints)); //依次读取每一个MapPoints,并将其加入到地图中 cerr<<"The number of MapPoints:"<<nMapPoints<<endl; for ( unsigned int i = 0; i < nMapPoints; i ++ ) { MapPoint* mp = LoadMapPoint(f); AddMapPoint(mp); } //获取所有的MapPoints; std::vector<MapPoint*> vmp = GetAllMapPoints(); //读取关键帧的数目; unsigned long int nKeyFrames; f.read((char*)&nKeyFrames, sizeof(nKeyFrames)); cerr<<"The number of KeyFrames:"<<nKeyFrames<<endl; //依次读取每一关键帧,并加入到地图; vector<KeyFrame*>kf_by_order; for( unsigned int i = 0; i < nKeyFrames; i ++ ) { KeyFrame* kf = LoadKeyFrame(f, mySystemSetting); AddKeyFrame(kf); kf_by_order.push_back(kf); } cerr<<"KeyFrame Load OVER!"<<endl; //读取生长树; map<unsigned long int, KeyFrame*> kf_by_id; for ( auto kf: mspKeyFrames ) kf_by_id[kf->mnId] = kf; cerr<<"Start Load The Parent!"<<endl; for( auto kf: kf_by_order ) { //读取当前关键帧的父节点ID; unsigned long int parent_id; f.read((char*)&parent_id, sizeof(parent_id)); //给当前关键帧添加父节点关键帧; if ( parent_id != ULONG_MAX ) kf->ChangeParent(kf_by_id[parent_id]); //读取当前关键帧的关联关系; //先读取当前关键帧的关联关键帧的数目; unsigned long int nb_con; f.read((char*)&nb_con, sizeof(nb_con)); //然后读取每一个关联关键帧的ID和weight,并把该关联关键帧加入关系图中; for ( unsigned long int i = 0; i < nb_con; i ++ ) { unsigned long int id; int weight; f.read((char*)&id, sizeof(id)); f.read((char*)&weight, sizeof(weight)); kf->AddConnection(kf_by_id[id],weight); } } cerr<<"Parent Load OVER!"<<endl; for ( auto mp: vmp ) { if(mp) { mp->ComputeDistinctiveDescriptors(); mp->UpdateNormalAndDepth(); } } f.close(); cerr<<"Load IS OVER!"<<endl; return; }

其过程就是根据保存的顺序依次加载地图点的数目、地图点、关键帧的数目、关键帧、生长树和关联关系。

下面是LoadMapPoints函数的构成:

MapPoint* Map::LoadMapPoint( ifstream &f ) { //主要包括MapPoints的位姿和ID; cv::Mat Position(3,1,CV_32F); long unsigned int id; f.read((char*)&id, sizeof(id)); f.read((char*)&Position.at<float>(0), sizeof(float)); f.read((char*)&Position.at<float>(1), sizeof(float)); f.read((char*)&Position.at<float>(2), sizeof(float)); //初始化一个MapPoint,并设置其ID和Position; MapPoint* mp = new MapPoint(Position, this ); mp->mnId = id; mp->SetWorldPos( Position ); return mp; }

从这里开始涉及到了MapPoint类的初始化问题,由于这里只有Position以及当前的Map,所以需要从新定义MapPoint的构造函数,分别加入到MapPoint的头文件和源文件中,在MapPoint.h添加定义:

MapPoint( const cv::Mat& Pos, Map* pMap );

在MapPoint.cc中

MapPoint::MapPoint(const cv::Mat &Pos, Map* pMap): mnFirstKFid(0), mnFirstFrame(0), nObs(0), mnTrackReferenceForFrame(0), mnLastFrameSeen(0), mnBALocalForKF(0), mnFuseCandidateForKF(0), mnLoopPointForKF(0), mnCorrectedByKF(0), mnCorrectedReference(0), mnBAGlobalForKF(0), mpRefKF(static_cast<KeyFrame*>(NULL)), mnVisible(1), mnFound(1), mbBad(false), mpReplaced(static_cast<MapPoint*>(NULL)), mfMinDistance(0), mfMaxDistance(0), mpMap(pMap) { Pos.copyTo(mWorldPos); mNormalVector = cv::Mat::zeros(3,1,CV_32F); // MapPoints can be created from Tracking and Local Mapping. This mutex avoid conflicts with id. unique_lock<mutex> lock(mpMap->mMutexPointCreation); mnId=nNextId++; }

紧接着是LoadKeyFrame函数的构成,这里由于KeyFrame类需要的初始化信息比较多,因此定义了一个InitKeyFrame类,它通过SystemSetting进行初始化,二SystemSetting的主要作用就是读取设置文件(相机内参,ORB特征参数等),后面将给出SystemSetting和InitKeyFrame类的代码:

KeyFrame* Map::LoadKeyFrame( ifstream &f, SystemSetting* mySystemSetting ) { //声明一个初始化关键帧的类initkf; InitKeyFrame initkf(*mySystemSetting); //按照保存次序,依次读取关键帧的ID和时间戳; f.read((char*)&initkf.nId, sizeof(initkf.nId)); f.read((char*)&initkf.TimeStamp, sizeof(double)); //读取关键帧位姿矩阵; cv::Mat T = cv::Mat::zeros(4,4,CV_32F); std::vector<float> Quat(4); //Quat.reserve(4); for ( int i = 0; i < 4; i ++ ) f.read((char*)&Quat[i],sizeof(float)); cv::Mat R = Converter::toCvMat( Quat ); for ( int i = 0; i < 3; i ++ ) f.read((char*)&T.at<float>(i,3),sizeof(float)); for ( int i = 0; i < 3; i ++ ) for ( int j = 0; j < 3; j ++ ) T.at<float>(i,j) = R.at<float>(i,j); T.at<float>(3,3) = 1; // for ( int i = 0; i < 4; i ++ ) // { // for ( int j = 0; j < 4; j ++ ) // { // f.read((char*)&T.at<float>(i,j), sizeof(float)); // cerr<<"T.at<float>("<<i<<","<<j<<"):"<<T.at<float>(i,j)<<endl; // } // } //读取当前关键帧特征点的数目; f.read((char*)&initkf.N, sizeof(initkf.N)); initkf.vKps.reserve(initkf.N); initkf.Descriptors.create(initkf.N, 32, CV_8UC1); vector<float>KeypointDepth; std::vector<MapPoint*> vpMapPoints; vpMapPoints = vector<MapPoint*>(initkf.N,static_cast<MapPoint*>(NULL)); //依次读取当前关键帧的特征点和描述符; std::vector<MapPoint*> vmp = GetAllMapPoints(); for(int i = 0; i < initkf.N; i ++ ) { cv::KeyPoint kp; f.read((char*)&kp.pt.x, sizeof(kp.pt.x)); f.read((char*)&kp.pt.y, sizeof(kp.pt.y)); f.read((char*)&kp.size, sizeof(kp.size)); f.read((char*)&kp.angle,sizeof(kp.angle)); f.read((char*)&kp.response, sizeof(kp.response)); f.read((char*)&kp.octave, sizeof(kp.octave)); initkf.vKps.push_back(kp); //根据需要读取特征点的深度值; //float fDepthValue = 0.0; //f.read((char*)&fDepthValue, sizeof(float)); //KeypointDepth.push_back(fDepthValue); //读取当前特征点的描述符; for ( int j = 0; j < 32; j ++ ) f.read((char*)&initkf.Descriptors.at<unsigned char>(i,j),sizeof(char)); //读取当前特征点和MapPoints的对应关系; unsigned long int mpidx; f.read((char*)&mpidx, sizeof(mpidx)); //从vmp这个所有的MapPoints中查找当前关键帧的MapPoint,并插入 if( mpidx == ULONG_MAX ) vpMapPoints[i] = NULL; else vpMapPoints[i] = vmp[mpidx]; } initkf.vRight = vector<float>(initkf.N,-1); initkf.vDepth = vector<float>(initkf.N,-1); //initkf.vDepth = KeypointDepth; initkf.UndistortKeyPoints(); initkf.AssignFeaturesToGrid(); //使用initkf初始化一个关键帧,并设置相关参数 KeyFrame* kf = new KeyFrame( initkf, this, NULL, vpMapPoints ); kf->mnId = initkf.nId; kf->SetPose(T); kf->ComputeBoW(); for ( int i = 0; i < initkf.N; i ++ ) { if ( vpMapPoints[i] ) { vpMapPoints[i]->AddObservation(kf,i); if( !vpMapPoints[i]->GetReferenceKeyFrame()) vpMapPoints[i]->SetReferenceKeyFrame(kf); } } return kf;

从文件中读取的内容同保存的一致,同时由于是通过InitKeyFrame初始化的关键帧类KeyFrame,因此这里同样需要添加构造函数以及初始化方式:
在KeyFrame.h中添加对应的函数

KeyFrame(InitKeyFrame &initkf, Map* pMap, KeyFrameDatabase* pKFDB,vector< MapPoint*>& vpMapPoints);

在对应的KeyFrame.cc中添加函数对应的定义

KeyFrame::KeyFrame(InitKeyFrame &initkf, Map *pMap, KeyFrameDatabase *pKFDB, vector<MapPoint*> &vpMapPoints): mnFrameId(0), mTimeStamp(initkf.TimeStamp), mnGridCols(FRAME_GRID_COLS), mnGridRows(FRAME_GRID_ROWS), mfGridElementWidthInv(initkf.fGridElementWidthInv), mfGridElementHeightInv(initkf.fGridElementHeightInv), mnTrackReferenceForFrame(0), mnFuseTargetForKF(0), mnBALocalForKF(0), mnBAFixedForKF(0), mnLoopQuery(0), mnLoopWords(0), mnRelocQuery(0), mnRelocWords(0), mnBAGlobalForKF(0), fx(initkf.fx), fy(initkf.fy), cx(initkf.cx), cy(initkf.cy), invfx(initkf.invfx), invfy(initkf.invfy), mbf(initkf.bf), mb(initkf.b), mThDepth(initkf.ThDepth), N(initkf.N), mvKeys(initkf.vKps), mvKeysUn(initkf.vKpsUn), mvuRight(initkf.vRight), mvDepth(initkf.vDepth), mDescriptors(initkf.Descriptors.clone()), mBowVec(initkf.BowVec), mFeatVec(initkf.FeatVec), mnScaleLevels(initkf.nScaleLevels), mfScaleFactor(initkf.fScaleFactor), mfLogScaleFactor(initkf.fLogScaleFactor), mvScaleFactors(initkf.vScaleFactors), mvLevelSigma2(initkf.vLevelSigma2),mvInvLevelSigma2(initkf.vInvLevelSigma2), mnMinX(initkf.nMinX), mnMinY(initkf.nMinY), mnMaxX(initkf.nMaxX), mnMaxY(initkf.nMaxY), mK(initkf.K), mvpMapPoints(vpMapPoints), mpKeyFrameDB(pKFDB), mpORBvocabulary(initkf.pVocabulary), mbFirstConnection(true), mpParent(NULL), mbNotErase(false), mbToBeErased(false), mbBad(false), mHalfBaseline(initkf.b/2), mpMap(pMap) { mnId = nNextId ++; }

加载了一个关键帧之后还需要计算其BoW向量等操作,同时指定关键帧对地图点的观测。

在MapPoint.h中添加函数定义

KeyFrame* SetReferenceKeyFrame(KeyFrame* RFKF);

在MapPoint.cc中添加

KeyFrame* MapPoint::SetReferenceKeyFrame(KeyFrame* RFKF) { return mpRefKF = RFKF; }

补充SystemSetting和InitKeyFrame两个类的代码。实际上,由于是通过SystemSetting来读取的相机内参以及ORB特征参数,所以就可以将Tracking.cc中关于读取内参的部分替换掉了

创建新的头文件SystemSetting.h

#ifndef SYSTEMSETTING_H #define SYSTEMSETTING_H #include <string> #include "ORBVocabulary.h" #include<opencv2/core/core.hpp> namespace ORB_SLAM2 { class SystemSetting{ //Load camera parameters from setting file public: SystemSetting(ORBVocabulary* pVoc); //SystemSetting::SystemSetting(ORBVocabulary* pVoc, KeyFrameDatabase* pKFDB ); bool LoadSystemSetting(const std::string strSettingPath); public: //The Vocabulary and KeyFrameDatabase ORBVocabulary* pVocabulary; //KeyFrameDatabase* pKeyFrameDatabase; //Camera parameters float width; float height; float fx; float fy; float cx; float cy; float invfx; float invfy; float bf; float b; float fps; cv::Mat K; cv::Mat DistCoef; bool initialized; //Camera RGB parameters int nRGB; //ORB feature parameters int nFeatures; float fScaleFactor; int nLevels; float fIniThFAST; float fMinThFAST; //other parameters float ThDepth = -1; float DepthMapFactor = -1; }; } //namespace ORB_SLAM2 #endif //SystemSetting

SystemSetting.cc的函数具体实现:

#include <iostream> #include "SystemSetting.h" using namespace std; namespace ORB_SLAM2 { SystemSetting::SystemSetting(ORBVocabulary* pVoc): pVocabulary(pVoc) { } //SystemSetting::SystemSetting(ORBVocabulary* pVoc, KeyFrameDatabase* pKFDB): // pVocabulary(pVoc), pKeyFrameDatabase(pKFDB) // { // } bool SystemSetting::LoadSystemSetting(const std::string strSettingPath){ cout<<endl<<"Loading System Parameters form:"<<strSettingPath<<endl; cv::FileStorage fSettings(strSettingPath, cv::FileStorage::READ); width = fSettings["Camera.width"]; height = fSettings["Camera.height"]; fx = fSettings["Camera.fx"]; fy = fSettings["Camera.fy"]; cx = fSettings["Camera.cx"]; cy = fSettings["Camera.cy"]; cv::Mat tmpK = cv::Mat::eye(3,3,CV_32F); tmpK.at<float>(0,0) = fx; tmpK.at<float>(1,1) = fy; tmpK.at<float>(0,2) = cx; tmpK.at<float>(1,2) = cy; tmpK.copyTo(K); cv::Mat tmpDistCoef(4,1,CV_32F); tmpDistCoef.at<float>(0) = fSettings["Camera.k1"]; tmpDistCoef.at<float>(1) = fSettings["Camera.k2"]; tmpDistCoef.at<float>(2) = fSettings["Camera.p1"]; tmpDistCoef.at<float>(3) = fSettings["Camera.p2"]; const float k3 = fSettings["Camera.k3"]; if( k3!=0 ) { tmpDistCoef.resize(5); tmpDistCoef.at<float>(4) = k3; } tmpDistCoef.copyTo( DistCoef ); bf = fSettings["Camera.bf"]; fps= fSettings["Camera.fps"]; invfx = 1.0f/fx; invfy = 1.0f/fy; b = bf /fx; initialized = true; cout<<"- size:"<<width<<"x"<<height<<endl; cout<<"- fx:" <<fx<<endl; cout << "- fy: " << fy << endl; cout << "- cx: " << cx << endl; cout << "- cy: " << cy << endl; cout << "- k1: " << DistCoef.at<float>(0) << endl; cout << "- k2: " << DistCoef.at<float>(1) << endl; if(DistCoef.rows==5) cout << "- k3: " << DistCoef.at<float>(4) << endl; cout << "- p1: " << DistCoef.at<float>(2) << endl; cout << "- p2: " << DistCoef.at<float>(3) << endl; cout << "- bf: " << bf << endl; //Load RGB parameter nRGB = fSettings["Camera.RGB"]; //Load ORB feature parameters nFeatures = fSettings["ORBextractor.nFeatures"]; fScaleFactor = fSettings["ORBextractor.scaleFactor"]; nLevels = fSettings["ORBextractor.nLevels"]; fIniThFAST = fSettings["ORBextractor.iniThFAST"]; fMinThFAST = fSettings["ORBextractor.minThFAST"]; cout << endl << "ORB Extractor Parameters: " << endl; cout << "- Number of Features: " << nFeatures << endl; cout << "- Scale Levels: " << nLevels << endl; cout << "- Scale Factor: " << fScaleFactor << endl; cout << "- Initial Fast Threshold: " << fIniThFAST << endl; cout << "- Minimum Fast Threshold: " << fMinThFAST << endl; //Load others parameters, if the sensor is MONOCULAR, the parameters is zero; //ThDepth = fSettings["ThDepth"]; //DepthMapFactor = fSettings["DepthMapFactor"]; fSettings.release(); return true; } }

创建 InitKeyFrame.h,并在keyframe.h中加上InitKeyFrame头文件,还需要加上“class InitKeyFrame".

#ifndef INITKEYFRAME_H #define INITKEYFRAME_H #include "Thirdparty/DBoW2/DBoW2/BowVector.h" #include "Thirdparty/DBoW2/DBoW2/FeatureVector.h" #include "SystemSetting.h" #include <opencv2/opencv.hpp> #include "ORBVocabulary.h" #include "KeyFrameDatabase.h" //#include "MapPoints.h" namespace ORB_SLAM2 { #define FRAME_GRID_ROWS 48 #define FRAME_GRID_COLS 64 class SystemSetting; class KeyFrameDatabase; //class ORBVocabulary; class InitKeyFrame { public: InitKeyFrame(SystemSetting &SS); void UndistortKeyPoints(); bool PosInGrid(const cv::KeyPoint& kp, int &posX, int &posY); void AssignFeaturesToGrid(); public: ORBVocabulary* pVocabulary; //KeyFrameDatabase* pKeyFrameDatabase; long unsigned int nId; double TimeStamp; float fGridElementWidthInv; float fGridElementHeightInv; std::vector<std::size_t> vGrid[FRAME_GRID_COLS][FRAME_GRID_ROWS]; float fx; float fy; float cx; float cy; float invfx; float invfy; float bf; float b; float ThDepth; int N; std::vector<cv::KeyPoint> vKps; std::vector<cv::KeyPoint> vKpsUn; cv::Mat Descriptors; //it's zero for mono std::vector<float> vRight; std::vector<float> vDepth; DBoW2::BowVector BowVec; DBoW2::FeatureVector FeatVec; int nScaleLevels; float fScaleFactor; float fLogScaleFactor; std::vector<float> vScaleFactors; std::vector<float> vLevelSigma2; std::vector<float> vInvLevelSigma2; std::vector<float> vInvScaleFactors; int nMinX; int nMinY; int nMaxX; int nMaxY; cv::Mat K; cv::Mat DistCoef; }; } //namespace ORB_SLAM2 #endif //INITKEYFRAME_H

InitKeyFrame.cc的函数实现

#include "InitKeyFrame.h" #include <opencv2/opencv.hpp> #include "SystemSetting.h" namespace ORB_SLAM2 { InitKeyFrame::InitKeyFrame(SystemSetting &SS):pVocabulary(SS.pVocabulary)//, pKeyFrameDatabase(SS.pKeyFrameDatabase) { fx = SS.fx; fy = SS.fy; cx = SS.cx; cy = SS.cy; invfx = SS.invfx; invfy = SS.invfy; bf = SS.bf; b = SS.b; ThDepth = SS.ThDepth; nScaleLevels = SS.nLevels; fScaleFactor = SS.fScaleFactor; fLogScaleFactor = log(SS.fScaleFactor); vScaleFactors.resize(nScaleLevels); vLevelSigma2.resize(nScaleLevels); vScaleFactors[0] = 1.0f; vLevelSigma2[0] = 1.0f; for ( int i = 1; i < nScaleLevels; i ++ ) { vScaleFactors[i] = vScaleFactors[i-1]*fScaleFactor; vLevelSigma2[i] = vScaleFactors[i]*vScaleFactors[i]; } vInvScaleFactors.resize(nScaleLevels); vInvLevelSigma2.resize(nScaleLevels); for ( int i = 0; i < nScaleLevels; i ++ ) { vInvScaleFactors[i] = 1.0f/vScaleFactors[i]; vInvLevelSigma2[i] = 1.0f/vLevelSigma2[i]; } K = SS.K; DistCoef = SS.DistCoef; if( SS.DistCoef.at<float>(0)!=0.0) { cv::Mat mat(4,2,CV_32F); mat.at<float>(0,0) = 0.0; mat.at<float>(0,1) = 0.0; mat.at<float>(1,0) = SS.width; mat.at<float>(1,1) = 0.0; mat.at<float>(2,0) = 0.0; mat.at<float>(2,1) = SS.height; mat.at<float>(3,0) = SS.width; mat.at<float>(3,1) = SS.height; mat = mat.reshape(2); cv::undistortPoints(mat, mat, SS.K, SS.DistCoef, cv::Mat(), SS.K); mat = mat.reshape(1); nMinX = min(mat.at<float>(0,0), mat.at<float>(2,0)); nMaxX = max(mat.at<float>(1,0), mat.at<float>(3,0)); nMinY = min(mat.at<float>(0,1), mat.at<float>(1,1)); nMaxY = max(mat.at<float>(2,1), mat.at<float>(3,1)); } else { nMinX = 0.0f; nMaxX = SS.width; nMinY = 0.0f; nMaxY = SS.height; } fGridElementWidthInv=static_cast<float>(FRAME_GRID_COLS)/(nMaxX-nMinX); fGridElementHeightInv=static_cast<float>(FRAME_GRID_ROWS)/(nMaxY-nMinY); } void InitKeyFrame::UndistortKeyPoints() { if( DistCoef.at<float>(0) == 0.0) { vKpsUn = vKps; return; } cv::Mat mat(N,2,CV_32F); for ( int i = 0; i < N; i ++ ) { mat.at<float>(i,0) = vKps[i].pt.x; mat.at<float>(i,1) = vKps[i].pt.y; } mat = mat.reshape(2); cv::undistortPoints(mat, mat, K, DistCoef, cv::Mat(), K ); mat = mat.reshape(1); vKpsUn.resize(N); for( int i = 0; i < N; i ++ ) { cv::KeyPoint kp = vKps[i]; kp.pt.x = mat.at<float>(i,0); kp.pt.y = mat.at<float>(i,1); vKpsUn[i] = kp; } } void InitKeyFrame::AssignFeaturesToGrid() { int nReserve = 0.5f*N/(FRAME_GRID_COLS*FRAME_GRID_ROWS); for ( unsigned int i = 0; i < FRAME_GRID_COLS; i ++ ) { for ( unsigned int j = 0; j < FRAME_GRID_ROWS; j ++) vGrid[i][j].reserve(nReserve); } for ( int i = 0; i < N; i ++ ) { const cv::KeyPoint& kp = vKpsUn[i]; int nGridPosX, nGridPosY; if( PosInGrid(kp, nGridPosX, nGridPosY)) vGrid[nGridPosX][nGridPosY].push_back(i); } } bool InitKeyFrame::PosInGrid(const cv::KeyPoint &kp, int &posX, int &posY) { posX = round((kp.pt.x-nMinX)*fGridElementWidthInv); posY = round((kp.pt.y-nMinY)*fGridElementHeightInv); if(posX<0 || posX>=FRAME_GRID_COLS ||posY<0 || posY>=FRAME_GRID_ROWS) return false; return true; } }

在System.h中添加函数定义:

void LoadMap(const string &filename,SystemSetting* mySystemSetting);

并在对应的System.cc中添加了定义

void System::LoadMap(const string &filename,SystemSetting* mySystemSetting) { mpMap->Load(filename, mySystemSetting); }

还需要在System.h中添加声明

std::string mySettingsFile;

同时添加构造函数

mySettingsFile = strSettingsFile;

如果这样读进来好像没法做定位只能显示出地图,需要在Map.cc中Load函数读入每一个关键帧后添加到KeyFrameDatabase中去,但这样需要在Map.h中加入KeyFrameDatabase的头文件,这样就可以读入地图后进行relocalisation了。

会出现什么样的问题呢?如果在重新播放之前的场景,并不能进行重定位,同一个特征点可能会被认为是不同的点,即在空间中会产生两个点。导致地图错位!但是,本篇博客主要是记载如何进行mapName.bin的地图加载,其他工作还得完善。

这是直接加载的之前一篇文章的地图:

没有办法进行重定位,一直都是下图的状态:

参考博客地址:
https://blog.csdn.net/u012177641/article/details/78802315
http://www.cnblogs.com/mafuqiang/p/6972841.html

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/22 16:56:35

实测AI论文网站:代写陷阱与润色神器,2025届避坑指南

又到了一年一度论文季。“2025届必备的AI论文网站实际效果”这个话题&#xff0c;最近后台留言至少几十条&#xff0c;全是在问同一个问题&#xff1a;网上吹得天花乱坠的那些AI论文网站&#xff0c;到底真能帮我写论文&#xff0c;还是就是个高级点的废话生成器&#xff1f; …

作者头像 李华
网站建设 2026/10/22 16:55:56

从信息逻辑到排版演示:PPT制作基础知识完整清单

做PPT这件事&#xff0c;我一直觉得是典型的“看着简单&#xff0c;做起来全是坑”。前几年每次接到汇报任务&#xff0c;我都会先在空白页面里折腾配色和动画&#xff0c;最后做出来的东西&#xff0c;自己看着都心虚。后来跟着一位资深PPT讲师把基础知识从头到脚过了一遍&…

作者头像 李华
网站建设 2026/10/22 16:55:54

AI日报:效率竞赛开启,KV Cache优化与端侧部署加速智能体落地

1. 今日速览&#xff1a;先花三分钟抓住重点 今天是2026年10月7日&#xff0c;照例把凌晨到现在的各路AI动态捋了一遍。跟过去一段时间相比&#xff0c;这周的节奏明显不一样了&#xff1a;大家不再比拼“谁家的模型参数多、榜单刷得高”&#xff0c;而是开始扎堆聊“推理成本怎…

作者头像 李华
网站建设 2026/10/22 16:55:54

高校考务管理系统实战:从资源调度到智慧校园考试安排

说实话&#xff0c;第一次听到“高校考务管理系统”这个需求的时候&#xff0c;我是有点抗拒的。我当时的想法很简单&#xff1a;排个考试、发个通知&#xff0c;用 Excel 加微信群不就够了&#xff1f;结果真坐下来聊了一个下午&#xff0c;发现自己错得离谱。某高校信息中心找…

作者头像 李华
网站建设 2026/10/22 16:54:55

深入Android 10通知服务NMS:渠道、排名与免打扰机制解析

Android 10 的通知体验和 8、9 差别很大&#xff0c;通知渠道&#xff08;NotificationChannel&#xff09;、通知分组、免打扰策略在 10 上已经趋于成熟&#xff0c;但真正让我差点被逼疯的&#xff0c;不是怎么调 API&#xff0c;而是系统侧那个负责所有通知"生杀大权&q…

作者头像 李华
网站建设 2026/10/22 16:54:16

Pikachu靶场SQL注入全解析:10种注入类型与实战复现

1. 动手前先分清&#xff1a;Pikachu靶场里这10种注入到底怎么分类1.1 为什么新手首选Pikachu如果你是第一次接触SQL注入&#xff0c;千万不要去公网上随便找个站点练手。我见过太多新人直接对着线上业务乱打&#xff0c;结果要么被防护组件拦下来&#xff0c;要么把别人测试数…

作者头像 李华