Help with FOC of NEMA 17 Stepper Motor with as5600, L298N, and Arduino Mega 2560

Looking for support for FOC control of a NEMA 17 Stepper Motor, with as5600 magnetic sensor, L298N motor driver, and Arduino Mega 2560 MCU.

Open loop control works, (but noisy), but when I switch over to closed loop, the motor jitters.

I have been working in FOC Studio to tune the PID values, but with no luck. I have ran the magnetic encoder standalone example code and have received great values from the sensor when manually turning the motor shaft. What am I missing?

See the code below:

#include <SimpleFOC.h>


StepperMotor motor = StepperMotor(50);
StepperDriver4PWM driver = StepperDriver4PWM(5,6,9,10);

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


// commander interface
Commander command = Commander(Serial);
void doMotor(char* cmd){ command.motor(&motor, cmd); }

void setup() {

  // initialize encoder sensor hardware
  sensor.init();
  
  // link the motor to the sensor
  motor.linkSensor(&sensor);

  // choose FOC modulation
  motor.foc_modulation = FOCModulationType::SpaceVectorPWM;

  // power supply voltage [V]
  driver.voltage_power_supply = 24;
  driver.init();
  // link the motor to the sensor
  motor.linkDriver(&driver);

  // set control loop type to be used
  motor.controller = MotionControlType::torque;

  // controller configuration based on the control type 
  motor.PID_velocity.P = 0;
  motor.PID_velocity.I = 0;
  motor.PID_velocity.D = 0;
  // default voltage_power_supply
  motor.voltage_limit = 24;

  // velocity low pass filtering time constant
  motor.LPF_velocity.Tf = 0.01;

  // angle loop controller
  motor.P_angle.P = 0;
  // angle loop velocity limit
  motor.velocity_limit = 50;

  // use monitoring with serial for motor init
  // monitoring port
  Serial.begin(115200);
  // comment out if not needed
  motor.useMonitoring(Serial);

  // initialise motor
  motor.init();
  // align encoder and start FOC
  motor.initFOC();

  // set the initial target value
  motor.target = 2;

  // define the motor id
  command.add('M', doMotor, "motor");

  // Run user commands to configure and the motor (find the full command list in docs.simplefoc.com)
  Serial.println(F("Motor commands sketch | Initial motion control > torque/voltage : target 2V."));
  
  _delay(1000);
}


void loop() {
  // iterative setting FOC phase voltage
  motor.loopFOC();

  motor.monitor();
  // iterative function setting the outter loop target
  // velocity, position or voltage
  // if tatget not set in parameter uses motor.target variable
  motor.move();

  // user communication
  command.run();
}

Hey @Clayton_Young,

Noisy open loop is probably because of the very high motor.voltage_limit
It will create a very high current in the motor and make all kinds of noises :smiley:
Try reducing the voltage limit to 1 or 2 volts. The current induced in the motor will be proportional to this voltage (at least in the open-loop mode)

I = \frac{Voltage}{Resistance}
motor.voltage_limit = 1; // Volt

Now in terms of jittery closed loop, could you copy here your monitoring output of the motor initialization (the text that is printed in your serial terminal). This might help us to find the source of the problem.

In the torque control mode using voltage command, no PID is being used so no changes in the PID values will make a difference. You can see this better in the docs, the block scheme does not have any PIDs for voltage torque control.

Thank you for the reply. I turned down the voltage limit to 4 volts. See the updated code I am using below.

And here is the motor monitoring output:

Connected …
MOT: Init
MOT: Enable driver.
MOT: Align sensor.
MOT: sensor_direction==CCW
MOT: PP check: OK!
MOT: Zero elec. angle: 4.94
MOT: Ready.

I have been messing with the PID values in velocity closed loop control with no luck. As I turn up the “P” value the motor will start to jitter, but never rotates more than 15 degrees (you can hear it jitter in the video).

Here are two attached videos. The first showing open loop velocity and then a switch to closed loop using the FOC Studio software. The second shows the motor jittering.

What am I missing?

#include <SimpleFOC.h>


StepperMotor motor = StepperMotor(50);
StepperDriver4PWM driver = StepperDriver4PWM(9,10,5,6);

// Example of AS5600 configuration 
MagneticSensorI2C encoder = MagneticSensorI2C(AS5600_I2C);

// commander interface
Commander command = Commander(Serial);
void doMotor(char* cmd){ command.motor(&motor, cmd); }

void setup() {

  // initialize encoder sensor hardware
  encoder.init();
  // link the motor to the sensor
  motor.linkSensor(&encoder);

  // choose FOC modulation
  motor.foc_modulation = FOCModulationType::SpaceVectorPWM;

  // power supply voltage [V]
  driver.voltage_power_supply = 24;
  driver.init();
  // link the motor to the sensor
  motor.linkDriver(&driver);

  // set control loop type to be used
  motor.controller = MotionControlType::velocity;

  // controller configuration based on the control type 
  motor.PID_velocity.P = 0;
  motor.PID_velocity.I = 0;
  motor.PID_velocity.D = 0;
  // default voltage_power_supply
  motor.voltage_limit = 4;
  motor.current_limit = 2;
  motor.sensor_direction = CCW;

  // velocity low pass filtering time constant
  motor.LPF_velocity.Tf = 0.01;

  // angle loop controller
  // motor.P_angle.P = 20;
  // angle loop velocity limit
  motor.velocity_limit = 50;

  // use monitoring with serial for motor init
  // monitoring port
  Serial.begin(115200);
  // comment out if not needed
  motor.useMonitoring(Serial);

  // initialise motor
  motor.init();
  // align encoder and start FOC
  motor.initFOC();

  // set the initial target value
  motor.target = 5;

  // define the motor id
  command.add('M', doMotor, "motor");

  motor.useMonitoring(Serial);

  // Run user commands to configure and the motor (find the full command list in docs.simplefoc.com)
  Serial.println(F("Motor commands sketch | Initial motion control > torque/voltage : target 2V."));
  
   _delay(1000);
}


void loop() {
  // iterative setting FOC phase voltage
  motor.loopFOC();

  motor.move();

  motor.monitor();
  // user communication
  command.run();
}

What happens if you set the speed way down, like way way down? Also I would loop on motor.loopFOC() like 50 times before doing the other stuff.

I found dealing with things very hard, picking things apart is quite hard. You can use the commander that Deku used to check the various variables etc. Use serial monitor and serial plotter to look in there and follow the flow of how you were thinking things were going, see if you can spot where things have started to go wrong.

Here is the code you can use to check various variables etc, add the lines and variables you need:

void SerialComm()
{
  switch(Serial.read())
  {
  case 'T': goal_speed = Serial.parseFloat(); Serial.print("T");break;
  case 't': Serial.print("T:"); Serial.println(goal_speed); break;

  case 'V': v_diff = Serial.parseFloat(); Serial.print("V");break;
  case 'v': Serial.print("V:"); Serial.println(v_diff); break;
  
 // case 'P': p_gain = Serial.parseFloat(); Serial.print("P");break;
 // case 'p': Serial.print("p:"); Serial.println(p_gain); break;
 // case 'I': diff_filter.Tf = Serial.parseFloat(); Serial.print("I");break;
//  case 'i': Serial.print("f:"); Serial.println(diff_filter.Tf); break;
 // case 'D': d_gain = Serial.parseFloat(); Serial.print("d_gain set");
 // case 'd': Serial.print("d_gain is:"); Serial.println(d_gain); break;
//  case 'O': i_windup_limit = Serial.parseFloat(); Serial.print("i_windup_limit set");
  //case 'o': Serial.print("windup limit is:"); Serial.println(i_windup_limit); break;
//  case 'U': setpoint = Serial.parseFloat(); Serial.print("S"); break;
//  case 'u': Serial.print("s:"); Serial.println(setpoint); break;
  
  }
}

If you are planning to do anything more than extremely basic, my plan should I ever have to do anything with Arduino again was to learn to use Atmel studio and their debugging tools. It’s supposed to be arduino compatible but you can do actual debugging, look into the memory of the device and see what’s happening at high speed. Print statements don’t work well to spot things that are happenign fast.

I also used functions like this a lot, add the stuff you want to print and then use the serial plotter to spot patterns etc.

void print_pid_stuff(){

  //Serial.print("rawcount:");
  //Serial.print(sensor.getRawCount()); 
  //Serial.print(",");
  Serial.print("calsens:");
  Serial.print(sensor_calibrated.getMechanicalAngle()); 
  Serial.print(",");
  Serial.print("e:");
  Serial.print(e); 
  Serial.print(",");
  Serial.print("s:");
  Serial.print(s); 
  Serial.print(",");
  Serial.print("cs:");
  Serial.print(control_signal); 
  Serial.print(",");
  //Serial.print("csa:");
  //Serial.print((sensor_calibrated.getMechanicalAngle())); 
  //Serial.print(",");
  Serial.print("apd:");
  Serial.println(average); 
  
}

Also I noticed you are setting the current, which I think will have no effect in this situation, I don’t know if it may be causing a rpoblem, I would think not.

1 Like

Hey,

Looking at your code, of course it won’t move with the PID values at 0. So you set P and I via SimpleFOCStudio, I assume - I couldn’t 100% follow in the video what’s going on.

One thing is the motor vs. sensor, this can be hard to tune because the AS5600 is not that precise, while a 50 PP motor has very many electrical revolutions per physical (=higher frequency waveforms). With its high latency due to I2C the AS5600 isn’t a good choice for this motor.

So for sure do the initial tests at low speeds. You may also consider setting the output_ramp of the PID to a low value to prevent the motor changing speeds too quickly.

Then you’ll have to tune the PID, which could be quite a difficult/fussy task given the motor/sensor. Set a speed like 1 rad/s, and then slowly raise P until you get movement and it begins to try to track the target value. Increase it more until you get oscillations, then back it down again. Then raise I until it tracks the target value well.

Another question: is the torque-voltage mode working for you well? This is the first mode to try, you don’t need to tune a PID for this…

1 Like

Would you recommend the 14-bit version, like the AS5048A?

Yes, I think with this type of sensor which is SPI (much faster) and more precise I think it will be easier for you…

Hello everyone.
Its been a time since the last reply to this post but i hope i can help you with somethings @Clayton_Young, so…
I was checking the code and documentation about the implementation of FOC algorithms for a stepper motors class and i’ve notice you’re changing the motor.foc_modulation and you’re select SpaceVectorPWM type… well according to the current library version and documentation, for the stepper motor class this modulation type have no support (link for doc) i’ve verify that in the library source code , and for me, part of the problem is this code line.
Another part of the problems you have with the motor control are the problems mentioned by @runger related to the resolution and communication protocol of the AS5600 encoder and the PID output_ramp when you’ll use the position/velocity control mode. My recommendation is you’ll have change the encoder and the modulation type code line for: motor.foc_modulation = FOCModulationType::SinePWM;

p.d.: I’m not a native english speaker so, i’ll appreciate your help and understanding with that, and also is my first post haha.

1 Like