两轮平衡小车制作保姆式教程(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 Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

python: 多模块(.py)中全局变量的导入

文章目录 global关键字可变类型和不可变类型数据的内存地址单模块(单个py文件)的全局变量示例总结 多模块(多个py文件)的全局变量from x import x导入全局变量示例 import x导入全局变量示例 总结 global关键字 global 的作用范围是模块(.py)级别: 当你在一个模块(文件)中使用 global 声明变量时,这个变量只在该模块的全局命名空

Makefile简明使用教程

文章目录 规则makefile文件的基本语法:加在命令前的特殊符号:.PHONY伪目标: Makefilev1 直观写法v2 加上中间过程v3 伪目标v4 变量 make 选项-f-n-C Make 是一种流行的构建工具,常用于将源代码转换成可执行文件或者其他形式的输出文件(如库文件、文档等)。Make 可以自动化地执行编译、链接等一系列操作。 规则 makefile文件

深入探索协同过滤:从原理到推荐模块案例

文章目录 前言一、协同过滤1. 基于用户的协同过滤(UserCF)2. 基于物品的协同过滤(ItemCF)3. 相似度计算方法 二、相似度计算方法1. 欧氏距离2. 皮尔逊相关系数3. 杰卡德相似系数4. 余弦相似度 三、推荐模块案例1.基于文章的协同过滤推荐功能2.基于用户的协同过滤推荐功能 前言     在信息过载的时代,推荐系统成为连接用户与内容的桥梁。本文聚焦于

软件设计师备考——计算机系统

学习内容源自「软件设计师」 上午题 #1 计算机系统_哔哩哔哩_bilibili 目录 1.1.1 计算机系统硬件基本组成 1.1.2 中央处理单元 1.CPU 的功能 1)运算器 2)控制器 RISC && CISC 流水线控制 存储器  Cache 中断 输入输出IO控制方式 程序查询方式 中断驱动方式 直接存储器方式(DMA)  ​编辑 总线 ​编辑

SWAP作物生长模型安装教程、数据制备、敏感性分析、气候变化影响、R模型敏感性分析与贝叶斯优化、Fortran源代码分析、气候数据降尺度与变化影响分析

查看原文>>>全流程SWAP农业模型数据制备、敏感性分析及气候变化影响实践技术应用 SWAP模型是由荷兰瓦赫宁根大学开发的先进农作物模型,它综合考虑了土壤-水分-大气以及植被间的相互作用;是一种描述作物生长过程的一种机理性作物生长模型。它不但运用Richard方程,使其能够精确的模拟土壤中水分的运动,而且耦合了WOFOST作物模型使作物的生长描述更为科学。 本文让更多的科研人员和农业工作者

基于UE5和ROS2的激光雷达+深度RGBD相机小车的仿真指南(五):Blender锥桶建模

前言 本系列教程旨在使用UE5配置一个具备激光雷达+深度摄像机的仿真小车,并使用通过跨平台的方式进行ROS2和UE5仿真的通讯,达到小车自主导航的目的。本教程默认有ROS2导航及其gazebo仿真相关方面基础,Nav2相关的学习教程可以参考本人的其他博客Nav2代价地图实现和原理–Nav2源码解读之CostMap2D(上)-CSDN博客往期教程: 第一期:基于UE5和ROS2的激光雷达+深度RG

【STM32】SPI通信-软件与硬件读写SPI

SPI通信-软件与硬件读写SPI 软件SPI一、SPI通信协议1、SPI通信2、硬件电路3、移位示意图4、SPI时序基本单元(1)开始通信和结束通信(2)模式0---用的最多(3)模式1(4)模式2(5)模式3 5、SPI时序(1)写使能(2)指定地址写(3)指定地址读 二、W25Q64模块介绍1、W25Q64简介2、硬件电路3、W25Q64框图4、Flash操作注意事项软件SPI读写W2

沁恒CH32在MounRiver Studio上环境配置以及使用详细教程

目录 1.  RISC-V简介 2.  CPU架构现状 3.  MounRiver Studio软件下载 4.  MounRiver Studio软件安装 5.  MounRiver Studio软件介绍 6.  创建工程 7.  编译代码 1.  RISC-V简介         RISC就是精简指令集计算机(Reduced Instruction SetCom

免费也能高质量!2024年免费录屏软件深度对比评测

我公司因为客户覆盖面广的原因经常会开远程会议,有时候说的内容比较广需要引用多份的数据,我记录起来有一定难度,所以一般都用录屏工具来记录会议内容。这次我们来一起探索有什么免费录屏工具可以提高我们的工作效率吧。 1.福晰录屏大师 链接直达:https://www.foxitsoftware.cn/REC/  录屏软件录屏功能就是本职,这款录屏工具在录屏模式上提供了多种选项,可以选择屏幕录制、窗口