implement ramp-ups and ramp-downs for motor

This commit is contained in:
Leonetienne
2025-12-07 23:28:04 +01:00
parent 6f468eb880
commit d9f81e7c68
5 changed files with 349 additions and 82 deletions
+63
View File
@@ -1,5 +1,68 @@
#include "MainLoop.h" #include "MainLoop.h"
#include <iostream>
#include <thread>
#include <chrono>
MainLoop::MainLoop() noexcept:
oSerialBus(L"COM4"),
j0(
0,
oSerialBus,
0,
30.0,
180.0,
125.0
),
j1(
1,
oSerialBus,
0.0,
0.0,
120.0,
51.0
),
j2(
2,
oSerialBus,
0.0,
0.0,
180.0,
115.0
)
{
if (!oSerialBus.isOpen()) {
std::cerr << "Serial bus is not open! Is the COM port busy?" << std::endl;
}
j0.moveHomeImmediate();
j1.moveHomeImmediate();
j2.moveHomeImmediate();
}
MainLoop& MainLoop::getInstance()
{
static MainLoop instance;
return instance;
}
MainLoop::~MainLoop() noexcept
{
oSerialBus.close();
}
void MainLoop::run() void MainLoop::run()
{ {
static std::chrono::steady_clock::time_point lastBegin = std::chrono::high_resolution_clock::now();
while (isRunning) {
std::chrono::steady_clock::time_point begin = std::chrono::high_resolution_clock::now();
const double frametime = (begin - lastBegin).count() * 10e-7; // in milliseconds
j0.update(frametime);
j1.update(frametime);
j2.update(frametime);
lastBegin = std::chrono::high_resolution_clock::now();
}
} }
+17
View File
@@ -1,7 +1,24 @@
#pragma once #pragma once
#include "OSerialBus.h"
#include "Motor.h"
// Singleton-class, use MainLoop::getInstance() to get the only instance of the runtime
class MainLoop class MainLoop
{ {
public: public:
static MainLoop& getInstance();
MainLoop(const MainLoop& other) = delete;
MainLoop(MainLoop&& other) = delete;
~MainLoop() noexcept;
void run(); void run();
private:
MainLoop() noexcept;
OSerialBus oSerialBus;
Motor j0;
Motor j1;
Motor j2;
bool isRunning = true;
}; };
+226 -24
View File
@@ -1,67 +1,269 @@
#include "Motor.h" #include "Motor.h"
#include <iostream>
#include <algorithm> #include <algorithm>
constexpr double RAMPUP_REDUCTION_FAC = 0.1;
constexpr double RAMPUP_RECOVERY_SPEED = 0.1;
constexpr double SPEED_CORRECTION_FAC = 0.01; // Otherwhise speed=1.0 would be way too fast
Motor::Motor( Motor::Motor(
const std::size_t index, const uint8_t index,
OSerialBus& serialBus, OSerialBus& serialBus,
const double initialPosition, const double initialPosition,
const double mechanicalMinDeg, const double mechanicalMinPosition,
const double mechanicalMaxDeg, const double mechanicalMaxPosition,
const double homePosition const double homePosition,
const double initialSpeed
) noexcept: ) noexcept:
index(index), index(index),
serialBus(serialBus), serialBus(serialBus),
currentPosition(initialPosition), currentPosition(initialPosition),
mechanicalMinDeg(mechanicalMinDeg), targetPosition(currentPosition),
mechanicalMaxDeg(mechanicalMaxDeg), moveStartPosition(currentPosition),
mechanicalMinPosition(mechanicalMinPosition),
mechanicalMaxPosition(mechanicalMaxPosition),
homePosition(homePosition) homePosition(homePosition)
{ {
setSpeed(initialSpeed);
} }
Motor::Motor(Motor&& other) noexcept: Motor::Motor(Motor&& other) noexcept:
index(other.index), index(other.index),
serialBus(other.serialBus), serialBus(other.serialBus),
currentPosition(other.currentPosition), currentPosition(other.currentPosition),
mechanicalMinDeg(other.mechanicalMinDeg), targetPosition(other.targetPosition),
mechanicalMaxDeg(other.mechanicalMaxDeg), moveStartPosition(other.moveStartPosition),
homePosition(other.homePosition) mechanicalMinPosition(other.mechanicalMinPosition),
mechanicalMaxPosition(other.mechanicalMaxPosition),
homePosition(other.homePosition),
targetSpeed(other.targetSpeed),
speedLow(other.speedLow)
{ {
// Make moved object inert
other.disabled = true;
} }
bool Motor::moveTo(const double theta) noexcept bool Motor::moveTo(const double theta, const std::optional<double> speed) noexcept
{ {
if (theta < mechanicalMinDeg || theta > mechanicalMaxDeg) { if (disabled) {
std::cerr << "Trying to act on disabled motor object!" << std::endl;
return false; return false;
} }
char buffer[32]; if (speed.has_value()) {
snprintf(buffer, sizeof(buffer), "MA %d %d", (int)index, (int)theta); setSpeed(speed.value());
bool result = serialBus.sendl(buffer); }
if (!isPositionInMechanicalRange(theta)) {
return false;
}
moveStartPosition = currentPosition;
targetPosition = theta;
return false;
}
bool Motor::moveToImmediate(const double theta) noexcept
{
if (disabled) {
std::cerr << "Trying to act on disabled motor object!" << std::endl;
return false;
}
if (!isPositionInMechanicalRange(theta)) {
return false;
}
const bool result = sendMACommand(theta);
if (result) { if (result) {
currentPosition = theta; currentPosition = theta;
targetPosition = theta;
moveStartPosition = theta;
} }
return result; return result;
} }
bool Motor::moveBy(const double theta) noexcept
{
const double acuteTarget = currentPosition + theta;
if (acuteTarget < mechanicalMinDeg || acuteTarget > mechanicalMaxDeg) { bool Motor::moveBy(const double theta, const std::optional<double> speed) noexcept
{
if (disabled) {
std::cerr << "Trying to act on disabled motor object!" << std::endl;
return false; return false;
} }
char buffer[32]; if (speed.has_value()) {
snprintf(buffer, sizeof(buffer), "MA %d %d", (int)index, (int)acuteTarget); setSpeed(speed.value());
bool result = serialBus.sendl(buffer); }
const double acuteTarget = currentPosition + theta;
if (!isPositionInMechanicalRange(acuteTarget)) {
return false;
}
moveStartPosition = moveStartPosition;
targetPosition = acuteTarget;
return false;
}
bool Motor::moveByImmediate(const double theta) noexcept
{
if (disabled) {
std::cerr << "Trying to act on disabled motor object!" << std::endl;
return false;
}
const double acuteTarget = currentPosition + theta;
if (!isPositionInMechanicalRange(acuteTarget)) {
return false;
}
const bool result = sendMACommand(acuteTarget);
if (result) { if (result) {
moveStartPosition = currentPosition;
currentPosition = acuteTarget; currentPosition = acuteTarget;
targetPosition = acuteTarget;
} }
return result; return result;
} }
bool Motor::moveHome() noexcept bool Motor::moveHome(const std::optional<double> speed) noexcept
{ {
return moveTo(homePosition); if (disabled) {
std::cerr << "Trying to act on disabled motor object!" << std::endl;
return false;
}
return moveTo(homePosition, speed);
}
bool Motor::moveHomeImmediate() noexcept
{
if (disabled) {
std::cerr << "Trying to act on disabled motor object!" << std::endl;
return false;
}
return moveToImmediate(homePosition);
}
bool Motor::stopMoving() noexcept
{
if (disabled) {
std::cerr << "Trying to act on disabled motor object!" << std::endl;
return false;
}
targetPosition = currentPosition;
}
void Motor::pauseMoving() noexcept
{
if (disabled) {
std::cerr << "Trying to act on disabled motor object!" << std::endl;
return;
}
paused = true;
}
void Motor::resumeMoving() noexcept
{
if (disabled) {
std::cerr << "Trying to act on disabled motor object!" << std::endl;
return;
}
paused = false;
}
bool Motor::assumeTargetPositionImmediately() noexcept
{
if (disabled) {
std::cerr << "Trying to act on disabled motor object!" << std::endl;
return false;
}
return moveToImmediate(targetPosition);
}
void Motor::update(const double frametime)
{
if (disabled) {
std::cerr << "Trying to act on disabled motor object!" << std::endl;
return;
}
if (paused) {
return;
}
handleRampUp(frametime);
}
void Motor::setSpeed(const double speed) noexcept
{
this->targetSpeed = speed;
this->speedLow = speed * RAMPUP_REDUCTION_FAC;
}
bool Motor::isPositionInMechanicalRange(const double pos) const noexcept
{
if (disabled) {
std::cerr << "Trying to act on disabled motor object!" << std::endl;
return false;
}
return pos >= mechanicalMinPosition && pos <= mechanicalMaxPosition;
}
void Motor::handleRampUp(const double frametime)
{
constexpr double epsilon = 0.1;
if (std::abs(currentPosition - targetPosition) > epsilon) {
const double nextPositionStep = currentPosition + (targetPosition - moveStartPosition) * calculateCurrentSpeed() * SPEED_CORRECTION_FAC;
if (!isPositionInMechanicalRange(nextPositionStep)) {
return;
}
const bool result = sendMACommand(nextPositionStep);
if (result) {
currentPosition = nextPositionStep;
}
}
}
bool Motor::sendMACommand(const double targetPos) noexcept
{
char buffer[32];
snprintf(buffer, sizeof(buffer), "MA %d %d", index, (int)targetPos);
return serialBus.sendl(buffer);
}
double Motor::calculateCurrentSpeed() const noexcept
{
if (targetPosition == moveStartPosition) {
return 0.0;
}
auto smoothstep = [](double x) noexcept {
return x * x * (3.0 - 2.0 * x);
};
const double t = ((currentPosition - moveStartPosition) / (targetPosition - moveStartPosition));
// Accelleration
if (t < RAMPUP_RECOVERY_SPEED) {
return speedLow + (targetSpeed - speedLow) * smoothstep(t / RAMPUP_RECOVERY_SPEED);
}
// Decceleration
else if (t > 1.0 - RAMPUP_RECOVERY_SPEED) {
return speedLow + (targetSpeed - speedLow) * smoothstep((1.0 - t) / RAMPUP_RECOVERY_SPEED);
}
else {
return targetSpeed;
}
} }
+41 -13
View File
@@ -1,36 +1,64 @@
#pragma once #pragma once
#include "OSerialBus.h" #include "OSerialBus.h"
#include <optional>
class Motor class Motor
{ {
public: public:
Motor( Motor(
const std::size_t index, const uint8_t index,
OSerialBus& serialBus, OSerialBus& serialBus,
const double initialPosition, const double initialPosition,
const double mechanicalMinDeg, const double mechanicalMinPosition,
const double mechanicalMaxDeg, const double mechanicalMaxPosition,
const double homePosition const double homePosition,
const double initialSpeed = 0.5
) noexcept; ) noexcept;
Motor(const Motor& other) = delete; Motor(const Motor& other) = delete;
Motor(Motor&& other) noexcept; Motor(Motor&& other) noexcept;
bool moveTo(const double theta) noexcept; // Will move the motor immediately to the desired position (don't do that)
bool moveBy(const double theta) noexcept; bool moveToImmediate(const double theta) noexcept;
bool moveHome() noexcept; bool moveTo(const double theta, const std::optional<double> speed) noexcept;
bool moveBy(const double theta, const std::optional<double> speed) noexcept;
bool moveByImmediate(const double theta) noexcept;
bool moveHome(const std::optional<double> speed) noexcept;
bool moveHomeImmediate() noexcept;
bool stopMoving() noexcept;
void pauseMoving() noexcept;
void resumeMoving() noexcept;
bool assumeTargetPositionImmediately() noexcept;
void update(const double frametime);
std::size_t getIndex() const { return index; }; std::size_t getIndex() const { return index; };
double getCurrentPosition() const { return currentPosition; }; double getCurrentPosition() const { return currentPosition; };
double getMechanicalMinDeg() const { return mechanicalMinDeg; }; double getTargetPosition() const { return targetPosition; };
double getMechanicalMaxDeg() const { return mechanicalMaxDeg; }; double getSpeed() const { return targetSpeed; };
void setSpeed(const double speedTarget) noexcept;
double getMechanicalMinPosition() const { return mechanicalMinPosition; };
double getMechanicalMaxPosition() const { return mechanicalMaxPosition; };
double getHomePosition() const { return homePosition; }; double getHomePosition() const { return homePosition; };
double getPaused() const { return paused; };
bool isPositionInMechanicalRange(const double pos) const noexcept;
private: private:
const std::size_t index; void handleRampUp(const double frametime);
bool sendMACommand(const double targetPos) noexcept;
// Will calculate the current speed for rampup based on currentPosition, moveStartPosition, and targetPosition
double calculateCurrentSpeed() const noexcept;
const uint8_t index;
OSerialBus& serialBus; OSerialBus& serialBus;
double currentPosition = 0; double currentPosition; // we are here
const double mechanicalMinDeg; double moveStartPosition; // last move instruction started here
const double mechanicalMaxDeg; double targetPosition; // movement should end here
double targetSpeed; // Motor should ideally move at this speed
double speedLow; // End- and startpoint of ramp-up phase
const double mechanicalMinPosition;
const double mechanicalMaxPosition;
const double homePosition; const double homePosition;
bool disabled = false;
bool paused = false;
}; };
+2 -45
View File
@@ -1,51 +1,8 @@
#include <iostream>
#include "MainLoop.h" #include "MainLoop.h"
#include "OSerialBus.h"
#include "Motor.h"
#include <thread>
#include <chrono>
int main() int main()
{ {
OSerialBus bus(L"COM4"); MainLoop& mainloop = MainLoop::getInstance();
if (!bus.isOpen()) {
std::cerr << "Serial bus is not open! Is the COM port busy?" << std::endl;
return -1;
}
Motor j0(
0,
bus,
125.0,
30.0,
180.0,
125.0
);
j0.moveHome();
Motor j1(
1,
bus,
51.0,
0.0,
120.0,
51.0
);
j1.moveHome();
Motor j2(
2,
bus,
115.0,
0.0,
180.0,
115.0
);
j2.moveHome();
bus.close();
MainLoop mainloop;
mainloop.run(); mainloop.run();
} }