分享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 发送实际值// #endifreturn 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;}elsepid_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 PA2GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; // PA2GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; // 复用推挽输出GPIO_Init(GPIOB, &GPIO_InitStructure); // 初始化GPIOB2// USART3_RX GPIOB3 TX PA3GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11; // PB11GPIO_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; // 抢占优先级3NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; // 子优先级3NVIC_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); // 初始化串口2USART_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); // 0x5bwhile (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 发送实际值// #endifreturn Expect_Pwm;
}
二、调试经验分享
大家在实际做题的时候肯定会出现非常多的问题,不要慌,慢慢来,分析造成这种问题的原因是什么,从现象出发,再回到代码
等调出想要的代码的实际效果时,那一刻就会感觉之前的努力都值了
关于这道题,主要调试的有以下几部分
1、循迹
这个循迹,只要偏差值没错,补偿值两轮,一正一负,后面就是调节PID的问题了
2、识别数字
这个也是关键,如何准确识别数字,然后小车根据MV发过来的数据进行运动
识别数字的准确率也要有相应的保证,小车一切行进的基础都是在准确识别到数字的基础上的
3、转向
在调试的时候,多次发现小车不能完全转向90度,造成小车循迹出线,压黑线等
这个时候,要检查一下小车的硬件结构是否合理,程序是不是哪个地方写错了等
4、双车通信
通信部分,我建议的是,首先在上位机上模拟OpenMv或者蓝牙主机,发送定义的数据包协议,将接收到的数据在OLED上进行显示,如果接收到正确的数据,即可证明通信无问题,再去排除其他问题
5、逻辑处理
其实,在掌握了以上知识点后,我觉得逻辑还是很重要的,由自己亲身经历过,就是你知道如何实现单个功能,但是融合起来后就完全不行了,在比赛过程中是很浪费时间的,会造成时间的大量浪费,再加上身体的高强度运作和心态,就悲剧了
千万要在平时加强对程序逻辑的判断和处理,作者本人亲身经历,不要学我
比如这道题目,我首先是在OpenMv识别数字后,放上药品才可以小车行进去指定的病房送药,一二号病房没有太大难度,写死即可,后面的病房要根据OpenMv二次判断,来进行更正进病房,并要求返回药房。
这个逻辑就很重要,识别到Mv就返回一个标志位,左边为0x3A,右边即为0x3B等。发挥部分,从车等待主车卸载药品后取药,这个时候是主车卸载完药品后才给从车发行进标志,从车才可以动,防止撞车等
6、心态问题
不要被一种方案限制住了,比赛吗,有无限可能的,发挥不同的思路和方法,只要能实现就是好方法
我已经给大家以身试法过了,这次参加的电赛,由于没有车,只能做E题,赛前玩了玩二维云台追踪小球,大部分心思还是压在了车上,而这道题目比赛完后,再次复盘回想起这道题,是自己的思维局限把自己限定死了,导致越调越不行,前两天,想着闭环跑,可惜没有实现,后面自己稀里糊涂的实现了闭环,但是速度不可控,还是会超出黑线,误差很大,导致每分,自己也很急,就这样,第一次电赛也是最后一次就结束了
总归来说还是心态和实力,想的太多,每次力争最优解
电赛还是很考验人的,虽说可能结果不会怎么样,但是我觉得在准备电赛得过程中还是很棒得,一起每天早起得队友,大家互相配合,完成题目得喜悦,一起解决问题得思考,还是很有意义的。
总结
希望大家吸取教训,大家有什么不懂得问题也可以在评论区留言,看到了得话就肯定会恢复给大家解答,希望自己得绵薄之力可以帮助大家
大家一定要不断的加强训练,对学习的东西进行一个总结和汇总,加强控制题目的训练,而不是仅仅做小车,小车是一个载体,其内部原理也是控制的精细程度
开源链接
我会在这两天将自己这个开源链接的代码进行一个讲解
我采用了不同的主控,不同的巡线方案、大家可以自行访问,进行下载
下方为gitee开源链接,请大家点一个star,谢谢
oufen / 2021年电赛F题-智能送药小车

相关文章:
分享21年电赛F题-智能送药小车-做题记录以及经验分享
这里写目录标题 前言一、赛题分析1、车型选择2、巡线1、OpenMv循迹2、灰度循迹 3、装载药品4、识别数字5、LED指示6、双车通信7、转向方案1、开环转向2、位置环速度环闭环串级转向3、MPU6050转向 二、调试经验分享1、循迹2、识别数字3、转向4、双车通信5、逻辑处理6、心态问题 …...
字符串统计-C语言/Java
描述 计算字符串中含有的不同字符的个数。字符在 ASCII 码范围内( 0~127 ,包括 0 和 127 ),换行表示结束符,不算在字符里。不在范围内的不作统计。多个相同的字符只计算一次。数据范围: 1≤n≤500 例如,对于字符串 ab…...
Styled-components,另一种css in js的方案
介绍 Styled-components是一种流行的CSS-in-JS库,它为React和React Native应用程序提供了一种优雅的方式来管理组件的样式。它的设计理念是将CSS样式与组件逻辑紧密绑定在一起,从而使样式在组件层级中作用更加清晰和可维护 使用 安装Styled-components…...
nginx部署以及反向代理多域名实现HTTPS访问
nginx部署以及反向代理多域名实现 1.nginx部署 1.1 编写nginx部署文件 docker-compose.yml version: 3 services: nginx:restart: always image: nginx:1.20container_name: nginx-mainports:- 80:80- 443:443volumes: # 基础配置- /opt/nginx_main/nginx-info/nginx.conf:/…...
24届近5年东华大学自动化考研院校分析
今天给大家带来的是东华大学控制考研分析 满满干货~还不快快点赞收藏 一、东华大学 学校简介 东华大学(Donghua University),地处上海市,是教育部直属全国重点大学,国家“双一流”、“211工程”建设高校…...
nacos伪集群启动成功,但是服务注册不上的问题
项目场景: nacos 伪集群启动成功,但是服务未注册上的问题: 问题描述 在学习nacos中,我买了一台阿里云服务器,在其上部署了nginx及三个nacos 端口分别是 8848 8868 8888 我按照正常的流程 解压nacos修改application.p…...
tidevice+appium在windows系统实施iOS自动化
之前使用iOS手机做UI自动化都是在Mac电脑上进行的,但是比较麻烦,后来看到由阿里开源的tidevice工具可以实现在windows上启动WDA,就准备试一下,记录一下过程。 tidevice的具体介绍可以参考一下这篇文章:tidevice 开源&…...
面试热题(LRU缓存)
请你设计并实现一个满足 LRU (最近最少使用) 缓存 约束的数据结构。 实现 LRUCache 类: LRUCache(int capacity) 以 正整数 作为容量 capacity 初始化 LRU 缓存int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1 …...
微信小程序开发【从0到1~入门篇】2023.08
一个小程序主体部分由三个文件组成,必须放在项目的根目录,如下: 文件必须作用app.js是小程序逻辑app.json是小程序公告配置app.wxss否小程序公告样式表 3. 小程序项目结构 一个小程序页面由四个文件组成,分别是: 文…...
P1398 [NOI2013] 书法家
题目描述 输入 #1 3 13 1 1 -1 -1 1 -1 1 1 1 -1 1 1 1 1 -1 1 -1 1 -1 1 -1 1 -1 -1 1 -1 1 -1 -1 1 1 -1 1 1 1 -1 1 1 1 输出 #1 24 输入 #2 3 13 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1…...
【构建卷积神经网络】
构建卷积神经网络 卷积网络中的输入和层与传统神经网络有些区别,需重新设计,训练模块基本一致 全连接层:batch784,各个像素点之间都是没有联系的。 卷积层:batch12828,各个像素点之间是有联系的。 impor…...
SSH 认证原理
SSH协议登录服务器: $ ssh userhost 主要有两种登录方式:第一种为密码口令登录,第二种为公钥登录 密码口令登录 通过密码进行登录,主要流程为: 1、客户端连接上服务器之后,服务器把自己的公钥传给客户端…...
基于DETR (DEtection TRansformer)开发构建MSTAR雷达影像目标检测系统
关于DETR相关的实践在之前的文章中很详细地介绍过,感兴趣的话可以自行移步阅读即可: 《DETR (DEtection TRansformer)基于自建数据集开发构建目标检测模型超详细教程》 《书接上文——DETR评估可视化》 基于MSTAR雷达影像数据开发构建目标检测系统&am…...
Java分布式微服务1——注册中心(Eureka/Nacos)
文章目录 基础知识注册中心Eureka注册中心与Ribbon负载均衡1、Eureka注册中心2、Eureka的搭建3、Eureka服务注册4、复制服务实例5、拉取服务6、Ribbon负载均衡的流程及Eureka规则调整:7、Ribbon负载均衡饥饿加载 Nacos注册中心1、服务端Nacos安装与启动2、客户端Nac…...
(文章复现)建筑集成光储系统规划运行综合优化方法matlab代码
参考文献: [1]陈柯蒙,肖曦,田培根等.一种建筑集成光储系统规划运行综合优化方法[J].中国电机工程学报,2023,43(13):5001-5012. 1.基本原理 本文建立的双层耦合模型内、外层分别对应求解容量配置与能量调度问题。外层模型设置光伏与储能容量备选集并将容量配置组合…...
【Redis】——RDB快照
Redis 是内存数据库,但是它为数据的持久化提供了两个技术,一个是AOF日志,另一个是RDB快照: AOF 文件的内容是操作命令;RDB 文件的内容是二进制数据。 RDB 快照就是记录某一个瞬间的内存数据,记录的是实际…...
微服务监控技术skywalking的部署与使用(亲测无坑)
微服务监控技术skywalking的部署与使用 1. 前期准备2. skywalking安装部署2.1 Java Agent2.2 apache/skywalking-oap-server2.3 apache/skywalking-ui 3. 项目启动4.效果展示 1. 前期准备 注:本篇文章采用docker部署,采用8.2.0版本,版本一定…...
DLA 神经网络的极限训练方法:gradient checkpointing
gradient checkpointing 一般来说,训练的过程需要保存中间结果(不管是GPU还是CPU)。前向传播根据输入(bottom_data)计算输出(top_data),后向传播由top_diff计算bottom_diff(如果某个变量打开梯度进行训练的话ÿ…...
python excel 操作
excel文件内容如下: 一、xlrd 读Excel 操作 1、打开Excel文件读取数据 filexlrd.open_workbook(filename)#文件名以及路径,如果路径或者文件名有中文给前面加一个 r 2、常用函数 (1)获取一个sheet工作表 table file.sheets(…...
记一次Linux启动Mysql异常解决
文章目录 第一步: netstat -ntlp 查看端口情况2、启动Mysql3、查看MySQL日志 tail -100f /var/log/mysqld.log4、查看磁盘占用情况:df -h5、思路小结 第一步: netstat -ntlp 查看端口情况 并没有发现3306数据库端口 2、启动Mysql service …...
浅谈 React Hooks
React Hooks 是 React 16.8 引入的一组 API,用于在函数组件中使用 state 和其他 React 特性(例如生命周期方法、context 等)。Hooks 通过简洁的函数接口,解决了状态与 UI 的高度解耦,通过函数式编程范式实现更灵活 Rea…...
Leetcode 3576. Transform Array to All Equal Elements
Leetcode 3576. Transform Array to All Equal Elements 1. 解题思路2. 代码实现 题目链接:3576. Transform Array to All Equal Elements 1. 解题思路 这一题思路上就是分别考察一下是否能将其转化为全1或者全-1数组即可。 至于每一种情况是否可以达到…...
JVM垃圾回收机制全解析
Java虚拟机(JVM)中的垃圾收集器(Garbage Collector,简称GC)是用于自动管理内存的机制。它负责识别和清除不再被程序使用的对象,从而释放内存空间,避免内存泄漏和内存溢出等问题。垃圾收集器在Ja…...
vue3 字体颜色设置的多种方式
在Vue 3中设置字体颜色可以通过多种方式实现,这取决于你是想在组件内部直接设置,还是在CSS/SCSS/LESS等样式文件中定义。以下是几种常见的方法: 1. 内联样式 你可以直接在模板中使用style绑定来设置字体颜色。 <template><div :s…...
python如何将word的doc另存为docx
将 DOCX 文件另存为 DOCX 格式(Python 实现) 在 Python 中,你可以使用 python-docx 库来操作 Word 文档。不过需要注意的是,.doc 是旧的 Word 格式,而 .docx 是新的基于 XML 的格式。python-docx 只能处理 .docx 格式…...
Robots.txt 文件
什么是robots.txt? robots.txt 是一个位于网站根目录下的文本文件(如:https://example.com/robots.txt),它用于指导网络爬虫(如搜索引擎的蜘蛛程序)如何抓取该网站的内容。这个文件遵循 Robots…...
大学生职业发展与就业创业指导教学评价
这里是引用 作为软工2203/2204班的学生,我们非常感谢您在《大学生职业发展与就业创业指导》课程中的悉心教导。这门课程对我们即将面临实习和就业的工科学生来说至关重要,而您认真负责的教学态度,让课程的每一部分都充满了实用价值。 尤其让我…...
OPENCV形态学基础之二腐蚀
一.腐蚀的原理 (图1) 数学表达式:dst(x,y) erode(src(x,y)) min(x,y)src(xx,yy) 腐蚀也是图像形态学的基本功能之一,腐蚀跟膨胀属于反向操作,膨胀是把图像图像变大,而腐蚀就是把图像变小。腐蚀后的图像变小变暗淡。 腐蚀…...
论文笔记——相干体技术在裂缝预测中的应用研究
目录 相关地震知识补充地震数据的认识地震几何属性 相干体算法定义基本原理第一代相干体技术:基于互相关的相干体技术(Correlation)第二代相干体技术:基于相似的相干体技术(Semblance)基于多道相似的相干体…...
LRU 缓存机制详解与实现(Java版) + 力扣解决
📌 LRU 缓存机制详解与实现(Java版) 一、📖 问题背景 在日常开发中,我们经常会使用 缓存(Cache) 来提升性能。但由于内存有限,缓存不可能无限增长,于是需要策略决定&am…...
