基于STM32F103与FreeRTOS的自平衡小车实现

2023-05-16

首先移植FreeRTOS到STM32F103上,接着就是实现MPU6050的初始化,这里移植了正点原子的参考例程,基本实现是IIC初始化的,读写IIC,接着就可以配置MPU6050,就可以从MPU6050的指定寄存器读取到原始数据,这个原始数据还是不能用的,很好不会了,MPU6050有自带DMP(数字运动处理芯片),不错,再驱动一下DMP就能得到,经过DMP处理后就得到了姿态四元数,

nice,经过一个算法的转化就可以得到俯仰角,横滚角,航向角。这里我们用到了就两个,横滚角直立环与航向角角度环。

这下就差一个速度环的速度了,我们使用的是带霍尔编码器的减速电机,因此只需要配置一下定时器作为捕获模式就行了,我们可以再拿一个定时器来计时,这样当计时到达进入定时器中断我们可以查看一下脉冲数,然后我们拿着电机自转一周就能得到转一圈有多少个脉冲数了,就可以得到每一个脉冲所占的比例,就可以得到r/s这样的速度,我们又可以测量半径计算周长2Πr = (r)转得到实际速度。这样我们就得到了速度。

接着就是PID算法了,采用了直立环+速度环+角度环的形式实现

PID也就是比例调节+积分调节+微分调节

举一个经典例子:假设想操控无人机向上飞到离地面20m处的位置,我们启动无人机,无人机慢慢上飞,从正准备脱离地面开始,我们无人机实际位置与期待位置相距100m,此时我们给无人机一个速度v让它上飞t秒,我们预期是v*t刚好能飞到100m就保持悬停,这时我们其实就是做了PID的P(比例)调节,比例系数为1,但我们的比例系数为1不就行了吗?为什么不行呢?当无人机很接近目标距离,也就是当前误差很小时,我们给无人机一个动力来试图到达目标高度,这是不可行的,因为实际无人机运行一定会受到阻力,这时这个动力只能与上升受到的阻力相互抵消而实际并没有上升,这也就是说现实情况并不会那么理想,这时我们P调节有啥意义呢,当我们减小P比例时,我们会花更长的时间到达那个很小误差的临界值,而当我们的P增大时,我们只需很少的时间就能到达那个临界误差值,当我们的P过大时就会先在振荡目标距离振荡很多次后在到达那个临界误差。对于高精度的设备当然不允许有这种误差存在,于是I算法就是来解决这一问题,I算法会进行误差值的累加,但显而前面几次误差过大,我们得先忽略,不然I算法也无效,I算法是积分算法,累加之前的误差,加上一个合适的系数,这就会补偿前面那个临界误差来带的影响,这样我们的PID算法就差D算法了,D算法是为了解决当我们的无人机好好的飞着突然受到了一个强风吹那咋办,岂不会立马系统就变得晃动很大,因为P这时对于的差值很大,为了解决这一点,D算法微分就刚好能解决这一点,D算法积分算法是通过计算此次偏差与上一次偏差两者的差值,这样的话如果上次偏差很小而这次偏差很大,就会变为-此次偏差,这样与p导致的大幅度改变就能相抵

好了,PID懂了,接着我们就差三个环了

速度环:输入目标速度,通过PID算法得到速度环输出

直立环:输入横滚角+速度环输出,通过PID算法得到PWM

转向环:输入航向角,通过PID算法得到转向环输出

左右两电机PWM分别为直立环算出的PWM减去和加上转向环输出

完成。

参考代码

#include "control.h"

float Med_Angle=0;      // 机械中值,能使得小车真正平衡住的角度 
float Target_Speed=0;	  // 期望速度。---二次开发接口,用于控制小车前进后退及其速度。
float 
  Vertical_Kp=0,
  Vertical_Kd=0;     // 直立环Kp、Kd
float 
  Velocity_Kp=0,     // 速度环Kp、Ki(正反馈)
  Velocity_Ki=0;
float 
  Turn_Kp=0;

int Vertical_out,Velocity_out,Turn_out; // 直立环&速度环&转向环的输出变量

int Vertical(float Med,float Angle,float gyro_Y); // 函数声明
int Velocity(int Target,int encoder_left,int encoder_right);
int Turn(int gyro_Z);

void EXTI9_5_IRQHandler(void)
{
  int PWM_out;
  if(EXTI_GetITStatus(EXTI_Line5)!=0) // 一级判定
  {
    if(PBin(5)==0)    // 二级判断
    { 
      EXTI_ClearITPendingBit(EXTI_Line5); // 清除中断标志位
      // 1.采集编码器数据&MPU6050角度信息
      // 电机是相对安装,刚好相差180度,为了编码器输出极性一致,就需要对其中一个取反
      Encoder_Left  = -Read_Speed(2); 
      Encoder_Right = Read_Speed(4);
      
      mpu_dmp_get_data(&Pitch,&Roll,&Yaw);	    // 读取角度
      MPU_Get_Gyroscope(&gyrox,&gyroy,&gyroz);  // 读取角速度
      MPU_Get_Accelerometer(&aacx,&aacy,&aacz); // 读取加速度
      // 2.将数据压入闭环控制中,计算出控制输出量
			Velocity_out=Velocity(Target_Speed,Encoder_Left,Encoder_Right); // 速度环
      Vertical_out=Vertical(Velocity_out+Med_Angle,Roll,gyrox);			  // 直立环
			Turn_out=Turn(gyroz);	
      
      PWM_out=Vertical_out;//最终输出
      
      // 3.把控制输出量加载到电机上,完成最终控制
      MOTO1 = PWM_out-Turn_out; // 左电机
      MOTO2 = PWM_out+Turn_out; // 右电机
      Limit(&MOTO1,&MOTO2);     // PWM限幅
      Load(MOTO1,MOTO2);        // 加载到电机上
    }
  }
}

/*****************  
直立环PD控制器:Kp*Ek+Kd*Ek_D

入口:Med:机械中值(期望角度),Angle:真实角度,gyro_Y:真实角速度
出口:直立环输出
******************/
int Vertical(float Med,float Angle,float gyro_Y) 
{
  int PWM_out;
  
  PWM_out = Vertical_Kp*(Angle-Med)+Vertical_Kd*(gyro_Y-0);
  
  return PWM_out;
} 

/*****************  
速度环PI控制器:Kp*Ek+Ki*Ek_S(Ek_S:偏差的积分)
******************/
int Velocity(int Target,int encoder_left,int encoder_right)
{
  // 定义成静态变量,保存在静态存储器,使得变量不丢掉
  static int PWM_out,Encoder_Err,Encoder_S,EnC_Err_Lowout,EnC_Err_Lowout_last;
  float a=0.7;
  
  // 1.计算速度偏差
  //舍去误差--我的理解:能够让速度为"0"的角度,就是机械中值。
  Encoder_Err = ((encoder_left+encoder_right)-Target);
  // 2.对速度偏差进行低通滤波
  // low_out = (1-a)*Ek+a*low_out_last
  EnC_Err_Lowout = (1-a)*Encoder_Err + a*EnC_Err_Lowout_last; // 使得波形更加平滑,滤除高频干扰,放置速度突变
  EnC_Err_Lowout_last = EnC_Err_Lowout;   // 防止速度过大影响直立环的正常工作
  // 3.对速度偏差积分出位移
  Encoder_S+=EnC_Err_Lowout;
  // 4.积分限幅
  Encoder_S=Encoder_S>10000?10000:(Encoder_S<(-10000)?(-10000):Encoder_S);
  
  // 5.速度环控制输出
  PWM_out = Velocity_Kp*EnC_Err_Lowout+Velocity_Ki*Encoder_S;
  
  return PWM_out;
}

/*****************  
转向环:系数*Z轴角速度
******************/
int Turn(int gyro_Z)
{
  int PWM_out;
  
  PWM_out = Turn_Kp*gyro_Z;
  
  return PWM_out;
}

再加上一个蓝牙模块就可以通过蓝牙app进行控制了

这里采用的PID算法是位置式的,其实增量式呢就是就行保留上一次位置式进行相减而已。

这里可以看到使用到了速度环和直立环进行串联,速度环作为外环,直立环作为内环的形式进行串级PID,而角度环过于简单就没必要再搞这种串级关系

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

基于STM32F103与FreeRTOS的自平衡小车实现 的相关文章

  • 基于STM32的超声波感应垃圾桶

    目录 成果演示 材料 主要代码 总结 成果演示 材料 STM323f103开发板 最小系统均可 超声波模块 HC SR04模块 舵机一个 垃圾桶模型 主要代码 超声波模块 include ultrasonsic h include dela
  • FreeRTOS简述和移植文档

    FreeRTOS简述和移植文档 文章目录 FreeRTOS简述和移植文档 1 前言 2 FreeRTOS简述 1 概述 2 实现 3 主要特色 4 支持平台 3 移植FreeRTOS 4 最后 1 前言 目前由于IOT的飞速发展 针对单片机
  • FreeRTOS学习笔记(3、信号量、互斥量的使用)

    FreeRTOS学习笔记 3 信号量 互斥量的使用 前言 往期学习笔记链接 学习工程 信号量 semaphore 两种信号量的对比 信号量的使用 1 创建信号量 2 give 3 take 4 删除信号量 使用计数型信号量实现同步功能 使用
  • 【FreeRTOS】多任务创建

    作者主页 凉开水白菜 作者简介 共同学习 互相监督 热于分享 多加讨论 一起进步 专栏资料 https pan baidu com s 1nc1rfyLiMyw6ZhxiZ1Cumg pwd free 点赞 收藏 再看 养成习惯 订阅的粉丝
  • STM32移植FreeRTOS的Tips

    转自 http bbs armfly com read php tid 7140 1 在FreeRTOS的demo文件夹中拷贝对应的FreeRTOSConfig h文件后 需要加入一行 define configUSE MUTEXES 1
  • STM32CUBEMX F103 HAL库开发 两路定时器的Encoder编码器模式

    机器人开发过程中 对于直流电机来说 编码器至关重要 它不仅可以使我们对电极进行精确的速度闭环 位置闭环 还可以通过时间积分 根据运动学关系 获得速度 位置等信息 STM32的定时器有编码器模式 大大的方便我们的开发 使用STM32cubeM
  • Error: L6218E: Undefined symbol vApplicationGetIdleTaskMemory (referred from tasks.o).

    我用的是F103ZET6的板子 移植成功后 编译出现两个错误是关于stm32f10x it c 里 void SVC Handler void void PendSV Handler void 两个函数的占用问题 随后编译出现以下两个问题
  • 【FreeRTOS】任务通知的使用

    作者主页 凉开水白菜 作者简介 共同学习 互相监督 热于分享 多加讨论 一起进步 专栏资料 https pan baidu com s 1nc1rfyLiMyw6ZhxiZ1Cumg pwd free 点赞 收藏 再看 养成习惯 订阅的粉丝
  • STM32F103 - 配置规则通道参数 - 05 - unfinished -unfinished-unfinished

    五 配置规则通道参数 设置指定ADC的规则组通道 一个序列 采样时间 常规通道配置 ADC RegularChannelConfig ADC1 ch 1 ADC SampleTime 239Cycles5 ADC1 ADC通道 采样时间为2
  • FreeRTOS,串口中断接收中使用xQueueOverwriteFromISR()函数,程序卡死在configASSERT

    原因 UART的中断优先级设置的太高 高于了configMAX SYSCALL INTERRUPT PRIORITY宏定义的安全中断等级 UART的中断等级小于等于宏定义的优先等级即可
  • FreeRTOS学习(三)开关中断

    声明及感谢 跟随正点原子资料学习 在此作为学习的记录和总结 环境 keil stm32f103 背景知识 Cotex M3的NVIC最多支持240个IRQ 中断请求 1个不可屏蔽 NMI 1个Systick 滴答定时器 Cortex M处理
  • 啊哈C的简单使用

    打开啊哈C 新建一个程序输出hello world include
  • FreeRTOS死机原因

    1 中断回调函数中没有使用中断级API xxFromISR 函数 xSemaphoreGiveFromISR uart busy HighterTask 正确 xSemaphoreGive uart busy 错误 2 比configMAX
  • RT-Thread记录(五、RT-Thread 临界区保护与FreeRTOS的比较)

    本文聊聊临界区 以及RT Thread对临界区的处理 通过源码分析一下 RT Thread 对临界区保护的实现以及与 FreeRTOS 处理的不同 目录 前言 一 临界区 1 1 什么是临界区 1 2 RTOS中的临界区 二 RT Thre
  • freeRTOS出现任务卡死的情况。

    最近在做一个产品二代升级的项目 代码是上一任工程师留下的 很多BUG 而且融合了HAL库和LL库 以及github上下载的GSM源码 很不好用 我这边是将2G模块换成了4G 且添加了单独的BLE模块 因此只在源码的基础上 去除2G和BLE代
  • 13-FreeRTOS任务创建与删除

    任务创建和删除API函数位于文件task c中 需要包含task h头文件 task h里面包函数任务的类型函数 例如 对xTaskCreate的调用 通过指针方式 返回一个TaskHandle t 变量 然后可将该变量用vTaskDele
  • 当一个任务写入变量而其他任务读取该变量时,我们是否需要信号量?

    我正在研究 freeRtos 并且我有一个名为 x 的变量 现在 每秒只有一个任务正在写入该变量 而其他任务正在读取该变量值 我需要用互斥锁来保护变量吗 如果变量为 32 位或更小 并且其值是独立的并且不与任何其他变量一起解释 则不需要互斥
  • 如何更改 FreeRTOS 中任务的最大可用堆大小?

    我通过以下方式在任务中创建元素列表 l dllist pvPortMalloc sizeof dllist dlllist 有 32 字节大 我的嵌入式系统有 60kB SRAM 所以我希望系统可以轻松处理我的 200 个元素列表 我发现在
  • GNU Arm Cortex m4 上的 C++ 异常处理程序与 freertos

    2016 年 12 月更新现在还有一个关于此行为的最小示例 https community nxp com message 862676 https community nxp com message 862676 我正在使用带有 free
  • 有关 CMake 错误的问题:没有为目标提供源

    我正在尝试使用 cmake 和 eclipse 将 FreeRtos 添加到我的项目中 但出现错误 我运行的是 debian 10 我的 cmake 版本是 3 13 4 cmake 的文件可以在以下位置找到这个 git 仓库 https

随机推荐

  • Vue中引入自定义公共组件方法以及步骤

    什么是公共组件 xff0c 公共组件的使用场景 项目中如果多个页面都显示有相同的区域内容 xff0c 则该公共区域内容可以封装成公共组件进行使用 步骤 xff1a 1 创建自定义公共组件 xff0c 在src下的components目录中自
  • vue中的data为什么是一个函数

    首先 xff1a 组件是一个可复用的Vue实例 xff0c 一个组件被创建好之后 xff0c 就可能被用在各个地方 xff0c 而组件不管被复用多少次 xff0c 组件中data数据都应该是相互隔离 xff0c 互不影响的 xff0c 基于
  • Vue中key值的作用

    Vue中key值的作用 首先v for 在列表渲染时 xff0c 我们可以用v for基于一个数组来渲染一个列表 v for指令需要使用item in arr形式的特殊语法来进行渲染列表 xff0c arr是源数据 xff0c span c
  • 电商后台管理项目d01

    电商后台管理项目d01 1 项目技术栈 2 项目初始化 3 Element UI 的按需引入 4 路由配置 5 Axios 的封装 6 实现登录功能 7 完成首页部分 8 用户管理 用户列表 9 权限管理 1 角色
  • react之jsx语法规则

    希望在之后的日子里 xff0c 能够时常更新 xff01 定义虚拟DOM时 xff0c 不要写引号 标签中混入JSX表达式时 xff0c 要用 样式的类名不要用class属性 xff0c 要是用clsaaName属性 lt h1 class
  • 电子凭证文件上传

    最近 xff0c 一直在做一些关于文件上传 xff0c 以及凭证导出打印的工作 xff0c 做一些记录 xff0c 方便日后的查阅 对了 xff0c 我在这里用的是antDesign这个第三方组件 文件上传 vue模板中 lt p gt l
  • 可视化图表API格式要求有哪些?Sugar BI详细代码示例(2)

    Sugar BI中的每个图表可以对应一个数据 API xff0c 用户浏览报表时 xff0c 选定一定的过滤条件 xff0c 点击 查询 按钮将会通过 API 拉取相应的数据 xff1b 前面说过 xff0c 为了确保用户数据的安全性 xf
  • "Warning: GetWindowMenuPopup failed! "

    对mdi程序中一个弹出菜单警告原因的分析 作者 laomai 网址 http blog csdn net laomai xff08 转载时请注明出处 xff09 一 引子 最近在编译一个别人的mdi程序代码 xff0c 在调试程序时 vc6
  • div仿input的使用

    需求描述 xff0c 输入框支持文本输入 xff0c 以及支持标签在对应节点的插入 1 首先封装组件 xff0c 通过父子组件传参的方式进行数据的处理 用富文本插件体积略大通过div标签的contenteditable属性来处理成仿inpu
  • 关于优雅去重的一些感想

    也就不赘述有的没的 xff0c 看代码 1 通过reduce 方法进行去重 this pageDataList 61 this pageDataList reduce tempArr item 61 gt if tempArr findIn
  • java形参的改变会影响实参吗?

    java形参的改变会影响实参吗 xff1f 昨天做题的时候遇到了这个问题 xff08 如图所示 xff09 xff0c 传入的参数是int 数组 xff0c 实参跟着形参一起改变了 但是之前传入int型参数时形参的改变是不会影响实参的 所以
  • 快速教你数据清洗的步骤及方法,不可错过

    说起数据清洗 xff0c 可能会有些小伙伴会觉得这一步可以忽略掉 xff0c 但是 xff01 作为混迹在数据分析这一块多年的老油条 xff0c 小编在此严肃地声明 xff01 资料清理是资料处理中最不能被忽略的部分 xff0c 它是资料分
  • 阿里八年大佬,分享三款值得推荐的开源接口测试工具

    三款值得推荐的开源接口测试工具 接口测试可以测试APIs Application Programming Interface 是否符合功能 xff0c 可靠性 xff0c 性能和安全要求 接口测试对于成功的CI DevOps来说至关重要 J
  • Gazebo的安装&与ROS的连接

    一 安装 1 添加源 span class token function sudo span span class token function sh span c span class token string 39 echo 34 de
  • 3d仿真文献综述

    文献综述 Vincent文论创新点Digital Twin based synchronised control and simulation of the industrial robotic cell using Virtual Rea
  • MATLAB多个子图 用一个 colorbar

    多个子图使用同一个colorbar left bottom width height ps42 61 zeros 8 4 ps42 3 61 0 44 ps42 4 61 0 20 ps42 7 8 2 61 0 08 ps42 5 6 2
  • ARS_408毫米波雷达数据解析学习记录-利用RVIZ实现解析结果的可视化

    前面已经基本完成了ARS 408毫米波雷达数据的获取与解析工作 虽然已经将从CAN口获取的数据解析处理成指定的消息类型并进行了发布 xff0c 但是需要注意的是 xff0c 它们只是处于文本数据形态 xff0c 我们仍然无法得到直观的显示结
  • ROS+D435i+YOLOv5+deepsort实现目标识别跟踪、测距、测速

    本来在linux下实现目标识别不麻烦 xff0c 麻烦的是当你只有一个深度摄像头且别的应用需要在ROS下执行并占用摄像头资源导致别的线程无法获得摄像头数据 反观使用深度摄像头能干很多事情 xff0c 测距 xff0c 测速 xff0c 坐标
  • 可视化图表API格式要求有哪些?Sugar BI详细代码示例(3)

    Sugar BI中的每个图表可以对应一个数据 API xff0c 用户浏览报表时 xff0c 选定一定的过滤条件 xff0c 点击 查询 按钮将会通过 API 拉取相应的数据 xff1b 前面说过 xff0c 为了确保用户数据的安全性 xf
  • 基于STM32F103与FreeRTOS的自平衡小车实现

    首先移植FreeRTOS到STM32F103上 xff0c 接着就是实现MPU6050的初始化 xff0c 这里移植了正点原子的参考例程 xff0c 基本实现是IIC初始化的 xff0c 读写IIC xff0c 接着就可以配置MPU6050