Files
MotionCore/MotionCore/Motor.cpp
T

281 lines
6.3 KiB
C++
Raw Normal View History

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;
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;
moveStartPosition = currentPosition;
2025-12-07 23:28:04 +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;
moveStartPosition = currentPosition;
2025-12-07 23:28:04 +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
{
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);
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) {
return speedLow;
2025-12-07 23:28:04 +01:00
}
auto smoothstep = [](double x) noexcept {
return x * x * (3.0 - 2.0 * x);
};
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
}
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;
}