FOC的入门与代码

        最近要做一个项目,对于市面上常用的电机类型,总感觉还是无刷电机比较可靠,伺服效果更好,那么来简单探讨一下FOC的原理和代码实现的过程。

        此篇文章就是想让大家快速的应用FOC,不想理解太多的数学公式,让电机转起来。

        首先要理解一件事,无刷电机的组成一般是由永磁同步电机所构成,包括定子,转子,永磁铁,在电机的选型上永磁体会在转子上也可定子上,其原理就根据磁铁的同性相吸,异性相斥的原理,同时在结构上是没有电刷的,不能够进行机械换向,是通过电子换向来驱动转子不断地转动,电机的接线方法分为星形解法和三角形解法,普遍的接法是星形解法。如下片的文章。

https://zhuanlan.zhihu.com/p/428964390icon-default.png?t=O83Ahttps://zhuanlan.zhihu.com/p/428964390

同时在无刷电机的购买上,一般标有KV值,而电机的电压和KV值决定了电机的转速,而电机的转速也就决定了换向的频率。 

还需要理解一点,相电压、相电流和线电压、线电流。

相电压:是每一相的电压,如图Ua,Ub,Uc

线电压:是指3根导线任意2根之间的电压,Uab,Ubc,Uac

同时对于星形解法来说,线电压的幅值是相电压的根号3倍。矢量相加的结果。

反电动势电压:手动匀速转动它的转子,然后用示波器观察它的三相输出电压,是3个正玄波信号

电角度:它描述了转子磁场相对于定子绕组的空间位置关系,

  • 电角度 \theta_eθe​ 的范围是 0° 到 360°,超过 360° 后会重新从 0° 开始。

好了,知道了前提得一些知识,接下来就是电机FOC的东西了。主要参考,下面几篇文章。

https://zhuanlan.zhihu.com/p/147659820icon-default.png?t=O83Ahttps://zhuanlan.zhihu.com/p/147659820

https://zhuanlan.zhihu.com/p/152144886icon-default.png?t=O83Ahttps://zhuanlan.zhihu.com/p/152144886深入浅出FOC控制-CSDN博客文章浏览阅读10w+次,点赞640次,收藏3.5k次。深入浅出FOC(Field Oriented Control)前言:为什么要学习FOC?1.电机控制是自动化控制领域重要一环。2.目前直流无刷电机应用越来越广泛,如无人机、机械臂、云台、仿生机器人等等。3.电机控制工程师薪水较高。需要什么基础?1.C语言,指针,结构体,编程规范。2.STM32外设使用。3.原理图阅读。4.芯片手册阅读。5.数序坐标系知识为什么要出本教程?1.直流无刷电机应用越来越广泛,网上资料比较散落,因此想要出一篇系统性的教程,从头到尾,深入浅出,帮助初学者快速_fochttps://blog.csdn.net/qq_35947329/article/details/115483413
基于EG2133的有感/无感无刷电机驱动器设计 - 立创开源硬件平台关键词:STM32F401RCT6,EG2133三相半桥驱动芯片,AS5600磁编码器,MT6701磁编码器,有感FOC,无感FOC,SVPWM,可以使用SimpleFOC程序进行驱动。icon-default.png?t=O83Ahttps://oshwhub.com/shadow27/tai-yang-neng-wu-ren-chuan


1,电机FOC算法概述

        根据文章我们知道,想让一个无刷电机转起来,要就需要3路相差120度的正玄波信号,给入到uvw,三相电压。而此时FOC就是把单片机产生的3路pwm通过一些计算变成3路正玄波信号,使无刷电机进行转动。

这篇文章不深入探究FOC,仅仅是让其快速入门,让电机旋转起来。

先看一张图

可能第一次看这个有点懵,但是不要拍,如果仅仅让电机转起来的,并不需要全部的流程。

网上很对资料,都是由Clark变换和Park变换讲起,其实说白了一点。

Clark变换是把Ia,Ib,Ic的三项正玄波信号,通过数学的方法变换成Ialph,Itheta两项正玄波

Park变换是把Ialph,Itheta两项正玄波,通过数学的方法变成Iq和Id高低电平来显示

(其实这个2个变换,一般在电流环的时候出现,仅仅让电机转动则不需要)

那么让电机旋转需要什么呢?Park的逆变换和SVPWM

Park的逆变换是静态的Uq和Ud变换成两项正玄波的Ualph,Utheta

SVPWM是将两项正玄波的Ualph,Utheta变换成UVW电机的三项正玄波

由此可见,仅仅需要Park变换和SVPWM就可以实现电机的转动。

其中的符号在所有讲FOC的过程中都会解释的,这边就不赘述了。


2,硬件控制电机转动

好了还有一个问题,到底如何让无刷电机实现转向,持续运行呢?

        其实各个大神在讲解FOC都会提到的,这简单说一下,硬件电路一般有三相逆变电路来实现,逆变电路具体的实现则一般是采用半桥MOS电路来制作的。半桥电路的原型如下,其实很简单,就是两个MOS管组成的上桥臂下桥臂。

        如图,当a项的上桥臂和bc项的下桥臂打开,此时电流会从a流向b和c,依次类推共8种,但是全打开和全关闭会导致短路,并不使用。也就是说当我按照一定的规律进行打开和关闭这些mos管,和控制打开和关闭的时间,就能控制无刷电机的转动了。

注:mos相当于开关,如果人为的控制也能实现电机转动,而更好的方法把这些工作交给单片机来处理呢?


3,软硬件控制电机转动

        明白了基础的转动的思路,那么如何和单片机联系起来呢(不建议使用stm32F103系列的,没有单精度运算,可以使用stm32F4系列或者stm32G4系列)

使用一下大神的原理图。上面的连接。

芯片采用EG2133芯片,简单来说3个输入IN1、IN2、IN3,输入形式为pwm的形式。

        也就是说如果改变pwm的占空比,就能实现上下桥臂的打开还是关闭的状态,并保证不能同时打开(有死区时间限制),换一句话说,只要不断的改变pwm的占空比,就能控制6个mos管的开与关和开关的时间。

从而生成3路正玄波信号的。


4、SVPWM与FOC的关系

 那么最后什么是SVPWM,:使用这6个空间电压矢量作为基向量来合成任意矢量。在每一个扇区,选择相邻两个电压矢量以及零矢量,按照伏秒平衡原则来合成每个扇区内的任意电压矢量。

额。。有点难以明白是吧,不理解也没有事,知道一点,SVPWM是用来选择扇区的,并通过3路pwm的占空比来,确定mos的打开和关闭的时间来生成驱动无刷电机的信号的。

        这2张图出现的频率特别的高,也就是说电机旋转角度0-360都给分布在扇区中,也就是根据输入的角度,推算出所在的区域,之后根据角度信息来确定3路pwm的占空比,每一个mos打开关闭的时间。

        有的人会问了,你这是3路正常的pwm怎么变成了正玄波呢?其实是这样的每一个pwm的占空比对应着一个值,把一个周期所经历的占空比的数据用图像的方式显示出来,就是一周期的正玄波了。

注意哈,这个pwm占空比在写代码的时候要设置成中心对称的方式,即。

    // 定时器时钟配置
    htim3.Instance = TIM3;
    htim3.Init.Prescaler = 0; // 预分频器
    htim3.Init.CounterMode = TIM_COUNTERMODE_CENTERALIGNED1; // 中心对齐模式1
    htim3.Init.Period = 999; // ARR值,决定PWM周期
    htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
    htim3.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
    HAL_TIM_PWM_Init(&htim3);

5、代码的编写

        回顾上文,让电机开环运行,转起来,需要Park逆变换和SVPWM和单片机配置3路中心对其的pwm即可。

输入是uq和ud和电角度信息。

输出是3路pwm的占空比。

(1)Park逆变换

	// Park逆变换
	float U_alpha = -Uq * arm_sin_f32(angle_el) + Ud * arm_cos_f32(angle_el);
	float U_beta = Uq * arm_cos_f32(angle_el) + Ud * arm_sin_f32(angle_el);

(2)SVPWM生成

// 扇区判断
	float K = sqrt3 * Ts / Udc; // SVPWM调制比
	float u1 = U_beta * K;
	float u2 = (0.8660254f * U_alpha - 0.5f * U_beta) * K; // sqrt(3)/2 = 0.8660254
	float u3 = (-0.8660254f * U_alpha - 0.5f * U_beta) * K;

	uint8_t sector = (u1 > 0.0f) + ((u2 > 0.0f) << 1) + ((u3 > 0.0f) << 2); // sector = A + 2B + 4C

	// 非零矢量和零矢量作用时间的计算
	switch (sector)
	{
	case 3: // 扇区1
		t4 = u2;
		t6 = u1;
		sum = t4 + t6;
		if (sum > Ts) // 过调制处理
		{
			k_svpwm = Ts / sum;
			t4 *= k_svpwm;
			t6 *= k_svpwm;
		}
		t7 = (Ts - t4 - t6) / 2.0f;
		Ta = t4 + t6 + t7;
		Tb = t6 + t7;
		Tc = t7;
		break;
	case 1: // 扇区2
		t2 = -u2;
		t6 = -u3;
		sum = t2 + t6;
		if (sum > Ts)
		{
			k_svpwm = Ts / sum;
			t2 *= k_svpwm;
			t6 *= k_svpwm;
		}
		t7 = (Ts - t2 - t6) / 2.0f;
		Ta = t6 + t7;
		Tb = t2 + t6 + t7;
		Tc = t7;
		break;
	case 5: // 扇区3
		t2 = u1;
		t3 = u3;
		sum = t2 + t3;
		if (sum > Ts)
		{
			k_svpwm = Ts / sum;
			t2 *= k_svpwm;
			t3 *= k_svpwm;
		}
		t7 = (Ts - t2 - t3) / 2.0f;
		Ta = t7;
		Tb = t2 + t3 + t7;
		Tc = t3 + t7;
		break;
	case 4: // 扇区4
		t1 = -u1;
		t3 = -u2;
		sum = t1 + t3;
		if (sum > Ts)
		{
			k_svpwm = Ts / sum;
			t1 *= k_svpwm;
			t3 *= k_svpwm;
		}
		t7 = (Ts - t1 - t3) / 2.0f;
		Ta = t7;
		Tb = t3 + t7;
		Tc = t1 + t3 + t7;
		break;
	case 6: // 扇区5
		t1 = u3;
		t5 = u2;
		sum = t1 + t5;
		if (sum > Ts)
		{
			k_svpwm = Ts / sum;
			t1 *= k_svpwm;
			t5 *= k_svpwm;
		}
		t7 = (Ts - t1 - t5) / 2.0f;
		Ta = t5 + t7;
		Tb = t7;
		Tc = t1 + t5 + t7;
		break;
	case 2: // 扇区6
		t4 = -u3;
		t5 = -u1;
		sum = t4 + t5;
		if (sum > Ts)
		{
			k_svpwm = Ts / sum;
			t4 *= k_svpwm;
			t5 *= k_svpwm;
		}
		t7 = (Ts - t4 - t5) / 2.0f;
		Ta = t4 + t5 + t7;
		Tb = t7;
		Tc = t5 + t7;
		break;
	default:
		break;

(3)定时器使能

  htim3.Instance = TIM3;
  htim3.Init.Prescaler = 0;
  htim3.Init.CounterMode = TIM_COUNTERMODE_CENTERALIGNED1;
  htim3.Init.Period = 2800-1;
  htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  htim3.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;


// 使能TIMx的通道y
void PWM_Init(void)
{
	HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1);
	HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_2);
	HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_3);
}

// 输入参数0.0f ~ 1.0f,输出3路PWM
void Set_PWM(float _CCR1, float _CCR2, float _CCR3)
{
	__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, _CCR1 * PWM_ARR);
	__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_2, _CCR2 * PWM_ARR);
	__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_3, _CCR3 * PWM_ARR);
}

 (4)模拟电角度


const float _2PI = 6.28318530717958f;

// normalizing radian angle to [0, 2pi]
float _normalizeAngle(float angle)
{
    float a = fmod(angle, _2PI); // fmod()函数用于浮点数的取余运算
    return a >= 0.0f ? a : (a + _2PI);
}


// 开环运行测试
void OpenVelocity(float target)
{
   static float _estimateAngle = 0.1f;                                 // 开环虚拟机械角度
   static float deltaT = 0.00625f;                                     // 开环运行时间间隔
   _estimateAngle = _normalizeAngle(_estimateAngle + target * deltaT); 
   setPhaseVoltage(voltageLimit, 0.0f, _estimateAngle * polePairs);
}

angle_el = _normalizeAngle(angle_el);

注意范围是0-360哈。

代码链接:通过网盘分享的文件:1.SVPWM开环速度测试
链接: https://pan.baidu.com/s/1Q-cyH1m3cdUdpeEOQbqghw?pwd=zyie 提取码: zyie 

连线方法:定时器3通道123,PA6,PA7,PB0 ,注意需要工地哈(驱动器和单片机)

同时这个仅仅是让电机旋转起来,并没有完成3环的闭环操作,剩下的就可以进行闭环操作了。同时,这个仅仅是最简单的运行,里面的学问有点多,后续有需要可以深入理解一下。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值