Challenges going faster than certain rpm

Hi community,
First I want to say, thank you for being so awesome. The library is a great project and it’s amazing to see the involvement of everyone here on the forum.

I’ve been fidgeting with simplefoc, with different drivers, controllers, sensors and both BLDCs and stepper motors. And even though I get my motors to spin and get pretty good torque out of them, there are some aspects of which I’m not sure if it’s going as well as it should. Some of the main things being that the max rpm I’m getting is a bit lower than expected, and that the movement of the motors often doesn’t seem very smooth.

First off, my current setup:

  • Raspberry Pi Pico controller
  • Drivers : BTS7960B
  • Angle sensor: AS5600
  • Motor: cheap 14pole 360KV 5010 bldc from Ali-express
  • Current sensors: 2 x ACS712 hall sensor
  • powered from 2 li-ion cells (~7.4V)

I’m currently testing with voltage control mode. I’ve also gotten both of the current sense modes to work, but results were not necessarily better.

With this setup (and basically any other I’ve tried with the same motor), it seems that there is an ‘invisible wall’ to get the motor to spin at more than ~100 rad/s in closed loop velocity control or about 150 rad/s in closed loop torque control mode. Raising values above that just makes the motor more noisy, draw a lot more current and make rad/s more unstable or even go down.

In open loop velocity mode I can get it to around 200 rad/s, by manually stepping up both the target velocity and current limit. I haven’t tried to go above that as the sweet spot for the current limit becomes smaller and smaller, so it just becomes tedious.

I don’t think the limitation is the speed of the controller; my main loop runs at a fixed 2.5 kHz (both move and foc) I’ve read at other posts here that foc should work well with at least 4 loop iterations per pole pair (or was that per pole, so 8 per pair?), meaning that purely from that aspect I think I should be able to reach ~560 rad/s (2500hz / 7pp /4 its/pp * 2 * pi), so that’s quite a way to go from 100 or 150.

I think one issue I have is that the magnet is not perfectly aligned to the sensor. At low rpm there is a notable ‘pattern’ of different sounds and variation in velocity over the course of a motor rotation. However I don’t think this is the issue as with higher rpm the motor is actually running smoother and smoother.

So, 1: is there anything obvious that I should be doing differently?
And 2: could this possibly be an effect of the delay in the read-out of sensor values? I haven’t calculated or measured the actual delay, but as this is generally the limiting factor in loop iteration speed, I think it’s save to assume that by far most of the iteration time is spend in waiting for I2C communication back and forth, meaning that the sensor value was actually sensed around half of the iteration time earlier, let’s say 200uS in my case, by the time by the voltages are adjusted based on that reading. I would imagine that at low rpm this delay is no problem as it is only as small fraction of the time between two passing poles, but this fraction of course grows as rpm goes up and therefore time between passing poles reduces. Is there any compensation for this delay in the library?

That’s a bit pushing it. You may get better results if you go for SPI AS5047 sensor. AS5600 is a simple digital potentiometer (100RPM max), I’m surprised you got that far on it.

PS The AS5600 does not even list the RPM in the specs, it’s that low.

Hey @daniel,

Yes, your MCU is fast and you can have loop times even much higher than 2.5kHz if you use a different sensor. I2C based AS5600 is really not meant for high speeds or fast loop times. But even an ideal sensor you will still not be able to come to 500rad/s.

The open-loop control is really really fast. cca 10x-100x faster then the FOC and it makes a lot of difference.

Velocity control algorithm takes longer time to execute and that is probably the reason why you see such a high difference. Try using the motion downsampling to let more time to the FOC algorithm and less for your motion control as your task for the moment is very simple. With some motion downsampling you should be able to reach at least this 150rad/s.

You are using a timer?

There is part of the equation that influences a lot and that is power supply voltage. As when going faster your back-emf rises motor’s torque fill diminish and it will not be able to reach higher speeds. So one simple way you can increase your speed range would be to use a higher power-supply voltage.

We do not do the compensation of the delay in the library in this way. We do have the adaptive time sampling which will help in motion control applications but not in the field vector setting. There is something that you can do if you are motivated though, we do use the motor.zero_electric_angle which basically determines the calibrated offset in between sensor and the motor 0 angles. For higher velocities (due to this delay you are interested in) we will have a certain offset in between where the motor is and where we this it is which will depend on velocity.
So you could add your estimation of the offset to the motor.zero_electric_angle:

You could do something like this in your loop

float normal_zero_angle = motor.zero_electric_angle;
float alpha = 0.1; // something small - this is up to you to find depending on your motor and sensor
void loop (){
   motor.zero_electric_angle=normal_zero_angle + alpha*motor.shaft_velocty;
   ...
   motor.loopFOC();
   motor.move();
}
1 Like

Thank you for your responses! I was not aware that there was anything like an RPM-limitation on the AS5600. I’ve just received some AS5048A boards, hopefully I’ll manage to print a mount for it this weekend and dome so testing with it. I’ll report back on the results!

I don’t necessarily need to reach 500 rad/sec, I think that for my application (biped robot legs with probably 1:9 reduction) should be okay with 200 rad/sec, maybe even less. I’m just very curious what the limiting factors are (and of course, getting higher speeds is more satisfying :wink: )

I had assumed the difference between velocity control and torque control would mostly be caused by poor PID/filter tuning. I will experiment more with downsampling as well. I have been testing with higher supply voltages (adding extra li-ion cells). I mostly got higher torques but I don’t think I got much higher speeds. Also with a simple ESC and two cells these motors reach much higher RPMs, so I didn’t think voltage should be the limiting factor. But perhaps speed on an ESC and with FOC is completely incomparable?

For now I’ve simply limited the loop iteration speed with a simple timing check (and tested that the slowest loop iterations finish well in time of course). I might play around with some interrupt-based options later.

I realize that indeed much higher speeds can be achieved with open-loop control, given it’s simplicity and non-dependence on slow sensors and noisy values etc. But with zero ability to deal with disturbances of course. Is it expected though that I need to manually adjust the current limit corresponding to the speed? IE, at low speeds it doesn’t work with a high current limit so I need to start out with low current limits, then as speed is going up it doesn’t work anymore with the low current limits so I need to crank up the limits.

I was already sniffing around in the library code to find a good spot to experiment with some sort of timing compensation based on current velocity, but your suggestion to adjust the zero_electric_angle is much simpler! I will definitely play around a bit with that :slight_smile:

Thanks again for your thoughts!

I tried with the AS5048 and it was a difference like day and night! The motor is running super smooth at both low as well as higher rpm and effortlessly reaches velocities well above 200 rad/s! I sort of feel like I’ve just been wasting a lot of time with the 5600 :stuck_out_tongue: although I do wonder if the difference is fully caused by the sensor itself, or if perhaps the alligment also plays a role, since the 5600 are not even centered on their boards properly (its the cheap white boards from Ali Express) and the cogging and alignment tests reported error distributions in the range of 30 degrees with the as5600 (after I got it to work, see https://github.com/simplefoc/Arduino-FOC/pull/157).

I did notice something weird with the 5048 though. In the sensor test example sketch it just worked flawlessly, but with I my own code it didn’t work. Somehow it gets a very low clock rate (~2kbps), at which it seems the chip just refuses to respond. After a lot of trial and error I noticed one difference between the senor test example and my code : the sensor test example uses the as5147 default config and my code used the as5048 default config. This turned out to be the key: when I changed my code to use the 5147 default config it immediately worked. I’m super puzzled over this though, because as far as I understand from checking out MagneticSensorSPI.cpp, the as5048 default config is literally a copy of the as5147 default config. They do clearly behave differently though, so there must be something going on.

It’s really mainly the I2C interface vs. SPI. That makes a huge speed difference, which is what is allowing you to reach higher RPMs. But it is certainly also true that the 10 vs 14 bits and the quality of the sensor PCB help get smoother, better results.

Thanks a lot for reporting the problems with the MagneticSensorSPI class. I’ve made an issue and we’ll investigate it.
In the meantime you could also try the MagneticSensorAS5048A class from our drivers repository. This is specific to the AS5048A, and also lets you read out the magnitude and diagnostic registers which you can’t get with the generic MagneticSensorSPI class.

1 Like

Hello !!

I’ve been able to successfully run full FOC on the G431B-ESC board with an SPI magnetic sensor (AS5047) a 1 MHz thanks to all the great tips I could find on this forum (B-G431B-ESC1: Beginner guide + I2C guide - #91 by Grizzly). Motor is 100W AT2303 1500KV.

While operation at low RPMs runs smoothly after tuning, I can’t achieve speeds beyond 750 rad/s. As I increase velocity, current draw also increases as expected (all below 1A). Going beyond 600-800 rad/s, the current suddenly jumps to the power source limit (3A), the motor becomes very noisy and the reported speed actually decreases (and doesn’t reach the target).

For my application, I would need to go to 1500 rad/s. Is there any tips you would have to reach that? I am open to any changes, going open loop at high speed or even switching to 6 step control etc…

Have you tried voltage mode with lag compensation ?
What is your power supply voltage ?
At what voltage target are you having problems ?

Hey, 1500rad/s is pretty fast for FOC control. You’ll have to push the MCU to its limits :slight_smile:

How many pole pairs is this motor? Probably 7?

Could you add some timing to estimate your main loop speed (like, count the number of iterations in one second).

One thing you could try is to activate the CORDIC for the trig calculations, this should give a small speed increase.

Thanks for the super fast reply!

Trying Torquemode:voltage does not let me go beyond ~230rad/s. I will try the lag compensation now.

Power supply: 12V
I’ve been doing all my tests in velocity feedback, as such I don’t have voltage targets. I’ll update soon on that.

Here you can see the jump around 750 rad/s when I control in current (FOC current mode):

@runger, that’s what I figured looking through the comments, but it still seemed possible? I will try your suggestions, thank you so much! Yes, the motor has 7PP. Any chance that switching to 6 step control at higher speed could be a solution?
EDIT: the main loop runs at 7 kHz quite consistently across speeds. In voltage mode I get to 7.8 kHz.

Some quick thoughts:

7PP at 750rad/s is about 836 electrical revolutions per second. You will need several loopFOC iterations per e-rev to correctly commutate the motor. So I think it is reasonable to assume you are hitting a MCU speed limit, and improving the loop time will allow you to go faster.

Some things to speed up the loop:

  • reduce the calls to move using motor.motion_downsample
  • if you can use the sensor’s ABZ interface, and STM32HWEncoder from our drivers library, you can reduce the MCU overhead for the sensing, and reduce the sensor lag
  • use the MCU’s CORDIC functions to accelerate sine/cosine calculations

On the motor driving side:

  • @Candas1 suggestions to check the BEMF and lag compensation are excellent
  • You can try SVPWM vs Sine modulation, sometimes SVPWM has more torque
  • You can try adjusting the motor.voltage_limit to increase torque
  • The dev branch of the library has a faster version of the SVPWM code
2 Likes

And maybe use the dev branch :sweat_smile:

1 Like

Thanks for the great suggestions! I’ve been busy trying this out, here is the outcome:
Before any changes, I could reach reliably 600 rad/s (on release 2.3.0).

  • motor.motion_downsample = 3, improved loop frequency from 7kHz to 7.8kHz, reliable 700 rad/s
  • disable monitoring, increase loop frequency by another 0.2 kHz, reliable to 750 rad/s
  • Update to release 2.3.1, actually reduced loop frequency by 0.4 kHz
  • Switching to SVPWM, I couldn’t see any improvements with this, MCU gives up above 700 rad/s.
  • L=0.0001, tuning the inductance value did help, as it made using the voltage mode work much better, which runs at 8.7 kHz. I managed 850 rad/s.

I haven’t tried the CORDIC functions, could you point me to any resources that might show me how to enable that?

Were you using the recent release 2.3.1 already ?
The dev branch only has a SVPWM implementation that should be faster than SVPWM with 2.3.1.

No, I upgraded from 2.3.0 to 2.3.1, since I saw that it was very recent I assumed it would have the faster SVPWM. I’ll give the dev branch a try now

This delay between the info of the sensor coming in and then the electrical angle being set is a significant thing for sure, and not just due to I2C communication times. I used PWM and analog and it was still an issue just due to code execution time, because it affects motor timing. The main point of FOC is to get the motor timing nearly optimal at all times, and that is where most of the value comes from . But when your sensor readings are stale you are never going to get good motor timing, thus you get poor efficiency and torque without realizing what you are missing. I think this is happening a lot to people without being noticed.

This appproach of compensating the zero offset linearly with RPM is what I did and it helped a lot.

It is one legit reason in some cases for these apparent RPM ceilings, but I am sure there are other reasons as well.

It’s worth noting that the M0 core of an STM32 processor operating at 64 MHZ got 7khz loop frequency in open loop i.e. basically just outputting the 3 phase sine wave, and the M4 core, which has floating point hardware, of the b-G431b-esc1 board at 168 Mhz clock speed got 37.5 Mhz. So it’s way way faster.

Ralph, can you be sure to post your code and stuff at some point? I am a fan of this microcontroller board and you seem to be getting pretty good results so I think it would be good to share and encourage adoption of this board.

My only complaint is that the g431 processor that other board uses is clearly superior for motor drivers per se… I still think it’s more advisable to have a single actual flagship board with power stage and everything, because motor drivers are a special case, rolling your own isn’t really advisable and the requirements are not amenable to a general purpose board like the pico being used.

1 Like

I have discussed this topic with @dekutree64 offline.
FOC would need to extrapolate the position based on a delay/lag.
That’s one of the effects I wanted to highlight in this post.
image
It’s much more visible on slow hardware and as the speed increases of course.

One workaround in voltage mode could be to use the lag compensation and tweak the phase_inductance to also compensate for the delay.

Hi all,

Small report from my side, I am now managing to get 1000 rad/s. Things that I did to get there:

  • Increase SPI frequency to 10 MHz. This was by far what led to the biggest speed gain. My main loop went from 7kHz to 11.1kHz
  • Running in SVPWM in Voltage mode with the Dev branch. This was the most reliable and also provided the least current draw at given speed, once the inductance value was properly tuned. Got me to main loop speeds of 13.3 kHz.
  • Motion Downsample = 3

All this makes me think that my sensor is what is limiting the max speed. I actually have the AS5048A in SPI (not the AS5047 as indicated earlier). Would it make sense to switch to the faster AS5047P sensor or an ABI sensor instead? I could order one and test.

Here is my code:

#include <Arduino.h>
#include <SimpleFOC.h>
#include <pinmap_custom.h>

// MagneticSensorSPI(int cs, float _cpr, int _angle_register)
MagneticSensorSPI sensor = MagneticSensorSPI(PA15, 14, 0x3FFF);
SPIClass SPI_3(PB5, PB4, PB3); //(mosi, miso, sclk)

// BLDC motor & driver instance
BLDCMotor motor = BLDCMotor(7);
BLDCDriver6PWM driver = BLDCDriver6PWM(A_PHASE_UH, A_PHASE_UL, A_PHASE_VH, A_PHASE_VL, A_PHASE_WH, A_PHASE_WL);

// current sensor
LowsideCurrentSense current_sense = LowsideCurrentSense(0.003, -64.0 / 7.0, A_OP1_OUT, A_OP2_OUT, A_OP3_OUT);

// Commander interface constructor
Commander command = Commander(Serial);

void doTarget(char* cmd) { command.scalar(&motor.target, cmd); }
void doMotor(char* cmd){ command.motor(&motor,cmd); }

void setup(){
	pinMode(LED_BUILTIN, OUTPUT);

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

	sensor.init(&SPI_3);
	motor.linkSensor(&sensor);

	// driver config
	driver.voltage_power_supply = 15;
	driver.init();

	// link the driver to the current sense
 	current_sense.linkDriver(&driver);

	// link the motor and the driver
	motor.linkDriver(&driver);

	// limiting motor movements
	motor.phase_resistance = 0.251; // [Ohm]
	motor.phase_inductance = 0.0001; // [Ohm]
	motor.foc_modulation = FOCModulationType::SpaceVectorPWM;
	motor.current_limit = 10;   // [Amps] - if phase resistance defined
	// motor.voltage_limit = 1;   // [V] - if phase resistance not defined
	motor.velocity_limit = 100; // [rad/s] cca 50rpm


	motor.voltage_sensor_align = 1;

	// set torque mode to be used
	motor.torque_controller = TorqueControlType::voltage;   // ( default )
	// motor.torque_controller = TorqueControlType::dc_current;
	// motor.torque_controller = TorqueControlType::foc_current;

	// set motion control loop to be used
	// motor.controller = MotionControlType::torque; //      - torque control 
	motor.controller = MotionControlType::velocity; //    - velocity motion control
	// motor.controller = MotionControlType::angle; //       - position/angle motion control
	// motor.controller = MotionControlType::velocity_openloop; //    - velocity open-loop control
	// motor.controller = MotionControlType::angle_openloop; //       - position open-loop control

	motor.motion_downsample = 3.0; //gives us 10% speed range.

	motor.useMonitoring(Serial);
	motor.monitor_downsample = 100; // set downsampling can be even more > 100
  	motor.monitor_variables =  _MON_VEL;// _MON_CURR_Q | _MON_CURR_D; // set monitoring of d and q currents _MON_TARGET | _MON_VEL | _MON_ANGLE |
	motor.monitor_decimals = 2; //!< monitor outputs decimal places


	motor.PID_velocity.P = 0.04;
	motor.PID_velocity.I = 1;
	motor.PID_velocity.D = 0;
	motor.PID_velocity.output_ramp = 1000;
	motor.LPF_velocity.Tf = 0.005;
	motor.PID_current_q.P = 0.5;
	motor.P_angle.P = 60.0;

	// init motor hardware
	motor.init();

	// current sense init hardware
	current_sense.init();
	// link the current sense to the motor
	motor.linkCurrentSense(&current_sense);

	  // align sensor and start FOC
	motor.initFOC();

	// set the initial motor target
	motor.target = 0; // unit depends on control mode 

	// add target command T
	command.add('T', doTarget, "target angle");
	command.add('M',doMotor,"my motor motion");
	_delay(100);
}

void loop(){

	static int i = 0;
	static int millis_prev = 0;
	i ++;

	if(millis()-millis_prev>1000){
		Serial.print("Main frq: "); Serial.print(i);
		i=0;
		millis_prev = millis();
	}
	digitalWrite(LED_BUILTIN, millis()%2000>1000);

	motor.loopFOC();
	motor.move();
	motor.monitor();
	command.run();
}
2 Likes

I think that you could get even higher speeds if you change your hardware a little and use some STM32 board with the STM32HWEncoder sensor type, with a magnetic encoder that has ABZ output.

I can’t imagine that at 1000rad/s you are doing angle control, so a quadrature encoder will give you even lower overhead for reading current position (at lack of absolute position). It also does not require interrupts as the encoder is implemented as a ‘silicon/hardware peripheral’ so all you have to do is read a register → very quick sensor update rate. It doesn’t require any managed code to track the position.

Using ABZ with interrupts will be a non-starter at these speeds - your MCU will be totally overwhelmed with that kind of interrupt load.

1 Like