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

【CubeMX-HAL库】STM32F407—无刷电机开环驱动

目录

一、开环速度原理

1.电压矢量形式的逆变换

2.FOC过程总结

二、开环控制代码编写

1.FOC代码规划

2.FOC.c

3.FOC.h 

4.演示效果


一、开环速度原理

1.电压矢量形式的逆变换

在上一节【CubeMX-HAL库】STM32F407—无刷电机基础知识中我们探讨的帕克逆变换、克拉克逆变换全是根据电流Iα、Iβ计算的。

而实际中我们使用单片机很难控制输出的电流大小,反而比较多的使用PWM控制电压大小,所以需要将上面的电流算法转为电压算法,把每相的电流矢量都乘上其相电阻R(相电阻就是每相线圈所带的电阻)。

想要控制电流的话只能在控制电压的基础上通过电流传感器进行闭环。

2.FOC过程总结

下面我们开始捋一下整个FOC开环代码的运行:

①首先我们需要FOC代码中的Uq和电角度θ输入到帕克逆变换计算Uα、Uβ。

②通过计算出的Uα、Uβ然后用克拉克逆变换计算无刷电机三相所需要的Ua、Ub、Uc。

二、开环控制代码编写

1.FOC代码规划

我们来推算一下FOC所需要的代码:

①Uq是我们可以自定义的。

②电角度θ不知道,我们需要自己编写一个角度生成器。

在这个float Uq = voltage_power_supply / 5;中,我将3改为了5调整了Uq的幅度,因为经实际测试中输出过大我的电源和电脑撑不主,芯片总是死机,降低之后我甚至不给MOS供电,只用电脑都能驱动无刷电机。

uint32_t micros;//芯片运行时基
float FOC_Open_Speed_Start(float target_velocity)//开环速度函数
{ 
    uint32_t now_us = micros;//获取从开启芯片以来的微秒数,它的精度是 4 微秒。micros() 返回的是一个无符号长整型(unsigned long)的值

    float Ts = (now_us - open_loop_timestamp) * 1e-6f;//计算当前每个Loop的运行时间间隔

    //由于 micros() 函数返回的时间戳会在大约 70 分钟之后重新开始计数,在由70分钟跳变到0时,TS会出现异常,因此需要进行修正。
    //如果时间间隔小于等于零或大于 0.5 秒,则将其设置为一个较小的默认值,即 1e-3f
    if (Ts <= 0 || Ts > 0.5f) Ts = 1e-3f;

    // 通过乘以时间间隔和目标速度来计算需要转动的机械角度,存储在 shaft_angle 变量中。
    //在此之前,还需要对轴角度进行归一化,以确保其值在 0 到 2π 之间。
    open_loop_shaft_angle = FOC_Conversion_Angle_0_2PI(open_loop_shaft_angle + target_velocity * Ts);
    //以目标速度为 10 rad/s 为例,如果时间间隔是 1 秒,则在每个循环中需要增加 10 * 1 = 10 弧度的角度变化量,才能使电机转动到目标速度。
    //如果时间间隔是 0.1 秒,那么在每个循环中需要增加的角度变化量就是 10 * 0.1 = 1 弧度,才能实现相同的目标速度。
    //因此,电机轴的转动角度取决于目标速度和时间间隔的乘积。
    
    // 使用早前设置的voltage_power_supply的1/3作为Uq值,这个值会直接影响输出力矩
    // 最大只能设置为Uq = voltage_power_supply/2,否则ua,ub,uc会超出供电电压限幅
    float Uq = voltage_power_supply / 5;

    FOC_Parker_Clark(Uq,FOC_ElectricalAngle(open_loop_shaft_angle, 7));

    open_loop_timestamp = now_us;  //用于计算下一个时间间隔

    return Uq;
}

③帕克逆变换、克拉克逆变换我们自己手写。

void FOC_Parker_Clark(float Uq,float angle_el)//FOC核心算法,克拉克逆变换/帕克逆变换
{
    //Ud等于零,用Uq控制
    angle_el = FOC_Conversion_Angle_0_2PI(angle_el);
    Ualpha = -Uq * sin(angle_el);//帕克逆变换
    Ubeta = Uq * cos(angle_el);

    Ua = Ualpha + voltage_power_supply / 2;//克拉克逆变换,+V/2整体将数值挪到正数
    Ub = (sqrt(3) * Ubeta - Ualpha) / 2 + voltage_power_supply / 2;
    Uc = (-Ualpha - sqrt(3) * Ubeta) / 2 + voltage_power_supply / 2;
    FOC_SetPWM(Ua, Ub, Uc);
}

④经过FOC算法之后我们需要将Ua、Ub、Uc传入无刷电机三相,也就是幅值给定时器PWM通道。

void FOC_SetPWM(float Ua,float Ub,float Uc)//设置PWM到控制器输出
{
    Ua = LIMIT(Ua, 0.0f, voltage_limit);//限制电压上限
    Ub = LIMIT(Ub, 0.0f, voltage_limit);
    Uc = LIMIT(Uc, 0.0f, voltage_limit);

    float dc_a = LIMIT(Ua / voltage_power_supply, 0.0f, 1.0f);//计算占空比
    float dc_b = LIMIT(Ub / voltage_power_supply, 0.0f, 1.0f);//限制占空比从0到1
    float dc_c = LIMIT(Uc / voltage_power_supply, 0.0f, 1.0f);

    TIM1->CCR1 = dc_a * 255.0f;//写入PWM到PWM 1 2 3通道
    TIM1->CCR2 = dc_b * 255.0f;
    TIM1->CCR3 = dc_c * 255.0f;
//    printf("%d,%d,%d\n",TIM1->CCR1,TIM1->CCR2,TIM1->CCR3);
}

⑤角度生成器范围是[0-2PI]递增,根据实际中的无刷电机是带极对数的,无刷电机旋转一圈会产生7个波动周期(极对数以7为例),真正的电角度为机械角度x极对数,也就是我们需要7个周期的FOC运算无刷电机才能转一圈,所以需要一个根据机械角度与极对数求电角度的函数。

float FOC_ElectricalAngle(float machine_angle,int pole_pairs_num)//电角度求解
{
    return (machine_angle * pole_pairs_num);//电角度=机械角度×极对数
}

⑥电角度=机械角度x极对数,但是[0-2PI]x7难免会超过电机的一圈2PI,所以我们要将电角度限幅到[0-2PI]之间,所以需要一个归一化函数。

float FOC_Conversion_Angle_0_2PI(float angle)//归一化角度到[0,2PI]
{
    float a = fmod(angle, 2 * PI);//取余运算可以用于归一化,列出特殊值例子算便知
    return a >= 0 ? a : (a + 2 * PI);
    //三目运算符。格式:condition ? expr1 : expr2
    //其中,condition 是要求值的条件表达式,如果条件成立,则返回 expr1 的值,否则返回 expr2 的值。可以将三目运算符视为 if-else 语句的简化形式。
    //fmod 函数的余数的符号与除数相同。因此,当 angle 的值为负数时,余数的符号将与 _2PI 的符号相反。也就是说,如果 angle 的值小于 0 且 _2PI 的值为正数,则 fmod(angle, _2PI) 的余数将为负数。
    //例如,当 angle 的值为 -PI/2,_2PI 的值为 2PI 时,fmod(angle, _2PI) 将返回一个负数。在这种情况下,可以通过将负数的余数加上 _2PI 来将角度归一化到 [0, 2PI] 的范围内,以确保角度的值始终为正数。
}

2.FOC.c

#include "FOC.h"

//初始变量及函数定义
float voltage_limit=10;//输出电压最大限幅
float voltage_power_supply = 12;//转化为电压比
float open_loop_shaft_angle = 0,open_loop_timestamp = 0;//机械角度,开环时间间隔
float zero_electric_angle = 0;//零电角度值
float Ualpha,Ubeta = 0;//二位坐标向量
float Ua = 0,Ub = 0,Uc = 0;//三相电压

float FOC_ElectricalAngle(float machine_angle,int pole_pairs_num)//电角度求解
{
    return (machine_angle * pole_pairs_num);//电角度=机械角度×极对数
}

float FOC_Conversion_Angle_0_2PI(float angle)//归一化角度到[0,2PI]
{
    float a = fmod(angle, 2 * PI);//取余运算可以用于归一化,列出特殊值例子算便知
    return a >= 0 ? a : (a + 2 * PI);
    //三目运算符。格式:condition ? expr1 : expr2
    //其中,condition 是要求值的条件表达式,如果条件成立,则返回 expr1 的值,否则返回 expr2 的值。可以将三目运算符视为 if-else 语句的简化形式。
    //fmod 函数的余数的符号与除数相同。因此,当 angle 的值为负数时,余数的符号将与 _2PI 的符号相反。也就是说,如果 angle 的值小于 0 且 _2PI 的值为正数,则 fmod(angle, _2PI) 的余数将为负数。
    //例如,当 angle 的值为 -PI/2,_2PI 的值为 2PI 时,fmod(angle, _2PI) 将返回一个负数。在这种情况下,可以通过将负数的余数加上 _2PI 来将角度归一化到 [0, 2PI] 的范围内,以确保角度的值始终为正数。
}

void FOC_SetPWM(float Ua,float Ub,float Uc)//设置PWM到控制器输出
{
    Ua = LIMIT(Ua, 0.0f, voltage_limit);//限制电压上限
    Ub = LIMIT(Ub, 0.0f, voltage_limit);
    Uc = LIMIT(Uc, 0.0f, voltage_limit);

    float dc_a = LIMIT(Ua / voltage_power_supply, 0.0f, 1.0f);//计算占空比
    float dc_b = LIMIT(Ub / voltage_power_supply, 0.0f, 1.0f);//限制占空比从0到1
    float dc_c = LIMIT(Uc / voltage_power_supply, 0.0f, 1.0f);

    TIM1->CCR1 = dc_a * 255.0f;//写入PWM到PWM 1 2 3通道
    TIM1->CCR2 = dc_b * 255.0f;
    TIM1->CCR3 = dc_c * 255.0f;
//    printf("%d,%d,%d\n",TIM1->CCR1,TIM1->CCR2,TIM1->CCR3);
}

void FOC_Parker_Clark(float Uq,float angle_el)//FOC核心算法,克拉克逆变换/帕克逆变换
{
    //Ud等于零,用Uq控制
    angle_el = FOC_Conversion_Angle_0_2PI(angle_el);
    Ualpha = -Uq * sin(angle_el);//帕克逆变换
    Ubeta = Uq * cos(angle_el);

    Ua = Ualpha + voltage_power_supply / 2;//克拉克逆变换,+V/2整体将数值挪到正数
    Ub = (sqrt(3) * Ubeta - Ualpha) / 2 + voltage_power_supply / 2;
    Uc = (-Ualpha - sqrt(3) * Ubeta) / 2 + voltage_power_supply / 2;
    FOC_SetPWM(Ua, Ub, Uc);
}

uint32_t micros;//芯片运行时基
float FOC_Open_Speed_Start(float target_velocity)//开环速度函数
{ 
    uint32_t now_us = micros;//获取从开启芯片以来的微秒数,它的精度是 4 微秒。micros() 返回的是一个无符号长整型(unsigned long)的值

    float Ts = (now_us - open_loop_timestamp) * 1e-6f;//计算当前每个Loop的运行时间间隔

    //由于 micros() 函数返回的时间戳会在大约 70 分钟之后重新开始计数,在由70分钟跳变到0时,TS会出现异常,因此需要进行修正。
    //如果时间间隔小于等于零或大于 0.5 秒,则将其设置为一个较小的默认值,即 1e-3f
    if (Ts <= 0 || Ts > 0.5f) Ts = 1e-3f;

    // 通过乘以时间间隔和目标速度来计算需要转动的机械角度,存储在 shaft_angle 变量中。
    //在此之前,还需要对轴角度进行归一化,以确保其值在 0 到 2π 之间。
    open_loop_shaft_angle = FOC_Conversion_Angle_0_2PI(open_loop_shaft_angle + target_velocity * Ts);
    //以目标速度为 10 rad/s 为例,如果时间间隔是 1 秒,则在每个循环中需要增加 10 * 1 = 10 弧度的角度变化量,才能使电机转动到目标速度。
    //如果时间间隔是 0.1 秒,那么在每个循环中需要增加的角度变化量就是 10 * 0.1 = 1 弧度,才能实现相同的目标速度。
    //因此,电机轴的转动角度取决于目标速度和时间间隔的乘积。
    
    // 使用早前设置的voltage_power_supply的1/3作为Uq值,这个值会直接影响输出力矩
    // 最大只能设置为Uq = voltage_power_supply/2,否则ua,ub,uc会超出供电电压限幅
    float Uq = voltage_power_supply / 5;

    FOC_Parker_Clark(Uq,FOC_ElectricalAngle(open_loop_shaft_angle, 7));

    open_loop_timestamp = now_us;  //用于计算下一个时间间隔

    return Uq;
}

3.FOC.h 

#ifndef __FOC_H_
#define __FOC_H_

#include "main.h"
#include <math.h>
#include <stdlib.h>
#include <stdio.h>

#define PI       3.14159265359f
#define _2PI     6.28318530718f
#define _3PI_2   4.71238898f
#define _1_SQRT3 0.57735026919f
#define _2_SQRT3 1.15470053838f

#define LIMIT(amt,low,high) ((amt) < (low) ? (low) : ((amt) > (high) ? (high) : (amt)))//限幅函数

extern uint32_t micros;//芯片运行时基

float FOC_ElectricalAngle(float machine_angle,int pole_pairs_num);//电角度求解
float FOC_Conversion_Angle_0_2PI(float angle);//归一化角度到[0,2PI]
void FOC_SetPWM(float Ua,float Ub,float Uc);//设置PWM到控制器输出
void FOC_Parker_Clark(float Uq,float angle_el);//FOC核心算法,克拉克逆变换/帕克逆变换
float FOC_Open_Speed_Start(float target_velocity);//开环速度函数

#endif

4.演示效果

FOC_Open_Speed_Start(10);//FOC_Start

调用FOC开环控制代码后,通过VOFA+我们可以清晰地看到输出的三相sin波形。


http://www.kler.cn/a/539230.html

相关文章:

  • 增量hdfs数据追平
  • 【系统架构设计师】体系结构文档化
  • 多光谱成像技术在华为Mate70系列的应用
  • Vue与Konva:解锁Canvas绘图的无限可能
  • QML 和 Qt Quick 介绍
  • 【C++篇】 异常处理
  • 从算法到落地:DeepSeek如何突破AI工具的同质化竞争困局
  • 【Rust中级教程】1.1. 指针概览(上):什么是指针、指针和引用的区别
  • [高等数学]不定积分的概念与性质
  • python笔记2--组合数据类型
  • 操作系统—进程与线程
  • DeepSeek多软件协同效应,产生的王炸组合
  • 智慧交通:如何通过数据可视化提升城市交通效率
  • Nexus 实战详解:企业级制品仓库管理
  • 从Open R1来看如何训练DeepSeek R1模型
  • FFmpeg获取RTSP视频流时,视频帧的格式
  • Stability AI 联合 UIUC 提出单视图 3D 重建方法SPAR3D,可0.7秒完成重建并支持交互式用户编辑。
  • VirtualBox中Ubuntu 22.04网卡配置以及解决过程中遇到的问题
  • 算法与数据结构(爬楼梯)
  • #渗透测试#批量漏洞挖掘#某骋BPM Handler SQL注入漏洞
  • JavaScript系列(61)--边缘计算应用开发详解
  • 三星手机为何不大力扩展中国市场?
  • json格式化 网站--可以将json 数据放入,提取出来有用的信息
  • 网络防御高级02-综合实验
  • 代码随想录(二叉树所有题解)
  • SpringMVC SpringMVC拦截器 拦截器基础知识