stm32项目平衡车详解(stm32F407)上

2023-11-03

stm32项目

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



前言

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

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


一、平衡小车

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

平衡小车的功能介绍

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

平衡小车功能开发需求

在这里插入图片描述

  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 控制的使用以及原理。

下一节,stm32项目平衡车详解(stm32F407)下

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

stm32项目平衡车详解(stm32F407)上 的相关文章

随机推荐

  • rabbitmq基础5——集群节点类型、集群基础运维,集群管理命令,API接口工具

    文章目录 一 集群节点类型 1 1 内存节点 1 2 磁盘节点 二 集群基础运维 2 1 剔除单个节点 2 1 1 集群正常踢出正常节点 2 1 2 服务器异常宕机踢出节点 2 1 3 集群正常重置并踢出节点 2 2 集群节点版本升级 2
  • Redis漏洞总结

    Redis简介 redis是一个key value存储系统 和Memcached类似 它支持存储的value类型相对更多 包括string 字符串 list 链表 set 集合 zset sorted set 有序集合 和hash 哈希类型
  • 通过执行在 shell脚本中的命令,进入指定目录:source命令、点命令

    目录 在shell脚本里切换目录 执行shell脚本进入指定目录 Linux下通过Shell脚本快速进入指定目录 参考 Linux下通过Shell脚本快速进入指定目录 https blog csdn net han8040laixin ar
  • 智能制造中的高频应用技术之回归模型

    前言 当我们把基于数据驱动的方法和人工智能等技术引入工业 制造业领域时 会发现这样的应用场景与一般应用场景的一个重要差异是重视回归模型 一般AI应用场景与商业数据分析场景下 我们相对重视分类 聚类模型 这些模型对我们希望认知的目标形成定性判
  • 海外版“咸鱼”Carousell是什么?

    做跨境的都知道 一定不能只在一颗树上吊死 潮流总是把你推着向前的 现在跨境电商平台一样层出不穷 今天就来跟大家分享最近发现比价好做的外贸电商平台APP Carousell 号称海外版 咸鱼 它的功能也与咸鱼是差不多的 细心的老板会发现 咸鱼
  • 在c语言编程中float类型怎么比较大小?

    比较实数的大小时 由于一般情况下一个实数不能用有限位的二进制表示 所以会存在误差 当我们要比较二个实数是否相等时 不要使用 的比较运算符 而是用二个实数差的绝对值是否小于一个我们限定的值 比如比较一个float型与0的大小 应该用 if x
  • Error in created hook: “Error: Initialize failed: invalid dom.“

    报错解析 初始化时没有获取到DOM元素 原因分析 我这边在 created l里做了一个refresh的一个定时刷新方法 但因为我将 refresh 方法放到后面就导致先是加载了 生产统计 能源监控 质量墙 生产进度监控 等这几个方法 最后
  • Go语言面试题--进阶语法(31)

    文章目录 1 关于 channel 下面描述正确的是 2 下面的代码有几处问题 请详细说明 3 下面的代码有什么问题 4 下面的代码输出什么 1 关于 channel 下面描述正确的是 A 向已关闭的通道发送数据会引发 panic B 从已
  • vue遍历输出列表中以逗号隔开的字符串

    问题描述 后台接口返回一个以逗号隔开的字符串 在列表中需要将逗号隔开的字符串便利并将对应的用户编号转换成为用户姓名 代码展示 列表代码
  • Spring事务(三)——传播属性之REQUIRED

    事务是与连接对象紧密相关的 事务属性用来控制事务流转 Spring事务的传播属性有以下几种 Propagation REQUIRED 如果当前没有事务 就新建一个事务 如果已经存在一个事务中 则加入到这个事务中 默认属性 也是最常使用 Pr
  • Linux中修改MAC地址和IP地址的命令(netset、ip、ifconfig)

    修改MAC地址 ip link set eth0 address aa aa aa aa aa aa 解析 eth0是网卡的名字 可以用ifconfig命令查看 aa aa aa aa aa aa是想要修改后的MAC地址 修改IP地址 1
  • webBrowser对跳转后的页面的处理

    代码 using System using System Collections Generic using System ComponentModel using System Data using System Drawing usin
  • 浏览器检查页面的几种方式 b7faf554fecf4718b9bf5035708ff528

    浏览器检查页面的几种方式 前言 初学前端和爬虫的小伙伴应该有会遇到很多让人很头疼的小问题 例如 再遇到一个很有趣的网页时 想要探究一下这个网站的页面构造 但是在尝试鼠标右键时却无法像其他网站一样出菜单 在比如要对某个网站进行抓包 想要进入开
  • AIGC时代的王者:微软

    从2022年到2023年这不到一年的时间 AI圈经历两大爆火事件 Stable diffusion和chatGPT 正式宣布AIGC时代的到来 在这背后有一家一直被认为科技创新性较弱的公司在默默的努力着 田字牌即微软 chatGPT爆火之前
  • 原来tuxera ntfs for Mac激活码这么容易获取

    因为Tuxera NTFS可以帮助我们在Mac上完全读写NTFS磁盘 所以非常受大家的欢迎 这款软件可以使用一段时间 让大家体验它的便捷性 不过在试用期过去之后就需要我们激活这款软件才可以继续使用的 那么怎么才能正确获得Tuxera NTF
  • 生成4位不重复的字符串

    实际的业务场景中需要生成4位不重复的字符串 这个场景比较特殊 不具有普遍性 正常场景的唯一单号都不会只有4位 最先想到的是随机生成4位字符串 字符包括数字 大小写字母一共62位 基本可以满足使用要求 但是越到后面 重复的概率就会越大 想要保
  • latex 矩阵分块(block matrix)

    Lesson 12 Making Block Matrices in LATEX 按列分块 AAb An 1b left begin array c c c c A Ab cdots A n 1 b end array right left
  • 【排序算法】归并排序(C语言)

    排序算法 归并排序 C语言 目录 一 归并排序的原理 二 两个有序数组排序和合并 1 原地排序 2 创建临时空间 二 递归实现 三 非递归实现 1 实现思路 2 数组边界问题 3 代码实现 一 归并排序的原理 归并排序 MergeSort
  • import numpy as np_最新Python学习教程(Python学习路线):Numpy常用用法总结

    最新Python学习教程 Python学习路线 Numpy常用用法总结 一 前言 玩数据分析 数据挖掘 AI的都知道这个python库用的是很多的 里面包含各种操作 在实际的dataset的处理当中是非常常用的 这里我做一个总结 方便自己看
  • stm32项目平衡车详解(stm32F407)上

    stm32项目 stm32项目介绍值平衡车 本文章学习借鉴于创客学院团队 以表感谢 教学视频 文章目录 stm32项目 前言 一 平衡小车 平衡小车的功能介绍 平衡小车功能开发需求 平衡小车整体框架 小车环境数据采集进程 1 平衡小车姿态信