此次的电赛感觉结束的挺突然的,在找到方向准备细化地进行调试的时候却是没有足够时间来完成代码上的完善。想着此次遗憾,写一篇博客来记录一下自己的经历吧,总结总结此次的电赛时光。
首先便是题目的选择,由于我们之前校电赛时简单接触过小车的题目,在简单看了一下这几个题目后,还是选择了C题小车。
简单分析一下题目有以下几点要求:
赛道如图所示,详细任务要求在代码处分析
比赛赛道
整体流程记录:
在定下选题之后,我们首先要做的便是搭一个车架子出来。根据我们准备的材料我们采用了亚克力板来切割打孔并作为我们的车架。题目要求的长宽分别不超过25cm和15cm,我们便把亚克力板裁成10*20cm的大小后在上面打孔安装。
供电电路的焊接和车子的安装还是花了我们不少的时间,大概在第二天上午基本完成,且暂时缺少电机,于是用的两个万向轮和两个减速电机在初步调试。(现在拼出的车甚至可以丝滑地漂移,着实有点好van)
这之后我们开始先试着用32来调试,在网上搜到了一个用编码器控制电机例程,下下来修修改改,之后想着用AB相采到的脉冲数来计算距离速度(输入捕获法)。
dis += Read_Encoder(3)*0.25*2*3.14*3.5*0.05*0.076;//距离计算
这公式放在定时器1的中断里,每一次进中断它便会计算增加当前走过的距离。没记错的话Read_Encoder(3)为使用定时器3采集到的脉冲数,0.25为的4倍频,2*π*3.5为轮子周长,0.05为衰减比,0.076为每一个脉冲所走的距离。
距离32测试出来和实际的差不多,可惜我们在实际在430上尝试时误差感觉过大也不稳,没找到问题所在最后放弃了编码器测距。第二天白天晚上的时间就整了32的编码器测距以及430的串口上面的调试同步进行的还有超声波模块的移植。
在第三天白天到来的时候,除了串口我们其他的都已失败,这时遭不住赶紧回去再浅睡一下,直到我们的电机到来,开始把咱们的车子都换成四轮车再重理一下逻辑,调合适的速度。下午近晚上的时候,我们超声波移植失败,但另一个兄弟写了了一个出来。这时我们发现最为重要的两车通信还没整出来。于是开始了艰难地调试。
在晚上两点左右整出通信的时候,我们又遇到了新的问题!!!小车巡线寻不准要巡出去,车轮打滑?!后来轮子换来换去,整了个小轮子再给轮子缠了几圈胶才算整完。而巡线在观察其症状和好几遍的代码复查后,我们抱着怀疑的态度然后换了连接灰度模块的杜邦线,啪的一下很快蛤,他就能正常巡线了。。。于是时间来到了第四天早上七点左右,整个人状态也不是很得行。最后在一个白天的持续代码调整下结束了最后一天。遗憾的是在我们赛道上只实现了1、2、4要求,且代码优化上仍未做好,应是遗留了一些小bug,最后也是遗憾收场。
时间过的很快,我们有过错误的尝试,浪费了不少时间,若是时间运用好也许就能优化好代码了。长个经验吧。。。
一、整体要求计算及分析
内圈周长:C =2πr+2l+2w=2*3.14*0.3+2*0.9+2*0.6 = 4.884 m
外圈周长:C =2πr+2l+2w =2*3.14*0.3+2*1.2+2*0.6 = 5.484 m
要求1: 都沿着外圈行驶,领头小车放在路径的起始位置A点,跟随小车放在其后20cm处,领头小车的速度为0.3m/s,平均速度误差不大于10%,速度范围0.27m/s~0.33m/s,小车循迹一圈时间范围:16.62s~20.31s
要求2: 都沿着外圈行驶,领头小车放在路径的起始位置A点,跟随小车放在E边上,领头小车的速度为0.5m/s,平均速度误差不大于10%,速度范围0.45m/s~0.55m/s,小车循迹一圈时间范围:9.97s~12.18s
要求3:将领头小车放在路径的起始位置A点,跟随小车放在其后20cm处,领头小车和跟随小车连续完成三圈路径的行驶。第一圈领头小车和跟随小车都沿着外圈路径行驶。第二圈领头小车沿着外圈路径行驶,跟随小车沿着内圈路径行驶,实现超车领跑。第三圈跟随小车沿着外圈路径行驶,领头小车沿着内圈路径行驶,实现反超和再次领跑。速度最低0.3m/s
要求4:都沿着外圈行驶,领头小车放在路径的起始位置A点领头小车的速度为1m/s,平均速度误差不大于10%,速度范围0.9m/s~1.1m/s,小车循迹一圈时间范围:4.985s~6.093s,等停标志处停5s后继续向前行驶,达终点后停下。
在看完这四个要求后,首先想到的是整体分成四个模式,正好MSP430F5529的板子上刚好有两个按钮,一个来切换模式,另一个来控制小车启动。
主机整体的逻辑判断建立在按钮上,其代码如下:
if(KEY_isPressed(KEY2))//按钮2
{
DELAY_MS(100);// 消抖
if(KEY_isPressed(KEY2))
{
int i_num=0;//长按计数
while(KEY_isPressed(KEY2))
{
DELAY_MS(100);
i_num++;
}
if(i_num>20)//长按超过两秒进入设置模式
{
int set_out=1;//设置模式标志位
OLED_PrintfAt(FONT_ASCII_6X8,0,6,"MODE%d SET ",key_statues);//显示进入设置模式
while(set_out)
{
OLED_PrintfAt(FONT_ASCII_6X8,1,6,"forward%d",mode_forward[key_statues]);//显示当前设置的前进速度
if(KEY_isPressed(KEY1))
{
DELAY_MS(320);
if(KEY_isPressed(KEY1))
{
mode_forward[key_statues]=mode_forward[key_statues]+5;//按钮1按一次+0.5%占空比
}
}
if(KEY_isPressed(KEY2))
{
DELAY_MS(320);
if(KEY_isPressed(KEY2))
{
int j_num=0;
while(KEY_isPressed(KEY2))
{
DELAY_MS(100);
j_num++;
}
if(j_num>20)
{
set_out=0;//长按2s退出设置模式
}
else
{
mode_forward[key_statues]=mode_forward[key_statues]-5;//按钮2按一次-0.5%占空比
j_num=0;
}
}
}
}
i_num=0;
DELAY_MS(100);
}
else//短按切换运行模式一共4个
{
i_num=0;
LED_Turn(LED1);
key_statues++;
if(key_statues>4)
{key_statues=0;}
}
}
}
if(KEY_isPressed(KEY1))//按键1 触发启动
{
DELAY_MS(320);
if(KEY_isPressed(KEY1))
{
LED_Turn(LED1);
key_start++;
mode_2_num=1;
mode_4_num=1;
mode_3_num=2;
UART_Printf (UART1,"AT+CIPSEND=0,4\n");
DELAY_MS(3);
UART_Printf(UART1,"$%d\n",key_statues);//WIFI模块发送给从机模式以及启动信号
if(key_start>1)
{key_start=0;}
}
}
if(key_start)//判断是否按下启动按钮
{
switch(key_statues)//选择执行对应模式
{
case 0: no_mode();break;
case 1: MODE_1();break;
case 2: MODE_2();break;
case 3: MODE_3();break;
case 4: MODE_4();break;
}
}
二、巡线逻辑
5路灰度模块
首先是我们的5路灰度模块,用的中间3个比较近的在巡线,外面两个用来补救或判断,本次没有在此文档中完善。
本次巡线需注意这里的岔路分支点,我们由于灰度模块的宽度合适,可以刚好识别出让灰度模块识别出101(黑线返回为1空白返回为0),此处加个延时右转或左转即可稳定走外圈内圈。但是当小车从D点处回来时还是略有不稳,外圈没问题,但内圈有概率误判为终点
巡线部分逻辑代码如下:
//读取灰度模块的返回值
int read_sensor_values()
{
sensor[0] = GPIO_ReadBit(P4,1); //左 扫到为1
sensor[1] = GPIO_ReadBit(P4,2); //中
sensor[2] = GPIO_ReadBit(P2,7); //右
if ((sensor[0] == 1) && (sensor[1] == 1) && (sensor[2] == 1))
{
if(mode_2_num)
{
mode_2_num=0;
error = 0;
DELAY_MS(300);
}
else if(mode_4_num)
{
_stop_hard(500);
UART_Printf (UART1,"AT+CIPSEND=0,4\n");
DELAY_MS(3);
UART_Printf(UART1,"$6\n");//发送暂挺信号让后车与主车一同停止5s后继续完成路程
_stop();
DELAY_MS(4000);
mode_4_num=0;
forward(1000);
DELAY_MS(200);
}
else
{
error = 3;
}
// 1 1 1 停
}
else if ((sensor[0] == 0) && (sensor[1] == 0) && (sensor[2] == 1))
{
error = 2;// 0 0 1 右转
}
else if ((sensor[0] == 0) && (sensor[1] == 1) && (sensor[2] == 0))
{
error = 0;// 0 1 0 直行
}
else if ((sensor[0] == 1) && (sensor[1] == 0) && (sensor[2] == 0))
{
error = 1;// 1 0 0 左转
}
else if ((sensor[0] == 1) && (sensor[1] == 0) && (sensor[2] == 1))
{
error = 2;// 1 0 1 右转,主要用在寻到岔路时右转回正
}
// else if ((sensor[0] == 1) && (sensor[1] == 1) && (sensor[2] == 0))
// {
// error = 1;// 1 1 0 左转
// }
return error;
}
三、超声波模块
超声波模块HC-SR04时序图触发信号Tirig维持10Us以上的高电平即可等待Echo的返回,这里是用EXTI检测跳变信号,去打开和关闭定时器,最后一次测量计算结果后清除定时器计时,并等待下一个循环开始。
以上时序图表明你只需要提供一个10uS 以上脉冲触发信号,该模块内部将发出8个40kHz周期电平并检测回波。一旦检测到有回波信号则输出回响信号。回响信号的脉冲宽度与所测的距离成正比。
距离=高电平时间*声速(340M/S)/2
主要代码如下:
void HCSR04_TRIG_Send(void)//超声波模块触发测距
{
GPIO_SetBits (PORT6, GPIO_Pin_0);
delay_us(20); //延时20US(至少延时10us)
GPIO_ResetBits (PORT6, GPIO_Pin_0);
}
//定时器中断触发
__interrupt void TIMER_A1_IRQ_Handler()
{
static uint16_t start_value = 0,stop_value = 0;
// LED_Turn(LED1); //指示灯闪烁
if(TIMER_GetITStatus(TIMER_A1,TIMER_CCR1_IRQn)) //获取某一通道中断标志
{
//如果捕获到上升沿
if(TIMER_GetChannelPinInValue(TIMER_A1,TIMER_CH1) == BIT_SET)
{
start_value = TIMER_Capture_GetValue (TIMER_A1,TIMER_CH1); //读取发生该捕获条件时的计数值
overflowTime = 0;
}
//如果捕获到下降沿
else
{
stop_value = TIMER_Capture_GetValue (TIMER_A1,TIMER_CH1); //读取发生该捕获条件时的计数值
PulseWidth = TIMER_Capture_CalTime_Us(start_value,stop_value,overflowTime);//读取捕获时间并转化为距离
distance = PulseWidth/2 * 340.0 /1000000;//读取捕获时间并转化为距离;单位为m
distance =distance*100;//单位cm
TIMER_Capture_Clear (TIMER_A1,TIMER_CH1); //清零
overflowTime = 0;
}
TIMER_ClearITPendingBit(TIMER_A1,TIMER_CCR1_IRQn); //清除TIMER的某一个中断标志
}
if(TIMER_GetITStatus(TIMER_A1,TIMER_OverFlow_IRQn))//如果溢出
{
overflowTime++; //溢出值加1
TIMER_ClearITPendingBit(TIMER_A1,TIMER_OverFlow_IRQn); //清除TIMER的某一个中断标志
}
TIMER_ClearITPendingBit(TIMER_A1,TIMER_OverFlow_IRQn); //清除TIMER的某一个中断标志
}
四、两车通信
小车间使用的ESP32-C3进行通信,用串口配置好模块后就可以使用串口来进行两车间的通信了,要保证主机先上电开启热点,从机再上电连上主机WiFi模块,之后便可以通过串口来进行通信一起启动停止了。相关代码查模块对应的指令应该就行了。
五、完赛总结
封箱的时候想着我们这个拼拼凑凑的小车完成3个任务还是没问题的,也没想到最后只有第一个完美完成,其他要求后车都出了点问题,逻辑仍有一些bug(我们自己的赛道上没有,换了赛道之后就显现出来了)。没有用PID调速,用的定的PWM来确定速度也让我们的车不是十分丝滑,手动按钮调速增加了一些容错率。转弯速度定死了的,让我们只有在0.3m/s时比较丝滑。
可惜没出来就是没出来,就当一次很好的锻炼吧,在看了其他参赛小组的方案和小车的运行效果后,我也想到了一些继续优化我们自己小车逻辑的方向和方案。如要求2,我们不断在根据超声波模块测得的距离加速减速,其效果不是很好,容易撞车且后车也容易被甩远。我们可以先后车开机启动即高速定死,追上至一定距离后直接调速至前车速度相近的pwm值并不断微调,也许这样比一开始直接开始调速要好。
我们的不对称但时刻准备冲出去的小车本次的电赛结束了,有遗憾也有收获,认识到了自己的不足,也见识到了大佬的厉害,开阔了自己眼界。希望此次的经历化作未来的能力,简单记录一下这一同度过的特殊时光吧。望一起努力的小伙伴们共回忆(那天晚上墨客的汉堡是正的香!)。
---------------------------------------------------------------------------------------------------------------------------------
IOTAT——2022/8/3