两轮平衡小车制作保姆式教程(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_H

typedef 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 2

void 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;
		}
	}
}

代码放在我的资源了,有需要自取。
陀螺仪JY62底层代码

### 关于双轮平衡小车控制系统的设计与实现 #### 控制系统概述 在电子竞赛项目中,双轮平衡小车是一个复杂而有趣的挑战。这类车辆依靠精确的反馈机制保持稳定并执行各种动作。其核心在于有效的控制系统设计,该系统通常由多个闭环控制器组成,用于处理不同层面的任务。 #### 平衡控制策略 对于维持车身姿态至关重要的平衡功能而言,采用PID(比例-积分-微分)算法是一种常见做法[^2]。此方法能够依据陀螺仪和加速度计所提供的实时倾斜角度信息调整电机转速,从而抵消外界干扰力矩的影响,确保车子始终处于垂直状态。 #### 速度管理技术 除了静态稳定性外,动态性能同样不可忽视。为了使小车按照预期轨迹前进或停止,在速度层面上也需要引入相应的调控手段。一般情况下,这可以通过改变目标倾角间接影响行驶速率;当希望加速时增大前倾程度反之则减小之。这种基于物理特性的设计方案不仅简单直观而且易于实施。 #### 方向指引逻辑 最后还涉及到路径规划方面的要求,即让机器人具备自主导航能力。为此可以在原有基础上增加额外的方向环路,利用编码器测量左右两侧轮子各自的位移量差异作为输入变量参与计算转弯半径等参数设定过程。如此一来便能灵活应对多种应用场景下的转向需求了。 ```cpp // 示例代码片段展示了一个基本的比例控制回路用于角度校正 float Kp = 1.0; // 比例系数 void loop() { float angleError = getAngleFromSensor() - targetAngle; int motorSpeedAdjustment = Kp * angleError; setMotorSpeed(motorLeft, baseSpeed + motorSpeedAdjustment); setMotorSpeed(motorRight, baseSpeed - motorSpeedAdjustment); delay(20); // 延迟一段时间再读取新的传感器数值 } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

麦克斯同学

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

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

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

打赏作者

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

抵扣说明:

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

余额充值