2025-12-07 20:56:43 +01:00
|
|
|
#include "Motor.h"
|
2025-12-07 23:28:04 +01:00
|
|
|
#include <iostream>
|
2025-12-07 20:56:43 +01:00
|
|
|
#include <algorithm>
|
2025-12-14 19:59:51 +01:00
|
|
|
#include <array>
|
2025-12-07 20:56:43 +01:00
|
|
|
|
2025-12-07 23:28:04 +01:00
|
|
|
constexpr double RAMPUP_REDUCTION_FAC = 0.1;
|
|
|
|
|
constexpr double RAMPUP_RECOVERY_SPEED = 0.1;
|
2025-12-08 19:52:27 +01:00
|
|
|
constexpr double SPEED_CORRECTION_FAC = 100000.0;
|
|
|
|
|
constexpr double MOVEMENT_COMPARISON_EPSILON = 0.01;
|
2025-12-07 23:28:04 +01:00
|
|
|
|
2025-12-07 20:56:43 +01:00
|
|
|
Motor::Motor(
|
2025-12-07 23:28:04 +01:00
|
|
|
const uint8_t index,
|
2025-12-07 20:56:43 +01:00
|
|
|
OSerialBus& serialBus,
|
|
|
|
|
const double initialPosition,
|
2025-12-14 19:59:51 +01:00
|
|
|
const double safeMinPos,
|
|
|
|
|
const double safeMaxPos,
|
2025-12-07 23:28:04 +01:00
|
|
|
const double initialSpeed
|
2025-12-07 20:56:43 +01:00
|
|
|
) noexcept:
|
|
|
|
|
index(index),
|
|
|
|
|
serialBus(serialBus),
|
|
|
|
|
currentPosition(initialPosition),
|
2025-12-07 23:28:04 +01:00
|
|
|
targetPosition(currentPosition),
|
|
|
|
|
moveStartPosition(currentPosition),
|
2025-12-14 19:59:51 +01:00
|
|
|
safeMinPos(safeMinPos),
|
|
|
|
|
safeMaxPos(safeMaxPos)
|
2025-12-07 20:56:43 +01:00
|
|
|
{
|
2025-12-07 23:28:04 +01:00
|
|
|
setSpeed(initialSpeed);
|
2025-12-07 20:56:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Motor::Motor(Motor&& other) noexcept:
|
|
|
|
|
index(other.index),
|
|
|
|
|
serialBus(other.serialBus),
|
|
|
|
|
currentPosition(other.currentPosition),
|
2025-12-07 23:28:04 +01:00
|
|
|
targetPosition(other.targetPosition),
|
|
|
|
|
moveStartPosition(other.moveStartPosition),
|
2025-12-14 19:59:51 +01:00
|
|
|
safeMinPos(other.safeMinPos),
|
|
|
|
|
safeMaxPos(other.safeMaxPos),
|
2025-12-07 23:28:04 +01:00
|
|
|
targetSpeed(other.targetSpeed),
|
|
|
|
|
speedLow(other.speedLow)
|
2025-12-07 20:56:43 +01:00
|
|
|
{
|
2025-12-07 23:28:04 +01:00
|
|
|
// Make moved object inert
|
|
|
|
|
other.disabled = true;
|
2025-12-07 20:56:43 +01:00
|
|
|
}
|
|
|
|
|
|
2025-12-07 23:28:04 +01:00
|
|
|
bool Motor::moveTo(const double theta, const std::optional<double> speed) noexcept
|
2025-12-07 20:56:43 +01:00
|
|
|
{
|
2025-12-07 23:28:04 +01:00
|
|
|
if (disabled) {
|
|
|
|
|
std::cerr << "Trying to act on disabled motor object!" << std::endl;
|
2025-12-07 20:56:43 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-07 23:28:04 +01:00
|
|
|
if (speed.has_value()) {
|
|
|
|
|
setSpeed(speed.value());
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-14 19:59:51 +01:00
|
|
|
if (!isPositionInRange(theta)) {
|
2025-12-07 23:28:04 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
targetPosition = theta;
|
2025-12-08 19:52:27 +01:00
|
|
|
moveStartPosition = currentPosition;
|
2025-12-07 23:28:04 +01:00
|
|
|
|
2025-12-08 19:52:27 +01:00
|
|
|
return true;
|
2025-12-07 23:28:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool Motor::moveToImmediate(const double theta) noexcept
|
|
|
|
|
{
|
|
|
|
|
if (disabled) {
|
|
|
|
|
std::cerr << "Trying to act on disabled motor object!" << std::endl;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-14 19:59:51 +01:00
|
|
|
if (!isPositionInRange(theta)) {
|
2025-12-07 23:28:04 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const bool result = sendMACommand(theta);
|
2025-12-07 20:56:43 +01:00
|
|
|
if (result) {
|
|
|
|
|
currentPosition = theta;
|
2025-12-07 23:28:04 +01:00
|
|
|
targetPosition = theta;
|
|
|
|
|
moveStartPosition = theta;
|
2025-12-07 20:56:43 +01:00
|
|
|
}
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2025-12-07 23:28:04 +01:00
|
|
|
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;
|
2025-12-07 20:56:43 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-07 23:28:04 +01:00
|
|
|
if (speed.has_value()) {
|
|
|
|
|
setSpeed(speed.value());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const double acuteTarget = currentPosition + theta;
|
|
|
|
|
|
2025-12-14 19:59:51 +01:00
|
|
|
if (!isPositionInRange(acuteTarget)) {
|
2025-12-07 23:28:04 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
targetPosition = acuteTarget;
|
2025-12-08 19:52:27 +01:00
|
|
|
moveStartPosition = currentPosition;
|
2025-12-07 23:28:04 +01:00
|
|
|
|
2025-12-08 19:52:27 +01:00
|
|
|
return true;
|
2025-12-07 23:28:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
2025-12-14 19:59:51 +01:00
|
|
|
if (!isPositionInRange(acuteTarget)) {
|
2025-12-07 23:28:04 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const bool result = sendMACommand(acuteTarget);
|
2025-12-07 20:56:43 +01:00
|
|
|
if (result) {
|
2025-12-07 23:28:04 +01:00
|
|
|
moveStartPosition = currentPosition;
|
2025-12-07 20:56:43 +01:00
|
|
|
currentPosition = acuteTarget;
|
2025-12-07 23:28:04 +01:00
|
|
|
targetPosition = acuteTarget;
|
2025-12-07 20:56:43 +01:00
|
|
|
}
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-07 23:28:04 +01:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-08 00:59:09 +01:00
|
|
|
handleMovement(frametime);
|
2025-12-07 23:28:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Motor::setSpeed(const double speed) noexcept
|
|
|
|
|
{
|
|
|
|
|
this->targetSpeed = speed;
|
|
|
|
|
this->speedLow = speed * RAMPUP_REDUCTION_FAC;
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-14 19:59:51 +01:00
|
|
|
bool Motor::isPositionInRange(const double pos) const noexcept
|
2025-12-07 23:28:04 +01:00
|
|
|
{
|
|
|
|
|
if (disabled) {
|
|
|
|
|
std::cerr << "Trying to act on disabled motor object!" << std::endl;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-14 19:59:51 +01:00
|
|
|
return pos >= safeMinPos && pos <= safeMaxPos;
|
2025-12-07 23:28:04 +01:00
|
|
|
}
|
|
|
|
|
|
2025-12-08 00:59:09 +01:00
|
|
|
void Motor::handleMovement(const double frametime)
|
2025-12-07 23:28:04 +01:00
|
|
|
{
|
2025-12-08 19:52:27 +01:00
|
|
|
const double remaining = targetPosition - currentPosition;
|
|
|
|
|
if (std::abs(remaining) <= MOVEMENT_COMPARISON_EPSILON) {
|
|
|
|
|
currentPosition = targetPosition;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const double speed = calculateCurrentSpeed();
|
|
|
|
|
const double maxStep = speed * frametime * SPEED_CORRECTION_FAC;
|
|
|
|
|
double step;
|
|
|
|
|
|
|
|
|
|
if (std::abs(remaining) <= std::abs(maxStep)) {
|
|
|
|
|
step = remaining;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
step = (remaining > 0 ? 1.0 : -1.0) * std::abs(maxStep);
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-14 19:59:51 +01:00
|
|
|
const double nextPositionStep = std::clamp(currentPosition + step, safeMinPos, safeMaxPos);
|
2025-12-08 19:52:27 +01:00
|
|
|
|
|
|
|
|
if (sendMACommand(nextPositionStep)) {
|
|
|
|
|
currentPosition = nextPositionStep;
|
2025-12-07 23:28:04 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool Motor::sendMACommand(const double targetPos) noexcept
|
|
|
|
|
{
|
2025-12-14 19:59:51 +01:00
|
|
|
std::array<std::uint8_t, 5> buffer; // 5-byte packets
|
|
|
|
|
|
|
|
|
|
buffer[0] = 0x00; // Instruction: Instruction to MOVE ABSOLUTE
|
|
|
|
|
|
|
|
|
|
// Send motor index as u16
|
|
|
|
|
buffer[1] = 0x00; // Motor index MSB: Won't have more than 255 motors
|
|
|
|
|
buffer[2] = index; // Motor index LSB, take as-is
|
|
|
|
|
|
|
|
|
|
// Send motor degrees as i16
|
|
|
|
|
const std::int16_t deciDegrees = (std::int16_t)(10.0 * targetPos);
|
|
|
|
|
buffer[3] = (deciDegrees >> 8) & 0xFF; // Motor degree MSB
|
|
|
|
|
buffer[4] = deciDegrees & 0xFF; // Motor degree LSB
|
|
|
|
|
|
|
|
|
|
return serialBus.send(buffer.data(), buffer.size());
|
2025-12-07 23:28:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
double Motor::calculateCurrentSpeed() const noexcept
|
|
|
|
|
{
|
|
|
|
|
if (targetPosition == moveStartPosition) {
|
2025-12-08 19:52:27 +01:00
|
|
|
return speedLow;
|
2025-12-07 23:28:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto smoothstep = [](double x) noexcept {
|
|
|
|
|
return x * x * (3.0 - 2.0 * x);
|
|
|
|
|
};
|
|
|
|
|
|
2025-12-08 19:52:27 +01:00
|
|
|
const double t = getCurrentMovementProgress();
|
2025-12-07 23:28:04 +01:00
|
|
|
|
|
|
|
|
// 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;
|
|
|
|
|
}
|
2025-12-07 20:56:43 +01:00
|
|
|
}
|
2025-12-08 19:52:27 +01:00
|
|
|
|
|
|
|
|
double Motor::getCurrentMovementProgress() const noexcept
|
|
|
|
|
{
|
|
|
|
|
return std::clamp((currentPosition - moveStartPosition) / (targetPosition - moveStartPosition), 0.0, 1.0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool Motor::isCurrentlyMoving() const noexcept
|
|
|
|
|
{
|
|
|
|
|
return std::abs(currentPosition - targetPosition) > MOVEMENT_COMPARISON_EPSILON;
|
|
|
|
|
}
|