Qt系列——炫酷的Qt APP+STM32平衡小车

一、 Qt设计

采用了C++——Qt设计了炫酷的主页面,里面包含了各类仪表widget,其中有个类似钢铁侠的按钮可以触发和控制平衡小车的蜂鸣器。设计的摇杆按钮可以控制前进、后退、左转、右转、蓝牙连接等功能。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
Qt_C++工程:
在这里插入图片描述
部分代码:

#ifndef BLE_H
#define BLE_H

#include "Frm_ControlCar.h"
#include <QWidget>
#include <QListWidgetItem>
#include <QtBluetooth/qbluetoothglobal.h>
#include <QtBluetooth/qbluetoothlocaldevice.h>
#include <qbluetoothaddress.h>
#include <qbluetoothdevicediscoveryagent.h>
#include <qbluetoothlocaldevice.h>
#include <qbluetoothsocket.h>

#include "Public/typedefine.h"

namespace Ui {
class BLE;
}

class Frm_BLE : public QWidget
{
    Q_OBJECT

public:
    explicit Frm_BLE(QWidget *parent = 0);
    ~Frm_BLE();
    bool GetBlueState();
    void BLE_Write(char data);
    void BLE_Read();


private slots:
    void on_btn_openBLE_clicked();
    void on_btn_upDateBLE_clicked();
    void on_btn_return_clicked();
    void slotAddBLDeviToList(const QBluetoothDeviceInfo &info);
    void slotFindFinish();
    void slotClick(QListWidgetItem* item);
    void slotConnectBLE();
    void slotDisConnectBLE();
    void slotConnectOK();
    void slotConnectFailed();
private:
    Ui::BLE *ui;
    QBluetoothSocket *m_BlueSocket;
    QBluetoothDeviceDiscoveryAgent *m_discoveryAgent;
    QBluetoothLocalDevice *m_localDevice;
    QString m_BLName = "";
    bool m_bIsConnected = false;

};

//extern BOOLMY BluetoothPcRxPushFifo(INT8U uch_Data);
//extern BOOLMY BluetoothPcRxPopChar(INT8U *puch_Char);
//extern BOOLMY BluetoothPcTxPushFifo(INT8U uch_Char);
//extern BOOLMY BluetoothPcTxPopChar(INT8U *puch_Char);
//extern void   BluetoothSendEnable(void);

#endif // BLE_H

#ifndef FRM_CONTROLCAR_H
#define FRM_CONTROLCAR_H

#include <QWidget>

namespace Ui {
class Frm_ControlCar;
}

class Frm_ControlCar : public QWidget
{
    Q_OBJECT

public:
    explicit Frm_ControlCar(QWidget *parent = 0);
    ~Frm_ControlCar();

protected:
    void slotDoRockerDirect(int num);
    void slotDoScanTan(int num);

private slots:
    void on_btnToggleBlueTooth_clicked();
    void on_btnInfoFrm_clicked();

private:
    Ui::Frm_ControlCar *ui;

};

#endif // FRM_CONTROLCAR_H

Qt安卓界面

二、Stm32平衡小车部分

采用MPU6050六轴传感器,支持蓝牙模块、OLED、减速电机、RBG三色灯等控制,炫酷的钢铁侠开机动画。

Stm32平衡小车+Qt安卓


更多细节闲鱼搜索平衡小车。代码部分:

#include "control.h"	
#include "filter.h"	
#include "Stm32_Beep.h"

  /**************************************************************************
作者:平衡小车之家
我的淘宝小店:http://shop114407458.taobao.com/
**************************************************************************/
int Balance_Pwm=0,Velocity_Pwm=0,Turn_Pwm=0;
u8 Flag_Target;
/**************************************************************************
函数功能:所有的控制代码都在这里面
         5ms定时中断由MPU6050的INT引脚触发
         严格保证采样和数据处理的时间同步				 
**************************************************************************/
int EXTI4_IRQHandler(void) 
{    
	if(PAin(4)==0)		
	{   
		EXTI->PR=1<<4;                      //清除LINE4上的中断标志位  
		gul_softTmr++;
		
		Flag_Target=!Flag_Target;
		if(delay_flag==1)
		{
			if(++delay_50==10)	
			{
				delay_50=0,delay_flag=0;    //给主函数提供50ms的精准延时
			}
		}
		if(Flag_Target==1)        			//5ms读取一次陀螺仪和加速度计的值,更高的采样频率可以改善卡尔曼滤波和互补滤波的效果
		{
			Get_Angle(Way_Angle);           //===更新姿态	
			return 0;	                                               
		}                                   //10ms控制一次,为了保证M法测速的时间基准,首先读取编码器数据
		Encoder_Left=-Read_Encoder(2);      //===读取编码器的值,因为两个电机的旋转了180度的,所以对其中一个取反,保证输出极性一致
		Encoder_Right=Read_Encoder(4);      //===读取编码器的值
	  	Get_Angle(Way_Angle);               //===更新姿态	
		//Read_Distane();                     //===获取超声波测量距离值
  		if(Bi_zhang==0)
		{
			//Led_Flash(0);                 //===LED闪烁;常规模式 1s改变一次指示灯的状态	
		}                                   
		if(Bi_zhang==1)                     
		{                                   
			Led_Flash(1);                   //===LED闪烁;避障模式 指示灯常亮	
		}
		
  		//Voltage=Get_battery_volt();                                     //===获取电池电压	          
		Key();                                                          //===扫描按键状态 单击双击可以改变小车运行状态
		
		Balance_Pwm = balance(Angle_Balance,Gyro_Balance);              //===平衡PID控制	
		Velocity_Pwm = velocity(Encoder_Left,Encoder_Right);            //===速度环PID控制	 记住,速度反馈是正反馈,就是小车快的时候要慢下来就需要再跑快一点
 	    Turn_Pwm = turn(Encoder_Left,Encoder_Right,Gyro_Turn);          //===转向环PID控制  
		
 		Moto1=Balance_Pwm-Velocity_Pwm+Turn_Pwm;                            //===计算左轮电机最终PWM
 	  	Moto2=Balance_Pwm-Velocity_Pwm-Turn_Pwm;                            //===计算右轮电机最终PWM
   		Xianfu_Pwm();                                                       //===PWM限幅
		
		if(Pick_Up(Acceleration_Z,Angle_Balance,Encoder_Left,Encoder_Right))//===检查是否小车被那起
		{
			Flag_Stop=1;	                                                //===如果被拿起就关闭电机
		}
		if(Put_Down(Angle_Balance,Encoder_Left,Encoder_Right))              //===检查是否小车被放下
		{
			Flag_Stop=0;	                                                //===如果被放下就启动电机
		}
		if(Turn_Off(Angle_Balance,Voltage)==0)                              //===如果不存在异常
		{
 			Set_Pwm(Moto1,Moto2);                                           //===赋值给PWM寄存器  
		}
	}       	
	 return 0;	  
} 

/**************************************************************************
函数功能:直立PD控制
入口参数:角度、角速度
返回  值:直立控制PWM
作    者:平衡小车之家
**************************************************************************/
int balance(float Angle,float Gyro)
{  
	float Bias,kp=480,kd=0.73;
	int balance;
	Bias=Angle-ZHONGZHI;       //===求出平衡的角度中值 和机械相关
	balance=kp*Bias+Gyro*kd;   //===计算平衡控制的电机PWM  PD控制   kp是P系数 kd是D系数 
	return balance;
}

/**************************************************************************
函数功能:速度PI控制 修改前进后退速度,请修Target_Velocity,比如,改成60就比较慢了
入口参数:左轮编码器、右轮编码器
返回  值:速度控制PWM
作    者:平衡小车之家
**************************************************************************/
int velocity(int encoder_left,int encoder_right)
{  
	static float Velocity,Encoder_Least,Encoder,Movement;
	static float Encoder_Integral,Target_Velocity;
	///float kp=-120,ki=-0.6;
	float kp=-200,ki=-0.9;
	
	//=============遥控前进后退部分=======================// 
	if(Bi_zhang==1&&Flag_sudu==1)  
	{
		Target_Velocity=45;                 //如果进入避障模式,自动进入低速模式
	}
	else 	                         
	{
		Target_Velocity=30;//90;   
	}	
	
	if(1==Flag_Qian)    	
	{
		Movement=-Target_Velocity/Flag_sudu;	      //===前进标志位置1 
	}
	else if(1==Flag_Hou)	
	{
		Movement=Target_Velocity/Flag_sudu;      //===后退标志位置1
	}
	else  
	{
		Movement=0;	
	}
	
	if(Bi_zhang==1 && Distance<500 && Flag_Left!=1 &&Flag_Right!=1)        //避障标志位置1且非遥控转弯的时候,进入避障模式
	{
		Movement=-Target_Velocity/Flag_sudu;
	}
	
   //=============速度PI控制器=======================//	
	Encoder_Least =(Encoder_Left+Encoder_Right)-0;                    //===获取最新速度偏差==测量速度(左右编码器之和)-目标速度(此处为零) 
	Encoder *= 0.8;		                                              //===一阶低通滤波器       
	Encoder += Encoder_Least*0.2;	                                  //===一阶低通滤波器    
	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*kp+Encoder_Integral*ki;                          //===速度控制	
	if(Turn_Off(Angle_Balance,Voltage)==1||Flag_Stop==1)   
	{
		Encoder_Integral=0;      //===电机关闭后清除积分
	}
	return Velocity;
}

/**************************************************************************
函数功能:转向控制  修改转向速度,请修改Turn_Amplitude即可
入口参数:左轮编码器、右轮编码器、Z轴陀螺仪
返回  值:转向控制PWM
作    者:平衡小车之家
**************************************************************************/
int turn(int encoder_left,int encoder_right,float gyro)//转向控制
{
	static float Turn_Target,Turn,Encoder_temp,Turn_Convert=0.9,Turn_Count;
	float Turn_Amplitude=88/Flag_sudu,Kp=42,Kd=0; 
    
	//=============遥控左右旋转部分=======================//
	if(1==Flag_Left||1==Flag_Right)                      //这一部分主要是根据旋转前的速度调整速度的起始速度,增加小车的适应性
	{
		if(++Turn_Count==1)
		Encoder_temp=myabs(encoder_left+encoder_right);
		Turn_Convert=50/Encoder_temp;
		if(Turn_Convert<0.6)Turn_Convert=0.6;
		if(Turn_Convert>3)Turn_Convert=3;
	}	
	else
	{
		Turn_Convert=0.9;
		Turn_Count=0;
		Encoder_temp=0;
	}			
	if(1==Flag_Left)	           Turn_Target-=Turn_Convert;
	else if(1==Flag_Right)	     Turn_Target+=Turn_Convert; 
	else Turn_Target=0;
	
    if(Turn_Target>Turn_Amplitude)  Turn_Target=Turn_Amplitude;    //===转向速度限幅
	if(Turn_Target<-Turn_Amplitude) Turn_Target=-Turn_Amplitude;
	if(Flag_Qian==1||Flag_Hou==1)  Kd=0.5;        
	else Kd=0;   //转向的时候取消陀螺仪的纠正 有点模糊PID的思想
  	//=============转向PD控制器=======================//
	Turn=-Turn_Target*Kp -gyro*Kd;                 //===结合Z轴陀螺仪进行PD控制
	return Turn;
}

/**************************************************************************
函数功能:赋值给PWM寄存器
入口参数:左轮PWM、右轮PWM
返回  值:无
**************************************************************************/
void Set_Pwm(int moto1,int moto2)
{
	if(moto1<0)			AIN2=1,			AIN1=0;
	else 	          AIN2=0,			AIN1=1;
	PWMA=myabs(moto1);
	if(moto2<0)	BIN1=0,			BIN2=1;
	else        BIN1=1,			BIN2=0;
	PWMB=myabs(moto2);	
}

/**************************************************************************
函数功能:限制PWM赋值 
入口参数:无
返回  值:无
**************************************************************************/
void Xianfu_Pwm(void)
{	
	int Amplitude=6900;    //===PWM满幅是7200 限制在6900
	if(Flag_Qian==1)  Moto1-=DIFFERENCE;  //DIFFERENCE是一个衡量平衡小车电机和机械安装差异的一个变量。直接作用于输出,让小车具有更好的一致性。
	if(Flag_Hou==1)   Moto2+=DIFFERENCE;
	if(Moto1<-Amplitude) Moto1=-Amplitude;	
	if(Moto1>Amplitude)  Moto1=Amplitude;	
	if(Moto2<-Amplitude) Moto2=-Amplitude;	
	if(Moto2>Amplitude)  Moto2=Amplitude;		
	
}
/**************************************************************************
函数功能:按键修改小车运行状态 
入口参数:无
返回  值:无
**************************************************************************/
void Key(void)
{	
	u8 tmp,tmp2;
	tmp=click_N_Double(50); 
	
	if(tmp==1)
		Flag_Stop=!Flag_Stop;//单击控制小车的启停
	
	if(tmp==2)
		Flag_Show=!Flag_Show;//双击控制小车的显示状态
	
	tmp2=Long_Press();                   
	if(tmp2==1) 
		Bi_zhang=!Bi_zhang;		//长按控制小车是否进入超声波避障模式 
}

/**************************************************************************
函数功能:异常关闭电机
入口参数:倾角和电压
返回  值:1:异常  0:正常
**************************************************************************/
u8 Turn_Off(float angle, int voltage)
{
	u8 temp;
	//if(angle<-40||angle>40||1==Flag_Stop||voltage<1110)		//电池电压低于11.1V关闭电机
	if(angle<-40||angle>40||1==Flag_Stop)		//去掉电池电压低于11.1V关闭电机
	{	                                                 	//===倾角大于40度关闭电机
		temp=1;                                            	//===Flag_Stop置1关闭电机
		AIN1=0;                                            
		AIN2=0;
		BIN1=0;
		BIN2=0;
	}
	else
	{
		temp=0;
	}
	return temp;			
}
	
/**************************************************************************
函数功能:获取角度 三种算法经过我们的调校,都非常理想 
入口参数:获取角度的算法 1:DMP  2:卡尔曼 3:互补滤波
返回  值:无
**************************************************************************/
void Get_Angle(u8 way)
{ 
	float Accel_Y,Accel_X,Accel_Z,Gyro_Y,Gyro_Z;
	Temperature=Read_Temperature();      //===读取MPU6050内置温度传感器数据,近似表示主板温度。
	if(way==1)                           //===DMP的读取在数据采集中断提醒的时候,严格遵循时序要求
	{	
		Read_DMP();                      //===读取加速度、角速度、倾角
		
		//Angle_Balance=Pitch;             //===更新平衡倾角--
		Angle_Balance=Roll;             //===更新平衡倾角---
		
		//Gyro_Balance=gyro[1];            //===更新平衡角速度
		Gyro_Balance=gyro[0];            //===更新平衡角速度
		
		Gyro_Turn=gyro[2];               //===更新转向角速度
		Acceleration_Z=accel[2];         //===更新Z轴加速度计
	}			
	else
	{
		Gyro_Y=(I2C_ReadOneByte(devAddr,MPU6050_RA_GYRO_YOUT_H)<<8)+I2C_ReadOneByte(devAddr,MPU6050_RA_GYRO_YOUT_L);    //读取Y轴陀螺仪
		Gyro_Z=(I2C_ReadOneByte(devAddr,MPU6050_RA_GYRO_ZOUT_H)<<8)+I2C_ReadOneByte(devAddr,MPU6050_RA_GYRO_ZOUT_L);    //读取Z轴陀螺仪
		Accel_X=(I2C_ReadOneByte(devAddr,MPU6050_RA_ACCEL_XOUT_H)<<8)+I2C_ReadOneByte(devAddr,MPU6050_RA_ACCEL_XOUT_L); //读取X轴加速度计
		Accel_Z=(I2C_ReadOneByte(devAddr,MPU6050_RA_ACCEL_ZOUT_H)<<8)+I2C_ReadOneByte(devAddr,MPU6050_RA_ACCEL_ZOUT_L); //读取Z轴加速度计
		if(Gyro_Y>32768)  Gyro_Y-=65536;                       //数据类型转换  也可通过short强制类型转换
		if(Gyro_Z>32768)  Gyro_Z-=65536;                       //数据类型转换
		if(Accel_X>32768) Accel_X-=65536;                      //数据类型转换
		if(Accel_Z>32768) Accel_Z-=65536;                      //数据类型转换
		Gyro_Balance=-Gyro_Y;                                  //更新平衡角速度
		Accel_Y=atan2(Accel_X,Accel_Z)*180/PI;                 //计算倾角	
		Gyro_Y=Gyro_Y/16.4;                                    //陀螺仪量程转换	
		if(Way_Angle==2)		  	Kalman_Filter(Accel_Y,-Gyro_Y);//卡尔曼滤波	
		else if(Way_Angle==3)   Yijielvbo(Accel_Y,-Gyro_Y);    //互补滤波
		Angle_Balance=angle;                                   //更新平衡倾角
		Gyro_Turn=Gyro_Z;                                      //更新转向角速度
		Acceleration_Z=Accel_Z;                                //===更新Z轴加速度计	
	}
}
/**************************************************************************
函数功能:绝对值函数
入口参数:int
返回  值:unsigned int
**************************************************************************/
int myabs(int a)
{ 		   
	int temp;
	if(a<0)  temp=-a;  
	else temp=a;
	return temp;
}
/**************************************************************************
函数功能:检测小车是否被拿起
入口参数:int
返回  值:unsigned int
**************************************************************************/
int Pick_Up(float Acceleration,float Angle,int encoder_left,int encoder_right)
{ 		   
	static u16 flag,count0,count1,count2;
	if(flag==0)                                                                 //第一步
	{
		if(myabs(encoder_left)+myabs(encoder_right)<30)                         //条件1,小车接近静止
		{
			count0++;
		}
		else 
		{
			count0=0;
		}
		
		if(count0>10)	
		{			
			flag=1,count0=0; 
		}
	} 
	
	if(flag==1)                                                                 //进入第二步
	{
		if(++count1>200)       
		{
			count1=0,flag=0;                                 					//超时不再等待2000ms
		}
		if(Acceleration>26000&&(Angle>(-20+ZHONGZHI))&&(Angle<(20+ZHONGZHI)))   //条件2,小车是在0度附近被拿起
		{
			flag=2; 
		}
	} 
	
	if(flag==2)                                                                  //第三步
	{
		if(++count2>100)       count2=0,flag=0;                                   //超时不再等待1000ms
		if(myabs(encoder_left+encoder_right)>135)                                   //条件3,小车的轮胎因为正反馈达到最大的转速   
		{
			flag=0;                                                                                     
			return 1;                                                           //检测到小车被拿起
		}
	}
	return 0;
}
/**************************************************************************
函数功能:检测小车是否被放下
入口参数:int
返回  值:unsigned int
**************************************************************************/
int Put_Down(float Angle,int encoder_left,int encoder_right)
{ 		   
	static u16 flag,count;	 
	if(Flag_Stop==0)                           //防止误检      
		return 0;	                 
	if(flag==0)                                               
	{
		if(Angle>(-10+ZHONGZHI)&&Angle<(10+ZHONGZHI)&&encoder_left==0&&encoder_right==0)         //条件1,小车是在0度附近的
		flag=1; 
	} 
	if(flag==1)                                               
	{
		if(++count>50)                                          //超时不再等待 500ms
		{
			count=0;flag=0;
		}
		//if(encoder_left>3&&encoder_right>3&&encoder_left<60&&encoder_right<60)                //条件2,小车的轮胎在未上电的时候被人为转动  
		if(encoder_left>=0&&encoder_right>=0&&encoder_left<60&&encoder_right<60)                //条件2,小车的轮胎在未上电的时候被人为转动  
		{
			flag=0;
			flag=0;
			return 1;                                             //检测到小车被放下
		}
	}
	return 0;
}



  • 0
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

何以问天涯

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值