FOC Limited Speed reaching problem

Hello,

I hope you are doing well.

I work as an embedded systems engineer, focusing on developing software for client product firmware. Recently, I encountered a client project where one part involved developing software to control a three-phase BLDC motor equipped with integrated Hall sensors in the stator, powered by 24V and featuring 20 poles (10 pole pairs).

In this project, I developed an algorithm on an STM32G431KBT6 development board using the FOC (Field-Oriented Control) strategy. I implemented the algorithm with the SimpleFOC library in closed-loop mode to achieve velocity control, using space vector PWM and torque control through voltage modulation. If needed, I can provide further details about the code, which was developed in the Arduino IDE.

The results were promising in terms of speed, torque, and current consumption. According to the project’s requirements, the motor was expected to operate between 3000 RPM and 8000 RPM. While the motor performed well at all lower speeds, we observed that it could not exceed 6300 RPM, even though it should have reached the desired 8000 RPM.

Interestingly, when we tested the same motor with a simpler control strategy, such as servo control or a 1-PWM control mode, it reached 8000 RPM without any issues, which confirms that the motor is capable of achieving the target speed.

In terms of microcontroller performance, the STM32G431KBT6 is powerful enough to run the algorithm efficiently, given its high processing capability. I also double-checked the hardware connections, and everything appears to be properly connected.

As part of the debugging process, I tried adjusting parameters such as voltage_limit, voltage_power_supply, pwm_frequency, and output_ramp, but that did not solve the issue. The only modification that had an effect was increasing the voltage from 24V to 30V, which allowed the motor to reach 7000 RPM. However, this is not a viable solution, as the system is required to operate at 24V.

After further analysis, I suspect that the issue may be related to the internal behavior of the FOC method, particularly with the way vector variables are managed.

If you have any insights or suggestions on how to address this problem, or if you can provide guidance on the next steps to take, it would be greatly appreciated.

Thank you very much for your time and support. I am at your disposal if you need any additional information.

#include <SimpleFOC.h>
#include <SPI.h>
#include <stm32g4xx_hal.h>
#define   _MON_TARGET 0b1000000  // monitor target value
#define   _MON_VOLT_Q 0b0100000  // monitor voltage q value
#define   _MON_VOLT_D 0b0010000  // monitor voltage d value
#define   _MON_CURR_Q 0b0001000  // monitor current q value - if measured
#define   _MON_CURR_D 0b0000100  // monitor current d value - if measured
#define   _MON_VEL    0b0000010  // monitor velocity value
#define   _MON_ANGLE  0b0000001  // monitor angle value
ADC_HandleTypeDef hadc1;
ADC_HandleTypeDef hadc2;
I2C_HandleTypeDef hi2c1;
SPI_HandleTypeDef hspi1;
TIM_HandleTypeDef htim1;
TIM_HandleTypeDef htim2;
TIM_HandleTypeDef htim3;

UART_HandleTypeDef huart2;
//-----------------------------------------------------------------END SPI PART
//stm32 defines
/* Private defines -----------------------------------------------------------*/
#define ADC_NTCMotorTemp_Pin GPIO_PIN_0
#define ADC_NTCMotorTemp_GPIO_Port GPIOF
#define ADC_Voltage_Pin GPIO_PIN_1
#define ADC_Voltage_GPIO_Port GPIOF
#define NRST_Pin GPIO_PIN_10
#define NRST_GPIO_Port GPIOG
#define INHC_Pin GPIO_PIN_0
#define INHC_GPIO_Port GPIOA
#define ADC_Pressure_Pin GPIO_PIN_1
#define ADC_Pressure_GPIO_Port GPIOA
#define UART_TX_NRF52_Pin GPIO_PIN_2
#define UART_TX_NRF52_GPIO_Port GPIOA
#define UART_RX_NRF52_Pin GPIO_PIN_3
#define UART_RX_NRF52_GPIO_Port GPIOA
#define ADC_BoardTemp_Pin GPIO_PIN_0
#define ADC_BoardTemp_GPIO_Port GPIOB
#define INHA_Pin GPIO_PIN_8
#define INHA_GPIO_Port GPIOA
#define INLA_Pin GPIO_PIN_9
#define INLA_GPIO_Port GPIOA
#define HALLB_Pin GPIO_PIN_10
#define HALLB_GPIO_Port GPIOA
#define HALLA_Pin GPIO_PIN_11
#define HALLA_GPIO_Port GPIOA
#define HALLC_Pin GPIO_PIN_12
#define HALLC_GPIO_Port GPIOA
#define INLC_Pin GPIO_PIN_3
#define INLC_GPIO_Port GPIOB
#define INHB_Pin GPIO_PIN_4
#define INHB_GPIO_Port GPIOB
#define INLB_Pin GPIO_PIN_5
#define INLB_GPIO_Port GPIOB
#define ENABLE_Pin GPIO_PIN_6
#define ENABLE_GPIO_Port GPIOB
#define BOOT0_Pin GPIO_PIN_8
#define BOOT0_GPIO_Port GPIOB
#define HALLA PA11
#define HALLB PA10
#define HALLC PA12   //PA11 //NEW PB11
#define INHA  PA8    //TIM2 CH3 PA2  -----//new- //TIM1 CH1 PA8                          //pb6
#define INHB  PB4   //TIM4 CH1  PB6 //TIM1 CH4 PA11  ----- //new- //TIM1 CH2 PA9
#define INLA  PA9  //TIM2 CH4 PA3   //new- //TIM1 CH1N PA7                                 //PB7
#define INLB  PB5  //TIM4 CH2  PB7//TIM1 CH2 PA9   -----//new- //TIM1 CH2N  PB0
#define INHC  PA0  //TIM4 CH2  PB7//TIM1 CH2 PA9   -----//new- //TIM1 CH2N  PB0
#define INLC  PB3 //TIM4 CH4  PB9 //TIM1 CH1 PA8 ---- //new- //TIM1 CH3N PB1
#define ENABLEIO PB6
float rpm = 0;
float rpm_pot = 0;
float needReinitialize = 0;
float zero_electrical_angle_eeprom = 0;
float sensor_direction_eeprom = 0;
unsigned long previousMillis = 0; // Store last time RPM was updated
const long interval = 2000; // Interval at which to update RPM (milliseconds) //250
HallSensor sensor = HallSensor(HALLA, HALLB, HALLC, 10);// U V W Pole Pairs
void doA(){sensor.handleA();}
void doB(){sensor.handleB();}
void doC(){sensor.handleC();}

//Motor parameters: Set the number of pole pairs according to the motor
BLDCMotor motor = BLDCMotor(10);
BLDCDriver6PWM driver = BLDCDriver6PWM(INHA,INLA,INHB,INLB,INHC,INLC,ENABLEIO);
bool flag_run = false;
float target_velocity = (rpm * (2.0 * PI))/(60.0);
Commander command = Commander(Serial);
void doTarget(char* cmd) { command.scalar(&rpm, cmd); }
void doTarget2(char* cmd) { command.scalar(&needReinitialize, cmd); }

void calcKV() { 
  // calculate the KV
  float rpm1 = (motor.shaft_velocity* 60.0 )/(2.0 * PI);
  float Target_rpm = (target_velocity* 60.0 )/(2.0 * PI);
  Serial.print(rpm1);      //the first variable for plotting
  Serial.print(",");       //seperator
  Serial.println(rpm);
}


//---------------------------------------------
void setup() {

    Serial.begin(115200);
    HAL_Init();

    sensor.init();
    sensor.enableInterrupts(doA, doB, doC);
    motor.linkSensor(&sensor);
    motor.foc_modulation = FOCModulationType::SpaceVectorPWM;
    motor.torque_controller = TorqueControlType::voltage;
    driver.pwm_frequency = 20000;  
    driver.voltage_power_supply = 24;
    driver.voltage_limit = 24;
    driver.init();
    driver.enable();
    motor.linkDriver(&driver);
    motor.motion_downsample = 3;
    motor.voltage_sensor_align = 3;
    motor.velocity_index_search = 0.1;
    motor.controller = MotionControlType::velocity;
    motor.PID_velocity.P = 0.01; 
    motor.PID_velocity.I = 0.1;
    motor.PID_velocity.D = 0.00;
    motor.P_angle.P = 20;
    motor.voltage_limit = 24;
    motor.PID_velocity.output_ramp = 1000;
    motor.LPF_velocity.Tf = 0.01f;
    motor.velocity_limit = 45;
    motor.init();
    motor.initFOC();
    command.add('T', doTarget, "target velocity");
    Serial.println(F("Motor ready."));
    Serial.println(F("Set the target velocity using serial terminal:"));


}

void loop() 
{        
        
                  motor.loopFOC();
                  motor.move((rpm * (2.0 * PI)) / (60.0));  
                  command.run();
            
                  

}

Welcome to the community.

Remove that line. Your RPM will increase.

For even higher RPM use one of the STM32H7 series. Also, increase the PWM frequency to at least 40000 khz.

It would be helpful to post a picture of the setup and driver, as well as driver specs such as mosfet driver, mosfets, etc. Some issues may not be code related but hardware design problems.

Cheers,
Valentine

This will use doubles because you don’t specify the constants as float.
Which means it won’t use the FPU as it handles only single precision (without f suffix and with f suffix)
This can slow down the code.

This is wrong, it should be 13.92f (24 * 0.58f) otherwise you do overmodulation.

motor.voltage_limit = 24;

I would also not use velocity control for now, only torque control.
FOC control would be faster, lag compensation will help if you cannot do FOC.

I also shared some of those tips here

Hi @Candas1 ,

Thank you for your advice.

I tried the changes you suggested, but the result remained the same.

Regarding the motor.move() function, I used this format for development purposes, and it works fine. I can send you a screenshot if needed.

However, the problem is still unresolved. As I mentioned earlier, I believe it might be related to the theoretical aspects of FOC. The issue could be connected to the current and voltage phases or possibly the flux weakening control.

Hey @Zied_Jenhani,

It’s hard to achieve such high speeds without the true FOC control. You should consider using the current sensing to have the full range of your motor velocity.
You should be able to have it with your driver board right?

The other thing that you can try is to provide the inductance and KV rating of your motor to the BLDCmotor class. With them the code will compensate for some lag and depending on the inductance values you might get some field weakening too.

But I’d still say that you’d get better results if you’d just use the current sensing and allow the code to do the true FOC control. :slight_smile:

Hi @Antun_Skuric,

I hope you are doing well.

Thank you for your solution! Adding the KV value and inductance helped significantly in increasing the speed limit for the BLDC motor.

Currently, I am using a BLDC motor equipped with integrated Hall sensors. In future prototypes, we may use current sensors instead. For now, the current setup works well, but I’ve encountered another issue.

When I tried the same approach with a different BLDC motor that has a higher KV rating and the same number of pole pairs, I noticed that at speeds between 3000 and 7000 RPM, the current consumption was significantly higher compared to the lower KV motor.

I also compared it with a servo motor I controlled earlier using a similar approach (the same KV) and the results were much more efficient in the current.

Could you advise on factors I can adjust to reduce current consumption? I assume this can be done within the FOC algorithm,using voltage control with Back-EMF estimation and lag compensation (using KV, resistance, and inductance values).

Thanks again for your help!