当前位置: 首页 > news >正文

小知识点三、无刷电机闭环控制

0 前言

该部分只用于自学使用,作为笔记方便后续自查。
资料参考:http://dengfoc.com

硬件:2208云台电机+MT6701磁编码器

1 闭环位置控制理论

实际上这里的闭环控制相比与之前最大的区别就是机械角度计算这里,回忆一下上一节中我们是如何计算机械角度的,即 速度 × T s 速度\times Ts 速度×Ts。当我们有了磁编码器之后就可以通过磁编码器获取机械角度。

我们还是跟上一节一样从结果往回倒推,首先盘点一下我们现在已知的量:目标位置: t a r g e t a n g l e target_{angle} targetangle、实时机械角度: θ \theta θ、无刷电机供电电压 12 V 12V 12V

这里注意:由于电压没办法输出负值所以我们调整将 [ − 6 , 6 ] [-6,6] [6,6]上的变换映射到了 [ 0 , 12 ] [0,12] [0,12]
在这里插入图片描述
那么 U a l p h a Ualpha Ualpha的范围是 [ − 6 , 6 ] [-6,6] [6,6] U a U_a Ua的范围是 [ 0 , 12 ] [0,12] [0,12]。回忆一下 U a l p h a Ualpha Ualpha是从哪里来的, U a l p h a = − s i n ( θ ) ∗ U q Ualpha=-sin(\theta)*U_q Ualpha=sin(θ)Uq s i n sin sin三角函数的取值范围为 [ − 1 , 1 ] [-1,1] [1,1],那么实际上 U q U_q Uq的取值范围也是 [ − 6 , 6 ] [-6,6] [6,6],即 U m a x q = ± 6 U_{maxq}=\pm6 Umaxq=±6

那么假设说我们现在希望让电机转45度时的力矩最大(即达到6,那实际上 6 = k p ∗ 45 6=k_p * 45 6=kp45),则 K p = ∣ U m a x q ∣ / ∣ ± 45 ∣ = 6 / 45 = 0.133 K_p=|U_{maxq}|/|\pm45|=6/45=0.133 Kp=Umaxq∣/∣±45∣=6/45=0.133

那么转45度时: U q = 0.133 ∗ 45 = 5.985 v U_q=0.133*45=5.985v Uq=0.13345=5.985v
转60度时: U q = 0.133 ∗ 60 = 7.98 > 6 U_q=0.133*60=7.98>6 Uq=0.13360=7.98>6所以 U q = 6 U_q=6 Uq=6
转30度时: U q = 0.133 ∗ 30 = 3.99 U_q=0.133*30=3.99 Uq=0.13330=3.99

分析上述内容,我们会发现 K p K_p Kp的值实际上跟电机的供电电压和什么角度时力矩最大相关,实际上我们让上面规定的45度越小, K p K_p Kp就会越大,理论上到达目标位置就会越快。

step1: 那么已知 目标位置: t a r g e t a n g l e target_{angle} targetangle、实时机械角度: θ \theta θ、无刷电机供电电压 12 V 12V 12V,求 U q U_q Uq及电角度。
U q = k p ∗ ( t a r g e t a n g l e − θ ) U_q=k_p*(target_{angle}-\theta) Uq=kp(targetangleθ)
电角度 = 极对数 ∗ ( θ − 初始电角度 ) 电角度=极对数*(\theta-初始电角度) 电角度=极对数(θ初始电角度)

step2: 根据计算出的 U q U_q Uq和电角度,通过克拉克逆变化和帕克变换映射到 U a 、 U b 、 U c U_a、U_b、U_c UaUbUc。由于前两节都有叙述,此处不再重复

step3: 根据 U a 、 U b 、 U c U_a、U_b、U_c UaUbUc通过引脚的PWM信号模拟电压控制转子转动。

2 闭环位置控制代码

#include <SPI.h>
#include "MT6701.h"
#include <Arduino.h>  /*** 这部分根据开环速度控制课程部分整理(以及注意实际上这个控制器完全可以由单片机代替)* 主要看实现方式以及异同* 需要注意哪里的修改*///PWM输出引脚定义
int pwmA = 4;
int pwmB = 2;
int pwmC = 13;//初始变量及函数定义
#define _constrain(amt,low,high) ((amt)<(low)?(low):((amt)>(high)?(high):(amt)))
//宏定义实现的一个约束函数,用于限制一个值的范围。
//具体来说,该宏定义的名称为 _constrain,接受三个参数 amt、low 和 high,分别表示要限制的值、最小值和最大值。该宏定义的实现使用了三元运算符,根据 amt 是否小于 low 或大于 high,返回其中的最大或最小值,或者返回原值。
//换句话说,如果 amt 小于 low,则返回 low;如果 amt 大于 high,则返回 high;否则返回 amt。这样,_constrain(amt, low, high) 就会将 amt 约束在 [low, high] 的范围内。float voltage_power_supply=12.6; //设定板子供电电压
float shaft_angle=0,open_loop_timestamp=0;
float zero_electric_angle=0,Ualpha,Ubeta=0,Ua=0,Ub=0,Uc=0,dc_a=0,dc_b=0,dc_c=0;// 初始化代码放在这里
void setup() {// put your setup code here, to run once:// 加载一个串口,方便测试// 串口初始化定义Serial.begin(115200);BeginSensor();//PWM设置,设置输出的引脚口:即4,2,13是输出的引脚pinMode(pwmA, OUTPUT);pinMode(pwmB, OUTPUT);pinMode(pwmC, OUTPUT);//配置引脚PWM的输出模式//ledcAttachPin(pwmA, 0);ledcAttachPin(pwmB, 1);ledcAttachPin(pwmC, 2);// 频率30000,是只PWM周期1/30000秒(周期非常快),输出精度是8位(根据每个周期的占空比进行计算)ledcSetup(0, 30000, 8);  //pwm频道, 频率, 精度ledcSetup(1, 30000, 8);  //pwm频道, 频率, 精度ledcSetup(2, 30000, 8);  //pwm频道, 频率, 精度Serial.println("完成PWM初始化设置");delay(3000);//等待3秒pinMode(25, OUTPUT);     //使能引脚设置高电平
}// 归一化角度到 [0,2PI] 其实是将角度限制在[0,2PI]
float _normalizeAngle(float angle){float a = fmod(angle, 2*PI);   //取余运算可以用于归一化,列出特殊值例子算便知return a >= 0 ? a : (a + 2*PI);  
}#define _3PI_2 4.71238898038f
// 计算电角度
int PP=7,DIR=-1;
float _electricalAngle(){return _normalizeAngle((float)(DIR *  PP) * readAngle()-zero_electric_angle);
}// 设置PWM到控制器输出
//Ua Ub Uc是已经算出来的结果
void setPwm(float Ua, float Ub, float Uc) {// 本来的函数中还有一个Ua Ub Uc的上限设置,这里没有// 计算占空比// 限制占空比从0到1// 实际上相当于想用PWM来表示连续电压,以整个周期看作1的话,那么占空比实际上就是实际电压/最大能达到的电压dc_a = _constrain(Ua / voltage_power_supply, 0.0f , 1.0f );dc_b = _constrain(Ub / voltage_power_supply, 0.0f , 1.0f );dc_c = _constrain(Uc / voltage_power_supply, 0.0f , 1.0f );//写入PWM到PWM 0 1 2 通道// Arduino 的 ledcWrite() 函数使用 8 位分辨率(默认)// 8 位 = 2⁸ = 256 个可能值(0 到 255)// 0 表示 0% 占空比(始终低电平)// 255 表示 100% 占空比(始终高电平)ledcWrite(0, dc_a*255);ledcWrite(1, dc_b*255);ledcWrite(2, dc_c*255);
}// 这个函数就是进行克拉克变换和帕克变换(Uq实际上就是力矩的大小)
void setPhaseVoltage(float Uq,float Ud, float angle_el) {angle_el = _normalizeAngle(angle_el + zero_electric_angle);// 帕克逆变换// 这里注意实际上只用的Uq,没有用UdUalpha =  -Uq*sin(angle_el); Ubeta =   Uq*cos(angle_el); // 克拉克逆变换Ua = Ualpha + voltage_power_supply/2;Ub = (sqrt(3)*Ubeta-Ualpha)/2 + voltage_power_supply/2;Uc = (-Ualpha-sqrt(3)*Ubeta)/2 + voltage_power_supply/2;setPwm(Ua,Ub,Uc);
}//==============串口接收==============
float motor_target;
int commaPosition;
String serialReceiveUserCommand() {static String received_chars;String command = "";while (Serial.available()) {char inChar = (char)Serial.read();received_chars += inChar;if (inChar == '\n') {command = received_chars;commaPosition = command.indexOf('\n');//检测字符串中的逗号if(commaPosition != -1)//如果有逗号存在就向下执行{motor_target = command.substring(0,commaPosition).toDouble();            //电机角度Serial.println(motor_target);}received_chars = "";}}return command;
}void loop() {digitalWrite(25, HIGH);float Sensor_Angle=readAngle_still();float Kp=0.133;setPhaseVoltage(_constrain(Kp*(motor_target-DIR*Sensor_Angle)*180/PI,-6,6),0,_electricalAngle());serialReceiveUserCommand();
}

MT6701.h文件及所有相关代码,将会在无刷电机部分整体更新结束后整理并开源到github~

相关文章:

  • 静态指令和动态指令的区别 GPT版
  • qt信号与槽--01
  • 如何设置爬虫的访问频率?
  • Hadoop 003 — JAVA操作MapReduce入门案例
  • React Native 项目实战 —— 记账本应用开发指南
  • 龙虎榜——20250613
  • 对象存储数据一致性:S3 vs Azure Blob vs GCS对比解析 (2025)
  • 前端持续集成和持续部署简介
  • 当雷达学会“读心术” 汽车舱内安全迈入新纪元
  • PyTorch框架详解(1)
  • html+css+js趣味小游戏~(附源码)
  • Java过滤器的基本概念
  • 【PDF】常见纸张字体大小设置指南 / Common Paper Size Font Guidelines
  • 开源组件hive调优
  • 论文略读:Do Large Language Models Truly Understand Geometric Structures?
  • 产品推荐|一款具有单光子级探测能力的科学相机千眼狼Gloria 1605
  • python 爬虫,爬取某乎某个用户的全部内容 + 写个阅读 app,慢慢读。
  • PostgreSQL作为向量数据库
  • 高效账号信息管理工具,可安全随机生成密码
  • SQL力扣
  • 广州有做网站的公司吗/益阳网络推广
  • 河南seo和网络推广/seo优化教学视频
  • 广州网站建设团队/百度收录的网页数量
  • 网站建设 济南/网站权重怎么看
  • 付费网站 源码 下载链接/制作网站需要什么软件
  • 石家庄做网络推广的网站/阿里巴巴官网首页