QT上位机控制stm32,并利用PID控制编码电机旋转

这篇具有很好参考价值的文章主要介绍了QT上位机控制stm32,并利用PID控制编码电机旋转。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

QT上位机控制stm32,并利用PID控制编码电机旋转  

        由于最近在学习电机控制算法之类的东西,看到论文大多使用PID、或以PID衍生的ADRC作为电机的主流控制,于是自己也写了一个stm32控制L298N以驱动直流电机的程序,并用QT做了一个上位机实现了用软件改变PID的参数、电机转速、转向等功能。

一、硬件原理图     

QT上位机控制stm32,并利用PID控制编码电机旋转

实验所用到的硬件有:

带霍尔编码器的直流减速电机;

QT上位机控制stm32,并利用PID控制编码电机旋转

       霍尔编码器具体型号为JGB37-520,12V供电,一分钟旋转110转(这里指的时全速运转下的转速),两端红白两线为电机的电源(0、12V),棕蓝两线为霍尔编码器的电源(0、3.3V),中间黄绿两线为霍尔编码器的信号线(A、B两相)。

L298N电机驱动模块;

QT上位机控制stm32,并利用PID控制编码电机旋转

        L298N电机驱动模块我们用到了其中的IN1、IN2和OUT1、OUT2以及ENA使能端、接入12V电源即可驱动(注意:使能端ENA的跳帽要拔掉,不然就是默认全速运转、拔开跳帽后可以将其接入PWM信号实现电机调速)

STM32C8T6最小系统板;

用最简单的stm32模块即可。 

12V稳压电源

QT上位机控制stm32,并利用PID控制编码电机旋转        这个电源接入电脑的USB接口或其他电源的USB口都可以输出1~24V的可调电压,电压值通过旋钮旋转改变。 

二、QT上位机界面

QT上位机控制stm32,并利用PID控制编码电机旋转        此QT界面可以显示你所设定的目标速度,和当前电机旋转的实际速度,功能区可以实现对目标速度的更改,和对PID的比例、积分、微分的修改,同时还可以实现电机的正反向运转和制动。

STM32端主要程序:

l298n.c 

初始化l298n模块,并实现电机 正转、反转、制动功能。

#include "l298n.h"
#include "Delay.h"

#define IN1 GPIO_Pin_4
#define IN2 GPIO_Pin_5

u8 FLAG = 0;

void L298N_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
}

//控制电机正反转
void L298N_Positive_rotation(void)
{
	FLAG = 1;
	GPIO_SetBits(GPIOA, IN2); //正转
	GPIO_ResetBits(GPIOA, IN1);
}

void L298N_Stop(void)
{
	FLAG = 0;
	GPIO_ResetBits(GPIOA, IN2); //制动
	GPIO_ResetBits(GPIOA, IN1);
}

void L298N_Reverse_rotation(void)
{
	FLAG = 2;
	GPIO_SetBits(GPIOA, IN1); //反转
	GPIO_ResetBits(GPIOA, IN2);
}
	

pid.c 

声明pid的结构体,初始化pid各个参数,实现pid控制函数接口。

#include "stm32f10x.h"
#include "pwm.h"
#include "key.h"

struct pidstruct{
    float SetSpeed;            	//定义设定值
    float ActualSpeed;        	//定义实际值
    float err;                	//定义偏差值
    float err_last;            	//定义上一个偏差值
    float Kp,Ki,Kd;            	//定义比例、积分、微分系数
    float voltage;          	//定义电压值(控制执行器的变量)
    float integral;            	//定义积分值
}pid;

extern int32_t INPUT;

void pid_init() // pid参数初始化
{
    pid.SetSpeed=0.0;
    pid.ActualSpeed=0.0;
    pid.err=0.0;
    pid.err_last=0.0;
    pid.voltage=0.0;
    pid.integral=0.0;
    pid.Kp=0.1;
    pid.Ki=0.01;
    pid.Kd=0.3;
}

void pid_set(float Kp,float Ki,float Kd) // pid参数设置函数
{
	pid.Kp = Kp;
	pid.Ki = Ki;
	pid.Kd = Kd;
}
  
float pid_process()	// 简易位移式pid
{	 	
    pid.SetSpeed=INPUT;
    pid.err=pid.SetSpeed-pid.ActualSpeed;
    pid.integral+=pid.err;
    pid.voltage=pid.Kp*pid.err+pid.Ki*pid.integral+pid.Kd*(pid.err-pid.err_last);
    pid.err_last=pid.err;
    pid.ActualSpeed=pid.voltage*1.0;
    return pid.ActualSpeed;
}


Encoder.c

       利用TIM_EncoderInterfaceConfig函数直接配置正交编码器,本来之前用的时外部中断,但是由于霍尔编码器旋转时产生的脉冲数非常多,用外部中断的话非常占用程序资源,所以直接利用ARM提供的库函数TIM_EncoderInterfaceConfig,利用硬件资源来代替。

#include "stm32f10x.h"|

extern u8 FLAG;
void Encoder_init(void)
{
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	TIM_InternalClockConfig(TIM3);
	
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
	TIM_TimeBaseInitStructure.TIM_Period = 65536 - 1;		//ARR
	TIM_TimeBaseInitStructure.TIM_Prescaler = 1 - 1;		//PSC
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
	TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure);
	
	TIM_ICInitTypeDef TIM_ICInitStructure;
	TIM_ICStructInit(&TIM_ICInitStructure);
	TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;
	TIM_ICInitStructure.TIM_ICFilter = 0xF;
	TIM_ICInit(TIM3, &TIM_ICInitStructure);
	
	TIM_ICStructInit(&TIM_ICInitStructure);
	TIM_ICInitStructure.TIM_Channel = TIM_Channel_2;
	TIM_ICInitStructure.TIM_ICFilter = 0xF;
	TIM_ICInit(TIM3, &TIM_ICInitStructure);
	
	TIM_EncoderInterfaceConfig(TIM3,TIM_EncoderMode_TI12,TIM_ICPolarity_Rising,TIM_ICPolarity_Rising);
	
	TIM_Cmd(TIM3,ENABLE);
}

int16_t Encoder_Get(void)
{
	int16_t Temp;
	if(FLAG == 1){
		Temp = TIM_GetCounter(TIM3);
	}
	
	if(FLAG == 2){
		Temp = (uint16_t)65535 - TIM_GetCounter(TIM3);
		if(TIM_GetCounter(TIM3) == 0){
			Temp = 0;
		}
	}
	
	if(FLAG == 0){
		Temp = 0;
	}
	//Temp = TIM_GetCounter(TIM3);
	TIM_SetCounter(TIM3,0);
	return Temp;
}

void TIM_EncoderInterfaceConfig(TIM_TypeDef* TIMx, uint16_t TIM_EncoderMode,
                                uint16_t TIM_IC1Polarity, uint16_t TIM_IC2Polarity)

这个函数是用来配置正交编码器的,可以直接代替外部中断。

第一个参数是所使用的定时器;

第二个参数是编码器模式,参数可以是TIM_EncoderMode_TI1、TIM_EncoderMode_TI2、TIM_EncoderMode_TI12,这个参数的代表在哪个边沿计数还是双边计数;

后两个参数是反相和不反相的功能;

main.c 

        在定时器中断里获取电机当前速度,通过pid接口函数算出当前应有的实际速度,然后通过PWM调速改变电机速度。

#include "stm32f10x.h"                  // Device header
#include "timer.h"
#include "led.h"
#include "OLED.h"
#include "l298n.h"
#include "key.h"
#include "pid.h"
#include "pwm.h"
#include "Delay.h"
#include "Encoder.h"
#include "Serial.h"
#include "stdio.h"

extern int32_t INPUT;

extern struct pidstruct{
    float SetSpeed;            	//定义设定值
    float ActualSpeed;        	//定义实际值
    float err;                	//定义偏差值
    float err_last;            	//定义上一个偏差值
    float Kp,Ki,Kd;            	//定义比例、积分、微分系数
    float voltage;          	//定义电压值(控制执行器的变量)
    float integral;            	//定义积分值
}pid;

int16_t speed = 0;

int main(void)
{
	pid_init();
	Key_Init();
	LED_Init();
	uart_init();
	Encoder_init();
	Timer_Init();
	OLED_Init();
	L298N_Init();
	PWM_Init();
	
	OLED_ShowString(1, 1, "TargetSpeed:");
	OLED_ShowString(2, 1, "ActualSpeed:");
	
	while(1)
	{
		OLED_ShowNum(1, 13, INPUT, 4);
		OLED_ShowNum(2, 13, speed, 4);
		
		printf("T:%d A:%d",INPUT,speed);
		
		
		if(Receive_Flag == 1)						//接收数据标志位等于1(接收完毕,停止接收)
			Receive_Flag = 0;						//接收数据标志位置0(可以开始接收)		
	}
}

void TIM4_IRQHandler (void)
{	
	static uint32_t i=0;
	float tempx = 0;
	
	if(TIM_GetITStatus(TIM4, TIM_IT_Update)==SET)
	{
		i++;
		if(i>100){
			LED = !LED;
			i=0;
		}	
		speed = Encoder_Get();
		pid.ActualSpeed = speed;			
		tempx = pid_process();
		if(tempx>100)
			tempx=100;
		if(tempx<=0)
			tempx=0;
		PWM_SetCompare1(tempx*10);
		
	}
	TIM_ClearITPendingBit(TIM4, TIM_IT_Update);
}
speed = Encoder_Get();
pid.ActualSpeed = speed;

这两句代码通过 TIM_EncoderInterfaceConfig 函数获取的当前电机旋转的实际速度。

tempx = pid_process();
PWM_SetCompare1(tempx*10);

这两句代码是将获取的实际速度传给pid接口函数,用tempx来接受通过pid算出来的值,然后将算出的速度,用PWM输出给ENA使能端以驱动电机改变电机旋转速度。

       这是本人的第一篇博客,写的不好的地方大家可以提出来交流讨论,本人也在学习阶段,如果需要程序源码和QT接口的话之后可以上传。stm32程序在这,需要自取。如果觉得这篇文章对你有用请点赞评论一波谢谢。文章来源地址https://www.toymoban.com/news/detail-407832.html

到了这里,关于QT上位机控制stm32,并利用PID控制编码电机旋转的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【STM32】使用HAL库进行电机速度环PID控制,代码+调参

    主控:STM32F103C8T6 在进行速度控制之前,我们首先需要进行速度采样,这里参见这篇博文 ​ 这里不细说电机驱动模块的选型和使用,而是说一个常见的误区。我们驱动电机要使用两路PWM,一般是一路给PWM信号,一路是纯低电平。但这其实是不好的,正确的做法是一路给PWM,另

    2023年04月20日
    浏览(50)
  • 【STM32】使用HAL库进行电机PID位置环控制,代码+调参

    前面两篇博文已经实现了电机测速和PID速度环控制,在这篇博文中,我们主要说明位置环的代码怎么写以及PID参数怎么调。 ​ 写完速度环后位置环就很简单了。 ​ 在串级PID中,内环的控制量一般是外环控制量的微分。在我们这里,外环是控制量是电机转动的位置(也可以说

    2024年02月16日
    浏览(43)
  • STM32CubeMX 直流电机串级PID位置速度控制、HAL库、cubemx、PID、串级PID、位置控制、速度控制、双环控制

    提示:本文章的串级PID位置速度控制,是在前两篇文章速度控制,位置控制的基础上实现的,这一章节中不需要额外的cubemx的配置,只需要写简单的代码即可,复杂的地方在于串级pid的调试过程。 pid是我们在学习单片机中首先要学会的控制算法,而串级pid又是在单pid的基础上

    2024年02月14日
    浏览(51)
  • STM32 匿名助手提升电机控制调试效率(下位机代码和上位机操作演示)

    目录 前言 通信协议选择 上位机配置实现 下位机代码实现 通信效果演示 总结 前面介绍了通过VOFA上传变量并显示成波形方便调试的方法,VOFA上传的是浮点,一个浮点需要4个byte才能够表示,这对本就不是很快的串口带来说有不小负担,而且对于定点的MCU上传数据就不友好了

    2024年02月03日
    浏览(86)
  • 【STM32F4系列】【HAL库】电机控制(转速和角度)(PID实战1)

    实现电机最常使用的两个功能, 转速控制 和 位置控制 使用PID闭环控制(控制线性系统最简单快捷的控制方法) 为了实现 控制电机转动 和 闭环控制 需要: 电机(废话) 编码器( 霍尔编码器 或者 光电编码器 均可) 电机驱动(这里选的是 l298n模块 ) 千万注意黑色的地线,单片机的地要

    2024年02月02日
    浏览(52)
  • STM32 HAL库PID控制电机 第二章 TB6612FNG芯片驱动GB37-520电机

    1 电路图 2 TB6612简介 TB6612是双驱动,可同时驱动两个电机 STBY:接单片机的IO口清零电机全部停止,置1通过AIN1 AIN2,BIN1,BIN2 来控制正反转 VM:建议接10V以内电源( 瞬间上电12V可能会有尖峰电压击穿器件 ) VCC:接5V电源 GND:接电源负极 PWMA:接单片机的PWM口 ,控制转速 PWM

    2023年04月22日
    浏览(92)
  • 【STM32】步进电机及其驱动(ULN2003驱动28BYJ-48丨按键控制电机旋转)

    参考文章与课程:   【视频课程】步进电机基础原理和应用——程子华主讲   【视频课程】电机系列教学视频(基于STM32硬件)——野火   【霄耀在努力】STM32驱动步进电机(原理、程序、解决电机只震动不转动问题)   步进控制系统由以下三个部分组成: 控制器

    2023年04月09日
    浏览(46)
  • STM32利用USB的HID与QT上位机通信

      之前使用kingst的逻辑分析仪,打开上位机软件,插上带usb的硬件就可以通信,也不需要打开串口什么的,感觉很方便,于是借用一个周末研究下这个技术。本文主要是用于记录自己学习的过程,顺便分享下学习感悟。 首先初略说下大体是怎么回事,就是单片机使用hid功能需

    2024年02月07日
    浏览(43)
  • AS5600步进电机编码器(原理图+pcb+stm32控制代码)

    AS5600是一个易于编程的磁性旋转位置传感器,具有高分辨率的12位模拟或PWM输出。这种非接触式系统测量一个直径磁化的轴上磁铁的绝对角度。 引脚如下图 他有两种供电模式:5V和3.3V 我们为了和stm32F103C8T6单片机的电压一致,也使用3.3V供电,然后开始画PCB。 使用嘉立创EDA画

    2024年02月03日
    浏览(52)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包