新手小白的FOC学习之路(代码篇)(一)开环控制

前言       

        因为比赛的一些需要学习FOC电机控制技术,但个人水平有限一窍不通,在网上找资料研究了大半个月后,又跟着灯哥的教程看了一遍虽然脑子感觉都会了,但是自己去写一个FOC算法时时候还是遇到了不少坑于是打算记录一下,也是作为注册CSDN以来的第一篇博客。

开发环境和平台

        控制板:野火指南者F103VET6

        驱动板和电机:灯哥V3P套件

        驱动程序使用HAL库编写

HAL库配置

        时钟之类的配置这里就不展开了,都是常规操作

        既然要驱动FOC电机那么PWM肯定是少不了的,我们首先进行PWM输出的配置:我们选择定时器TIM3的通道2,3,4就是PB0,PB1,PB5三个引脚输出PWM信号这三个引脚正好连接着led也方便我们观察现象。

        定时器的相关配置如下图所示我们用15khz的pwm信号作为载波

        串口配置我们选择串口一、异步通讯,用于向vofa发送数据

       

然后生成代码即可

FOC算法的编写

        主要参考了dengfoc的相关算法

.C文件

#include "BY_FOC.h"
#include "bsp_UART.h"

#define voltage_power_supply 12.6
#define Pi 3.1415926

float Ualpha,Ubeta,Ua,Ub,Uc;
float APWM,BPWM,CPWM;
/**********************系统运行时间***************************/
__STATIC_INLINE uint32_t GXT_SYSTICK_IsActiveCounterFlag(void)
{
  return ((SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk) == (SysTick_CTRL_COUNTFLAG_Msk));
}
static uint32_t getCurrentMicros(void)
{
  /* Ensure COUNTFLAG is reset by reading SysTick control and status register */
  GXT_SYSTICK_IsActiveCounterFlag();
  uint32_t m = HAL_GetTick();
  const uint32_t tms = SysTick->LOAD + 1;
  __IO uint32_t u = tms - SysTick->VAL;
  if (GXT_SYSTICK_IsActiveCounterFlag()) {
    m = HAL_GetTick();
    u = tms - SysTick->VAL;
  }
  return (m * 1000 + (u * 1000) / tms);
}
//获取系统时间,单位us
uint32_t micros(void)
{
  return getCurrentMicros();
}
/**************************************************************/

/* Clark与Park变换*/
void ClarkAndPark(float Uq,float Ud,float angle_el)
{
	Ualpha = -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;
	APWM=Ua/voltage_power_supply;
	BPWM=Ub/voltage_power_supply;
	CPWM=Uc/voltage_power_supply;
	printf("%lf,%lf,%lf\n",APWM,BPWM,CPWM);
}

float electricalAngle(float shaft_angle, int pole_pairs) 
{
  return (shaft_angle * pole_pairs);
}

// 归一化角度到 [0,2PI]
float normalizeAngle(float angle){
  float a = fmod(angle, 2*Pi);   //取余运算可以用于归一化,列出特殊值例子算便知
  return a >= 0 ? a : (a + 2*Pi);  
}
uint32_t now=0,last=0;
float E_A,m_a=0;
double Tval;
void test(void)
{
	now=micros();
	Tval=(double)(now-last)/1000000;
	m_a=normalizeAngle(m_a+(10*Pi)*Tval*7);
//	printf("%lf\n",m_a);
	E_A= electricalAngle(m_a, 7);
	ClarkAndPark(5,0,m_a);
	last=now;
}

        开环控制的代码还是比较简单的,主要就是PARK变换和CLARK变换和一些简单的数学运算

H文件 

#ifndef __BY_FOC_H_
#define __BY_FOC_H_

#include "stm32f1xx.h"
#include "math.h"


void ClarkAndPark(float Uq,float Ud,float angle_el);
float electricalAngle(float shaft_angle, int pole_pairs) ;
float normalizeAngle(float angle);
void test(void);

#endif

主函数 

extern float APWM,BPWM,CPWM;

int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_I2C1_Init();
  MX_USART1_UART_Init();
  MX_TIM3_Init();
  /* USER CODE BEGIN 2 */
	HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_2);
	HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_3);
	HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_4);
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
		test();
		__HAL_TIM_SET_COMPARE(&htim3,TIM_CHANNEL_2,4799*APWM); 
    __HAL_TIM_SET_COMPARE(&htim3,TIM_CHANNEL_3,4799*BPWM); 
		__HAL_TIM_SET_COMPARE(&htim3,TIM_CHANNEL_4,4799*CPWM); 
  }
  /* USER CODE END 3 */
}

代码运行

我们打开vofa就可以得到这样的三条相位相差120度的正弦波

 

我们的电机也愉快的转起来了

 

foc开环控制

以下是一个简单的FOC代码示例,可以生成50Hz的正弦波: ``` #include <Arduino.h> #include <Servo.h> #include <math.h> #define PWM_FREQ 20000 // PWM频率 #define PWM_RES 8 // PWM分辨率 Servo servoU, servoV, servoW; float angle = 0; // 角度 float step = 2 * PI / 360; // 步长 void setup() { servoU.attach(10); servoV.attach(11); servoW.attach(12); TCCR1A = 0; // 清除计时器1控制寄存器A TCCR1B = 0; // 清除计时器1控制寄存器B TCCR1A |= (1 << COM1A1) | (1 << COM1B1) | (1 << COM1C1); // 设置计时器1 PWM模式 TCCR1B |= (1 << WGM13) | (1 << CS10); // 设置计时器1 PWM模式和分频器 OCR1A = 0; // 初始化PWM输出 OCR1B = 0; OCR1C = 0; TIMSK1 = 0; // 关闭计时器1中断 } void loop() { float sinU = sin(angle); float sinV = sin(angle + 2 * PI / 3); float sinW = sin(angle + 4 * PI / 3); int pwmU = map(sinU, -1, 1, 0, pow(2, PWM_RES) - 1); int pwmV = map(sinV, -1, 1, 0, pow(2, PWM_RES) - 1); int pwmW = map(sinW, -1, 1, 0, pow(2, PWM_RES) - 1); OCR1A = pwmU; // 设置PWM输出 OCR1B = pwmV; OCR1C = pwmW; angle += step; // 更新角度 delay(1000 / (50 * 360)); // 等待下一个周期 } ``` 这个代码使用Arduino的计时器1来生成PWM信号,通过设置计时器的PWM模式和分频器来控制PWM频率和分辨率。在`loop()`函数中,使用正弦函数生成三相电流的幅值,将其映射到PWM输出范围内,然后设置计时器的三个比较输出通道的PWM输出。最后,根据所需的正弦波频率,通过延迟函数等待下一个周期。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值