stm32项目平衡车详解(stm32F407)

news2024/11/26 2:22:32

stm32项目

stm32项目介绍值平衡车
本文章学习借鉴于创客学院团队,以表感谢。教学视频


文章目录

  • stm32项目
  • 前言
  • 一、平衡小车
    • 平衡小车的功能介绍
    • 平衡小车功能开发需求
    • 平衡小车整体框架
    • 小车环境数据采集进程
      • 1. 平衡小车姿态信息介绍
      • 2. 平衡小车项目工程框架搭建
      • 3. Mpu6050姿态传感器驱动eMPL库使用
    • 小车PID控制进程
      • 1、PWM 直流电机驱动开发
        • 测试电机小demo 。
      • 2、正交码盘测试小车速度功能开发
        • 编码器测试小demo
      • 3、小车PID控制功能开发
        • 1. 控制车模平衡
        • 2. 控制车模速度-速度环PID调节
        • 3. 控制车模方向-转向环PID
      • 4、PID 计算函数(已整理)也可以csdn 上找类似的PID平衡车
    • 超声波避障小车功能
    • 巡线小车功能
    • 遥控小车
  • 总结


前言

提示:这里可以添加本文要记录的大概内容:

例如:随着人工智能的不断发展,机器学习这门技术也越来越重要,很多人都开启了学习机器学习,本文就介绍了机器学习的基础内容。


一、平衡小车

实物图如下所示各类平衡车
在这里插入图片描述

平衡小车的功能介绍

大容量电池供电
双马达电机驱动
平衡控制调节
速度控制调节
方向控制条件
上位机远程控制

平衡小车功能开发需求

在这里插入图片描述

  1. 平衡小车PID控制
    直立、速度、方向
  2. 平衡小车遥控功能
    无线通信功能、上位机App
  3. 平衡小车避障功能
    超声波测距
  4. 平衡小车巡线功能(线路轨迹运行)
    摄像头CCD循迹
  5. 菜单显示及功能设置
    OLED以及按键

效果如下图所示

在这里插入图片描述

平衡小车整体框架

平衡小车框架图如下
在这里插入图片描述
数据采集进程
我们在控制小车的前提是获取小车当前的状态数据,加速度,角速度,偏移角度等小车的姿态数据,进行数据采集进程。流程图如下:

在这里插入图片描述
小车用到的姿态传感器是mup6050姿态传感器。所以需要获取改姿态传感器的数据。

获取到这些数据后,可以通过PID进行控制、流程图如下:

在这里插入图片描述
避障模式应用到超声波测距功能

采集姿态----》进行控制-----》信息显示
OLED 显示模块 框架图如下:

在这里插入图片描述
设备流程完毕后,开始上机交互,交互进程框架图如下:

在这里插入图片描述

平衡小城用到的中断处理部分框图如下:

请添加图片描述
以上即平衡小车开发的框架图和流程图、便于我们的学习与管理。下面我们按部就班一步一步来实习上框图功能。

小车环境数据采集进程

1. 平衡小车姿态信息介绍

获取mpu6050 通过eMPL 库 获取需要的数据如下图:

在这里插入图片描述
mpu6050 姿态传感器之前介绍过,即欧拉角的获取。
不理解的可以看 点击查看,stm32 MPU6050 6轴姿态传感器的介绍与DMP的应用

俯仰角 (抬头)( pitch ): 围绕Y轴旋转的。

在这里插入图片描述

偏航角(摇头)(yaw):围绕Z轴旋转的角度。
在这里插入图片描述

翻滚角 (翻滚)(roll):围绕X轴旋转的角度

在这里插入图片描述

三轴传感器,x、y、z个方向的角度。如下图所示:
在这里插入图片描述
以上就是获取小车姿态的环境数据采集进程。

2. 平衡小车项目工程框架搭建

使用STM32CuBeMX 创建工程,完成配置。导出项目使用keil5软件打开运行。

3. Mpu6050姿态传感器驱动eMPL库使用

  1. 初始化MPU6050
  2. 移植MPU6050官方的eMPL库
  3. 获取原始数据以及姿态

注意 MPU6050 eMPL库 针对不同的平台需要调整 参考inv_mpu.c文件

MPU6050_eMPL 库文件夹目录如下图:

在这里插入图片描述

在这里插入图片描述
通过mpu_dmp_get_data()获取加速度和角速度通过四元素quat 转化为我们需要的欧拉角

u8 mpu_dmp_get_data(void); //获取角,加速度,以及欧拉角数据
u8 mpu_dmp_init(void); //初始化MPU6050


u8 mpu_dmp_init(void)
{
	u8 res=0;
	IIC_Init(); 		//初始化IIC总线
	if(mpu_init()==0)	//初始化MPU6050
	{	 
		res=mpu_set_sensors(INV_XYZ_GYRO|INV_XYZ_ACCEL);//设置所需要的传感器
		if(res)return 1; 
		res=mpu_configure_fifo(INV_XYZ_GYRO | INV_XYZ_ACCEL);//设置FIFO
		if(res)return 2; 
		res=mpu_set_sample_rate(DEFAULT_MPU_HZ);	//设置采样率
		if(res)return 3; 
		res=dmp_load_motion_driver_firmware();		//加载dmp固件
		if(res)return 4; 
		res=dmp_set_orientation(inv_orientation_matrix_to_scalar(gyro_orientation));//设置陀螺仪方向
		if(res)return 5; 
		res=dmp_enable_feature(DMP_FEATURE_6X_LP_QUAT|DMP_FEATURE_TAP|	//设置dmp功能
		    DMP_FEATURE_ANDROID_ORIENT|DMP_FEATURE_SEND_RAW_ACCEL|DMP_FEATURE_SEND_CAL_GYRO|
		    DMP_FEATURE_GYRO_CAL);
		if(res)return 6; 
		res=dmp_set_fifo_rate(DEFAULT_MPU_HZ);	//设置DMP输出速率(最大不超过200Hz)
		if(res)return 7;   
//		res=run_self_test();		//自检
//		if(res)return 8;    
		res=mpu_set_dmp_state(1);	//使能DMP
		if(res)return 9;     
	}
	printf("init ok");
	return 0;
}




/***************************************************************************************************************
*函数名:mpu_dmp_get_data()
*功能:得到dmp处理后的数据(注意,本函数需要比较多堆栈,局部变量有点多)
*形参:(struct _out_angle *angle):DMP解算得到的姿态
*返回值:0:成功/1:DMP_FIFO读取失败/2:数据读取失败
***************************************************************************************************************/

//声明这些全局变量,外部好使用

float pitch,roll,yaw;
u8 mpu_dmp_get_data(void)
{
	float q0=1.0f,q1=0.0f,q2=0.0f,q3=0.0f;

	short gyro_dmp[3], accel_dmp[3], sensors_dmp;
	unsigned long sensor_timestamp;
	unsigned char more;
	long quat[4]; 

	if(dmp_read_fifo(gyro_dmp, accel_dmp, quat, &sensor_timestamp, &sensors_dmp,&more)) return 1;	

//			printf("%d\n",dmp_read_fifo(gyro, accel, quat, &sensor_timestamp, &sensors,&more));
	/* Gyro and accel data are written to the FIFO by the DMP in chip frame and hardware units.
	 * This behavior is convenient because it keeps the gyro and accel outputs of dmp_read_fifo and mpu_read_fifo consistent.
	**/
	/*if (sensors & INV_XYZ_GYRO )
	send_packet(PACKET_TYPE_GYRO, gyro);
	if (sensors & INV_XYZ_ACCEL)
	send_packet(PACKET_TYPE_ACCEL, accel); */
	/* Unlike gyro and accel, quaternions are written to the FIFO in the body frame, q30.
	 * The orientation is set by the scalar passed to dmp_set_orientation during initialization. 
	**/
	if(sensors_dmp&INV_WXYZ_QUAT) 
	{
		q0 = quat[0] / q30;	//q30格式转换为浮点数
		q1 = quat[1] / q30;
		q2 = quat[2] / q30;
		q3 = quat[3] / q30; 
		//计算得到俯仰角/横滚角/航向角
		roll = asin(-2 * q1 * q3 + 2 * q0* q2)* 57.3;																	// pitch
		pitch  = atan2(2 * q2 * q3 + 2 * q0 * q1, -2 * q1 * q1 - 2 * q2* q2 + 1)* 57.3;	// roll
		yaw   = atan2(2*(q1*q2 + q0*q3),q0*q0+q1*q1-q2*q2-q3*q3) * 57.3;	//yaw
	
	}else return 2;
	
	//返回数据给全局变量 OutMpu
	//加速度
	OutMpu.acc_x =  accel_dmp[0];
	OutMpu.acc_y =  accel_dmp[1];
	OutMpu.acc_z =  accel_dmp[2];
	
	
	//角速度
	OutMpu.gyro_x =  gyro_dmp[0];
	OutMpu.gyro_y =  gyro_dmp[1];
	OutMpu.gyro_z =  gyro_dmp[2];
	
	//计算机国赋值给偏航角,俯仰角,翻滚角
	OutMpu.pitch =  pitch;
	OutMpu.roll =   roll;
	OutMpu.yaw = yaw;
	
	return 0;
}

在这里插入图片描述
在这里插入图片描述
平衡车扶平 ,按下复位键
在这里插入图片描述

数据采集到这里就结束了。下面开始PID控制

小车PID控制进程

在这里插入图片描述

通过PID控制两个车轮,使其前后运动,调节车辆平衡,直立工作,利用速度差就控制转向。

1、PWM 直流电机驱动开发

控制板如何驱动电机,通过pwm 来控制

在这里插入图片描述
速度越快,扭矩越小,速度慢扭矩越大,丽日汽车起步需要低速费力

直流减速电机:损耗速度来增大扭矩
在这里插入图片描述
H桥电路,方便控制电机正反转

MC3386电机驱动芯片,该芯片包含有H桥电路
在这里插入图片描述

IN1和IN2控制正反转,IN1 输入高电平电机正转,IN2输入高电平电机反转。IN1和IN2 均是通过PWM电压来控制转速。

下图为PWM 周期
在这里插入图片描述
在这里插入图片描述

测试电机小demo 。

流程使用cubemx 创建新项目,使用keil5 调试程序,电机运转,加速减速。

  1. 创建工程
    在这里插入图片描述

  2. 设置时钟
    在这里插入图片描述

在这里插入图片描述

  1. 设置串口用于输出printf

在这里插入图片描述
4. 配置电机引脚

在这里插入图片描述
按照要求配置cube 工程
在这里插入图片描述
在这里插入图片描述
同理根据要求配置电机2

在这里插入图片描述

  1. 配置CCR (比较寄存器的值)的值,控制占空比,,控制输出的平均电压,将周期设置为8000,方便后面的pid 计算
    TIM4 配置RCC (电机1)
    在这里插入图片描述
    TIM5 配置RCC(电机2)
    在这里插入图片描述

    控制电机的速度示例代码如下

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */
	
  int	 pwm_value = 1000, temp =0;
	

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_TIM4_Init();
  MX_TIM5_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */
	
	
	//设置电机1的端口输出电平 用于电机2端
	HAL_GPIO_WritePin(GPIOC, GPIO_PIN_3, GPIO_PIN_RESET); //设置为低电平
	HAL_GPIO_WritePin(GPIOA, GPIO_PIN_3, GPIO_PIN_SET);   //设置为高电平
	//pwm设置来控制电机
	//启动pwm
	HAL_TIM_PWM_Start(&htim5, TIM_CHANNEL_3);
	
	//电机2配置
	HAL_GPIO_WritePin(GPIOE, GPIO_PIN_15, GPIO_PIN_RESET); //设置为低电平
	HAL_GPIO_WritePin(GPIOE, GPIO_PIN_13, GPIO_PIN_SET);   //设置为高电平
	//pwm设置来控制电机
	//启动pwm
	HAL_TIM_PWM_Start(&htim4, TIM_CHANNEL_1);
	



  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */
		pwm_value = pwm_value + temp;
		
		if(pwm_value >= 8000)
			temp = -100;
		else
			temp = 100;
		
		
		TIM5->CCR3 = pwm_value;
		
		TIM4->CCR1 = pwm_value;
		
		printf("pwm_value =  %d\n",pwm_value);
		HAL_Delay(500);
		
		
    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

上述即电机小demo 测试

2、正交码盘测试小车速度功能开发

正交码盘
在这里插入图片描述

原理理解说明
光码盘上有一周的孔,一侧有光敏检测器。另一侧有光源,光源通过孔讲光透过,并会在光敏接受器接收到,每接受一次,产生一个脉冲信号(如上图左侧所示),电动机转动一圈,产生的脉冲信号与孔的数量有关。
即可以得出结论:小车转动一圈产生的脉冲是固定的,控制板接受的脉冲次数可以计算小车的转圈次数,同时可以获取小车速度。

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
每个电机都有一个编码器,每个编码器输出2路编码器光信号。在这里插入图片描述

编码器测试小demo

  1. 创建工程
    在这里插入图片描述
    设置时钟,配置串口 略写

  2. 配置2个电机的编码器的定时器
    在这里插入图片描述
    同理配置TIM2

  3. 计数周期值得配置RCC

在这里插入图片描述

配置成65535 原因

#include <stdio.h>
int main()
{
     short i = 65535;
     printf("i=%d\n", i);
     return 0;
}
结果:-1

分析:因为内存中65535存储内容的16进制表示为:0x0FFFF,将此值传递给16位的变量i时,i只能接受到0xFFFF;看见首位为1,编译器会认为i是个负值,至于负值的绝对值=源码取反(0x0000+1 = 0x0001。因此最终输出-1int main()
{
     short i = 65536;
     printf("i=%d\n", i);
     return 0;
}

结果:0
分析:因为内存中65536存储内容的16进制表示为:0x010000,将此值传递给16的i的时候,i接受到0x0000,编译器认为i=0;

即,获取的值可以根据正负数判断正反转以及转数。

配置编码器进入的IO口的数量,1路或者2路
在这里插入图片描述

在这里插入图片描述
4. 导出工程项目名

int main(void)
{
  /* USER CODE BEGIN 1 */
	int  encoder_left,encode_right;
  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_TIM1_Init();
  MX_TIM2_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */
	
	//启动编码器
	HAL_TIM_Encoder_Start(&htim1, TIM_CHANNEL_ALL);
	HAL_TIM_Encoder_Start(&htim2, TIM_CHANNEL_ALL);
	

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
		//正转是正数,反转是负数
		encoder_left = (short)TIM1->CNT; //记录的脉冲的个数.65535 
		encode_right = (short)TIM2->CNT;
    /* USER CODE END WHILE */
		
		TIM1->CNT = 0;
		TIM2->CNT = 0;
		
		HAL_Delay(1000);
    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

3、小车PID控制功能开发

在这里插入图片描述

在这里插入图片描述在这里插入图片描述

在这里插入图片描述
按照之前的模块,分别把启动电机和启动编码器模块整理在项目中。引入已整理的PID控制文件(下面已发)
在这里插入图片描述

int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USART1_UART_Init();
  MX_TIM1_Init();
  MX_TIM2_Init();
  MX_TIM4_Init();
  MX_TIM5_Init();
  /* USER CODE BEGIN 2 */
	
  printf("平衡小车开发项目\n");
	
	//启动PWM
	HAL_TIM_PWM_Start(&htim5, TIM_CHANNEL_3);
	HAL_TIM_PWM_Start(&htim4, TIM_CHANNEL_1);
	
	//启动编码器
	HAL_TIM_Encoder_Start(&htim1, TIM_CHANNEL_ALL);
	HAL_TIM_Encoder_Start(&htim2, TIM_CHANNEL_ALL);
  /* USER CODE END 2 */

  /* Call init function for freertos objects (in freertos.c) */
  MX_FREERTOS_Init();
  /* Start scheduler */
  osKernelStart();

  /* We should never get here as control is now taken by the scheduler */
  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

根据小车的平衡任务分解,我们只是用-直立环PD控制,速度环PI控制,转向环PD

1. 控制车模平衡

直立环的使用 -直立环PD控制,车轮调整方向与倾斜方向许一致,才能保证小车平衡。
倾斜使用的是俯仰角。
在这里插入图片描述

/**************************************************************************************************************
*函数名:Vertical_Ring_PD()
*功能:直立环PD控制
*形参:(float Angle):x轴的角度/(float Gyro):x轴的角速度
*返回值:经过PID转换之后的PWM值
**************************************************************************************************************/
//直立环的PD
int	Vertical_Ring_PD(float Angle,float Gyro)
{
	 float Bias;
	 int balance;
   //偏差 Bias
   //当前角度 Angle 
   //调整后的角度 Mechanical_balance	(理想状态是平衡的0度) 
   Bias=Angle-Mechanical_balance; //计算直立偏差
   //Balance_Kp  
   //Balance_Kd 
   balance=PID.Balance_Kp*Bias+ Gyro*PID.Balance_Kd;  //计算直立PWM
	
	return balance;  //pwm 值
		
	 //printf("balance = %f\n",balance);
}

求值 /Balance_Kp //Balance_Kd
在这里插入图片描述
在这里插入图片描述
balance=PID.Balance_KpBias+ GyroPID.Balance_Kd;
balance 最大值,为电机最大速度的pwm ,周期值-最大静启动值(电机死区)
例如 我们设置pwm 周期为8000,最大死区为1200,则最大的pwm 为5800
Bias=10,偏移角度,我们设置为10度为最大角度,超过10度小车无法通过移动调节平衡
Balance_Kd = 0
balance=PID.Balance_KpBias+ GyroPID.Balance_Kd;
balance=PID.Balance_Kp10+ Gyro0;
则Balance_Kp = 580 最大值

在这里插入图片描述

电机死区

电机的运动,我们同步pwm 不断的增加,从静止到转动,再到加速,从静止到转动有一个摩擦阻止,类似于物理的静摩擦力一样。

2. 控制车模速度-速度环PID调节

在这里插入图片描述

/**************************************************************************************************************
*函数名:Vertical_speed_PI()
*功能;速度环PI控制
*形参:(int encoder_left):左轮编码器值/(int encoder_right):编码器右轮的值/(float Angle):x轴角度值
*返回值:
**************************************************************************************************************/

int Vertical_speed_PI(int encoder_left,int encoder_right,float Angle,float Movement )
{
	static float Velocity,Encoder_Least,Encoder;
	static float Encoder_Integral;
	Encoder_Least =(encoder_left+encoder_right)-0;    //获取最新速度偏差=测量速度(左右编码器之和)-目标速度(此处为零)
	Encoder *= 0.8f;																	//一阶低通滤波器 ,上次的速度占85%
	Encoder += Encoder_Least*0.2f;                   //一阶低通滤波器, 本次的速度占15% 
	Encoder_Integral +=Encoder;                       //积分出位移 积分时间:10ms
	Encoder_Integral=Encoder_Integral-Movement; 
	
	if(Encoder_Integral>10000)  	Encoder_Integral=10000;           //积分限幅
	if(Encoder_Integral<-10000)	  Encoder_Integral=-10000;            //积分限幅

	Velocity=Encoder*PID.Velocity_Kp+Encoder_Integral*PID.Velocity_Ki;      //速度控制
	
	
	if(Turn_off(Angle)==1)   Encoder_Integral=0;            //电机关闭后清除积分
	return Velocity;
}

在这里插入图片描述

Velocity=EncoderPID.Velocity_Kp+Encoder_IntegralPID.Velocity_Ki; //速度控制
需确定极限值
ki = kp/200
kp 最大值=6000/(160*40%)=93.75
在这里插入图片描述
实验需要通过数据的改变来确定我们需要的一个值,
.Velocity_Kp=-?,
.Velocity_Ki=- ?,
当Velocity_Kp 为负值,两轮才能同步方向,维持平衡。
.Velocity_Kp=-52,
.Velocity_Ki=-0.26,

/**************************************************************************************************************
*函数名:Read_Encoder()
*功能:读取编码器值(当作小车当前前进的速度)
*形参:(u8 TIMX):x为编码器1或者2
*返回值:无
*************************************************************************************************************/
int Read_Encoder(u8 TIMX)
{
    int Encoder_TIM;  
		
   switch(TIMX)
	 {
	   case 1:  Encoder_TIM= (short)TIM1 -> CNT;  TIM1 -> CNT=0;break;
		 case 2:  Encoder_TIM= (short)TIM2 -> CNT;  TIM2 -> CNT=0;break;
		 default:  Encoder_TIM=0;
	 }
		return Encoder_TIM;
}

在这里插入图片描述

3. 控制车模方向-转向环PID

2轮平衡车的转向,类似于坦克履带一样,利用速度差进行转向

/**************************************************************************************************************
*函数名:Vertical_turn_PD()
*功能:转向环PD
*形参:无  CCD小于34左转、CCD大于64右转。 yaw = z轴陀螺仪数值
*返回值:无
***************************************************************************************************************/
int Vertical_turn_PD(u8 CCD,short yaw)
{
	  float Turn;     
      float Bias;	  
	  Bias=CCD-64;
	  Turn=-Bias*PID.Turn_Kp-yaw*PID.Turn_Kd;
	  return Turn;
}
   Bias  目标角度。比如转向30度
   yaw Z轴的角度
  CCD 目标角度,正负来确定,转向
  Turn=-Bias*PID.Turn_Kp-yaw*PID.Turn_Kd;

Turn=-BiasPID.Turn_Kp-yawPID.Turn_Kd;

Balance_Pwm+Vertical_Pwm-Trun_Pwm = 6000;
平均取中间3000值为最大,
Turn=-BiasPID.Turn_Kp-yawPID.Turn_Kd;
Turn_Kp = 100*Turn_Kd
理想效果就是原地旋转,转向。

4、PID 计算函数(已整理)也可以csdn 上找类似的PID平衡车

功能函数

  • 直立环PD控制
  • 读取编码器值(当作小车当前前进的速度)
  • 速度环PI控制
  • 转向环PD
  • PWM限幅函数
  • 关闭电机
  • 输出PWM控制电机

.C文件

#include "math.h"
#include "stdlib.h"
#include "stm32f4xx_hal.h"
#include "contrl.h"

 



int   Dead_Zone=1200;    //电机死区  电机静摩擦转动值,达到后,,才会运动,
int   control_turn=64;   //转向控制


//PID调节参数
struct pid_arg PID = {
	.Balance_Kp=200,
	.Balance_Kd=1,
	.Velocity_Kp=-52,
	.Velocity_Ki=-0.26,
	.Turn_Kp = 18,
	.Turn_Kd = 0.18,
};

/**************************************************************************************************************
*函数名:Read_Encoder()
*功能:读取编码器值(当作小车当前前进的速度)
*形参:(u8 TIMX):x为编码器1或者2
*返回值:无
*************************************************************************************************************/
int Read_Encoder(u8 TIMX)
{
    int Encoder_TIM;  
		
   switch(TIMX)
	 {
	   case 1:  Encoder_TIM= (short)TIM1 -> CNT;  TIM1 -> CNT=0;break;
		 case 2:  Encoder_TIM= (short)TIM2 -> CNT;  TIM2 -> CNT=0;break;
		 default:  Encoder_TIM=0;
	 }
		return Encoder_TIM;
}



/**************************************************************************************************************
*函数名:Vertical_Ring_PD()
*功能:直立环PD控制
*形参:(float Angle):x轴的角度/(float Gyro):x轴的角速度
*返回值:经过PID转换之后的PWM值
**************************************************************************************************************/
//直立环的PD


int	Vertical_Ring_PD(float Angle,float Gyro)
{
	 float Bias;
	 int balance;
   Bias=Angle-Mechanical_balance;
   balance=PID.Balance_Kp*Bias+ Gyro*PID.Balance_Kd;
	
	return balance;
		
	 //printf("balance = %f\n",balance);
}


/**************************************************************************************************************
*函数名:Vertical_speed_PI()
*功能;速度环PI控制
*形参:(int encoder_left):左轮编码器值/(int encoder_right):编码器右轮的值/(float Angle):x轴角度值
*返回值:
**************************************************************************************************************/

int Vertical_speed_PI(int encoder_left,int encoder_right,float Angle,float Movement )
{
	static float Velocity,Encoder_Least,Encoder;
	static float Encoder_Integral;
	Encoder_Least =(encoder_left+encoder_right)-0;    //获取最新速度偏差=测量速度(左右编码器之和)-目标速度(此处为零)
	Encoder *= 0.8f;																	//一阶低通滤波器 ,上次的速度占85%
	Encoder += Encoder_Least*0.2f;                   //一阶低通滤波器, 本次的速度占15% 
	Encoder_Integral +=Encoder;                       //积分出位移 积分时间:10ms
	Encoder_Integral=Encoder_Integral-Movement; 
	
	if(Encoder_Integral>10000)  	Encoder_Integral=10000;           //积分限幅
	if(Encoder_Integral<-10000)	  Encoder_Integral=-10000;            //积分限幅

	Velocity=Encoder*PID.Velocity_Kp+Encoder_Integral*PID.Velocity_Ki;      //速度控制
	
	
	if(Turn_off(Angle)==1)   Encoder_Integral=0;            //电机关闭后清除积分
	return Velocity;
}


/**************************************************************************************************************
*函数名:Vertical_turn_PD()
*功能:转向环PD
*形参:无  CCD小于34左转、CCD大于64右转。 yaw = z轴陀螺仪数值
*返回值:无
***************************************************************************************************************/
int Vertical_turn_PD(u8 CCD,short yaw)
{
		float Turn;     
    float Bias;	  
	  Bias=CCD-64;
	  Turn=-Bias*PID.Turn_Kp-yaw*PID.Turn_Kd;
	  return Turn;
}



/**************************************************************************************************************
*函数名:PWM_Limiting()
*功能:PWM限幅函数
*形参:无
*返回值:无
***************************************************************************************************************/
void PWM_Limiting(int *motor1,int *motor2)
{
	int Amplitude=5800;
	if(*motor1<-Amplitude) *motor1=-Amplitude;	
	if(*motor1>Amplitude)  *motor1=Amplitude;	
	if(*motor2<-Amplitude) *motor2=-Amplitude;	
	if(*motor2>Amplitude)  *motor2=Amplitude;		
}


/**************************************************************************************************************
*函数名:Turn_off()
*功能:关闭电机
*形参:(const float Angle):x轴角度值
*返回值:1:小车当前处于停止状态/0:小车当前处于正常状态
***************************************************************************************************************/
u8 FS_state;

u8 Turn_off(const float Angle)
{
	u8 temp;
	if(fabs(Angle)>80){  //当小车角度已经达到80度,我们泽得出小车倒地,关闭电机,
		FS_state=1;
		temp=1;
		AIN2(0),			AIN1(0);
		BIN1(0),			BIN2(0);
	}
	else 
		temp=0;
		FS_state=0;
	return temp;
}

/**************************************************************************************************************
*函数名:Set_PWM()
*功能:输出PWM控制电机
*形参;(int motor1):电机1对应的PWM值/(int motor2):电机2对应的PWM值
*返回值:无
*************************************************************************************************************/


void Set_PWM(int motor1,int motor2)
{
	if(motor1>0)			AIN2(1),			AIN1(0);
	else 	          	AIN2(0),			AIN1(1);
	PWMA=Dead_Zone+(abs(motor1))*1.17;
	
	
	if(motor2>0)			BIN1(1),			BIN2(0);
	else       		 		BIN1(0),			BIN2(1);
	PWMB=Dead_Zone+(abs(motor2))*1.17;	
	
//	printf("PWMA = %d\n",PWMA);
//	printf("PWMB = %d\n",PWMB);
}




.H文件

#ifndef _CONTRIL_H_
#define _CONTRIL_H_

#include "sys.h"


//机械0点
#define Mechanical_balance 0

#define AIN1(PinState)    HAL_GPIO_WritePin( GPIOE, GPIO_PIN_13, (GPIO_PinState)PinState)
#define AIN2(PinState)    HAL_GPIO_WritePin( GPIOE, GPIO_PIN_15, (GPIO_PinState)PinState)

#define BIN1(PinState)    HAL_GPIO_WritePin( GPIOC, GPIO_PIN_3, (GPIO_PinState)PinState)
#define BIN2(PinState)    HAL_GPIO_WritePin( GPIOA, GPIO_PIN_3, (GPIO_PinState)PinState)

#define PWMA   TIM4->CCR1 
#define PWMB   TIM5->CCR3



extern volatile int Encoder_Left,Encoder_Right;		      //编码器左右速度值

struct pid_arg{
	
	float Balance_Kp;
	float Balance_Ki;
	float Balance_Kd;
	
	float Velocity_Kp;
	float Velocity_Ki;
	float Velocity_Kd;
	
	float  Turn_Kp;
	float  Turn_Ki;
	float  Turn_Kd;

};
extern struct pid_arg PID;

int Read_Encoder(u8 TIMX);
int	Vertical_Ring_PD(float Angle,float Gyro);
int Vertical_speed_PI(int encoder_left,int encoder_right,float Angle,float Movement );
int Vertical_turn_PD(u8 CCD,short yaw);


void PWM_Limiting(int *motor1,int *motor2);
u8 Turn_off(const float Angle);
void Set_PWM(int motor1,int motor2);


#endif

超声波避障小车功能

下一节单独讲解,本次内容过载太长

巡线小车功能

下一节单独讲解,本次内容过载太长

遥控小车

下一节单独讲解,本次内容过载太长


总结

1、理解平衡小车的工作原理,
2、学会使用cubumx 搭建项目,
3、掌握mpu6050 项目工作中使用
4、PID 控制的使用以及原理。

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

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

相关文章

【面试题】原型和原型链

1. 如何用class实现继承 // 父类 class People{constructor(name){this.name name}eat(){console.log(${this.name} eat something)} }// 子类 class Student extends People{constructor(name, number){super(name)this.number number}sayHi(){console.log(姓名&#xff1a…

自动化脚本如何切换环境?Pytest这些功能你必须要掌握

文章目录一、前言二、安装三、使用第1种:使用方式是终端添加–base-url这个命令第2种:使用方式是在pytest.ini配置文件种去配置base_url,然后自动读取url的数据&#xff0c;这样就不用添加–base-url这个命令行参数了&#xff1a;第3种:pytest有个hooks函数&#xff0c;可以自定…

最优二叉搜索树问题(Java)

最优二叉搜索树问题&#xff08;Java&#xff09; 文章目录最优二叉搜索树问题&#xff08;Java&#xff09;1、前置介绍2、算法设计思路2.1 最优二叉搜索树的结构2.2 一个递归算法2.3 计算最优二叉搜索树的期望搜索代价3、代码实现4、复杂度分析5、参考资料1、前置介绍 设S{x…

R语言探索 BRFSS 数据和预测

加载包 library(ggplot2) library(dplyr) library(Hmisc) library(corrplot) 加载数据 load("brfss2013.RData") 第1部分&#xff1a;关于数据 行为风险因素监测系统&#xff08;BRFSS&#xff09;是美国的年度电话调查。BRFSS旨在识别成年人口的风险因素并报告…

docker启动出现Error response from daemon: Cannot restart container的报错

1、发现问题 突然发现启动(重启)容器的时候报这个错 Error response from daemon: Cannot restart container 容器id: driver failed programming external connectivity on endpoint 容器名 (容器id): (iptables failed: iptables --wait -t nat -A DOCKER -p tcp -d 0/0 --…

图像超分辨率:优化最近邻插值Super-Resolution by Predicting Offsets

文章目录3. Super-Resolution by Predicting Offsets3.1. 这篇论文用于处理栅格化图像的超分&#xff0c;不知道这样翻译对不对&#xff0c;3.2. 作者认为栅格图像的边缘比较规则&#xff0c;可以训练一个offset map移动栅格图像的 边缘点&#xff08;背景和前景像素 移动 和交…

能率携手梦想改造家,打造适老化住宅新典范

家装改造类节目《梦想改造家》第九季温情回归&#xff0c;日本一级建筑设计师本间贵史携手知名燃热品牌能率&#xff0c;与节目组一起关注民生&#xff0c;走进由一家五口组成的“足不出户的家”&#xff0c;共启老宅改造计划&#xff0c;倾情助力普通家庭拥抱生活与梦想&#…

(Matlab实现)蚂蚁狮子优化算法在电力系统中的应用

目录 1 知识一网打尽 2 蚂蚁狮子优化算法在电力系统经济调度中的应用 3 运行结果 4 Matlab代码实现 1 知识一网打尽 这里总结一位博主的电力系统经济调度目录 蚂蚁狮子优化算法&#xff08;完整Matlab代码实现&#xff09; 多目标蚂蚁狮子优化算法&#xff08;Matlab代码…

拒绝灵感焦虑,藏在UI设计师书签里的宝藏网站!

都在说UI设计“越来越吃香”&#xff0c;导致其他门类的设计师一心想转行。 上次和入行8年的UI大佬聊天&#xff0c;她告诉小摹3条UI设计师必备能力&#xff1a; 审美能力和眼界&#xff1a;一个界面好不好看最后都是UI来定&#xff0c;为了不背锅&#xff0c;UI一定要有国际流…

dolphinscheduler2.0.5性能手动测试

目录&#x1f42c;官方配置文件说明&#x1f42c;测试并发量&#x1f420;线程数量设置100&#x1f420;线程数量设置200&#x1f420;线程数量设置500&#x1f42c;测试结论&#x1f42c;官方配置文件说明 官方说明 master.exec.threads&#xff1a; master工作线程数量,用于…

【2013】408联考数据结构真题整理

2013年 1 题目 解析 原始&#xff1a;升序 升序 变 升序 尾插法 改编&#xff1a;升序 升序 变 降序 头插法 2 题目 答案&#xff1a;C 解析 3 题目 答案&#xff1a;B 解析 二叉排序树&#xff0c;或者是空树&#xff0c;或者是满足以下性质的二叉树&#xff1a; …

基于PHP+MySQL音乐网站的设计与实现

随着时代的发展,音乐已经逐渐成为了人们生活中必不可少的一种调剂品,人们对音乐的追求也越来越强烈,为此我通过PHP和MYSQL开发了本音乐网站 本音乐网站是一个综合性的音乐分享网站,它主要实现了娱乐新闻,上榜歌手,音乐试听,音乐下载,下载排行,音乐库,在线留言等基本的音乐共享功…

Matplotlib绘制折线图、散点图、柱状图、直方图、饼图代码

一、折线图 以折线的上升或下降来表示统计数量的增减变化的统计图 特点&#xff1a;能够显示数据的变化趋势&#xff0c;反映事物的变化情况(变化)函数&#xff1a;plt.plot(x, y) import matplotlib.pyplot as plt import randomfrom pylab import mpl mpl.rcParams["f…

day31 文件上传js验证mimeuser.ini语言特性

前言 #知识点&#xff1a; 1、文件上传-前端验证 2、文件上传-黑白名单 3、文件上传-user.ini妙用 4、文件上传-PHP语言特性 #详细点&#xff1a; 1、检测层面&#xff1a;前端&#xff0c;后端等 2、检测内容&#xff1a;文件头&#xff0c;完整性&#xff0c;二次渲染…

基于MATLABsimulink的《电路原理》课程仿真实验平台开发

目 录 摘 要 I Abstract II 第一章 绪论 1 1.1选题背景及意义 1 1.2设计内容 1 1.3设计思想 2 第二章 MATLAB简介 3 2.1 MATLAB程序设计 3 2.2 Simulink仿真 4 2.2.1 Simulink 启动 4 2.2.2 Simulink 模块库及模块操作 4 2.2.3 仿真参数设置 6 2.3图形用户界面&#xff08;GUI&…

基于STM32的温控风扇

本设计是基于STM32的温控风扇&#xff0c;主要实现以下功能&#xff1a; 温度控制风速&#xff0c;四个挡位&#xff0c;停止、低速、中速、高速 按键可切换模式&#xff0c;可手动切换挡位&#xff0c;四个挡位&#xff0c;停止、低速、中速、高速 按键设置温度值&#xff0c…

网络刷卡器开发,刷新移动物联新生活

在物联网应用需求和身份校验普及的影响下&#xff0c;沐渥自主研发生产了一款基于网络协议传输的读卡设备——网络刷卡器&#xff0c;这是一款体积小巧&#xff0c;方便携带&#xff0c;即插即用&#xff0c;无需安装驱动&#xff0c;采用USB通讯&#xff0c;即刻响应对接客户的…

Linux系统漏洞本地提权

目录 一、实验项目名称 二、实验目的 三、实验内容 四、实验环境 五、实验步骤 六、实验结果 七、实验总结 一、实验项目名称 Linux系统漏洞本地提权及跳板设置实验 二、实验目的 1.msf工具的使用&#xff1b; 2.“脏牛”漏洞CVE-2016-5195漏洞利用方法。 三、实验…

MCE | 铁死亡——调节性细胞死亡

多细胞生物中&#xff0c;调节性细胞死亡过程 (RCD) 是细胞维持组织形态和功能必不可少的稳态机制。此前研究较多的调节性细胞死亡包括三大类&#xff1a;细胞凋亡、自噬和坏死。 “铁死亡”这一概念最早在 2012 年由 Dr. Brent R Stockwell 提出&#xff0c;它是一种铁离子依赖…

ORM概念

ORM概念 ORM是Object Relational Mapping 对象关系映射。简单来说&#xff0c;就是把数据库表和实体类及实体类的属性对应起来&#xff0c;让开发者操作实体类就实现操作数据库表。 ORM(Object Relation Mapping)对象关系映射 思想&#xff1a;将关系数据库中表中的记录映射为对…