两轮平衡小车制作保姆式教程(2-1)——软件模块:JY62

2024-01-27 10:36

本文主要是介绍两轮平衡小车制作保姆式教程(2-1)——软件模块:JY62,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

✅作者简介:大家好我是:麦克斯科技,希望一起努力,一起进步!

📃个人主页:麦克斯科技

🔥系列专栏:两轮平衡小车制作保姆式教程
🏷️非常欢迎大家在评论区留言交流,互相学习!

提前声明:博客中给出的代码经过多个项目测试,实测能用,性能稳定,请大家放心使用!

前言

本系列博客将从硬件到软件详细介绍“如何制作一辆两轮自平衡小车”,笔者毫无保留,以最通俗易懂的语言,以最简单的实现方案,分享自己从0到1制作平衡小车的全过程,相信跟着我的教程,大家也能顺利制作一台属于自己的平衡车。系列专栏:🔥两轮平衡小车制作保姆式教程🔥

首先,给大家提前交个底,其实制作一台平衡小车并不难,用到的主要模块就是陀螺仪,而最主要的控制算法就是PID算法,而且平衡小车对陀螺仪与PID算法的掌握程度要求并不是很高,所以适合初学者来作为项目练手。

该系列教程一共分为4个板块,分为《硬件选型》、《软件模块》、《直立环、速度环、转向环》、《调参保姆级教程》,4个板块条理清晰,层次分明,简明扼要,请大家跟着我开始学习吧!

软件模块

软件主要模块:陀螺仪、编码器、0.96寸OLED显示屏、电机、直立环、速度环、转向环等。

陀螺仪JY62

这里我用钱来解决了麻烦的问题,重金入手了一个性能较好的六轴陀螺仪jy62。只需要使用单片机的一个串口,这个模块对数据的处理已经做的很成功了。

我使用的主控是TM4C123,我也会给出使用STM32的代码,两个芯片是同一个处理思路,只是两个芯片的底层函数不一样。照着我的文件形式,建立imu.c与imu.h或者mt_jy62.c与mt_jy62.c,复制粘贴我的代码,根据实际情况修改,绝对能用,我在电赛和课设都是用的这个代码,实测真好用!

TM4C123的JY62底层程序

imu.c

//
// Created by 麦克斯科技.
//
#include "platform/platform.h"static uint8_t buffer[11];
imu_data_t acceleration, gyroscope, angle;// uart1
void imu_init()
{SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);SysCtlPeripheralEnable(SYSCTL_PERIPH_UART1);GPIOPinConfigure(GPIO_PB0_U1RX);GPIOPinConfigure(GPIO_PB1_U1TX);GPIOPinTypeUART(GPIO_PORTB_BASE, GPIO_PIN_0);GPIOPinTypeUART(GPIO_PORTB_BASE, GPIO_PIN_1);UARTConfigSetExpClk(UART1_BASE, SysCtlClockGet(), 115200, UART_CONFIG_WLEN_8|UART_CONFIG_STOP_ONE|UART_CONFIG_PAR_NONE);UARTFIFOEnable(UART1_BASE);UARTFIFOLevelSet(UART1_BASE, UART_FIFO_TX2_8, UART_FIFO_RX2_8);UARTIntEnable(UART1_BASE, UART_INT_RX|UART_INT_RT);void imu_handler(void);UARTIntRegister(UART1_BASE, imu_handler);IntPrioritySet(INT_UART1, USER_INT2);IntEnable(INT_UART1);IntMasterEnable();UARTEnable(UART1_BASE);
}static uint8_t get_verify_code()
{uint32_t sum = 0;for (uint8_t i = 0; i < 10; ++i) {sum += buffer[i];}return sum&0xff;
}int32_t k = 0;
void imu_analysis()
{static float last_angle_z = 0, angle_z = 0;switch (buffer[1]) {case 0x51:acceleration.x = (float )((int16_t)(buffer[2]|(buffer[3]<<8)))/32768*16;acceleration.y = (float )((int16_t)(buffer[4]|(buffer[5]<<8)))/32768*16;acceleration.z = (float )((int16_t)(buffer[6]|(buffer[7]<<8)))/32768*16;break;case 0x52:gyroscope.x = (float )((int16_t)(buffer[2]|(buffer[3]<<8)))/32768*2000;gyroscope.y = (float )((int16_t)(buffer[4]|(buffer[5]<<8)))/32768*2000;gyroscope.z = (float )((int16_t)(buffer[6]|(buffer[7]<<8)))/32768*2000;break;case 0x53:angle.x = (float )((int16_t)(buffer[2]|(buffer[3]<<8)))/32768*180;angle.y = (float )((int16_t)(buffer[4]|(buffer[5]<<8)))/32768*180;angle_z = (float )((int16_t)(buffer[6]|(buffer[7]<<8)))/32768*180;break;}if (last_angle_z < -90 && angle_z > 90)k --;if (last_angle_z > 90 && angle_z < -90)k ++;last_angle_z = angle_z;
//    printf("k:%ld\n", k);angle.z = angle_z + (float )k*360;
}#define ERROR   0
#define DOING   1
#define SUCCESS 2
static uint8_t n = 0, state = 0;
void imu_handler()
{uint32_t data;uint32_t status=UARTIntStatus(UART1_BASE, true);UARTIntClear(UART1_BASE, status);while(UARTCharsAvail(UART1_BASE)){data = UARTCharGetNonBlocking(UART1_BASE)&0xff;//	data = UARTCharGet(UART1_BASE)&0xff;//	UARTCharPutNonBlocking(UART0_BASE, data);//	printf("data:%d\n", data);if (state == ERROR){n = 0;}buffer[n] = (uint8_t)data;if (n == 0){if (buffer[0] == 0x55){state = DOING;} else{state = ERROR;}} else if (n == 10){if (buffer[10] == get_verify_code()){state = SUCCESS;} else{state = ERROR;}}n += 1;if (state == SUCCESS){
//			printf("OK\n");imu_analysis();state = ERROR;}}
}

imu.h

//
// Created by 麦克斯科技.
//
#ifndef IMU_H
#define IMU_Htypedef struct {float x;float y;float z;
}imu_data_t;void imu_init(void);
extern imu_data_t acceleration, gyroscope, angle;
#endif /* IMU_H */

STM32F103的JY62底层程序

mt_jy62.c

//
// Created by 麦克斯科技.
//
#include "mt_jy62.h"
#include <string.h>
#include <stdio.h>
#include "hal_usart.h"
#include "mt_pid.h"static int32_t n = 0 ;
int32_t state = 0;
static uint8_t buffer[11];
imu_data_t acceleration, gyroscope, angle; 
int32_t k = 0;
char analysis_flag = 0;
void imu_analysis(void)
{static float last_angle_z = 0, angle_z = 0;switch (buffer[1]) {case 0x51:acceleration.x = (float )((int16_t)(buffer[2]|(buffer[3]<<8)))/32768*16;acceleration.y = (float )((int16_t)(buffer[4]|(buffer[5]<<8)))/32768*16;acceleration.z = (float )((int16_t)(buffer[6]|(buffer[7]<<8)))/32768*16;break;case 0x52:gyroscope.x = (float )((int16_t)(buffer[2]|(buffer[3]<<8)))/32768*2000;gyroscope.y = (float )((int16_t)(buffer[4]|(buffer[5]<<8)))/32768*2000;gyroscope.z = (float )((int16_t)(buffer[6]|(buffer[7]<<8)))/32768*2000;break;case 0x53:angle.x = (float )((int16_t)(buffer[2]|(buffer[3]<<8)))/32768*180;angle.y = (float )((int16_t)(buffer[4]|(buffer[5]<<8)))/32768*180;angle_z = (float )((int16_t)(buffer[6]|(buffer[7]<<8)))/32768*180;break;}if (last_angle_z < -90 && angle_z > 90){//	k = -1;k--;}if (last_angle_z > 90 && angle_z < -90){ //	k = 1;k++;}last_angle_z = angle_z;angle.z = angle_z + k*360;}static uint8_t get_verify_code()
{uint32_t sum = 0;for (uint8_t i = 0; i < 10; ++i) {sum += buffer[i];}return sum&0xff;
}
void USART1_IRQHandler(void)                  //串口3中断服务程序
{uint32_t data;if(USART_GetITStatus(USART1,USART_IT_RXNE) != RESET){							data =  USART_ReceiveData(USART1)&0xff;if (state == ERROR){n = 0;}buffer[n] = (uint8_t)data;if (n == 0){if (buffer[0] == 0x55){state = DOING;} else{state = ERROR;}}else if (n == 10){if (buffer[10] == get_verify_code()){state = SUCCESS;} else{state = ERROR;}}
//		else
//		{
//			state = ERROR;
//		}n += 1;if (state == SUCCESS){imu_analysis();//analysis_flag = 1;state = ERROR;}else{analysis_flag = 0;}USART_ClearITPendingBit(USART1,USART_IT_RXNE);}	}

mt_jy62.h

//
// Created by 麦克斯科技.
//
#ifndef _MT_JY62_H
#define _MT_JY62_H#include "stm32f10x.h"
//**************************************************
typedef struct {int x;int y;int z;
}imu_data_t;#define ERROR   0
#define DOING   1
#define SUCCESS 2void imu_init(void);
void imu_analysis(void);
extern imu_data_t acceleration, gyroscope, angle;
//extern int32_t state;
extern char analysis_flag;#endif

代码分析

按函数分析

void imu_init(); //imu初始化

陀螺仪的初始化,初始化引脚,注册串口中断函数,打开中断。

void imu_init()
{SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);SysCtlPeripheralEnable(SYSCTL_PERIPH_UART1);GPIOPinConfigure(GPIO_PB0_U1RX);GPIOPinConfigure(GPIO_PB1_U1TX);GPIOPinTypeUART(GPIO_PORTB_BASE, GPIO_PIN_0);GPIOPinTypeUART(GPIO_PORTB_BASE, GPIO_PIN_1);UARTConfigSetExpClk(UART1_BASE, SysCtlClockGet(), 115200, UART_CONFIG_WLEN_8|UART_CONFIG_STOP_ONE|UART_CONFIG_PAR_NONE);UARTFIFOEnable(UART1_BASE);UARTFIFOLevelSet(UART1_BASE, UART_FIFO_TX2_8, UART_FIFO_RX2_8);UARTIntEnable(UART1_BASE, UART_INT_RX|UART_INT_RT);void imu_handler(void);UARTIntRegister(UART1_BASE, imu_handler);IntPrioritySet(INT_UART1, USER_INT2);IntEnable(INT_UART1);IntMasterEnable();UARTEnable(UART1_BASE);
}
static uint8_t get_verify_code();//数据校准

10个为一组,对串口获取到的 buffer[i]数据进行求和。

static uint8_t get_verify_code()
{uint32_t sum = 0;for (uint8_t i = 0; i < 10; ++i) {sum += buffer[i];}return sum&0xff;
}
void imu_analysis(); //数据分析

对陀螺仪获取到的数据进行处理,得到角速度和角度。

其中有一个对角度的巧妙处理,是的偏航角以z轴作为0度分界,解决陀螺仪自身得到角度的不连续性。

    if (last_angle_z < -90 && angle_z > 90)k --;if (last_angle_z > 90 && angle_z < -90)k ++;last_angle_z = angle_z;
//    printf("k:%ld\n", k);angle.z = angle_z + (float )k*360;
int32_t k = 0;
void imu_analysis()
{static float last_angle_z = 0, angle_z = 0;switch (buffer[1]) {case 0x51:acceleration.x = (float )((int16_t)(buffer[2]|(buffer[3]<<8)))/32768*16;acceleration.y = (float )((int16_t)(buffer[4]|(buffer[5]<<8)))/32768*16;acceleration.z = (float )((int16_t)(buffer[6]|(buffer[7]<<8)))/32768*16;break;case 0x52:gyroscope.x = (float )((int16_t)(buffer[2]|(buffer[3]<<8)))/32768*2000;gyroscope.y = (float )((int16_t)(buffer[4]|(buffer[5]<<8)))/32768*2000;gyroscope.z = (float )((int16_t)(buffer[6]|(buffer[7]<<8)))/32768*2000;break;case 0x53:angle.x = (float )((int16_t)(buffer[2]|(buffer[3]<<8)))/32768*180;angle.y = (float )((int16_t)(buffer[4]|(buffer[5]<<8)))/32768*180;angle_z = (float )((int16_t)(buffer[6]|(buffer[7]<<8)))/32768*180;break;}if (last_angle_z < -90 && angle_z > 90)k --;if (last_angle_z > 90 && angle_z < -90)k ++;last_angle_z = angle_z;
//    printf("k:%ld\n", k);angle.z = angle_z + (float )k*360;
}
void imu_handler(); //中断回调函数

处理中断工作。在这里面调用imu_analysis()函数。

#define ERROR   0
#define DOING   1
#define SUCCESS 2
static uint8_t n = 0, state = 0;
void imu_handler()
{uint32_t data;uint32_t status=UARTIntStatus(UART1_BASE, true);UARTIntClear(UART1_BASE, status);while(UARTCharsAvail(UART1_BASE)){data = UARTCharGetNonBlocking(UART1_BASE)&0xff;//	data = UARTCharGet(UART1_BASE)&0xff;//	UARTCharPutNonBlocking(UART0_BASE, data);//	printf("data:%d\n", data);if (state == ERROR){n = 0;}buffer[n] = (uint8_t)data;if (n == 0){if (buffer[0] == 0x55){state = DOING;} else{state = ERROR;}} else if (n == 10){if (buffer[10] == get_verify_code()){state = SUCCESS;} else{state = ERROR;}}n += 1;if (state == SUCCESS){
//			printf("OK\n");imu_analysis();state = ERROR;}}
}

这篇关于两轮平衡小车制作保姆式教程(2-1)——软件模块:JY62的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/649978

相关文章

spring AMQP代码生成rabbitmq的exchange and queue教程

《springAMQP代码生成rabbitmq的exchangeandqueue教程》使用SpringAMQP代码直接创建RabbitMQexchange和queue,并确保绑定关系自动成立,简... 目录spring AMQP代码生成rabbitmq的exchange and 编程queue执行结果总结s

Nginx添加内置模块过程

《Nginx添加内置模块过程》文章指导如何检查并添加Nginx的with-http_gzip_static模块:确认该模块未默认安装后,需下载同版本源码重新编译,备份替换原有二进制文件,最后重启服务验... 目录1、查看Nginx已编辑的模块2、Nginx官网查看内置模块3、停止Nginx服务4、Nginx

python urllib模块使用操作方法

《pythonurllib模块使用操作方法》Python提供了多个库用于处理URL,常用的有urllib、requests和urlparse(Python3中为urllib.parse),下面是这些... 目录URL 处理库urllib 模块requests 库urlparse 和 urljoin编码和解码

创建springBoot模块没有目录结构的解决方案

《创建springBoot模块没有目录结构的解决方案》2023版IntelliJIDEA创建模块时可能出现目录结构识别错误,导致文件显示异常,解决方法为选择模块后点击确认,重新校准项目结构设置,确保源... 目录创建spChina编程ringBoot模块没有目录结构解决方案总结创建springBoot模块没有目录

idea Maven Springboot多模块项目打包时90%的问题及解决方案

《ideaMavenSpringboot多模块项目打包时90%的问题及解决方案》:本文主要介绍ideaMavenSpringboot多模块项目打包时90%的问题及解决方案,具有很好的参考价值,... 目录1. 前言2. 问题3. 解决办法4. jar 包冲突总结1. 前言之所以写这篇文章是因为在使用Mav

Python标准库datetime模块日期和时间数据类型解读

《Python标准库datetime模块日期和时间数据类型解读》文章介绍Python中datetime模块的date、time、datetime类,用于处理日期、时间及日期时间结合体,通过属性获取时间... 目录Datetime常用类日期date类型使用时间 time 类型使用日期和时间的结合体–日期时间(

python使用Akshare与Streamlit实现股票估值分析教程(图文代码)

《python使用Akshare与Streamlit实现股票估值分析教程(图文代码)》入职测试中的一道题,要求:从Akshare下载某一个股票近十年的财务报表包括,资产负债表,利润表,现金流量表,保存... 目录一、前言二、核心知识点梳理1、Akshare数据获取2、Pandas数据处理3、Matplotl

Python pandas库自学超详细教程

《Pythonpandas库自学超详细教程》文章介绍了Pandas库的基本功能、安装方法及核心操作,涵盖数据导入(CSV/Excel等)、数据结构(Series、DataFrame)、数据清洗、转换... 目录一、什么是Pandas库(1)、Pandas 应用(2)、Pandas 功能(3)、数据结构二、安

2025版mysql8.0.41 winx64 手动安装详细教程

《2025版mysql8.0.41winx64手动安装详细教程》本文指导Windows系统下MySQL安装配置,包含解压、设置环境变量、my.ini配置、初始化密码获取、服务安装与手动启动等步骤,... 目录一、下载安装包二、配置环境变量三、安装配置四、启动 mysql 服务,修改密码一、下载安装包安装地

电脑提示d3dx11_43.dll缺失怎么办? DLL文件丢失的多种修复教程

《电脑提示d3dx11_43.dll缺失怎么办?DLL文件丢失的多种修复教程》在使用电脑玩游戏或运行某些图形处理软件时,有时会遇到系统提示“d3dx11_43.dll缺失”的错误,下面我们就来分享超... 在计算机使用过程中,我们可能会遇到一些错误提示,其中之一就是缺失某个dll文件。其中,d3dx11_4