写在前面
我参加了浙江省那个机器人竞赛的购物赛,现在还是在比赛的前期准备阶段,在这个购物赛的规则当中,设计的机器人最基本的要求就是需要能够巡这些黑色背景下的白线,我用的是以串口传输数据的光电传感器,关于这个光电传感器的调试以及数据格式我已在另外一篇博客中进行了分享,传送门在下面:
CubeMX配置STM32F103C8T6芯片调试光电传感器
调试完光电传感器之后就把他装到车子的前部和后部,在这篇文章里我将分享STM32F427IIH6芯片(RoboMaster A型)上运行的FreeRTOS系统通过串口+DMA方式接收传感器数据并且根据反馈数据调整机器人姿态的方法
场地图如下:
机器人后面的光电传感器:
机器人前面的光电传感器:
流程概述
上图是数据感知和姿态调整的大致流程图,接下来我将结合程序来为大家进行介绍
程序介绍
首先最重要的就是数据的感知了,在整个工程中总共跑了2个线程,分别是StartDefaultTask和StartCalcucrossTask,StartCalcucrossTask里面主要是数白线的交叉点,这个我会在后续的博客中进行分享,StartDefaultTask完成了机器人底盘的大部分功能,包括开发板和树莓派的通信,CAN总线驱动c620电调,接收光电传感器的反馈数据等,因为芯片设置的时候是用串口+DMA接收光电传感器数据,因此在初始化串口的时候设置了DMA中断,一旦开发板接受到了光电传感器反馈的数据之后,开发板的DMA模块会自动把数据写到对应的模块里面,这在程序里面对应的回调函数是下列代码段:
uint8_t RxLenHi, RxLenlo;
void HAL_UART_IdleCpltCallback(UART_HandleTypeDef *huart)
{//uart6Rx和uart7Rx是2个数组,分别用来存储前后光电传感器传回来的数据
__HAL_UART_CLEAR_IDLEFLAG(huart);
if( huart == &huart8)
{
test3++;
HAL_UART_Receive_DMA(&huart8, uart8Rx, buffer_size);
uart8RxLength = buffer_size-__HAL_DMA_GET_COUNTER(&hdma_uart8_rx);
if(uart8Rx[0]=='@'
&&uart8Rx[1]=='c'
&&uart8Rx[2]=='m'
&&uart8Rx[3]=='d')
{
if(!SERDEB_CmdValid())
SERDEB_PushCmd(uart8Rx, uart8RxLength);
}
__HAL_DMA_DISABLE(&hdma_uart8_rx);
HAL_GPIO_TogglePin(GPIOG,GPIO_PIN_8);
}
if( huart == &huart7)
{
__HAL_DMA_DISABLE(&hdma_uart7_rx);
HAL_UART_Receive_DMA(&huart7, uart7Rx, buffer_size);
__HAL_DMA_DISABLE(&hdma_uart7_rx);
}
if( huart == &huart6)
{
__HAL_DMA_DISABLE(&hdma_usart6_rx);
HAL_UART_Receive_DMA(&huart6, uart6Rx, buffer_size);
__HAL_DMA_DISABLE(&hdma_usart6_rx);
}
}
上述代表表示了uart6和uart7就是分别接收前后2个光电传感器数据的串口,uart8是跟树莓派通信的串口,暂时不去管他。可以很明显的看到uart6和uart7都是通过DMA方式将数据从外围设备搬运到芯片里的 在接收到数据之后就是对于数据的处理了,让我们先来看一下这个示意图
虽然一个光电传感器会传回来16个循迹灯的数据,但是并不是所有的都是有用的数据,因为我只选了4个灯做为我纠正机器人姿态的依据
上面5个灯最里面那个都是不用的,而是通过S1和S2两组灯来进行机器人的姿态纠正,接下来我通过举例子来进行说明
当车子向左偏离航线的时候,S1左边的灯会亮,S2右边的灯会灭,此时左轮加速,右轮减速,后面2个轮速度不变,以纠正姿态
当车子向右偏离航线的时候,S2左边的灯会灭,S1右边的灯会亮,此时右轮加速,左轮减速,后轮2个轮速度不变,以纠正姿态
那么看官可能会想,看起来只需要一个灯就可以看到机器人是否偏离航线了,为啥要用2个灯呢?其实是因为涉及到了加减速的问题,加减速的数值是多少呢?这就通过S1和S2的灯的亮灭状态来进行确定了,具体计算方式看程序:
int set_speed(int motor_number1 ,int *offset_1,int motor_number2 , int *offset_2)
{
return (speed+offset_1[motor_number1]*speed_add1
+offset_2[motor_number2]*speed_add2)
-speed_cut*(offset_2[motor_number2]);
}
这个函数的调用时这样的:set_speed(motor_b_r,mid_sen1,motor_b_l,mid_sen2); 那么上述这些变量是从哪里来的呢?
mid_sen1[motor_f_r]=((~uart7Rx[1])&0x40)>>6;
mid_sen1[motor_f_l]=((~uart7Rx[1])&0x10)>>4;
mid_sen1[motor_b_l]=((~uart6Rx[0])&0x08)>>3;
mid_sen1[motor_b_r]=((~uart6Rx[0])&0x02)>>1;
mid_sen2[motor_f_r]=(uart7Rx[1]&0x80)>>7;
mid_sen2[motor_f_l]=(uart7Rx[1]&0x08)>>3;
mid_sen2[motor_b_l]=(uart6Rx[0]&0x10)>>4;
mid_sen2[motor_b_r]=(uart6Rx[0]&0x01);
uartRx6和uartRx7就是接收光电传感器的数据的数组,uartRx6[0]是后面那个光电传感器数据的低8位,uart6Rx[1]是后面那个光电传感器数据的高8位,uart7Rx是前面的光电传感器,数据格式相同。mid_sen1就是S1,mid_sen2就是S2
效果展示
(づ ̄3 ̄)づ╭❤~一键三连,这次一定(๑•̀ㅂ•́)و✧