智能车浅谈——图像篇

2023-05-16

文章目录

  • 前言
  • 认识图像
    • 基本含义
    • 图像类型
    • 数字图像
      • 彩色图像
      • 灰度图像
      • 黑白图像
    • 小结
  • 图像处理
    • 图像压缩
    • 二值化
      • 固定阈值法
      • 大津法
    • 图像降噪(腐蚀)
    • 寻边线
  • 总结
  • 17届完赛代码
  • 17届完赛代码
  • 智能车系列文章汇总

前言

前面已经记录了智能车硬件系统、方向控制以及电机控制,今天接着记录一些有关智能车摄像头相关的内容,本文主要参考opencv、图像处理以及高等数学的部分知识。

认识图像

基本含义

首先,咱来了解一下图像的基本含义,图像是人类视觉的基础,是自然事物的客观反映,“图”是物体反射或透射光的分布,“像“是人的视觉系统所接受的图在人脑中所形成的印象或认识,照片、绘画、手写汉字、心电图等都是图像(来自百度百科对“图像”一词的解释)。

图像类型

广义上,图像就是所有具有视觉效果的画面,图像根据图像记录方式的不同可分为两大类:模拟图像数字图像。模拟图像可以通过某种物理量(如光、电等)的强弱变化来记录图像亮度信息,例如模拟电视图像;而数字图像则是用计算机存储的数据来记录图像上各点的亮度信息。1

数字图像

在智能车系统中,通过摄像头对赛道信息进行采集处理,将赛道转换成由像素组成的二维排列的数字图像。(一般采用120×188的分辨率)
例如智能车灰度摄像头采集的一帧图像如下图所示(120×188):
请添加图片描述
而在单片机内部图像的存储是一个二维数组Image_Data[120][188],数组中的每个数据对应图像中该点位置的亮度信息。(这里由于数据量太大,截图不清晰,仅截取了整幅图像的部分)在这里插入图片描述
为了更形象的凸显数字图像与二维数组的对应关系,这里放一张经过二值化处理后的图片:
在这里插入图片描述
这张图片可以清晰的看出原始图像的轮廓。
这里估计有人会有疑问,为什么智能车摄像头采集显示的是黑白图像与肉眼看见的彩色赛道不一致;为什么原始数组里面的数据也都是0-255的数值;为什么清晰显示轮廓的数组中数值只有0和1。要弄清楚这些问题,首先需要了解一下彩色图像、灰度图像和黑白图像。

彩色图像

彩色图像可以理解为一个像素点的亮度信息是由RGB三原色的不同配比表示;其中R、G、B三种颜色都被分为了0-255共256个色阶,每个像素点的信息通过三原色的色阶搭配进行表示,举例如下图该像素点的红色色阶是205,绿色色阶是89,蓝色色阶是68,所以这一个像素点的数据集合就是(205,89,68)也就是说彩色数字图像在计算机内部就是由这样一个个三原色组合而成的,做个简单的排列组合,如果要把所有的颜色用0、1、2这样的数字表示,则需要256×256×256个数,也就是一个24位的数,一个像素点就是一个24位的数据,可想一副图像的数据量会有多么庞大,显然一般单片机是没法完成这么大的数据处理的,这也就是为什么智能车中很少有人使用彩色摄像头的原因。
在这里插入图片描述

灰度图像

灰度图像,也就是我们智能车使用的灰度摄像头所采集那种图像,不是彩色画面,但也不是非黑即白,而是将黑色分成了0-255共256个色阶,整幅图像的每一个像素都是用0-255中间的一个数值来表示的就类似于上面提到的二维数组Image_Data[120][188]中的数据。对比彩色图像,可以发现一个像素点只需要一个8位数据表示即可,数据量相对彩色图像是不是大大减少了。
在这里插入图片描述

在这里插入图片描述

黑白图像

黑白图像就是整个图像中只有黑和白两种颜色,如下图所示:

在这里插入图片描述
而且图像的数据表示也只有0和1两个数,类似于上面提到的二值化后形式。
下图是彩色图像、灰度图像以及黑白图像的对比图。
在这里插入图片描述
这三者都是图像亮度的表示形式,彩色图像可以通过计算公式转换成灰度图像,灰度图像也可以通过二值化处理转换成黑白图像,有关灰度到黑白图像的转换下一节图像处理介绍。

彩色图像 灰度图像 黑白图像 -转换公式 - 二值化 彩色图像 灰度图像 黑白图像

小结

结合前面讲到的场中断行中断就好理解了,智能车摄像头是采集的是灰度图像,一帧图像有188×120个像素组成,每个像素是由0-255共256个色号来表达图像的亮度信息。关于图像认识就介绍到这里,智能车中使用到的一般都是灰度图像和黑白图像,有关灰度数据如何通过单片机传递给单片机,在之前的硬件篇也已经介绍智能车浅谈——硬件篇;
有关图像的详细介绍参考数字图像处理学习笔记(一)——数字图像处理概述、也可以参考百度百科关于图像的介绍。

图像处理

图像处理是信号处理在图像领域的应用,是信号处理的一个子类,与计算机科学、人工智能等领域有着密切的关系。智能车中使用到的图像均是数字图像,所以使用的理论知识也都是信号处理。数字图像处理过程中许多传统的一维信号处理方法和概念仍然适用,如降噪和量化。不同之处在于图像属于二维信号,与一维信号相比,它有其特殊的一面,处理方法和角度也不同。以下内容主要依托智能车的图像处理,有关数字图像处理的整体知识框架可以参考这篇博文——传送门。

图像压缩

上一节已经弄清楚了摄像头采集的图像是什么样子的,接下来就处理采集到的图像,前面提到过一帧图像是188×120个像素点,每个像素点是一个8位数据即0-255的数字,通过我们单片机的DMA搬运,可以得到一帧可供操作的图像Image_Data[120][188]。得到这样一个188×120的二维数组后,我们不难发现处理起来还是有些麻烦,数据量太大了,为了在保持图像特征的基础上减少数据量,我们需要进行图像压缩。
首先看一段视频
假设视频中小狗的初始图像为188×120。
在这里插入图片描述
经过第一次切割和组合,图片的宽度变成了原来的一半。
在这里插入图片描述

此时两张图片变成120×94。
在这里插入图片描述
经过第二次的切割和组合,图片分割成了四张,这四张的尺寸变成了60×94。
在这里插入图片描述
在这里插入图片描述
通过这个例子,不难发现,把一张高清图片按照横竖切割,重新奇偶排列后还可以得到与原图类似的图片,只是细节部分有所丢失,但是整体框架还是可以看出,智能车中的图像提取和图像压缩利用的就是此原理,只选取原数组中的奇数或者偶数行列进行重新组合得到一帧压缩的图像,图像压缩代码如下代码片

// An highlighted block
/*************************************************************************
 *  函数名称:void Get_Use_Image (void)
 *  功能说明:把摄像头采集到原始图像,缩放到赛道识别所需大小
 *  参数说明:无
 *  函数返回:无
 *  修改时间:2020年10月28日
 *  备    注:  IMAGEW为原始图像的宽度,神眼为188,OV7725为320
 *       IMAGEH为原始图像的高度,神眼为120,OV7725为240
 *************************************************************************/
void Get_Use_Image(void)
{
    short i = 0, j = 0, row = 0, line = 0;

    for (i = 0; i < IMAGEH; i += 2)          //神眼高 120 / 2  = 60,
    // for (i = 0; i < IMAGEH; i += 3)       //OV7725高 240 / 3  = 80,
    {
        for (j = 0; j <= IMAGEW; j += 2)     //神眼宽188 / 2  = 94,
        // for (j = 0; j <= IMAGEW; j += 3)  //OV7725宽320 / 3  = 106,
        {
            Image_Use[row][line] = Image_Data[i][j];
            line++;
        }
        line = 0;
        row++;
    }
}


处理前和经过图像压缩处理后得到的图像如下:
在这里插入图片描述

在这里插入图片描述

二值化

经过图像压缩后,我们为了进一步简化数据,会将灰度图像处理成黑白图像,这个转换过程使用的就是二值化;二值化见名知意就是将灰度图像中的0-255这些数据转换成0-1两个值,将原先的灰度图像转化为黑白两色图,那么怎样进行二值化呢,常见的方法有以下几种:

固定阈值法

这种方法比较好理解,即设置一个阈值Threshold(1-254),当像素数值大于这个阈值Threshold就置1,小于等于这个值就置0,这样就可以把原来的图像转化成黑白图像。

// An highlighted block
 /* 二值化 */
    for (i = 0; i < LCDH; i++)
    {
        for (j = 0; j < LCDW; j++)
        {
            if (Image_Use[i][j] > Threshold) //数值越大,显示的内容越多,较浅的图像也能显示出来
                Bin_Image[i][j] = 0;
            else
                Bin_Image[i][j] = 1;
        }
    }

以下同一图像不同阈值的二值化情况。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
此法中阈值数值越大,显示的内容越多,较浅的图像也能显示出来,由于是固定阈值对赛道环境要求很高,小车在运行过程中的图像是在不断变化的,显然靠设置唯一阈值的方法有较大的风险。

大津法

大津法是通过获取一帧图像的灰度分布直方图,并根据直方图的波峰和波谷计算出灰度的中间值,根据中间值进行二值化处理,实现一个动态的阈值调整。想要学习这个算法的参考B站工训大魔王的【智能车制作加餐:摄像头数字图像处理算法-哔哩哔哩】 具体代码如下

// An highlighted block
/*************************************************************************
 *  函数名称:short GetOSTU (unsigned char tmImage[LCDH][LCDW])
 *  功能说明:大津法求阈值大小
 *  参数说明:tmImage : 图像数据
 *  函数返回:无
 *  修改时间:2011年10月28日
 *  备    注:  GetOSTU(Image_Use);//大津法阈值
Ostu方法又名最大类间差方法,通过统计整个图像的直方图特性来实现全局阈值T的自动选取,其算法步骤为:
1) 先计算图像的直方图,即将图像所有的像素点按照0~255共256个bin,统计落在每个bin的像素点数量
2) 归一化直方图,也即将每个bin中像素点数量除以总的像素点
3) i表示分类的阈值,也即一个灰度级,从0开始迭代 1
4) 通过归一化的直方图,统计0~i 灰度级的像素(假设像素值在此范围的像素叫做前景像素) 所占整幅图像
        的比例w0,        并统计前景像素的平均灰度u0;统计i~255灰度级的像素(假设像素值在此范围的像素叫做背
        景像素)  * 所占整幅图像的比例w1,并统计背景像素的平均灰度u1;
5) 计算前景像素和背景像素的方差 g = w0*w1*(u0-u1) (u0-u1)
6) i++;转到4),直到i为256时结束迭代
7) 将最大g相应的i值作为图像的全局阈值
缺陷:OSTU算法在处理光照不均匀的图像的时候,效果会明显不好,因为利用的是全局像素信息。
*************************************************************************/
short GetOSTU (unsigned char tmImage[LCDH][LCDW])
{
    signed short i, j;
    unsigned long Amount = 0;
    unsigned long PixelBack = 0;
    unsigned long PixelshortegralBack = 0;
    unsigned long Pixelshortegral = 0;
    signed long PixelshortegralFore = 0;
    signed long PixelFore = 0;
    float OmegaBack, OmegaFore, MicroBack, MicroFore, SigmaB, Sigma; // 类间方差;
    signed short MinValue, MaxValue;
    signed short Threshold = 0;
    unsigned char HistoGram[256];              //

    for (j = 0; j < 256; j++)
        HistoGram[j] = 0; //初始化灰度直方图

    for (j = 0; j < LCDH; j++)
    {
        for (i = 0; i < LCDW; i++)
        {
            HistoGram[tmImage[j][i]]++; //统计灰度级中每个像素在整幅图像中的个数
        }
    }

    for (MinValue = 0; MinValue < 256 && HistoGram[MinValue] == 0; MinValue++);        //获取最小灰度的值
    for (MaxValue = 255; MaxValue > MinValue && HistoGram[MinValue] == 0; MaxValue--); //获取最大灰度的值

    if (MaxValue == MinValue)
        return MaxValue;         // 图像中只有一个颜色
    if (MinValue + 1 == MaxValue)
        return MinValue;        // 图像中只有二个颜色

    for (j = MinValue; j <= MaxValue; j++)
        Amount += HistoGram[j];        //  像素总数

    Pixelshortegral = 0;
    for (j = MinValue; j <= MaxValue; j++)
    {
        Pixelshortegral += HistoGram[j] * j;        //灰度值总数
    }
    SigmaB = -1;
    for (j = MinValue; j < MaxValue; j++)
    {
        PixelBack = PixelBack + HistoGram[j];     //前景像素点数
        PixelFore = Amount - PixelBack;           //背景像素点数
        OmegaBack = (float) PixelBack / Amount;   //前景像素百分比
        OmegaFore = (float) PixelFore / Amount;   //背景像素百分比
        PixelshortegralBack += HistoGram[j] * j;  //前景灰度值
        PixelshortegralFore = Pixelshortegral - PixelshortegralBack;  //背景灰度值
        MicroBack = (float) PixelshortegralBack / PixelBack;   //前景灰度百分比
        MicroFore = (float) PixelshortegralFore / PixelFore;   //背景灰度百分比
        Sigma = OmegaBack * OmegaFore * (MicroBack - MicroFore) * (MicroBack - MicroFore);   //计算类间方差
        if (Sigma > SigmaB)                    //遍历最大的类间方差g //找出最大类间方差以及对应的阈值
        {
            SigmaB = Sigma;
            Threshold = j;
        }
    }
    return Threshold;                        //返回最佳阈值;
}

实际效果如下:
在这里插入图片描述

图像降噪(腐蚀)

经过二值化处理后,整个数组内可能会出现一下孤立的小白点,如下图所示,这会对我们后面的巡线造成干扰,所以需要进行降噪处理,思路就是,判断一个像素点周围的数值是否与其一致,如果 周围像素点的数据都与此点不一致则此点将被修改为与周围相同的数值。
在这里插入图片描述
降噪代码如下:

// An highlighted block
/*---------------------------------------------------------------
 【函    数】Bin_Image_Filter
 【功    能】过滤噪点
 【参    数】无
 【返 回 值】无
 【注意事项】
 ----------------------------------------------------------------*/
void Bin_Image_Filter (void)
{
    sint16 nr; //行
    sint16 nc; //列

    for (nr = 1; nr < LCDH - 1; nr++)
    {
        for (nc = 1; nc < LCDW - 1; nc = nc + 1)
        {
            if ((Bin_Image[nr][nc] == 0)
                    && (Bin_Image[nr - 1][nc] + Bin_Image[nr + 1][nc] + Bin_Image[nr][nc + 1] + Bin_Image[nr][nc - 1] > 2))
            {
                Bin_Image[nr][nc] = 1;
            }
            else if ((Bin_Image[nr][nc] == 1)
                    && (Bin_Image[nr - 1][nc] + Bin_Image[nr + 1][nc] + Bin_Image[nr][nc + 1] + Bin_Image[nr][nc - 1] < 2))
            {
                Bin_Image[nr][nc] = 0;
            }
        }
    }
}

处理前与处理后对比图如下所示:
在这里插入图片描述
在这里插入图片描述

寻边线

经过上述一系列的简化处理后,我们就可以正式开始赛道的识别处理了,首先需要根据图像建立一个坐标系,根据坐标系内的图像需要提取出赛道信息,由图像可以很直观地想到一种寻找边线的办法,就是寻找每行的黑白跳变点,也就是0-1跳变的位置,根据这个跳变点我们可以找到整个赛道的左右边线(理想情况下),然后根据两个边线的横坐标就可以得到中线位置,
中线=(左边线横坐标+右边线横坐标)/2
再根据中线位置与理论中值进行比较就可以得到偏差,进而控制舵机打角,利用拟合、补线、求斜率、曲率(此处可以用高数求曲率的思路)等方式来获取偏差进行计算实现控制,还有特殊元素的识别判断处理这都是需要自己去编写代码的,网上有很多类似的文章介绍,笔者推荐一篇来自博主温水很好喝的第十六届全国大学生智能汽车比赛—摄像头算法控制总结。
在这里插入图片描述
以下是赛道经过大津法二值化,以及巡线补线后的效果。(下面的效果采用的是乾勤科技的开源代码,平台是VS2019)
在这里插入图片描述
在这里插入图片描述
最后放一些笔者自己用RT1064按上述处理后的效果:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
程序大体框架如下

// An highlighted block
int main(void)
{
	All_Init();
	while(1)
	{
		// IWDG_Feed();  				//如果WK_UP按下,则喂狗
        All_Module_ProDeal();           //所有的动作
        Usart1_Command_Handle();		//串口控制
		Display();						//显示函数
		Key_Action();					//按键操作函数 
		//保证每次while循环的时间固定,消除程序运行时的时序混乱 		
        while(1)
        {
            if(delay_10ms_Arrive)		//定时10ms到达标志位
            {
                delay_10ms_Arrive = 0;	//定时10ms到达标志位
                break;
            }
        }
	}	
}

/*******************************************************************************
* 函数名		:All_Module_ProDeal
* 描述			:各动作模块过程处理
* 参数			:无
* 返回			:无
* 编写者		:
* 编写日期	:2022-01-12
*******************************************************************************/
void All_Module_ProDeal(void)
{
	static u16 time_out=0;
	if(mt9v03x_finish_flag)
    {
		Get_Use_Image();     // 取出赛道及显示所需图像数据
		Get_Bin_Image(3);    // 转换为01格式数据,0、1原图;2、3边沿提取
		Bin_Image_Filter();  // 滤波,三面被围的数据将被修改为同一数值
		for(int i=0;i<60;i++)
		{
			bord_L[i]=0;
			bord_R[i]=0;
		}
		FindBorder_L(59,20,45,0,WHITE_MY);//寻左边线
		FindBorder_R(59,20,45,80,WHITE_MY);//寻右边线
		CalculateCentralLine(60,20,WHITE_MY);//计算出中线位置
		OLED_Road(LCDH,LCDW,(unsigned char *) Bin_Image);//显示到OLED上
		mt9v03x_finish_flag = 0;
	}
	if(BasicArea.XIn[STOP] == 0) //停车标志位没到
	{
		if(run_bit == 0)   //小车动作
		{
		  switch(step)       //根据状态执行动作
		  {
			case Go_Stragist:		// 直行
			{
				//直行的操作
				//例如修改PID参数
				BasicArea.Kp=0;
				BasicArea.Ki=1;
				
				//直行状态的时间,如果超时则退出此状态
				if(TIMER_1ms(time_out,3500))
				{
					user_printf(USART1,"Cylinder=1,front,NG\r\n");
					step=WAIT;
				}
				// 跳出直行的判断条件
				if(BasicArea.XIn[SENSOR_DOWN]==0)
				{
					user_printf(USART1,"Cylinder=1,front,OK\r\n");
					step=WAIT;//进入下一状态
				}
				break;
			}
			case Round_About:		// 环岛
			{
				//执行的动作
				BasicArea.YOut[CY_UP]=1;
				BasicArea.YOut[CY_DOWN]=0;
				//环岛状态的时间,如果超时则退出此状态
				if(TIMER_1ms(time_out,3500))
				{
					user_printf(USART1,"Cylinder=1,back,NG\r\n");
					step=WAIT;
				}
				// 跳出环岛标志
				if(BasicArea.XIn[SENSOR_UP]==0)
				{
					user_printf(USART1,"Cylinder=1,back,OK\r\n");
					step=WAIT;
				}
				break;
			}
			case Cross_Shaped:	//十字
			{
				//执行的动作
				BasicArea.YOut[CY_L_UP]=1;
				//环岛状态的时间,如果超时则退出此状态
				if(TIMER_1ms(time_out,3500))
				{
					user_printf(USART1,"Cylinder=2,front,NG\r\n");
					step=WAIT;
				}
				// 跳出十字的标志
				if(BasicArea.XIn[L_UP_SENSOR_FRONT]==0)
				{
					user_printf(USART1,"Cylinder=2,front,OK\r\n");
					step=WAIT;
				}
				break;
			}
			case Access_Road:		//三岔路
			{
				//执行的动作
				BasicArea.YOut[CY_L_UP]=0;
				//三岔状态的时间,如果超时则退出此状态
				if(TIMER_1ms(time_out,3500))
				{
					user_printf(USART1,"Cylinder=2,back,NG\r\n");
					step=WAIT;
				}
				// 退出三岔状态的标志
				if(BasicArea.XIn[L_UP_SENSOR_BACK]==0)
				{
					user_printf(USART1,"Cylinder=2,back,OK\r\n");
					step=WAIT;
				}
				break;
			}
			case :	// 其他元素处理大弯,小弯,坡道
			{
				//对应元素的操作
				//进入元素后的时间
				if(TIMER_1ms(time_out,3500))
				{
					step=WAIT;
				}
				//跳出次状态的判断
				if(BasicArea.XIn[L_DOWN_SENSOR_FRONT]==0)
				{
					step=WAIT;
				}
				break;
			}
	
			case WAIT:	
			{
				//判断标志位
				if(直道条件满足 && step == WAIT)
				{
					//清除直道标志位
					step=Go_Stragist;//进入直道状态机
				}
				else if(环岛条件满足 && step == WAIT)
				{
					//清除直道标志位
					step=Round_About;//进入环岛状态机
				}
				else if(...)
				{
					...
				}
				else
				{
				//正常寻迹,不做特殊处理
				}
				time_out=_TIMER_1MS;
				break;
			}
			default:
				break;
		  }
		}
	}
	else	//停车标志位到了
	{
		if(run_bit==0)
		{
			run_bit=1;
		}
		//停车操作
	}
	
}

总结

智能车比赛已经举办了十六届,网上各式各样的资料很多,大家自己平时多留意,在此祝愿各位参赛者都能取得好成绩。有关图像处理的一些高级算法,例如卷积、soble边沿检测算子、八领域、四邻域这些方法都是很厉害也是很好用的,但是笔者能力有限,这方面就不再做分析了,而且不一定非要按照笔者上述流程去操作处理图像,之前逐飞科技就出过一个用灰度识别处理赛道的方法,效果也是很好,这里大家可以参考,链接奉上
欢迎大佬来沟通交流。
如果文章对你的比赛有所帮助,而且赛后对此文还有映像,笔者在评论区等待你们的分享。

17届完赛代码

17届完赛代码

最近有不少问问题的同学,笔者这边可能回复不是很及时,有需要17届完赛方案和代码的同学可以去海鲜市场,笔者学弟在出售17届方案,价格比较实惠。
【全国大学生第十七届智能车电磁租组2m+代码(全元素+模糊控制】
【第十七届全国大学生智能车竞赛摄像头四轮软硬件资料】
【17届户外极速越野软硬件资料】

智能车系列文章汇总

智能车浅谈——硬件篇
智能车浅谈——方向控制篇
智能车浅谈——电机控制篇
智能车浅谈——图像篇
智能车浅谈——控制规律篇
智能车浅谈——过程通道篇
智能车浅谈——抗干扰技术硬件篇
智能车浅谈——抗干扰技术软件篇
智能车浅谈——手把手让车跑起来(电磁篇)
智能车浅谈 电磁组——环岛处理
第十七届智能车越野硬件篇——无刷电机驱动
无刷驱动设计——浅谈MOS驱动电路
芯源&立创EDA训练营——无刷电机驱动


  1. 张弘.数字图像处理与分析.北京:机械工业出版社.2008 . ↩︎

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

智能车浅谈——图像篇 的相关文章

  • HDU-2121(朱刘算法优化版+虚根处理无根树形图)

    hdu2121 span class token macro property span class token directive keyword include span span class token string lt bits
  • #10091. 「一本通 3.5 例 1」受欢迎的牛(强联通+度数结论)

    libreoj10091 题解 xff1a 首先简化一下 xff0c 若欢迎关系图是一个DAG 有向无环图 xff0c 则只要统计每个点的出度 xff0c 出度为0的点即为受所有牛欢迎的点且只有唯一一个 xff0c 因为若存在两个以上出度为
  • https://ac.nowcoder.com/acm/problem/13947(牛客网)

    Contest xff1a 链接 xff1a https ac nowcoder com acm problem 13947 来源 xff1a 牛客网 n支队伍一共参加了三场比赛 一支队伍x认为自己比另一支队伍y强当且仅当x在至少一场比赛中
  • https://ac.nowcoder.com/acm/problem/14136(牛客网 监视任务)

    题目链接 xff1a 题解 xff1a 本题我们不能一上来就用树状数组来统计每一位的贡献 xff0c 我们需要先对区间进行一个排序 xff0c 先按照区间的右端点由小到大排序 xff0c 再按照区间的左端点从小到大排序 xff0c 再按照区
  • https://ac.nowcoder.com/acm/problem/14269(Sum 牛客网)

    位运算 43 组合数学 43 树状数组 xff1a 题解 xff1a 我们如果直接计算操作2的话会很困难 xff0c 我们可以直接考虑一个数的二进制位对答案做出的贡献 xff0c 显然二进制位为0时就不会有任何贡献 xff0c 当我们知道所
  • 异或的路径(牛客网)

    异或路径 43 位运算 43 考虑贡献 题解 xff1a 我们要求所有点对构成的所有路径的异或权值总和 xff0c 肯定不能暴力 xff0c 我们可以知道 xff0c 先搞一个数组d i 表示i节点到根节点1的路径上边权异或和 xff0c
  • TCP为什么采用三次握手而不是两次握手

    希仁版 计算机网络 中的例子是这样的 xff0c 已失效的连接请求报文段 的产生在这样一种情况下 xff1a client发出的第一个连接请求报文段并没有丢失 xff0c 而是在某个网络结点长时间的滞留了 xff0c 以致延误到连接释放以后
  • linux生成可执行文件的命令

    linux生成带调试的可执行文件命令行 xff1a g span class token operator 43 43 span span class token operator span g main span class token
  • git rebase origin/develop

    1 进行git rebase origin develop之前需要进行 lt git add gt lt git commit gt 操作先将修改给提交到暂存区 2 执行git rebase origin develop时候有冲突的话需要自
  • linux---文件描述符和重定向

    文件描述符 进程就是通过struct file结构体来描述打开的文件 xff0c 使用struct file fd array 来存储我们的文件 那什么是文件描述符呢 xff1f 什么是文件描述符 xff1a 文件描述符就是struct f
  • git合并多个commit

    git合并多个commit
  • 深度学习资料

    深度学习资料
  • cmake总结

    cmake 用CMakeList txt产生makefile xff0c make 使用makefile编译可执行程序
  • kuangbin连通图专题,Network of Schools (POJ - 1236,tarjan算法求缩点+缩点的入度与出度的运用)

    kuangbin 题意是有N个学校 每个学校可能向几个学校进行数据传输 xff08 单向的 xff09 问 至少需要把一个文件给几个学校可以使给的N个学校都收到文件 再问在加几个通信线路可以使各个学校之间都能直接或间接的传递文件 一个强连通
  • endnote 文献保留前三个作者

    1 问题描述 xff1a endnote使用GBT7714文献格式 xff0c 显示文献的全部作者 2 想要达到的效果 xff1a 最多显示三个作者 3 解决方法 xff1a 还不知道怎么弄 xff0c 看看以后再来补充 xff0c 心情烦
  • RTK_LIB 源码、可执行文件、rtkget、观测文件、星历文件(精密星历、广播星历)、精密钟差文件介绍

    1 RTK LIB开源程序下载 xff1a 点开rtklib链接 xff1a 下载最新版本的可执行文件和程序源码 2 GNSS数据处理需要的文件 2 1 伪距定位 xff1a spp 观测数据 xff08 0 xff09 导航星历 广播星历
  • RTKLIB ppp rtk_post

    1 实时ppp xff1a IGS MGEX数据处理中心的播发的实时轨道钟差产品 xff0c 结合广播星历 xff0c 实现实时定位 2 事后的 xff08 近似实时 xff09 xff1a 下载精密星历 钟差产品 xff0c 结合其他的精
  • 4.使用静态库、动态库,常见问题解决

    使用动态库的流程 xff08 ORBSLAM3实例 xff09 xff1a 在调用动态库或静态库的 cpp文件添加库的头文件 xff08 可以包含路径 xff0c 也可以在cmakelist文件加头文件搜索路径 xff09 span cla
  • vscode查看代码更新历史

    开源代码推出新版本后 xff0c 如何查看代码更改信息 1 首先打开vscode xff0c 点击左侧的插件管理器 xff0c 进入插件面板 xff0c 搜索Git Graph并安装 2 点击下图图标 xff0c 即可进入Git Graph
  • git更新代码

    一 git一般有很多分支 xff0c 我们clone到本地的时候一般都是master分支 xff0c 那么如何切换到其他分支呢 xff1f 主要命令如下 xff1a 1 查看本地分支文件信息 xff0c 确保更新时不产生冲突 span cl

随机推荐

  • linux---硬链接和软链接

    文件系统 磁盘上文件读写存储与查找系统 xff08 管理 xff09 就是文件系统 xff0c 在每一个分区都会存在自己的文件系统 在这里我们有swap交换分区和文件分区 xff0c 我们这里只介绍文件分区 在文件分区都会有上图中的分块管理
  • char类型数组

    字符数组 xff08 一维 二维 xff09 字符数组是数组元素为char类型的一种数组 凡是适合数组的定义和赋值 xff0c 也都适合于字符数组 由于C语言没有提供字符串类型 xff0c 字符串一般用一维字符数组来存放 xff0c 而二维
  • ubuntu18.04 安装腾讯会议

    腾讯会议现在以及上线了Linux版本 xff0c 可以直接在腾讯会议官网下载linux 版本 xff0c 在官网点击免费下载 xff0c 可以直接下载Linux版本 腾讯会议下载链接 选择Linux版本 xff0c x86 64版本 xff
  • 2.树莓派系统备份

    树莓派使用SD卡来装载系统 xff0c 如果SD卡丢失或者损坏 xff0c 那么树莓派上的数据都会丢失 xff0c 所以一定要备份好SD卡 这篇文章可以帮你备份你的树莓派系统 主要内容为备份SD卡 xff0c 制作树莓派系统镜像以及在需要的
  • ic_gvins编译及环境配置问题解决

    RTK VIO松组合 对惯导精度要求较高 1 环境配置和编译 安装依赖项 span class token comment gcc 8 span span class token function sudo span span class
  • EVO画图设置

    一 绘图设置 1 更改背景色和网格 span class token comment 白色网格 span evo config span class token builtin class name set span plot seabor
  • GINS_OB环境配置

    1 程序简介 武大开源GNSS INS松组合IMU预积分有考虑地球自传和不考虑两种形式可以灵活设置GNSS中断时间IMU可以和里程计进行融合 2 环境配置 span class token comment gcc 8 g 43 43 8 s
  • OB_GINS程序框架

    1 程序运行 span class token builtin class name cd span OB GINS span class token comment 编译好的可执行文件 xff1a bin ob gins xff0c 参数
  • KEIL、MDK中关于__LINE__宏 printf 的显示不正确的问题

    span class token operator gt span define span class token function DEBUG span span class token punctuation span log span
  • VINS-回环检测与重定位

    参考博客 pose graph分析1 pose graph分析2 pose graph分析3
  • 源码安装naviagtion,但是出现[move_base-2] process has died 运行错误的解决办法

    今天开始记录ros遇到的问题 安装navigation可以使用两种方法 第一种 xff1a sudo apt get install ros kinetic navigation 这种安装方法最简单 xff0c 新手或者不需要动naviag
  • linux---静态库和动态库的制作和使用

    静态链接和动态链接 静态链接 xff1a 生成可执行代码 xff0c 链接静态库 xff08 与代码位置有关的链接方式 xff09 xff0c 需要将代码拷贝到我们的源代码中才能运行 动态链接 xff1a 生成可执行代码 xff0c 链接动
  • 加一

    加一 描述 给定一个由整数组成的非空数组所表示的非负整数 xff0c 在该数的基础上加一 最高位数字存放在数组的首位 xff0c 数组中每个元素只存储单个数字 你可以假设除了整数 0 之外 xff0c 这个整数不会以零开头 示例 1 输入
  • STM32bootloader原理解释

    STM32bootloader原理解释 一 STM32的常规启动流程 STM32的内部flash地址起始于0x8000000 xff0c 一般情况下 xff0c 程序文件就从此地址开始写入 此外STM32是基于Cortex M3内核的微控制
  • 模糊PID基本原理及matlab仿真实现(新手!新手!新手!)

    有关模糊pid的相关知识就把自己从刚接触到仿真出结果看到的大部分资料总结一下 xff0c 以及一些自己的ps 以下未说明的都为转载内容 1 转自 https blog csdn net weixin 36340979 article det
  • VMware+ubuntu+win10笔记本实现笔记本连接WIFI且ubuntu既可以上网又能连接开发板

    背景 最近在学习imx6ull开发板的时候 xff0c 发现开发板通过网线连接笔记本电脑却无法ping通ubuntu xff0c 于是捣鼓了很久终于可以了 xff0c 却又发现ubuntu不能上网了 xff0c 经过一番查找资料和尝试 xf
  • 在windows上用vscode打造比vc++6.0好用的C/C++ IDE,适用编程小白

    准备 xff1a 1 安装MinGW xff0c 添加gcc gdb等编译调试工具bin目录 头文件Include目录 库lib的路径到系统环境变量 xff0c 安装LLVM 添 加Clang编译器所在bin目录到系统环境变量 具体操作百度
  • C语言数据结构——线性表的链式存储结构

    文章目录 线性表的链式存储结构1 基本概念2 设计与实现3 优点和缺点 线性表的链式存储结构 1 基本概念 链式存储定义 xff1a 为了表示每个数据元素与其直接后继元素之间的逻辑关系 xff0c 每个元素除了存储本身的信息之外 xff0c
  • 智能车浅谈——硬件篇

    目录 初识小车硬件系统1 电源系统线性电源开关电源 2 人机交互系统3 MCU最小系统4 传感器系统摄像头电感编码器 5 驱动系统 机械结构 17届完赛代码智能车系列文章汇总 前言 xff1a 作为一名老三本玩家 xff0c 笔者深知一些同
  • 智能车浅谈——图像篇

    文章目录 前言认识图像基本含义图像类型数字图像彩色图像灰度图像黑白图像 小结 图像处理图像压缩二值化固定阈值法大津法 图像降噪 xff08 腐蚀 xff09 寻边线 总结17届完赛代码17届完赛代码智能车系列文章汇总 前言 前面已经记录了智