Remove all stall detection logic, add 100ms current logging
- Removed all STALL_* configuration parameters from config.h - Simplified motor.h: removed stall-related methods and member variables - Simplified motor.cpp: deleted checkStall(), resetStallDetection() - Added frequent current logging (100ms) for data collection - Removed stall callback system from main.cpp - Simplified pingpong mode: time-based only, removed stall-return option - Updated webserver: removed stall warning UI, removed stallReturn checkbox - Updated status JSON: removed stalled and ppStallReturn fields This version is for testing with new beefy PSU to collect current data before designing new stall detection algorithm.
This commit is contained in:
202
src/motor.cpp
202
src/motor.cpp
@@ -45,34 +45,21 @@ void MotorController::begin() {
|
||||
|
||||
void MotorController::setSpeed(int speed) {
|
||||
_speed = constrain(speed, 0, 100);
|
||||
resetStallDetection();
|
||||
applyMotorState();
|
||||
}
|
||||
|
||||
void MotorController::setDirection(int dir) {
|
||||
_direction = constrain(dir, -1, 1);
|
||||
resetStallDetection();
|
||||
applyMotorState();
|
||||
}
|
||||
|
||||
void MotorController::stop() {
|
||||
// Don't reset _speed - keep last speed setting
|
||||
_direction = 0;
|
||||
resetStallDetection();
|
||||
ledcWrite(PWM_CHANNEL_R, 0);
|
||||
ledcWrite(PWM_CHANNEL_L, 0);
|
||||
}
|
||||
|
||||
void MotorController::resetStallDetection() {
|
||||
_stalled = false;
|
||||
_stallStartTime = 0;
|
||||
_undercurrentStartTime = 0;
|
||||
_stallCandidateCount = 0;
|
||||
_stallCandidateWindowStart = 0;
|
||||
_emaInitialized = false; // Re-seed EMA on motor state change
|
||||
_motorStartTime = millis();
|
||||
}
|
||||
|
||||
void MotorController::update() {
|
||||
#if CURRENT_SENSING_ENABLED
|
||||
static unsigned long lastPrintTime = 0;
|
||||
@@ -81,17 +68,13 @@ void MotorController::update() {
|
||||
_currentRight = readCurrentSense(R_IS_PIN);
|
||||
_currentLeft = readCurrentSense(L_IS_PIN);
|
||||
|
||||
// Log current readings every 500ms when motor is running
|
||||
if ((_direction != 0 || _speed != 0) && (millis() - lastPrintTime > 500)) {
|
||||
lastPrintTime = millis();
|
||||
float activeCurrent = getCurrentActive();
|
||||
float delta = activeCurrent - _currentEMA;
|
||||
Serial.printf("Current: R=%.2fA L=%.2fA Active=%.2fA (avg=%.2fA, delta=%.2fA, thresh=%.1fA)\n",
|
||||
_currentRight, _currentLeft, activeCurrent, _currentEMA, delta, STALL_DELTA_THRESHOLD);
|
||||
// Log current readings frequently for data collection
|
||||
unsigned long now = millis();
|
||||
if (now - lastPrintTime >= CURRENT_LOG_INTERVAL_MS) {
|
||||
lastPrintTime = now;
|
||||
Serial.printf("CURRENT: R=%.2fA L=%.2fA dir=%d spd=%d\n",
|
||||
_currentRight, _currentLeft, _direction, _speed);
|
||||
}
|
||||
|
||||
// Check for stall condition
|
||||
checkStall();
|
||||
#endif
|
||||
|
||||
// Update pingpong mode
|
||||
@@ -123,14 +106,6 @@ float MotorController::getCurrentActive() {
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
bool MotorController::isStalled() {
|
||||
return _stalled;
|
||||
}
|
||||
|
||||
void MotorController::setStallCallback(void (*callback)(float current)) {
|
||||
_stallCallback = callback;
|
||||
}
|
||||
|
||||
void MotorController::applyMotorState() {
|
||||
// Apply minimum PWM when motor is running
|
||||
int effectiveSpeed = _speed;
|
||||
@@ -206,32 +181,27 @@ void MotorController::calibrateCurrentOffset() {
|
||||
#endif
|
||||
}
|
||||
|
||||
// Pingpong mode implementation
|
||||
void MotorController::startPingpong(int speed, int timeMs, int speedRandomPercent, int timeRandomPercent, bool useStallReturn) {
|
||||
// Pingpong mode implementation (time-based only)
|
||||
void MotorController::startPingpong(int speed, int timeMs, int speedRandomPercent, int timeRandomPercent) {
|
||||
_pingpongBaseSpeed = constrain(speed, 0, 100);
|
||||
_pingpongBaseTime = constrain(timeMs, 100, 30000);
|
||||
_pingpongSpeedRandomPercent = constrain(speedRandomPercent, 0, 100);
|
||||
_pingpongTimeRandomPercent = constrain(timeRandomPercent, 0, 100);
|
||||
_pingpongUseStallReturn = useStallReturn;
|
||||
|
||||
_pingpongDirection = 1;
|
||||
_pingpongCurrentSpeed = applyRandomness(_pingpongBaseSpeed, _pingpongSpeedRandomPercent);
|
||||
// Time randomness disabled when using stall return
|
||||
_pingpongCurrentTime = _pingpongUseStallReturn ? _pingpongBaseTime : applyRandomness(_pingpongBaseTime, _pingpongTimeRandomPercent);
|
||||
_pingpongCurrentTime = applyRandomness(_pingpongBaseTime, _pingpongTimeRandomPercent);
|
||||
_pingpongLastSwitch = millis();
|
||||
_pingpongActive = true;
|
||||
_stalled = false; // Reset stall state when starting pingpong
|
||||
_stallStartTime = 0;
|
||||
|
||||
// Apply initial state
|
||||
_speed = _pingpongCurrentSpeed;
|
||||
_direction = _pingpongDirection;
|
||||
applyMotorState();
|
||||
|
||||
Serial.printf("Pingpong started: speed=%d%% (base=%d, rand=%d%%), time=%dms (base=%d, rand=%d%%), stallReturn=%s\n",
|
||||
Serial.printf("Pingpong started: speed=%d%% (base=%d, rand=%d%%), time=%dms (base=%d, rand=%d%%)\n",
|
||||
_pingpongCurrentSpeed, _pingpongBaseSpeed, _pingpongSpeedRandomPercent,
|
||||
_pingpongCurrentTime, _pingpongBaseTime, _pingpongTimeRandomPercent,
|
||||
_pingpongUseStallReturn ? "true" : "false");
|
||||
_pingpongCurrentTime, _pingpongBaseTime, _pingpongTimeRandomPercent);
|
||||
}
|
||||
|
||||
void MotorController::stopPingpong() {
|
||||
@@ -260,41 +230,19 @@ int MotorController::getPingpongTimeRandom() {
|
||||
return _pingpongTimeRandomPercent;
|
||||
}
|
||||
|
||||
bool MotorController::getPingpongStallReturn() {
|
||||
return _pingpongUseStallReturn;
|
||||
}
|
||||
|
||||
void MotorController::updatePingpong() {
|
||||
if (!_pingpongActive) return;
|
||||
|
||||
bool shouldSwitch = false;
|
||||
unsigned long now = millis();
|
||||
|
||||
if (_pingpongUseStallReturn) {
|
||||
// Switch direction only when stall is detected
|
||||
if (_stalled) {
|
||||
shouldSwitch = true;
|
||||
Serial.println("Pingpong: stall detected, switching direction");
|
||||
}
|
||||
} else {
|
||||
// Time-based switching
|
||||
if ((now - _pingpongLastSwitch) >= (unsigned long)_pingpongCurrentTime) {
|
||||
shouldSwitch = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (shouldSwitch) {
|
||||
// Time-based switching
|
||||
if ((now - _pingpongLastSwitch) >= (unsigned long)_pingpongCurrentTime) {
|
||||
// Switch direction
|
||||
_pingpongDirection = -_pingpongDirection;
|
||||
|
||||
// Full stall detection reset for new direction
|
||||
// This triggers STALL_STABILIZE_MS grace period to ignore motor inrush current
|
||||
resetStallDetection();
|
||||
|
||||
// Apply randomness for next cycle
|
||||
_pingpongCurrentSpeed = applyRandomness(_pingpongBaseSpeed, _pingpongSpeedRandomPercent);
|
||||
// Time randomness disabled when using stall return
|
||||
_pingpongCurrentTime = _pingpongUseStallReturn ? _pingpongBaseTime : applyRandomness(_pingpongBaseTime, _pingpongTimeRandomPercent);
|
||||
_pingpongCurrentTime = applyRandomness(_pingpongBaseTime, _pingpongTimeRandomPercent);
|
||||
_pingpongLastSwitch = now;
|
||||
|
||||
// Apply new state
|
||||
@@ -302,9 +250,8 @@ void MotorController::updatePingpong() {
|
||||
_direction = _pingpongDirection;
|
||||
applyMotorState();
|
||||
|
||||
Serial.printf("Pingpong switch: dir=%d, speed=%d%%, next_time=%dms, stallReturn=%s\n",
|
||||
_pingpongDirection, _pingpongCurrentSpeed, _pingpongCurrentTime,
|
||||
_pingpongUseStallReturn ? "true" : "false");
|
||||
Serial.printf("Pingpong switch: dir=%d, speed=%d%%, next_time=%dms\n",
|
||||
_pingpongDirection, _pingpongCurrentSpeed, _pingpongCurrentTime);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -318,120 +265,3 @@ int MotorController::applyRandomness(int baseValue, int randomPercent) {
|
||||
// Ensure result stays positive and reasonable
|
||||
return max(1, result);
|
||||
}
|
||||
|
||||
void MotorController::checkStall() {
|
||||
#if CURRENT_SENSING_ENABLED
|
||||
if (DISABLE_STALL_DETECT) return;
|
||||
|
||||
// Only check stall when motor should be running
|
||||
if (_direction == 0 || _speed == 0) {
|
||||
_stalled = false;
|
||||
_stallStartTime = 0;
|
||||
_undercurrentStartTime = 0;
|
||||
_emaInitialized = false;
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned long now = millis();
|
||||
float activeCurrent = getCurrentActive();
|
||||
|
||||
// Initialize EMA at expected baseline (prevents inrush from looking like stall)
|
||||
if (!_emaInitialized) {
|
||||
_currentEMA = STALL_EMA_BASELINE;
|
||||
_emaInitialized = true;
|
||||
}
|
||||
|
||||
// During stabilization: DON'T update EMA - keep baseline to avoid drift toward 0
|
||||
// This lets stall detection work correctly even if starting from stalled position
|
||||
if ((now - _motorStartTime) < STALL_STABILIZE_MS) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Calculate delta from average
|
||||
float delta = activeCurrent - _currentEMA;
|
||||
|
||||
// Check if current spike exceeds delta threshold
|
||||
bool spikeDetected = (delta > STALL_DELTA_THRESHOLD);
|
||||
|
||||
// Update EMA only when no spike is being investigated
|
||||
// This prevents EMA from "chasing" up to the spike during detection window
|
||||
if (!spikeDetected && !_stalled) {
|
||||
_currentEMA = (STALL_EMA_ALPHA * activeCurrent) + ((1.0f - STALL_EMA_ALPHA) * _currentEMA);
|
||||
}
|
||||
|
||||
if (spikeDetected) {
|
||||
if (_stallStartTime == 0) {
|
||||
// Start timing potential stall
|
||||
_stallStartTime = now;
|
||||
|
||||
// Count this as a stall candidate for repeated-spike detection
|
||||
if (_stallCandidateWindowStart == 0) {
|
||||
_stallCandidateWindowStart = now;
|
||||
_stallCandidateCount = 1;
|
||||
} else if ((now - _stallCandidateWindowStart) > STALL_CANDIDATE_WINDOW_MS) {
|
||||
// Window expired, start new window
|
||||
_stallCandidateWindowStart = now;
|
||||
_stallCandidateCount = 1;
|
||||
} else {
|
||||
_stallCandidateCount++;
|
||||
}
|
||||
|
||||
Serial.printf("Stall candidate #%d: current=%.2fA, avg=%.2fA, delta=%.2fA\n",
|
||||
_stallCandidateCount, activeCurrent, _currentEMA, delta);
|
||||
|
||||
// Check if enough candidates to confirm stall (oscillating pattern)
|
||||
if (_stallCandidateCount >= STALL_CANDIDATE_COUNT && !_stalled) {
|
||||
_stalled = true;
|
||||
Serial.printf("STALL DETECTED (repeated spikes)! %d candidates in %lums\n",
|
||||
_stallCandidateCount, now - _stallCandidateWindowStart);
|
||||
|
||||
if (_stallCallback != nullptr) {
|
||||
_stallCallback(activeCurrent);
|
||||
}
|
||||
}
|
||||
} else if ((now - _stallStartTime) > STALL_CONFIRM_MS) {
|
||||
// Sustained spike - stall confirmed
|
||||
if (!_stalled) {
|
||||
_stalled = true;
|
||||
Serial.printf("STALL DETECTED! Current: %.2fA (avg: %.2fA, delta: %.2fA)\n",
|
||||
activeCurrent, _currentEMA, delta);
|
||||
|
||||
// Call callback if registered
|
||||
if (_stallCallback != nullptr) {
|
||||
_stallCallback(activeCurrent);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Current normal, reset spike stall timing (but keep EMA updating)
|
||||
if (_stallStartTime != 0) {
|
||||
Serial.printf("Stall candidate cleared: current=%.2fA, avg=%.2fA\n",
|
||||
activeCurrent, _currentEMA);
|
||||
}
|
||||
_stallStartTime = 0;
|
||||
// Don't clear _stalled here - under-current/repeated-spike detection may have set it
|
||||
}
|
||||
|
||||
// Under-current stall detection: motor commanded but not drawing current
|
||||
// This catches stalls where the motor can't even start (e.g., already at end stop)
|
||||
if (!_stalled && activeCurrent < STALL_UNDERCURRENT_THRESHOLD) {
|
||||
if (_undercurrentStartTime == 0) {
|
||||
_undercurrentStartTime = now;
|
||||
} else if ((now - _undercurrentStartTime) > STALL_UNDERCURRENT_MS) {
|
||||
_stalled = true;
|
||||
Serial.printf("STALL DETECTED (under-current)! Current: %.2fA (threshold: %.2fA)\n",
|
||||
activeCurrent, STALL_UNDERCURRENT_THRESHOLD);
|
||||
|
||||
if (_stallCallback != nullptr) {
|
||||
_stallCallback(activeCurrent);
|
||||
}
|
||||
}
|
||||
} else if (activeCurrent >= STALL_UNDERCURRENT_THRESHOLD) {
|
||||
// Current is normal, reset under-current timer and clear stall
|
||||
_undercurrentStartTime = 0;
|
||||
if (!spikeDetected) {
|
||||
_stalled = false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user