191 lines
6.3 KiB
C++
191 lines
6.3 KiB
C++
#include "motoraid.h"
|
|
#include "motorcontrol.h"
|
|
#include "robotconfig.h"
|
|
|
|
// Global instance
|
|
MotorAid motorAid;
|
|
|
|
extern RobotConfig config;
|
|
|
|
void MotorAid::addMotor(uint8_t motorID) {
|
|
// Don't add duplicates
|
|
if (findMotor(motorID) != nullptr) return;
|
|
|
|
AidedMotor motor;
|
|
motor.motorID = motorID;
|
|
|
|
// 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.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.println(motor.lastPosition);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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) {
|
|
// 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 instantaneous velocity (position units per second)
|
|
int16_t positionDelta = (int16_t)currentPosition - (int16_t)motor.lastPosition;
|
|
int16_t instantVelocity = (positionDelta * 1000) / (int16_t)deltaTime;
|
|
|
|
// 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.lastPosition = currentPosition;
|
|
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) ? 150 : -150;
|
|
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] = { assistSpeed };
|
|
servoManager.syncWritePositions(ids, positions, speeds, 1, config, 0);
|
|
|
|
// Extend assist if still moving fast
|
|
if (abs(motor.smoothedVelocity) > resetThreshold) {
|
|
motor.assistStartTime = now; // Keep extending
|
|
}
|
|
|
|
// Check if assist duration has elapsed (only ends when movement stops)
|
|
if (now - motor.assistStartTime >= 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) < resetThreshold) {
|
|
motor.wasTriggered = false; // Reset, can trigger again
|
|
}
|
|
continue; // Don't trigger while in hysteresis zone
|
|
}
|
|
|
|
// Check if smoothed velocity exceeds threshold (in either direction)
|
|
if (abs(motor.smoothedVelocity) > velocityThreshold) {
|
|
// Someone is moving the motor - 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) ? 150 : -150;
|
|
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] = { 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);
|
|
}
|
|
}
|
|
}
|