Skip to content

Instantly share code, notes, and snippets.

@devyte
Last active September 24, 2020 23:08
Show Gist options
  • Save devyte/2b5d797663726095fbe616646ce3877d to your computer and use it in GitHub Desktop.
Save devyte/2b5d797663726095fbe616646ce3877d to your computer and use it in GitHub Desktop.
Stepper accel limiter
class AccelLimiter
{
public:
int accel; // unit is Hz/s, i.e.: pulses/s2, always positive
int maxAccel; //this is the physical limitation. A frequency change above this value between consecutive updates is not allowed. Always positive.
AccelLimiter(int accel, int maxAccel)
: accel(accel), maxAccel(maxAccel)
{
}
private:
unsigned long prevFreq = 0;
unsigned long prevTime = 0;
bool firstTime = false;
unsigned long targetFreq = 0;
public:
unsigned long calculateNewFreq()
{
if(firstTime)
{
prevTime = millis();
firstTime = false;
return 0;
}
unsigned long now = millis();
unsigned long deltaTime = now - prevTime; //should be about the same as the periodic update time
long deltaFreq = accel * deltaTime / 1000; //accel is Hz/s, deltaTime is in ms
deltaFreq = std::min(deltaFreq, maxAccel); //cap rate at maxAccel between updates, this ensures sticking to the limit if there is stretching of time between accel updates
long errorFreq = targetFreq - prevFreq; //This says how far away we are from the target speed (may be negative)
//cap the freq error: if target is closer than deltaFreq, then just change by the missing amount, because the entire delta would overshoot
if(errorFreq >= 0)
deltaFreq = std::min(errorFreq, deltaFreq);
else
deltaFreq = std::max(errorFreq, -deltaFreq);
long newFreq = prevFreq + deltaFreq;
newFreq = std::max(newFreq, 0L); //because paranoid
prevFreq = newFreq;
prevTime = now;
return newFreq;
}
void setTargetFreq(int newTarget) {targetFreq = newTarget;}
void reset() {prevFreq = 0; firstTime = true;}
bool isStopped() {return prevFreq == 0;}
};
#include "AccelLimiter.h"
...
void setup()
{
...
}
/*
Use should be along the lines of the below.
Need to check that setting the same speed again in the waveform gen has no negative impact. If it does, then StepperManager::setSpeed() should only set a new speed if different than current speed.
*/
periodic timeToUpdate(100);
StepperManager s1(...);
StepperManager s2(...);
AccelLimiter a1(...); //args are: desired accel, maxAccel
AccelLimiter a2(...);
void stepperOverseer(StepperManager s, AccelLimiter a)
{
//Beware: there is a min on the settable frequency for a stepper. The AccelLimiter is not aware of that. Going to freq == 0 means trying to set a speed below that min, which the waveform gen should ignore.
//To be explicit: AccelLimiter::isStopped() can be true, returned freq will be 0, but the steppers will continue to move when setting speed below the waveform gen min.
//The following check is meant to address that: if the accel shows freq dropped to 0, just stop the stepper. In between the min waveform gen freq and 0, the stepper should continue to move at min freq.
if(a.isStopped())
{
s.stop();
}
else
{
auto newFreq1 = a1.calculateNewFreq();
s1.updateSpeed(newFreq1);
}
}
void loop()
{
...
auto newTargetFreq1 = GetNewTargetFreq1FromSomewhere();
auto newTargetFreq2 = GetNewTargetFreq2FromSomewhere();
a1.setTargetFreq(newTargetFreq1);
a2.setTargetFreq(newTargetFreq2);
if(timeToUpdate)
{
stepperOverseer(s1, a1);
stepperOverseer(s2, a2);
}
...
}
@moose4621
Copy link

Rev 4 error list from arduino ide.

Arduino: 1.8.13 (Linux), Board: "LOLIN(WEMOS) D1 R2 & mini, 80 MHz, Flash, Legacy (new can return nullptr), All SSL ciphers (most compatible), 4MB (FS:3MB OTA:~512KB), v2 Lower Memory, Disabled, None, Only Sketch, 921600"

In file included from /home/chris/Arduino/stepper_accel/stepper_accel.ino:3:0:
sketch/AccelLimiter.h: In member function 'long unsigned int AccelLimiter::calculateNewFreq()':
AccelLimiter.h:34:45: error: no matching function for call to 'min(long int&, int&)'
deltaFreq = std::min(deltaFreq, maxAccel); //cap rate at maxAccel between updates, this ensures sticking to the limit if there is stretching of time between accel updates
^
sketch/AccelLimiter.h:34:45: note: candidates are:
In file included from /home/chris/.arduino15/packages/esp8266/tools/xtensa-lx106-elf-gcc/2.5.0-4-b40a506/xtensa-lx106-elf/include/c++/4.8.2/algorithm:61:0,
from /home/chris/.arduino15/packages/esp8266/hardware/esp8266/2.7.4/cores/esp8266/Arduino.h:238,
from /home/chris/Arduino/stepper_accel/stepper_accel.ino:1:
/home/chris/.arduino15/packages/esp8266/tools/xtensa-lx106-elf-gcc/2.5.0-4-b40a506/xtensa-lx106-elf/include/c++/4.8.2/bits/stl_algobase.h:193:5: note: template const _Tp& std::min(const _Tp&, const _Tp&)
min(const _Tp& __a, const _Tp& __b)
^
/home/chris/.arduino15/packages/esp8266/tools/xtensa-lx106-elf-gcc/2.5.0-4-b40a506/xtensa-lx106-elf/include/c++/4.8.2/bits/stl_algobase.h:193:5: note: template argument deduction/substitution failed:
In file included from /home/chris/Arduino/stepper_accel/stepper_accel.ino:3:0:
sketch/AccelLimiter.h:34:45: note: deduced conflicting types for parameter 'const _Tp' ('long int' and 'int')
deltaFreq = std::min(deltaFreq, maxAccel); //cap rate at maxAccel between updates, this ensures sticking to the limit if there is stretching of time between accel updates
^
In file included from /home/chris/.arduino15/packages/esp8266/tools/xtensa-lx106-elf-gcc/2.5.0-4-b40a506/xtensa-lx106-elf/include/c++/4.8.2/algorithm:61:0,
from /home/chris/.arduino15/packages/esp8266/hardware/esp8266/2.7.4/cores/esp8266/Arduino.h:238,
from /home/chris/Arduino/stepper_accel/stepper_accel.ino:1:
/home/chris/.arduino15/packages/esp8266/tools/xtensa-lx106-elf-gcc/2.5.0-4-b40a506/xtensa-lx106-elf/include/c++/4.8.2/bits/stl_algobase.h:239:5: note: template<class _Tp, class _Compare> const _Tp& std::min(const _Tp&, const _Tp&, _Compare)
min(const _Tp& __a, const _Tp& __b, _Compare __comp)
^
/home/chris/.arduino15/packages/esp8266/tools/xtensa-lx106-elf-gcc/2.5.0-4-b40a506/xtensa-lx106-elf/include/c++/4.8.2/bits/stl_algobase.h:239:5: note: template argument deduction/substitution failed:
In file included from /home/chris/Arduino/stepper_accel/stepper_accel.ino:3:0:
sketch/AccelLimiter.h:34:45: note: deduced conflicting types for parameter 'const _Tp' ('long int' and 'int')
deltaFreq = std::min(deltaFreq, maxAccel); //cap rate at maxAccel between updates, this ensures sticking to the limit if there is stretching of time between accel updates
^
In file included from /home/chris/.arduino15/packages/esp8266/tools/xtensa-lx106-elf-gcc/2.5.0-4-b40a506/xtensa-lx106-elf/include/c++/4.8.2/algorithm:62:0,
from /home/chris/.arduino15/packages/esp8266/hardware/esp8266/2.7.4/cores/esp8266/Arduino.h:238,
from /home/chris/Arduino/stepper_accel/stepper_accel.ino:1:
/home/chris/.arduino15/packages/esp8266/tools/xtensa-lx106-elf-gcc/2.5.0-4-b40a506/xtensa-lx106-elf/include/c++/4.8.2/bits/stl_algo.h:4221:5: note: template _Tp std::min(std::initializer_list<_Tp>)
min(initializer_list<_Tp> __l)
^
/home/chris/.arduino15/packages/esp8266/tools/xtensa-lx106-elf-gcc/2.5.0-4-b40a506/xtensa-lx106-elf/include/c++/4.8.2/bits/stl_algo.h:4221:5: note: template argument deduction/substitution failed:
In file included from /home/chris/Arduino/stepper_accel/stepper_accel.ino:3:0:
sketch/AccelLimiter.h:34:45: note: mismatched types 'std::initializer_list<_Tp>' and 'long int'
deltaFreq = std::min(deltaFreq, maxAccel); //cap rate at maxAccel between updates, this ensures sticking to the limit if there is stretching of time between accel updates
^
In file included from /home/chris/.arduino15/packages/esp8266/tools/xtensa-lx106-elf-gcc/2.5.0-4-b40a506/xtensa-lx106-elf/include/c++/4.8.2/algorithm:62:0,
from /home/chris/.arduino15/packages/esp8266/hardware/esp8266/2.7.4/cores/esp8266/Arduino.h:238,
from /home/chris/Arduino/stepper_accel/stepper_accel.ino:1:
/home/chris/.arduino15/packages/esp8266/tools/xtensa-lx106-elf-gcc/2.5.0-4-b40a506/xtensa-lx106-elf/include/c++/4.8.2/bits/stl_algo.h:4226:5: note: template<class _Tp, class _Compare> _Tp std::min(std::initializer_list<_Tp>, _Compare)
min(initializer_list<_Tp> __l, _Compare __comp)
^
/home/chris/.arduino15/packages/esp8266/tools/xtensa-lx106-elf-gcc/2.5.0-4-b40a506/xtensa-lx106-elf/include/c++/4.8.2/bits/stl_algo.h:4226:5: note: template argument deduction/substitution failed:
In file included from /home/chris/Arduino/stepper_accel/stepper_accel.ino:3:0:
sketch/AccelLimiter.h:34:45: note: mismatched types 'std::initializer_list<_Tp>' and 'long int'
deltaFreq = std::min(deltaFreq, maxAccel); //cap rate at maxAccel between updates, this ensures sticking to the limit if there is stretching of time between accel updates
^
exit status 1
no matching function for call to 'min(long int&, int&)'

This report would have more information with
"Show verbose output during compilation"
option enabled in File -> Preferences.

@moose4621
Copy link

`#include "Arduino.h"
#include "steppermanager.h"
#include "AccelLimiter.h"

#include <PolledTimeout.h>

namespace
{
constexpr int pinEnS1 = 5;
constexpr int pinPulseS1 = 4;
constexpr int linkagePin = 14;

StepperManager stepper1(pinEnS1, pinPulseS1);
AccelLimiter a1(30, 300); //args are: desired accel, maxAccel

}
bool accelSwitch = true;
using periodic = esp8266::polledTimeout::periodicMs;
//periodic accelerator(1500);
periodic timeToUpdate(10);
void stepperOverseer(StepperManager s, AccelLimiter a)
{
if(a.isStopped())
{
s.stop();
}
else
{
auto newFreq1 = a1.calculateNewFreq();
stepper1.updateSpeed(newFreq1);
Serial.println(newFreq1);
}
}

void setup(){
Serial.begin(115200);
Serial.print("starting");
}
void loop(){
stepper1.handle();

auto newTargetFreq1 = (2000);
// auto newTargetFreq2 = GetNewTargetFreq2FromSomewhere();

a1.setTargetFreq(newTargetFreq1);
// a2.setTargetFreq(newTargetFreq2);

if(timeToUpdate)
{
stepperOverseer(stepper1, a1);
Serial.print("stepper speed ");
Serial.println(stepper1.getSpeed());
// stepperOverseer(s2, a2);
}

}`

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment