orb-slam2 从单目开始的简单学习(4):match

2023-11-04

前言

本章不是作为主线,而是作为主线的辅助理解

1. Search

常见函数 用途
GetFeaturesInArea 返回的以x,y为中心,半径为r的圆形内且金字塔层级在[minLevel, maxLevel]的特征点

可以打开文档之后ctrl+f寻找自己不了解的函数
【参考文档】orb-slam2 从单目开始的简单学习(6)Frame

1.1 SearchByBoW

int ORBmatcher::SearchByBoW(KeyFrame* pKF,Frame &F, vector<MapPoint*> &vpMapPointMatches)
int ORBmatcher::SearchByBoW(KeyFrame *pKF1, KeyFrame *pKF2, vector<MapPoint *> &vpMatches12)

1.1.1数据类型 和 基本函数

1.1.1.1.数据类型
数据名 类型
FeatureVector map
// Vector of nodes with indexes of local features具有局部特征索引的节点向量
class FeatureVector: 
  public std::map<NodeId, std::vector<unsigned int> >

NodeId:节点向量(可以理解为树干)
vector :局部特征索引(可以理解为该树干对应的叶子)

vFeatVecKF,F.mFeatVec本质上都是上面这个map<NodeId, std::vector<unsigned int> >,概括起来就是词袋特征向量

1.1.1.2.基本函数
KFit = vFeatVecKF.lower_bound(Fit->first);

vFeatVecKF底层是一个map,因此lower_bound也是map的一个方法

terator lower_bound(const key_type& _Keyval):返回一个迭代器,指向键值 >= _Keyval 的第一个元素;

1.1.2 对于词袋的理解

进一步了解BOW建议是移步到computeBOW

对于每一个节点应该是两个维度上的观察
竖直维度上:节点之间相互连接
水平维度上:该节点上对应的索引

画了个二维理解图希望能帮助理解

请添加图片描述

1.1.3 完整代码

int ORBmatcher::SearchByBoW(KeyFrame* pKF,Frame &F, vector<MapPoint*> &vpMapPointMatches)
{
    const vector<MapPoint*> vpMapPointsKF = pKF->GetMapPointMatches();
    								//返回mvpMapPoints:与KeyPoint相关联的MapPoints

    vpMapPointMatches = vector<MapPoint*>(F.N,static_cast<MapPoint*>(NULL));
    					// F.N:Number of KeyPoints.

    const DBoW2::FeatureVector &vFeatVecKF = pKF->mFeatVec;

    int nmatches=0;
    
//----------------为了检查KeyPoints方向性做准备-----------------
    vector<int> rotHist[HISTO_LENGTH];//const int ORBmatcher::HISTO_LENGTH = 30;
    for(int i=0;i<HISTO_LENGTH;i++)
        rotHist[i].reserve(500);
    const float factor = 1.0f/HISTO_LENGTH;
//----------------为了检查KeyPoints方向性做准备-----------------
//--------------为了遍历KeyPoints做准备----------------
    // 对于同一结点上的orb进行匹配
    DBoW2::FeatureVector::const_iterator KFit = vFeatVecKF.begin();//map类型
    DBoW2::FeatureVector::const_iterator Fit = F.mFeatVec.begin();
    DBoW2::FeatureVector::const_iterator KFend = vFeatVecKF.end();
    DBoW2::FeatureVector::const_iterator Fend = F.mFeatVec.end();
            
        //vFeatVecKF,F.mFeatVec都属于FeatureVector
  //FeatureVector:map<NodeId, std::vector<unsigned int> >
  
//--------------为了遍历KeyPoints做准备-------------
    

//-------------开始遍历-------------------
//FeatureVector:map<node_id,std::vector<feature_id>>                                                 
    while(KFit != KFend && Fit != Fend)
    {
    //具有一定相似性的会集中在同一个结点下
    //为了确保其唯一性,需要找到vector中最好的
        if(KFit->first == Fit->first)
        {
            const vector<unsigned int> vIndicesKF = KFit->second;
            const vector<unsigned int> vIndicesF = Fit->second;

            for(size_t iKF=0; iKF<vIndicesKF.size(); iKF++)
            {
                const unsigned int realIdxKF = vIndicesKF[iKF];

                MapPoint* pMP = vpMapPointsKF[realIdxKF];

                if(!pMP)
                    continue;

                if(pMP->isBad())
                    continue;         
                           
//-------确认KeyPoint的唯一性(最小/次小 bit距离)------------------------
                const cv::Mat &dKF= pKF->mDescriptors.row(realIdxKF);

                int bestDist1=256;
                int bestIdxF =-1 ;
                int bestDist2=256;

                for(size_t iF=0; iF<vIndicesF.size(); iF++)
                {
                    const unsigned int realIdxF = vIndicesF[iF];

                    if(vpMapPointMatches[realIdxF])//已经进入过下面vpMapPointMatches[bestIdxF]=pMP;
                        continue;
                        
					
                    const cv::Mat &dF = F.mDescriptors.row(realIdxF);
                    //orb描述子,每一行与一个关键点相关联

                    const int dist =  DescriptorDistance(dKF,dF);//按位计算距离

                    if(dist<bestDist1)//找最小距离
                    {
                        bestDist2=bestDist1;
                        bestDist1=dist;
                        bestIdxF=realIdxF;
                    }
                    else if(dist<bestDist2)//次小距离  bestDist1<dist<bestDist2
                    {
                        bestDist2=dist;
                    }
                    
                }//最小和次小距离:描述当前结点下的帧中与关键帧中kp【最匹配的】的
			//--------------为检查方向做准备----------------
                if(bestDist1<=TH_LOW)//const int ORBmatcher::TH_LOW = 50;
                {
                    if(static_cast<float>(bestDist1)<mfNNratio*static_cast<float>(bestDist2))
                    {
                        vpMapPointMatches[bestIdxF]=pMP;

                        const cv::KeyPoint &kp = pKF->mvKeysUn[realIdxKF];

                        if(mbCheckOrientation)
                        {
                            float rot = kp.angle-F.mvKeys[bestIdxF].angle;
                            if(rot<0.0)
                                rot+=360.0f;
                            int bin = round(rot*factor);
                            if(bin==HISTO_LENGTH)
                                bin=0;
                            assert(bin>=0 && bin<HISTO_LENGTH);
                            rotHist[bin].push_back(bestIdxF);
                        }
                        nmatches++;
                    }
                }
              //--------------为检查方向做准备----------------
//-------确认KeyPoint的唯一性(最小/次小距离)------------------------
            }

            KFit++;
            Fit++;
        }
        else if(KFit->first < Fit->first)
        {
            KFit = vFeatVecKF.lower_bound(Fit->first);
        }
        else
        {
            Fit = F.mFeatVec.lower_bound(KFit->first);
        }
    }

//-------确认KeyPoint的 有纪念意义(方向)------------------------
    if(mbCheckOrientation)
    {
        int ind1=-1;
        int ind2=-1;
        int ind3=-1;

        ComputeThreeMaxima(rotHist,HISTO_LENGTH,ind1,ind2,ind3);

        for(int i=0; i<HISTO_LENGTH; i++)
        {
            if(i==ind1 || i==ind2 || i==ind3)
                continue;
            for(size_t j=0, jend=rotHist[i].size(); j<jend; j++)
            {
                vpMapPointMatches[rotHist[i][j]]=static_cast<MapPoint*>(NULL);
                nmatches--;
            }
        }
    }
//-------确认KeyPoint的有 纪念意义(方向显著)------------------------
    return nmatches;
}

1.1.4代码分块详解

这个博主注释上算是比较好理解的。理解这一堆代码的核心还是在树和map的映射上。

1)构建旋转直方图

在这里插入图片描述

将两个要匹配的特征点的方向做差得到rot,将rot转换到0-360度,设定一个直方图,直方图的HISTO_LENGTH即条形的个数设置为30,0-360分布在30个bins中,即每个bin代表12度.然后根据角度差将n对匹配特征点分布在直方图中,在直方图中找出三个匹配特征点最多的直方图bin,最后将不属于这三个bin的匹配关系删除
———————————————— 版权声明:本文为CSDN博主「Bobsweetie」的原创文章,遵循CC 4.0
BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/Bobsweetie/article/details/107367464

 vector<int> rotHist[HISTO_LENGTH];//const int ORBmatcher::HISTO_LENGTH = 30;
    for(int i=0;i<HISTO_LENGTH;i++)
        rotHist[i].reserve(500);
    const float factor = 1.0f/HISTO_LENGTH;

可以理解成分成30个基本块,每个块的高度最多500

2)if-elif-else
while(KFit != KFend && Fit != Fend)
    {
        if(KFit->first == Fit->first)
        {
            KFit++;
            Fit++;
        }
        else if(KFit->first < Fit->first)
        {
            KFit = vFeatVecKF.lower_bound(Fit->first);
        }
        else
        {
            Fit = F.mFeatVec.lower_bound(KFit->first);
        }

KFit->first == Fit->first寻找相同结点(基于前面对于词袋的理解)
KFit = vFeatVecKF.lower_bound(Fit->first)可以理解为将相同结点对齐

总结

还是和我在tracking中反复提及的唯一性有纪念意义

  • 唯一性:通过寻找当前帧与关键帧最相似的点
    寻找方法:KP 送进BOW结点中,在节点中进一步寻找最小距离
  • 有纪念意义:记录显著方向的

另一个重载

int ORBmatcher::SearchByBoW(KeyFrame *pKF1, KeyFrame *pKF2, vector<MapPoint *> &vpMatches12)

其实也差不多,节省篇幅就不上全部代码了
只不过就是变成了两个都是关键帧

1.2 SearchByProjection

1.2.1解析

利用重投影在其投影点附近根据描述子距离选取匹配,由此增加当前帧的MapPoints 。在TrackWithMotionModel()中使用
重投影:利用将相机坐标系下(三维)的Local MapPoints投影到图像坐标系(二维)。

共有四种:

int ORBmatcher::SearchByProjection(Frame &F, const vector<MapPoint*> &vpMapPoints, const float th)

int ORBmatcher::SearchByProjection(KeyFrame* pKF, cv::Mat Scw, const vector<MapPoint*> &vpPoints, vector<MapPoint*> &vpMatched, int th)

int ORBmatcher::SearchByProjection(Frame &CurrentFrame, const Frame &LastFrame, const float th, const bool bMono)

int ORBmatcher::SearchByProjection(Frame &CurrentFrame, KeyFrame *pKF, const set<MapPoint*> &sAlreadyFound, const float th , const int ORBdist)

根据TrackWithMotionModel()中代码

 int nmatches = matcher.SearchByProjection(mCurrentFrame,mLastFrame,th,mSensor==System::MONOCULAR);

应该是第三种,因此着重讲解第三种这篇中有对四种进行讲解

1.2.1.1完整代码

1.2.2代码分块解析

int ORBmatcher::SearchByProjection(Frame &CurrentFrame, const Frame &LastFrame, const float th, const bool bMono)

将上一帧跟踪的地图点投影到当前帧,并且搜索匹配点。

th:搜索范围阈值
bMono:是否为单目相机

1) 位姿关系
//当前相机坐标系到世界坐标系的平移向量
    const cv::Mat twc = -Rcw.t()*tcw;

《slam14讲》p44
基于特殊欧式群的性质:求解该矩阵的逆表示一个反向的变换
特殊欧式群:
请添加图片描述

其中矩阵T称为变化矩阵

该矩阵的逆:

请添加图片描述

const cv::Mat tlc = Rlw*twc+tlw;

//截图过来

2)针对双目/RGB-D 相机向前/后
// 仅针对双目或RGB-D相机
    const bool bForward = tlc.at<float>(2)>CurrentFrame.mb && !bMono;
    const bool bBackward = -tlc.at<float>(2)>CurrentFrame.mb && !bMono;
3)小孔成像模型

代码的整体逻辑是

  • 以下是三维世界中的MapPoint点到像素坐标的计算过程
    1.世界坐标–>相机坐标;
    2.相机坐标–>相机归一化平面坐标;
    3.相机归一化平面坐标–>像素坐标;
	  const float invzc = 1.0/x3Dc.at<float>(2)//归一化平面
    float u = CurrentFrame.fx*xc*invzc+CurrentFrame.cx;
    float v = CurrentFrame.fy*yc*invzc+CurrentFrame.cy;

看到uv应该很熟悉,即就是经典的世界坐标系转相机坐标系

vector<size_t> KeyFrame::GetFeaturesInArea(const float &x, const float &y, const float &r) const

vector<size_t> Frame::GetFeaturesInArea(const float &x, const float  &y, const float  &r, const int minLevel, const int maxLevel) const
4) GetFeaturesInArea

【参考文档】orb-slam2 从单目开始的简单学习(6)Frame详见GetFeaturesInArea有完整介绍

根据相机的前后前进方向来判断搜索尺度范围
当相机前进时,原来的特征点需要在更高的尺度下才能找到正确的匹配点
当相机后退时,原来的特征点需要在更低的尺度下才能找到正确的匹配点

金字塔结构图:
在这里插入图片描述
层级越高,所看见的内容在实际场景中越大

if(bForward)
	vIndices2 = CurrentFrame.GetFeaturesInArea(u,v, radius, nLastOctave);
else if(bBackward)
	vIndices2 = CurrentFrame.GetFeaturesInArea(u,v, radius, 0, nLastOctave);
else//非双目情况
	vIndices2 = CurrentFrame.GetFeaturesInArea(u,v, radius, nLastOctave-1, nLastOctave+1);

5)搜索半径限制

双目和rgbd的情况,需要保证右图的点也在搜索半径以内

if(CurrentFrame.mvuRight[i2]>0)
{			//(u,v)
    const float ur = u - CurrentFrame.mbf*invzc;// mbf:Stereo baseline multiplied by fx.
    						//invzc:归一化参数
    const float er = fabs(ur - CurrentFrame.mvuRight[i2]);
    
    if(er>radius)
        continue;
}

1.3. SearchForInitialization

2.Fuse

将地图点与帧中图像的特征点进行匹配,实现地图点融合

// 将地图点投影到KeyFrame并搜索重复的地图点
int Fuse(KeyFrame* pKF, const vector<MapPoint *> &vpMapPoints, const float th=3.0);

// 使用给定的Sim3将MapPoints投影到KeyFrame中,并搜索重复的MapPoints。
int Fuse(KeyFrame* pKF, cv::Mat Scw, const std::vector<MapPoint*> &vpPoints, float th, vector<MapPoint *> &vpReplacePoint);

有两个重载

const float ur = u-bf*invz

基于公式

3. SearchForTriangulation

3.1 极点的理解

在这里插入图片描述
极点可以理解为 :第一帧图像相机光心在第二帧坐标系中的位置

3.2 完整代码和注释

int ORBmatcher::SearchForTriangulation(KeyFrame *pKF1, KeyFrame *pKF2, cv::Mat F12,
                                       vector<pair<size_t, size_t> > &vMatchedPairs, const bool bOnlyStereo)
{    
    const DBoW2::FeatureVector &vFeatVec1 = pKF1->mFeatVec;
    const DBoW2::FeatureVector &vFeatVec2 = pKF2->mFeatVec;

//-------step 1. 计算在第二帧中极点---------------------------------Compute epipole(baseline和平面的连线) in second image
     //极点可以理解为第一帧图像相机光心在第二帧坐标系中的位置
    cv::Mat Cw = pKF1->GetCameraCenter();//pKF1光心在世界坐标系中的坐标
    cv::Mat R2w = pKF2->GetRotation();//世界坐标系到相机坐标系
    cv::Mat t2w = pKF2->GetTranslation();
    cv::Mat C2 = R2w*Cw+t2w;//将pKF1光心从世界坐标系转移到pKF2相机坐标系
    
    const float invz = 1.0f/C2.at<float>(2);
    const float ex =pKF2->fx*C2.at<float>(0)*invz+pKF2->cx;//归一化u
    const float ey =pKF2->fy*C2.at<float>(1)*invz+pKF2->cy;//归一化v

    // 查找未跟踪关键点之间的匹配项Find matches between not tracked keypoints
    // 通过ORB词汇表,加快匹配速度Matching speed-up by ORB Vocabulary
    // 仅比较共享同一节点的ORBCompare only ORB that share the same node

    int nmatches=0;		//先进行预设值
    vector<bool> vbMatched2(pKF2->N,false);
    vector<int> vMatches12(pKF1->N,-1);//记录第二张图中第一张图的对应匹配点序号

    vector<int> rotHist[HISTO_LENGTH];//每一个rotHist[i]都是一个vector<int>类型
    for(int i=0;i<HISTO_LENGTH;i++)
        rotHist[i].reserve(500);//bin的最大高度是500

    const float factor = 1.0f/HISTO_LENGTH;
    
//-------step2 寻找图2中图1的匹配点------------
//FeatureVector本质上是map<NodeId, std::vector<unsigned int> >
//NodeId:代表节点 vector<unsigned int>:映射在同一节点的关键点的索引
    DBoW2::FeatureVector::const_iterator f1it = vFeatVec1.begin();
    DBoW2::FeatureVector::const_iterator f2it = vFeatVec2.begin();
    DBoW2::FeatureVector::const_iterator f1end = vFeatVec1.end();
    DBoW2::FeatureVector::const_iterator f2end = vFeatVec2.end();

//索引对应关系:关键点,地图点,描述子
    while(f1it!=f1end && f2it!=f2end)
    {//-------step2.1对应相同节点通过寻找最小距离来进行匹配---------
        if(f1it->first == f2it->first)
        {//-------step2.1.1遍历第一张图中该节点所有对应的关键点-----
            for(size_t i1=0, iend1=f1it->second.size(); i1<iend1; i1++)
            {
                const size_t idx1 = f1it->second[i1];//取索引
                
                MapPoint* pMP1 = pKF1->GetMapPoint(idx1);//取索引对应的地图点
                
                // 跳过已经存在地图点的 If there is already a MapPoint skip
                if(pMP1)
                    continue;

                const bool bStereo1 = pKF1->mvuRight[idx1]>=0;//判断是否为双目

                if(bOnlyStereo)
                    if(!bStereo1)
                        continue;
                
                const cv::KeyPoint &kp1 = pKF1->mvKeysUn[idx1];//取索引对应的未矫正的关键点
                
                const cv::Mat &d1 = pKF1->mDescriptors.row(idx1);
                
                int bestDist = TH_LOW;
                int bestIdx2 = -1;
                //-------step2.1.2遍历第二张图中该节点所有对应的关键点以寻找最小距离------
                for(size_t i2=0, iend2=f2it->second.size(); i2<iend2; i2++)
                {
                    size_t idx2 = f2it->second[i2];
                    
                    MapPoint* pMP2 = pKF2->GetMapPoint(idx2);
                    
                    // If we have already matched or there is a MapPoint skip
                    if(vbMatched2[idx2] || pMP2)
                        continue;

                    const bool bStereo2 = pKF2->mvuRight[idx2]>=0;

                    if(bOnlyStereo)
                        if(!bStereo2)
                            continue;
                    
                    const cv::Mat &d2 = pKF2->mDescriptors.row(idx2);
                    
                    const int dist = DescriptorDistance(d1,d2);
                    
                    if(dist>TH_LOW || dist>bestDist)
                        continue;

                    const cv::KeyPoint &kp2 = pKF2->mvKeysUn[idx2];

                    if(!bStereo1 && !bStereo2)
                    {// 极点到kp2的像素距离如果小于阈值th,认为kp2对应的MapPoint距离pKF1相机太近,跳过该匹配点对
                        const float distex = ex-kp2.pt.x;
                        const float distey = ey-kp2.pt.y;
                        if(distex*distex+distey*distey<100*pKF2->mvScaleFactors[kp2.octave])
                            continue;
                    }

                    if(CheckDistEpipolarLine(kp1,kp2,F12,pKF2))
                    {
                        bestIdx2 = idx2;
                        bestDist = dist;
                    }
                }
                
                if(bestIdx2>=0)
                {
                    const cv::KeyPoint &kp2 = pKF2->mvKeysUn[bestIdx2];
                    vMatches12[idx1]=bestIdx2;
                    nmatches++;//观测点

                    if(mbCheckOrientation)
                    {
                        float rot = kp1.angle-kp2.angle;
                        if(rot<0.0)
                            rot+=360.0f;
                        int bin = round(rot*factor);
                        if(bin==HISTO_LENGTH)
                            bin=0;
                        assert(bin>=0 && bin<HISTO_LENGTH);
                        rotHist[bin].push_back(idx1);
                    }
                }
            }

            f1it++;
            f2it++;
        }
        else if(f1it->first < f2it->first)
        {
            f1it = vFeatVec1.lower_bound(f2it->first);
        }
        else
        {
            f2it = vFeatVec2.lower_bound(f1it->first);
        }
    }
//-------step3 只记录前三方向变化点--------------
    if(mbCheckOrientation)
    {
        int ind1=-1;
        int ind2=-1;
        int ind3=-1;

        ComputeThreeMaxima(rotHist,HISTO_LENGTH,ind1,ind2,ind3);

        for(int i=0; i<HISTO_LENGTH; i++)
        {
            if(i==ind1 || i==ind2 || i==ind3)
                continue;
            for(size_t j=0, jend=rotHist[i].size(); j<jend; j++)
            {
                vMatches12[rotHist[i][j]]=-1;
                nmatches--;
            }
        }

    }

    vMatchedPairs.clear();
    vMatchedPairs.reserve(nmatches);
//-------step4 将匹配特征点对在帧中序号放入vMatchedPairs--------------
    for(size_t i=0, iend=vMatches12.size(); i<iend; i++)
    {
        if(vMatches12[i]<0)//被初始化为-1,会被过滤
            continue;
        vMatchedPairs.push_back(make_pair(i,vMatches12[i]));
//i代表第一帧中的关键点,vMatches12[i]代表第二帧中对应的匹配点
    }

    return nmatches;
}
  • vMatchedPairs:得到的是两帧图片对应的关键点在对应帧中序号
  • 序号对应关系:关键点,地图点,未矫正的点
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

orb-slam2 从单目开始的简单学习(4):match 的相关文章

  • Linux 中的 id 命令

    id是一个命令行实用程序 可以打印真实有效的用户和组 ID 使用id命令 语法为id命令如下 id OPTIONS USERNAME 如果省略用户名 则id命令显示有关当前登录用户的信息 当没有任何选项调用时 id打印真实用户 ID uid
  • 如何在 Debian 9 上安装 Elasticsearch

    Elasticsearch 是一个开源分布式全文搜索和分析引擎 它支持 RESTful 操作 允许您实时存储 搜索和分析大量数据 Elasticsearch 是最流行的搜索引擎之一 为具有复杂搜索要求的应用程序 例如大型电子商务商店和分析应
  • 如何在 Debian 9 上安装 PostgreSQL

    PostgreSQL 通常简称为 Postgres 是一个开源通用对象关系数据库管理系统 PostgreSQL 拥有许多高级功能 例如在线备份 时间点恢复 嵌套事务 SQL 和 JSON 查询 多版本并发控制 MVCC 异步复制等 在本教程
  • 如何在Linux中创建用户(useradd命令)

    Linux 是一个多用户系统 这意味着多个人可以同时与同一个系统交互 作为系统管理员 您有责任通过创建和管理系统的用户和组来管理系统的用户和组 删除用户并将它们分配给不同的groups 在本文中 我们将讨论如何使用创建新用户帐户userad
  • python采集信息+Python预处理+tableau绘制可视化大屏

    制作完成的效果 注 这个图绘制的右上角违和感较高 所以各位小伙伴绘制时要注意不要使用这种大块的图形 绘制一些可以设置背景色为透明的哪一种 最后一张图的话设计的是1920 1080的大小 太大了 所以录制的时候并没有完全录制上 现在制作可视化
  • WXML:微信小程序版HTML

    完整微信小程序 Java后端 技术贴目录清单页面 必看 WXML WeiXin Markup Language 是框架设计的一套标签语言 结合基础组件 事件系统 可以构建出页面的结构 3 7 1 标签与属性 常用基础标签text view
  • 微前端:一种的前端架构新思路

    在当今的Web开发世界 前端架构正在经历一场革命 随着应用程序的规模和复杂性日益增长 一个新的概念被引入 那就是 微前端 微前端架构为前端开发提供了新的思路和解决方案 使得团队能够更有效地构建大型Web应用 那么 什么是微前端呢 简单来说
  • 什么是SD-WAN?图文详解五大技术点

    目录 什么是SD WAN 为什么需要SD WAN 1 更快 规划最优路线 降低网络延时 2 更稳 绕开拥塞线路 避免抖动丢包 SD WAN 为极速远程而生 庞大机房节点数量决定扎实基建 千万终端实时探测网络质量 智能路线优化 高速数据包转发
  • SQLite 基础

    SQLite 为什么使用SQLite SQLite 命令 SQLite 安装 SQLite 命令 SQLite 语法 SQLite 语句 SQLite 数据类型 SQLite 特殊运算符 SQLite SQL 高级用法 SQLite PRA
  • UPF_iso cell 替换

    目标 使用stdand cell替换isolation cell 在低功耗SOC中 难免会有signal从掉电阈传输到未掉电阈 两个阈的电压相同 这个时候就需要用ISO cell做隔离 但在一些大工艺的cell中 是没有这种cell的 在这
  • ReactNative中js与原生如何交互

    第一部分 在ReactNative中 原生与js交互常用的是原生通过向js发送事件 参考webview源代码 1 定义事件与发送消息方法 public class ReactExpandListViewEvent extends Event
  • flash player_Flash Player的两种开源替代品

    flash player 2017年7月 Adobe敲响了其Flash Media Player的丧钟 宣布它将在2020年终止对曾经无处不在的在线视频播放器的支持 但是 实际上 Flash在过去八年中一直处于下滑状态破坏其声誉的零时差攻击
  • Docker----Docker容器的启动流程

    详细内容见 DevOps技术社区文章 Docker Docker容器的启动流程
  • new Option("文本","值",true,true).后面两个true分别表示默认被选中和有效!

    var url city cityList json post url parentCityCode code function data var jsonObj JSON parse data areaId append
  • 线程优先级设置

    线程测试需要root用户 不然创建不成功 所以要用sudo su命令 Linux内核的三种调度策略 1 SCHED OTHER 分时调度策略 2 SCHED FIFO 实时调度策略 先到先服务 一旦占用cpu则一直运行 一直运行直到有更高优
  • ad导入candence 更改pin引脚长度

    前言 很多时候我们使用的封装 可能是不完整的 引脚长度也不一样 原理图连接的时候就发现连接不上线 明显没对齐grid 1 右键edit pin 更改第一个引脚为short 然后下拉那个小点完成对目标覆盖 2 确认之后 再重新选择为line

随机推荐