用Arduino实现霍尔编码减速电机PI调速(增量式)
这个调速算法是用在搬运机器人上的,一方面之前没有用过PID算法,另一方面在Arduino上实现的资料也比较少,所以我想把自己的尝试过程记录下来,如果能帮到别人再好不过了。
1.硬件线路连接
这里我使用的是用到了两个减速比为30的霍尔编码器减速电机,电机驱动为AQMH2407ND,主控采用Arduino Mega 2560.
这是程序中的端口设置,可以看出硬件线路连接情况。
//左电机端口定义
#define MotorLpin1 35 //控制位3 这是电机驱动板上的,用来控制正,反转和停止的。
#define MotorLpin2 37 //控制位4 这是电机驱动板上的
#define MotorLpwm 3 //使能调速 ENB 这是电机驱动板上的PWM调速接口
#define MotorLcountA 18 //编码器A 中断号:5
#define MotorLcountB 19 //编码器B 中断号:4//右电机端口定义
#define MotorRpin1 31 //控制位1 这是电机驱动板上的
#define MotorRpin2 33 //控制位2 这是电机驱动板上的
#define MotorRpwm 2 //使能调速 ENA 这是电机驱动板上的PWM调速接口
#define MotorRcountA 20 //编码器A 中断号:3
#define MotorRcountB 21 //编码器B 中断号:2
2.PI增量调速算法
PID算法有位置式和增量式两种,位置式:考虑从开机到现在所有的偏差,增量式:考虑最近偏差。如果对PID算法不了解,可以看一下这个老师的视频:https://www.bilibili.com/video/BV14E411L7GB/
因为这个小车是要多次启动停止的,再加上很多电机调速算法用的都是增量式,所以我采用的也是增量式。并且在实际的调参过程中,PI的效果是比PID要好的,所以综合下来采用的就是增量式PI调速。
3.增量式调速算法的C语言表达
这个我参考的式平衡小车之家的代码,这里用的是-=,不是+=。因为用-=,调试出来的PI参数是负数:
/********************************************************** 函数功能:增量式PI控制器(左轮)* 入口参数:当前速度(编码器测量值),目标速度* 返回 值:电机PWM * 参考资料: * 增量式离散PID公式:* Pwm-=Kp*[e(k)-e(k-1)]+Ki*e(k)+Kd*[e(k)-2e(k-1)+e(k-2)]* e(k):本次偏差* e(k-1):上一次偏差* e(k-2):上上次偏差* Pwm:代表增量输出* 在速度闭环控制系统里面我们只使用PI控制,因此对PID公式可简化为:* Pwm-=Kp*[e(k)-e(k-1)]+Ki*e(k)* e(k):本次偏差* e(k-1):上一次偏差* Pwm:代表增量输出* * 注意增量式PID先调I,再调P,最后再调D*********************************************************/int Incremental_Pi_L(int current_speed,int target_speed){static float pwm,bias,last_bias,prev_bias; //静态变量存在程序全周期:pwm:增量输出,bias:本次偏差,last_bias:上次偏差,prev_bais_:上上次偏差bias=current_speed-target_speed; //计算本次偏差e(k)pwm-=(kp*(bias-last_bias)+ki*bias+kd*(bias-2*last_bias+prev_bias)); //增量式PID控制器prev_bias=last_bias; //保存上上次偏差last_bias=bias; //保存上一次偏差//PWM 限幅度 Arduino的PWM 最高为255 限制在250if(pwm<-250){pwm=250; }if(pwm>250){pwm=250; }//Serial.println(pwm);return pwm; //增量输出}
4.霍尔编码器的脉冲检测
这里我只用到了每个轮子的A相,线数是13,电机减速比为30,那么输出轴转动一周,A相编码器会输出390脉冲。这里用来测轮子的速度完全够用了。用Arduino的外部中断读取编码器脉冲,
/************************************ 电机实际速度计算:* 公式:* 已知参数:* 车轮直径65mm,* 左边轮子一圈:360脉冲(RISING),* 右边轮子一圈:390脉冲(RISING),* 单位时间读两个轮子脉冲读取两个轮子脉冲* 已知道参数:* 车轮直径65mm,* 左边轮子一圈:360脉冲(RISING),* 右边轮子一圈:390脉冲(RISING),* * 外部中断触发模式:** LOW:低电平触发;* CHANGE:电平变化触发;* RISING :上升沿触发(由LOW变为HIGH);* FALLING:下降沿触发(由HIGH变为LOW); * HIGH:高电平触发(该中断模式仅适用于Arduino due);* ************************************/void Read_Moto_V(){unsigned long nowtime=0;motorL=0;motorR=0;nowtime=millis()+50;//读50毫秒attachInterrupt(digitalPinToInterrupt(MotorLcountA),Read_Moto_L,RISING);//左轮脉冲开中断计数attachInterrupt(digitalPinToInterrupt(MotorRcountA),Read_Moto_R,RISING);//右轮脉冲开中断计数while(millis()<nowtime); //达到50毫秒关闭中断detachInterrupt(digitalPinToInterrupt(MotorLcountA));//左轮脉冲关中断计数detachInterrupt(digitalPinToInterrupt(MotorRcountA));//右轮脉冲关中断计数V_L=((motorL/390)*6.5*PI)/0.05; //单位cm/sV_R=((motorR/390)*6.5*PI)/0.05; //单位cm/sv1=V_L;v2=V_R;
}/**************************** 中断函数:读左轮脉冲***************************/
void Read_Moto_L(){motorL++;
}/*************************** 中断函数:读右轮脉冲* *************************/
void Read_Moto_R(){motorR++;
}
5.PID参数调试整体程序
这个调试过程我参考的是这篇文章:https://blog.csdn.net/C1664510416/article/details/107229958,PID算法的代码是现成的,改改挪到自己的程序中就能用了,但是要考虑到怎样嵌入到自己的程序中,还有就是参数应该怎么调节,如果是第一次用PID,可能你在把PID代码挪到自己程序中的时候,还是一头雾水,但是一旦你调完了参数,就会豁然开朗了。
曲线绘制我直接用的Arduino自带的串口绘图器,主控将实时速度通过Serial.println(V_L),传到电脑,打开串口绘图器就可以直接看到曲线。
我这里比较麻烦,就是不断的修改PID参数,然后开启电源让主控控制电机转动,打印数据。
增量式PID的调试与位置式不同,要先调I,再调P,再调D,最后再统一微调。
//左电机端口定义
#define MotorLpin1 35 //控制位3
#define MotorLpin2 37 //控制位4
#define MotorLpwm 3 //使能调速 ENB
#define MotorLcountA 18 //编码器A 中断号:5
#define MotorLcountB 19 //编码器B 中断号:4//右电机端口定义
#define MotorRpin1 31 //控制位1
#define MotorRpin2 33 //控制位2
#define MotorRpwm 2 //使能调速 ENA
#define MotorRcountA 20 //编码器A 中断号:3
#define MotorRcountB 21 //编码器B 中断号:2volatile float motorL=0;//中断变量,左轮子脉冲计数
volatile float motorR=0;//中断变量,右轮子脉冲计数
float V_L=0; //左轮速度 单位cm/s
float V_R=0; //右边轮速 单位cm/s
int v1=0; //单位cm/s
int v2=0; //单位cm/s
float Target_V_L=20,Target_V_R=20; //单位cm/s
int Pwm_L=0,Pwm_R=0; //左右轮PWM//PID变量
float kp=0,ki=0,kd=0; //PID参数/*************************************** Arduino初始化函数* *************************************/
void setup() {Motor_Init();//电机端口初始化Serial.begin(9600);//开启串口
}/********************************************************** 函数功能:增量式PI控制器(左轮)* 入口参数:当前速度(编码器测量值),目标速度* 返回 值:电机PWM * 参考资料: * 增量式离散PID公式:* Pwm-=Kp*[e(k)-e(k-1)]+Ki*e(k)+Kd*[e(k)-2e(k-1)+e(k-2)]* e(k):本次偏差* e(k-1):上一次偏差* e(k-2):上上次偏差* Pwm:代表增量输出* 在速度闭环控制系统里面我们只使用PI控制,因此对PID公式可简化为:* Pwm-=Kp*[e(k)-e(k-1)]+Ki*e(k)* e(k):本次偏差* e(k-1):上一次偏差* Pwm:代表增量输出* * 注意增量式PID先调I,再调P,最后再调D*********************************************************/int Incremental_Pi_L(int current_speed,int target_speed){static float pwm,bias,last_bias,prev_bias; //静态变量存在程序全周期:pwm:增量输出,bias:本次偏差,last_bias:上次偏差,prev_bais_:上上次偏差bias=current_speed-target_speed; //计算本次偏差e(k)pwm-=(kp*(bias-last_bias)+ki*bias+kd*(bias-2*last_bias+prev_bias)); //增量式PID控制器prev_bias=last_bias; //保存上上次偏差last_bias=bias; //保存上一次偏差//PWM 限幅度 Arduino的PWM 最高为255 限制在250if(pwm<-250){pwm=250; }if(pwm>250){pwm=250; }//Serial.println(pwm);return pwm; //增量输出}//右轮速度增量式PID控制器
int Incremental_Pi_R(float current_speed,float target_speed){static float pwm,bias,last_bias,prev_bias; //静态变量存在程序全周期:pwm:增量输出,bias:本次偏差,last_bias:上次偏差,prev_bais_:上上次偏差bias=current_speed-target_speed; //计算本次偏差e(k)pwm-=(kp*(bias-last_bias)+ki*bias+kd*(bias-2*last_bias+prev_bias)); //增量式PID控制器prev_bias=last_bias; //保存上上次偏差last_bias=bias; //保存上一次偏差//PWM 限幅度 Arduino的PWM 最高为255限制在250if(pwm<-250){pwm=250; }if(pwm>250){pwm=250; }//Serial.println(pwm);return pwm; //增量输出}/**************************************************************************(测试完成)函数功能:设置双轮工作模式和运动速度入口参数:工作模式,左右轮pwm返回 值:无
**************************************************************************/
void Set_Pwm(int mode,int speed_L,int speed_R){if(mode==1){//前进模式//左电机digitalWrite(MotorLpin1,LOW);digitalWrite(MotorLpin2,HIGH);analogWrite(MotorLpwm,speed_L);//右电机digitalWrite(MotorRpin1,HIGH);digitalWrite(MotorRpin2,LOW);analogWrite(MotorRpwm,speed_R);}else if(mode==2){//后退模式//左电机digitalWrite(MotorLpin1,HIGH);digitalWrite(MotorLpin2,LOW);analogWrite(MotorLpwm,speed_L);//右电机digitalWrite(MotorRpin1,LOW);digitalWrite(MotorRpin2,HIGH);analogWrite(MotorRpwm,speed_R);}else if(mode==3){//左转模式//左电机digitalWrite(MotorLpin1,HIGH);digitalWrite(MotorLpin2,LOW);analogWrite(MotorLpwm,speed_L);//右电机digitalWrite(MotorRpin1,HIGH);digitalWrite(MotorRpin2,LOW);analogWrite(MotorRpwm,speed_R);}else if(mode==4){//右转模式//左电机digitalWrite(MotorLpin1,LOW);digitalWrite(MotorLpin2,HIGH);analogWrite(MotorLpwm,speed_L);//右电机digitalWrite(MotorRpin1,LOW);digitalWrite(MotorRpin2,HIGH);analogWrite(MotorRpwm,speed_R);}
}/**************************************************************************(测试完成)函数功能:电机端口初始化,控制芯片引脚拉低入口参数:无返回 值:无
**************************************************************************/
void Motor_Init(){//左电机pinMode(MotorLpin1,OUTPUT); //驱动芯片控制引脚pinMode(MotorLpin2,OUTPUT); //驱动芯片控制引脚pinMode(MotorLpwm,OUTPUT); //驱动芯片控制引脚,PWM调速pinMode(MotorLcountA,INPUT); //左轮编码器A引脚pinMode(MotorLcountB,INPUT); //左轮编码器B引脚//右电机pinMode(MotorRpin1,OUTPUT); //驱动芯片控制引脚pinMode(MotorRpin2,OUTPUT); //驱动芯片控制引脚pinMode(MotorRpwm,OUTPUT); //驱动芯片控制引脚,PWM调速pinMode(MotorRcountA,INPUT); //右轮编码器A引脚pinMode(MotorRcountB,INPUT); //右轮编码器B引脚//驱动芯片控制引脚全部拉低digitalWrite(MotorLpin1,LOW); //左电机digitalWrite(MotorLpin2,LOW);digitalWrite(MotorLpwm,LOW);digitalWrite(MotorRpin1,LOW); //右电机digitalWrite(MotorRpin2,LOW);digitalWrite(MotorRpwm,LOW);
}/************************************ 电机实际速度计算:* 公式:* 已知参数:* 车轮直径65mm,* 左边轮子一圈:390脉冲(RISING),* 右边轮子一圈:390脉冲(RISING),* 单位时间读两个轮子脉冲读取两个轮子脉冲***********************************/void Read_Moto_V(){unsigned long nowtime=0;motorL=0;motorR=0;nowtime=millis()+50;//读50毫秒attachInterrupt(digitalPinToInterrupt(MotorLcountA),Read_Moto_L,RISING);//左轮脉冲开中断计数attachInterrupt(digitalPinToInterrupt(MotorRcountA),Read_Moto_R,RISING);//右轮脉冲开中断计数while(millis()<nowtime); //达到50毫秒关闭中断detachInterrupt(digitalPinToInterrupt(MotorLcountA));//左轮脉冲关中断计数detachInterrupt(digitalPinToInterrupt(MotorRcountA));//右轮脉冲关中断计数V_L=((motorL/390)*6.5*PI)/0.05; //单位cm/sV_R=((motorR/390)*6.5*PI)/0.05; //单位cm/sv1=V_L;v2=V_R;
}/**************************** 中断函数:读左轮脉冲***************************/
void Read_Moto_L(){motorL++;
}/*************************** 中断函数:读右轮脉冲* *************************/
void Read_Moto_R(){motorR++;
}/**************************************** Arduino主循环* ***************************************/
void loop() {Read_Moto_V();//读取脉冲计算速度Pwm_L=Incremental_Pi_L(V_L,Target_V_L);//左轮PI运算Pwm_R=Incremental_Pi_R(V_R,Target_V_R);//右轮PI运算Serial.println(V_L); //直接用串口绘图画出速度曲线Set_Pwm(1,Pwm_L,Pwm_R); //设置左右轮速度
}
6.调参过程
参考:https://blog.csdn.net/C1664510416/article/details/107229958
7.最终结果
kp=1.6,ki=0.8,kd=0;
用Arduino实现霍尔编码减速电机PI调速(增量式)相关推荐
- 学习方波有霍尔传感的电机调速记录
学习报告2020-9-30 学习报告: 第4期 时间周期: 9月1日-9月30日 姓名.专业:闫春幸- 电子信息 一.内容 1.首先继续对带霍尔传感器的BLDC方波调速的程序继续学习,对其中的原理进行 ...
- STM32——直流电机PI调速
所需元件 STM32F103开发板.L298N一个.带编码器的直流电机一个(如下图所示,淘宝上有很多) 系统框图 通过系统框图,我们需要做两件事,一是要测速,二是要调节.测速目前流行的就是通过 ...
- 电机调速制动matlab,鼠笼式三相异步电机:起动、调速、制动(原理与Simulink仿真)...
3种起动方式: Y→∆换接起动.自耦变压器减压起动.定子回路串电抗起步: 3种调速方式: 电阻调速.电压调速.变频调速: 3种制动方式: 能耗制动.反接制动.变频器回馈制动. 目录 0 电机参数与负载 ...
- 鼠笼式三相异步电机 | 起动、调速、制动原理与Simulink仿真
博客搬家到自己搭建的 主页(wonghaotian.com) 啦q(≧▽≦q),大家快来逛逛鸭! 3种起动方式: Y→∆换接起动.自耦变压器减压起动.定子回路串电抗起步: 3种调速方式: 电阻调速.电 ...
- Arduino 让小车走直线的秘密 增量式PID 直流减速编码电机
直流减速电机增量式PID 通俗易懂版 对于我一个双非学校没上过自控的电子专业本科生而言,这个东西真的是太难了,之前研究了一个A4950驱动,价格便宜,驱动能力高,安全性高,便捷性高,一句话就是比L29 ...
- 基于MSP430f5529 编码电机测速 接收脉冲数 PWM调速 CCS编译器 代码分析
前言:2022年TI杯大学生电子设计竞赛,小车跟随行驶系统(C题)要求:设计一套小车跟随行驶系统,采用TI的MCU,由一辆领头小车和一辆跟随小车组成,要求小车具有循迹功能,且速度在0.3~1m/s可调 ...
- 基于STM32F103C6T6的AB相霍尔编码电机的PID转速调节(CubeMx-HAL库)(未完成-持续更新)
基于STM32F103C6T6的AB相霍尔编码电机的PID转速调节(CubeMx-HAL库)(未完成-持续更新) 主要是记录一下,以后忘了再来看看,也记录记录自己做过的东西 首先是硬件电路图,一下是驱 ...
- 学习带霍尔传感器的BLDC方波调速
带霍尔传感器的BLDC方波调速 基本原理 BLDC控制框图 三路霍尔信号图 120°Hall换相真值表 PID 调节控制电机转速 程序应用 按键读取 启动后调速 PWM换相调压 霍尔信号处理 基本原理 ...
- Arduino驱动L298N控制直流电机的正反转和调速
Arduino驱动L298N控制直流电机的正反转和调速 一.前言 二.产品参数 三.驱动直流电机 三.接线图 四.程序 五.实验结果 总结 一.前言 本模块使用ST公司的L298N作为主驱动芯片,具有 ...
最新文章
- module ‘tensorrt‘ has no attribute ‘BuilderFlag‘
- PostgreSQL 11 100亿 tpcb 性能测试 on ECS
- Keras之DNN:基于Keras(sigmoid+binary_crossentropy+predict_proba)利用DNN实现分类预测概率——DIY二分类数据集预测新数据点
- linux 读取内存颗粒,linux查看主板内存槽与内存信息的命令dmidecode怎么用
- PHP笔记-文件上传例子
- activity启动流程_以AMS视角看Activity启动过程
- 【Go语言】集合与文件操作
- plsql学习笔记---plsql相关概念,以及基础结构
- windows 64位sed.exe_32位,64位,x86,x64到底是什么关系?差距居然这么大
- WINCC AUDIT审计组建教程
- 遗传算法matlab_当结构设计遇到遗传算法应用ANSYS和MATLAB联合仿真优化设计探索(前传)...
- 第二章 01 节 常用信号及其基本特征
- PS制作(LOGO)步骤流程
- c语言课程设计家谱管理系统,数据结构-家谱管理系统
- spring-ant-处理zip
- 企业遇到什么问题一定要用360评估?
- 多态的表现形式有哪些?
- java如何获取复选框选中的值
- nginx端口映射配置(Windows)
- 网站申请服务器,网站申请服务器空间