24电赛H题总结

news2024/12/22 22:06:34

一、题目

        题目链接:自动行驶小车(H题)

        我们截取一些重要信息

1. 小车行驶场地示意图

        

2.要求

二、赛题分析

技术挑战与准备

  • MCU熟悉度:尽管TI MSPM0系列MCU在使用上类似于STM32CUBEIDE+Keil,但其开发环境也需要熟悉。因此,首要任务是熟悉TI MSPM0的编程环境,以及其基本的寄存器配置和编程接口。// 我这里使用的嘉立创的地猛星MSPM0G3507

  • 硬件接口与驱动:根据小车配置,需要编写电机驱动、传感器(寻迹,MPU6050,OLED等)的接口代码。这要求能移植即可,且嘉立创也提供了移植教程。

  • 实时性与稳定性:由于小车行驶过程中需要快速响应环境变化,因此代码的实时性和稳定性至关重要。

开发流程建议

  1. 环境搭建:安装并配置TI MSPM0的开发环境,确保能够编译、下载和调试代码。

  2. 硬件测试:单独测试各个硬件组件(电机、传感器等),确保其工作正常。

  3. 基础功能实现:先实现小车的基本移动功能,如前进、后退、左转、右转等。

  4. 路径跟踪与避障:在基础功能基础上,逐步加入路径和寻迹逻辑。

  5. 集成测试与调优:将各功能模块集成,进行整体测试,并根据测试结果进行调优。

MSPM0G3507开发教程: 立创·地猛星MSPM0G3507开发板

熟悉基础控制

1. LED点灯

  • 目标:学会如何通过编程控制MSPM0G3507的GPIO(通用输入输出)端口来点亮和熄灭LED灯。
  • 学习要点:了解GPIO端口的配置(如设置为输出模式)、编写控制LED亮灭的代码、理解电平高低与LED状态的关系。

2. 按键控制

  • 目标:掌握如何通过编程读取MSPM0G3507的GPIO端口状态来检测按键是否被按下,并根据按键状态执行相应的操作。
  • 学习要点:了解GPIO端口的输入模式配置、按键的去抖动处理(避免误触发)、编写按键扫描和状态处理的代码。

3. 串口通信

  • 目标:学会配置MSPM0G3507的USART(通用同步/异步接收/发送器)模块,实现与PC或其他设备之间的串口通信。
  • 学习要点:理解串口通信的基本原理(如波特率、数据位、停止位等参数)、USART模块的初始化配置、编写数据发送和接收的代码、使用串口调试助手等工具进行测试。

4. PWM输出

  • 目标:掌握MSPM0G3507的PWM(脉冲宽度调制)模块的配置和使用,实现模拟信号的输出控制(如电机速度调节)。
  • 学习要点:了解PWM的基本原理、PWM模块的初始化配置(如时钟源选择、占空比设置等)、编写控制PWM输出的代码、通过实验观察PWM波形并调整参数以达到预期效果。

通过系统地学习和实践上述基础功能,我们可以为后续的自动行驶小车项目打下坚实的基础。这些技能不仅有助于我们更好地理解MCU的工作原理和编程方法,还能在项目中发挥关键作用,如通过PWM控制电机转速、通过串口通信实现与上位机的数据交换等。

三、硬件设计

// 无效内容,只需要注意我们这里采用的是双MCU即可

在比赛进程中,我们遭遇了一个挑战:在尝试将控制程序加入MPU6050传感器后,发现MCU(微控制器单元)意外地失去了响应。面对时间紧迫的竞赛环境,我们迅速评估了当前状况,并决定采取一个高效且直接的解决方案来应对这一突发问题。

为了在最短时间内恢复系统功能并确保任务能够顺利进行,我们决定采用双MCU(两块地猛星)的并行架构作为最优解。这一策略旨在通过分散负载和提供冗余度来绕过原系统中的潜在障碍,同时保留关键传感器(MPU6050)的数据采集能力。

通过部署双MCU系统,我们可以将控制逻辑分配到两个独立的处理器上,其中一个专注于与MPU6050的通信和数据处理,而另一个则负责执行其他关键任务,如电机控制、通信接口管理等。这样的设计不仅能够有效避免单一故障点导致的系统瘫痪,还能提升整体系统的稳定性和响应速度。在实施这一方案时,我们将紧密关注两个MCU之间的同步与协调,确保数据传输的准确性和实时性

四、基础功能编写

1. MPU6050

*  MPU6050姿态角获取

MPU6050的移植方式:MPU6050六轴传感器

值得注意的是,关于stdio.c和stdio.h文件的移植步骤中,直接获取这些文件的方式可能未予详细说明,但关键信息已包含在文章的末尾部分——即文章最后的移植成功案例的代码下载链接中。

移植成功案例代码下载链接:

链接:MPU6050移植成功案例

提取码:yaah

注意:我们在直接按照移植步骤完成后并未成功实现MPU6050的姿态获取(串口无反应)

我们做了如下更改:将void delay_us(unsigned long __us) ;更改如下,同时也发生了二中的情况。

void delay_us(unsigned long __us) 
{
	int delay__1us = 32000;
			int dd = 0;
	for(int i = 0; i< delay__1us;i++){
		dd++;
	}
}

*  MPU6050姿态角发送与接收

发送:使用UART1将信息发送出去(此处为了简便和配合不熟悉MCU,我们只发送一个字节)

为了通过UART1发送信息(在此场景中简化为仅发送一个字节),同时考虑到我们处理的角度数据范围在-180度到180度之间,并且单个字节(0-255)的传输限制,我们设计一个简单的编码方案。

			int yaw_int = yaw;
			yaw_int = yaw_int + 180;
			yaw_int = yaw_int/2;
	
			
			sendInt(yaw_int);
			delay_ms(20);

将 int 类型的数据装换后发出

void sendInt(int value)
{
    uint8_t sendBuff[sizeof(int)];
    
    // 将 int 转换为字节数组
    for (int i = 0; i < sizeof(int); i++)
    {
        sendBuff[i] = (value >> (i * 8)) & 0xFF; // 提取每个字节
    }

    // 逐字节发送
    for (int i = 0; i < sizeof(int); i++)
    {
        DL_UART_Main_transmitData(UART_1_INST, sendBuff[i]);
    }
}

接收:主控板通过UART1接受数据并解码

我们MPU6050的MCU会一直发送姿态角,所以我们只需要在使用时接受一次就可以了。


//获取姿态角
int yaw_out(){
		int  usrt_dd = 0;
		usrt_dd = DL_UART_Main_receiveData(UART_1_INST);
		usrt_dd  =usrt_dd*2 ;
	return usrt_dd;
}

2. 主控环境配置

BEEP

Track

TB6612

KEY

PWM

UART: 串口1不需要中断

3. BEEP+LED

#include "BEEP.h"
#include "ti_msp_dl_config.h"
//#include "board.h"

void beep_delay_ms(unsigned int ms)
{
    unsigned int i, j;
    // 下面的嵌套循环的次数是根据主控频率和编译器生成的指令周期大致计算出来的,
    // 需要通过实际测试调整来达到所需的延时。
    for (i = 0; i < ms; i++)
    {
        for (j = 0; j < 8000; j++)
        {
            // 仅执行一个足够简单以致于可以预测其执行时间的操作
            __asm__("nop"); // "nop" 代表“无操作”,在大多数架构中,这会消耗一个或几个时钟周期
        }
    }
}
void BEEP(int delay, int en){
		static  int delay_cnt=0;
		
		if(delay_cnt >= delay*32000){
				delay_cnt = 0;
				DL_GPIO_togglePins(BEEP_GPIO, BEEP_GPIO_PIN);// 翻转电平
				DL_GPIO_togglePins(BEEP_GPIO, BEEP_LED_PIN);// 翻转电平
		}
		
		if(en == 0) {
			DL_GPIO_clearPins(BEEP_GPIO, BEEP_LED_PIN);//输出低电平
			
			DL_GPIO_setPins(BEEP_GPIO, BEEP_GPIO_PIN);
			//输出高电平
		}
			
}

void BEEP_delay(int delay, int en){
	for(int i =0;i<en*2;i++){
		beep_delay_ms(delay);
		DL_GPIO_togglePins(BEEP_GPIO, BEEP_GPIO_PIN);// 翻转电平
		DL_GPIO_togglePins(BEEP_GPIO, BEEP_LED_PIN);// 翻转电平
	}
	
	BEEP_init();
}

void BEEP_init()
{
			DL_GPIO_setPins(BEEP_GPIO, BEEP_GPIO_PIN);
			DL_GPIO_clearPins(BEEP_GPIO, BEEP_LED_PIN);//输出低电平
}

4. 寻迹模块

#include "track.h"
#include "ti_msp_dl_config.h"


void track_delay_ms(unsigned int ms)
{
    unsigned int i, j;
    // 下面的嵌套循环的次数是根据主控频率和编译器生成的指令周期大致计算出来的,
    // 需要通过实际测试调整来达到所需的延时。
    for (i = 0; i < ms; i++)
    {
        for (j = 0; j < 8000; j++)
        {
            // 仅执行一个足够简单以致于可以预测其执行时间的操作
            __asm__("nop"); // "nop" 代表“无操作”,在大多数架构中,这会消耗一个或几个时钟周期
        }
    }
}

// track为寻迹数组, size为寻迹模块大小
int tracking_7(int *track){
	int i = 0;
	int white = 0;
	
	//如果读取到的引脚值大于0,说明引脚为高电平
	i++;
	if( DL_GPIO_readPins(Track_GPIO, Track_PIN_1) > 0 )
	{
		track[i] = H;  //保留为高电平
		white++;
	}
	else//如果引脚为低电平
	{
		track[i] = L;//保留为低电平
	}
	
	i++;
	if( DL_GPIO_readPins(Track_GPIO, Track_PIN_2) > 0 )
	{
		track[i] = H;  //保留为高电平
		white++;
	}
	else//如果引脚为低电平
	{
		track[i] = L;//保留为低电平
	}
	
	
	i++;
	if( DL_GPIO_readPins(Track_GPIO, Track_PIN_3) > 0 )
	{
		track[i] = H;  //保留为高电平
		white++;
	}
	else//如果引脚为低电平
	{
		track[i] = L;//保留为低电平
	}
	
	i++;
	if( DL_GPIO_readPins(Track_GPIO, Track_PIN_4) > 0 )
	{
		track[i] = H;  //保留为高电平
		white++;
	}
	else//如果引脚为低电平
	{
		track[i] = L;//保留为低电平
	}
	
	
	i++;
	if( DL_GPIO_readPins(Track_GPIO, Track_PIN_5) > 0 )
	{
		track[i] = H;  //保留为高电平
		white++;
	}
	else//如果引脚为低电平
	{
		track[i] = L;//保留为低电平
	}
	
	i++;
	if( DL_GPIO_readPins(Track_GPIO, Track_PIN_6) > 0 )
	{
		track[i] = H;  //保留为高电平
		white++;
	}
	else//如果引脚为低电平
	{
		track[i] = L;//保留为低电平
	}
	
	
	i++;
	if( DL_GPIO_readPins(Track_GPIO, Track_PIN_7) > 0 )
	{
		track[i] = H;  //保留为高电平
		white++;
	}
	else//如果引脚为低电平
	{
		track[i] = L;//保留为低电平
	}
	
	return white;
	
}

5. TB6612驱动

car.h

#ifndef CAR_H
#define CAR_H


//	PA3 为左 	||		PA2为右
#define PWM_MAX 999													// PWM最大数值
#define PWM_name	PWM_car_INST							// 小车PWM组名字
#define PWM_L			GPIO_PWM_car_C0_IDX				// 小车左PWM
#define PWM_R			GPIO_PWM_car_C1_IDX				// 小车右PWM
void PWM_init();														// 初始化PWM
void PWM_left(int speed);										// 左侧PWM速度
void PWM_right(int speed);									// 右侧PWM速度

//	TB6612 
#define TB6612	TB6612_PORT
#define L0			TB6612_PIN_L0_PIN						//	PA18
#define L1			TB6612_PIN_L1_PIN						//	PA17
#define R0			TB6612_PIN_R0_PIN						//	PA16
#define R1			TB6612_PIN_R1_PIN						//	PA15

#define L0_L	DL_GPIO_clearPins(TB6612,L0);//输出低电平
#define L0_H	DL_GPIO_setPins(TB6612,L0);  //输出高电平
	
#define L1_L	DL_GPIO_clearPins(TB6612,L1);//输出低电平
#define L1_H  DL_GPIO_setPins(TB6612,L1);  //输出高电平
	
	
#define R0_L	DL_GPIO_clearPins(TB6612,R0);//输出低电平
#define R0_H	DL_GPIO_setPins(TB6612,R0);  //输出高电平
	
#define R1_L	DL_GPIO_clearPins(TB6612,R1);//输出低电平
#define R1_H  DL_GPIO_setPins(TB6612,R1);  //输出高电平


void car_init();
void car_forward(int LL, int RR);
void car_retreat(int LL, int RR);
void car_left(int LL, int RR);
void car_right(int LL, int RR);
void car_stop();
void car_turn_Left(int i);
void car_turnn_right(int i);
void car_main(int LL , int RR);





#endif // CAR_H

car.c

#include "car.h"
#include "ti_msp_dl_config.h"


//	PB6 为左 	||		PA2为右
//  #define PWM_MAX 999							// PWM最大数值
void PWM_init()								// 初始化PWM
{
	DL_TimerG_setCaptureCompareValue(PWM_name,0,PWM_L);
	DL_TimerG_setCaptureCompareValue(PWM_name,0,PWM_R);
}
void PWM_left(int speed)				// 左侧PWM速度
{
	speed = 1000 - speed;
	if(speed >= 1000) speed = 999;
	if(speed < 0)	speed = 0;
	DL_TimerG_setCaptureCompareValue(PWM_name,speed,PWM_L);
}

void PWM_right(int speed)			// 右侧PWM速度
{
	speed = 1000 - speed;
	if(speed >= 1000) speed = 999;
	if(speed < 0)	speed = 0;
	DL_TimerG_setCaptureCompareValue(PWM_name,speed,PWM_R);
}



//	TB6612 
#define TB6612	TB6612_PORT
#define L0			TB6612_PIN_L0_PIN						//	PA18
#define L1			TB6612_PIN_L1_PIN						//	PA17
#define R0			TB6612_PIN_R0_PIN						//	PA16
#define R1			TB6612_PIN_R1_PIN						//	PA15

void car_init(){
	PWM_init();
	
	L0_L;
	L1_L;
	
	R0_L;
	R1_L;
}

void car_forward(int LL, int RR)
{
	PWM_left(LL);
	PWM_right(RR);
	
	L0_H;
	L1_L;
	
	R0_H;
	R1_L;	
	
}

void car_retreat(int LL, int RR)
{
	
	PWM_left(LL);
	PWM_right(RR);
	
	L0_L;
	L1_H;
	
	R0_L;
	R1_H;	
}	

void car_left(int LL, int RR)
{

	PWM_left(LL);
	PWM_right(RR);
	
	L0_H;
	L1_L;
	
	R0_H;
	R1_L;	
}	


void car_right(int LL, int RR)
{
	
	PWM_left(LL);
	PWM_right(RR);
	
	L0_H;
	L1_L;
	
	R0_H;
	R1_L;	
}
void car_stop()
{
	
	L0_L;
	L1_L;
	
	R0_L;
	R1_L;	
}	

void car_turn_Left(int i)
{

	PWM_left(i);
	PWM_right(i);
	
	L0_L;
	L1_H;
	
	R0_H;
	R1_L;	
}	

void car_turnn_right(int i)
{
	
	PWM_left(i);
	PWM_right(i);
	
	L0_H;
	L1_L;
	
	R0_L;
	R1_H;	
}	


void car_main(int LL , int RR)
{
	if(RR >= 0){
		R0_H;
		R1_L;
	}else if(RR < 0){
		R0_L;
		R1_H;		
		RR = -RR;
	}
	
	
	if(LL >= 0)
	{
		L0_H;
		L1_L;
	}else if(LL < 0)
	{
		L0_L;
		L1_H;
		LL = -LL;
	}
	
	
	
	//范围限制
	if(RR >= 1000) RR = 999;
	if(RR < 0)	RR = 0;
	if(LL>=1000) LL = 999;
	if(LL< 0)	LL = 0;
	
	PWM_left(LL);
	PWM_right(RR);
}

7. KEY

#include "key.h"
#include "ti_msp_dl_config.h"
#include "board.h"
//自定义延时(不精确)
void key_delay_ms(unsigned int ms)
{
    unsigned int i, j;
    // 下面的嵌套循环的次数是根据主控频率和编译器生成的指令周期大致计算出来的,
    // 需要通过实际测试调整来达到所需的延时。
    for (i = 0; i < ms; i++)
    {
        for (j = 0; j < 8000; j++)
        {
            // 仅执行一个足够简单以致于可以预测其执行时间的操作
            __asm__("nop"); // "nop" 代表“无操作”,在大多数架构中,这会消耗一个或几个时钟周期
        }
    }
}

void KEY_number(int *key)
{
	int i = 0;
	int key_delay = 50;
	//如果读取到的引脚值大于0,说明引脚为高电平
	i++;
	if( DL_GPIO_readPins(KEY_GPIO, KEY1) > 0 )
	{
		key[i] = 1;  //保留为高电平
	}
	else//如果引脚为低电平
	{
		key_delay_ms(key_delay);
		if( DL_GPIO_readPins(KEY_GPIO, KEY1) > 0 )
		{
			key[i] = 1;  //保留为高电平
		}
		else	key[i] = 0;//保留为低电平
	}
	
	i++;
	if( DL_GPIO_readPins(KEY_GPIO, KEY2) > 0 )
	{
		key[i] = 1;  //保留为高电平
	}
	else//如果引脚为低电平
	{
		key_delay_ms(key_delay);
		if( DL_GPIO_readPins(KEY_GPIO, KEY2) > 0 )
		{
			key[i] = 1;  //保留为高电平
		}
		else	key[i] = 0;//保留为低电平
	}
	
	
	
	i++;
	if( DL_GPIO_readPins(KEY_GPIO, KEY3) > 0 )
	{
		key[i] = 1;  //保留为高电平
	}
	else//如果引脚为低电平
	{
		key_delay_ms(key_delay);
		if( DL_GPIO_readPins(KEY_GPIO, KEY3) > 0 )
		{
			key[i] = 1;  //保留为高电平
		}
		else	key[i] = 0;//保留为低电平
	}
	
	
	
	i++;
	if( DL_GPIO_readPins(KEY_GPIO, KEY4) > 0 )
	{
		key[i] = 1;  //保留为高电平
	}
	else//如果引脚为低电平
	{
		key_delay_ms(key_delay);
		if( DL_GPIO_readPins(KEY_GPIO, KEY4) > 0 )
		{
			key[i] = 1;  //保留为高电平
		}
		else	key[i] = 0;//保留为低电平
	}
	
	
	
	i++;
	if( DL_GPIO_readPins(KEY_GPIO, KEY5) > 0 )
	{
		key[i] = 1;  //保留为高电平
	}
	else//如果引脚为低电平
	{
		key_delay_ms(key_delay);
		if( DL_GPIO_readPins(KEY_GPIO, KEY5) > 0 )
		{
			key[i] = 1;  //保留为高电平
		}
		else	key[i] = 0;//保留为低电平
	}
	
	
	
	i++;
	if( DL_GPIO_readPins(KEY_GPIO, KEY6) > 0 )
	{
		key[i] = 1;  //保留为高电平
	}
	else//如果引脚为低电平
	{
		key_delay_ms(key_delay);
		if( DL_GPIO_readPins(KEY_GPIO, KEY6) > 0 )
		{
			key[i] = 1;  //保留为高电平
		}
		else	key[i] = 0;//保留为低电平
	}
	
	
}

五、任务编写

经过对题目的深入剖析,我们可以将其核心内容精炼地划分为两大核心任务:一是实现车辆的直线稳定行驶,确保行进过程中方向的精确控制与稳定;二是执行寻迹功能,即车辆需具备根据预设路径或轨迹进行智能追踪与导航的能力。这两个部分相辅相成,共同构成了题目要求的关键解决方案。

1. 直线调整方案

// 直线调整方案
void yaw_adjust(float target, float now_yaw, int speed, int B) {
    // 确保目标和当前偏航在 0-359 范围内
    if (target >= 360) target -= 360;
    if (now_yaw >= 360) now_yaw -= 360;

    // 计算调整量
    int adjust = target - now_yaw;

    // 归一化 adjust 到 -180 到 180 的范围
    if (adjust > 180) {
        adjust -= 360;
    } else if (adjust < -180) {
        adjust += 360;
    }

    // 调整电机速度
    car_main(speed - adjust * B, speed + adjust * B);
}

函数概述

yaw_adjust 函数接收四个参数:target(目标偏航角)、now_yaw(当前偏航角)、speed(基础速度)和B(调整系数),用于调整车辆或机器人的左右电机速度,以修正其行进方向。

参数解析

  • target:这是车辆或机器人应当达到的偏航角目标值,用于指明最终的前进方向。通过比较当前偏航角与目标偏航角,可以计算出所需的调整量。
  • now_yaw:当前车辆或机器人的偏航角,反映了其当前的行进方向。这一参数的实时获取对于方向调整至关重要。
  • speed:基础速度,即在不进行方向调整时,车辆或机器人应维持的速度。
  • B:调整系数,用于将偏航角的调整量转换为电机速度的调整量。这个系数决定了调整的灵敏度和速度。

核心逻辑

  1. 角度标准化:首先,函数通过模运算(隐式地,通过加减360的倍数)确保targetnow_yaw都在0到359度的范围内,这是处理周期性角度数据的常用方法。

  2. 计算调整量:接着,计算目标偏航角与当前偏航角之间的差值adjust,这代表了需要进行的调整方向和大小。

  3. 归一化调整量:由于偏航角的差值可能是正数或负数,且可能超出-180到180度的范围,函数通过加减360度的方式将adjust归一化到这个范围内。这一步骤确保了调整指令的直观性和一致性。

  4. 调整电机速度:最后,根据adjust的值和调整系数B,计算左右电机的速度调整量,并调用car_main函数(这里假设是控制车辆电机速度的函数)来应用这些调整。左电机速度减小、右电机速度增加意味着向左转,反之则向右转。

2. 任务一

任务一就比较简单了,保持直线行驶,识别到寻迹线即可停止。


//	任务1
void test1(float yaw_init){
	//delay_ms(200);
	float pitch=0,roll=0,yaw=0;   //欧拉角
	float	now_yaw = yaw;

	
	
	int track[8];
	
	int speed = 400;	//	直线行驶速度
	int BS = 3;				//	偏角倍数
	
	int time_cnt = 0;	//	行驶时间计数
	int time_cnt_max = 32*1000*100;	//	32MHZ约等于	1s
	
	int target_time = 0;	//	当有疑似目标时,记录时间
	
	int write_min = 5;		//	当出现最小白色数时为疑似目标
	
	int white = 0;
	int en = 1;
	while(en == 1)
	{
		white = tracking_7(track);
		//获取欧拉角
		now_yaw = yaw_out();
			
		
		yaw_adjust(yaw_init, now_yaw, 300, BS);
		
		if(track[1] == 1 ) {
					car_main(100,-100);
			//delay_ms(2000);
			return;
		}
		if(track[7] == 1 ) {
			car_main(-100,100);
			//delay_ms(2000);
			return;
		}
		
		//delay_ms(5);
		

				if(white >= 1 )
				{			
					en = 0;
						return;
				}
	}
	
}

变量定义

  • 欧拉角 (pitchrollyaw): 这里定义了三个欧拉角,但pitchroll被初始化为0且未在后续代码中使用,可能是预留的变量或用于其他目的。yaw用于存储从某个函数(假设是yaw_out())获取的当前偏航角,但实际上被now_yaw变量覆盖。

  • now_yaw: 存储当前的偏航角,用于与初始偏航角yaw_init进行比较和调整。

  • track数组: 用于存储追踪到的轨迹信息,这里假设tracking_7(track)函数会填充这个数组,其中track[1]track[7]可能代表特定位置(如左右边界)的追踪结果。

  • 速度和调整系数speed定义了直线行驶的速度,而BS(偏角倍数)用于计算偏航角调整时电机速度的调整量。

  • 时间相关变量time_cnttime_cnt_max用于时间计数,但在这个函数体中并未使用。

  • 目标时间和疑似目标检测target_timewrite_min变量似乎是为了检测疑似目标而设计的,但在函数体内并未完全实现这一逻辑。

  • whiteenwhite用于存储追踪到的白色(或特定颜色)的数量,而en用作循环的控制变量。

逻辑流程

  1. 初始化变量: 设置初始速度、偏角倍数等。

  2. 主循环:

    • 调用tracking_7(track)函数追踪轨迹,并获取白色(或特定颜色)的数量。
    • 调用yaw_out()获取当前偏航角,并使用yaw_adjust函数进行偏航角调整。
    • 检查track[1]track[7]的值,如果检测到特定条件(可能是左右边界),则执行相应的转向操作并退出函数。
    • 如果white的数量大于等于1(这里逻辑上可能有误,因为通常寻迹会要求连续多个白色点或达到一定阈值),则停止循环并退出函数。但这里直接返回可能会忽略一些重要的逻辑处理。

潜在问题和改进建议

  1. 变量yaw的未使用: 如果yaw不用于其他目的,可以考虑移除它以减少混淆。

  2. white的逻辑可能不够严谨: 通常寻迹会要求连续检测到一定数量的白色点或满足其他更复杂的条件,而不是简单地检查是否大于等于1。

  3. 时间计数器的未使用: 如果不需要时间控制,可以移除time_cnttime_cnt_max变量。如果需要,应实现相应的逻辑。

  4. 代码的可读性和可维护性: 可以通过添加注释、重构代码(如将特定逻辑封装为函数)来提高代码的可读性和可维护性。

  5. 错误处理和异常检测: 在实际应用中,应考虑添加错误处理和异常检测机制,以应对可能的传感器故障、通信错误等问题。

  6. 返回值的处理: 函数直接通过return语句退出,可能需要在调用该函数时考虑如何处理其返回值或确保它不会导致意外的行为。

3. 寻迹阶段

在寻迹过程中,如果纯靠识别全白的话容易出现误判,我们加入时间变量来缓解这种错误。

//    寻迹
void track_adjust(int speed)
{
    int track[8];
    int track_old[8];    //记录先前一次的寻迹表
    int Mobile_records =0;
    int BW = 0;
    int BW_N = 55;
    int cnt = 0;
    int cnt_max = 32*1000/2 + 5000 + 500 ;
    while(1)
    {
        int write = 0;
        write = tracking_7(track);
        
        if(track[1] == 1)    Mobile_records = -1;    //    需要左转+
        else if(track[7] == 1)    Mobile_records = 1;      //    需要右转
        else Mobile_records = 0;
        
        if(write <= 0 && Mobile_records == 0)    // 全为 0 且不是由寻迹不当导致
        {
            cnt++;
        }
        else    cnt = 0;
        
        if(cnt >= cnt_max) return;
        
        
        if(track[1] == 1)    BW = -5;
        else if(track[7] == 1) BW = 3;
        else if(track[2] == 1) BW = -3;
        else if(track[6] == 1) BW = 3;
        else if(track[3] == 1) BW = -1;
        else if(track[5] == 1) BW = 1;
        else if(track[4] == 1) BW = 0;
        
        car_main(speed + BW*BW_N, speed - BW*BW_N);
            
            
    }
}

功能

  • 寻迹调整:根据寻迹传感器返回的8个位置(track[8]数组)的检测结果,调整车辆的左右轮速度,以保持在预定的轨迹上行驶。
  • 错误处理:如果连续检测到没有有效轨迹(即write <= 0Mobile_records == 0),则增加计数器cnt。如果计数器超过某个阈值(cnt_max),则函数返回,可能是为了避免无限循环或处理无法找到轨迹的情况。也同时是判断完成寻迹的标志。

变量说明

  • track[8]:存储寻迹传感器返回的8个位置的状态(通常是二进制值,表示是否检测到轨迹)。
  • track_old[8]:虽然声明了,但在函数体内未使用,可能是为了未来扩展,比如记录上一次的状态以进行比较。
  • Mobile_records:用于记录车辆需要进行的转向操作(左转、右转或直行)。
  • BW:基础转向值,根据寻迹传感器的不同位置设置不同的值,用于调整左右轮的速度差,实现转向。
  • BW_N:转向调整的倍数,用于放大BW的影响,使转向更加明显。
  • cnt:计数器,用于记录连续未检测到有效轨迹的次数。
  • cnt_max:计数器阈值,当cnt达到此值时,函数返回。

逻辑流程

  1. 调用tracking_7函数获取寻迹传感器的状态,存储在track数组中。
  2. 根据track数组的值,设置Mobile_records变量,以决定是否需要左转、右转或直行。
  3. 检查是否连续未检测到有效轨迹(write <= 0 && Mobile_records == 0),如果是,则增加cnt计数器。
  4. 如果cnt计数器达到阈值cnt_max,则函数返回。
  5. 根据track数组的值,设置BW变量,用于调整左右轮的速度差。
  6. 调用car_main函数,根据speedBW的值调整左右轮的速度,实现转向或直行。

注意事项

  • 无限循环:由于函数使用了while(1),它将在没有外部干预的情况下无限循环。这通常是期望的行为,但需要有适当的退出条件(如cnt >= cnt_max)。
  • 性能优化:如果tracking_7函数或car_main函数的执行时间较长,可能会影响系统的响应速度。可能需要考虑优化这些函数的性能。
  • 代码扩展track_old数组当前未使用,但为未来可能的扩展(如比较当前和上一次的状态)提供了空间。
  • 错误处理:除了连续未检测到轨迹的情况外,还可以考虑添加其他错误处理逻辑,如传感器故障检测等。

4. 任务二

任务二即为前面的调用,注意中间进行了小量的微调即可。



//任务2
void test2(float yaw_init,int speed)
{
		
		test1(yaw_init);
	
	
						car_stop();
						BEEP_delay(100, 2);
	
		track_adjust(speed);
					car_stop();
						BEEP_delay(100, 2);
	
	
	yaw_init = yaw_init + 180;
	if(yaw_init >= 360){
			yaw_init = yaw_init - 360;
	}
	

		int now = yaw_out();
		while(1){
		int ccccc = 10;
			if(yaw_init - now >= 0) car_main(-ccccc,ccccc);
				else car_main(ccccc,-ccccc);
			
			now = yaw_out();
			if(yaw_init - now + 1>=yaw_init - now && yaw_init - now -1 < yaw_init - now) break;
	}
	
		test1(yaw_init);
	
	car_stop();
						BEEP_delay(100, 2);
	
	
		track_adjust(speed);
				car_stop();
						BEEP_delay(100, 2);
	

return;
}

5. 任务三 认为四

任务三对角度进行计算即可(当然也会出现误差)


//	任务3
void test3(float yaw_init, int speed){
	int yaw_init_180 = yaw_init;
	if(yaw_init_180 >= 360) yaw_init_180 = yaw_init_180 - 360;
	
	
	yaw_init = yaw_init - 33;
	if(yaw_init < 0) yaw_init = 360-yaw_init;;
	turn_yaw(yaw_init,yaw_init);
	
	test1(yaw_init);
	car_stop();
	car_main(-100,100);
	delay_ms(1000);
	BEEP_delay(100, 2);
	
	track_adjust(speed);
	car_stop();
	BEEP_delay(100, 2);
	
	
	/*
		yaw_init = yaw_init - 90;
	if(yaw_init < 0){
			yaw_init = 360 + yaw_init;
	}*/
	/*
			int now = yaw_out();
	while(1){
		int ccccc = 10;
			if(yaw_init_180 - now >= 0) car_main(-ccccc,ccccc);
				else car_main(ccccc,-ccccc);
			
			now = yaw_out();
			if(yaw_init_180 - now + 1>=yaw_init_180 - now && yaw_init_180 - now -1 < yaw_init_180 - now) break;
	}*/
	
	test1( yaw_init_180-180+ 42);
	car_stop();
	car_main(100,-100);
	delay_ms(1000);
	BEEP_delay(100, 2);
	
	track_adjust(speed);
	car_stop();
	BEEP_delay(100, 2);
	
	
	//test2(dd,speed);
		return;
}



//	任务4
void test4(float yaw_init, int speed){
	for(int i = 0; i<4;i++){
		
		test3(yaw_init,speed);	
	car_stop();
	BEEP_delay(100, 2);	
	}
}

6. main.c

#include "ti_msp_dl_config.h"
#include "BEEP.h"
#include "track.h"
#include "car.h"
#include <stdio.h>
#include <stdio.h>
//#include "oled.h"
#include "board.h"
#include "key.h"
//#define DELAY (32000000)

//	整体实现函数

//	直线调整方案
void yaw_adjust(float target, float now_yaw, int speed, int B);


//	寻迹
void track_adjust(int speed);


//	任务1
void test1(float yaw_init);

//任务2
void test2(float yaw_init,int speed);

//	任务3
void test3(float yaw_init, int speed);

void test4(float yaw_init, int speed);

//	通用变量
#define KEY_MAX 6
//int track[8];
int key[KEY_MAX + 1];


void receiveInt(int *receivedValue)
{
    uint8_t recvBuff[sizeof(int)] = {0}; // 接收缓冲区

    // 逐字节接收
    for (int i = 0; i < sizeof(int); i++)
    {
        // 接收一个字节
        recvBuff[i] = DL_UART_Main_receiveData(UART_1_INST);
    }

    // 将接收到的字节组合成一个 int
    *receivedValue = 0; // 初始化
    for (int i = 0; i < sizeof(int); i++)
    {
        *receivedValue |= (recvBuff[i] << (i * 8)); // 组合字节
    }
}


//获取姿态角
int yaw_out(){
		int  usrt_dd = 0;
		usrt_dd = DL_UART_Main_receiveData(UART_1_INST);
		usrt_dd  =usrt_dd*2 ;
	return usrt_dd;
}



int main(void)
{
	SYSCFG_DL_init(); //芯片资源初始化,由SysConfig配置软件自动生成
	car_init();
	
			
	BEEP_init();		//初始化声光
			
			
	while(1)
	{
		//car_main(300,300);
		KEY_number(key);
		//receiveInt(&usrt_dd);
		
		if(key[1] == 0){
			test1(yaw_out());
						car_stop();
						BEEP_delay(100, 2);
		}
		
		if(key[2] == 0){
			int yew_test2 = yaw_out();
			
			test2(yew_test2, 300);
				//track_adjust(300);
					//	car_stop();
					//	BEEP_delay(100, 2);
		}
		

		if(key[3] == 0){
			int yew_test2 = yaw_out();
			
			test3(yew_test2, 350);
				//track_adjust(300);
					//	car_stop();
					//	BEEP_delay(100, 2);
		}
		
		
		if(key[4] == 0){
			int yew_test2 = yaw_out();
			
			test4(yew_test2, 350);
				//track_adjust(300);
					//	car_stop();
					//	BEEP_delay(100, 2);
		}
	}
	
}
// 直线调整方案
void yaw_adjust(float target, float now_yaw, int speed, int B) {
    // 确保目标和当前偏航在 0-359 范围内
    if (target >= 360) target -= 360;
    if (now_yaw >= 360) now_yaw -= 360;

    // 计算调整量
    int adjust = target - now_yaw;

    // 归一化 adjust 到 -180 到 180 的范围
    if (adjust > 180) {
        adjust -= 360;
    } else if (adjust < -180) {
        adjust += 360;
    }

    // 调整电机速度
    car_main(speed - adjust * B, speed + adjust * B);
}

/*
//	直线调整方案
void yaw_adjust(float target, float now_yaw, int speed, int B){
	
	if(target >= 360) target = target - 360;
	if(now_yaw >= 360)	now_yaw = now_yaw-360;
	int adjust = target - now_yaw;
	//if(adjust >= )
		//如果旋转需要过大角度
	if(adjust >= 180 || adjust <= -180)
	{
			if(adjust > 0){
				adjust = 360 - adjust;
			}
			else{
					adjust = -adjust - 360;
			}
	}

		
	
	car_main(speed - adjust * B, speed + adjust * B);
	
}*/

void turn_yaw(float target, float now_yaw){
		

	while(1){
		int ccccc = 10;
			if(target - now_yaw >= 0) car_main(-ccccc,ccccc);
				else car_main(ccccc,-ccccc);
			
			now_yaw = yaw_out();
			if(target - now_yaw + 1>=target - now_yaw && target - now_yaw-1 < target - now_yaw) break;
	}
	
}

//	任务1
void test1(float yaw_init){
	//delay_ms(200);
	float pitch=0,roll=0,yaw=0;   //欧拉角
	float	now_yaw = yaw;

	
	
	int track[8];
	
	int speed = 400;	//	直线行驶速度
	int BS = 3;				//	偏角倍数
	
	int time_cnt = 0;	//	行驶时间计数
	int time_cnt_max = 32*1000*100;	//	32MHZ约等于	1s
	
	int target_time = 0;	//	当有疑似目标时,记录时间
	
	int write_min = 5;		//	当出现最小白色数时为疑似目标
	
	int white = 0;
	int en = 1;
	while(en == 1)
	{
		white = tracking_7(track);
		//获取欧拉角
		now_yaw = yaw_out();
			
		
		yaw_adjust(yaw_init, now_yaw, 300, BS);
		
		if(track[1] == 1 ) {
					car_main(100,-100);
			//delay_ms(2000);
			return;
		}
		if(track[7] == 1 ) {
			car_main(-100,100);
			//delay_ms(2000);
			return;
		}
		
		//delay_ms(5);
		

				if(white >= 1 )
				{			
					en = 0;
						return;
				}
	}
	
}



//任务2
void test2(float yaw_init,int speed)
{
		
		test1(yaw_init);
	
	
						car_stop();
						BEEP_delay(100, 2);
	
		track_adjust(speed);
					car_stop();
						BEEP_delay(100, 2);
	
	
	yaw_init = yaw_init + 180;
	if(yaw_init >= 360){
			yaw_init = yaw_init - 360;
	}
	

		int now = yaw_out();
		while(1){
		int ccccc = 10;
			if(yaw_init - now >= 0) car_main(-ccccc,ccccc);
				else car_main(ccccc,-ccccc);
			
			now = yaw_out();
			if(yaw_init - now + 1>=yaw_init - now && yaw_init - now -1 < yaw_init - now) break;
	}
	
		test1(yaw_init);
	
	car_stop();
						BEEP_delay(100, 2);
	
	
		track_adjust(speed);
				car_stop();
						BEEP_delay(100, 2);
	

return;
}




//	任务3
void test3(float yaw_init, int speed){
	int yaw_init_180 = yaw_init;
	if(yaw_init_180 >= 360) yaw_init_180 = yaw_init_180 - 360;
	
	
	yaw_init = yaw_init - 33;
	if(yaw_init < 0) yaw_init = 360-yaw_init;;
	turn_yaw(yaw_init,yaw_init);
	
	test1(yaw_init);
	car_stop();
	car_main(-100,100);
	delay_ms(1000);
	BEEP_delay(100, 2);
	
	track_adjust(speed);
	car_stop();
	BEEP_delay(100, 2);
	
	
	/*
		yaw_init = yaw_init - 90;
	if(yaw_init < 0){
			yaw_init = 360 + yaw_init;
	}*/
	/*
			int now = yaw_out();
	while(1){
		int ccccc = 10;
			if(yaw_init_180 - now >= 0) car_main(-ccccc,ccccc);
				else car_main(ccccc,-ccccc);
			
			now = yaw_out();
			if(yaw_init_180 - now + 1>=yaw_init_180 - now && yaw_init_180 - now -1 < yaw_init_180 - now) break;
	}*/
	
	test1( yaw_init_180-180+ 42);
	car_stop();
	car_main(100,-100);
	delay_ms(1000);
	BEEP_delay(100, 2);
	
	track_adjust(speed);
	car_stop();
	BEEP_delay(100, 2);
	
	
	//test2(dd,speed);
		return;
}



//	任务4
void test4(float yaw_init, int speed){
	for(int i = 0; i<4;i++){
		
		test3(yaw_init,speed);	
	car_stop();
	BEEP_delay(100, 2);	
	}
}


//    寻迹
void track_adjust(int speed)
{
    int track[8];
    int track_old[8];    //记录先前一次的寻迹表
    int Mobile_records =0;
    int BW = 0;
    int BW_N = 55;
    int cnt = 0;
    int cnt_max = 32*1000/2 + 5000 + 500 ;
    while(1)
    {
        int write = 0;
        write = tracking_7(track);
        
        if(track[1] == 1)    Mobile_records = -1;    //    需要左转+
        else if(track[7] == 1)    Mobile_records = 1;      //    需要右转
        else Mobile_records = 0;
        
        if(write <= 0 && Mobile_records == 0)    // 全为 0 且不是由寻迹不当导致
        {
            cnt++;
        }
        else    cnt = 0;
        
        if(cnt >= cnt_max) return;
        
        
        if(track[1] == 1)    BW = -5;
        else if(track[7] == 1) BW = 3;
        else if(track[2] == 1) BW = -3;
        else if(track[6] == 1) BW = 3;
        else if(track[3] == 1) BW = -1;
        else if(track[5] == 1) BW = 1;
        else if(track[4] == 1) BW = 0;
        
        car_main(speed + BW*BW_N, speed - BW*BW_N);
            
            
    }
}

六、总结

        在圆满完成本次任务之际,我们也不得不正视在项目实施过程中遭遇的几项挑战。首先,一个显著的问题是系统偶尔会出现卡死现象,初步分析后怀疑MCU可能遭遇了不明原因的复位。这一突发状况目前尚未明确其具体根源,但它无疑提醒我们在未来的设计和调试中,需要更加细致地检查电源稳定性、代码逻辑错误以及MCU的复位电路,确保系统的稳定运行。

        此外,我们还遭遇了MPU6050传感器数据不准确的问题。面对这一技术障碍,我们并未止步,而是积极寻求解决方案,最终通过创新性的方法绕过了这一难题,确保了项目的顺利推进。值得注意的是,虽然我们的处理方式并非严格遵循嘉立创的移植教程,但实践证明,这种灵活应对的策略同样有效。

        展望未来,我们将继续深化对MCU及传感器技术的理解,致力于提升系统的稳定性和精确度。同时,我们也将积极总结经验教训,优化开发流程,为下一次项目的顺利实施奠定更加坚实的基础。我们相信,通过不断学习和努力,我们能够克服更多技术难题,推动项目向更高水平迈进。

参考资料:

1. 立创·地猛星MSPM0G3507开发板

2. MPU6050稳定姿态获取(代码)

3. 24电赛H题主控代码(代码)

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

数据结构入门——04栈

1.栈 栈是限制在一端进行插入操作和删除操作的线性表&#xff08;俗称堆栈&#xff09; 允许进行操作的一端称为“栈顶”&#xff0c;另一固定端称为“栈底”&#xff0c;当栈中没有元素时称为“空栈”。 栈的特点 &#xff1a;后进先出LIFO&#xff08;Last In First Out&a…

支持I2C接口、抗干扰性强、14通道触摸按键的电容式触摸芯片-GTX314L

电容式触摸芯片 - GTX314L是具有多通道触发传感器的14位触摸传感器系列&#xff0c;它是通过持续模式提供中断功能和唤醒功能&#xff0c;广泛适用于各种控制面板应用&#xff0c;可直接兼容原机械式轻触按键的处理信号。 GTX314L芯片内部采用特殊的集成电路&#xff0c;具有高…

C++进阶-智能指针

1. 为什么需要智能指针&#xff1f; 下面我们先分析一下下面这段程序有没有什么内存方面的问题&#xff1f;提示一下&#xff1a;注意分析MergeSort函数中的问题。 int div() {int a, b;cin >> a >> b;if (b 0)throw invalid_argument("除0错误");retur…

【C语言】内存管理

C语言-内存管理 一、C进程内存布局二、栈内存1、存储在栈内存中的参数有哪些&#xff1f;2、栈内存的特点&#xff1f; 三、静态数据四、数据段与代码段五、堆内存 一、C进程内存布局 \qquad 任何一个程序&#xff0c;正常运行都需要内存资源&#xff0c;用来存放诸如变量、常量…

第九届“创客中国”武汉区域赛正式启幕 灵途科技勇夺前三,晋级决赛!

8月8日&#xff0c;第九届“创客中国”武汉区域赛正式启幕&#xff0c;首场聚焦先进制造领域。灵途科技勇夺先进制造领域专场企业组前三名&#xff0c;成功晋级决赛。 “创客中国”大赛是工业和信息化部组织开展的双创赛事活动&#xff0c;以构建产业链协同发展为出发点&#…

Win10 VisualStudio 2022编译ollvm 13.x

VisualStudio配置 1&#xff0c;正常配置C桌面环境 2&#xff0c;在单个组件中选择用于Windows得C Cmake工具 下载OLLVM13.x https://github.com/heroims/obfuscator/tree/llvm-13.x 解压后进入文件夹&#xff0c;命令行输入 cmake -G “Visual Studio 17 2022” -DLLVM_EN…

Java面试--设计模式

设计模式 目录 设计模式1.单例模式&#xff1f;2.代理模式&#xff1f;3.策略模式&#xff1f;4.工厂模式&#xff1f; 1.单例模式&#xff1f; 单例模式是Java的一种设计思想&#xff0c;用此模式下&#xff0c;某个对象在jvm只允许有一个实例&#xff0c;防止这个对象多次引…

依赖倒置原则:构建灵活软件架构的基石 - 通过代码实例深入解析

1.引言 1.1为什么要学习依赖倒置原则 在软件开发过程中&#xff0c;我们经常需要对代码进行修改和扩展。如果代码之间的耦合度过高&#xff0c;那么在进行修改或扩展时&#xff0c;可能会对其他部分的代码产生影响&#xff0c;甚至引发错误。这就要求我们在编写代码时&#xf…

【VS Code】 vue项目使用scss显示语法错误、build编译正常

开发vue项目&#xff0c;使用scss老是报这个错误 解决方式&#xff1a; 1.安装vetur 2.在vs code的设置中添加 "files.associations": { "*.vue": "vue" }解决&#xff1a;

线性规划约束一个矩形在Polygon内部

最近在用线性规划&#xff0c;有一个比较有趣的问题&#xff0c;记录一下思路。 如何用线性规划约束一个矩形在Polygon内部&#xff1f; 问题&#xff1a;有如下图蓝色矩形&#xff0c;用线性规划表示出绿色矩形被约束在polygon内部&#xff0c;矩形的中心坐标是(x, y),宽和高…

计算机组成原理---关于乘法电路与除法运算电路的理解

目录 一.乘法电路 1.无符号数乘法运算的硬件实现逻辑&#xff1a; 2.补码1位乘法运算的硬件实现逻辑&#xff1a; 3.无符号阵列乘法器 4.补码阵列乘法器 二.除法电路 1.原码除法运算 2.补码除法运算&#xff08;不恢复余数法&#xff09; 本篇是看湖科大与王道视频总结…

35_WebShell管理工具、中国蚁剑AntSword的安装及使用、御剑的使用、后台目录扫描

WebShell管理工具 WebShell 以asp、php、jsp或cgi等网页形式存在的一种代码执行环境主要用于网站和服务器管理由于其便利性和功能强大&#xff0c;被特别修改后的WebShell也被部分人当作网站后门工具使用国内常用的WebShell有海阳ASP木马&#xff0c;Phpspy&#xff0c;c99sh…

<Qt> 系统 - 事件

目录 前言&#xff1a; 一、事件介绍 二、事件的处理 &#xff08;一&#xff09;鼠标事件 1. 进入和离开事件 2. 鼠标点击事件 3. 释放事件 4. 双击事件 5. 移动事件 6. 滚轮事件 &#xff08;二&#xff09;键盘按键事件 1. 单个按键 2. 组合按键 &#xff08;…

如何判断监控设备是否支持语音对讲

目录 一、大华摄像机 二、海康摄像机 三、宇视摄像机 一、大华摄像机 注意&#xff1a;大华摄像机支持跨网语音对讲&#xff0c;即设备和服务器可以不在同一网络内&#xff0c;大华设备的语音通道填写&#xff1a;34020000001370000001 配置接入示例&#xff1a; 音频输入…

十日Python项目——第七日(商品购物车)

#前言&#xff1a; 在最近十天我会用Python做一个购物类电商项目&#xff0c;会用到DjangoMysqlRedisVue等。 今天是第六天&#xff0c;主要负责撰写编写关于商品购物车的编写&#xff0c;以及相应的增删改查。若是有不懂大家可以先阅读我的前六篇博客以能够顺承。 若是大家…

Github Copilot 使用技巧

&#x1f3af;目标读者 本文不包含如何安装 Github Copilot本文介绍了 Github Copilot 使用方法和一些技巧 本人已经使用 Github Copilot 2 年了&#xff0c;交了 3 次年费&#xff0c;每年 100$ 着实心痛&#xff0c;但是用着确实爽歪歪 但是感觉一直只用了一小部分功能&am…

(第二十七天)

上午 核心&#xff1a;内核中的 ipvs &#xff0c; ipvsadm 1 、安装 ipvsadm [rootnat ~] # yum -y install ipvsadm 2 、配置规则 查看所有的规则&#xff0c;如果已经配置好规则&#xff0c;重启之后也就没有了 [rootnat ~] # ipvsadm -L -n 1 、配置 vip 网卡 &…

服装租赁押金管理-押金原路退回系统开通方法

一、婚纱影楼服装租赁收押金必要性 1. 保障服装的按时归还&#xff1a; - 押金的存在能促使租客按时归还服装&#xff0c;避免因拖延归还影响后续的租赁业务。比如&#xff0c;在一些大型活动期间&#xff0c;服装租赁需求旺盛&#xff0c;如果租客不按时归还&#xff0c;…

HTML样式- CSS——WEB开发系列08

一、HTML 基础概述 HTML 用于创建网页的结构。网页的所有内容&#xff0c;例如文本、图像、链接、表单等&#xff0c;都是通过 HTML 标签来定义的。以下是一个简单的 HTML 文档结构示例&#xff1a; <!DOCTYPE html> <html lang"en"> <head><…

matlab 音频音量处理(音量大小按照dB调节)

1 音量(声压级)以分贝(dB)表示的计算公式为: 2 % 已知的 x 值 x = 0:-1:-127; % 在这里填入 x 的具体值% 计算 y %y = 10