基于開源項目ESP32 SVPWM驅動無刷電機開環速度測試

基于開源項目ESP32 SVPWM驅動無刷電機開環速度測試


  • ?本篇硬件電路和代碼來源于此開源項目:https://github.com/MengYang-x/STM3F401-FOC/tree/main
  • 📍硬件電路和項目介紹,立創開源廣場:https://oshwhub.com/shadow27/tai-yang-neng-wu-ren-chuan
  • 🥕相關篇《基于開源項目HAL STM32F4 +DSP庫跑SVPWM開環速度測試》
  • 🔖代碼基于Arduino平臺。
  • 🌼 ESP32 SVPWM開環測試效果:
    在這里插入圖片描述
  • ?無刷電機運行正常運作過程中,代碼二的測試波形效果:
    在這里插入圖片描述
  • 🍁驅動電路參考:
    在這里插入圖片描述

?如果是通過6路信號來驅動無刷電機的不支持。如果需要測試6路信號驅動的可以參考我上面的相關內容,有關STM32 通過高級定時器3路互補輸出來實現SVPWM驅動無刷電機。

?🔬模擬仿真測試

  • 📍ESP32在線SVPWM模擬仿真測試地址:https://wokwi.com/projects/396507548266030081

在這里插入圖片描述

  • 📝仿真代碼
#include <Arduino.h>
#include <math.h>#define PI 3.14159265359
#define PI_2 1.57079632679
#define PI_3 1.0471975512
#define _SQRT3 1.73205080757
#define voltage_power_supply 12.0float normalizeAngle(float angle) {float a = fmod(angle, 2 * PI);return a >= 0 ? a : (a + 2 * PI);
}void setPwm(float Ua, float Ub, float Uc) {Serial.print(Ua);Serial.print(",");Serial.print(Ub);Serial.print(",");Serial.println(Uc);
}void setTorque(float Uq, float angle_el) {if (Uq < 0)angle_el += PI;Uq = abs(Uq);angle_el = normalizeAngle(angle_el + PI_2);int sector = floor(angle_el / PI_3) + 1;// calculate the duty cyclesfloat T1 = _SQRT3 * sin(sector * PI_3 - angle_el) * Uq / voltage_power_supply;float T2 = _SQRT3 * sin(angle_el - (sector - 1.0) * PI_3) * Uq / voltage_power_supply;float T0 = 1 - T1 - T2;float Ta, Tb, Tc;switch (sector){case 1:Ta = T1 + T2 + T0 / 2;Tb = T2 + T0 / 2;Tc = T0 / 2;break;case 2:Ta = T1 + T0 / 2;Tb = T1 + T2 + T0 / 2;Tc = T0 / 2;break;case 3:Ta = T0 / 2;Tb = T1 + T2 + T0 / 2;Tc = T2 + T0 / 2;break;case 4:Ta = T0 / 2;Tb = T1 + T0 / 2;Tc = T1 + T2 + T0 / 2;break;case 5:Ta = T2 + T0 / 2;Tb = T0 / 2;Tc = T1 + T2 + T0 / 2;break;case 6:Ta = T1 + T2 + T0 / 2;Tb = T0 / 2;Tc = T1 + T0 / 2;break;default:Ta = 0;Tb = 0;Tc = 0;}float Ua = Ta * voltage_power_supply;float Ub = Tb * voltage_power_supply;float Uc = Tc * voltage_power_supply;setPwm(Ua, Ub, Uc);
}void setup() {Serial.begin(115200);  // Make sure to match the baud rate with Serial Monitor
}void loop() {float Uq = 1/_SQRT3;  // Test value for voltagefloat angle_el = 0;  // Test value for angle// Test values across a full circlefor (int i = 0; i < 360; i++) {angle_el = i * PI / 180;setTorque(Uq, angle_el);delay(20);  // Delay for visibility in plotter}
}

📙驅動測試代碼 一

  • ?此代碼直接驅動無刷電機轉動沒有問題,但是開啟Vofa+波形就不正常了,打印函數太占用時間,驅動無刷電機對SVPWM要求實時連續性很高,因任務執行所消耗的時間,一個loop循環下來,運行時間大大超出了預期值。波形輸出的直接變成了正弦波,而不是馬鞍波,導致電機不能轉動,
  • 🥕不開啟打印,一個loop循環下來。大概就是60us左右,也就是代碼中 velocityOpenloop(2.5f);執行一遍的時間。
  • 🧨開啟打印,如果波特率設置比較低,打印3個浮點類型數據,消耗的時間會超過1ms。
  • 🎉如果需要查看波形,串口通訊波特率盡可能的設置高一些,給定的預設的角度值大一些。
  • 👉在驅動無刷電機前,調試前期,可以直接通過查看3路波形,即可預測驅動電機的實際效果。一定要是SVPWM波形(馬鞍波),才能正常轉起來。
/** 日期:2023.7.22* 開環速度控制代碼* 使用vofa+ 進行串口調試,波特率需要設置為57600* 電機參數 A2212/15T的極對數:7**/
#include <Arduino.h>
#include <math.h>const int poles = 7;  // 電機的極對數// PWM輸出引腳定義
// 定義LEDC通道、GPIO引腳和分辨率
#define LEDC_CHANNEL1    0
#define LEDC_CHANNEL2    1
#define LEDC_CHANNEL3    2#define LEDC_GPIO1 5
#define LEDC_GPIO2 18
#define LEDC_GPIO3 19#define LEDC_RESOLUTION 10 // 設置分辨率為10位
#define PWM_FREQ 15000 // 設置PWM頻率為15000Hz// const char pwmA = 5;
// const char pwmB = 18;
// const char pwmC = 19;const float voltagePowerSupply = 12.0;
float open_loop_timestamp = 0;
float shaft_angle = 0; // 機械角度
float zero_electric_angle = 0;
float Ualpha, Ubeta = 0;
float Ua = 0, Ub = 0, Uc = 0;
float dc_a = 0, dc_b = 0, dc_c = 0;void setup()
{Serial.begin(57600);// PWM設置pinMode(LEDC_GPIO1, OUTPUT);pinMode(LEDC_GPIO2, OUTPUT);pinMode(LEDC_GPIO3, OUTPUT);ledcSetup(LEDC_CHANNEL1, PWM_FREQ, LEDC_RESOLUTION); // pwm通道(1-16), 頻率, 精度(0-14)ledcAttachPin(LEDC_GPIO1, 0); // 將GPIO引腳與LEDC通道關聯,這樣才能讓LEDC信號輸出到這個引腳ledcSetup(LEDC_CHANNEL2, PWM_FREQ, LEDC_RESOLUTION); // pwm通道, 頻率, 精度ledcAttachPin(LEDC_GPIO2, 1); // 將GPIO引腳與LEDC通道關聯ledcSetup(LEDC_CHANNEL3, PWM_FREQ, LEDC_RESOLUTION); // pwm通道, 頻率, 精度ledcAttachPin(LEDC_GPIO3, 2); // 將GPIO引腳與LEDC通道關聯Serial.println("完成PWM初始化設置");delay(3000);
}/**  電角度 = 機械角度 * 極對數* @brief 電角度計算函數* @param shaft_angle 機械角度* @param pole_pairs 電機的極對數
*/
float _electricalAngle(float shaft_angle, int pole_pairs)
{return (shaft_angle * pole_pairs);
}/**角度歸一化到[0, 2pi],把輸入的角度限制在[0, 2pi]* @brief 角度歸一化函數* @param angle 輸入的角度* @return 歸一化后的角度* 例如:_normalizeAngle(3.1415926) 返回 0
*/
float _normalizeAngle(float angle)
{float a = fmod(angle, 2 * PI); // 取余,結果可能為負值return a >= 0 ? a : (a + 2 * PI);
}/**設置PWM輸出* @brief 設置PWM輸出* @param Ua 電機A的占空比* @param Ub 電機B的占空比* @param Uc 電機C的占空比
*/
void setPwm(float Ua, float Ub, float Uc)
{// 計算占空比,并使用constrain()函數限制相電壓的范圍0到1dc_a = constrain(Ua / voltagePowerSupply, 0.0f, 1.0f);dc_b = constrain(Ub / voltagePowerSupply, 0.0f, 1.0f);dc_c = constrain(Uc / voltagePowerSupply, 0.0f, 1.0f);// 寫入PWM到PWM 0 1 2 通道ledcWrite(0, static_cast<uint32_t>(dc_a * 1023));  //使用10位分辨率計算占空比值ledcWrite(1, static_cast<uint32_t>(dc_b * 1023));ledcWrite(2, static_cast<uint32_t>(dc_c * 1023));}/*** @brief 設置相位電壓* @param Uq 電流值* @param Ud 電壓值* @param angle_el 電機的電角度,單位 rad* 電角度 = 機械角度 * 極對數* 機械角度 = 電角度 / 極對數
*/
void setPhaseVoltage(float Uq, float Ud, float angle_el)
{angle_el = _normalizeAngle(angle_el + zero_electric_angle); // 電角度// 帕克逆變換Ualpha = -Uq * sin(angle_el);Ubeta = Uq * cos(angle_el);// 克拉克逆變換Ua = Ualpha + voltagePowerSupply / 2;Ub = (sqrt(3) * Ubeta - Ualpha) / 2 + voltagePowerSupply / 2;Uc = (-Ualpha - sqrt(3) * Ubeta) / 2 + voltagePowerSupply / 2;setPwm(Ua, Ub, Uc);
}/** 開環速度函數,Uq和電角度生成器* @brief 開環速度控制函數* @param target_velocity 目標速度,單位 rad/s* @return 返回Uq值,用于控制電機轉速*/
float velocityOpenloop(float target_velocity)
{//  unsigned long now_us = micros(); // 獲取從開啟芯片以來的微秒數,它的精度是 4 微秒。 micros() 返回的是一個無符號長整型(unsigned long)的值static float deltaT = 6.5e-5f;     // 給定一個固定的開環運行時間間隔// 計算當前每個Loop的運行時間間隔//  float Ts = (now_us - open_loop_timestamp) * 1e-6f;// 由于 micros() 函數返回的時間戳會在大約 70 分鐘之后重新開始計數,在由70分鐘跳變到0時,TS會出現異常,因此需要進行修正。如果時間間隔小于等于零或大于 0.5 秒,則將其設置為一個較小的默認值,即 1e-3f//   if (Ts <= 0 || Ts > 0.5f)//       Ts = 6.5e-5f;// 通過乘以時間間隔和目標速度來計算需要轉動的機械角度,存儲在 shaft_angle 變量中。在此之前,還需要對軸角度進行歸一化,以確保其值在 0 到 2π 之間。//  shaft_angle = _normalizeAngle(shaft_angle + target_velocity * Ts);shaft_angle = _normalizeAngle(shaft_angle + target_velocity * deltaT);// 以目標速度為 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 = voltagePowerSupply / 24;setPhaseVoltage(Uq, 0, _electricalAngle(shaft_angle, poles)); // 極對數可以設置為常量//  open_loop_timestamp = now_us; // 用于計算下一個時間間隔return Uq;
}
/*** @brief 調試函數,用于輸出PWM占空比* @return 無*/
void debug()
{Serial.printf("%f,%f,%f\n", dc_a, dc_b, dc_c);
}void loop()
{velocityOpenloop(2.5f);// debug();
}

📙驅動測試代碼二

?代碼中換算采用的是上面仿真中的算法,在開啟VOFA+串口波形查看時,務必將波特率盡可能設置高一些,以減少打印信息執行的時間。

  • 🌼波形效果:
    在這里插入圖片描述
  • 📑說明:
  • 🔖力矩大小影響因素: setTorque(0.3f, _electricalAngle(shaft_angle, poles));//Uq影響振幅,力矩大小

  • 🔖轉速影響因素: velocityOpenloop(6.0f);//數值越大和變量deltaT

/** 日期:2023.7.22* 開環速度控制代碼*  進行串口調試,波特率需要設置為576000* 電機參數 A2212/15T的極對數:7**/
#include <Arduino.h>
#include <math.h>#define VOFA_SERIAL     // 使用vofa+串口調試器查看馬鞍波波形
const int poles = 7;  // 電機的極對數// PWM輸出引腳定義
// 定義LEDC通道、GPIO引腳和分辨率
#define LEDC_CHANNEL1    0
#define LEDC_CHANNEL2    1
#define LEDC_CHANNEL3    2#define LEDC_GPIO1 5
#define LEDC_GPIO2 18
#define LEDC_GPIO3 19#define LEDC_RESOLUTION 10 // 設置分辨率為10位
#define PWM_FREQ 15000 // 設置PWM頻率為10000Hz// const char pwmA = 5;
// const char pwmB = 18;
// const char pwmC = 19;const float voltagePowerSupply = 12.0;
float open_loop_timestamp = 0;
float shaft_angle = 0; // 機械角度
float zero_electric_angle = 0;
float Ualpha, Ubeta = 0;
float Ua = 0, Ub = 0, Uc = 0;
float dc_a = 0, dc_b = 0, dc_c = 0;//#define PI 3.14159265359
#define PI_2 1.57079632679
#define PI_3 1.0471975512
#define _SQRT3 1.73205080757/**  電角度 = 機械角度 * 極對數* @brief 電角度計算函數* @param shaft_angle 機械角度* @param pole_pairs 電機的極對數
*/
float _electricalAngle(float shaft_angle, int pole_pairs)
{return (shaft_angle * pole_pairs);
}/**角度歸一化到[0, 2pi],把輸入的角度限制在[0, 2pi]* @brief 角度歸一化函數* @param angle 輸入的角度* @return 歸一化后的角度* 例如:_normalizeAngle(3.1415926) 返回 0
*/
float _normalizeAngle(float angle)
{float a = fmod(angle, 2 * PI); // 取余,結果可能為負值return a >= 0 ? a : (a + 2 * PI);
}/**設置PWM輸出* @brief 設置PWM輸出* @param Ua 電機A的占空比* @param Ub 電機B的占空比* @param Uc 電機C的占空比
*/
void setPwm(float Ua, float Ub, float Uc)
{// 計算占空比,并使用constrain()函數限制相電壓的范圍0到1dc_a = constrain(Ua / voltagePowerSupply, 0.0f, 1.0f);dc_b = constrain(Ub / voltagePowerSupply, 0.0f, 1.0f);dc_c = constrain(Uc / voltagePowerSupply, 0.0f, 1.0f);// 寫入PWM到PWM 0 1 2 通道ledcWrite(0, static_cast<uint32_t>(dc_a * 1023));  //使用10位分辨率計算占空比值ledcWrite(1, static_cast<uint32_t>(dc_b * 1023));ledcWrite(2, static_cast<uint32_t>(dc_c * 1023));}/*** @brief 設置相位電壓* @param Uq 電流值* @param Ud 電壓值* @param angle_el 電機的電角度,單位 rad* 電角度 = 機械角度 * 極對數* 機械角度 = 電角度 / 極對數
*/
void setPhaseVoltage(float Uq, float Ud, float angle_el)
{angle_el = _normalizeAngle(angle_el + zero_electric_angle); // 電角度// 帕克逆變換Ualpha = -Uq * sin(angle_el);Ubeta = Uq * cos(angle_el);// 克拉克逆變換Ua = Ualpha + voltagePowerSupply / 2;Ub = (sqrt(3) * Ubeta - Ualpha) / 2 + voltagePowerSupply / 2;Uc = (-Ualpha - sqrt(3) * Ubeta) / 2 + voltagePowerSupply / 2;setPwm(Ua, Ub, Uc);}void setTorque(float Uq, float angle_el) {if (Uq < 0)angle_el += PI;Uq = abs(Uq);angle_el = _normalizeAngle(angle_el + PI_2);int sector = floor(angle_el / PI_3) + 1;// calculate the duty cyclesfloat T1 = _SQRT3 * sin(sector * PI_3 - angle_el) * Uq / voltagePowerSupply;float T2 = _SQRT3 * sin(angle_el - (sector - 1.0) * PI_3) * Uq / voltagePowerSupply;float T0 = 1 - T1 - T2;float Ta, Tb, Tc;switch (sector){case 1:Ta = T1 + T2 + T0 / 2;Tb = T2 + T0 / 2;Tc = T0 / 2;break;case 2:Ta = T1 + T0 / 2;Tb = T1 + T2 + T0 / 2;Tc = T0 / 2;break;case 3:Ta = T0 / 2;Tb = T1 + T2 + T0 / 2;Tc = T2 + T0 / 2;break;case 4:Ta = T0 / 2;Tb = T1 + T0 / 2;Tc = T1 + T2 + T0 / 2;break;case 5:Ta = T2 + T0 / 2;Tb = T0 / 2;Tc = T1 + T2 + T0 / 2;break;case 6:Ta = T1 + T2 + T0 / 2;Tb = T0 / 2;Tc = T1 + T0 / 2;break;default:Ta = 0;Tb = 0;Tc = 0;}float Ua = Ta * voltagePowerSupply;float Ub = Tb * voltagePowerSupply;float Uc = Tc * voltagePowerSupply;setPwm(Ua, Ub, Uc);}/** 開環速度函數,Uq和電角度生成器* @brief 開環速度控制函數* @param target_velocity 目標速度,單位 rad/s* @return 返回Uq值,用于控制電機轉速*/
float velocityOpenloop(float target_velocity)
{//  unsigned long now_us = micros(); // 獲取從開啟芯片以來的微秒數,它的精度是 4 微秒。 micros() 返回的是一個無符號長整型(unsigned long)的值//影響T周期float deltaT = 4.2e-4f;     // 給定一個固定的開環運行時間間隔6.5e-5f 8.4e-5f 8.4e-3f 1.7e-2f// 計算當前每個Loop的運行時間間隔
//unsigned long mid_value = now_us - open_loop_timestamp;
//Serial.println(mid_value);// 計算當前每個Loop的運行時間間隔//  float deltaT = mid_value * 1e-6f;// 計算電機軸的機械角度// 計算電機軸的電角度//  float Ts = (now_us - open_loop_timestamp) * 1e-6f;//float Ts = mid_value * 1e-6f;// 由于 micros() 函數返回的時間戳會在大約 70 分鐘之后重新開始計數,在由70分鐘跳變到0時,TS會出現異常,因此需要進行修正。如果時間間隔小于等于零或大于 0.5 秒,則將其設置為一個較小的默認值,即 1e-3f
//    if (Ts <= 0 || Ts > 0.5f)
//        Ts = 6.5e-5f;// 通過乘以時間間隔和目標速度來計算需要轉動的機械角度,存儲在 shaft_angle 變量中。在此之前,還需要對軸角度進行歸一化,以確保其值在 0 到 2π 之間。//   shaft_angle = _normalizeAngle(shaft_angle + target_velocity * Ts);shaft_angle = _normalizeAngle(shaft_angle + target_velocity * deltaT);// 以目標速度為 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 = voltagePowerSupply / 24;// setPhaseVoltage(Uq, 0, _electricalAngle(shaft_angle, poles)); // 極對數可以設置為常量setTorque(0.3f,  _electricalAngle(shaft_angle, poles));//Uq影響振幅,力矩大小//   open_loop_timestamp = now_us; // 用于計算下一個時間間隔return Uq;
}}
void setup()
{Serial.begin(576000);// PWM設置pinMode(LEDC_GPIO1, OUTPUT);pinMode(LEDC_GPIO2, OUTPUT);pinMode(LEDC_GPIO3, OUTPUT);ledcSetup(LEDC_CHANNEL1, PWM_FREQ, LEDC_RESOLUTION); // pwm通道(1-16), 頻率, 精度(0-14)ledcAttachPin(LEDC_GPIO1, 0); // 將GPIO引腳與LEDC通道關聯,這樣才能讓LEDC信號輸出到這個引腳ledcSetup(LEDC_CHANNEL2, PWM_FREQ, LEDC_RESOLUTION); // pwm通道, 頻率, 精度ledcAttachPin(LEDC_GPIO2, 1); // 將GPIO引腳與LEDC通道關聯ledcSetup(LEDC_CHANNEL3, PWM_FREQ, LEDC_RESOLUTION); // pwm通道, 頻率, 精度ledcAttachPin(LEDC_GPIO3, 2); // 將GPIO引腳與LEDC通道關聯Serial.println("完成PWM初始化設置");delay(3000);
}void loop()
{velocityOpenloop(6.0f);//數值越大,電機旋轉的速度越快 。(Limited:0 到 2π 之間)#ifdef VOFA_SERIALprintf("%f,%f,%f\n", dc_a, dc_b, dc_c);#endif}

?解決上面程序中的痛點問題,引入雙核心多線程任務運行方案

?在Arduino平臺,esp32程序默認運行在核心1上的,引入雙核心多線程任務運行,將串口打印和SVPWM計算分別運行在核心0和核心1上,來保證任務執行的實時性。由于不同線程間的任務執行,任務的執行時間差異,需要及時給rtc看門狗,進行喂狗操作,否則,每執行一段時間,esp32就會產生看門狗復位的動作。
/** 日期:2024.5.31更新* 開環速度控制代碼*  進行串口調試,波特率需要設置為576000* 電機參數 2204-1400KV-12N14P 的極對數:7**/
#include <Arduino.h>
#include <math.h>
#include "soc/rtc_wdt.h" //設置看門狗用
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "driver/ledc.h"
#include "esp_log.h"
#include "esp_system.h"
#include "esp_timer.h"
#include "esp_attr.h"
#include "esp_intr_alloc.h"
#include "esp_err.h"
#include "esp_task_wdt.h"#define VOFA_SERIAL     // 使用vofa+串口調試器查看馬鞍波波形
const int poles = 7;  // 電機的極對數// PWM輸出引腳定義
// 定義LEDC通道、GPIO引腳和分辨率
#define LEDC_CHANNEL1    0
#define LEDC_CHANNEL2    1
#define LEDC_CHANNEL3    2#define LEDC_GPIO1 5
#define LEDC_GPIO2 18
#define LEDC_GPIO3 19#define LEDC_RESOLUTION 10 // 設置分辨率為10位
#define PWM_FREQ 15000 // 設置PWM頻率為10000Hzconst float voltagePowerSupply = 12.0;
float open_loop_timestamp = 0;
float shaft_angle = 0; // 機械角度
float zero_electric_angle = 0;
float Ualpha, Ubeta = 0;
float Ua = 0, Ub = 0, Uc = 0;
float dc_a = 0, dc_b = 0, dc_c = 0;//#define PI 3.14159265359
#define PI_2 1.57079632679
#define PI_3 1.0471975512
#define _SQRT3 1.73205080757TaskHandle_t th_p[2];// 任務句柄,對xTaskCreate的調用返回。可用作參數到vTaskDelete以刪除任務。/**  電角度 = 機械角度 * 極對數* @brief 電角度計算函數* @param shaft_angle 機械角度* @param pole_pairs 電機的極對數
*/
float _electricalAngle(float shaft_angle, int pole_pairs)
{return (shaft_angle * pole_pairs);
}/**角度歸一化到[0, 2pi],把輸入的角度限制在[0, 2pi]* @brief 角度歸一化函數* @param angle 輸入的角度* @return 歸一化后的角度* 例如:_normalizeAngle(3.1415926) 返回 0
*/
float _normalizeAngle(float angle)
{float a = fmod(angle, 2 * PI); // 取余,結果可能為負值return a >= 0 ? a : (a + 2 * PI);
}/**設置PWM輸出* @brief 設置PWM輸出* @param Ua 電機A的占空比* @param Ub 電機B的占空比* @param Uc 電機C的占空比
*/
void setPwm(float Ua, float Ub, float Uc)
{// 計算占空比,并使用constrain()函數限制相電壓的范圍0到1dc_a = constrain(Ua / voltagePowerSupply, 0.0f, 1.0f);dc_b = constrain(Ub / voltagePowerSupply, 0.0f, 1.0f);dc_c = constrain(Uc / voltagePowerSupply, 0.0f, 1.0f);// 寫入PWM到PWM 0 1 2 通道ledcWrite(0, static_cast<uint32_t>(dc_a * 1023));  //使用10位分辨率計算占空比值ledcWrite(1, static_cast<uint32_t>(dc_b * 1023));ledcWrite(2, static_cast<uint32_t>(dc_c * 1023));}/*** @brief 設置相位電壓* @param Uq 電流值* @param Ud 電壓值* @param angle_el 電機的電角度,單位 rad* 電角度 = 機械角度 * 極對數* 機械角度 = 電角度 / 極對數
*/
void setPhaseVoltage(float Uq, float Ud, float angle_el)
{angle_el = _normalizeAngle(angle_el + zero_electric_angle); // 電角度// 帕克逆變換Ualpha = -Uq * sin(angle_el);Ubeta = Uq * cos(angle_el);// 克拉克逆變換Ua = Ualpha + voltagePowerSupply / 2;Ub = (sqrt(3) * Ubeta - Ualpha) / 2 + voltagePowerSupply / 2;Uc = (-Ualpha - sqrt(3) * Ubeta) / 2 + voltagePowerSupply / 2;setPwm(Ua, Ub, Uc);}void setTorque(float Uq, float angle_el) {if (Uq < 0)angle_el += PI;Uq = abs(Uq);angle_el = _normalizeAngle(angle_el + PI_2);int sector = floor(angle_el / PI_3) + 1;// calculate the duty cyclesfloat T1 = _SQRT3 * sin(sector * PI_3 - angle_el) * Uq / voltagePowerSupply;float T2 = _SQRT3 * sin(angle_el - (sector - 1.0) * PI_3) * Uq / voltagePowerSupply;float T0 = 1 - T1 - T2;float Ta, Tb, Tc;switch (sector){case 1:Ta = T1 + T2 + T0 / 2;Tb = T2 + T0 / 2;Tc = T0 / 2;break;case 2:Ta = T1 + T0 / 2;Tb = T1 + T2 + T0 / 2;Tc = T0 / 2;break;case 3:Ta = T0 / 2;Tb = T1 + T2 + T0 / 2;Tc = T2 + T0 / 2;break;case 4:Ta = T0 / 2;Tb = T1 + T0 / 2;Tc = T1 + T2 + T0 / 2;break;case 5:Ta = T2 + T0 / 2;Tb = T0 / 2;Tc = T1 + T2 + T0 / 2;break;case 6:Ta = T1 + T2 + T0 / 2;Tb = T0 / 2;Tc = T1 + T0 / 2;break;default:Ta = 0;Tb = 0;Tc = 0;}float Ua = Ta * voltagePowerSupply;float Ub = Tb * voltagePowerSupply;float Uc = Tc * voltagePowerSupply;setPwm(Ua, Ub, Uc);}/** 開環速度函數,Uq和電角度生成器* @brief 開環速度控制函數* @param target_velocity 目標速度,單位 rad/s* @return 返回Uq值,用于控制電機轉速*/
float velocityOpenloop(float target_velocity)
{//  unsigned long now_us = micros(); // 獲取從開啟芯片以來的微秒數,它的精度是 4 微秒。 micros() 返回的是一個無符號長整型(unsigned long)的值//影響T周期float deltaT = 4.2e-4f;     // 給定一個固定的開環運行時間間隔6.5e-5f 8.4e-5f 8.4e-3f 1.7e-2f// 計算當前每個Loop的運行時間間隔
//unsigned long mid_value = now_us - open_loop_timestamp;
//Serial.println(mid_value);// 計算當前每個Loop的運行時間間隔//  float deltaT = mid_value * 1e-6f;// 計算電機軸的機械角度// 計算電機軸的電角度//  float Ts = (now_us - open_loop_timestamp) * 1e-6f;//float Ts = mid_value * 1e-6f;// 由于 micros() 函數返回的時間戳會在大約 70 分鐘之后重新開始計數,在由70分鐘跳變到0時,TS會出現異常,因此需要進行修正。如果時間間隔小于等于零或大于 0.5 秒,則將其設置為一個較小的默認值,即 1e-3f
//    if (Ts <= 0 || Ts > 0.5f)
//        Ts = 6.5e-5f;// 通過乘以時間間隔和目標速度來計算需要轉動的機械角度,存儲在 shaft_angle 變量中。在此之前,還需要對軸角度進行歸一化,以確保其值在 0 到 2π 之間。//   shaft_angle = _normalizeAngle(shaft_angle + target_velocity * Ts);shaft_angle = _normalizeAngle(shaft_angle + target_velocity * deltaT);// 以目標速度為 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 = voltagePowerSupply / 24;// setPhaseVoltage(Uq, 0, _electricalAngle(shaft_angle, poles)); // 極對數可以設置為常量setTorque(0.35f,  _electricalAngle(shaft_angle, poles));//Uq影響振幅,力矩大小//   open_loop_timestamp = now_us; // 用于計算下一個時間間隔return Uq;
}void Core0task(void *args) {while(1){ // 多線程中必須使用一個死循環#ifdef VOFA_SERIALrtc_wdt_feed();  //喂狗函數Serial.printf("%f,%f,%f\n", dc_a, dc_b, dc_c);vTaskDelay(1);//1MS
//    delayMicroseconds(150);//以微秒為單位時間
//    yield();#endif
}}void Core1task(void *args) {while(1){ // 多線程中必須使用一個死循環rtc_wdt_feed();  //喂狗函數velocityOpenloop(6.0f);//數值越大,電機旋轉的速度越快 。(Limited:0 到 2π 之間)vTaskDelay(1);//1MS// delayMicroseconds(150);//    yield();}
}void setup()
{Serial.begin(576000);rtc_wdt_protect_off();     //看門狗寫保護關閉 關閉后可以喂狗//rtc_wdt_protect_on();    //看門狗寫保護打開 打開后不能喂狗//rtc_wdt_disable();       //禁用看門狗rtc_wdt_enable();          //啟用看門狗rtc_wdt_set_time(RTC_WDT_STAGE0,1000); //看門狗超時時間設置為1秒// PWM設置pinMode(LEDC_GPIO1, OUTPUT);pinMode(LEDC_GPIO2, OUTPUT);pinMode(LEDC_GPIO3, OUTPUT);ledcSetup(LEDC_CHANNEL1, PWM_FREQ, LEDC_RESOLUTION); // pwm通道(1-16), 頻率, 精度(0-14)ledcAttachPin(LEDC_GPIO1, 0); // 將GPIO引腳與LEDC通道關聯,這樣才能讓LEDC信號輸出到這個引腳ledcSetup(LEDC_CHANNEL2, PWM_FREQ, LEDC_RESOLUTION); // pwm通道, 頻率, 精度ledcAttachPin(LEDC_GPIO2, 1); // 將GPIO引腳與LEDC通道關聯ledcSetup(LEDC_CHANNEL3, PWM_FREQ, LEDC_RESOLUTION); // pwm通道, 頻率, 精度ledcAttachPin(LEDC_GPIO3, 2); // 將GPIO引腳與LEDC通道關聯// 創建兩個任務xTaskCreatePinnedToCore(Core0task, "Core0task", 4096, NULL, 3, &th_p[0], 0);xTaskCreatePinnedToCore(Core1task, "Core1task", 4096, NULL, 4, &th_p[1], 1);Serial.println("完成PWM初始化設置");delay(3000);
}void loop()
{
}

🔬調參測試過程,記錄分析參考

  • 🌿3.2過度到6.0f波形變化
velocityOpenloop(6.0f);//3.2過度到6.0f
float deltaT = 3.4e-3f; 

在這里插入圖片描述

  • 🌿修改deltaT3.4e-3f; 3.4e-4f波形變化。
velocityOpenloop(6.0f);
float deltaT = 3.4e-4f; 

在這里插入圖片描述

  • 🌿修改形參6.0f12.0f變化:
 velocityOpenloop(12.0f);float deltaT = 3.4e-4f;

在這里插入圖片描述

  • 🌿修改deltaT 3.4e-4f6.5e-4f變化:
 velocityOpenloop(12.0f);float deltaT = 6.5e-4f;

在這里插入圖片描述

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/20043.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/20043.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/20043.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【bug】在 Windows 上安裝 SDKMAN! 的完整指南

在 Windows 系統上&#xff0c;安裝 SDKMAN! 可能會遇到一些小問題。本文將詳細介紹如何解決這些問題并成功安裝 SDKMAN!。 問題描述 當在 PowerShell 中運行以下命令以安裝 SDKMAN! 時&#xff1a; curl -s get.sdkman.io | bash你可能會遇到以下錯誤&#xff1a; bash : …

前端面試題日常練-day45 【面試題】

題目 希望這些選擇題能夠幫助您進行前端面試的準備&#xff0c;答案在文末 1. 在Bootstrap中&#xff0c;以下哪個類用于創建一個具有響應式的導航欄&#xff1f; a) .navbar-responsive b) .responsive-nav c) .navbar-collapse d) .collapse-navbar 2. 哪個Bootstrap類用…

2024 HN CTF WebMisc 部分 wp

Web ez_tp 判斷是thinkphp 3.2 參考官方手冊:https://www.kancloud.cn/manual/thinkphp/1697 判斷路由模式 URL_CASE_INSENSITIVE > true, // 默認false 表示URL區分大小寫 true則表示不區分大小寫URL_MODEL > 1, // URL訪問模式,可選參數0、1、…

Python使用動態代理的多元應用

Python作為一種功能強大且易于學習的編程語言&#xff0c;在網絡編程領域具有廣泛的應用。當Python與動態代理技術結合時&#xff0c;便開啟了一扇通往更多可能性的大門。以下將深入探討Python使用動態代理可以實現的多種應用。 首先&#xff0c;Python結合動態代理在網絡爬蟲…

中文多模態InternVL-Chat-V1-5,中文理解能力強勁,8 項指標超越商業模型,性能媲美 GPT-4V

前言 近年來&#xff0c;多模態大型語言模型&#xff08;MLLM&#xff09;的快速發展&#xff0c;為人工智能在圖像、文本等多模態信息理解和處理方面帶來了前所未有的突破。然而&#xff0c;現有的主流多模態模型多以英文為訓練語言&#xff0c;在中文理解和處理方面存在著明…

可用于嵌入式的解釋器調研對比,及lua解釋器介紹

嵌入式不一定只能用C! ---------------------------------------------------------------------------------------手動分割線-------------------------------------------------------------------------------- 本文章參考了以下文章&#xff1a; 這里是引用 ------------…

1113 錢串子的加法

idea 測試點3&#xff1a;輸入的兩個整數都是0測試點4.5&#xff1a;大數&#xff0c;需要用大數加法 solution1(測試點4&#xff0c;5不通過) 直接相加再轉30進制 #include<iostream> #include<string> using namespace std; typedef long long ll; string a,…

linux sed命令替換文件端口

1、需求描述&#xff1a;因sed -i ‘s/舊端口/新端口/g’ 文件&#xff0c;替換會直接增加端口導致端口直接追加后面&#xff0c;因此需要修改 要求&#xff1a;2300替換為23003&#xff0c;23001替換為23004 <value>192.168.1.133</value></constructor-arg>…

RGMII接口--->(001)FPGA實現RGMII接口(一)

&#xff08;001&#xff09;FPGA實現RGMII接口(一) 1 目錄 &#xff08;a&#xff09;FPGA簡介 &#xff08;b&#xff09;IC簡介 &#xff08;c&#xff09;Verilog簡介 &#xff08;d&#xff09;FPGA實現RGMII接口(一) &#xff08;e&#xff09;結束 1 FPGA簡介 &…

【科普向】【文末附gpt升級秘笈】《慶余年》鳳冠之工藝探究——Blender建模與3D打印之奧秘

《慶余年》鳳冠之工藝探究——Blender建模與3D打印之奧秘 一、引言 昔者&#xff0c;《慶余年》之熱播&#xff0c;引發天下觀眾之熱議。今者&#xff0c;其續作《慶余年2》之中&#xff0c;一場盛大的婚禮更是矚目。而此婚禮之上&#xff0c;唯一之鳳冠&#xff0c;竟出自一…

windows 10下conda環境目錄轉移

目錄 一&#xff1a;背景 二&#xff1a;轉移過程 三&#xff1a;環境驗證 一&#xff1a;背景 最近用conda安裝了幾個python環境&#xff0c;隨著安裝包和數據的不斷增大&#xff0c;發現C盤占用空間一直在增加&#xff0c;已經有十幾個G了&#xff0c;系統也變的越來越慢。…

Mac/Linux getline 無法讀取文件內容(讀取內容無法顯示)

如下面代碼 #include <iostream> #include <fstream>using namespace std;int main() {string file_name "1.txt";std::ifstream file(file_name);if (file.is_open()) {std::string line;while (std::getline(file, line)) {char c line.back();cout…

【深度學習】安全帽檢測,目標檢測,yolov10算法,yolov10訓練

文章目錄 一、數據集二、yolov10介紹三、數據voc轉換為yolo四、訓練五、驗證六、數據、模型、訓練后的所有文件 尋求幫助請看這里&#xff1a; https://docs.qq.com/sheet/DUEdqZ2lmbmR6UVdU?tabBB08J2一、數據集 安全帽佩戴檢測 數據集&#xff1a;https://github.com/njvi…

MySql part1 安裝和介紹

MySql part1 安裝和介紹 數據 介紹 什么是數據庫&#xff0c;數據很好理解&#xff0c;一般來說數據通常是我們所認識的 描述事物的符號記錄&#xff0c; 可以是數字、 文字、圖形、圖像、聲音、語言等&#xff0c;數據有多種形式&#xff0c;它們都以經過數字化后存入計算機…

Nuxt3項目實現 OG:Image

目錄 前言 1、安裝 2、設置網站 URL 3、啟用 Nuxt DevTools 4、創建您的第一個Og:Image a. 定義OG鏡像 b. 查看您的Og:Image 5、自定義NuxtSeo模板 a. 定義 NuxtSeo模板 b. 使用其他可用的社區模板 6、創建自己的模板 a. 定義組件 BlogPost.vue b. 使用新模板 c.…

vue3 watch學習

watch的偵聽數據源類型 watch的第一個參數為偵聽數據源&#xff0c;有4種"數據源"&#xff1a; ref&#xff08;包括計算屬性&#xff09; reactive(響應式對象) getter函數 多個數據源組成的數組。 //ref const xref(0)//單個ref watch(x,(newX)>{console.…

Python庫之Scrapy-Redis的高級用法深度解析

Python庫之Scrapy-Redis的高級用法深度解析 引言 Scrapy-Redis作為Scrapy框架的擴展庫&#xff0c;不僅支持基本的分布式爬取功能&#xff0c;還提供了一系列的高級用法&#xff0c;使得爬蟲的開發和維護更加靈活和高效。本文將深入探討Scrapy-Redis的高級用法&#xff0c;幫…

python實訓——回歸類型數據挖掘任務

回歸類型數據挖掘任務 基于ARIMA和多層神經網絡模型的地鐵站點日客流量預測。有鄭州市2015年8月-11月各地鐵閘機刷卡數據集。對每日各地鐵站的客流量進行分析并進行可視化。基于上一步的分析結果&#xff0c;分別采用ARIMA模型和多層神經網絡模型對數據進行建模&#xff0c;訓…

Usage - hackthebox

簡介 靶場&#xff1a;hackmyvm 靶機&#xff1a;Usage(10.10.11.18) 難度&#xff1a;Easy 靶機鏈接:https://app.hackthebox.com/machines/Usage 攻擊機1&#xff1a;ubuntu22.04 (10.10.16.21) 攻擊機2&#xff1a;windows11(10.10.14.33) 掃描 nmap起手 nmap -sT …

Centos7.9環境下keepalived結合nginx實現負載均衡的高可用(親測版)

目錄 一、負載均衡高可用解釋 二、安裝 三、Nginx檢查腳本創建 四、修改keepalived配置文件 一、負載均衡高可用解釋 nginx 作為負載均衡器&#xff0c;所有請求都到了nginx&#xff0c;如果nginx服務器宕機后端web服務將無法提供服務&#xff0c;影響嚴重。這樣nginx作為負…