## Logistical Curve

This sketch contains a function softCurve which, given a value between 0 and 1 returns another value between 0 and 1 that has a "soft" start and end.

The logistical curve is infinitely differentiable, and so the speed, the acceleration, the jerk, and derivatives beyond that all have non-infinite values. The curve is, however, infinitely long. To accommodate that, this sketch clips the curve at -3 and 4. Some experimentation may be required, depending on your application.

#include <Servo.h>
Servo s;
const uint32_t SPEED = 1000;
const int target[] = {
10, 60, 170, 90
};
const int targets = sizeof(target) / sizeof(*target);
uint32_t pollMs;
uint32_t startMs;
int whichTarget = 0;
void setup() {
s.attach(6);
pinMode(LED_BUILTIN, OUTPUT);
s.write(target[0]);
delay(SPEED);
startMs = millis();
digitalWrite(LED_BUILTIN, HIGH);
}
void loop() {
if (millis() == pollMs) return;
pollMs = millis();
uint32_t ms = millis();
if (ms - startMs >= SPEED) {
digitalWrite(LED_BUILTIN, LOW);
delay(SPEED);
digitalWrite(LED_BUILTIN, HIGH);
whichTarget = (whichTarget + 1) % targets;
startMs = ms = millis();
}
const int a = target[whichTarget];
const int b = target[(whichTarget + 1) % targets];
s.write(
softCurve((ms - startMs) / (double)SPEED) * (b - a) + a
);
}
// SOFT CURVE implementation
#define LOGISTICAL(x) (exp(x)/(exp(x)+1))
double logistical(double x) {
double y = exp(x);
return y / (y + 1);
}
// pecomputed constants
// I make the start of the movement a little more jerky than the end
// making these values larger makes the start and end of the range of movement
// softer, at the price of the servo needing to move more quickly towards
// the center of its motion.
const double STARTING_X = -3;
const double ENDING_X = 4;
const double STARTING_Y = LOGISTICAL(STARTING_X);
const double ENDING_Y = LOGISTICAL(ENDING_X);
/*
* Compute a curve with a soft acelleration and decelleration, and less jerk
* @param amt a value between 0 and 1, 0 being the start of the soft motion, 1 the end
* @return a value between 0 and 1. Do arithmetic to get the range you want.
*/
double softCurve(double amt) {
double x = STARTING_X + amt * (ENDING_X - STARTING_X);
double y = logistical(x);
// convert to a 0-1 value,
y = (y - STARTING_Y) / (ENDING_Y - STARTING_Y);
return y;
}