Changing pole pair estimation and other unexpected behavior

Hello everyone,

I have been trying to implement a closed angle control loop using a stepper motor but have run into some unexpected behavior. My current setup includes an Arduino Mega 2560, L298N motor driver, a 0.9 deg/step bipolar stepper motor and an AMT103-V capacitive encoder. I have the motor driver powered at 12v from a variable benchtop power supply. I have set my encoder for 512 ppr.

encoder

Encoder_ppr

FOC_setup

I have an application that requires a positional control loop and backdrivability. When attempting to run this example from the SimpleFOCDocs I have encountered erratic jittering when using MotionControlType::angle and no motion or holding torque when attempting to use MotionControlType::torque. When calculating the pole pair number for my motor, I used the following equation: pole_pair = (steps/rotation) / 4; In my case, (400 steps/rev) / 4 = 100 PP, however when I set StepperMotor motor = StepperMotor(100); I get a failed PP check when running the code shown below, and a changing estimation that is always higher than the calculated value.

Assuming I had miscalculated the PP number, I tried using find_pole_pairs_number under Simple FOC>utils>calibration>find_pole_pair_number>encoder>find_pole_pairs_number. This yields something like this:

pole_pair_response

When using StepperMotor motor = StepperMotor(102); (or whatever the estimated PP) I still get the erratic behavior, as seen in the clip below:
https://youtu.be/bVqnWa4HgVQ
The motor heats up, and when the rotor is moved it begins to jump erratically until it settles again, usually in a random angle. Additionally, the index search I believe should be one full rotation, but as you can see in the clip is far less than that.

I have tried running the same setup with MotionControlType::angle_openloop and MotionControlType::velocity_openloop, using the same setup and both worked as expected.

I have also tried:

  • Running the setup with/without index pin on encoder
  • Running the example using a Nucleo-64 board (as in the example)
  • Changing encoder resolution (tried 2048 PPR and 512 PPR)
  • Changing the PID gains

I believe that I am misunderstanding something about the functionality seen in the code I am running below. If anyone has any ideas on why this is happening, let me know!

#include <SimpleFOC.h>

// Stepper motor instance
StepperMotor motor = StepperMotor(102);
// Stepper driver instance
StepperDriver4PWM driver = StepperDriver4PWM(5, 6, 9, 10);

// encoder instance
Encoder encoder = Encoder(2, 3, 512, 21);
// channel A and B callbacks
void doA(){encoder.handleA();}
void doB(){encoder.handleB();}
void doIndex(){encoder.handleIndex();}


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

void setup() {

  // initialize encoder sensor hardware
  encoder.init();
  encoder.enableInterrupts(doA, doB, doIndex); 
  // 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 = 12;
  driver.init();
  // link the motor to the sensor
  motor.linkDriver(&driver);

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

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

  // 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 = 1;
  
  // define the motor id
  command.add('M', onMotor, "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);
}

  // set the initial target value
  float target_angle = 1;
  long timestamp_us = _micros();
  
void loop() {

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

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

  // real time monitoring calls
  //motor.monitor();
  
  // user communication
  //command.run();
}

Thank you!
Michael L.

Hi, and welcome @Michael_Laffin !

Hmmm… The other behaviour you describe is normal when the pole pairs and hence electrical alignment are wrong. 100pp is really a lot, see also this thread:

Maybe the encoder at 512ppr is not precise enough for 0.9° stepper…

Or maybe the pole count is wrong, 100 is very many poles… how many teeth in the stepper’s rotor?

  1. I also assume it is 50 pole pairs = 100 poles. You enter the number of polepairs into the code.
  2. 512 ppr encoder ist definitely not enough, even without the div50 “penalty” I discovered, see quote above. I have a 14bit mag encoder and i am not sure if that is enough! Currently I setup a test bench with an industrial 13 bit opto-encoder to verify my mag sensor and the resulting FOC precision. I will publish the results, of course. As the sensor has CANopen, some extra effort is needed to get the values…
    Peter

Hello everyone,

Thank you for the advice. I have the demo working finally, as seen in this clip.

As far as I could tell, I was correct in assuming 100 pole pairs. Below is a photo of the rotor teeth:

The problem was not the pole pair number, I believe it was due to the PI controller exhibiting some sort of underdamped behavior. When running the initial code I posted with StepperMotor motor = StepperMotor(100); and attached a larger weighted disk to the rotor. This increased load seemed to entirely resolve the erratic jittering. It is using 512 ppr, but I do agree that a higher resolution would yield better results.