电赛2024年H题智能小车基于MSPM0G3507主控MCU(利用8路灰度加上MPU6050的解决方式)

本文主要是介绍电赛2024年H题智能小车基于MSPM0G3507主控MCU(利用8路灰度加上MPU6050的解决方式),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一.前言

        前段时间,激烈的电赛刚刚结束,很荣幸啊,也是十分的不甘心,本次的湖北赛区H题只拿到了一个省二,看最终的排名,在H题中我们离省一也就差几名。但是整个比赛已经过去了,现在不甘与不舍,也没有任何意义了,只有接收这一现实了。

        当时我们整个比赛要求一二三都完美完成,要求四能够十分稳定的跑下来但是跑完四圈得花1分30秒,大概是跑十次才死一两次的样子(毕竟比赛,谁也没办法保证车能够百分百的完美跑完发挥一)。后面也想过提升速度,但是结果都是不尽人意,要么速度提上去了,但是稳定性不强,要么稳定性可以,速度提不上去,最终也来到了四天三夜的最后一天,当时我和另外两个队友商量,觉得稳定还是最主要,所有就抛弃了速度快但是不稳定的所有结果,就留下了最稳定的一个。但是最后直到测试的时候帮我三个人看懵了,看别人武大的发挥一跑完只花了30多秒,当时我们十分紧张的。他们一个发挥一有两个模式,一个是速度慢,但是非常稳定,一个是跑起来不怎么稳定,就车身刚好在线上的那种,但是速度出奇的快。看到这里我三个人直接是慌了,不知道该 这么进行下一步了。

        好在我们三个人十分快速的调制状态,我们 三个人互相鼓励着不管别人怎么样,做好自己就行了,然后我们一项一项的保证每一项最后测试的时候都是一次过的,这也证明我们整个解决方案是十分稳定的。但是今年这个题目光稳定是不够的,或者说是最基础的。最后也十分遗憾的没有拿到省一,只拿了一个省二。

二.具体硬件组成

       本项目由微处理器MSPM0G3507,编码器电机驱动,8路灰度传感器指示线巡线单元,MPU6050六轴传感器无线直行单元,OLED显示人机互动单元,红色LED及蜂鸣器声光提示单元构成。系统运行由两部分组成:自动行驶小车的无指示线直行控制部分和有指示线弯道行驶的实时转向控制部分,小车的无指示线直行控制部分,由MPU6050六轴传感器获得小车姿态的偏航角,通过获得的实时偏航角,编码器的实际值,构成双反馈串行PID实时监控调制小车实际运行轨迹的目的。指示线弯道行驶控制部分,由8路灰度传感器获得小车在指示线上实时运动方位,输出模拟量,通过模拟量和编码器脉冲值构成串行闭环PID实时控制小车投影位于指示线方位的目的。具体的实物图如下:

俯视图

左视图

俯视图

        具体的实物连接图就如上所示了,其实本项目对硬件部分的要求不高,只要能够搭建起来,跑的时候结构稳定,不跑几次容易螺丝松动那种问题,基本就行了,而且本项目对灰度,mpu6050安装的位置不是十分的严格,不管装在那个地方都能够将其做出来的。

三.理论分析部分

        整个项目的准备工作做完之后(硬件搭好了)就开始想着该如何实现,第一个就是有线的时候怎么走?第二个是无线的时候如何走直线?

        首先解决第一个问题:有线的时候直接利用灰度返回的模拟量,得到小车的一个大概位置,也就是偏线的左边还是右边,这样就可以反馈给主控芯片了,主控芯片收到实时反馈之后就要进行处理,也就是用到pid实时调控(如果小车偏左就减少pwm让其回到中心)利用一个10ms定时器去计算实际的小车位置和目标位置(直线的正中心)的差值然后把差值补偿给小车这样就可以达到一个动态平衡的一个状态。也就解决了巡线的理论问题。

        其次就是解决没有线走直线的问题,这个就用到MPU6050陀螺仪了,这里理论上是和上面的一样的,只不过不同的一点是上面利用灰度反射黑线来识别小车的位置,而这里是利用六轴陀螺仪作为参考形成一个闭环串行的pid调制过程。这里实际上只用到一个个轴也就是水平面的x轴,偏航角和其角速度。

        这里两个部分的处理都是有编码器进行速度反馈调制的,也就是第一个环,然后加上后面的两种方式最后就可以达到有线巡线行驶,无线直线行驶。具体的mpu6050和灰度的利用这里就不细讲。

四.代码实现

电机部分

#include "motor.h"
int PWM_MAX =8000;
int PWM_MIN =-8000;void Limit(int *motoA,int *motoB)
{if(*motoA>PWM_MAX)*motoA=PWM_MAX;if(*motoA<PWM_MIN)*motoA=PWM_MIN;if(*motoB>PWM_MAX)*motoB=PWM_MAX;if(*motoB<PWM_MIN)*motoB=PWM_MIN;
}int abs(int p)
{int q;q=p>0?p:(-p);return q;
}void Load(int moto1,int moto2)
{//1.研究正负号,对应正反转if(moto1<0)	AIN1_OUT(1),AIN2_OUT(0);//正转else 				AIN1_OUT(0),AIN2_OUT(1);//反转//2.研究PWM值DL_TimerG_setCaptureCompareValue(PWM_A_INST ,abs(moto1),GPIO_PWM_A_C0_IDX);if(moto2<0)BIN1_OUT(1),BIN2_OUT(0);else 				BIN1_OUT(0),BIN2_OUT(1);	//2.研究PWM值DL_TimerG_setCaptureCompareValue(PWM_B_INST ,abs(moto2),GPIO_PWM_B_C0_IDX);
}

pid调制

#include "control.h"PID_Velocit_TypeDef L_pid, R_pid;
PID_Turn_TypeDef Turn_pid;
void PID_Init(void)
{
L_pid.Kp=80;
L_pid.Kd=0;
L_pid.Ki=0.03;
L_pid.Err=0;
L_pid.Err_Last=0;
L_pid.Integral=0;
L_pid.PID_out=0;
L_pid.Target=0;
R_pid.Kp=75;
R_pid.Kd=0;
R_pid.Ki=0.03;
R_pid.Err=0;
R_pid.Err_Last=0;
R_pid.Integral=0;
R_pid.PID_out=0;
R_pid.Target=0;
}
void PID_turn_Init(PID_Turn_TypeDef *PID)
{
PID->Kp=-5;
PID->Kd=-10;
PID->Ki=0;
PID->Err=0;
PID->Err_Last=0;
PID->Integral=0;
PID->PID_out=0;
PID->Target=0;
}float pid_Velocity(PID_Velocit_TypeDef *PID,float CurrentValue)
{PID->Err =  PID->Target - CurrentValue;PID->Integral += PID->Err;  /*积分限幅*/if(PID->Integral > 2000){PID->Integral = 2000;}if(PID->Integral < -2000){PID->Integral = -2000;}    PID->PID_out = PID->Kp * PID->Err 										/*比例*/+ PID->Ki * PID->Integral  								/*积分*/+ PID->Kd * (PID->Err - PID->Err_Last);						/*微分*/PID->Err_Last = PID->Err;return PID->PID_out;
}float pid_Turn(PID_Turn_TypeDef *PID,float CurrentValue)
{PID->Err =  PID->Target - CurrentValue;PID->Integral += PID->Err;  /*积分限幅*/if(PID->Integral > 2000){PID->Integral = 2000;}if(PID->Integral < -2000){PID->Integral = -2000;}    PID->PID_out = PID->Kp * PID->Err 										/*比例*/+ PID->Ki * PID->Integral  								/*积分*/+ PID->Kd * (PID->Err - PID->Err_Last);						/*微分*/PID->Err_Last = PID->Err;return PID->PID_out;
}
void set_Velocity_Target(PID_Velocit_TypeDef *PID,float target)
{PID->Target = target;
}void set_Turn_Target(PID_Turn_TypeDef *PID,float target)
{PID->Target = target;}

灰度部分

#include "gw.h"
#include "delay.h"__STATIC_INLINE uint8_t DL_GPIO_readPin(GPIO_Regs* gpio, uint32_t pin)  
{  // 假设pin是一个只设置了单个位的位掩码  // 检查该引脚在DIN31_0寄存器中是否被设置  if (gpio->DIN31_0 & pin) {  return 1; // 或 (uint8_t)Bit_SET,如果Bit_SET已定义  } else {  return 0; // 或 (uint8_t)Bit_RESET,如果Bit_RESET已定义  }  
}uint8_t gw_gray_serial_read(int gpio_clk,int gpio_dat)
{uint8_t ret = 0;uint8_t i;for (i = 0; i < 8; ++i){/* 输出时钟下降沿 */DL_GPIO_clearPins(SW_SPI_PORT, gpio_clk);delay_us(5); //有外部上拉源(4k ~ 10k电阻) 可不加此行ret |= DL_GPIO_readPin(SW_SPI_PORT, gpio_dat) << i;/* 输出时钟上升沿,让传感器更新数据*/DL_GPIO_setPins(SW_SPI_PORT, gpio_clk);/* 延迟需要在5us左右 */delay_us(5);}return ret;
}void track(void)                           
{if(distance==0xe7)        //1110 0111 黑零白一 识别黑线在中央{error=0;}else if(distance==0xef)   //1110 1111 稍偏右{error=5; }else if(distance==0xf7)    //1111 0111 稍偏左{error=-5; }else if(distance==0xcf)   //1100 1111 中度偏右{error=10; }else if(distance==0xf3)   //1111 0011 中度偏左{error=-10; }else if(distance==0xdf)   //1101 1111 高度偏右{error=15;}else if(distance==0xfb)   //1111 1011 高度偏左{error=-15; }else if(distance==0x9f)   //1001 1111  高2度偏右{error=20; }else if(distance==0xf9)   //1111 1001  高2度偏左{error=-20; }else if(distance==0xbf)   //1011 1111  高3度偏右{error=25; }else if(distance==0xfd)   //1111 1101  高三度偏左{error=-25; }else if(distance==0x3f)   //0011 1111  高4度偏右{error=30; }else if(distance==0xfc)   //1111 1100  高4度偏左{error=-30; }else if(distance==0x7f)   //0111 1111  高五度偏右{error=35; }	else if(distance==0xfd)    //1111 1110  高六度偏左{error=-35; }else if (distance==0xff)                       //0000000  走到终点了{error=100;}
}

mpu6050部分

#include "bsp_mpu6050.h"
#include "stdio.h"
#include "delay.h"
enum I2cControllerStatus {I2C_STATUS_IDLE = 0,I2C_STATUS_TX_STARTED,I2C_STATUS_TX_INPROGRESS,I2C_STATUS_TX_COMPLETE,I2C_STATUS_RX_STARTED,I2C_STATUS_RX_INPROGRESS,I2C_STATUS_RX_COMPLETE,I2C_STATUS_ERROR,
} gI2cControllerStatus;uint32_t gTxLen, gTxCount, gRxCount, gRxLen;
uint8_t gTxPacket[128];
uint8_t gRxPacket[128];char MPU6050_WriteReg(uint8_t addr,uint8_t regaddr,uint8_t num,uint8_t *regdata)
{uint16_t i;gI2cControllerStatus = I2C_STATUS_IDLE;gTxLen = num+1;gTxPacket[0] = regaddr;for(i=1; i<=num; i++){gTxPacket[i] = (uint8_t)regdata[i-1];}gTxCount = DL_I2C_fillControllerTXFIFO(I2C_MPU6050_INST, &gTxPacket[0], gTxLen);if (gTxCount < gTxLen) {DL_I2C_enableInterrupt(I2C_MPU6050_INST, DL_I2C_INTERRUPT_CONTROLLER_TXFIFO_TRIGGER);} else {DL_I2C_disableInterrupt(I2C_MPU6050_INST, DL_I2C_INTERRUPT_CONTROLLER_TXFIFO_TRIGGER);}gI2cControllerStatus = I2C_STATUS_TX_STARTED;while (!(DL_I2C_getControllerStatus(I2C_MPU6050_INST) & DL_I2C_CONTROLLER_STATUS_IDLE));DL_I2C_startControllerTransfer(I2C_MPU6050_INST, addr, DL_I2C_CONTROLLER_DIRECTION_TX, gTxLen);while ((gI2cControllerStatus != I2C_STATUS_TX_COMPLETE) && (gI2cControllerStatus != I2C_STATUS_ERROR));while (DL_I2C_getControllerStatus(I2C_MPU6050_INST) & DL_I2C_CONTROLLER_STATUS_BUSY_BUS);while (!(DL_I2C_getControllerStatus(I2C_MPU6050_INST) & DL_I2C_CONTROLLER_STATUS_IDLE));delay_cycles(1000);return 0;
}char MPU6050_ReadData(uint8_t addr, uint8_t regaddr,uint8_t num,uint8_t* Read)
{uint8_t data[2], i;data[0] = regaddr;gI2cControllerStatus = I2C_STATUS_IDLE;DL_I2C_fillControllerTXFIFO(I2C_MPU6050_INST, &data[0], 1);DL_I2C_disableInterrupt(I2C_MPU6050_INST, DL_I2C_INTERRUPT_CONTROLLER_TXFIFO_TRIGGER);gI2cControllerStatus = I2C_STATUS_TX_STARTED;while (!(DL_I2C_getControllerStatus(I2C_MPU6050_INST) & DL_I2C_CONTROLLER_STATUS_IDLE));DL_I2C_startControllerTransfer(I2C_MPU6050_INST, addr, DL_I2C_CONTROLLER_DIRECTION_TX, 1);while ((gI2cControllerStatus != I2C_STATUS_TX_COMPLETE) && (gI2cControllerStatus != I2C_STATUS_ERROR));while (DL_I2C_getControllerStatus(I2C_MPU6050_INST) & DL_I2C_CONTROLLER_STATUS_BUSY_BUS);while (!(DL_I2C_getControllerStatus(I2C_MPU6050_INST) & DL_I2C_CONTROLLER_STATUS_IDLE));// delay_cycles(1000);gRxLen = num;gRxCount = 0;gI2cControllerStatus = I2C_STATUS_RX_STARTED;DL_I2C_startControllerTransfer(I2C_MPU6050_INST, addr, DL_I2C_CONTROLLER_DIRECTION_RX, gRxLen);while (gI2cControllerStatus != I2C_STATUS_RX_COMPLETE);while (DL_I2C_getControllerStatus(I2C_MPU6050_INST) &DL_I2C_CONTROLLER_STATUS_BUSY_BUS);for(i=0; i<num; i++){Read[i] = gRxPacket[i];}return 0;
}/******************************************************************* 函 数 名 称:MPU_Set_Gyro_Fsr* 函 数 说 明:设置MPU6050陀螺仪传感器满量程范围* 函 数 形 参:fsr:0,±250dps;1,±500dps;2,±1000dps;3,±2000dps* 函 数 返 回:0,设置成功  其他,设置失败* 作       者:LC* 备       注:无
******************************************************************/
uint8_t MPU_Set_Gyro_Fsr(uint8_t fsr)
{uint8_t tmp[2];tmp[0] = fsr<<3;return MPU6050_WriteReg(0x68,MPU_GYRO_CFG_REG,1,tmp); //设置陀螺仪满量程范围
}    /******************************************************************* 函 数 名 称:MPU_Set_Accel_Fsr* 函 数 说 明:设置MPU6050加速度传感器满量程范围* 函 数 形 参:fsr:0,±2g;1,±4g;2,±8g;3,±16g* 函 数 返 回:0,设置成功  其他,设置失败* 作       者:LC* 备       注:无
******************************************************************/
uint8_t MPU_Set_Accel_Fsr(uint8_t fsr)
{uint8_t tmp[2];tmp[0] = fsr<<3;return MPU6050_WriteReg(0x68,MPU_ACCEL_CFG_REG,1,tmp); //设置加速度传感器满量程范围  
}/******************************************************************* 函 数 名 称:MPU_Set_LPF* 函 数 说 明:设置MPU6050的数字低通滤波器* 函 数 形 参:lpf:数字低通滤波频率(Hz)* 函 数 返 回:0,设置成功  其他,设置失败* 作       者:LC* 备       注:无
******************************************************************/
uint8_t MPU_Set_LPF(uint16_t lpf)
{uint8_t data=0;uint8_t tmp[2];if(lpf>=188)data=1;else if(lpf>=98)data=2;else if(lpf>=42)data=3;else if(lpf>=20)data=4;else if(lpf>=10)data=5;else data=6; tmp[0] = data;return data=MPU6050_WriteReg(0x68,MPU_CFG_REG,1,tmp);//设置数字低通滤波器  
}
/******************************************************************* 函 数 名 称:MPU_Set_Rate* 函 数 说 明:设置MPU6050的采样率(假定Fs=1KHz)* 函 数 形 参:rate:4~1000(Hz)  初始化中rate取50* 函 数 返 回:0,设置成功  其他,设置失败* 作       者:LC* 备       注:无
******************************************************************/
uint8_t MPU_Set_Rate(uint16_t rate)
{uint8_t data;uint8_t tmp[2];if(rate>1000)rate=1000;if(rate<4)rate=4;data=1000/rate-1;tmp[0] = data;data=MPU6050_WriteReg(0x68,MPU_SAMPLE_RATE_REG,1,tmp);        //设置数字低通滤波器return MPU_Set_LPF(rate/2);            //自动设置LPF为采样率的一半
}/******************************************************************* 函 数 名 称:MPU6050ReadGyro* 函 数 说 明:读取陀螺仪数据* 函 数 形 参:陀螺仪数据存储地址 * 函 数 返 回:无* 作       者:LC* 备       注:无
******************************************************************/
void MPU6050ReadGyro(short *gyroData)
{uint8_t buf[6];uint8_t reg = 0;//MPU6050_GYRO_OUT = MPU6050陀螺仪数据寄存器地址//陀螺仪数据输出寄存器总共由6个寄存器组成,//输出X/Y/Z三个轴的陀螺仪传感器数据,高字节在前,低字节在后。//每一个轴16位,按顺序为xyzreg = MPU6050_ReadData(0x68,MPU6050_GYRO_OUT,6,buf);if( reg == 0 ){gyroData[0] = (buf[0] << 8) | buf[1];gyroData[1] = (buf[2] << 8) | buf[3];gyroData[2] = (buf[4] << 8) | buf[5];}
}/******************************************************************* 函 数 名 称:MPU6050ReadAcc* 函 数 说 明:读取加速度数据* 函 数 形 参:加速度数据存储地址 * 函 数 返 回:无* 作       者:LC* 备       注:无
******************************************************************/
void MPU6050ReadAcc(short *accData)
{uint8_t buf[6];uint8_t reg = 0;//MPU6050_ACC_OUT = MPU6050加速度数据寄存器地址//加速度传感器数据输出寄存器总共由6个寄存器组成,//输出X/Y/Z三个轴的加速度传感器值,高字节在前,低字节在后。reg = MPU6050_ReadData(0x68, MPU6050_ACC_OUT, 6, buf);if( reg == 0){accData[0] = (buf[0] << 8) | buf[1];accData[1] = (buf[2] << 8) | buf[3];accData[2] = (buf[4] << 8) | buf[5];}
}/******************************************************************* 函 数 名 称:MPU6050_GetTemp* 函 数 说 明:读取MPU6050上的温度* 函 数 形 参:无* 函 数 返 回:温度值单位为℃* 作       者:LC* 备       注:温度换算公式为:Temperature = 36.53 + regval/340
******************************************************************/
float MPU6050_GetTemp(void)
{short temp3;uint8_t buf[2];float Temperature = 0;MPU6050_ReadData(0x68,MPU6050_RA_TEMP_OUT_H,2,buf); temp3= (buf[0] << 8) | buf[1];        Temperature=((double) temp3/340.0)+36.53;return Temperature;
}/******************************************************************* 函 数 名 称:MPU6050ReadID* 函 数 说 明:读取MPU6050的器件地址* 函 数 形 参:无* 函 数 返 回:0=检测不到MPU6050   1=能检测到MPU6050* 作       者:LC* 备       注:无
******************************************************************/
uint8_t MPU6050ReadID(void)
{unsigned char Re[2] = {0};//器件ID寄存器 = 0x75printf("mpu=%d\r\n",MPU6050_ReadData(0x68,0X75,1,Re)); //读器件地址if (Re[0] != 0x68) {printf("检测不到 MPU6050 模块");return 1;} else{printf("MPU6050 ID = %x\r\n",Re[0]);return 0;}return 0;
}/******************************************************************* 函 数 名 称:MPU6050_Init* 函 数 说 明:MPU6050初始化* 函 数 形 参:无* 函 数 返 回:0成功  1没有检测到MPU6050* 作       者:LC* 备       注:无
******************************************************************/
char MPU6050_Init(void)
{uint8_t tmp[2];delay_ms(10);//复位6050tmp[0] = 0x80;MPU6050_WriteReg(0x68,MPU6050_RA_PWR_MGMT_1, 1, tmp);delay_ms(100);//电源管理寄存器//选择X轴陀螺作为参考PLL的时钟源,设置CLKSEL=001tmp[0] = 0x00;MPU6050_WriteReg(0x68,MPU6050_RA_PWR_MGMT_1,1, tmp);MPU_Set_Gyro_Fsr(3);    //陀螺仪传感器,±2000dpsMPU_Set_Accel_Fsr(0);   //加速度传感器,±2gMPU_Set_Rate(50);                MPU6050_WriteReg(0x68,MPU_INT_EN_REG , 1,tmp);        //关闭所有中断MPU6050_WriteReg(0x68,MPU_USER_CTRL_REG,1,tmp);        //I2C主模式关闭MPU6050_WriteReg(0x68,MPU_FIFO_EN_REG,1,tmp);                //关闭FIFOtmp[0] = 0x80;MPU6050_WriteReg(0x68,MPU_INTBP_CFG_REG,1,tmp);        //INT引脚低电平有效if( MPU6050ReadID() == 0 )//检查是否有6050{       tmp[0] = 0x01;MPU6050_WriteReg(0x68,MPU6050_RA_PWR_MGMT_1, 1,tmp);//设置CLKSEL,PLL X轴为参考tmp[0] = 0x00;MPU6050_WriteReg(0x68,MPU_PWR_MGMT2_REG, 1,tmp);//加速度与陀螺仪都工作MPU_Set_Rate(50);        return 1;}return 0;
}void I2C_MPU6050_INST_IRQHandler(void)
{switch (DL_I2C_getPendingInterrupt(I2C_MPU6050_INST)) {case DL_I2C_IIDX_CONTROLLER_RX_DONE:gI2cControllerStatus = I2C_STATUS_RX_COMPLETE;break;case DL_I2C_IIDX_CONTROLLER_TX_DONE:DL_I2C_disableInterrupt(I2C_MPU6050_INST, DL_I2C_INTERRUPT_CONTROLLER_TXFIFO_TRIGGER);gI2cControllerStatus = I2C_STATUS_TX_COMPLETE;break;case DL_I2C_IIDX_CONTROLLER_RXFIFO_TRIGGER:gI2cControllerStatus = I2C_STATUS_RX_INPROGRESS;/* Receive all bytes from target */while (DL_I2C_isControllerRXFIFOEmpty(I2C_MPU6050_INST) != true) {if (gRxCount < gRxLen) {gRxPacket[gRxCount++] =DL_I2C_receiveControllerData(I2C_MPU6050_INST);} else {/* Ignore and remove from FIFO if the buffer is full */DL_I2C_receiveControllerData(I2C_MPU6050_INST);}}break;case DL_I2C_IIDX_CONTROLLER_TXFIFO_TRIGGER:gI2cControllerStatus = I2C_STATUS_TX_INPROGRESS;/* Fill TX FIFO with next bytes to send */if (gTxCount < gTxLen) {gTxCount += DL_I2C_fillControllerTXFIFO(I2C_MPU6050_INST, &gTxPacket[gTxCount], gTxLen - gTxCount);}break;/* Not used for this example */case DL_I2C_IIDX_CONTROLLER_ARBITRATION_LOST:case DL_I2C_IIDX_CONTROLLER_NACK:if ((gI2cControllerStatus == I2C_STATUS_RX_STARTED) ||(gI2cControllerStatus == I2C_STATUS_TX_STARTED)) {/* NACK interrupt if I2C Target is disconnected */gI2cControllerStatus = I2C_STATUS_ERROR;}case DL_I2C_IIDX_CONTROLLER_RXFIFO_FULL:case DL_I2C_IIDX_CONTROLLER_TXFIFO_EMPTY:case DL_I2C_IIDX_CONTROLLER_START:case DL_I2C_IIDX_CONTROLLER_STOP:case DL_I2C_IIDX_CONTROLLER_EVENT1_DMA_DONE:case DL_I2C_IIDX_CONTROLLER_EVENT2_DMA_DONE:default:break;}
}

五.总结

        最后我觉得这个项目最重要的部分还是陀螺仪的精准度,如果一个陀螺仪的精准度高,这个项目就十分简单了,像mpu6050这种陀螺仪精准度就稍微差了一点,他有个最严重的问题就是温漂,偏航角会随着温度变化为变化,这一点在跑发挥项一的时候十分关键的,因为这个只差基督就会导致车子跑偏,导致无法完成 整个项目。如果条件足够的话建议可以换一个好一点的陀螺仪,然后就是灰度如果没有条件同样也可以换成红外。以上就是本次项目的一些小建议。

这篇关于电赛2024年H题智能小车基于MSPM0G3507主控MCU(利用8路灰度加上MPU6050的解决方式)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1132470

相关文章

2024年流动式起重机司机证模拟考试题库及流动式起重机司机理论考试试题

题库来源:安全生产模拟考试一点通公众号小程序 2024年流动式起重机司机证模拟考试题库及流动式起重机司机理论考试试题是由安全生产模拟考试一点通提供,流动式起重机司机证模拟考试题库是根据流动式起重机司机最新版教材,流动式起重机司机大纲整理而成(含2024年流动式起重机司机证模拟考试题库及流动式起重机司机理论考试试题参考答案和部分工种参考解析),掌握本资料和学校方法,考试容易。流动式起重机司机考试技

嵌入式QT开发:构建高效智能的嵌入式系统

摘要: 本文深入探讨了嵌入式 QT 相关的各个方面。从 QT 框架的基础架构和核心概念出发,详细阐述了其在嵌入式环境中的优势与特点。文中分析了嵌入式 QT 的开发环境搭建过程,包括交叉编译工具链的配置等关键步骤。进一步探讨了嵌入式 QT 的界面设计与开发,涵盖了从基本控件的使用到复杂界面布局的构建。同时也深入研究了信号与槽机制在嵌入式系统中的应用,以及嵌入式 QT 与硬件设备的交互,包括输入输出设

【专题】2024飞行汽车技术全景报告合集PDF分享(附原数据表)

原文链接: https://tecdat.cn/?p=37628 6月16日,小鹏汇天旅航者X2在北京大兴国际机场临空经济区完成首飞,这也是小鹏汇天的产品在京津冀地区进行的首次飞行。小鹏汇天方面还表示,公司准备量产,并计划今年四季度开启预售小鹏汇天分体式飞行汽车,探索分体式飞行汽车城际通勤。阅读原文,获取专题报告合集全文,解锁文末271份飞行汽车相关行业研究报告。 据悉,业内人士对飞行汽车行业

让树莓派智能语音助手实现定时提醒功能

最初的时候是想直接在rasa 的chatbot上实现,因为rasa本身是带有remindschedule模块的。不过经过一番折腾后,忽然发现,chatbot上实现的定时,语音助手不一定会有响应。因为,我目前语音助手的代码设置了长时间无应答会结束对话,这样一来,chatbot定时提醒的触发就不会被语音助手获悉。那怎么让语音助手也具有定时提醒功能呢? 我最后选择的方法是用threading.Time

高效录音转文字:2024年四大工具精选!

在快节奏的工作生活中,能够快速将录音转换成文字是一项非常实用的能力。特别是在需要记录会议纪要、讲座内容或者是采访素材的时候,一款优秀的在线录音转文字工具能派上大用场。以下推荐几个好用的录音转文字工具! 365在线转文字 直达链接:https://www.pdf365.cn/ 365在线转文字是一款提供在线录音转文字服务的工具,它以其高效、便捷的特点受到用户的青睐。用户无需下载安装任何软件,只

内核启动时减少log的方式

内核引导选项 内核引导选项大体上可以分为两类:一类与设备无关、另一类与设备有关。与设备有关的引导选项多如牛毛,需要你自己阅读内核中的相应驱动程序源码以获取其能够接受的引导选项。比如,如果你想知道可以向 AHA1542 SCSI 驱动程序传递哪些引导选项,那么就查看 drivers/scsi/aha1542.c 文件,一般在前面 100 行注释里就可以找到所接受的引导选项说明。大多数选项是通过"_

如何解决线上平台抽佣高 线下门店客流少的痛点!

目前,许多传统零售店铺正遭遇客源下降的难题。尽管广告推广能带来一定的客流,但其费用昂贵。鉴于此,众多零售商纷纷选择加入像美团、饿了么和抖音这样的大型在线平台,但这些平台的高佣金率导致了利润的大幅缩水。在这样的市场环境下,商家之间的合作网络逐渐成为一种有效的解决方案,通过资源和客户基础的共享,实现共同的利益增长。 以最近在上海兴起的一个跨行业合作平台为例,该平台融合了环保消费积分系统,在短

用命令行的方式启动.netcore webapi

用命令行的方式启动.netcore web项目 进入指定的项目文件夹,比如我发布后的代码放在下面文件夹中 在此地址栏中输入“cmd”,打开命令提示符,进入到发布代码目录 命令行启动.netcore项目的命令为:  dotnet 项目启动文件.dll --urls="http://*:对外端口" --ip="本机ip" --port=项目内部端口 例: dotnet Imagine.M

智能交通(二)——Spinger特刊推荐

特刊征稿 01  期刊名称: Autonomous Intelligent Systems  特刊名称: Understanding the Policy Shift  with the Digital Twins in Smart  Transportation and Mobility 截止时间: 开放提交:2024年1月20日 提交截止日

2024网安周今日开幕,亚信安全亮相30城

2024年国家网络安全宣传周今天在广州拉开帷幕。今年网安周继续以“网络安全为人民,网络安全靠人民”为主题。2024年国家网络安全宣传周涵盖了1场开幕式、1场高峰论坛、5个重要活动、15场分论坛/座谈会/闭门会、6个主题日活动和网络安全“六进”活动。亚信安全出席2024年国家网络安全宣传周开幕式和主论坛,并将通过线下宣讲、创意科普、成果展示等多种形式,让广大民众看得懂、记得住安全知识,同时还