Stepper cogging in open-loop and sensor issues (AS5600 magnet confirmed to be correct)

I am following the Getting Started tutorial and have not been able to get the motor running properly or correct readings from the sensor (at least that I can understand).
My setup is as follows:

I am using a NEMA 11 200step/rev(50pp), 24v stepper motor. Each phase has 8.5ohms of resistance.

The AS5600 magnetic encoder is on SDA/SCL pins 6 and 7 with 2.2kOhm pull-up resistors to ESP 3.3v. The AS5600 is powered by said 3.3v on VCC, with a capacitor across VCC and ground. The I2C Magnetic Encoder example sketch works fine. I have confirmed that the magnet is split hemispherically rather than axially.

My motor driver is an L289N. The driver is supplied with 24v and 5v from a bench power supply with plenty of ampacity. Phase A+ and A- are on ESP32-C3 pins 3 and 4, respectively, while phase B+ and B- are on pins 10 and 9. Phase A enable is on pin 2, and B is on pin 8.

The ESP is powered by and connected through a good-quality USB-C cable.

All grounds are tied together

Arduino IDE is version 2.3.5, with Espressif ESP32 board manager version 3.3.2 and simpleFOC library version 2.3.5. My computer is a Macbook M2 on OSX 14.8.

I successfully ran the Magnetic Sensor I2C test sketch (6.28radians per motor revolution when turned by hand), the driver test (ā€œEP32-DRV: Configuring 4PWM, EP32-DRV: 4PWM setup in group: 0, EP32-DRV: 4PWM setup suggessful!, Driver ready!ā€, though this sketch needed an extra second of delay() after Serial.begin()) for the notice to display on Serial Monitor), and the Open Loop example (this has a distinct thump of cogging every two seconds, frequency independent of speed but growing stronger with voltage and speed).

When I merged the Open Loop and the sensor test, the motor speed was steady (T=6, L=6), but the sensor reading angular velocity was all over the place, jumping erratically both positive and negative. I then confirmed that the sensor works fine with the AdaFruit AS5600 library and a drill gun on the motor shaft (disconnected motor wires first!).

Maybe my sketch merge was poorly crafted? Here is what I used to run Open Loop/Sensor Test

// Run motor in Open Loop with the sensor providing speed feedback

#include <SimpleFOC.h>

// Stepper motor & driver instance
StepperMotor motor = StepperMotor(50, 8.5); //Motor pole pairs, phase resistance
StepperDriver4PWM driver = StepperDriver4PWM(3, 4, 10, 9, 2, 8); //(A+, A-, B+, B-, Enable A(optional), Enable B(optional))

//AS5600 configuration 
MagneticSensorI2C sensor = MagneticSensorI2C(AS5600_I2C);


//target variable
float target_velocity = 2; //starting speed in rad/s

// instantiate the commander
Commander command = Commander(Serial);
void doTarget(char* cmd) { command.scalar(&target_velocity, cmd); }
void doLimit(char* cmd) { command.scalar(&motor.voltage_limit, cmd); }

void setup() {

  // use monitoring with serial 
  Serial.begin(115200);
  // enable more verbose output for debugging
  // comment out if not needed
  SimpleFOCDebug::enable(&Serial);

  // configure i2C
  Wire.setClock(400000);
  // initialise magnetic sensor hardware
  sensor.init();

  // driver config
  // power supply voltage [V]
  driver.voltage_power_supply = 24;
  // limit the maximal dc voltage the driver can set
  // as a protection measure for the low-resistance motors
  // this value is fixed on startup
  driver.voltage_limit = 24;
  if(!driver.init()){
    Serial.println("Driver init failed!");
    return;
  }
  // link the motor and the driver
  motor.linkDriver(&driver);

  // limiting motor movements
  // limit the voltage to be set to the motor
  // start very low for high resistance motors
  // current = voltage / resistance, so try to be well under 1Amp
  motor.voltage_limit = 12;   // [V]
 
  // open loop control config
  motor.controller = MotionControlType::velocity_openloop;

  // init motor hardware
  if(!motor.init()){
    Serial.println("Motor init failed!");
    return;
  }

  // add target command T
  command.add('T', doTarget, "target velocity");
  command.add('L', doLimit, "voltage limit");

  Serial.println("Motor ready!");
  Serial.println("Set target velocity [rad/s]");
  _delay(1000);
}

void loop() {

  // open loop velocity movement
  // using motor.voltage_limit and motor.velocity_limit
  // to turn the motor "backwards", just set a negative target_velocity
  motor.move(target_velocity);

  // user communication
  command.run();

//read the sensor
  sensor.update();
  
  // display the angle and the angular velocity to the terminal
  Serial.print(sensor.getAngle());
  Serial.print("\t");
  Serial.println(sensor.getVelocity());
}

And here is a sample of the serial output of the above. I do see the pattern in the sensor reading, but I don’t understand what it means. Noise? Misalignment?

-56.16	-7.24
-56.17	-12.72
-56.18	-11.04
-56.18	-3.71
-56.18	1.94
-56.18	1.94
-56.18	-2.80
-56.19	-11.05
-56.20	-12.92
-56.21	-7.65
-56.21	-7.65
-56.21	-7.65
-56.21	-1.23
-56.22	-10.97
-56.23	-11.08
-56.24	-9.87
-56.24	-3.69
-56.24	3.70
-56.24	-1.88
-56.24	-7.40
-56.26	-12.86
-56.27	-13.47
-56.27	-5.53
-56.27	-1.83

The KV test is equally erratic and has the same thunk every 2 seconds.

EP32-DRV: Configuring 4PWM
EP32-DRV: 4PWM setup in group: 0
EP32-DRV: 4PWM setup successful!
MOT: Monitor enabled!
MOT: Init
MOT: Enable driver.
MOT: Align sensor.
MOT: sensor_direction==CCW
MOT: PP check: OK!
MOT: Zero elec. angle: 1.98
MOT: No current sense.
MOT: Ready.
Motor ready.
Set the target voltage : - commnad T
Calculate the motor KV : - command K
88.97
105.07
38.08
36.51
51.03
-182.48
-215.74

Then, to really put icing on the cake, my ESP32s also just stop transmitting over serial after a while; ~10 minutes the first time running Open Loop and ~25 minutes the second time running Find KV Rating. To test if this was a simpleFOC-related bug, I ran the AdaFruit AS5600 test sketch again, and that lost serial comms after ~22 minutes. I feel like this has happened within much shorter timeframes though, as I have not had an FOC sketch running successfully that would need to be tested for so long yet. This may be related to a watchdog timer not being reset? I had to push the reset button before the Arduino IDE would connect again.
When the ESP32 stops transmitting, the motor runs noticeably smoother(due to no delay from the serial interrupt, I understand), but the 2-second thunk is still there.

I have used these motor and sensor arrangements for months with normal stepper drivers and Arduinos/libraries, so I’m confident those two elements work fine.

I have troubleshot these issues for three evenings now and have read many similar posts but nothing that quite matches my troubles; any help would be greatly appreciated.
Here’s my setup and a link to a video of the thunk

https://imgur.com/ftZNgrT


You shouldn’t do any serial printing every time through the main loop like that. I do it like this:

  static u32 lastPrintTime = 0;
  u32 t = millis();
  if (printVariable && t >= lastPrintTime + 50)
  {
    lastPrintTime = t;
    char string[256];
    switch(printVariable)
    {
    case 'a': sprintf(string, "%i\n", (int)(motor.shaft_angle*1000)); break;
    case 'v': sprintf(string, "%i\n", (int)(motor.shaft_velocity*1000)); break;
    case 'b': sprintf(string, "%i\t%i\n", (int)(motor.shaft_angle*1000), (int)(motor.shaft_velocity*1000)); break;
    default: string[0] = 0;
    }
    if(string[0]) Serial.print(string);
  }

So I can easily add different things to monitor and switch between them by changing the value of printVariable. And there’s only one call to Serial.print because consecutive calls on STM32 will block (I don’t know about ESP32). STM32’s implementation of sprintf does not support floating point, so I have to multiply by 1000 and cast to int to be able to see some decimal places, but you can try sprintf(string, ā€œ%f\nā€, motor.shaft_angle); and see if ESP32’s works properly.

Thank you for the reply. The Serial.prints I have included come from the example, but I can see how they aren’t optimized for a high-speed loop. But that doesnt explain the poor behavior prior to my altering the example sketches. I’ll still try your method though for better data.

To separate variables, I connected the sensor to an Arduino. The ESP32 is driving the motor at the commanded speed in Open Loop and the sensor is reading just fine.The knocking stutter that I can see is displayed in the data as a drop in speed (driving the motor at 12rad/s shows a droop to as low as 4rad/s when sampled at 20hz) which is what I expected.

I tried reading the Watchdog documents, but that stuff is beyond my understanding yet. I don’t know know what else to try or how else to proceed.

I doubt any watchdogs would be running, but I don’t know what would cause serial to fail after 10-30 minutes either.

The thunking definitely sounds like something is blocking the CPU for a few milliseconds on a set interval.

Try stripping your code down to the bare minimum and see if it still does it. If not, you can add things back in until it starts happening again to narrow down the cause. If it does still happen, then it would probably be easier to change to a different microcontroller than to try to debug its startup code.

#include <SimpleFOC.h>

// Stepper motor & driver instance
StepperMotor motor = StepperMotor(50, 8.5); //Motor pole pairs, phase resistance
StepperDriver4PWM driver = StepperDriver4PWM(3, 4, 10, 9, 2, 8); //(A+, A-, B+, B-, Enable A(optional), Enable B(optional))

//target variable
float target_velocity = 2; //starting speed in rad/s

void setup() {
  // driver config
  // power supply voltage [V]
  driver.voltage_power_supply = 24;
  // limit the maximal dc voltage the driver can set
  // as a protection measure for the low-resistance motors
  // this value is fixed on startup
  driver.voltage_limit = 24;
  driver.init();
  // link the motor and the driver
  motor.linkDriver(&driver);

  // limiting motor movements
  // limit the voltage to be set to the motor
  // start very low for high resistance motors
  // current = voltage / resistance, so try to be well under 1Amp
  motor.voltage_limit = 12;   // [V]
 
  // open loop control config
  motor.controller = MotionControlType::velocity_openloop;

  // init motor hardware
  motor.init();
}

void loop() {
  // open loop velocity movement
  // using motor.voltage_limit and motor.velocity_limit
  // to turn the motor "backwards", just set a negative target_velocity
  motor.move(target_velocity);
}

Shoot, it’s not even my code: I’m still trying to get through the Getting Started examples! I have no idea what I can remove that won’t break the process outright, but I guess this is how I learn. Thank you for the advice.

My hardware and sketches run trouble-free on an Arduino Uno, so it really does come down to the ESP32, unfortunately.

I dont know if it matters, but this is the C3 variant I am using

And I have installed the dev branch which fixed previous C3 bugs

@runger Trying not to threadjack from the C3 support thread since my problem seems to be more involved.

I’m trying to follow the different threads of the conversation :slight_smile:

The communications breaking down would seem to be unrelated to SimpleFOC if it also happens with other sketches? Then I would treat that issue seperately from the motor related problems…

you seem to have a number of different issues… the weirdest one to me is the two second thunk that occurs independently of speed. Do you run the WiFi on the C3?

I should mention that you’ve chosen a really hard combination to run:

  • stepper with 50PP requires high accuracy sensors, and high speed MCU to run the commutation

  • AS5600 is very slow due to I2C and isn’t very accurate compared to other solutions

  • L298N is a very old and slow switching driver, which you generally can’t run at high PWM frequencies, and has trouble outputting high resolution PWM (when the duty cycles are very high or low it can’t switch fast enough)

So doing this combination as your first test of SimpleFOC is not recommended…

I am not actively using the wifi on the C3 and have not done anything to it. Maybe it’s trying to connect in the background by default? Would it do that if not commanded by the sketch?

Thank you for the critique of my hardware; I’ll go back to the drawing-board and get MCUs, motors, drivers, and encoders better suited for FOC purposes. My application will be low speed and relatively high torque, which is why I thought steppers would be well-suited. I won’t need particularly high accuracy for my application, I don’t think. I do want the MCU/driver/encoder package to be as small as possible, though, ideally fitting into the same footprint as the motor.

I found a post about a similar issue to the 2-second thunk Periodic Jerk in velocity control mode

Which links to this reddit post https://www.reddit.com/r/arduino/comments/1iuwcdq/psa_arduino_with_single_core_esp32s_adds_5ms/

I’m not sure I fully understand what the solution is, but I’ll play with it and report back.

On thing i can recomend is buy 2 of everything. That way you can swap out components and see if the problem is associated with the part. I had an AS5600 that went bad, but I couldn’t identify what was wrong with it other than the fact that the problem followed the encoder board, not the motor, driver, or cpu.

Heck, I bought five of everything! My project will end up using four of each. Hopefully I can make this stuff work sufficiently well, seeing as I already have it on hand :face_with_diagonal_mouth:

Yeah, the motor control doesn’t like being interrupted by other tasks…

You can possibly disable the watchdog, or just feed it from your own loop. It sounds like the internet has some code examples for you :slight_smile:

That will take care of the ā€žthunkā€œ.

For the other problems it might be helpful to get a cheap BLDC motor with 14 or 22 poles to begin with. That way you can set up your code and driver and get everything working on a system that isn’t so difficult… then once it’s all working you swap in the other motor and see how you do with re-tuning things to get that one working…

1 Like