SimpleDC Problem

Hi,

I am trying to do close loop control in some DC motors, but it appears that the motor is not receiving the data from the encoder.

I am testing with the example codes.

I already tested the motor and the encoder separately and they work.

For testing the encoder, I used this code:

#include <SimpleFOC.h>

Encoder encoder = Encoder(4, 5, 1680);
// interrupt routine initialization
void doA(){encoder.handleA();}
void doB(){encoder.handleB();}

void setup() {
  // monitoring port
  Serial.begin(115200);

  // enable/disable quadrature mode
  encoder.quadrature = Quadrature::OFF;

  // initialize encoder hardware
  encoder.init();
  // hardware interrupt enable
  encoder.enableInterrupts(doA, doB);

  Serial.println("Encoder ready");
  _delay(1000);
}

void loop() {
  // IMPORTANT - call as frequently as possible
  // update the sensor values 
  encoder.update();
  // display the angle and the angular velocity to the terminal
  Serial.print(encoder.getAngle());
  Serial.print("\t");
  Serial.println(encoder.getVelocity());
}

It prints the right values.

Then I try the following code for velocity control:

#include <Arduino.h>

#include "SimpleFOC.h"
#include "SimpleFOCDrivers.h"
#include "SimpleDCMotor.h"

// DCMotor object
DCMotor motor = DCMotor();
// DCDriver object
DCDriver2PWM driver = DCDriver2PWM(8, 9);


//Encoder
Encoder encoder = Encoder(4, 5, 1680);
// interrupt routine initialization
void doA(){encoder.handleA();}
void doB(){encoder.handleB();}


// Commander object, used for serial control
Commander commander = Commander(Serial);
// motor control function - this is needed to link the incoming commands 
// to the motor object
void onMotor(char* cmd){ commander.motor(&motor, cmd); }


void setup() {
  // monitoring port
  Serial.begin(115200);


  while (!Serial) {};   // wait for serial connection
  // enable debug output to the serial port
  SimpleFOCDebug::enable();
  
  // enable/disable quadrature mode
  encoder.quadrature = Quadrature::OFF;

// basic driver setup - set power supply voltage
  driver.voltage_power_supply = 6.0f;
 
  driver.voltage_limit = 6.0f;
  // Optionally set the PWM frequency.
  driver.pwm_frequency = 5000;


  // init driver
  driver.init();
  // initialize encoder hardware
  encoder.init();
  // hardware interrupt enable
  encoder.enableInterrupts(doA, doB);
  // link motor to driver
  motor.linkDriver(&driver);
  // link sensor to motor
  motor.linkSensor(&encoder);


  // set a voltage limit on the motor, optional. The value set here
  // has to be lower than the power supply voltage.
  motor.voltage_limit = 6.0f;
  motor.velocity_limit = 500.0f;

  // control type - for this example we use velocity mode.
  motor.controller = MotionControlType::velocity;
  motor.torque_controller = TorqueControlType::voltage;
  // init motor
  motor.init();
  // set the PID parameters for velocity control.
  motor.PID_velocity.P = 0.05f;
  motor.PID_velocity.I = 0.02f;
  motor.PID_velocity.D = 0.0f;
  // output ramp limits the rate of change of the velocity, e.g. limits the
  // accelleration.
  motor.PID_velocity.output_ramp = 200.0f;
  // low pass filter time constant. higher values smooth the velocity measured
  // by the sensor, at the cost of latency and control responsiveness.
  motor.LPF_velocity.Tf = 0.01f;
  // set the target velocity to 0, we use the commander to set it later
  motor.target = 0.0f;
  // enable motor
  motor.enable();
  // add the motor and its control function to the commander
  commander.add('M', onMotor, "dc motor");
  // enable monitoring on Serial port
  motor.useMonitoring(Serial);
  Serial.println("Initialization complete.");
}

void loop() {

  motor.move(); // target speed can be set via commander input

  // call commander.run() once per loop iteration, it will process incoming commands
  commander.run();

  // call motor.monitor() once per loop iteration, it will print the motor state
  motor.monitor();
}

When I set a target for the motor to reach, the motor never stops ramping up the velocity. I am pretty sure this is caused for the “I” term in the PID. I think this because the monitor does not print the position neither the velocity.

This is my first time using this library so is very probable that it is user error, but I don’t have any clue what it is.

I will be incredibly grateful if someone could help me.

You should verify that torque mode works first. You can set the I term to zero, usually you set all the pid gains to zero and bump it up slowly.

I looked at your code but my own understanding is bounded by the previous issues I have encountered so not much help there. I have no idea what 2 pin pwm even is.

Also 500 radians per second is pretty fast, that alone could be confusing you.

I already tested that.

I am very sure that the problem is in linking the motor with the encoder, because the monitor prints the right values for the target and voltage but prints zeros for the angle and the velocity. So basically, the PID only receives 0 for those values.

Maybe this is a bug in the SimpleDC library, or I am missing one step.

Hi,

Sorry for the late response, I somehow missed this thread…

The DCMotor was working in principle, also in combination with a sensor.

So perhaps it is a regression of some kind with the new version of the SimpleFOC lib, its possible, I have to test it.

But could I ask you to add a small delay to the main loop, something like delayMicroseconds(100); ? I’m not sure which MCU you’re using, but perhaps the updates are happening a bit too fast…

1 Like

@runger I am also facing the same problem.
I have implemented sensor using GenericSensor. The sensor I am using is 2 hall effect sensor. Typical sensor you get in N20 encoder motors.

When I run position control mode, I am able to read angle using sensor.getAngle()
but monitor shows angle and velocity as 0

Putting delayMicroseconds(100) is not working as well
I am attaching the code for your reference

#include <Arduino.h>

#include "SimpleFOC.h"
#include "SimpleFOCDrivers.h"
#include "SimpleDCMotor.h"


int encoderPin1 = 3; //Encoder Output 'A' must connected with intreput pin of arduino.
int encoderPin2 = 2; //Encoder Otput 'B' must connected with intreput pin of arduino.
volatile int lastEncoded = 0; // Here updated value of encoder store.
volatile long encoderValue = 0; // Raw encoder value
volatile float cangle = 0.0f;


DCMotor motor = DCMotor();
DCDriver2PWM driver = DCDriver2PWM(5, 6);

Commander commander = Commander(Serial);
void onMotor(char* cmd){ commander.motor(&motor, cmd); }
void onTarget(char* cmd){commander.target(&motor, cmd);}

float readMySensorCallback(){
 return cangle;
}

void initMySensorCallback(){
  // do the init
}
float getSensorAngle(){
  return cangle;
}
// create the sensor
GenericSensor sensor = GenericSensor(readMySensorCallback, initMySensorCallback);

void setup() {
  pinMode(encoderPin1, INPUT_PULLUP); 
  pinMode(encoderPin2, INPUT_PULLUP);

  digitalWrite(encoderPin1, HIGH); //turn pullup resistor on
  digitalWrite(encoderPin2, HIGH); //turn pullup resistor on

  attachInterrupt(0, updateEncoder, CHANGE); 
  attachInterrupt(1, updateEncoder, CHANGE);


  // to use serial control we have to initialize the serial port
  Serial.begin(115200); // init serial communication
  while (!Serial) {};   // wait for serial connection
  SimpleFOCDebug::enable();
  
  driver.voltage_power_supply = 6.0f;
  driver.voltage_limit = 6.0f;
  driver.pwm_frequency = 5000;
  driver.init();
  sensor.init();
  pinMode(10, OUTPUT);
  motor.linkDriver(&driver);
  motor.linkSensor(&sensor);

  motor.voltage_limit = 6.0f;
  motor.velocity_limit = 500.0f;
  motor.controller = MotionControlType::angle;
  motor.torque_controller = TorqueControlType::voltage;
  // init motor
  motor.init();
  motor.PID_velocity.P = 0.05f;
  motor.PID_velocity.I = 0.02f;
  motor.PID_velocity.D = 0.0f;
  motor.PID_velocity.output_ramp = 200.0f;
  motor.LPF_velocity.Tf = 0.01f;
  motor.P_angle.P = 15.0f;  
  motor.target = 0.0f;
  motor.enable();
  commander.add('M', onMotor, "dc motor");

  motor.useMonitoring(Serial);
  Serial.println("Initialization complete.");
}

void updateEncoder(){
  int MSB = digitalRead(encoderPin1); //MSB = most significant bit
  int LSB = digitalRead(encoderPin2); //LSB = least significant bit

  int encoded = (MSB << 1) |LSB; //converting the 2 pin value to single number
  int sum  = (lastEncoded << 2) | encoded; //adding it to the previous encoded value

  if(sum == 0b1101 || sum == 0b0100 || sum == 0b0010 || sum == 0b1011) encoderValue --;
  if(sum == 0b1110 || sum == 0b0111 || sum == 0b0001 || sum == 0b1000) encoderValue ++;

  lastEncoded = encoded; //store this value for next time

  cangle = (encoderValue * 6.28319)/4000.0f;


}

void loop() {
  motor.move(); // target position can be set via commander input
  delayMicroseconds(100); 
  // call commander.run() once per loop iteration, it will process incoming commands
  commander.run();

  // call motor.monitor() once per loop iteration, it will print the motor state
  motor.monitor();

  sensor.update();
  // display the angle and the angular velocity to the terminal
  Serial.println(sensor.getAngle());
}

Serial Monitor Output
image

Hi @Sanjeev_Hegde , welcome to SimpleFOC!

I’m sorry to hear you also have problems. I assume it must be a bug.

I’m on vacation right now, so I cannot test it until I get back to my motors next week…

Until then, looking at your main loop, you can remove the sensor.update(), as this is called in motor.move(). Also, printing the angle once per loop is too often, you will probably overload the Serial port and cause the program to block on IO. To print the angle, introduce a counter and print it only every 100 or 1000 iterations…

1 Like

Hi,

I already try adding the delay, but it didn’t work.

I am using an RP2040.

Sadly, I think the library has some sort of bug.

Thanks @runger , the Serial print was just added to check if sensor is working. I will remove this.

Also, I tested the torque-voltage mode. This is not working for me.
Is this working for you @Samir ?

#include <Arduino.h>

#include "SimpleFOC.h"
#include "DCMotor.h"
#include "drivers/DCDriver2PWM.h"


DCMotor motor = DCMotor();
DCDriver2PWM driver = DCDriver2PWM(5, 6);

void setup() {

  driver.init();
  motor.linkDriver(&driver);

  driver.voltage_power_supply = 10.0f;
  driver.voltage_limit = 10.0f;
  motor.torque_controller = TorqueControlType::voltage;
  motor.controller = MotionControlType::torque;

  motor.init();

}

void loop() {
  motor.move(5.0f); // 3 Volts
}

Hmmm, maybe try adding the following line in setup:

motor.sensor_direction = Direction::CW;

(or Direction:CCW)

1 Like

Thanks @runger !!!
Adding this works perfectly with Position control example !! :slightly_smiling_face:
For Voltage torque example, I also had to add driver.pwm_frequency = 5000; in setup, since frequency is by default null here

I have raised this PR for fix.

1 Like

Thank you very much, this solved the problem.

1 Like