当前位置: 首页 > news >正文

智能车摄像头开源—9 动态权、模糊PID、速度决策、路径优化

目录

 

一、前言

二、动态权

1.概述

2.偏差值加动态权

三、模糊PID

四、速度决策

 1.曲率计算

2.速度拟合

3.速度控制

五、路径

六、国赛视频


 

一、前言

        在前中期通过识别直道、弯道等元素可进行加减速操作实现速度的控制,可进一步缩减一圈的运行速度,但会出现刹车不及时、加速不及时、车路径较差等问题,反而导致处理后运行一圈时间反而多于之前。由此我们引入多个方法,使车辆能自行解算自行控制速度、转向力度等,以跑出最好效果。

        当然有部分代码为了提高摩托运行稳定性的,对于四轮车模反而是一种限制,需读者使用时自行删减。

 

二、动态权

1.概述

       如果图像有六十行,那我们可以得到六十行有效偏差值(弯道时有效行数会减少),我们在计算偏差值时,常用的方法是将所有有效行的偏差值相加,再取均值。

        但调过车的都知道,当所有行取均值时,会导致弯道打角力度不够,直线打角力度过大导致车辆摆动的情况。

        参考我们平时开车或者骑车时的情形,车快时我们眼睛就看的远,车速慢时我们眼睛就看的近。由此我们可以优化上述情况,引入加权。

        最简单且繁琐的方法是引入固定权值(提前录入权值数组),即提前设定好不同元素的加权情况:

        1. 如识别出直线时,我们将图像远端的权值加大,而图像中端和近端的权值减小,在远端出现弯道时,会让车辆高速情况下提前出现打角倾向。

        2. 当贴近弯道时,我们将图像中端的权值加大,就可以减少底端较小偏差值的干扰,保证车的打角力度。

        3. 识别出小S弯时,我们将权值情况如同直线处理,高权值放在远端,那么就可以惊奇的发现,车在跑小S弯时几乎无左右摆动情况,直线路径穿过小S弯,在提高车速的同时也减少了打角消耗的时间。

        4. 在处理十字和环岛时,我们可以对多个状态设定权值,可以优化在元素不同状态时的转向效果。

        但上述方法存在弊端:一方面元素与元素之间权值会切换的很生硬,使车辆出现摆动;另一方面当我们需要加速时,所有的元素权值都需要调整,十分繁琐。由此我们引入动态权。

2.给偏差值加动态权

       控制动态权我们使用一个数据,为爬线相遇点的Y值(一个可以反映偏差值有效行的数据),那么在直线和小S弯等地方,Y贴近图像最远端,那么高权值就自动滑动到最远端。在由直线靠近弯道时,相遇点Y值不断下压,那么高权值就随着Y值向下滑动到中间区域,整个过程很丝滑,不会出现强硬的切换,并且十分易于修改。下面上代码:

            //相遇点滑动均值滤波uint8 Y_Meet_Max = 0;uint8 Y_Meet_Min = 0;uint16 Y_Meet_Sum = 0;uint8 Y_Meet_Average = 0;Y_Meet_Min = Y_Meet_Array[0];Y_Meet_Max = Y_Meet_Array[0];for(i = 0; i < 19; i++){Y_Meet_Array[i + 1] = Y_Meet_Array[i];}Y_Meet_Array[0] = start;for(i = 0; i < 20; i++){if(Y_Meet_Array[i] < Y_Meet_Min){Y_Meet_Min = Y_Meet_Array[i];}if(Y_Meet_Array[i] > Y_Meet_Max){Y_Meet_Max = Y_Meet_Array[i];}}for(i = 0; i < 20; i++){Y_Meet_Sum += (uint16)Y_Meet_Array[i];}Y_Meet_Sum = Y_Meet_Sum - (uint16)Y_Meet_Min - (uint16)Y_Meet_Max;Y_Meet_Average = (uint8)(Y_Meet_Sum / 18);if(Y_Meet_Average > 20){Y_Meet_Average = 20;}else if(Y_Meet_Average < 2){Y_Meet_Average = 2;}   //一直到这里都是华东均值滤波,滤的比较狠,防止数据跳动//        float Temp_Float = (0.9f * (float)(Y_Meet_Average - 2) + 1.8f * Sensitivity);float Temp_Float = (float)(Y_Meet_Average - 2);     //当直线时,我的Y_Meet值是2if(Temp_Float > 18.0f)      //限幅{Temp_Float = 18.0f;}if(Temp_Float < 0){Temp_Float = 0;}//固定权值uint8 Base_Weight[60] = {0, 0, 2, 0, 2, 0, 2, 0, 2, 0,      2, 0, 2, 0, 2, 0, 2, 0, 2, 0,      2, 0, 2, 0, 2, 0, 2, 0, 2, 0,2, 0, 2, 0, 2, 0, 2, 0, 2, 0,      2, 0, 2, 0, 2, 0, 2, 0, 2, 0,      2, 0, 2, 0, 2, 0, 2, 1, 0, 0};//局部动态权值,可根据车速自行调整区间和数值uint8 Trends_Weight[19] = {4, 5, 5, 6, 6, 7, 7, 8, 9, 10, 9, 8, 7, 7, 6, 6, 5, 5, 4};//动态权重加权起始行只能落在24 - 40行之间(由下往上),相遇点最大值为20(减2为Temp_Float最大值为18)uint8 Start_Line = (uint8)(Temp_Float / 18.0f * 16.0f + 24.0f);//将动态权值赋值给固定权值数组for(i = 0; i < 19; i ++){Base_Weight[Start_Line - i] = Trends_Weight[i];}for(i = Y_Meet_Average + 1; i < 57; i++)    //加权,可直接加权给中线之后再算偏差值,也可直接加权给每行偏差值{Sum += (int32)Base_Weight[i] * (int32)C_Line[i];Weight_Sum += (int32)Base_Weight[i];}

        识别出元素时也可适当给固定权保证车辆行驶的稳定性。

 

三、模糊PID

        模糊PID,是对P, I, D其中一个变量或几个变量进行模糊,每模糊一个参数,就需要写一个对应的模糊表。当模糊多个参数时,调参工作量会增大。因此我们组别只模糊了专项环的P值

        模糊PID重要的是原理,先上一张图:

(自己手绘潦草勿喷)

       模糊的原理即查表映射,上述图中,a为偏差值;b网络上多为偏差值的差值,即偏差值的变化率(反映偏差值的变化情况,如直线时变化率很小,进入弯道时变化率会很大);c就是对应的Kp值了。举例理解:我的偏差值区间为 -1 到 1 (其中负数向左转,正数向右转),那么我将(-1,1)等分为六个区间(七个值),作为模糊表的横向变量。列向变量也是同理。

        那我们映射的时候,是获得一个偏差值,再获得一个偏差值的差值,然后在表中一查找直接对应到一个值作为转向环的P吗?

        是也不是。我们会发现我们很少会获得一个a参数和b参数,其中a参数和 a1~a7中的某一个值正好对应,b参数也正好和b1~b7中的某一个值正好对应,绝大多数情况下两者是都无法正好相对的,那么我们就需使用占比对应的方式。

        正如上述图中的式子,假设我们获得一个偏差a位于区间a5~a6,偏差的差值b位于区间b3~b4,那我们可列出如下式子:

Kp =  [(b - b3)/  (b4 - b3)] * [(a - a5) / (a6 - a5)] * c1 +

         [(b - b3)/  (b4 - b3)] * [(a6 - a) / (a6 - a5)] * c2 +

         [(b4 - b)/  (b4 - b3)] * [(a - a5) / (a6 - a5)] * c3 +

         [(b4 - b)/  (b4 - b3)] * [(a6 - a) / (a6 - a5)] * c4

        不要觉得式子很长很繁琐,就是初中的知识,一眼就能看懂。理解了上述原理后,就懂了模糊PID查表的核心思路。然后阅读这篇文章(其中隶属度通俗点讲就是所占的比例,代码可看可不看):

【智能车】模糊PID控制原理详解与代码实现

        然后我们再细说下c,即如何在表格中填入Kp的值:

        1. 首先我们让车以一个恒定速度行进,测出弯道转向时需要的Kp值,即PB(尽量使路径最好);

        2. 然后给一个较快的速度,测出直线时车身比较平稳的Kp值,即NB(尽量使路径最好);

        3. 然后将最大~最小值均分为七个值,对应上述文章里的NB ~ PB;

        4. 最后将Kp的七个值,按照既定的规则表填入表格内(注意不要自己乱填,当自己能力足够时可自行优化规则表)

        到这一步,相必已经完全了解模糊PID的原理和使用方法了,那么接下来就是代码的实现了。这里代码与上述方法相似但不相同,但核心原理与方法是完全一致的,只是实现的方式进行了改进。此处我的b使用的并不是偏差值的差值,而是爬线相遇点的Y值:

//除了注释了(需要改)的地方,其他地方直接照抄就可以float Dif_Effictive_Line = 40.0f;    //偏差值最大值
int16 View_Effictive_Line = 20;      //相遇点Y坐标的最大值(需要改)float P_Value_L[7] = {11.0f, 12.0f, 12.5f, 13.0f, 14.5f, 15.5f, 17.2f};    //这里没有等分,自己调一调车可摸出规律(需要改)float Vague_Array[4][4] = { {0, 1, 2, 3},{1, 2, 3, 4},{3, 4, 5, 6},{5, 6, 6, 6}};    //这个表原样抄下来float f_Get_H_approximation(int16 i16_ViewH)     
{float H_approximation;if (i16_ViewH < 0){i16_ViewH = 0;}H_approximation = ((float)i16_ViewH * 3.0f / (float)View_Effictive_Line);    //*3.0是为了将结果放大三倍return H_approximation;
}float f_Get_E_approximation(float i16_E)
{float E_approximation;if (i16_E < 0){i16_E = -i16_E;}E_approximation = (float)i16_E * 40.0f * 3.0f/ Dif_Effictive_Line;    //*3.0与上述同理,还多乘了个四十,与Dif_Effictive_Line 的值对应上return E_approximation;
}int16 Off_Line = 0;
int16 Now_Off_Line = 0;    //用于滤波
int16 Last_Off_Line = 0;    //用于滤波第一个参数输入相遇点Y坐标
第二个参数输入归一化后的偏差值
float Get_P(int16 off_line, float dif_value)
{Last_Off_Line = Now_Off_Line;Now_Off_Line = off_line;if(((Now_Off_Line - Last_Off_Line) >= 10) || ((Now_Off_Line - Last_Off_Line) <= -10) || Now_Off_Line >= 45)    //需要改{Off_Line = Last_Off_Line;}else{Off_Line = (int16)(0.3f * (float)(Now_Off_Line) + 0.7f * (float)(Last_Off_Line));}//下面这部分代入几个数据,结合整段代码计算几遍即可理解,只看的话比较吃力float VH = f_Get_H_approximation(off_line - 2);float VE = f_Get_E_approximation(dif_value);float X2Y = 0;float X1Y = 0;float Y2X = 0;float Y1X = 0;int8 VH1 = (int)VH;if (VH1 > VH){VH--;}int8 VH2 = VH1 + 1;int8 VE1 = (int8)VE;if (VE1 > VE){VE1--;}int8 VE2 = VE1 + 1;if (VH1 > 3){VH1 = 3;}if (VH2 > 3){VH2 = 3;}if (VE1 > 3){VE1 = 3;}if (VE2 > 3){VE2 = 3;}X2Y = (Vague_Array[VH1][VE2] - Vague_Array[VH1][VE1]) * (VE - VE1) + Vague_Array[VH1][VE1];X1Y = (Vague_Array[VH2][VE2] - Vague_Array[VH2][VE1]) * (VE - VE1) + Vague_Array[VH2][VE1];Y2X = (Vague_Array[VH2][VE1] - Vague_Array[VH1][VE1]) * (VH - VH1) + Vague_Array[VH1][VE1];Y1X = (Vague_Array[VH2][VE2] - Vague_Array[VH1][VE2]) * (VH - VH1) + Vague_Array[VH1][VE2];float P_approximation = (X2Y + X1Y + Y2X + Y1X) / 4.0;int8 P1 = (int8)P_approximation;if (P1 > P_approximation){P1--;}int8 P2 = P1 + 1;return (P_Value_L[P2] - P_Value_L[P1]) * (P_approximation - P1) + P_Value_L[P1]; //返回p值
}

 

四、速度决策

 1.曲率计算

        速度要变化起来,就需要有参考,即根据一个或两个实时求出的数据去控制权值的滑动。那么第一个变量我使用的是中线曲率:

        曲率我尝试了很多算法,都不是很稳定,由此大道至简,直接将中线每两行之间的X坐标的差值相加求均值再归一化(归一化所除的系数在弯道处实测得出),就可以反映中线的弯曲程度,也近似可看为曲率。

        注:这里每两行之间X坐标的差值不可取绝对值后再相加。在弯道时,中线是向一侧弯的,那么X坐标的差值的符号都是一样的,可以得到一个比较大的均值。而在小S弯处,X坐标的差值有正有负,那么相加之后求均值会获得一个比较小的值,这是将小S弯当直线处理的关键所在。

        这里我的代码参照动态权的思想给曲率的计算也加了个小型化的动态权,即突出重点区域,使用爬线相遇点Y坐标(即图像最大有效行)控制权值移动,可自行修改这部分,根据实际情况选用是否需要这部分处理。

        下面上代码:

float Last_Curvature_Value = 0;     //防止
/**
* 函数功能:      计算中线曲率,作为控制速度的一个数据
* 特殊说明:      无
* 形  参:        uint8 *line      中线
*                 uint8 start      爬线相遇点的Y值,或最大有效行
*                 uint8 end        爬线起始行(或图像最底端)
*
* 示例:          Calculate_Curvature_2(C_Line, Y_Meet, L_Start_Point[1]);
* 返回值:        Last_Curvature_Value      所得曲率
*/
float Calculate_Curvature_2(uint8 *line, uint8 start, uint8 end)
{int16 Sum_Difference_Value = 0;     //用于X坐标差值的累积int16 Sum_Weight = 0;               //用于权值相加uint8 i = 0;uint8 Base_Curvature_Weight[60] = {0, 0, 1, 0, 1, 0, 1, 0, 1, 0,1, 0, 1, 0, 1, 0, 1, 0, 1, 0,1, 0, 1, 0, 1, 0, 1, 0, 1, 0,1, 0, 1, 0, 1, 0, 1, 0, 1, 0,1, 0, 1, 0, 1, 0, 1, 0, 1, 0,1, 0, 1, 0, 1, 0, 1, 0, 1, 0};       //固定权值,0,1,0,1间隔加权uint8 Trends_Curvature_Weight[19] = {4, 5, 5, 6, 6, 7, 7, 8, 8, 8, 8, 8, 7, 7, 6, 6, 5, 5, 4};      //动态权值,高权值放中间uint8 Start_Line = (uint8)((float)(Y_Meet - 2) / 18.0f * 16.0f + 24.0f);    //动态权替换固定权起始行,我图像为60行,最低从24行向上替换,最高为40行向上替换,Y_Meet最大值为20//将动态权值赋值给固定权值数组for(i = 0; i < 19; i ++){Base_Curvature_Weight[Start_Line - i] = Trends_Curvature_Weight[i];}for(i = start; i < end - 1; i++)    //求差值和并求出权值{Sum_Difference_Value += (int16)(My_ABS((int)line[i] - (int)line[i + 1]) * (int)Base_Curvature_Weight[i]);Sum_Weight += (int16)Base_Curvature_Weight[i];}if(Sum_Weight != 0)     //防止权值为0,但好像不会出现,当时不知道为啥要写这个{Last_Curvature_Value = (float)Sum_Difference_Value / (float)Sum_Weight;     //求加权平均值,归一化放在了另一个函数里return Last_Curvature_Value;}else{return Last_Curvature_Value;}
}

2.速度拟合

        除了曲率,速度拟合还需引入爬线相遇点Y值作为第二个参考数据,下面这部分代码思想有一定深度:

float Speed_Value[7] = {0.0f, 0.166f, 0.333f, 0.5f, 0.666f, 0.833f, 1.0f};  //速度采用了归一化,将1.0均分为六段float Speed_Vague_Array[4][4] = { {0, 1, 2, 3},{1, 2, 3, 4},{3, 4, 5, 6},{5, 6, 6, 6}};    //映射数组int16 Y_Meet_Count = 0;     //累积满足相遇点条件的次数,比如由弯道进入直道时,车身未转正时Y_Meet已经趋近于2了,此时加速会导致车轨迹不稳//那我们就进行一定的缓冲,当Y_Meet满足我们设定的条件一定次数后,再去执行相应的加速程序,可确保车身转正再加速
/**
* 函数功能:      使用相遇点和曲率映射速度,参照模糊PID思想
* 特殊说明:      无
* 形  参:        float sensitivity                //曲率
*                 float speed_min_proportion       //速度最小比率(设定最小速度与最大速度的比值),为0~1之间
*                 float Speed_max_proportion       //速度最大比率,通常为1
*                 uint8 y_meet                     //爬线相遇点的Y坐标
*                 uint8 min_y_meet                 //爬线相遇点的最小值,看得越远值越小
*                 uint8 max_y_meet                 //爬线相遇点的最大值,看得越远值越大
*
* 示例:          Calculate_Curvature_2(C_Line, Y_Meet, L_Start_Point[1]);
* 返回值:        Last_Curvature_Value      所得曲率
*/
float Speed_Mapping(float sensitivity, float speed_min_proportion, float Speed_max_proportion, uint8 y_meet, uint8 min_y_meet, uint8 max_y_meet)
{uint16 i = 0;float VH = 0, VE = 0;float X2Y = 0;float X1Y = 0;float Y2X = 0;float Y1X = 0;uint8 View_Effictive_Line = max_y_meet - min_y_meet;//弯道、环岛、十字时设定最大速度为35(这是我们组控制位给的一个值,换成自己组的)if(Element_State == L_Turn ||  Element_State == R_Turn || Element_State == L_Circle || Element_State == R_Circle || Element_State == Cross){Y_Meet_Count = 0;Max_Speed = 35;}//下面就是所说的缓冲代码,弯道到直道切换时,爬线相遇点Y趋近于2,当满足一千次以后(计算一千张图像),将最大速度设定为40//另一方面更重要的是为了防止误判,导致乱加减速//冲!冲!冲!冲!冲!冲!冲!冲!冲!冲!冲!冲!冲!冲!冲!冲!冲!冲!冲!冲!冲!冲!冲!冲!//冲!冲!冲!冲!冲!冲!冲!冲!冲!冲!冲!冲!冲!冲!冲!冲!冲!冲!冲!冲!冲!冲!冲!冲!//冲!冲!冲!冲!冲!冲!冲!冲!冲!冲!冲!冲!冲!冲!冲!冲!冲!冲!冲!冲!冲!冲!冲!冲!else if(Y_Meet <= 4 && Max_Speed == 35){Y_Meet_Count ++;if(Y_Meet_Count >= 1000){Y_Meet_Count = 0;Max_Speed = 40;}}//一次不满足就重新来,比较严格,可以不用或者判定放缓一些else if(Y_Meet != 2 && Max_Speed == 35){Max_Speed = 35;Y_Meet_Count = 0;}//直道到弯道的缓冲(作用于弯道在图像最远端,还未识别到弯道时),这个缓冲时间短一些,减速快些,这两个计数值根据自己情况调整else if(Y_Meet != 2 && Max_Speed == 40){Y_Meet_Count --;if(Y_Meet_Count <= -350){Y_Meet_Count = 0;Max_Speed = 35;}}//获取左右两侧边线落在边框上的个数Get_L_Border_Point_Num();Get_R_Border_Point_Num();//当识别出元素是直道,爬线相遇点位于顶端,左右两侧几乎没有落在边框上的点,并且中线曲率很小时,直接返回最大速度开冲!//此处判定十分严格,不会误判,不满足时才执行下方速度拟合部分代码if(Element_State == 2 && Y_Meet <= 3 && L_Border_Point_Num <= 2 && R_Border_Point_Num <= 2 && sensitivity <= 0.2f){return Max_Speed;}//下面使用曲率和爬线相遇点Y坐标拟合速度//与模糊PID原理相同,不过多注释VH = ((float)(Y_Meet - 2) * 3.0f / (float)View_Effictive_Line);VE = sensitivity * 3.0f;int8 VH1 = (int8)VH;if (VH1 > VH){VH--;}int8 VH2 = VH1 + 1;int8 VE1 = (int8)VE;if (VE1 > VE){VE1--;}int8 VE2 = VE1 + 1;if (VH1 > 3){VH1 = 3;}if (VH2 > 3){VH2 = 3;}if (VE1 > 3){VE1 = 3;}if (VE2 > 3){VE2 = 3;}X2Y = (Speed_Vague_Array[VH1][VE2] - Speed_Vague_Array[VH1][VE1]) * (VE - VE1) + Speed_Vague_Array[VH1][VE1];X1Y = (Speed_Vague_Array[VH2][VE2] - Speed_Vague_Array[VH2][VE1]) * (VE - VE1) + Speed_Vague_Array[VH2][VE1];Y2X = (Speed_Vague_Array[VH2][VE1] - Speed_Vague_Array[VH1][VE1]) * (VH - VH1) + Speed_Vague_Array[VH1][VE1];Y1X = (Speed_Vague_Array[VH2][VE2] - Speed_Vague_Array[VH1][VE2]) * (VH - VH1) + Speed_Vague_Array[VH1][VE2];float Speed_approximation = (X2Y + X1Y + Y2X + Y1X) / 4.0;int8 Speed_1 = (int8)Speed_approximation;if (Speed_1 > Speed_approximation){Speed_1--;}int8 Speed_2 = Speed_1 + 1;return (1.0f - ((Speed_Value[Speed_2] - Speed_Value[Speed_1]) * (Speed_approximation - Speed_1) + Speed_Value[Speed_1])) * (float)(Max_Speed - Min_Speed) + (float)Min_Speed;   //返回拟合好的速度
}

3.速度控制

        我们需要一个整体的函数进行速度的调控,减速拉伸部分有一定难度(也可以不使用),多读几次就好,下面上代码:

float Last_Sensitivity = 0;
float Temp_Sensitivity = 0;
float Speed_Proportion_Array[100] = {0};    //用于滑动均值滤波
/**
* 函数功能:      进行速度控制使用相遇点和曲率映射速度,参照模糊PID思想
* 特殊说明:      无
* 形  参:        uint8 Auto_Control_Flag           是否要车辆自行控制速度
*                                                   //当自动控制标志位为1时,速度根据中线曲率和相遇点进行拟合
*                                                   //当自动控制标志位为0时,速度由手动输入                              
*                 uint16 Manual_Control_Value       //手动控制速度
*
* 示例:          Fitting_Speed_Control(1, 0);
* 返回值:        无
*/
void Fitting_Speed_Control(uint8 Auto_Control_Flag, uint16 Manual_Control_Value, uint8 element, uint8 element_state, uint8 start, uint8 end)
{uint8 i = 0;if(Auto_Control_Flag == 1){float Temp_Speed_Proportion = 0;//计算中线曲率,用于速度控制Last_Sensitivity = Sensitivity;Temp_Sensitivity = Calculate_Curvature_2(C_Line, Y_Meet, L_Start_Point[1]) / 1.0f;//计算最小速度比率Speed_Min_Proportion = (float)Min_Speed / (float)Max_Speed;//Stretch_Coefficient:减速拉伸系数,此参数越大,减速距离越短,最大值为1//Stretch_Coefficient 等于手动设定一个曲率最小阈值,小于这个阈值时直接将曲率设为0if(Temp_Sensitivity <= Stretch_Coefficient){Sensitivity = 0.0f;}else    //将(阈值~1.0)之间的数进行拉伸,可以代入几个实际的数理解下这部分原理{float Temp_Float_Num = (Temp_Sensitivity - Stretch_Coefficient) * (1.0f / (1.0f - Stretch_Coefficient));Sensitivity = Limit_Float(Temp_Float_Num * 0.3f + Last_Sensitivity * 0.7f, 0.01f, 1.0f);    //曲率值滤波}Temp_Speed_Proportion = Speed_Mapping(Sensitivity, Speed_Min_Proportion, 1.0f, Y_Meet, 2, 12);  //计算速度Speed_Proportion = Limit_Float(Temp_Speed_Proportion, (float)Min_Speed, (float)Max_Speed);  //速度限幅//滑动均值滤波,防止速度突变float Speed_Proportion_Sum = 0;for(i = 0; i < 99; i ++){Speed_Proportion_Array[i + 1] = Speed_Proportion_Array[i];}Speed_Proportion_Array[0] = Speed_Proportion;for(i = 0; i < 100; i ++){Speed_Proportion_Sum += Speed_Proportion_Array[i];}Speed_Proportion = Speed_Proportion_Sum / 100.0f;}    else{//手动控制速度部分的代码相必就很简单了,我这里比较乱,需读者自行编写了}
}

五、路径

        车运行时,路径是极为重要的,匀速下好的路径甚至要快于加减速下路径较差的情况,因此对于转向的路径是每个组别需要下功夫的难题。对于路径控制,因摩托比较难掌控,所以未在这方面多加探索,所熟知的有上交开源方案中的纯跟踪控制,比较适用于四轮车模,可查询资料多加探索和尝试。

        但是,一定要尽可能优化路径!尽可能优化路径!尽可能优化路径!

 

六、国赛视频

        比赛时正好直播镜头切到了摩托组,学弟及时开启了录屏,留下了这一段非常有纪念意义的视频(此处只剪出了最快速完赛的部分)。卓大的亲自解说也算是弥补了部分国二的遗憾,同时也证明整个系列开源的文案并非一纸空文,而是有实际效果的,各位车友可放心参考。

智能车赛国二摩托完赛视频

 

 

相关文章:

智能车摄像头开源—9 动态权、模糊PID、速度决策、路径优化

目录 一、前言 二、动态权 1.概述 2.偏差值加动态权 三、模糊PID 四、速度决策 1.曲率计算 2.速度拟合 3.速度控制 五、路径 六、国赛视频 一、前言 在前中期通过识别直道、弯道等元素可进行加减速操作实现速度的控制&#xff0c;可进一步缩减一圈的运行速度&#xff…...

java基础 this和super的介绍

this和super this关键字的用法super关键字的用法this与super的区别和注意事项 this关键字的用法 this是自身的一个对象&#xff0c;代表对象本身&#xff0c;可以理解为&#xff1a;指向对象本身的一个指针 class Person{private String name;private int age;public String …...

《Python星球日记》第25天:Pandas 数据分析

名人说&#xff1a;路漫漫其修远兮&#xff0c;吾将上下而求索。—— 屈原《离骚》 创作者&#xff1a;Code_流苏(CSDN)&#xff08;一个喜欢古诗词和编程的Coder&#x1f60a;&#xff09; 订阅专栏&#xff1a;《Python星球日记》 目录 一、引言二、数据分组与聚合1. 分组操…...

C++在Linux上生成动态库并调用接口测试

加减乘除demo代码 项目结构 CPP/ ├── calculator.cpp ├── calculator.h ├── main.cpp 头文件 #ifndef CALCULATOR_H #define CALCULATOR_H#ifdef __cplusplus extern "C" {#endifdouble add(double a, double b);double subtract(double a, double b…...

Cesium.js(6):Cesium相机系统

Camera表示观察场景的视角。通过操作摄像机&#xff0c;可以控制视图的位置、方向和角度。 帮助文档&#xff1a;Camera - Cesium Documentation 1 setView setView 方法允许你指定相机的目标位置和姿态。你可以通过 Cartesian3 对象来指定目标位置&#xff0c;并通过 orien…...

机器学习中的数学(PartⅡ)——线性代数:概述

首先引入代数和线性代数的概念&#xff1a; 在将一些直观的、基于经验或直觉的概念转化为严格的数学或逻辑定义时&#xff0c;一种常用方法是构建一组对象和一组操作这些对象的规则&#xff0c;这就是代数。线性代数是研究向量和某些操作向量的规则。 其次从更广泛的意义上定…...

基于双闭环PID控制器的永磁同步电机控制系统匝间故障Simulink仿真

欢迎微♥关注“电击小子程高兴的MATLAB小屋”获取巨额优惠 1.模型简介 本仿真模型基于MATLAB/Simulink&#xff08;版本MATLAB 2013Rb&#xff09;软件。建议采用matlab2013 Rb及以上版本打开。&#xff08;若需要其他版本可联系代为转换&#xff0c;高于该版本的matlab均可正…...

在51单片机上实现平滑呼吸灯:50us定时器PWM实战指南

在51单片机上实现平滑呼吸灯:50us定时器PWM实战指南 引言 本文将详细介绍如何在51单片机平台上,通过精确的50us定时器中断实现无闪烁的呼吸灯效果。相比常见的125us实现方案,50us定时器能提供更高的PWM频率和更细腻的亮度控制。 硬件设计 基本电路配置 主控芯片:SC92F8…...

asm汇编源代码之CPU型号检测

提供1个子程序: 1. CPU型号检测 CPUTYPE 无输入参数&#xff0c;返回值AX指示CPU类型(报歉,当时最新CPU型号只有80486) 函数的返回值详细描述如下 CPUTYPE PROC  FAR ;OUT: AX01, 8086; AX02, 80286; AX03, 80386; AX04, 80486 UP; ; more source code at http://www.ahj…...

提高课:数据结构之树状数组

1&#xff0c;楼兰图腾 #include<iostream> #include<cstring> #include<cstdio> #include<algorithm>using namespace std;typedef long long LL;const int N 200010;int n; int a[N]; int tr[N]; int Greater[N], lower[N];int lowbit(int x) {ret…...

python可变对象与不可变对象

文章目录 Python 中的可变对象与不可变对象不可变对象(Immutable Objects)可变对象(Mutable Objects)重要区别 Python 中的可变对象与不可变对象 在 Python 中&#xff0c;对象可以分为可变对象(mutable)和不可变对象(immutable)&#xff0c;这是 Python 中非常重要的概念&…...

C++学习之金融类安全传输平台项目git

目录 1.知识点概述 2.版本控制工具作用 3.git和SVN 4.git介绍 5.git安装 6.工作区 暂存区 版本库概念 7.本地文件添加到暂存区和提交到版本库 8.文件的修改和还原 9.查看提交的历史版本信息 10.版本差异比较 11.删除文件 12.本地版本管理设置忽略目录 13.远程git仓…...

果篮问题 Python

# 给你两个长度为 n 的整数数组&#xff0c;fruits 和 baskets&#xff0c;其中 fruits[i] 表示第 i 种水果的 数量&#xff0c;baskets[j] 表示第 j 个篮子的 容量。 # 你需要对 fruits 数组从左到右按照以下规则放置水果&#xff1a; # 每种水果必须放入第一个 容量大于等于 …...

Spring 是如何解决循环依赖的?

在使用 Spring 框架进行开发时&#xff0c;循环依赖是一个常见而棘手的问题。循环依赖指的是两个或多个 bean 之间的相互依赖&#xff0c;导致 Spring 容器无法正常创建这些 bean。下面将深入探讨 Spring 如何解决循环依赖问题&#xff0c;并提供一些最佳实践。 什么是循环依赖…...

部署NFS版StorageClass(存储类)

部署NFS版StorageClass存储类 NFS版PV动态供给StorageClass(存储类)基于NFS实现动态供应下载NFS存储类资源清单部署NFS服务器为StorageClass(存储类)创建所需的RBAC部署nfs-client-provisioner的deployment创建StorageClass使用存储类创建PVC NFS版PV动态供给StorageClass(存储…...

深入理解 PyTorch 的 nn.Embedding:词向量映射及变量 weight 的更新机制

文章目录 前言一、直接使用 nn.Embedding 获得变量1、典型场景2、示例代码&#xff1a;3、特点 二、使用 iou_token nn.Embedding(1, transformer_dim) 并访问 iou_token.weight1、典型场景2、示例代码&#xff1a;3、特点 三、第一种方法在模型更新中会更新其值吗&#xff1f…...

go语言内存泄漏的常见形式

go语言内存泄漏 子字符串导致的内存泄漏 使用自动垃圾回收的语言进行编程时&#xff0c;通常我们无需担心内存泄漏的问题&#xff0c;因为运行时会定期回收未使用的内存。但是如果你以为这样就完事大吉了&#xff0c;哪里就大错特措了。 因为&#xff0c;虽然go中并未对字符串…...

操作系统

操作系统 操作系统&#xff08;OperatingSystem&#xff0c;OS&#xff09;是指控制和管理整个计算机系统的硬件和软件资源&#xff0c;并合理地组织调度计算机的工作和资源的分配&#xff1b;以提供给用户和其他软件方便的接口和环境&#xff1b;它是计算机系统中最基本的系统…...

《JVM考古现场(十八):造化玉碟·用字节码重写因果律的九种方法》

"鸿蒙初判&#xff01;当前因果链突破十一维屏障——全体码农修士注意&#xff0c;《JVM考古现场&#xff08;十八&#xff09;》即将渡劫飞升&#xff01;" 目录 上卷阴阳交缠 第一章&#xff1a;混沌初开——JVM因果律的量子纠缠 第二章&#xff1a;诛仙剑阵改—…...

【2】k8s集群管理系列--包应用管理器之helm(Chart语法深入应用)

一、Chart模板&#xff1a;函数与管道 常用函数&#xff1a; • quote&#xff1a;将值转换为字符串&#xff0c;即加双引号 • default&#xff1a;设置默认值&#xff0c;如果获取的值为空则为默认值 • indent和nindent&#xff1a;缩进字符串 • toYaml&#xff1a;引用一…...

汇编获取二进制

mov_.S mov %r8d,0 nop执行命令&#xff1a; gcc -c mov_.S 会输出 mov_.o 文件&#xff1a;objdump -D mov_.o : mov_.o&#xff1a; 文件格式 elf64-x86-64Disassembly of section .text:0000000000000000 <.text>:0: 44 89 04 25 00 00 00 mov %r8d,0x0…...

《嵌套调用与链式访问:C语言中的函数调用技巧》

&#x1f680;个人主页&#xff1a;BabyZZの秘密日记 &#x1f4d6;收入专栏&#xff1a;C语言 &#x1f30d;文章目入 一、嵌套调用&#xff08;一&#xff09;定义&#xff08;二&#xff09;实现方式&#xff08;三&#xff09;优点&#xff08;四&#xff09;缺点 二、链式…...

txt、Csv、Excel、JSON、SQL文件读取(Python)

txt、Csv、Excel、JSON、SQL文件读取&#xff08;Python&#xff09; txt文件读写 创建一个txt文件 fopen(rtext.txt,r,encodingutf-8) sf.read() f.close() print(s)open( )是打开文件的方法 text.txt’文件名 在同一个文件夹下所以可以省略路径 如果不在同一个文件夹下 ‘…...

前端工程化之新晋打包工具

新晋打包工具 新晋打包工具前端模块工具的发展历程分类初版构建工具grunt使用场景 gulp采用管道机制任务化配置与api简洁 现代打包构建工具基石--webpack基于webpack改进的构建工具rollup 推荐举例说明package.jsonrollup.config.mjsmy-extract-css-rollup-plugin.mjssrc/index…...

Python语言介绍

Python 是一种高级、通用、解释型的编程语言&#xff0c;由 Guido van Rossum 于 1991 年首次发布。其设计哲学强调代码的可读性和简洁性。 Python通过简洁的语法和强大的生态系统&#xff0c;成为当今最受欢迎的编程语言之一。 一、核心特点 Python 是一种解释型、面向对象、…...

关于 Spring Boot 部署到 Docker 容器的详细说明,涵盖核心概念、配置步骤及关键命令,并附上表格总结

以下是关于 Spring Boot 部署到 Docker 容器的详细说明&#xff0c;涵盖核心概念、配置步骤及关键命令&#xff0c;并附上表格总结&#xff1a; 1. Docker 核心概念 概念描述关系镜像&#xff08;Image&#xff09;预定义的只读模板&#xff0c;包含运行环境和配置&#xff08…...

Tomcat 服务频繁崩溃的排查方法

# Tomcat 服务频繁崩溃排查方法 当Tomcat服务频繁崩溃时&#xff0c;可以按照以下步骤进行系统化排查&#xff1a; ## 1. 检查日志文件 **关键日志位置**&#xff1a; - catalina.out (标准输出和错误) - catalina.log (主日志) - localhost.log (应用相关日志) - host-mana…...

分布式系统-脑裂,redis的解决方案

感谢你的反馈&#xff01;很高兴能帮到你。关于你提到的“脑裂”&#xff08;split-brain&#xff09;&#xff0c;这是一个分布式系统中的常见术语&#xff0c;尤其在像 Redis Cluster 这样的高可用集群中会涉及。既然你问到了&#xff0c;我会从头解释“脑裂”的含义、Redis …...

MySQL InnoDB 索引与B+树面试题20道

1. B树和B+树的区别是什么? 数据存储位置: B树:所有节点(包括内部节点和叶子节点)均存储数据。 B+树:仅叶子节点存储数据,内部节点仅存储键值(索引)。 叶子节点结构: B+树:叶子节点通过双向链表连接,支持高效的范围查询。 查询稳定性: B+树:所有查询必须走到叶子…...

深入解析 Spring AI Alibaba 多模态对话模型:构建下一代智能应用的实践指南

一、多模态对话模型的技术演进 1.1 从单一文本到多模态交互 现代AI应用正经历从单一文本交互到多模态融合的革命性转变。根据Gartner预测&#xff0c;到2026年将有超过80%的企业应用集成多模态AI能力。Spring AI Alibaba 对话模型体系正是为这一趋势量身打造&#xff0c;其技…...

2025年ESWA SCI1区TOP:动态分类麻雀搜索算法DSSA,深度解析+性能实测

目录 1.摘要2.麻雀搜索算法SSA原理3.孤立微电网经济环境调度4.改进策略5.结果展示6.参考文献7.代码获取 1.摘要 污染物排放对环境造成负面影响&#xff0c;而可再生能源的不稳定性则威胁着微电网的安全运行。为了在保障电力供应可靠性的同时实现环境和经济目标的平衡&#xff…...

MySQL Error Log

MySQL Error Log Error Log 的开启Error Log 查看Error Log 滚动 MySQL Error Log MySQL主从复制&#xff1a;https://blog.csdn.net/a18792721831/article/details/146117935 MySQL Binlog&#xff1a;https://blog.csdn.net/a18792721831/article/details/146606305 MySQL Ge…...

让DeepSeek API支持联网搜索

引子 DeepSeek官网注册的API token是不支持联网搜索的&#xff0c;这导致它无法辅助分析一些最新的情况或是帮忙查一下互联网上的资料。本文从实战角度提供一种稳定可靠的方法使得DeepSeek R1支持联网搜索分析。 正文 首先登录火山方舟控制台&#xff0c;https://www.volcen…...

SQL 语句说明

目录 数据库和数据表什么是 SQL 语言数据操作语言&#xff08;DML&#xff09;1、SELECT 单表查询通过 WHERE 对原始数据进行筛选通过 聚合函数 获取汇总信息通过 ORDER BY 对结果排序通过 GROUP BY 对数据进行分组通过 HAVING 对分组结果进行筛选 2、SELECT 多表查询3、INSERT…...

PostgreSQL内幕探索—基础知识

PostgreSQL内幕探索—基础知识 PostgreSQL&#xff08;以下简称PG&#xff09; 起源于 1986 年加州大学伯克利分校的 ‌POSTGRES 项目‌&#xff0c;最初以对象关系模型为核心&#xff0c;支持高级数据类型和复杂查询功能‌。 1996 年更名为 PostgreSQL 并开源&#xff0c;逐…...

Springboot项目正常启动,访问资源却出现404错误如何解决?

我在自己的springboot项目中的启动类上同时使用了SprinBootApplication和ComponentScan注解, 虽然项目能够正常启动,但是访问资源后,返回404错误,随后在启动类中输出bean,发现controller创建失败: 而后我将ComponentScan去掉后资源就能访问到了. 原因 SprinBootApplication本身…...

MaxPooling层的作用(通俗解释)

MaxPooling层的作用&#xff08;通俗解释&#xff09; MaxPooling层是卷积神经网络中非常重要的组成部分&#xff0c;它的主要作用可以用以下几个简单的比喻来理解&#xff1a; 1. 信息压缩器&#xff08;降维作用&#xff09; 就像把一张高清照片缩小尺寸一样&#xff0c;M…...

0.DockerCE起步之Linux相关【完善中】

ubuntu用户组&权限&文件/目录 服务启停操作 sudo systemctl start docker # 启动服务3,4 sudo systemctl stop docker # 停止服务 sudo systemctl restart docker ps top 以下内容参考 Vim编辑器 Linux系统常用命令 管理Linux实例软件源 Cron定时任务 在Linux系统上…...

树莓派Pico C/C++ OpenOCD调试环境搭建(Windows)

树莓派Pico C/C OpenOCD调试环境搭建(Windows) 参考资料和背景 从上次树莓派Pico C/C 开发环境搭建(一键完成版)后&#xff0c;一直想找个合适调试器&#xff0c;最后测试了多种方案&#xff0c;还是使用另一块树莓派pico作为picoprobe 来调试比较方便&#xff0c;其中参考的…...

【图像生成之21】融合了Transformer与Diffusion,Meta新作Transfusion实现图像与语言大一统

论文&#xff1a;Transfusion: Predict the Next Token and Diffuse Images with One Multi-Modal Model 地址&#xff1a;https://arxiv.org/abs/2408.11039 类型&#xff1a;理解与生成 Transfusion模型‌是一种将Transformer和Diffusion模型融合的多模态模型&#xff0c;旨…...

《人件》第二章 办公环境

二、办公环境 电话铃不停的响&#xff0c;打印机维修人员顺道过来聊聊天&#xff0c;复印机不工作了&#xff0c;人事部不停催促更新的能力调查表&#xff0c;下午3点之前就要提交时间表…然后一天就这样过去了。 2.1 家具警察 人们怎么使用空间、需要的桌子空间多大、花多少小…...

哈希表系列一>存在重复元素II 存在重复元素I

目录 题目&#xff1a;解析&#xff1a;存在重复元素 II-->代码&#xff1a;存在重复元素-->代码&#xff1a; 题目&#xff1a; 链接: link 链接: link 解析&#xff1a; 存在重复元素 II–>代码&#xff1a; class Solution {public boolean containsNearbyDuplic…...

文献总结:AAAI2025-UniV2X-End-to-end autonomous driving through V2X cooperation

UniV2X 一、文章基本信息二、文章背景三、UniV2X框架1. 车路协同自动驾驶问题定义2. 稀疏-密集混合形态数据3. 交叉视图数据融合&#xff08;智能体融合&#xff09;4. 交叉视图数据融合&#xff08;车道融合&#xff09;5. 交叉视图数据融合&#xff08;占用融合&#xff09;6…...

LeetCode --- 444 周赛

题目列表 3507. 移除最小数对使数组有序 I 3508. 设计路由器 3509. 最大化交错和为 K 的子序列乘积 3510. 移除最小数对使数组有序 II 一、移除最小数对使数组有序 I & II 由于数组是给定的&#xff0c;所以本题的操作步骤是固定的&#xff0c;我们只要能快速模拟操作的过…...

单片机Day05---静态数码管

目录 一、原理图&#xff1a;​编辑 二、思路梳理&#xff1a; 三&#xff1a;一些说明&#xff1a; 1.点亮方式&#xff1a; 2.数组&#xff1a; 3.数字与段码对应&#xff1a; 四&#xff1a;程序实现&#xff1a; 一、原理图&#xff1a; 二、思路梳理&#xff1a; …...

kernel32!GetQueuedCompletionStatus函数分析之返回值得有效性

第一部分&#xff1a;//#define STATUS_SUCCESS 0x0返回值为0 } else { // // Set the completion status, capture the completion // information, deallocate the associated IRP, and // attempt to write the…...

gazebo 启动卡死的解决方法汇总

1. 排查显卡驱动是否正常安装 nvidia-smi # 英伟达显卡--------------------------------------------------------------------------------------- | NVIDIA-SMI 535.230.02 Driver Version: 535.230.02 CUDA Version: 12.2 | |------------------------…...

硬件设计-MOS管快速关断的原因和原理

目录 简介&#xff1a; 来源&#xff1a; MOS管快关的原理 先简单介绍下快关的原理&#xff1a; 同电阻时为什么关断时间会更长 小结 简介&#xff1a; 本章主要介绍MOS快速关断的原理和原因。 来源&#xff1a; 有人会问&#xff0c;会什么要求快速关断&#xff0c;而…...

塔能科技解节能密码,工厂成本“效益方程式”精准破题

在全球积极推进可持续发展战略的当下&#xff0c;各行业都在努力探索节能减排、绿色发展的新路径&#xff0c;对于工厂而言&#xff0c;节能早已不是锦上添花的选择&#xff0c;而已成为关乎企业生死存亡与长远发展的核心要素&#xff0c;是实现可持续运营的必由之路。塔能科技…...

swift ui基础

一个朴实无华的目录 今日学习内容&#xff1a;1.三种布局&#xff08;可以相互包裹&#xff09;1.1 vstack&#xff08;竖直&#xff09;&#xff1a;先写的在上面1.1 hstack&#xff08;水平&#xff09;&#xff1a;先写的在左边1.1 zstack&#xff08;前后&#xff09;&…...