HansonServo/motoraid.cpp

222 lines
7.4 KiB
C++

#include "motoraid.h"
#include "motorcontrol.h"
#include "robotconfig.h"
// Global instance
MotorAid motorAid;
extern RobotConfig config;
void MotorAid::addMotor(uint8_t motorID) {
// Use default settings
AidedMotorSettings defaultSettings;
addMotor(motorID, defaultSettings);
}
void MotorAid::addMotor(uint8_t motorID, const AidedMotorSettings& settings) {
// Don't add duplicates
if (findMotor(motorID) != nullptr) return;
AidedMotor motor;
motor.motorID = motorID;
motor.settings = settings;
// Read actual position directly from servo (not config which may be stale)
uint8_t model = config.getMotorModel(motorID);
servoManager[0]->setFeetechMode(model);
motor.lastPosition = servoManager[0]->getPosition(motorID);
motor.stablePosition = motor.lastPosition; // Initialize stable position
motor.lastUpdateTime = millis();
// Initialize velocity samples to zero
for (uint8_t i = 0; i < VELOCITY_SAMPLES; i++) {
motor.velocitySamples[i] = 0;
}
aidedMotors.push_back(motor);
// Disable torque on this motor so it can be moved freely
servoManager[0]->disableTorque(motorID);
Serial.print("[MotorAid] Added motor ");
Serial.print(motorID);
Serial.print(" at pos ");
Serial.print(motor.lastPosition);
Serial.print(" (thresh=");
Serial.print(settings.velocityThreshold);
Serial.print(", lead=");
Serial.print(settings.leadOffset);
Serial.println(")");
}
void MotorAid::removeMotor(uint8_t motorID) {
for (auto it = aidedMotors.begin(); it != aidedMotors.end(); ++it) {
if (it->motorID == motorID) {
aidedMotors.erase(it);
Serial.print("[MotorAid] Removed motor ");
Serial.println(motorID);
return;
}
}
}
bool MotorAid::isMotorAided(uint8_t motorID) const {
return findMotor(motorID) != nullptr;
}
AidedMotorSettings* MotorAid::getMotorSettings(uint8_t motorID) {
AidedMotor* motor = findMotor(motorID);
if (motor) return &motor->settings;
return nullptr;
}
AidedMotor* MotorAid::findMotor(uint8_t motorID) {
for (auto& motor : aidedMotors) {
if (motor.motorID == motorID) return &motor;
}
return nullptr;
}
const AidedMotor* MotorAid::findMotor(uint8_t motorID) const {
for (const auto& motor : aidedMotors) {
if (motor.motorID == motorID) return &motor;
}
return nullptr;
}
int16_t MotorAid::calculateSmoothedVelocity(AidedMotor& motor) {
// Calculate average of velocity samples
int32_t sum = 0;
for (uint8_t i = 0; i < VELOCITY_SAMPLES; i++) {
sum += motor.velocitySamples[i];
}
return (int16_t)(sum / VELOCITY_SAMPLES);
}
void MotorAid::update() {
unsigned long now = millis();
for (AidedMotor& motor : aidedMotors) {
const AidedMotorSettings& s = motor.settings; // Shorthand
// Calculate time delta
unsigned long deltaTime = now - motor.lastUpdateTime;
if (deltaTime < updateInterval) continue;
// Get current position from config (already updated by updateMotorPositions)
uint16_t currentPosition = config.getMotorPosition(motor.motorID);
// Calculate position delta from stable position (filters noise)
int16_t positionDelta = (int16_t)currentPosition - (int16_t)motor.stablePosition;
// Only register movement if it exceeds minimum delta (noise filter)
int16_t instantVelocity = 0;
if (abs(positionDelta) >= MIN_POSITION_DELTA) {
// Significant movement - calculate velocity and update stable position
instantVelocity = (positionDelta * 1000) / (int16_t)deltaTime;
motor.stablePosition = currentPosition;
}
// else: tiny movement, treat as zero velocity (noise)
// Add to velocity samples (circular buffer)
motor.velocitySamples[motor.sampleIndex] = instantVelocity;
motor.sampleIndex = (motor.sampleIndex + 1) % VELOCITY_SAMPLES;
// Track samples collected for warmup
if (motor.samplesCollected < VELOCITY_SAMPLES * 2) {
motor.samplesCollected++;
}
// Don't calculate or trigger until we have enough samples (2x buffer = ~300ms warmup)
if (motor.samplesCollected < VELOCITY_SAMPLES * 2) {
continue; // Still warming up
}
motor.warmedUp = true;
// Calculate smoothed velocity
motor.smoothedVelocity = calculateSmoothedVelocity(motor);
// Update tracking
motor.lastUpdateTime = now;
// Check if currently assisting
if (motor.assisting) {
// Calculate target position AHEAD of current position in direction of movement
int16_t leadOffset = (motor.smoothedVelocity > 0) ? s.leadOffset : -s.leadOffset;
motor.assistTargetPosition = constrain((int16_t)currentPosition + leadOffset, 0, 4095);
// Send updated position command to lead the movement
uint8_t model = config.getMotorModel(motor.motorID);
servoManager[0]->setFeetechMode(model);
uint8_t ids[1] = { motor.motorID };
uint16_t positions[1] = { motor.assistTargetPosition };
uint16_t speeds[1] = { s.assistSpeed };
servoManager.syncWritePositions(ids, positions, speeds, 1, config, 0);
// Check if assist duration has elapsed (fixed duration, always ends)
if (now - motor.assistStartTime >= s.assistDuration) {
motor.assisting = false;
// Disable torque again
servoManager[0]->disableTorque(motor.motorID);
Serial.print("[MotorAid] Assist ended, motor ");
Serial.print(motor.motorID);
Serial.print(" at pos ");
Serial.println(currentPosition);
}
continue;
}
// Hysteresis: if we were triggered, wait for velocity to drop before re-triggering
if (motor.wasTriggered) {
if (abs(motor.smoothedVelocity) < s.resetThreshold) {
motor.wasTriggered = false; // Reset, can trigger again
motor.consecutiveHighCount = 0; // Reset consecutive counter too
}
continue; // Don't trigger while in hysteresis zone
}
// Check if smoothed velocity exceeds threshold (in either direction)
if (abs(motor.smoothedVelocity) > s.velocityThreshold) {
motor.consecutiveHighCount++;
} else {
motor.consecutiveHighCount = 0; // Reset on low reading
}
// Only trigger after CONSECUTIVE_REQUIRED high readings (filters noise spikes)
if (motor.consecutiveHighCount >= CONSECUTIVE_REQUIRED) {
// Sustained movement detected - assist!
motor.assisting = true;
motor.assistStartTime = now;
motor.wasTriggered = true;
// Calculate target position AHEAD of current position in direction of movement
int16_t leadOffset = (motor.smoothedVelocity > 0) ? s.leadOffset : -s.leadOffset;
motor.assistTargetPosition = constrain((int16_t)currentPosition + leadOffset, 0, 4095);
// Set servo mode based on motor model
uint8_t model = config.getMotorModel(motor.motorID);
servoManager[0]->setFeetechMode(model);
// Enable torque and move AHEAD to assist
servoManager[0]->enableTorque(motor.motorID);
uint8_t ids[1] = { motor.motorID };
uint16_t positions[1] = { motor.assistTargetPosition };
uint16_t speeds[1] = { s.assistSpeed };
servoManager.syncWritePositions(ids, positions, speeds, 1, config, 0);
// Debug output
Serial.print("[MotorAid] Assisting motor ");
Serial.print(motor.motorID);
Serial.print(" vel=");
Serial.print(motor.smoothedVelocity);
Serial.print(" pos=");
Serial.print(currentPosition);
Serial.print(" -> ");
Serial.println(motor.assistTargetPosition);
}
}
}