# 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

// 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

// 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

// 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
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

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

// 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

// 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

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()
{
{
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