Sensorless BEMF reading with B-G431-ESC1

I am currently using the B-G431-ESC1 to drive a sensorless BLDC motor. I have the motor running well in an velocity open loop, but I need to run the motor in a closed loop. From my understanding I need to use BEMF as feedback but don’t know how to actually get the BEMF values and then what to do with those values afterwards. Is there a different way to do a sensorless closed velocity loop? Does anyone have any pointers as to how to get the BEMF values and what to do with them afterwards?

I have attempted to configure the ADC using the STM32 library but was unsuccessful. If that is the path to take are there any pointers there?

This is my current code:


#include <Arduino.h>
#include <SimpleFOC.h>
#include <string>
#include "stm32g4xx_hal.h"

//local variable definitions
float target = 0.0;     // used to change currently tested variable
int spinIt = 1;         // iterator for the startup
int spinItSpeed = 1500; // rate of the startup, higher = slower
float radPerSec = 0;    // driver the motor speed
int mxRads = 0;         // max motor speed
int outIt = 0;          //used for communication rate iteration

//Set up the motor and driver, motor is 7 pp, .12 phase resistor, and 1400KV 
BLDCMotor motor = BLDCMotor(7, .12, 1400);
BLDCDriver6PWM driver = BLDCDriver6PWM(A_PHASE_UH, A_PHASE_UL, A_PHASE_VH, A_PHASE_VL, A_PHASE_WH, A_PHASE_WL);

/*controls the motor using the UART. 
-g starts the motor and revs up to 300 rad/s, 
-s stops the motor, 
-and entering a number sets the P in pid to that number
  -(currently not useful, but implemented for later tuning)
*/
void serialControl(){
  static String received_chars;
  while (Serial.available())
  {
    char inChar = (char)Serial.read();
    received_chars += inChar;
    if (inChar == '\n')
    {
      float num = received_chars.toFloat();
      if(received_chars == "s\n"){
        radPerSec = 0;
        mxRads = 0;
        received_chars = "";
        Serial.println("Stopping Motor.");
        break;
      }
      else if(received_chars == "g\n"){
        radPerSec = 0;
        mxRads = 300;
        received_chars = "";
        Serial.println("Starting Motor.");
        break;
      }
      else{
        target = num;
        Serial.print("PID.P = ");
        Serial.println(target);
        received_chars = "";
      }    
    }
  }
}

void setup()
{

  //initializes the psu and driver
  driver.voltage_power_supply = 14.8;
  driver.init();

  motor.linkDriver(&driver);
  motor.voltage_limit = 14.8;
  motor.velocity_limit = 2212;

  //Sets up openloop FOC
  motor.controller = MotionControlType::velocity_openloop;
  // set FOC modulation type to sinusoidal
  motor.foc_modulation = FOCModulationType::SinePWM;


  motor.init();

  motor.initFOC();

  
  Serial.begin(115200);
  delay(1000);

}


void loop()
{
  //starts the motor moving at radPerSec
  motor.loopFOC();
  motor.move(radPerSec);
  //this loop controls the rate at which the motor speeds up
  if (spinIt%spinItSpeed == 0 && radPerSec<mxRads){
    radPerSec++;
    spinIt = 1;
  }
  else{
    spinIt++;
  }

  //motor.PID_velocity.P = target;
  
  serialControl();
}


It’s not using the BEMF sensing but maybe you can try the flux observer I ported as a simplefoc sensor, I tested it on a hoverboard motor with this example project.

I am actually working on a unified current sense driver for stm32 that let’s you sample any other pin easily, it works but I still have a list of things to do there.
Here you can see I added the BEMF pins already.
If I remember well you need to do this in the main loop to get the values:

_readADCVoltageLowSide(A_BEMF1, currentSense->params);

But I haven’t tried to use it for sensorless, you should probably calibrate the 0 offset for each phase. I am not sure if this is meant for 6 step communation only, or you can do a clarke transform and use atan2 to get an angle.

1 Like

That’s meant for 6 step, iirc. You only get an interesting value by reading the voltage of a phase when it’s half bridge is in high-z mode, otherwise it’ll just read psu voltage or ground.

OK so other than 6 step it could be interesting for tracking when the motor is not driven only.

Hello sir, I was trying to use the code that you provided for my project. I have a B-G431-ESC1 and x2216-5 2400KV BLDC motor. I adapted it to my current motor, but motor did not spin. It just created sounds. I will provide the code and the images from my osilloscope. Could you help me about it? At least i expected it to spin, however it did not.

#include <Arduino.h>
#include <SimpleFOC.h>
#include
#include “stm32g4xx_hal.h”

//local variable definitions
float target = 0.0; // used to change currently tested variable
int spinIt = 1; // iterator for the startup
int spinItSpeed = 1500; // rate of the startup, higher = slower
float radPerSec = 0; // driver the motor speed
int mxRads = 0; // max motor speed
int outIt = 0; //used for communication rate iteration
BLDCMotor motor = BLDCMotor(7, .36, 2400, 0.02);
BLDCDriver6PWM driver = BLDCDriver6PWM(A_PHASE_UH, A_PHASE_UL, A_PHASE_VH, A_PHASE_VL, A_PHASE_WH, A_PHASE_WL);

LowsideCurrentSense current_sense = LowsideCurrentSense(0.003, 9.1f, A_OP1_OUT, A_OP2_OUT, A_OP3_OUT);

void serialControl(){
static String received_chars;
while (Serial.available())
{
char inChar = (char)Serial.read();
received_chars += inChar;
if (inChar == ‘\n’)
{
float num = received_chars.toFloat();
if(received_chars == “s\n”){
radPerSec = 0;
mxRads = 0;
received_chars = “”;
Serial.println(“Stopping Motor.”);
break;
}
else if(received_chars == “g\n”){
radPerSec = 0;
mxRads = 300;
received_chars = “”;
Serial.println(“Starting Motor.”);
break;
}
else{
target = num;
Serial.print("PID.P = ");
Serial.println(target);
received_chars = “”;
}
}
}
}

void setup()
{

//initializes the psu and driver
driver.voltage_power_supply = 14.8;
driver.init();
motor.linkDriver(&driver);

current_sense.init();
current_sense.skip_align = false;
motor.linkCurrentSense(&current_sense);

motor.voltage_limit = 14.8;
motor.velocity_limit = 2212;

//Sets up openloop FOC
motor.controller = MotionControlType::velocity_openloop;
// set FOC modulation type to sinusoidal
motor.foc_modulation = FOCModulationType::SinePWM;

motor.init();
motor.initFOC();

Serial.begin(115200);
delay(1000);
mxRads = 300;
}

void loop()
{
//starts the motor moving at radPerSec
motor.loopFOC();
motor.move(radPerSec);
//this loop controls the rate at which the motor speeds up
if (spinIt%spinItSpeed == 0 && radPerSec<mxRads){
radPerSec++;
spinIt = 1;
}
else{
spinIt++;
}

serialControl();
}

With the full 14.8V applied to a 0.36 ohm motor, it’s a puzzle why it didn’t go up in smoke immediately. I’d recommend motor.voltage_limit = 1.0 or less for open loop.

For confusing reasons, the maximum motor voltage limit is half of the driver limit (or 58% with SpaceVectorPWM). So 7.4V in this case. With it set to 14.8V, the sine waves will be saturated to trapezoidal.

I have updated the code as you suggested. motor.voltage_limit = 1.0
and the driver voltage to 12.0. This is the osilloscope image. It does not spin still. I have ordered AS5600 btw. I did not research deeply, but i will try to implement it for my motor. However, firstly i need to spin that motor w/o a sensor. So, i need your help about it sir.