Stepper motor speed problems - Balancing robot

Hey, I’m pretty new to simpleFOC but I’m trying to use it to build a balancing robot!

My setup is as follows:
Nema17 Stepper motors
AS5048a encoders via SPI
L289N Drivers
ESP32 (Using newest version of library so not the pwm issue from previous)

I’ve managed to get the robot balancing with closed loop velocity control (See video). But I’m having issues when I try and move the robot or introduce disturbances as the motors seem to be quickly hitting a velocity limit at around 10 rad/s and promptly falling over.

The weird problem I’m having is that when I’m testing velocities, I seem to be able to go faster in one direction (like 20 rad/s). I’m powering the motors with a 4S Lipo and I’ve got the voltage limit set to 12V, in one direction for some reason it is ramping up to that 12V much quicker.
Has anyone else had any weird issues with stepper motors like this? Let me know if there’s any more info that might help, thanks!

1 Like

@Neil_Harrison welcome!

This may be an indication that the motor and sensor are not perfectly aligned.

The initFOC function does it’s best job at working out where sensor/motor zero_electric_offset and direction but if you have low sensor resolution or high pole count (like with a stepper) it might be out.

It is possible to skip calibration and pass your own values into the function. If you print out motor.zero_electric_offset and sensor.natural_direction after calibration, you can use these as a starting point and adjust offset by ±0.05 increments until forward/reverse speeds are more even.

That is awesome, this has been one of the things I wanted to try also, well done! :smiley:

I would suggest you the same thing as Owen suggested. Go to the examples and find the zero offset and the direction for both motors.
Once when you have it try to tune the zero_electrical_offset so that in the voltage mode your motor spins equally as fast in both directions. (You can maybe redo the example in utils few times examples/utils to get a good value.)

The other thing that you might try is to downasmple the motor.move() function. To leave more time to the motor to do actual foc algorithm. You should be able to balance your robot with sample times around 10ms. So that would probably mean you would have 10+ loopFOC() calls per one move() call. Which will get you better alignment of phase currents and higher achievable velocity.

Thank you both! I was starting to suspect that, I’d been getting varying values for the zero offset in the calibration so I’d averaged a few of them and put that in the init function. Do you guys know how precisely the sensor has to be 2mm away from the magnet? I think I might redesign the encoder mount as I’m starting to lose trust in my current one.

I tried downsampling the move function and the motors are able to go a bit faster but they start making a lot of noise at higher speeds, that might be down to the PID tuning I guess though.

Another odd thing that might be playing a part, with the L289N driver, when the battery is not plugged in, the motors can still rotate at a low speed. Should this be able to happen? When I measure the voltage at the +12V pin of the driver when it’s only powered on the 5V pin it reads 5V too, have I messed up something in the wiring or is this normal behavior?

What you can try is increasing the voltage that is used for sensor alignement: motor.voltage_sensor_align that should give the motor a bit more force when aligning to push the stator exactly where it’s supposed to go. :slight_smile:

You can try to see what is the difference in achievable velocity in between voltage and velocity mode. Maybe it would make sense to close the loop through voltage instead velocity. And that way you could even remove the move function completely.

What is your loop sample time?
Does it go under 1ms?

Ah that might help, the motors don’t move very much during calibration. I’ve found the motors don’t really start up until 2/3V so this is making low speeds in voltage control difficult, hence using velocity control for balancing. I’ll try re-align the sensor and get back to you.

The loop time is usually below 1ms but does spike to nearly 2ms every so often, I’ve tried fixing this to 2ms but that didn’t seem to help too much.

The L289N is quite a simple driver, and the boards are usually inexpensive and don’t implement any protections. So yes, this is expected behaviour, the 5V is back-powering the 12V rail.

And it can be a problem, depending on your setup: the motors are trying to turn from the 5V supply, and if you’re powering the whole thing via the USB port of your laptop while programming the MCU, you run the risk of drawing too much current and damaging the USB hardware.

IMHO using a powered USB-Hub between your computer and the MCU is a good idea. And one of those USB-protector plugs that displays the voltage and current drawn can’t hurt either. Mine beeps at me when the USB gets back-powered or power falls out of spec.

1 Like

I think I’ve aligned the zero calibration better now, the max speed still seems to be only around 18 rad/s, does this seem reasonable or should I be able to get it running faster?
I downsampled the motor.move() loop to various loops() per move() and found around 5 seemed best.

Thanks for the heads up on the USB port, I’ll have a look for a hub.

Anyway, I’ve added some extra bits and now have a raspberry pi and a depth camera so I can run this on ros! As you can see in the video, disturbance rejection isn’t too bad!

1 Like

Have your tried increasing your voltage_limit? Do you have a velocity_limit set?
Are you able to (safely) increase your voltage supply?

@Neil_Harrison I have to admit that is the coolest balance bot demo I’ve seen :smile_cat:

1 Like

I’m using a 4S battery so I’ve been testing with up to 16ishV, I have had the voltage limit set to 12V and increasing this any more just seems to make the motors louder with no increase in speed. Should I be looking at running this at 24v or something if I want it faster?

And the velocity limit has been set to 50 for most of my testing.

Two of my favorite kinds of YouTube videos: Cat video + balancir robot :smiley:

I think that 20 rad/sec is reasonable. The issue is not the voltage or the velocity limits the issues is the processing limits. The loop time is I think just too long to make it faster. 2xspi reads + 2xFOC calculations. You should be able to verify this if you run only one motor. It should go faster.

Now, if you really wish to make everything more efficient and run everything faster, maybe you can consider building your minimal library version and then optimizing step by step. :slight_smile:

Regarding the downsamplig of move(), can you try to see what is the velocity that you can achieve if the motor is run in voltage mode. See if it is higher than 20rad/s, try with both motors at the same time. If it is higher than maybe there are some more downsamplig type tricks that we can try. If it isn’t higher downsamplig will not help. :slight_smile:

Yes. :slight_smile: My cat liked the robot too… But now the cat has grown up and started to be scared. :slight_smile:

Neil_Harrison, if not enough stepper motor RPM, try bigger wheels. This little helps…


Love the Video!

If Antun is right here, you can also look into the trick I did to get my I2C performance up: at the moment, both getAngle() and getVelocity() read the sensor from SPI. By changing the code of MagneticSensorSPI.cpp to only read SPI on getAngle(), and compute the velocity in getVelocity() by remembering the previous 2 values read by getAngle()… in this way you can instantly double your sensor-read performance. I couldn’t see any problems in motor behaviour with this modification for my test setups.

Next optimization: you can actually hook up the AS5048As in a daisy chain, and read their values continuously, ideally using an interrupt. I just got my latest batch of SPI-based encoders from PCBWay today (yay!) so I’m actually working on this right now. SimpleFOC doesn’t support it yet, but if I didn’t screw up the PCB design, and manage to get it working software-wise it will soon. I’ll report back on that.


Thank you all for the ideas!

Just a little update, I was looking into ways to get the loopFOC() to run faster and I remembered that the esp32 has two cores! I rewrote my code using freeRTOS, and dedicated one core to running loopFOC as quick as possible and then moved everything else (motor moves, imu reading, pid control) to the other core.

The results were very good! I’ve not tested yet how much quicker the loop is running but I was able to get the max speed up to 30-35 rad/s!
This seems plenty for stable balancing for now.

I’ve not seen anyone else using the esp32’s extra core for this so I’ll clean up my code and post it on github at some point soon!

1 Like

Daisy chaining the encoders sounds very interesting! I’ll do some reading, but I have a feeling this might be out of my depth, so you might be finished with this before I get my head round it!

Hey, I think you are halfway there by using multithreading. The benefit from using daisy chain mode on top of this would be a reduction (to 50%) in the bytes going across the wires, so probably a doubling of the speed. To get it set up with SimpleFOC will probably require at least interrupt driven SPI communication, or a multi-threaded approach like you are already doing.

But if multithreaded sensor-reads are working well for you, I’m glad to hear that. Its something I have been wondering about, if you make the sensor values and the FOC loop asynchronous, what will be the effect on performance.
I guess there is a link between the two threads - if the sensors update too slowly, performance will be affected, but also I am not sure there is benefit to calling the FOC code without new sensor values…

I would like to build a pitch actuator system for a small wind turbine with almost identical hardware
2x Nema17 Stepper motors
2x AS5048a encoders via SPI
2x L289N Drivers

My target angular stepper speed is about 20-30 rad/s.

It would be really helpful if you would share your code. I have some experience with freeRTOS and using the dual cores.


Did have time to try out the daisy chain?

Also it seems the standard SPI interface speed that is used in the library is 1 MHz

agneticSensorSPIConfig_s AS5147_SPI = {
  .spi_mode = SPI_MODE1,
  .clock_speed = 1000000,
  .bit_resolution = 14,
  .angle_register = 0x3FFF,
  .data_start_bit = 13,
  .command_rw_bit = 14,
  .command_parity_bit = 15

However the AS5048A should be able to work at 10MHz?

Have you tried bumping the SPI speed?


I made some small PCBs to link the sensors in a chain, but I have not yet tried it out. I put it on the back burner because in this scenario the sensors are read sequentially. There’s also a way (I think) to read the right kind of sensor in parallel, which would be much cooler.

For sure. On-PCB you can do 10MHz, but via the cables you have to take special care. Generally, 2 or 4MHz is not a problem in my setups, but any higher than this and the SPI transfers tend to become unreliable. I’m sure that if you used shielded cables, maybe used twisted pairs (with GND) on the signal lines and things like that then you could get even more MHz over the cables, but with standard cables you won’t.

1 Like