Gradual Speed Ramp Up

Hi fellow members,

I have tried to search a fair bit, but have not found some code to allow the BLDC motor on my vinyl turntable to spool up gradually.

Have any of you found a way to do this?

Cheers,
Andy
https://www.printables.com/model/650628-lil-pucker-3d-printed-vinyl-turntable

Hi,

You’ve came to the right place.

If you get a SimpleFOC supported PCB driver board, this can be done by the library out of the box.
From your link I can’t tell what driver you use.

Cheers,
Valentine

Thanks Valentine,

I am using an Uno and SimpleFOC shield in an open loop configuration. Not using closed loop because the open seems smoother for a turntable and was confirmed by quite a few folks here.

I have not found the code snippet.

Cheers,
Andy

Can‘t you slowly increase the target velocity?

Yes, @Grizzly ’s suggestion is the right one…

Add another variable like “target_target” to your code, and a timestamp. Use target_target to keep your real target value.
Whenever the current motor.target and the target_target are different adjust motor.target by some amount “delta” until it reaches the target_target.
Use the timestamp to do this only every 10ms or so.

This will ramp your target speed up gradually instead of suddenly.

I also did that using the kalman smoothing if the target which does a nice S-curve. If you set the delay rather long, it acts as smoothing algorithm.

Cheers,
Valentine

@runger, wasn’t there a ramp-up parameter you set and this way you avoid extra coding?

Since open loop is used, it is probably irrelevant, but the ramp up parameter limits the how fast the current may change and therefore limits how fast the algorithm can react to disturbances. So, that would not be my preferred method here. Apart from that, I am not sure if the ramp up parameter is active in open loop mode.

Something like this could be good enough to smooth out the target:

LowPassFilter LPF_target(0.5);  //  the higher the longer new values need to take effect
... 
motor.move(LPF_target(target));

Like what is being done for the PID output ramp here
Maybe another PID with Kp = 1 could be used as a workaround.

Thanks. Would this work for open loop?

Thanks, will try. Looks like an ideal simple solution.

A ramp up parameter would be awesome as I have basically no real programming skills. Chef’s hat and a cookbook for me. :rofl:

Post your code here, I will fix it for you and send it back.

Cheers,
Valentine

1 Like

Yes, absolutely.

This is an excellent suggestion, I’d try this first, @jazzboy

Yes, the PID has a output_ramp but unfortunately we can’t use this for open loop as the PID isn’t used in open loop.

1 Like

Thank you, thank you! :grinning:

// Open loop motor control example
#include <SimpleFOC.h>
bool doonce = 1;
int debouncetime = 10000;

const int BUTTON_PIN = 10;

int input4Pin = 13;
int input3Pin = 12;
int input2Pin = 11;
int input1Pin = 10;
// this declares each of our buttons and their pins
// make sure that you use the pins your buttons are wired to

// BLDC motor & driver instance
// BLDCMotor motor = BLDCMotor(pole pair number);
BLDCMotor motor = BLDCMotor (7, 5.0, 255.0);
// BLDCDriver3PWM driver = BLDCDriver3PWM(pwmA, pwmB, pwmC, Enable(optional));
BLDCDriver3PWM driver = BLDCDriver3PWM(9, 5, 6, 8);

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

void setup() {
pinMode(input4Pin, INPUT_PULLUP);
pinMode(input3Pin, INPUT_PULLUP);
pinMode(input2Pin, INPUT_PULLUP);
pinMode(input1Pin, INPUT_PULLUP); // these lines declare each of the buttons as an input

// driver config
// power supply voltage [V]
driver.voltage_power_supply = 12;
// 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 = 12;
driver.init();
// link the motor and the driver
motor.linkDriver(&driver);

// limiting motor current (provided resistance)
motor.current_limit = 0.35; // [Amps]

// open loop control config
motor.controller = MotionControlType::velocity_openloop;

// init motor hardware
motor.init();

Serial.begin(115200);
Serial.println(“Motor ready!”);
Serial.println(“Set target velocity [rad/s]”);
_delay(1000);

motor.disable(); // stops motor
}

void loop() {

 // read the state of the switch/button:

int buttonState = digitalRead(BUTTON_PIN);

// print out the button’s state
Serial.println(buttonState);

// button on pin 10
if (digitalRead(10)==LOW) { // high state means on
motor.disable(); // stops motor
}
// button on pin 11
if (digitalRead(11)==LOW) { // high state means on
motor.move(28.954); // this button sets the speed to 33.3rpm
}

// button on pin 12
if (digitalRead(12)==LOW) { // high state means on
motor.move(39.071); // this button sets the speed to 45rpm
}
// button on pin 13
if (digitalRead(13)==LOW) { // high state means on
motor.enable(); // starts motor
doonce = 1;
delay(debouncetime);
}

}

It’s going to take me perhaps a day or two due to my present load.

Cheers,
Valentine

Zero rush my friend, I am super grateful for your time. You guys are incredible!!!

@jazzboy

What MCU are you using?

Cheers,
Valentine

Arduino Uno. Specifically Elegoo R3.

@jazzboy

This is cold code, I wrote it without testing it.

See if it works and what breaks.

Cheers,
Valentine

// Open loop motor control example
#include <SimpleFOC.h>
#include "KalmanFilter.h"

#define DEBUG true

KalmanFilter velocityKalmanFilter;

//static const bool doonce = 1;
//int debouncetime = 10000;

static bool motorRunning = false;

int stopButtonState = 0;
int startButtonState = 0;
int rpm333ButtonState = 0;
int rpm450ButtonState = 0;

static const float RPM333 = 28.954;  // 33.3 RPM
static const float RPM450 = 39.071;  // 45.0 RPM

// this declares each of our buttons and their pins
// make sure that you use the pins your buttons are wired to
static const int MOTOR_STRT_PIN = 13;
static const int MOTOR_R450_PIN = 12;
static const int MOTOR_R333_PIN = 11;
static const int MOTOR_STOP_PIN = 10;

// BLDC motor & driver instance
// BLDCMotor motor = BLDCMotor(pole pair number);
BLDCMotor motor = BLDCMotor(7, 5.0, 255.0);
// BLDCDriver3PWM driver = BLDCDriver3PWM(pwmA, pwmB, pwmC, Enable(optional));
BLDCDriver3PWM driver = BLDCDriver3PWM(9, 5, 6, 8);

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

float target_velocity = 0;
unsigned long printTimer = 0;
unsigned long velocityTimer = 0;

void setup() {
  pinMode(MOTOR_STRT_PIN, INPUT_PULLUP);
  pinMode(MOTOR_STOP_PIN, INPUT_PULLUP);
  pinMode(MOTOR_R333_PIN, INPUT_PULLUP);
  pinMode(MOTOR_R450_PIN, INPUT_PULLUP);  // these lines declare each of the buttons as an input

  // driver config
  // power supply voltage [V]
  driver.voltage_power_supply = 12;
  // 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 = 12;
  driver.init();
  // link the motor and the driver
  motor.linkDriver(&driver);

  // limiting motor current (provided resistance)
  motor.current_limit = 0.35;  // [Amps]

  // open loop control config
  motor.controller = MotionControlType::velocity_openloop;

  // init motor hardware
  motor.init();

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

  motor.disable();  // stops motor
}

void loop() {

  // read the state of the switch/button:
  stopButtonState = digitalRead(MOTOR_STOP_PIN);
  startButtonState = digitalRead(MOTOR_STOP_PIN);
  rpm333ButtonState = digitalRead(MOTOR_STOP_PIN);
  rpm450ButtonState = digitalRead(MOTOR_STOP_PIN);

  // Print out the button’s state
  // Printing here at each loop is very bad
  // Need to print each 100 millis or something at least (10 times per second)
  // Else you will choke the loops and stall / stutter the motor
  // In final code must remove the print statements
  if (DEBUG) {
    if ((millis() - printTimer) > 100) {

      Serial.print("stopButtonState   = ");
      Serial.println(stopButtonState);
      Serial.print("startButtonState  = ");
      Serial.println(startButtonState);
      Serial.print("rpm333ButtonState = ");
      Serial.println(rpm333ButtonState);
      Serial.print("rpm450ButtonState = ");
      Serial.println(rpm450ButtonState);

      printTimer = millis();
    }
  }

  // button on pin 10
  if (!stopButtonState) {  // high state means on
    motor.disable();       // stops motor
    motorRunning = false;
    delay(1000);           // block the loop for 1 second
  } else {

    // button on pin 13
    if (startButtonState) {  // high state means on
      if (!motorRunning) {
        motor.enable();  // starts motor
        delay(1000);     // block the loop for 1 second
        motorRunning = true;
        target_velocity = 0;
      }

      // check buttons states every 100 millis
      // assuming sticky radio-buttons
      // e;se the logic will need to be changed
      if ((millis() - velocityTimer) > 100) {

        // button on pin 11
        if (rpm333ButtonState && (!rpm450ButtonState)) {  // high state means on
          target_velocity = velocityKalmanFilter.getEstimate(RPM333, 2.0, 2.0, 0.01);
        }

        // button on pin 12
        if (rpm450ButtonState && (!rpm333ButtonState)) {  // high state means on
          target_velocity = velocityKalmanFilter.getEstimate(RPM450, 2.0, 2.0, 0.01);
        }
      }

      velocityTimer = millis();
    }
    motor.move(target_velocity);
  }
}

/*
[c] Valentine 2020

One-dimensional Kalman Filtering for simple, well behaved time series
physical measurements such as angle, temperature, velocity, etc.
This algorithm works by a two-phase process. For the prediction phase, the Kalman filter produces estimates
of the current state variables, along with their uncertainties. Once the outcome of the next measurement
(necessarily corrupted with some error, including random noise) is observed, these estimates are updated
using a weighted average, with more weight being given to estimates with greater certainty.
The algorithm is recursive. It can operate in real time, using only the present input measurements and
the state calculated previously and its uncertainty matrix; no additional past information is required.
The predicted value could be fine-tuned by modifying the error and system noise inputs real-time to achieve optimal results.
This however will require extending the class to monitor error and noise real-time and is a very complex topic.
Fun fact, the first publicly known application was made at NASA Ames Research Center in the early 1960s
during feasibility studies for circumlinear navigation and control of the Apollo space capsule.
The algorithm was implemented in assembler in magnetic core memory on the capsule board navigation module.
Some decent default values: measurement_error = 2; estimation_error = 2; system_noise = 0.01;

Example:

// Place outside, before setup and loop
include <KalmanFilter.h>
KalmanFilter batteryKalmanFilter;

// Place inside the loop()
// assuming 0 to 1024 range
battVoltage = 3.3*(float)analogRead(4)/1024.0;
battVoltage = batteryKalmanFilter.getEstimate(battVoltage, 2.0, 2.0, 0.01);
*/
    class KalmanFilter
    {
    public:
      KalmanFilter();
      float getEstimate(float signal_measurement, float measurement_error, float estimation_error, float system_noise);
    private:
      float previous_estimate = 0.0;
      float gain = 0.0;
      float signal_estimate = 0.0;
      float error_estimate = 0.0;
    };

    KalmanFilter::KalmanFilter(){};

    float KalmanFilter::getEstimate(float signal_measurement, float measurement_error, float estimation_error, float system_noise)
    {
      if ( error_estimate == 0.0 ) error_estimate = measurement_error;
      gain = error_estimate/(error_estimate + measurement_error);
      signal_estimate = previous_estimate + gain * (signal_measurement - previous_estimate);
      error_estimate =  (1.0 - gain)*error_estimate + fabs(previous_estimate-signal_estimate)*system_noise;
      previous_estimate=signal_estimate;
      return signal_estimate;
    }