草庐IT

从零开始写STM32平衡小车代码,从0到1

大方老师讲单片机 2023-08-14 原文

从零开始写STM32平衡小车代码,从0到1

教你从零开始写STM32平衡小车代码

前言:

本人也是学生,只是分享一下自己的设计思路与代码教学。

这次STM32平衡小车是基于STM32CubeMX软件生成HAL库代码编写。

第一部分:前期准备

这部分主要是关于组装基本平衡小车需要的零件

1.小车车架以及电机固定架轮子和联轴器(我是通过淘宝上购买的)

2.带有编码器的直流电机

3.直流电机驱动电路(也可以通过网上购买推荐大鱼电子的电机驱动价格:30+)

4.一个STM32最小系统板(本人芯片型号为STM32F103ZET6)

5.一个陀螺仪随意型号可以买贵一点的这样数据不会很多毛刺,如果要节约成本就需要自己进行滤波优化了。(本人使用的是维特智能jy61p)

6.3S航模电池,想要小车跑起来电池是必不可少的

第二部分:新建STM32CubeMX工程

主页面

进入页面后先选择一款MUC:点击 ACCESS TO MCU

在此搜索你们使用的芯片型号即可

进去后先点击SYS修改Debug模式,我这里选择SWD

在RCC下选择外部晶振如图

点击上方的时钟树,正确配置后只要在最后

这个位置,输入最大的速率,软件就会自动配置好时钟树

最后在上方的项目管理界面选择你要的IDE

勾选需要的配置,这样工程就基本建立完成。

第三部分:配置需要的外设

在第二部分的基础上配置

1.定时器

首先是定时器,想要驱动电机,我们需要配置定时器生成PWM波

这里使用TIM1的CH1和CH2生成PWM控制两路电机

自动重装载值设置为7200-1

这样生成的是10khz的方波足够我们使用了

这里顺便说一下电机的驱动原理:电机的转速是和PWM的占空比成正比的。

2.编码器

编码器是读取电机真实速度,作为后序PID控制的反馈值。

TIM2和TIM3都如图配置编码器模式

3.主要控制定时器

配置一个10ms的定时器,作为我们的控制周期

网上很多平衡小车都是通过陀螺仪的引脚外部中进行控制周期

其实不是很需要这样,这样写代码也不好理解

只需要确保陀螺仪的回传速率能够大于等于控制周期即可

4.串口

作为陀螺仪数据的接收和处理

波特率为115200

如果和我是同款陀螺仪的话初始波特率是9600回传速率是10hz需要使用官方的上位机进行设置。

其实也可以使用指令进行配置,但是我一直没有成功,最后还是使用TTL连接上位机配置。

这样所有需要的配置都配置完啦,接下来只要点击右上角的生成代码就可以啦。

会生成这样一个文件夹

打开里面的MDK-ARM就是熟悉的Keil了因为我选择的是MDK IDE

第四部分:代码编写

1.基础HAL库运行编写

因为上面的步骤软件已经帮我们把基础的外设驱动代码编写完了

接下来我们要做一些必要的代码

在HAL库中定时器中断需要我们手动开启

找到main.c

开启定时器中断开启编码器和PWM以及串口接收中断

以及编码器启动,和PWM生成都需要手动先开启

2.编码器代码

因为编码器是基于定时器的,所以我们找到tim.c底下进行代码编写

找到user code begin1

因为HAL库代码是软件生成的我们需要按照提示进行编写代码,否则下次生成代码,会消失

intRead_Encoder(uint8_tTIMX){

intEncoder_TIM;

switch(TIMX)

{

case2:Encoder_TIM=(short)TIM2->CNT;TIM2->CNT=0;break;

case3:Encoder_TIM=(short)TIM3->CNT;TIM3->CNT=0;break;

case4:Encoder_TIM=(short)TIM4->CNT;TIM4->CNT=0;break;

default:Encoder_TIM=0;

}

returnEncoder_TIM;}

编码器代码如上,控制周期为10ms

3.电机控制代码

先前讲过电机主要是通过PWM的占空比进行速度控制

所以我们需要编写一个控制占空比的函数

voidsetspeed(intleft_1,intright_1){

if(right_1>0)

{

HAL_GPIO_WritePin(GPIOB,GPIO_PIN_14,GPIO_PIN_RESET);//电机方向控制HAL_GPIO_WritePin(GPIOB,GPIO_PIN_15,GPIO_PIN_SET);

}

else

{

HAL_GPIO_WritePin(GPIOB,GPIO_PIN_14,GPIO_PIN_SET);//电机方向控制HAL_GPIO_WritePin(GPIOB,GPIO_PIN_15,GPIO_PIN_RESET);

}

if(left_1>0)

{

HAL_GPIO_WritePin(GPIOF,GPIO_PIN_9,GPIO_PIN_RESET);

HAL_GPIO_WritePin(GPIOF,GPIO_PIN_10,GPIO_PIN_SET);

}

else

{

HAL_GPIO_WritePin(GPIOF,GPIO_PIN_9,GPIO_PIN_SET);

HAL_GPIO_WritePin(GPIOF,GPIO_PIN_10,GPIO_PIN_RESET);

}

if(left_16900)//满速是7200限制速度避免坏电机left_1=-6900;

if(left_1>6900)

left_1=6900;

if(right_16900)

right_1=-6900;

if(right_1>6900)

right_1=6900;

left_1=abs(left_1);//输出占空比一定不能是负值right_1=abs(right_1);

__HAL_TIM_SET_COMPARE(&htim1,TIM_CHANNEL_2,left_1);

__HAL_TIM_SET_COMPARE(&htim1,TIM_CHANNEL_1,right_1);}

稍微讲解一下代码,首先需要对输入的占空比进行判断,是负的还是正的

对电机进行方向控制

后序进行一个限制速率的操作,避免电机太快损坏

由于输出给驱动的占空比一定不是能负数,所以我们还需要一个abs函数取绝对值

__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_2,left_1);

就是HAL库设置TIM1通道2的占空比函数

4.陀螺仪

陀螺仪是整个小车的灵魂,但是代码太长不好展示,大家可以去卖家找对应的例程

这里说一下思路就是通过串口不断读取数据

/***************串口中断回调函数数据处理***************/voidHAL_UART_RxCpltCallback(UART_HandleTypeDef*huart){

if(huart->Instance==USART2)

{

USART2_IT_OK=USART2_IT_OK+1;

CopeSerial2Data(jydata);

//陀螺仪数据处理

//加速度a[0]=(float)stcAcc.a[0]/32768*16;

a[1]=(float)stcAcc.a[1]/32768*16;

a[2]=(float)stcAcc.a[2]/32768*16;

//角速度w[0]=(float)stcGyro.w[0]/32768*2000;

w[1]=(float)stcGyro.w[1]/32768*2000;

w[2]=(float)stcGyro.w[2]/32768*2000;

//角度Angle[0]=(float)stcAngle.Angle[0]/32768*180;

Angle[1]=(float)stcAngle.Angle[1]/32768*180;

Angle[2]=(float)stcAngle.Angle[2]/32768*180;

//四元素q[0]=(float)stcQ.q[0]/32768;

q[1]=(float)stcQ.q[1]/32768;

q[2]=(float)stcQ.q[2]/32768;

q[3]=(float)stcQ.q[3]/32768;

HAL_UART_Receive_IT(&huart2,&jydata,1);

}}

5.PID算法代码

PID算法在小车中有很多种

这里使用的是经过推导后的直立环和速度环

要让小车直立这两个环是必不可少的

转向环可以暂时不需要

直立环是让小车具有一个直立的趋势

速度环是让小车在直立的情况下还能在原地不动

转向环是让小车具有走直线的能力

这里的代码借鉴了平衡小车之家的代码

直立环代码

floatBias,kp=100,kd=12;intbalance(floatAngle,floatGyro){

intbalance;

Bias=Angle-3;//===求出平衡的角度中值和机械相关balance=kp*Bias+Gyro*kd;//===计算平衡控制的电机PWM PD控制   kp是P系数 kd是D系数returnbalance;}

单单有直立环是很难直立起来的还需要加入速度环

速度环代码

floatVelocity_Kp=60,Velocity_Ki=0.3;//PID参数intvelocity(intencoder_left,intencoder_right){

staticfloatVelocity,Encoder_Least,Encoder,Movement;

staticfloatEncoder_Integral;

//=============速度PI控制器=======================//Encoder_Least=(encoder_left+encoder_right)-0;//===获取最新速度偏差==测量速度(左右编码器之和)-目标速度(此处为零)Encoder*=0.8;//===一阶低通滤波器Encoder+=Encoder_Least*0.2;//===一阶低通滤波器Encoder_Integral+=Encoder;//===积分出位移积分时间:10msEncoder_Integral=Encoder_Integral-Movement;//===接收遥控器数据,控制前进后退if(Encoder_Integral>10000)Encoder_Integral=10000;//===积分限幅if(Encoder_Integral10000)Encoder_Integral=-10000;//===积分限幅Velocity=Encoder*Velocity_Kp+Encoder_Integral*Velocity_Ki;//===速度控制returnVelocity;}

至于PID的调参,那就是另一门学问了,这里就先不讲解

PID代码里面的一些低通滤波也不做讲解,想要做出来只要会用就行了

至此基本的代码都已经写完,是不是感觉其实平衡小车并不难造

接下来就是控制部分的代码了,要将上面的代码有序的组合起来

合成小车的灵魂

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)

{

  /* USER CODE BEGIN Callback 0 */

  /* USER CODE END Callback 0 */

  if (htim->Instance == TIM8) {

    HAL_IncTick();

  }

  /* USER CODE BEGIN Callback 1 */

if (htim->Instance == TIM1) //10khz

{

TIM1_IT_OK = 1;

}

if (htim->Instance == TIM2) //encoder

{

TIM2_IT_OK = 1;

}

if (htim->Instance == TIM3) //encoder

{

TIM3_IT_OK = 1;

}

if (htim->Instance == TIM4) //5ms

{

TIM4_IT_OK = 1;

}

if (htim->Instance == TIM6) //10ms

{

TIM6_IT_OK = 1;

Left_Speed = -Read_Encoder(3);

Right_Speed = -Read_Encoder(2);

Balance_Pwm = balance(-Angle[0],-w[0]);

Velocity_Pwm = velocity(Left_Speed,Right_Speed);

setspeed(Balance_Pwm+Velocity_Pwm,Balance_Pwm+Velocity_Pwm);

}

  /* USER CODE END Callback 1 */

}

这里是HAL库的定时器中断回调函数

因为我设置的是TIM6就在TIM6的中断回调函数中进行编写

注意控制周期是10ms对应陀螺仪的速率一定不能低于100hz不然小车就会一直摇摆。

先读取编码器的数据,自己在操作的时候要调好正负,因为每个人安装都不太一样

平衡小车pid调参后

ok教程到此结

有关从零开始写STM32平衡小车代码,从0到1的更多相关文章

  1. ruby - 如何在 buildr 项目中使用 Ruby 代码? - 2

    如何在buildr项目中使用Ruby?我在很多不同的项目中使用过Ruby、JRuby、Java和Clojure。我目前正在使用我的标准Ruby开发一个模拟应用程序,我想尝试使用Clojure后端(我确实喜欢功能代码)以及JRubygui和测试套件。我还可以看到在未来的不同项目中使用Scala作为后端。我想我要为我的项目尝试一下buildr(http://buildr.apache.org/),但我注意到buildr似乎没有设置为在项目中使用JRuby代码本身!这看起来有点傻,因为该工具旨在统一通用的JVM语言并且是在ruby中构建的。除了将输出的jar包含在一个独特的、仅限ruby​​

  2. ruby-on-rails - Rails 源代码 : initialize hash in a weird way? - 2

    在rails源中:https://github.com/rails/rails/blob/master/activesupport/lib/active_support/lazy_load_hooks.rb可以看到以下内容@load_hooks=Hash.new{|h,k|h[k]=[]}在IRB中,它只是初始化一个空哈希。和做有什么区别@load_hooks=Hash.new 最佳答案 查看rubydocumentationforHashnew→new_hashclicktotogglesourcenew(obj)→new_has

  3. ruby - 匹配未转义的平衡定界符对 - 2

    如何匹配未被反斜杠转义的平衡定界符对(其本身未被反斜杠转义)(无需考虑嵌套)?例如对于反引号,我试过了,但是转义的反引号没有像转义那样工作。regex=/(?!$1:"how\\"#expected"how\\`are"上面的正则表达式不考虑由反斜杠转义并位于反引号前面的反斜杠,但我愿意考虑。StackOverflow如何做到这一点?这样做的目的并不复杂。我有文档文本,其中包括内联代码的反引号,就像StackOverflow一样,我想在HTML文件中显示它,内联代码用一些spanMaterial装饰。不会有嵌套,但转义反引号或转义反斜杠可能出现在任何地方。

  4. ruby-on-rails - 浏览 Ruby 源代码 - 2

    我的主要目标是能够完全理解我正在使用的库/gem。我尝试在Github上从头到尾阅读源代码,但这真的很难。我认为更有趣、更温和的踏脚石就是在使用时阅读每个库/gem方法的源代码。例如,我想知道RubyonRails中的redirect_to方法是如何工作的:如何查找redirect_to方法的源代码?我知道在pry中我可以执行类似show-methodmethod的操作,但我如何才能对Rails框架中的方法执行此操作?您对我如何更好地理解Gem及其API有什么建议吗?仅仅阅读源代码似乎真的很难,尤其是对于框架。谢谢! 最佳答案 Ru

  5. ruby - 模块嵌套代码风格偏好 - 2

    我的假设是moduleAmoduleBendend和moduleA::Bend是一样的。我能够从thisblog找到解决方案,thisSOthread和andthisSOthread.为什么以及什么时候应该更喜欢紧凑语法A::B而不是另一个,因为它显然有一个缺点?我有一种直觉,它可能与性能有关,因为在更多命名空间中查找常量需要更多计算。但是我无法通过对普通类进行基准测试来验证这一点。 最佳答案 这两种写作方法经常被混淆。首先要说的是,据我所知,没有可衡量的性能差异。(在下面的书面示例中不断查找)最明显的区别,可能也是最著名的,是你的

  6. ruby - 寻找通过阅读代码确定编程语言的ruby gem? - 2

    几个月前,我读了一篇关于ruby​​gem的博客文章,它可以通过阅读代码本身来确定编程语言。对于我的生活,我不记得博客或gem的名称。谷歌搜索“ruby编程语言猜测”及其变体也无济于事。有人碰巧知道相关gem的名称吗? 最佳答案 是这个吗:http://github.com/chrislo/sourceclassifier/tree/master 关于ruby-寻找通过阅读代码确定编程语言的rubygem?,我们在StackOverflow上找到一个类似的问题:

  7. ruby - Net::HTTP 获取源代码和状态 - 2

    我目前正在使用以下方法获取页面的源代码:Net::HTTP.get(URI.parse(page.url))我还想获取HTTP状态,而无需发出第二个请求。有没有办法用另一种方法做到这一点?我一直在查看文档,但似乎找不到我要找的东西。 最佳答案 在我看来,除非您需要一些真正的低级访问或控制,否则最好使用Ruby的内置Open::URI模块:require'open-uri'io=open('http://www.example.org/')#=>#body=io.read[0,50]#=>"["200","OK"]io.base_ur

  8. 程序员如何提高代码能力? - 2

    前言作为一名程序员,自己的本质工作就是做程序开发,那么程序开发的时候最直接的体现就是代码,检验一个程序员技术水平的一个核心环节就是开发时候的代码能力。众所周知,程序开发的水平提升是一个循序渐进的过程,每一位程序员都是从“菜鸟”变成“大神”的,所以程序员在程序开发过程中的代码能力也是根据平时开发中的业务实践来积累和提升的。提高代码能力核心要素程序员要想提高自身代码能力,尤其是新晋程序员的代码能力有很大的提升空间的时候,需要针对性的去提高自己的代码能力。提高代码能力其实有几个比较关键的点,只要把握住这些方面,就能很好的、快速的提高自己的一部分代码能力。1、多去阅读开源项目,如有机会可以亲自参与开源

  9. Observability:从零开始创建 Java 微服务并监控它 (二) - 2

    这篇文章是继上一篇文章“Observability:从零开始创建Java微服务并监控它(一)”的续篇。在上一篇文章中,我们讲述了如何创建一个Javaweb应用,并使用Filebeat来收集应用所生成的日志。在今天的文章中,我来详述如何收集应用的指标,使用APM来监控应用并监督web服务的在线情况。源码可以在地址 https://github.com/liu-xiao-guo/java_observability 进行下载。摄入指标指标被视为可以随时更改的时间点值。当前请求的数量可以改变任何毫秒。你可能有1000个请求的峰值,然后一切都回到一个请求。这也意味着这些指标可能不准确,你还想提取最小/

  10. 7个大一C语言必学的程序 / C语言经典代码大全 - 2

    嗨~大家好,这里是可莉!今天给大家带来的是7个C语言的经典基础代码~那一起往下看下去把【程序一】打印100到200之间的素数#includeintmain(){ inti; for(i=100;i 【程序二】输出乘法口诀表#includeintmain(){inti;for(i=1;i 【程序三】判断1000年---2000年之间的闰年#includeintmain(){intyear;for(year=1000;year 【程序四】给定两个整形变量的值,将两个值的内容进行交换。这里提供两种方法来进行交换,第一种为创建临时变量来进行交换,第二种是不创建临时变量而直接进行交换。1.创建临时变量来

随机推荐