智能车学习笔记乱写
乱写的归纳总结,翻着PPT想到什么写什么。
代码都是用的逐飞的库。
中断#
定时循环触发一些事件。
中断处理优先级高于main且各个中断之间也有优先级差异。
一些需要高频重复进行的动作或者一些特殊情况判断可以扔到中断里。
举一个典型应用是在main里执行摄像头图像处理程序,然后定时中断读取error来执行PID控制舵机打角。和这个例子有关的代码如下:
copy//在main里初始化一个中断进程。前一个参数是定时器通道号,后一个参数是触发间隔时间
pit_ms_init(CCU60_CH0,10);
while(1){
bin_image(KP,KD,image);//重复执行图像处理
}
copy//下面是中断里放的部分
IFX_INTERRUPT(cc60_pit_ch0_isr, 0, CCU6_0_CH0_ISR_PRIORITY) //选用和初始化的通道号相同的isr
{
interrupt_global_enable(0); // 开启中断嵌套
pit_clear_flag(CCU60_CH0);
if(page==1){
uint32 PID=(uint32)(KP*now_error-KD*(now_error-lst_error)+MID_SERVO); //PID控制
if(PID>MID_SERVO+MAX_ANGLE) PID=MID_SERVO+MAX_ANGLE;
if(PID<MID_SERVO-MAX_ANGLE) PID=MID_SERVO-MAX_ANGLE;
pwm_set_duty(SERVO,PID);
lst_error=now_error;
}
}
PID#
简而言之就是我现在有一个可以观测的量(比如车的实际速度)和一个我可以控制的量(比如电机的PWM输出),现在我希望通过控制控制量来使观测量达到一个目标值(例如预期速度),但是控制量与观测量之间并没有具体的数学关系,那么我就可以通过PID控制来确定控制量,从而影响观测量使其逼近目标值。
具体而言:我们不妨设控制量为p,观测量为a,目标值为t。
设e=a−t,即e为误差。
那么将控制量实时设为p=PID=KPe+KDΔe+KIΣe可以使a逐渐逼近t。
其中KP,KD,KI为参数,Δe,Σe分别是e的微分量和积分量。
一般而言在控制舵机时只使用PD控制,控制舵机时只使用PI控制。
下面是一段PI控制速度时的代码:
copypwm_set_duty(MOTOR_L,(uint32)(KP*error+KI*sum_speed_error));
用编码器获取速度#
编码器实际获得的数据是目前已经走过的格数(不妨设该量为n)(走一圈是走过多少格固定,比如我现在用的编码器是走一圈记1024格(不妨设该量为N))。
所以可以通过v=kΔnΔt来确定速度。其中k是一个比例系数,用于把走过的格数映射为走过的距离,具体而言k=2πraN(其中a表示轮子用齿轮传递到编码器时的齿数比,r为轮子半径)。当然如果懒得搞计算的话完全可以推着车在地上走1m,然后看看编码器走了多少格,由此确定k。
要注意的是编码器有计数极限,爆了之后会变成正负性相反的极限值,所以车连着跑的时候检测到的速度会有突变,要注意筛掉。
图像处理#
二值化#
摄像头读进来的初始图像是灰度图,每个像素是[0,255],为了方便后面图像识别首先需要进行二值化处理,处理成黑白,01。显然我们需要确定一个阈值,大于阈值的判定为1,否则为0。
这个阈值可以自己给定,但是需要根据场地光照及时调整;也可以用下面所述的OTSU大津法确定。
OTSU大津法是指假设阈值把颜色分成两类,我们应该使类间方差最大,即[μ1(t)−μ2(t)]2最大,其中的平均值指的是一个类中所有像素颜色大小的平均值。
代码实现如下:
copyuint8 get_line(uint8 image[][MT9V03X_W]){
int cnt[256];
int i,j;
uint8 ans;
float now_max=0;
float sum1=0,sum2=0; //sum1,sum2分别是两类的颜色加和
float tmp;
int cnt1=0,cnt2=MT9V03X_H*MT9V03X_W;
for(i=0;i<256;i++) cnt[i]=0;
for(i=0;i<MT9V03X_H;i++){
for(j=0;j<MT9V03X_W;j++){
cnt[(int)image[i][j]]++;
sum2+=image[i][j];
}
}
for(i=0;i<256;i++){ //依次尝试每种阈值
cnt1+=cnt[i],cnt2-=cnt[i];
sum1+=i*cnt[i],sum2-=i*cnt[i];
tmp=(sum1*cnt1*cnt2*cnt2-sum2*cnt2*cnt1*cnt1)*(sum1*cnt1*cnt2*cnt2-sum2*cnt2*cnt1*cnt1);
if(tmp>now_max){
now_max=tmp;
ans=(uint8) i;
}
}
return ans;
}
当然,如果场地打光过于离谱(超级亮或者超级暗)OTSU也会爆,这种时候就考虑改固定阈值或者调摄像头的增益/曝光时间吧。
搜线#
-
从画面中心开始向两侧搜索,碰到黑线则认为是边线。逻辑很简单,但是需要保持车身尽量在中线上,劣势挺明显的,实际上我也没采用过。
-
最长白列搜线法:先找到赛道的最长白列,从最长白列开始向两侧搜索。最长白列可以顾名思义,具体而言就是下面图里那条红线,它比其他列都长:
误差的确定#
搜到边界线后显然可以根据每行的左右边界位置,由li+ri2确定赛道中线,然后将其与画面中线比较得到误差。其中每行的误差对总误差的贡献加权可以由该行的宽度确定,这样近处的赛道将获得更多的加权。
copyfor(i=MT9V03X_H-1;i>=MT9V03X_H-max_length+FRONT_LINE;i--) error+=((r[i]+l[i])/2-MT9V03X_W/2)*(float)(r[i]-l[i])/(float)sum_s;
作者: DarthVictor
出处:https://www.cnblogs.com/DarthVictor/p/17871142.html
版权:本文采用「署名-非商业性使用-相同方式共享 4.0 国际」知识共享许可协议进行许可。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】博客园携手 AI 驱动开发工具商 Chat2DB 推出联合终身会员
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 2025年广告第一单,试试这款永久免费的开源BI工具
· o3 发布了,摔碎了码农的饭碗
· [.NET] API网关选择:YARP还是Ocelot?
· 用 2025 年的工具,秒杀了 2022 年的题目。
· C#+ WPF 实现蓝牙转WIFI计步上位机