BLDC motor contoller development - question about the FOCcurrent mode and motor.disable() function

So the BLDC motor contoller for the Escooter, that i am trying to develop is slowly getting where i want it to be. The current state of the project - open loop works great, and right now i am testing closed loop but without current sensing just yet.

So i have 2 questions that i wanted to ask before i hook up the current sensors.

  1. For position sensing i am using hall sensors. So far i tested the velocity controll and
    torque controll - voltage mode. The code works great, but becouse of the hall sensors being located inside of the motor, they pick up quite a lot of noise. I used a Low Pass filter to filter some of the noise. After filtering, the output signals from the sensors are enough to run the
    torque - voltage mode very smoothly. Unfortunately it is not enough to run the velocity loop. The velocity loop also works but the rotation is much less smooth. Obviously, when using velocity loop, a very precise reading of the velocity is needed.

Now my question is:
When i hook up the current sensors and try the FOCcurrent mode, how crucial for the proper work of the motor are the signals from the hall sensors?

I am asking this becouse i am not sure, what should i expect after using current sensing (assuming the current sensors will work well). Should i give more care to filter the signals from the hall sensors? Or becouse the signals are enough for torque - voltage mode, they will also be enough to run
torque-FOCcurrent mode. In my mind better signals should provide better feedback and better motor rotation. That’s why i am thinking about implementing an active LP filter. This will allow setting the cut-off frequency lower and the high impedance of the amplifier, should help filter more noise. But i am not sure if there is sense in doing it. Maybe it’s just not that significant when using current sensing and FOCcurrent mode.

  1. The second question is about the motor.disable() function.
    The escooter controller is going to work with a trigger-like potentiometer. So i dont want the motor to stop and the scooter to flip, with me riding on it, when i let off the “gas” :smiley: . I wanted to use the motor.disable() function. I dont have the full code yet to support this functionality, but i am using this function to disable the motor when the target_velocity that i adjust with the potentiometer, gets below a specific value.
    The problem is that that the motor.disable() command gets into some conflict with rest the of the code, when used in closed loop. When i use this command in open loop, everything works the way it should. When i use it in closed loop, both, velocity and torque - voltage mode, the code works and the motor disables when at low speed, but the motor rotation gets way less smooth than without using the motor.disable() function. I am not sure if i am using it the correct way, but i am using it the very same way in open loop and it works flawlesly there.

So the second question is, how to use the motor.disable() function/command the proper way? Or maybe a different function to achieve freewheeling.
I’m using the nucleo32 G431KB
My code:

#include <SimpleFOC.h>

//#define PIN_SERIAL_RX PA3
//#define PIN_SERIAL_TX PA2

//HardwareSerial Serial1(PA3, PA2);

float target_velocity;

int analog_read_A0;

// Hall sensor instance
// HallSensor(int hallA, int hallB , int hallC , int pp)
//  - hallA, hallB, hallC    - HallSensor A, B and C pins
//  - pp                     - pole pairs
BLDCMotor motor = BLDCMotor(15);

HallSensor sensor =  HallSensor(PB7, PA15, PB6, 15);

BLDCDriver6PWM driver = BLDCDriver6PWM(PA9, PB0, PA10, PF0, PA8, PA7);

// Interrupt routine initialization
// channel A and B callbacks
void doA(){sensor.handleA();}
void doB(){sensor.handleB();}
void doC(){sensor.handleC();}

//Commander command = Commander(Serial);
//void onTarget(char* cmd) { command.target(&motor, cmd); }
//void onMotor(char* cmd)  { command.motor(&motor, cmd); }

void setup() {

  
  // monitoring port
  //Serial1.begin(115200);

  // check if you need internal pullups
  sensor.pullup = Pullup::USE_EXTERN;
  
  // initialize sensor hardware
  sensor.init();
  // hardware interrupt enable
  sensor.enableInterrupts(doA, doB, doC);
  sensor.velocity_max = 61;  
  
  motor.linkSensor(&sensor);
   


  //Serial.println("Sensor ready");
  _delay(1000);

  driver.voltage_power_supply = 12.5;
  driver.pwm_frequency = 25000;
  driver.dead_zone = 0.01;
  
  driver.init();
  //Serial.print("Driver init ");// init driver
  //if (driver.init()) 
  //Serial.println("success!");
  //else{Serial.println("failed!");
  //return;}
  motor.linkDriver(&driver);
  
  motor.voltage_sensor_align = 1;
  
  motor.velocity_limit = 1000; // [rad/s] cca 50rpm
  motor.phase_resistance = 0.57; // [Ohm]
  //motor.KV_rating = 87;
  motor.current_limit = 10;
  //motor.voltage_limit = 20;
  motor.torque_controller = TorqueControlType::voltage;
  // set motion control loop to be used
  motor.controller = MotionControlType::torque;
  //motor.controller = MotionControlType::velocity;
  motor.PID_velocity.P = 0.1;
  motor.PID_velocity.I = 15;
  //motor.PID_velocity.D = 0.005;
  motor.PID_velocity.output_ramp = 200;  
  //default voltage_power_supply
  // velocity low pass filtering
  // default 5ms - try different values to see what is the best. 
  // the lower the less filtered
  motor.LPF_velocity.Tf = 0.18;
  motor.init();
  motor.initFOC();
  //Serial.println("Motor ready.");

  //command.add('T', onTarget, "target velocity");
  //command.add('M', onMotor,  "motor");
  //command.motor(&motor,"E0");
  _delay(1000);
}


void loop() {
  sensor.update(); 
  motor.loopFOC(); 
  analog_read_A0 = analogRead(PA1);
  target_velocity = float(map(analog_read_A0, 0, 4096, 0, 10000))/60.0;

  if (target_velocity >= 1.0f)
  motor.enable();
  else
  motor.disable();
  
  motor.move(target_velocity);
  //Serial1.println(sensor.getVelocity());
}

In the end i wanted to say, big Thank You to the SimpleFOC Crew for their work, and all the help i got so far with my project!!!

1 Like

There seems to be quite an avalanche of uncertainty and potential issues and assumptions that may not be valid. I haven’t tried the current sensing much but I think it needs at least two phases, or I don’t see much point. There is only one in your code. The issues with the velocity mode could be PI controller tuning issues. I think if torque mode gives smooth motion velocity mode would too if properly tuned.

As an aside, this does not seem like an advisable undertaking, to actually power a scooter that you are going to ride with a driver built with minimal time invested in the project… if you are going to do a good job on this it’s going to take a while to make sure it’s safe etc.

Hey, it sounds like you’ve made quite some progress :slight_smile:

Unfortunately they’re still quite crucial, as the sensor (hall or otherwise) is used to determine the current rotor position, which the FOC algorithm needs to be able to align the stator’s field at 90° to this…

Is there a possibility to use sensors other than Hall-Sensors? If not, then making sure they give a good signal is probably a good idea.

This is how I would have done it. The assumption being that the driver hardware closes all FETs when you use the disable().
Are you using 6-PWM control? Is it hardware or software based dead-time?

In your code, please remove the sensor.update(); in the main loop. motor.move()/motor.loopFOC() already do the sensor update.

On a G431 MCU you may also want to set the motor.motion_downsample to a value like 4 to 10, which means move() will run less frequently compared to loopFOC(). You might also want to downsample the analogRead(), you don’t need to call this once per loop iteration I don’t think.
All this may help to make the performance a bit more smooth.

1 Like

Try increasing the velocity low-pass filter constant. I use motor.LPF_velocity.Tf = 0.05f with hall sensors and get fairly stable velocity readings.

There can also be occasional “blips” in the hall sensor readings due to lack of interrupt-safe code. I’m going to be giving it an overhaul in the coming days, so it should work better after that, and we may even be able to use my SmoothingSensor class to get high resolution position data for current control.

1 Like

@Lambert14 I worked with hoverboard motor and halls a few years back and may have had similar issues w.r.t noise.

Symptoms were hall interrupts worked nicely with motor off but fired far too much with motor on.

From memory, i think I initially had pullups on the hall sensors that we’re two weak (2k-10k) and found that super strong pull-ups (1k or a bit less) helped. I think i pushed a 1k resistor between hall and 5/3.3v on each hall’s connector.

2 Likes

So i wanted to make a small update on the problem if someone would be interested. I finally managed to get satisfying signals from the hall sensors.

@Anthony_Douglas thanks for the concern, i am trying to stick to good engineering practices and won’t be using it in unsafe situations until sure it works well. But still a long way to go.
@Owen_Williams thank you for the tip. At the time i was writting this post i actually also found out that strong pull ups work much better. Right now i run 0.9K pull ups (10k preinstalled in the sensor and aditional 1k on the board). When running 5k pull ups and higher, the signals were going completely kahoot.

But the thing that did the magic for me, was a combination of a Low Pass Filter, and a resistor between the filter output and the Nucleo input pin.
I tested multiple LPF and 2nd order LPF configurations and i have to say, that from my point of view, the theory didn’t match the way it actually worked in practice
(unless i misunderstood the theory :sweat_smile:). Also, many design schematics that i was able to find, were setting the cut-off frequency extremally low. For example at around 600Hz or less. While the signals from the Hall sensors, were at max motor speed, at around 200-300Hz.
So at first glance it would make sense, but in my case, setting the cut-off frequency that low, was worse than setting it much, much higher. In my opinion that was because such low cut-off frequency, introduces a phase shift in the signals from much lower frequencies of the signal, so the actuall good hall signals are also shiftted a few degrees in the phases. Furthermore it also introduced a slower
step-response. Although i am not completely sure if that was the problem. If someone has an idea i would love to hear it, becouse i was surprised how many designes from what i was able to find, filtered the signals in a way that completely didn’t work for me.

So right now i am running 10nF 100Ohm basic LPF and 1kOhm resistor in between the filter and mcu input. The cut off freq is relatively high set for hall sensors at 159154 Hz. Resistance on the filter is also relatively low, comparing to other designs that i saw. Generally speaking rather lower capacitances
1-10nF and lower resistances 51-330Ohm worked best in my case. But the game changer was adding the resistor behind the filter. Weird, becouse a 2nd filter after the first one (2nd order LPF) 1kOhm and 1nF made the situtation worse. Im not sure if this filter is optimal but it worked best for me so far. It filtered 99% of the noise.
Finally time to hook-up current sensing.

@runger Unfortunately i wasn’t yet able to deal with the problem of a very weird noise, while using the motor.disable() in the code. After fixing the hall signals i can say that the code for sure works and it disables when at low speed, and the smoothnes of the motor and torque is also very good. But the noise is very noticeable, like a soud of nails on a chalkboard but higher pitch ;D. I downsampled the motor motion function and deleted the sensor.update(), and it got a littlebit better. I have to teach myself now how to downsample the analogRead(), from what i was able to find, i have to configure the ADC, correct? I am using 6-PWM controll. I have a software deadtime set and also an internal dead time on the halfbridges, 540ns.
Can the problem be the fact that i dont have the Enable pin in use? If i used the Enable pin, i should be able to send signal from nucleo back to that enable pin and disable/enable the motor. Correct me if i’m wrong please ;D.

Yes, you should have the enable pin connected if possible. Then the call to disable() can switch off the driver, making sure that there is no power to the motor. Then there can also be no noise.

If you call disable() without the driver enable pin, then it depends on whether you have 6-PWM or 3-PWM control. With 6-PWM control disable() should put the phases into High-Z mode.
With 3-PWM control, it depends on whether you have EN-pins set or not.