题目:
探测跟踪系统(C 题)
- 在区域内随便摆放易拉罐,能够探测并显示探测装置与目标物体之间的距离 l。
- 在区域内随便摆放多个易拉罐,探测跟踪系统能够探测并显示指定距离范围内的目标物体数量。
- 在区域内随便摆放多个易拉罐,探测装置能够探测并指示 2 个相距最近的目标物体之间的最近距离。
- 在探测区域内随机画一条长度为 50cm 的直线,将目标物体沿着直线轨迹挪动,探测跟踪系统能够记录并显示目标物体挪动的极坐标轨迹。
- 在探测区域内随机画一条曲线,端点相距 50cm,将目标物体沿着曲线轨迹挪动,探测跟踪系统能够记录并显示目标物体挪动的极坐标轨迹。
1.探测区域及探测跟踪系统布置在水平地面上,目标物体为易拉罐(直径约为 6.6cm,高约为( 11.0 cm),目标物体数量 n≤6,径向方向目标物体摆放互不遮挡。
2.探测跟踪系统数据的传输方法、显示形式及安放位置无限制要求。
3.发挥部分要求记录含起始点、终止点在内的 11 个点的极坐标轨迹,各点按角度均匀分布。
4.要求探测距离误差绝对值≤1cm。超出误差范围时,随误差增大扣分,极坐标 P1(ρ1,θ1)和 P2(ρ2,θ2) 两点间距离
5.题目中每项测试操作必须在 30s 内完成。
方案计划:
硬件架构:
- 易拉罐选择使用白色的易拉罐,白色相对黑色而言反射红外性能更好。
- 由于高度有限制,易拉罐高度为11,所以垂直方向上只摆放了电机和激光模块,其他硬件在底板进行安放。
实验验证过程:
最终成果:
终端部分:
终端部分需要进行易拉罐的识别、电机的控制、屏幕的显示、按键控制题目和范围的选择。
-
MCU:
本次比赛选择使用STM32F407VET6,刚开始使用STM32F103C8T6进行了逻辑部分的验证,由于MCU主频太低,激光测距的数据采集部分DMA有些吃力,所以使用STM32F407VET6进行替代。
-
测距模块:
-
方案一:使用超声波测距模块,但是进行测量时,超声波测距模块在进行高精度识别易拉罐的时候会有偏移现象,很难准确采集到当前的距离。而且当两个易拉罐放的较近时很难进行区分,识别不出中间的空隙,所以放弃使用超声波测距。
-
方案二:使用激光测距,大部分激光测距模块由于有距离限制,所以最终我们选择使用DFROBOT的SEN0366激光测距模块,按照参考手册,精度能从0.05开始最远到达70m,采样速率最高可达20Hz,足以满足本次比赛要求。
刚开始进行模块选择的时候进行验证方案的时候主要注重于各个模块的测量距离极限范围、测量角度的精度、两个易拉罐之间的分别能力。
-
-
电机、舵机的选择:
-
方案一:使用360°舵机,飞特SCS20 306° 20kg舵机,通过串口可以进行方向的控制,且负载能力能达到20Kg,操作简单,数据精准完全适用于本次比赛。
-
方案二:使用步进电机,反馈部分通过计算脉冲个数进行角度的计算,负载能力可以满足需求,能够满足本次比赛需求。
-
实现流程步骤:
测距部分实现:
测距模块使用的是串口进行通信,固件内部存在,所以通过相应的命令进行基础参数的配置后就可以进行距离的检测。
void laser_init(UART_HandleTypeDef *huart)
{
uint8_t sendbuff[5];
g_laser.huart = huart;
HAL_Delay(40);
//5m量程
sendbuff[0] = 0xFA;
sendbuff[1] = 0x04;
sendbuff[2] = 0x09;
sendbuff[3] = 0x05;
sendbuff[4] = 0xF4;
HAL_UART_Transmit(&huart1, sendbuff, 5, 5*10);
HAL_Delay(40);
//分辨率1mm
sendbuff[0] = 0xFA;
sendbuff[1] = 0x04;
sendbuff[2] = 0x0C;
sendbuff[3] = 0x01;
sendbuff[4] = 0xF5;
HAL_UART_Transmit(&huart1, sendbuff, 5, 5*10);
HAL_Delay(40);
//5Hz
sendbuff[0] = 0xFA;
sendbuff[1] = 0x04;
sendbuff[2] = 0x0A;
sendbuff[3] = 0x05;
sendbuff[4] = 0xF3;
HAL_UART_Transmit(&huart1, sendbuff, 5, 5*10);
HAL_Delay(40);
sendbuff[0] = 0x80;
sendbuff[1] = 0x06;
sendbuff[2] = 0x03;
sendbuff[3] = 0x77;
HAL_UART_Transmit(&huart1, sendbuff, 4, 4*10);
__HAL_UART_CLEAR_IDLEFLAG(&huart1);
__HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE);
HAL_UART_Receive_DMA(&huart1, (uint8_t*)g_laser.rev_buffer, 22);
}
电机部分控制:
最终选择使用步进电机进行控制角度,使用基本的io电平进行PWM的控制。
屏幕实现:
屏幕部分通过移植正点原子的官方库进行屏幕的控制。
扫描易拉罐逻辑:
电机部分通过函数进行控制,而串口返回的消息通过消息队列进行处理,使得在进行电机移动的同时可以采集距离和信息通过当前的脉冲数算出角度,知道当前检测的情况。由于单片机为单线程所以电机通过STEP步进移动,使得在移动的同时也不断地扫描消息队列,判断当前是否有串口的数据要处理。
void test1()
{
int32_t nowstep = 0;
uint8_t nowkey;
vwm_init(step1_handle_msg);
beep_on();
HAL_Delay(500);
beep_off();
LCD_Clear(WHITE);
POINT_COLOR = BLACK;
LCD_ShowString(20,30,200,16,16,"Return to Zero");
duoji_runto_zero();
scan_init();
LCD_ShowString(20,30,200,16,16,"Test1 Start Press OK");
vwm_init(NULL);
while (1) {
nowkey = key_scan();
if (nowkey == KEY_3) { //纭畾
beep_on(); HAL_Delay(20); beep_off();
break;
}
else if (nowkey == KEY_4) {//鍙栨秷
beep_on(); HAL_Delay(20); beep_off();
return;
}
vwm_exec();
}
vwm_init(step1_handle_msg);
LCD_ShowString(20,30,200,16,16,"Test1 Process on");
while (1) {
if (nowstep < DUOJI_XIFEN / 2) {
zhengzhuan(TEST_DELAY_TIME, TEST_STEP1);
nowstep += TEST_STEP1;
if (nowstep >= DUOJI_XIFEN / 2) {
beep_on(); HAL_Delay(300); beep_off();
}
}
else {
nowkey = key_scan();
if (nowkey == KEY_4)
beep_on(); HAL_Delay(20); beep_off();
LCD_ShowString(20,30,200,16,16,"Return to Zero");
duoji_runto_zero();
return;
}
}
vwm_exec();
}
}
追踪易拉罐逻辑:
由于是通过消息队列处理激光传出了的数据,所以激光数据和电机的数据并非在一个函数中。二者通过全局变量进行沟通,类似于标志位的作用。
刚开始电机进行正向转动,当激光扫描到物体的时候继续正向移动,后当检测到超出易拉罐的边界(边沿)的时候(也就是检测的距离超出这个长度)的时候改变当前状态,使得电机开始反向转动同时降低转动的速度。由于激光连续采样检测,当向反向转动检测的时候,检测到物体的时候继续反向转动,当检测到超出物体的边界的时候再次切换状态进行正向转动。如此往复可以达到追踪易拉罐的目的。
uint8_t test4_delay_time = 4;
void step4_handle_msg(msg_t * pmsg)
{
if(scan_find_object.fundobject == 0){
if(pmsg->msgdata.len *100 <= 81){
//myprintf("scan_object.fundobject, way: %d\r\n", way_flag);
scan_find_object.min_len = pmsg->msgdata.len;
scan_find_object.min_angle = pmsg->msgdata.angle;
scan_find_object.fundobject = 1;
test4_delay_time = 5;
beep_on(); HAL_Delay(5); beep_off();
POINT_COLOR = RED;
LCD_DrawPoint_angle(scan_find_object.min_len, scan_find_object.min_angle);
scan_find_object.date_old.angle = scan_find_object.min_angle;
scan_find_object.date_old.len = scan_find_object.min_len;
}
}
else if (scan_find_object.fundobject == 1) {
if(scan_find_object.min_len > pmsg->msgdata.len){
scan_find_object.min_len = pmsg->msgdata.len;
scan_find_object.min_angle = pmsg->msgdata.angle;
POINT_COLOR = RED;
LCD_DrawPoint_angle(scan_find_object.min_len, scan_find_object.min_angle);
scan_find_object.date_old.angle = scan_find_object.min_angle;
scan_find_object.date_old.len = scan_find_object.min_len;
}
if (pmsg->msgdata.len *100 > 81){
scan_find_object.fundobject = 0;
way_flag = way_flag + 1;
way_flag = way_flag % 2;
beep_on(); HAL_Delay(5); beep_off();
}
}
}
#define ADD_PAUSED 70
void test4()
{
int32_t nowstep = 0;
uint8_t nowkey;
vwm_init(NULL);
beep_on();
HAL_Delay(300);
beep_off();
LCD_Clear(WHITE);
POINT_COLOR = BLACK;
LCD_ShowString(20,30,200,16,16,"Return to Zero");
duoji_runto_zero();
LCD_ShowString(20,30,200,16,16,"Test4 Start Press OK");
scan_init();
vwm_init(step4_handle_msg);
way_flag = 0;
nowstep = 0;
LCD_Clear(WHITE);
POINT_COLOR = BLACK;
LCD_DrawLine(0,160,160,160);
LCD_DrawLine(160,0,160,160);
LCD_DrawLine(0,80,160,80);
LCD_DrawLine(80,0,80,160);
while (1) {
if (way_flag == 0) {
if (nowstep < DUOJI_XIFEN / 2) {
zhengzhuan(test4_delay_time, TEST_STEP1);
nowstep += TEST_STEP1;
if (nowstep >= DUOJI_XIFEN / 2) {
beep_on(); HAL_Delay(10); beep_off();
way_flag = 1;
}
}
else {
way_flag = 1;
}
}
else if(way_flag == 1){
if (nowstep > 0) {
fanzhuan(test4_delay_time, TEST_STEP1);
nowstep -= TEST_STEP1;
if (nowstep <= 0) {
beep_on(); HAL_Delay(10); beep_off();
way_flag = 0;
}
}
else {
way_flag = 0;
}
}
nowkey = key_scan();
if (nowkey == KEY_4) {
beep_on(); HAL_Delay(20); beep_off();
LCD_ShowString(20,30,200,16,16,"Return to Zero");
duoji_runto_zero();
return;
}
vwm_exec();
}
}
演示结果
问题总结:
Q1:激光在进行移动的时候有时候会没有返回结果。
解决办法:有可能是由于在识别的时候中间部分因为没有识别到物体从而内部切换到休眠状态进行短暂的休眠导致后续识别的失败,通过在外围搭建一圈环境使得它一直进行工作进行检测。
Q2:激光最大限度为20hz,有时候检测失败。
解决办法:根据官方手册该模块最大检测为20Hz,但是很可能并不能完全工作在工作极限范围,通过改变频率为10Hz 和 5Hz发现检测效果反而更佳。
Q3:在移植正点原子官方库的时候发现并不显示。
解决办法:后面通过对照原代码发现,正点原子在进行初始化的时候加了168的延迟,后面通过增加成功显示,可能是刚开始初始化的时候没有延迟导致初始化并不完全。
传送门:https://github.com/ChampDeLin/2022-DianSai-Unite-level.git
😃