分享21年电赛F题-智能送药小车-做题记录以及经验分享

这里写目录标题

    • 前言
    • 一、赛题分析
      • 1、车型选择
      • 2、巡线
        • 1、OpenMv循迹
        • 2、灰度循迹
      • 3、装载药品
      • 4、识别数字
      • 5、LED指示
      • 6、双车通信
      • 7、转向方案
        • 1、开环转向
        • 2、位置环速度环闭环串级转向
        • 3、MPU6050转向
    • 二、调试经验分享
      • 1、循迹
      • 2、识别数字
      • 3、转向
      • 4、双车通信
      • 5、逻辑处理
      • 6、心态问题
    • 总结
    • 开源链接

前言

自己是今年准备电赛的同学一名,电赛结束了,想把自己之前刷过的题目,通过这篇文章,来分享一波做这道题的思路和调试方法

自己在做之前的电赛题目时,也苦苦没有思路,不知道该怎么去下手,面对题目的要求和限制,应该如何去分析和实现

由于我们主要是准备小车相关的,大部分时间都用来刷电赛期间出现的小车题目了

其中包括21年F题——智能送药小车、22年C题——小车跟随系统、22年B题——自动泊车系统

可是今年电赛题目并没有小车,小车和无人机一起

可以看出电赛题目更贴近于综合,对个人能力的要求更高了

好了,废话不多说,我会在这段时间,将学习到的知识和做电赛题目的代码一步一步写出文章分享和教学,希望以后的同学可以参考学习,而不是盲目无从下手
在这里插入图片描述

一、赛题分析

赛题地图如下
在这里插入图片描述

1、车型选择

在做这道题目的时候,采用过好几种车型,并且采用过不同的方案,其中一种是

  • 四轮小车,前轮为舵机转向,后两轮为驱动轮
  • 三轮小车,前轮为万向轮或牛眼轮,后两轮为驱动轮

在实践的过程中,因为题目要求是转向90°,这种情况下三轮小车前轮为从动轮的更占优势,转向更为方便,而舵机前轮后轮驱动的车型转向就无法像三轮一般丝滑,略显笨重,但是也可以实现

2、巡线

巡线有两种方案

  • OpenMv巡线
  • 灰度循迹

这两种循迹方案都是差不多的,都是检测当前小车行进的位置的偏差的误差值来改变小车的行进方向

就是接收误差,将误差带入PID进行计算,得出巡线补偿,使小车始终保持在期望中心

1、OpenMv循迹

这部分我就不多写了,OpenMv有很多种方法,来巡线,这部分是队友负责,我大概也知道一些,比如二值化、色块追踪、模拟灰度传感器等

32这边始终接收到的数据就是一个偏差Err,根据这个Err来计算PID,调节PID参数,到达期巡线望值

最后这个计算出来的值一般也是串起来的,循线环输出带入速度环的输入,两轮不同的方向,一边+,一边-,调节PID参数即可丝滑循迹


float location_pid_realize(_pid *pid, float actual_val)
{
    /*    计算偏差    这里的偏差是指 巡线偏差 设定的巡线期望值 和 MV传回的巡线实际值 得偏差 */
    pid->err = pid->target_val - actual_val;

    pid->integral += pid->err; // 误差累积

    // if (pid->err >= 1000) // 积分限幅
    // {
    //     pid->err = 0;
    // }

    // if (huidu.output >= 550)
    //     huidu.output = 550;
    // if (huidu.output <= -600)
    //     huidu.output = -600;

    /*PID算法实现*/
    pid->actual_val = pid->Kp * pid->err + pid->Ki * pid->integral + pid->Kd * (pid->err - pid->err_last);

    /*误差传递*/
    pid->err_last = pid->err;

    /*返回当前实际值*/
    return pid->actual_val;
}


/**
 * @brief  巡线pid输出函数      后轮差速
 * @param  actual_val:实际值
 *	@note 	无
 * @retval 通过PID计算后的输出
 */
float OpenMV_location_pid_control(void)
{

    float Expect_Pwm = 0.0; // 当前控制值

    Pid_location = OpenMv_data1 * 10; // 获取巡线模块当前误差       后轮差速

    // Pid_location = 0;

    Expect_Pwm = location_pid_realize(&OpenMv_pid_track, Pid_location); // 进行 PID 计算

    //  #if defined(PID_ASSISTANT_EN)
    //    set_computer_value(SEND_FACT_CMD, CURVES_CH1, &actual_speed, 1);                // 给通道 1 发送实际值
    //  #endif

    return Expect_Pwm;
}

2、灰度循迹

这部分我使用的是五路灰度循迹,也是根据灰度管检测当前时刻的巡线变化来得出巡线偏差,带入PID进行计算,这和OpenMv巡线类似,我给出如何获得误差,大家就可以把误差带入PID进行计算了

最后这个计算出来的值一般也是串起来的,循线环输出带入速度环的输入,两轮不同的方向,一边+,一边-,调节PID参数即可丝滑循迹

/*
 * @Author: _oufen
 * @Date: 2023-04-15 09:06:56
 * @LastEditTime: 2023-07-27 19:50:46
 * @Description:  五路灰度传感器
 */

/* Includes -------------------------------------------------------------------------------------------------------------*/
#include "tracking.h"

/* Define -----------------------------------------------------------------------------------------------------------------*/
PID_Track pid_track;
/* Functions -----------------------------------------------------------------------------------------------------------------*/
/**
 * @brief 五路灰度GPIO初始化
 * @param None
 * @retval None
 * 分别使用到了 PB0/PB1/PB8/PB9/PA4
 */
void Tracking_Init(void)
{
    // GPIO初始化  上拉输入
    MY_GPIO_Init(GPIOB, GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_8 | GPIO_Pin_9, GPIO_Mode_IPD);
    MY_GPIO_Init(GPIOA, GPIO_Pin_4, GPIO_Mode_IPD);
}

/**
 * @brief 五路灰度GPIO初始化
 * @param None
 * @retval None
 * 分别使用到了 PB0/PB1/PB8/PB9/PA4
 * 计划使用中间三路进行巡线  最左端和最右端进行识别十字路口和丁字路口
 * 这里行驶的距离和巡线要有巡线补偿
 * 低电平有效输出   感应黑色输出高电平
 */
void Tracking_Control(void)
{
}

void PID_xun_init(void) // 五路灰度循迹pid初始化
{
    pid_track.Kp = 35.0;
    pid_track.Ki = 0.0;
    pid_track.Kd = 0.0;

    pid_track.err = 0.0;
    pid_track.err_last = 0.0;
    pid_track.integral = 0.0;
    pid_track.output = 0.0;
}

/**
 * @brief   循迹模块对应所有初始化
 * @param   None
 * @retval  None
 */
void Tracking_All_Init(void)
{
    Tracking_Init(); // 循迹GPIO初始化
    PID_xun_init();  // 循迹PID参数初始化
}

float PID_output(void)
{
    if ((L2 == 1) && (L1 == 0) && (M == 0) && (R1 == 0) && (R2 == 0)) // 10000
    {
        pid_track.err = -3;
    }

    else if ((L2 == 1) && (L1 == 1) && (M == 0) && (R1 == 0) && (R2 == 0)) // 11000
    {
        pid_track.err = -2;
    }

    else if ((L2 == 0) && (L1 == 1) && (M == 0) && (R1 == 0) && (R2 == 0)) // 01000
    {
        pid_track.err = -1.5;
    }

    else if ((L2 == 0) && (L1 == 1) && (M == 1) && (R1 == 0) && (R2 == 0)) // 01100
    {
        pid_track.err = -1;
    }

    else if ((L2 == 1) && (L1 == 1) && (M == 1) && (R1 == 0) && (R2 == 0)) // 11100
    {
        pid_track.err = -4;
    }

    else if ((L2 == 0) && (L1 == 0) && (M == 1) && (R1 == 0) && (R2 == 0)) // 00100
    {
        pid_track.err = 0;
    }

    else if ((L2 == 0) && (L1 == 0) && (M == 1) && (R1 == 1) && (R2 == 0)) // 00110
    {
        pid_track.err = 1;
    }

    else if ((L2 == 0) && (L1 == 0) && (M == 0) && (R1 == 1) && (R2 == 0)) // 00010
    {
        pid_track.err = 1.5;
    }

    else if ((L2 == 0) && (L1 == 0) && (M == 0) && (R1 == 1) && (R2 == 1)) // 00011
    {
        pid_track.err = 2;
    }

    else if ((L2 == 0) && (L1 == 0) && (M == 0) && (R1 == 0) && (R2 == 1)) // 00001
    {
        pid_track.err = 3;
    }

    else if ((L2 == 0) && (L1 == 0) && (M == 1) && (R1 == 1) && (R2 == 1)) // 00111
    {
        pid_track.err = 4;
    }

    // 十字路口

    else if ((L2 == 0) && (L1 == 0) && (M == 1) && (R1 == 0) && (R2 == 1)) // 00101
    {
        pid_track.err = 4;
    }

    else
        pid_track.err = 0;

    pid_track.integral += pid_track.err;

    pid_track.output = pid_track.Kp * pid_track.err + pid_track.Ki * pid_track.integral + pid_track.Kd * (pid_track.err - pid_track.err_last);

    pid_track.err_last = pid_track.err;

    return pid_track.output;
}

/*****************************************************END OF FILE*********************************************************/

3、装载药品

是否装载药品采用红外对管来实现

这部分我也不多说了,就是检测红外的高低电平,分别对应药品的状态

4、识别数字

识别数字,我们采用的是OpenMv4和OpenMv4Plus,病房前识别数字采用模板匹配,小车行进时采用神经网络匹配数字,效果很好

但是由于我不是负责这方面的,所以,只知道一个大概思路

5、LED指示

这个也没什么好说的,到达相应的状态时,点亮和熄灭LED就行了

6、双车通信

这里我们采用的方案有两种

  • 蓝牙通信模块,HC-05
  • Zigbee通信模块

其实,配置好了之后,这两种使用都是一样的,不同的方面就是Zigbee相较于蓝牙配置好配置一些,更方便一些

就一般的通信来说,蓝牙足够了

关于这方面的通信模块,一般肯定不会只发一个数据,这个时候就需要定义数据包协议,以保证发方和收方接收的数据的准确性

我自己定义的数据包协议和OpenMv协议相同

数据包为 b3 b3 data1 data2 data3 5b

其中b3 b3为帧头,5b为帧尾

下方是相关代码

/*
 * @Author: _oufen
 * @Date: 2023-07-06 15:05:38
 * @LastEditTime: 2023-07-07 16:55:46
 * @Description:
 */
#include "usart3.h"

int Bluetooth_Receive_Buff[6]; // 蓝牙接收数据
int16_t data1;                 // 三个有效数据位  第一个为  模式选择
int16_t data2;                 // 第二个为  停车标志
int16_t data3;                 // 第三个为   主车走的圈数

/**
 * @brief 	串口3初始化
 * @param 	无
 * @retval  无
 */
void uart3_init(u32 bound)
{
    // GPIO端口设置
    GPIO_InitTypeDef GPIO_InitStructure;
    USART_InitTypeDef USART_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); // 使能USART3,GPIOB时钟

    // USART3_TX   GPIOB2       RX 	PA2
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; // PA2
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; // 复用推挽输出
    GPIO_Init(GPIOB, &GPIO_InitStructure);          // 初始化GPIOB2

    // USART3_RX	GPIOB3   	TX  	PA3
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;            // PB11
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; // 浮空输入
    GPIO_Init(GPIOB, &GPIO_InitStructure);                // 初始化GPIOB3

    // USART3 NVIC 配置
    NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3; // 抢占优先级3
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;        // 子优先级3
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;           // IRQ通道使能
    NVIC_Init(&NVIC_InitStructure);                           // 根据指定的参数初始化VIC寄存器

    // USART 初始化设置
    USART_InitStructure.USART_BaudRate = bound;                                     // 串口波特率
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;                     // 字长为8位数据格式
    USART_InitStructure.USART_StopBits = USART_StopBits_1;                          // 一个停止位
    USART_InitStructure.USART_Parity = USART_Parity_No;                             // 无奇偶校验位
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; // 无硬件数据流控制
    USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;                 // 收发模式

    USART_Init(USART3, &USART_InitStructure);      // 初始化串口2
    USART_ITConfig(USART3, USART_IT_RXNE, ENABLE); // 开启串口接受中断
    USART_Cmd(USART3, ENABLE);                     // 使能串口2
}

/**
 * @brief 	串口3发送数据包
 * @param 	无
 * @retval  无
 */
void u3_sendData(u8 *str)
{

    u8 i = 0;
    for (i = 0; i < 2; i++) // b3 b3
    {
        USART_SendData(USART3, 0xb3);
        while (USART_GetFlagStatus(USART3, USART_FLAG_TC) != SET)
            ;
    }

    for (i = 0; i < 3; i++) // 发送三位有效位
    {
        USART_SendData(USART3, str[i]);
        while (USART_GetFlagStatus(USART3, USART_FLAG_TC) != SET)
            ;
    }

    USART_SendData(USART3, 0x5b); // 0x5b
    while (USART_GetFlagStatus(USART3, USART_FLAG_TC) != SET)
        ;
}

void Bluetooth_Data(void) // 处理蓝牙接收的数据
{
    data1 = Bluetooth_Receive_Buff[2];
    data2 = Bluetooth_Receive_Buff[3];
    data3 = Bluetooth_Receive_Buff[4];
}

/**
 * @brief 	串口3接收数据包  解析接收数据包   解析数据包   b3 b3 data1 data2 data3 5b
 * @param 	无
 * @retval  无
 */
void Bluetooth_Receive_Data(int16_t data)
{
    int i = 0;
    static u8 state = 0; // 初始化状态值

    if (state == 0 && data == 0xb3) // 第一个帧头 0xb3
    {
        state = 1;
        Bluetooth_Receive_Buff[0] = data;
    }
    else if (state == 1 && data == 0xb3) // 第二个帧头  0xb3
    {
        state = 2;
        Bluetooth_Receive_Buff[1] = data;
    }
    else if (state == 2) // 第一个有效数据
    {
        state = 3;
        Bluetooth_Receive_Buff[2] = data;
    }
    else if (state == 3) // 第二个有效数据
    {
        state = 4;
        Bluetooth_Receive_Buff[3] = data;
    }
    else if (state == 4) // 第三个有效数据
    {
        state = 5;
        Bluetooth_Receive_Buff[4] = data;
    }
    else if (state == 5) // 接收帧尾
    {
        if (data == 0x5b) // 是帧尾  直接回到初始状态
        {
            state = 0;
            Bluetooth_Receive_Buff[5] = data;
            Bluetooth_Data(); // 处理数据
        }
        else if (data != 0x5b) // 不是帧尾  所有数据置0
        {
            state = 0;
            for (i = 0; i < 6; i++)
            {
                Bluetooth_Receive_Buff[i] = 0;
            }
        }
    }
    else
    {
        state = 0;
        for (i = 0; i < 6; i++)
        {
            Bluetooth_Receive_Buff[i] = 0;
        }
    }
}

/**
 * @brief 	串口3接收中断
 * @param 	无
 * @retval  无
 */
void USART3_IRQHandler(void) // 串口2中断服务程序
{
    uint8_t ReceiveData;
    if (USART_GetITStatus(USART3, USART_IT_RXNE) != RESET) // 接收中断(接收到的数据必须是0x0d 0x0a结尾)
    {

        USART_ClearITPendingBit(USART3, USART_IT_RXNE); // 清除中断标志

        ReceiveData = USART_ReceiveData(USART3); // 接收的数据

        Bluetooth_Receive_Data(ReceiveData);

        Bluetooth_Data();

        // USART_SendData(USART3, ReceiveData);
        //  测试收发数据
        // if (ReceiveData == 58) // 当接收到0x3A后  接收数据灯亮    这个是基于最小系统板的测试程序
        // {
        //     MY_GPIO_Init(GPIOC, GPIO_Pin_13, GPIO_Mode_Out_PP);
        // }
    }
}

/********************************* 测试蓝牙接收数据 ****************************************************/

// 在主函数中显示  但是要先引入usart3头文件   注意要在主函数中初始化  连线连接无错误

// 在上位机中以定义协议格式发送 b3 b3 01 02 03 5b

// 接收数据在OLED上显示  如发送数据和接收数据相同 则表示接收协议无错误

// 下一步就是接收蓝牙主机的数据

// // 显示串口3接收数据  这是蓝牙从机  接收数据
// OLED_ShowNum(3, 8, data1, 2);  // 有效数据1
// OLED_ShowNum(3, 11, data2, 2); // 有效数据2
// OLED_ShowNum(3, 14, data3, 2); // 有效数据3

/*********************************** 结束 *************************************************************/

7、转向方案

转向方案可有很多种了嘞

在经过实际测试后,我总结出来了主要的三种转向方案

  • 开环转向
  • 位置环速度环串级转向
  • MPU6050转向

1、开环转向

就是电机IO,分别给0、1,然后给一个PWM,记录此时两轮的脉冲数相加的绝对值,当超过设定的脉冲时就停止

这时设定的脉冲,就可以是90°的脉冲和180度的脉冲,这需要自己一步一步的测试

我经过测试发现可行,并且还挺稳定的

下面给出相关代码

/**
 * @brief 转向   开环转向
 * @param 脉冲值;方向(左1 右2) pwm
 * @retval 无
 */
void Car_turn(int pluse, int mode, int pwm)
{

    while (1)
    {
        if (mode == 1) // 向左
        {
            Load_Motor_PWM(-pwm, pwm);
        }
        else if (mode == 2) // 向右
        {
            Load_Motor_PWM(pwm, -pwm);
        }
        if (pathLenth > pluse) // 达到预设角度
        {
            break;
        }
    }
    Load_Motor_PWM(0, 0); // 立马定住
    Motor_Left_DIR(STOP);
    Motor_Right_DIR(STOP);
}

void TIM6_IRQHandler(void) // 10ms
{
    if (TIM_GetITStatus(TIM6, TIM_IT_Update) != RESET)
    {
        LED_RED = !LED_RED; // 测试程序是否卡死
        TIM_ClearITPendingBit(TIM6, TIM_IT_Update);

        a = -Read_Encoder(2);
        b = Read_Encoder(3);

        pathLenth += MyABS(a) + MyABS(b); // 累积脉冲数

        // rpm   330脉冲

        //        speed1 = ((float)a * 60000.0) / 13200;
        //        speed2 = ((float)b * 60000.0) / 13200;
    }
}

可以看出当小车当前脉冲到达自己设置的脉冲时,小车就停止,这个时候小车差速,即可到达期望角度

// Car_turn(2000, 1, 1000); // 开环转弯 左转90
// Car_turn(2000, 2, 1000); // 开环转弯 右转90
// Car_turn(4000, 1, 1000); // 掉头180

2、位置环速度环闭环串级转向

位置环为外环,速度环为内环,位置环的输出即是速度环的输入

这个时候小车旋转的角度就是以小车两轮为直径的圆的四分之一的距离,这个距离如何计算呢

小车转一圈的脉冲我们知道,假定1040个脉冲

那么当前行进的脉冲数/小车转一圈的脉冲数,我们就是不是就知道了小车转了几圈,然后把这个×小车转一圈的周长,就知道了小车行进的距离

把小车行进距离转换为脉冲带入位置环,得到输出后赋值给速度环即可,使小车转90°的距离恒定,掉头的距离也恒定,转向就完成了

这部分代码有些多,我在文末会贴出代码,大家可以自行查看

3、MPU6050转向

通过yaw角的反馈,来改变小车的旋转方向,yaw角为90°时即转向90°,yaw角为180°时即掉头

这个也是串起来的PID,角度环的输出是速度环的输入

调节好PID参数后即可丝滑转向



/**
 * @brief  角度环pid输出函数
 * @param  actual_val:实际值  6050yaw
 *	@note 	无
 * @retval 通过PID计算后的输出
 */
float Angle_pid_realize(_pid *pid, float actual_val)
{
    /*    计算偏差    这里的偏差是指 巡线偏差 设定的巡线期望值 和 MV传回的巡线实际值 得偏差 */
    pid->err = pid->target_val - actual_val;

    if (pid->err > 180) // 防止小车转到180度时一直旋转的问题
        pid->err = pid->err - 360;
    if (pid->err < -180)
        pid->err = pid->err + 360;

    pid->integral += pid->err; // 误差累积

    // if (pid->err >= 1000) // 积分限幅
    // {
    //     pid->err = 0;
    // }

    // if (huidu.output >= 550)
    //     huidu.output = 550;
    // if (huidu.output <= -600)
    //     huidu.output = -600;

    /*PID算法实现*/
    pid->actual_val = pid->Kp * pid->err + pid->Ki * pid->integral + pid->Kd * (pid->err - pid->err_last);

    /*误差传递*/
    pid->err_last = pid->err;

    /*返回当前实际值*/
    return pid->actual_val;
}

/**
 * @brief  角度环pid输出函数
 * @param  actual_val:无
 *	@note 	无
 * @retval 通过PID计算后的输出
 */
float Angle_pid_control(void)
{

    float Expect_Pwm = 0.0; // 当前控制值

    // pid_speed1.actual_val = ((float)Param.UnitTime_Motor1Pluse * 1000.0 * 60.0) / (RESOULTION_TOTAL_RATIO * REDUCTION_RATIO * PID_PERIOD);

    // Pid_speed2 = ((float)Param.UnitTime_Motor2Pluse * 60000.0) / 17680;

    Pid_Actual_angle = KLM(yaw); // 实际值 为yaw  滤波后的数据

    Expect_Pwm = Angle_pid_realize(&pid_angle, Pid_Actual_angle); // 进行 PID 计算

    //  #if defined(PID_ASSISTANT_EN)
    //    set_computer_value(SEND_FACT_CMD, CURVES_CH1, &actual_speed, 1);                // 给通道 1 发送实际值
    //  #endif

    return Expect_Pwm;
}

二、调试经验分享

大家在实际做题的时候肯定会出现非常多的问题,不要慌,慢慢来,分析造成这种问题的原因是什么,从现象出发,再回到代码

等调出想要的代码的实际效果时,那一刻就会感觉之前的努力都值了

关于这道题,主要调试的有以下几部分

1、循迹

这个循迹,只要偏差值没错,补偿值两轮,一正一负,后面就是调节PID的问题了

2、识别数字

这个也是关键,如何准确识别数字,然后小车根据MV发过来的数据进行运动
识别数字的准确率也要有相应的保证,小车一切行进的基础都是在准确识别到数字的基础上的

3、转向

在调试的时候,多次发现小车不能完全转向90度,造成小车循迹出线,压黑线等
这个时候,要检查一下小车的硬件结构是否合理,程序是不是哪个地方写错了等

4、双车通信

通信部分,我建议的是,首先在上位机上模拟OpenMv或者蓝牙主机,发送定义的数据包协议,将接收到的数据在OLED上进行显示,如果接收到正确的数据,即可证明通信无问题,再去排除其他问题

5、逻辑处理

其实,在掌握了以上知识点后,我觉得逻辑还是很重要的,由自己亲身经历过,就是你知道如何实现单个功能,但是融合起来后就完全不行了,在比赛过程中是很浪费时间的,会造成时间的大量浪费,再加上身体的高强度运作和心态,就悲剧了

千万要在平时加强对程序逻辑的判断和处理,作者本人亲身经历,不要学我
比如这道题目,我首先是在OpenMv识别数字后,放上药品才可以小车行进去指定的病房送药,一二号病房没有太大难度,写死即可,后面的病房要根据OpenMv二次判断,来进行更正进病房,并要求返回药房。

这个逻辑就很重要,识别到Mv就返回一个标志位,左边为0x3A,右边即为0x3B等。发挥部分,从车等待主车卸载药品后取药,这个时候是主车卸载完药品后才给从车发行进标志,从车才可以动,防止撞车等

6、心态问题

不要被一种方案限制住了,比赛吗,有无限可能的,发挥不同的思路和方法,只要能实现就是好方法

我已经给大家以身试法过了,这次参加的电赛,由于没有车,只能做E题,赛前玩了玩二维云台追踪小球,大部分心思还是压在了车上,而这道题目比赛完后,再次复盘回想起这道题,是自己的思维局限把自己限定死了,导致越调越不行,前两天,想着闭环跑,可惜没有实现,后面自己稀里糊涂的实现了闭环,但是速度不可控,还是会超出黑线,误差很大,导致每分,自己也很急,就这样,第一次电赛也是最后一次就结束了

总归来说还是心态和实力,想的太多,每次力争最优解

电赛还是很考验人的,虽说可能结果不会怎么样,但是我觉得在准备电赛得过程中还是很棒得,一起每天早起得队友,大家互相配合,完成题目得喜悦,一起解决问题得思考,还是很有意义的。

总结

希望大家吸取教训,大家有什么不懂得问题也可以在评论区留言,看到了得话就肯定会恢复给大家解答,希望自己得绵薄之力可以帮助大家

大家一定要不断的加强训练,对学习的东西进行一个总结和汇总,加强控制题目的训练,而不是仅仅做小车,小车是一个载体,其内部原理也是控制的精细程度

开源链接

我会在这两天将自己这个开源链接的代码进行一个讲解

我采用了不同的主控,不同的巡线方案、大家可以自行访问,进行下载

下方为gitee开源链接,请大家点一个star,谢谢
oufen / 2021年电赛F题-智能送药小车

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/65305.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

RISC-V架构的演变

随着苹果基于ARM的硅和新的RISC-V CPU的推出&#xff0c;对于CPU开发来说&#xff0c;这是一个令人兴奋的时刻&#xff0c;尽管开发人员的旅程目前对后者来说有点坎坷。 我最喜欢的理论是&#xff0c;没有发生是孤独的&#xff0c;而只是重复了以前发生过的事情&#xff0c;也…

【数据结构与算法】平衡二叉树(AVL树)

平衡二叉树&#xff08;AVL树&#xff09; 给你一个数列{1,2,3,4,5,6}&#xff0c;要求创建二叉排序树&#xff08;BST&#xff09;&#xff0c;并分析问题所在。 BST 存在的问题分析&#xff1a; 左子树全部为空&#xff0c;从形式上看&#xff0c;更像一个单链表。插入速度…

Softing工业获得自动化产品安全开发流程认证

Softing工业获得了TV Sd颁发的IEC 62443-4-1产品安全开发流程认证。 &#xff08;IEC 62443-4-1认证确保网络安全&#xff09; 截至2023年6月&#xff0c;位于德国哈尔和纽伦堡的工厂以及罗马尼亚克卢日的Softing工业研发部门已获得IEC 62443-4-1:2018标准的认证。该认证流程由…

Webpack5新手入门简单配置

1.初始化项目 yarn init -y 2.安装依赖 yarn add -D webpack5.75.0 webpack-cli5.0.0 3.新建index.js 说明&#xff1a;写入下面的一句话 console.log("hello webpack"); 4.执行命令 说明&#xff1a;如果没有安装webpack脚手架就不能执行yarn webpack&#xff08…

k8sday02

第四章 实战入门 本章节将介绍如何在kubernetes集群中部署一个nginx服务&#xff0c;并且能够对其进行访问。 Namespace ​ Namespace是kubernetes系统中的一种非常重要资源&#xff0c;它的主要作用是用来实现多套环境的资源隔离或者多租户的资源隔离。 ​ 默认情况下&…

linux手动安装 golangci-lint-1.53.3-linux-386.rpm

首先还是 去下载对应的 rpm 包 https://github.com/golangci/golangci-lint/releases 然后上传到 服务器/usr/local 目录下 执行安装命令 sudo rpm -ivh golangci-lint-1.53.3-linux-386.rpm 查看版本 golangci-lint --version

Nginx与docker配置安装

目录&#xff1a; Nginx的安装配置&#xff1a; 1、安装依赖包&#xff1a; 2、下载Nginx安装包&#xff1a; 3、解压Nginx压缩包&#xff1a; 4、配置Nginx编译环境&#xff1a; 5、编译并安装Nginx&#xff1a; 6、安装完Nginx后&#xff0c;可以切换到Nginx的安装目录…

使用MIT Kerberos Ticket Manager在windows下浏览器访问hadoop页面

Author : Spinach | GHB Link : http://blog.csdn.net/bocai8058文章目录 前言准备配置说明安装Firefox浏览器安装MIT Kerberos Ticket Manager客户端配置krb5.ini文件配置MIT Kerberos Ticket Manager客户端配置Firefox浏览器代理参数 访问WebUI 前言 kerberos是一种计算机…

vite项目中使用@代表根路径

1.配置vite.config.ts import { defineConfig } from vite import vue from vitejs/plugin-vue import path from pathexport default defineConfig({plugins: [vue()],resolve: {alias:{: path.resolve(__dirname, src) }} })2.报错path和__dirname 找不到模块“path”或其相…

实力认证!TDengine 入选 Gartner 中国数据分析与人工智能技术成熟度曲线

近日&#xff0c;国际权威研究机构 Gartner 发布了《2023 年中国数据分析及人工智能技术成熟度曲线》&#xff08;即《Hype Cycle for Data, Analytics and AI in China, 2023》&#xff09;报告&#xff0c;TDengine 成功入选实时数据管理领域代表产品。 作为评估全球新技术成…

MySQL— 基础语法大全及操作演示!!

MySQL—— 基础 一、MySQL概述1.1 、数据库相关概念1.2 、MySQL 客户端连接1.3 、数据模型 二、SQL2.1、SQL通用语法2.2、SQL分类2.3、DDL2.4、DML2.5、DQL2.6、DCL 三、函数四、约束五、多表查询六、事务 一、MySQL概述 1.1 、数据库相关概念 数据库、数据库管理系统、SQL&a…

STM32自带的DSP库的滤波初体验(一)

最近在弄STM32自带的DSP库里的滤波&#xff0c;记录一下&#xff1a; arm_fir_instance_q15 instance_q15_S; #define NUM_TAPS 16 //滤波系数的个数 #define BLOCK_SIZE 32 q15_t firStateF32[BLOCK_SIZE NUM_TAPS]; q15_t Fir_Coeff[NUM_TAPS] {-79, -136, 312, 6…

Docker mysql+nacos单机部署

docker 网络创建 由于nacos需要访问mysql的数据&#xff0c;因此mysql容器和nacos容器之间需要进行通信。容器间通信有很多方式&#xff0c;在这里采用同一网络下的方式进行实现。因此需要创建网络。创建网络的命令如下&#xff1a; docker network create --driver bridge n…

【el-image图片查看时 样式穿透表格问题】

element-ui el-image图片查看 样式混乱 解决方式 ::v-deep(.el-table__cell) {position: static !important; // 解决el-image 和 el-table冲突层级冲突问题 }加个样式即可

Qt5.14.2+QtCreator+PDB 查看源码

1. 在Creator添加源码 2. 安装PDB文件 Qt下载时没有整合最新的PDB文件下载&#xff0c;如果没有安装PDB文件&#xff0c;即使安装了src也无法调试。 双击MaintenanceTool.exe->设置->资料档案库->临时资料档案库->添加按钮&#xff0c;添加如下下载源&#xff1a…

MongoDB:Unrecognized option: storage

MongoDB一直显示 Unrecognized option: storage try ‘mongod --help’ for more information 意思是我们配置的config文件出了问题。 说明&#xff1a;MongoDB采用的是YAML格式&#xff0c;所以我们只需要稍微改改就好。 在storage前面&#xff1a;没有空格 下面两行最前面…

机加工行业如何做好生产管理?

导 读 ( 文/ 2715 ) 机加工行业是制造业中的一个重要领域&#xff0c;它涉及将原材料通过机械设备进行切削、加工和加工成形的过程。 机械加工通常从原料开始&#xff0c;通过不断的切削或去除材料的过程&#xff0c;逐步将工件加工成所需的形状和尺寸。这个过程中&#xff0…

PHP实现保质期计算器

1.php实现保质期计算&#xff0c; 保质期日期可选&#xff0c;天 、月、年 2. laravel示例 /*** 保质期计算器* return void*/public function expirationDateCal(){$produce_date $this->request(produce_date); // 生产日期$warranty_date $this->reques…

TCP三次握手、四次握手过程,以及原因分析

TCP的三次握手和四次挥手实质就是TCP通信的连接和断开。 三次握手&#xff1a;为了对每次发送的数据量进行跟踪与协商&#xff0c;确保数据段的发送和接收同步&#xff0c;根据所接收到的数据量而确认数据发送、接收完毕后何时撤消联系&#xff0c;并建立虚连接。 四次挥手&…

机械厂工厂360全景展示拍摄制作,以便随时随地进行展示和更新

随着5G互联网技术的不断发展&#xff0c;线上全景虚拟展示已经成为了一种重要的展示方式。在工业领域中&#xff0c;厂区线上全景虚拟展示的应用也越来越广泛。 厂区线上vr全景虚拟展示是VR全景制作公司公司借助VR全景和web3d开发技术把企业的环境、研发、生产、产品、质检、仓…