基于STM32平衡车软硬件方案(全套资料分享)
平衡车部分1、STM32F103C8T6核心板:用于PWM控制平衡车电机、显示屏幕、蓝牙及无线接收、LED灯、计算轮数灯2、底板:用于引出核心板信号,链接蓝牙模块、无线模块、按键模块、提供各模块供电灯3、编码电机两个、电池、车轮遥控器部分1、基于STM32F103C8T6处理器的遥控器、显示屏幕、无线接收、摇杆等,具体见原理图。
·
一、 硬件组成
平衡车部分
1、STM32F103C8T6核心板:用于PWM控制平衡车电机、显示屏幕、蓝牙及无线接收、LED灯、计算轮数灯
2、底板:用于引出核心板信号,链接蓝牙模块、无线模块、按键模块、提供各模块供电灯
3、编码电机两个、电池、车轮
遥控器部分
1、基于STM32F103C8T6处理器的遥控器、显示屏幕、无线接收、摇杆等,具体见原理图。


二、原理图资料
底板原理图:

遥控器原理图

三、软件应用代码
(1)机载部分主函数部分
int main(void)
{
/*初始化*/
OLED_Init();//屏幕初始化并清零
LED_Init();//LED灯初始化
Key_Init();//按键初始化
Motor_Init();//电机PWM初始化
Encoder_Init();//编码器计数中断初始化
MPU6050_Init();//MPU6050初始化
BlueSerial_Init();//蓝牙初始化
Serial_Init();//串口初始化
NRF24L01_Init();//无线模块初始化
Timer_Init();//定时器初始化
/*显示初始界面*/
OLED_Clear();//屏幕清零
OLED_ShowString(4, 16, " 平衡车测试程序 ", OLED_8X16);//显示内容、坐标、文字像素宽度
OLED_ShowString(0, 32, " V1.0 ", OLED_8X16);//显示内容、坐标、文字像素宽度
OLED_ShowString(0, 48, " K4>", OLED_8X16);//显示内容、坐标、文字像素宽度
OLED_Update();
/*按K4键继续*/
while (Key_Check(KEY_4, KEY_SINGLE) == 0);//KEY_SINGLE 0x08
Key_Clear();
/*初始化FLASH并检查FLASH中有没有存储参数*/
if (Store_Init())
{
/*未存储参数,将程序中的默认参数保存到FLASH中*/
SaveParam();
/*提示已重置参数*/
OLED_Clear();
OLED_ShowString(0, 0, " [提示] ", OLED_8X16);
OLED_ShowString(0, 16, " 已重置参数 ", OLED_8X16);
OLED_ShowString(0, 32, " 请注意执行校准 ", OLED_8X16);
OLED_ShowString(0, 48, " K4>", OLED_8X16);
OLED_Update();
/*按K4键继续*/
while (Key_Check(KEY_4, KEY_SINGLE) == 0);
Key_Clear();
}
/*上电后加载FLASH中保存的参数*/
LoadParam();
OLED_Clear();
BlueSerial_ClearBuffer();//蓝牙清理缓存
while (1)
{
/*指示灯*/
if (RunFlag) {LED_ON();} else {LED_OFF();}
/*按键*/
if (Key_Check(KEY_1, KEY_SINGLE)) //K1按下,启动/停止
{
if (RunFlag == 0)
{
PID_Init(&AnglePID);
PID_Init(&SpeedPID);
PID_Init(&TurnPID);
Angle = AngleAcc_Filter;
RunFlag = 1;
}
else
{
RunFlag = 0;
}
BlueSerial_Printf("RunFlag=%d\r\n", RunFlag);
}
if (RunFlagUpdate)
{
RunFlagUpdate = 0;
BlueSerial_Printf("RunFlag=%d\r\n", RunFlag);
}
if (Key_Check(KEY_2, KEY_SINGLE)) //K2按下,速度等级减1
{
if (SpeedLevel > 1)
{
SpeedLevel --;
SaveParam();
}
BlueSerial_Printf("SpeedLevel=%d\r\n", SpeedLevel);
}
if (Key_Check(KEY_3, KEY_SINGLE)) //K3按下,速度等级加1
{
if (SpeedLevel < 6)
{
SpeedLevel ++;
SaveParam();
}
BlueSerial_Printf("SpeedLevel=%d\r\n", SpeedLevel);
}
if (Key_Check(KEY_4, KEY_SINGLE)) //K4按下,进入调试模式
{
DebugFlag = 1;
RunFlag = 0;
Motor_SetPWM(1, 0);
Motor_SetPWM(2, 0);
LED_OFF();
Key_Clear();
DebugMode(); //执行调试模式函数
}
/*蓝牙串口*/
if (BlueSerial_ReceiveFlag()) //判断是否收到蓝牙数据包
{
BlueSerial_Receive(); //收到数据包,接收并解析数据包
/*摇杆数据包,格式为:[joystick,LH,LV,RH,RV]*/
if (strcmp(BlueSerial_StringArray[0], "joystick") == 0)
{
/*得到摇杆数据,转换为数值*/
int8_t LH = atoi(BlueSerial_StringArray[1]);
int8_t LV = atoi(BlueSerial_StringArray[2]);
int8_t RH = atoi(BlueSerial_StringArray[3]);
int8_t RV = atoi(BlueSerial_StringArray[4]);
/*设定目标速度和目标转向幅度*/
SpeedPID.Target = LV / 100.0 * SpeedLevel;
TurnPID.Target = RH / 100.0 * SpeedLevel;
}
/*按键数据包,格式为:[key,按键名称,down/up]*/
else if (strcmp(BlueSerial_StringArray[0], "key") == 0)
{
if (strcmp(BlueSerial_StringArray[1], "1") == 0
&& strcmp(BlueSerial_StringArray[2], "up") == 0) //收到数据包[key,1,up]
{
Key_Flag[KEY_1] |= KEY_SINGLE; //触发平衡车K1键按下
}
else if (strcmp(BlueSerial_StringArray[1], "2") == 0
&& strcmp(BlueSerial_StringArray[2], "up") == 0) //收到数据包[key,2,up]
{
Key_Flag[KEY_2] |= KEY_SINGLE; //触发平衡车K2键按下
}
else if (strcmp(BlueSerial_StringArray[1], "3") == 0
&& strcmp(BlueSerial_StringArray[2], "up") == 0) //收到数据包[key,3,up]
{
Key_Flag[KEY_3] |= KEY_SINGLE; //触发平衡车K3键按下
}
}
}
/*无线模块*/
if (NRF24L01_Receive() == 1) //判断是否收到无线模块数据包
{
if (NRF24L01_RxPacket[0] == 1) //字节0为1,表示需要返回状态信息
{
/*写入相关的状态信息*/
NRF24L01_TxPacket[0] = 0x00; //0
NRF24L01_TxPacket[1] = SpeedLevel; //1
NRF24L01_TxPacket[2] = PWML; //2
NRF24L01_TxPacket[3] = PWMR; //3
*(float *)&NRF24L01_TxPacket[4] = Angle; //4 5 6 7
*(float *)&NRF24L01_TxPacket[8] = SpeedLeft; //8 9 10 11
*(float *)&NRF24L01_TxPacket[12] = SpeedRight; //12 13 14 15
/*发送状态信息给遥控器,以便在遥控器上观察平衡车状态*/
NRF24L01_Send();
}
/*得到遥控器数据*/
int8_t LV = NRF24L01_RxPacket[2];
int8_t RH = NRF24L01_RxPacket[3];
uint8_t KEY0 = NRF24L01_RxPacket[5];
/*设定目标速度和目标转向幅度*/
SpeedPID.Target = LV / 100.0 * SpeedLevel;
TurnPID.Target = RH / 100.0 * SpeedLevel;
/*处理遥控器按键事件*/
if (KEY0 & 0x01) //遥控器K1键按下
{
Key_Flag[KEY_3] |= KEY_SINGLE; //触发平衡车K3键按下
}
if (KEY0 & 0x02) //遥控器K2键按下
{
Key_Flag[KEY_2] |= KEY_SINGLE; //触发平衡车K2键按下
}
if (KEY0 & 0x04) //遥控器K3键按下
{
Key_Flag[KEY_1] |= KEY_SINGLE; //触发平衡车K1键按下
}
}
/*OLED显示*/
OLED_Printf(0, 0, OLED_6X8, "Angle Speed Turn");
OLED_Printf(0, 10, OLED_6X8, "%+06.1f %+06.1f %+06.1f", AnglePID.Target, SpeedPID.Target, TurnPID.Target);
OLED_Printf(0, 18, OLED_6X8, "%+06.1f %+06.1f %+06.1f", Angle, AveSpeed, DifSpeed);
OLED_Printf(0, 26, OLED_6X8, "%+06.1f %+06.1f %+06.1f", AnglePID.Out, SpeedPID.Out, TurnPID.Out);
OLED_DrawLine(0, 36, 127, 36);
OLED_Printf(0, 40, OLED_6X8, "PWML:%+05d PWMR:%+05d", PWML, PWMR);
OLED_Printf(0, 48, OLED_6X8, "SpdL:%+05.1f SpdR:%+05.1f", SpeedLeft, SpeedRight);
OLED_Printf(0, 56, OLED_6X8, "SpdLevel:%1d", SpeedLevel);
OLED_Update();
}
}
(2)遥控器部分主函数代码
int main(void)
{
/*初始化*/
OLED_Init();
Key_Init();
AD_Init();
NRF24L01_Init();
Timer_Init();
/*显示初始界面*/
OLED_Clear();
OLED_ShowString(0, 0, " [江协科技] ", OLED_8X16);
OLED_ShowString(4, 16, " 遥控器测试程序 ", OLED_8X16);
OLED_ShowString(0, 32, " V1.0 ", OLED_8X16);
OLED_ShowString(0, 48, " K10>", OLED_8X16);
OLED_Update();
/*按K10键继续*/
while (Key_Check(KEY_10, KEY_SINGLE) == 0);
Key_Clear();
OLED_Clear();
while (1)
{
KeyNum = 0;
/*读取左右摇杆的AD值*/
AD_LH = AD_GetValue(ADC_Channel_0);
AD_LV = AD_GetValue(ADC_Channel_1);
AD_RH = AD_GetValue(ADC_Channel_2);
AD_RV = AD_GetValue(ADC_Channel_3);
/*对AD值进行数据处理,得到摇杆数据*/
LH = DataProcess(AD_LH);
LV = DataProcess(AD_LV);
RH = DataProcess(AD_RH);
RV = DataProcess(AD_RV);
/*读取按键*/
if (Key_Check(KEY_1, KEY_SINGLE))
{
KEY0 |= 0x01;
KeyNum = 1;
}
if (Key_Check(KEY_2, KEY_SINGLE))
{
KEY0 |= 0x02;
KeyNum = 2;
}
if (Key_Check(KEY_3, KEY_SINGLE))
{
KEY0 |= 0x04;
KeyNum = 3;
}
if (Key_Check(KEY_4, KEY_SINGLE))
{
KEY0 |= 0x08;
KeyNum = 4;
}
if (Key_Check(KEY_5, KEY_SINGLE))
{
KEY0 |= 0x10;
KeyNum = 5;
}
if (Key_Check(KEY_6, KEY_SINGLE))
{
KEY0 |= 0x20;
KeyNum = 6;
}
if (Key_Check(KEY_7, KEY_SINGLE))
{
KEY0 |= 0x40;
KeyNum = 7;
}
if (Key_Check(KEY_8, KEY_SINGLE))
{
KEY0 |= 0x80;
KeyNum = 8;
}
if (Key_Check(KEY_9, KEY_SINGLE))
{
KEY1 |= 0x01;
KeyNum = 9;
}
if (Key_Check(KEY_10, KEY_SINGLE))
{
KEY1 |= 0x02;
KeyNum = 10;
}
if (Key_Check(KEY_11, KEY_SINGLE))
{
KEY1 |= 0x04;
KeyNum = 11;
}
if (Key_Check(KEY_12, KEY_SINGLE))
{
KEY1 |= 0x08;
KeyNum = 12;
}
/*定时器中,每隔100ms,置一次Flag*/
/*即每隔100ms,发送一次数据*/
if (Flag)
{
Flag = 0;
/*填充遥控器数据*/
NRF24L01_TxPacket[0] = Mode;
NRF24L01_TxPacket[1] = LH;
NRF24L01_TxPacket[2] = LV;
NRF24L01_TxPacket[3] = RH;
NRF24L01_TxPacket[4] = RV;
NRF24L01_TxPacket[5] = KEY0;
NRF24L01_TxPacket[6] = KEY1;
/*发送遥控器数据*/
SendFlag = NRF24L01_Send();
/*发送成功后,清除按键,确保按键被成功发出*/
if (SendFlag == 1)
{
KEY0 = 0;
KEY1 = 0;
}
/*计算发送成功率,用于显示信号值*/
SuccessRatio = CalculateSuccessRatio(SendFlag);
}
/*按键按下,在屏幕右上角闪烁此按键值*/
if (KeyNum)
{
OLED_Printf(104, 0, OLED_8X16, "K%d", KeyNum);
OLED_UpdateArea(104, 0, 24, 16);
Delay_ms(100);
OLED_ClearArea(104, 0, 24, 16);
OLED_UpdateArea(104, 0, 24, 16);
}
/*K9键按下,切换显示模式*/
if (KeyNum == 9)
{
Mode = !Mode;
OLED_Clear();
}
/*模式0时,屏幕显示摇杆数据*/
if (Mode == 0)
{
OLED_Printf(0, 16, OLED_8X16, "LH:%+04d", LH);
OLED_Printf(0, 32, OLED_8X16, "LV:%+04d", LV);
OLED_Printf(72, 16, OLED_8X16, "RH:%+04d", RH);
OLED_Printf(72, 32, OLED_8X16, "RV:%+04d", RV);
}
/*模式1时,屏幕显示平衡车返回的状态信息*/
else if (Mode == 1)
{
if (NRF24L01_Receive() == 1)
{
uint8_t SpeedLevel = NRF24L01_RxPacket[1];
int8_t PWML = NRF24L01_RxPacket[2];
int8_t PWMR = NRF24L01_RxPacket[3];
float Angle = *(float *)&NRF24L01_RxPacket[4];
float SpeedLeft = *(float *)&NRF24L01_RxPacket[8];
float SpeedRight = *(float *)&NRF24L01_RxPacket[12];
OLED_Clear();
OLED_Printf(0, 21, OLED_6X8, "Angle:%+06.1f", Angle);
OLED_DrawLine(0, 36, 127, 36);
OLED_Printf(0, 40, OLED_6X8, "PWML:%+05d PWMR:%+05d", PWML, PWMR);
OLED_Printf(0, 48, OLED_6X8, "SpdL:%+05.1f SpdR:%+05.1f", SpeedLeft, SpeedRight);
OLED_Printf(0, 56, OLED_6X8, "SpdLevel:%1d", SpeedLevel);
}
}
/*根据发送成功率,在屏幕左上角显示对应的信号值*/
if (SuccessRatio >= 9)
{
OLED_ShowImage(0, 0, 16, 16, Signal_3);
}
else if (SuccessRatio >= 5)
{
OLED_ShowImage(0, 0, 16, 16, Signal_2);
}
else if (SuccessRatio >= 1)
{
OLED_ShowImage(0, 0, 16, 16, Signal_1);
}
else
{
OLED_ShowImage(0, 0, 16, 16, Signal_0);
}
OLED_Update();
}
}
四、MATLAB建模
平衡车MATLAB建模是利用MATLAB强大的数学计算与仿真能力,对两轮自平衡电动车进行动态系统建模、控制器设计与仿真实现的完整过程。本文通过建立状态空间模型描述车体倾斜角、车轮转速等动态行为,结合牛顿-欧拉方程和实际物理参数,构建精确的系统模型。利用Simulink进行可视化建模与仿真,采用PID等控制策略实现稳定控制,并通过PID Tuner优化参数。最终可将模型转化为C/C++代码部署至嵌入式系统,实现从理论到实践的闭环开发。

五、理论研究

六、演示视频
七、资料下载
通过网盘分享的文件:平衡车入门教程资料
链接: https://pan.baidu.com/s/1NbZryQY33YjNtP9MZxyCiA 提取码: 7nmc
--来自百度网盘超级会员v2的分享
更多推荐



所有评论(0)