RE usability: live data for idle controller

This commit is contained in:
rusefillc 2022-01-10 21:55:52 -05:00
parent 68fda494cb
commit 699a5ef302
7 changed files with 148 additions and 118 deletions

View File

@ -19,6 +19,14 @@ bit needReset
bit isInDeadZone
bit isBlipping
bit useClosedLoop
bit badTps
bit looksLikeRunning
bit looksLikeCoasting
bit looksLikeCrankToIdle
int targetRpmByClt
int targetRpmAcBump
! end of idle_state_s structure definition
end_struct

View File

@ -1,4 +1,4 @@
// this section was generated automatically by rusEFI tool ConfigDefinition.jar based on (unknown script) controllers/actuators/idle_state.txt Tue Jan 11 01:55:22 UTC 2022
// this section was generated automatically by rusEFI tool ConfigDefinition.jar based on (unknown script) controllers/actuators/idle_state.txt Mon Jan 10 21:12:38 EST 2022
// by class com.rusefi.output.CHeaderConsumer
// begin
#pragma once
@ -66,69 +66,77 @@ struct idle_state_s {
bool useClosedLoop : 1 {};
/**
offset 16 bit 11 */
bool unusedBit_15_11 : 1 {};
bool badTps : 1 {};
/**
offset 16 bit 12 */
bool unusedBit_15_12 : 1 {};
bool looksLikeRunning : 1 {};
/**
offset 16 bit 13 */
bool unusedBit_15_13 : 1 {};
bool looksLikeCoasting : 1 {};
/**
offset 16 bit 14 */
bool unusedBit_15_14 : 1 {};
bool looksLikeCrankToIdle : 1 {};
/**
offset 16 bit 15 */
bool unusedBit_15_15 : 1 {};
bool unusedBit_19_15 : 1 {};
/**
offset 16 bit 16 */
bool unusedBit_15_16 : 1 {};
bool unusedBit_19_16 : 1 {};
/**
offset 16 bit 17 */
bool unusedBit_15_17 : 1 {};
bool unusedBit_19_17 : 1 {};
/**
offset 16 bit 18 */
bool unusedBit_15_18 : 1 {};
bool unusedBit_19_18 : 1 {};
/**
offset 16 bit 19 */
bool unusedBit_15_19 : 1 {};
bool unusedBit_19_19 : 1 {};
/**
offset 16 bit 20 */
bool unusedBit_15_20 : 1 {};
bool unusedBit_19_20 : 1 {};
/**
offset 16 bit 21 */
bool unusedBit_15_21 : 1 {};
bool unusedBit_19_21 : 1 {};
/**
offset 16 bit 22 */
bool unusedBit_15_22 : 1 {};
bool unusedBit_19_22 : 1 {};
/**
offset 16 bit 23 */
bool unusedBit_15_23 : 1 {};
bool unusedBit_19_23 : 1 {};
/**
offset 16 bit 24 */
bool unusedBit_15_24 : 1 {};
bool unusedBit_19_24 : 1 {};
/**
offset 16 bit 25 */
bool unusedBit_15_25 : 1 {};
bool unusedBit_19_25 : 1 {};
/**
offset 16 bit 26 */
bool unusedBit_15_26 : 1 {};
bool unusedBit_19_26 : 1 {};
/**
offset 16 bit 27 */
bool unusedBit_15_27 : 1 {};
bool unusedBit_19_27 : 1 {};
/**
offset 16 bit 28 */
bool unusedBit_15_28 : 1 {};
bool unusedBit_19_28 : 1 {};
/**
offset 16 bit 29 */
bool unusedBit_15_29 : 1 {};
bool unusedBit_19_29 : 1 {};
/**
offset 16 bit 30 */
bool unusedBit_15_30 : 1 {};
bool unusedBit_19_30 : 1 {};
/**
offset 16 bit 31 */
bool unusedBit_15_31 : 1 {};
/** total size 20*/
bool unusedBit_19_31 : 1 {};
/**
* offset 20
*/
int targetRpmByClt = (int)0;
/**
* offset 24
*/
int targetRpmAcBump = (int)0;
/** total size 28*/
};
// end
// this section was generated automatically by rusEFI tool ConfigDefinition.jar based on (unknown script) controllers/actuators/idle_state.txt Tue Jan 11 01:55:22 UTC 2022
// this section was generated automatically by rusEFI tool ConfigDefinition.jar based on (unknown script) controllers/actuators/idle_state.txt Mon Jan 10 21:12:38 EST 2022

View File

@ -7,24 +7,7 @@
*
*
* @date May 23, 2013
* @author Andrey Belomutskiy, (c) 2012-2020
*
* enable verbose_idle
* disable verbose_idle
*
* This file is part of rusEfi - see http://rusefi.com
*
* rusEfi is free software; you can redistribute it and/or modify it under the terms of
* the GNU General Public License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* rusEfi is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*
* @author Andrey Belomutskiy, (c) 2012-2022
*/
#include "pch.h"
@ -40,21 +23,22 @@
#include "stepper.h"
#endif
int IdleController::getTargetRpm(float clt) const {
auto target = interpolate2d(clt, engineConfiguration->cltIdleRpmBins, engineConfiguration->cltIdleRpm);
int IdleController::getTargetRpm(float clt) {
targetRpmByClt = interpolate2d(clt, engineConfiguration->cltIdleRpmBins, engineConfiguration->cltIdleRpm);
// Bump for AC
target += engine->acSwitchState ? engineConfiguration->acIdleRpmBump : 0;
targetRpmAcBump = engine->acSwitchState ? engineConfiguration->acIdleRpmBump : 0;
return target;
return targetRpmByClt + targetRpmAcBump;
}
IIdleController::Phase IdleController::determinePhase(int rpm, int targetRpm, SensorResult tps, float vss, float crankingTaperFraction) const {
IIdleController::Phase IdleController::determinePhase(int rpm, int targetRpm, SensorResult tps, float vss, float crankingTaperFraction) {
if (!engine->rpmCalculator.isRunning()) {
return Phase::Cranking;
}
badTps = !tps;
if (!tps) {
if (badTps) {
// If the TPS has failed, assume the engine is running
return Phase::Running;
}
@ -66,18 +50,21 @@ IIdleController::Phase IdleController::determinePhase(int rpm, int targetRpm, Se
// If rpm too high (but throttle not pressed), we're coasting
int maximumIdleRpm = targetRpm + engineConfiguration->idlePidRpmUpperLimit;
if (rpm > maximumIdleRpm) {
looksLikeCoasting = rpm > maximumIdleRpm;
if (looksLikeCoasting) {
return Phase::Coasting;
}
// If the vehicle is moving too quickly, disable CL idle
auto maxVss = engineConfiguration->maxIdleVss;
if (maxVss != 0 && vss > maxVss) {
looksLikeRunning = maxVss != 0 && vss > maxVss;
if (looksLikeRunning) {
return Phase::Running;
}
// If still in the cranking taper, disable closed loop idle
if (crankingTaperFraction < 1) {
looksLikeCrankToIdle = crankingTaperFraction < 1;
if (looksLikeCrankToIdle) {
return Phase::CrankToIdleTaper;
}
@ -100,7 +87,7 @@ float IdleController::getCrankingOpenLoop(float clt) const {
return engineConfiguration->crankingIACposition * mult;
}
float IdleController::getRunningOpenLoop(float clt, SensorResult tps) const {
percent_t IdleController::getRunningOpenLoop(float clt, SensorResult tps) const {
float running =
engineConfiguration->manIdlePosition // Base idle position (slider)
* interpolate2d(clt, config->cltIdleCorrBins, config->cltIdleCorr);
@ -120,25 +107,27 @@ float IdleController::getRunningOpenLoop(float clt, SensorResult tps) const {
return clampF(0, running, 100);
}
float IdleController::getOpenLoop(Phase phase, float clt, SensorResult tps, float crankingTaperFraction) {
float cranking = getCrankingOpenLoop(clt);
percent_t IdleController::getOpenLoop(Phase phase, float clt, SensorResult tps, float crankingTaperFraction) {
percent_t crankingValvePosition = getCrankingOpenLoop(clt);
isCoasting = phase == Phase::Cranking;
// if we're cranking, nothing more to do.
if (phase == Phase::Cranking) {
return cranking;
if (isCoasting) {
return crankingValvePosition;
}
// If coasting (and enabled), use the coasting position table instead of normal open loop
// TODO: this should be a table of open loop mult vs. RPM, not vs. clt
if (engineConfiguration->useIacTableForCoasting && phase == Phase::Coasting) {
useIacTableForCoasting = engineConfiguration->useIacTableForCoasting && phase == Phase::Coasting;
if (useIacTableForCoasting) {
return interpolate2d(clt, engineConfiguration->iacCoastingBins, engineConfiguration->iacCoasting);
}
float running = getRunningOpenLoop(clt, tps);
percent_t running = getRunningOpenLoop(clt, tps);
// Interpolate between cranking and running over a short time
// This clamps once you fall off the end, so no explicit check for >1 required
return interpolateClamped(0, cranking, 1, running, crankingTaperFraction);
return interpolateClamped(0, crankingValvePosition, 1, running, crankingTaperFraction);
}
float IdleController::getIdleTimingAdjustment(int rpm) {
@ -189,12 +178,12 @@ float IdleController::getClosedLoop(IIdleController::Phase phase, float tpsPos,
auto idlePid = getIdlePid();
if (shouldResetPid) {
needReset = idlePid->getIntegration() <= 0 || mustResetPid;
// we reset only if I-term is negative, because the positive I-term is good - it keeps RPM from dropping too low
if (idlePid->getIntegration() <= 0 || mustResetPid) {
if (needReset) {
idlePid->reset();
mustResetPid = false;
}
// alternatorPidResetCounter++;
shouldResetPid = false;
wasResetPid = true;
}
@ -205,7 +194,8 @@ float IdleController::getClosedLoop(IIdleController::Phase phase, float tpsPos,
efitimeus_t nowUs = getTimeNowUs();
if (phase != IIdleController::Phase::Idling) {
notIdling = phase != IIdleController::Phase::Idling;
if (notIdling) {
// Don't store old I and D terms if PID doesn't work anymore.
// Otherwise they will affect the idle position much later, when the throttle is closed.
if (mightResetPid) {
@ -223,7 +213,8 @@ float IdleController::getClosedLoop(IIdleController::Phase phase, float tpsPos,
// #1553 we need to give FSIO variable offset or minValue a chance
bool acToggleJustTouched = (nowUs - engine->acSwitchLastChangeTime) < MS2US(500);
// check if within the dead zone
if (!acToggleJustTouched && absI(rpm - targetRpm) <= engineConfiguration->idlePidRpmDeadZone) {
isInDeadZone = !acToggleJustTouched && absI(rpm - targetRpm) <= engineConfiguration->idlePidRpmDeadZone;
if (isInDeadZone) {
idleState = RPM_DEAD_ZONE;
// current RPM is close enough, no need to change anything
return m_lastAutomaticPosition;
@ -277,7 +268,7 @@ float IdleController::getClosedLoop(IIdleController::Phase phase, float tpsPos,
return newValue;
}
float IdleController::getIdlePosition() {
float IdleController::getIdlePosition() {
// Simplify hardware CI: we borrow the idle valve controller as a PWM source for various stimulation tasks
// The logic in this function is solidly unit tested, so it's not necessary to re-test the particulars on real hardware.
#ifdef HARDWARE_CI
@ -329,7 +320,8 @@ float IdleController::getClosedLoop(IIdleController::Phase phase, float tpsPos,
percent_t iacPosition;
if (engine->timeToStopBlip != 0) {
isBlipping = engine->timeToStopBlip != 0;
if (isBlipping) {
iacPosition = engine->blipIdlePosition;
idleState = BLIP;
} else {
@ -337,8 +329,9 @@ float IdleController::getClosedLoop(IIdleController::Phase phase, float tpsPos,
iacPosition = getOpenLoop(phase, clt, tps, crankingTaper);
baseIdlePosition = iacPosition;
useClosedLoop = tps.Valid && engineConfiguration->idleMode == IM_AUTO;
// If TPS is working and automatic mode enabled, add any automatic correction
if (tps.Valid && engineConfiguration->idleMode == IM_AUTO) {
if (useClosedLoop) {
iacPosition += getClosedLoop(phase, tps.Value, rpm, targetRpm);
}

View File

@ -22,8 +22,8 @@ struct IIdleController {
Running, // On throttle
};
virtual Phase determinePhase(int rpm, int targetRpm, SensorResult tps, float vss, float crankingTaperFraction) const = 0;
virtual int getTargetRpm(float clt) const = 0;
virtual Phase determinePhase(int rpm, int targetRpm, SensorResult tps, float vss, float crankingTaperFraction) = 0;
virtual int getTargetRpm(float clt) = 0;
virtual float getCrankingOpenLoop(float clt) const = 0;
virtual float getRunningOpenLoop(float clt, SensorResult tps) const = 0;
virtual float getOpenLoop(Phase phase, float clt, SensorResult tps, float crankingTaperFraction) = 0;
@ -40,16 +40,16 @@ public:
float getIdlePosition();
// TARGET DETERMINATION
int getTargetRpm(float clt) const override;
int getTargetRpm(float clt) override;
// PHASE DETERMINATION: what is the driver trying to do right now?
Phase determinePhase(int rpm, int targetRpm, SensorResult tps, float vss, float crankingTaperFraction) const override;
Phase determinePhase(int rpm, int targetRpm, SensorResult tps, float vss, float crankingTaperFraction) override;
float getCrankingTaperFraction() const override;
// OPEN LOOP CORRECTIONS
float getCrankingOpenLoop(float clt) const override;
float getRunningOpenLoop(float clt, SensorResult tps) const override;
float getOpenLoop(Phase phase, float clt, SensorResult tps, float crankingTaperFraction) override;
percent_t getCrankingOpenLoop(float clt) const override;
percent_t getRunningOpenLoop(float clt, SensorResult tps) const override;
percent_t getOpenLoop(Phase phase, float clt, SensorResult tps, float crankingTaperFraction) override;
float getIdleTimingAdjustment(int rpm);
float getIdleTimingAdjustment(int rpm, int targetRpm, Phase phase);

View File

@ -1,6 +1,23 @@
/*
* @file idle_thread_io.cpp
*
*
* enable verbose_idle
* disable verbose_idle
*
* This file is part of rusEfi - see http://rusefi.com
*
* rusEfi is free software; you can redistribute it and/or modify it under the terms of
* the GNU General Public License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* rusEfi is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*
* @date Oct 17, 2021
* @author Andrey Belomutskiy, (c) 2012-2020
*/

View File

@ -1,6 +1,6 @@
package com.rusefi.config.generated;
// this file was generated automatically by rusEFI tool ConfigDefinition.jar based on (unknown script) controllers/actuators/idle_state.txt Tue Jan 11 01:55:22 UTC 2022
// this file was generated automatically by rusEFI tool ConfigDefinition.jar based on (unknown script) controllers/actuators/idle_state.txt Mon Jan 10 21:12:38 EST 2022
// by class com.rusefi.output.FileJavaFieldsConsumer
import com.rusefi.config.*;
@ -21,27 +21,29 @@ public class IdleState {
public static final Field ISINDEADZONE = Field.create("ISINDEADZONE", 16, FieldType.BIT, 8);
public static final Field ISBLIPPING = Field.create("ISBLIPPING", 16, FieldType.BIT, 9);
public static final Field USECLOSEDLOOP = Field.create("USECLOSEDLOOP", 16, FieldType.BIT, 10);
public static final Field UNUSEDBIT_15_11 = Field.create("UNUSEDBIT_15_11", 16, FieldType.BIT, 11);
public static final Field UNUSEDBIT_15_12 = Field.create("UNUSEDBIT_15_12", 16, FieldType.BIT, 12);
public static final Field UNUSEDBIT_15_13 = Field.create("UNUSEDBIT_15_13", 16, FieldType.BIT, 13);
public static final Field UNUSEDBIT_15_14 = Field.create("UNUSEDBIT_15_14", 16, FieldType.BIT, 14);
public static final Field UNUSEDBIT_15_15 = Field.create("UNUSEDBIT_15_15", 16, FieldType.BIT, 15);
public static final Field UNUSEDBIT_15_16 = Field.create("UNUSEDBIT_15_16", 16, FieldType.BIT, 16);
public static final Field UNUSEDBIT_15_17 = Field.create("UNUSEDBIT_15_17", 16, FieldType.BIT, 17);
public static final Field UNUSEDBIT_15_18 = Field.create("UNUSEDBIT_15_18", 16, FieldType.BIT, 18);
public static final Field UNUSEDBIT_15_19 = Field.create("UNUSEDBIT_15_19", 16, FieldType.BIT, 19);
public static final Field UNUSEDBIT_15_20 = Field.create("UNUSEDBIT_15_20", 16, FieldType.BIT, 20);
public static final Field UNUSEDBIT_15_21 = Field.create("UNUSEDBIT_15_21", 16, FieldType.BIT, 21);
public static final Field UNUSEDBIT_15_22 = Field.create("UNUSEDBIT_15_22", 16, FieldType.BIT, 22);
public static final Field UNUSEDBIT_15_23 = Field.create("UNUSEDBIT_15_23", 16, FieldType.BIT, 23);
public static final Field UNUSEDBIT_15_24 = Field.create("UNUSEDBIT_15_24", 16, FieldType.BIT, 24);
public static final Field UNUSEDBIT_15_25 = Field.create("UNUSEDBIT_15_25", 16, FieldType.BIT, 25);
public static final Field UNUSEDBIT_15_26 = Field.create("UNUSEDBIT_15_26", 16, FieldType.BIT, 26);
public static final Field UNUSEDBIT_15_27 = Field.create("UNUSEDBIT_15_27", 16, FieldType.BIT, 27);
public static final Field UNUSEDBIT_15_28 = Field.create("UNUSEDBIT_15_28", 16, FieldType.BIT, 28);
public static final Field UNUSEDBIT_15_29 = Field.create("UNUSEDBIT_15_29", 16, FieldType.BIT, 29);
public static final Field UNUSEDBIT_15_30 = Field.create("UNUSEDBIT_15_30", 16, FieldType.BIT, 30);
public static final Field UNUSEDBIT_15_31 = Field.create("UNUSEDBIT_15_31", 16, FieldType.BIT, 31);
public static final Field BADTPS = Field.create("BADTPS", 16, FieldType.BIT, 11);
public static final Field LOOKSLIKERUNNING = Field.create("LOOKSLIKERUNNING", 16, FieldType.BIT, 12);
public static final Field LOOKSLIKECOASTING = Field.create("LOOKSLIKECOASTING", 16, FieldType.BIT, 13);
public static final Field LOOKSLIKECRANKTOIDLE = Field.create("LOOKSLIKECRANKTOIDLE", 16, FieldType.BIT, 14);
public static final Field UNUSEDBIT_19_15 = Field.create("UNUSEDBIT_19_15", 16, FieldType.BIT, 15);
public static final Field UNUSEDBIT_19_16 = Field.create("UNUSEDBIT_19_16", 16, FieldType.BIT, 16);
public static final Field UNUSEDBIT_19_17 = Field.create("UNUSEDBIT_19_17", 16, FieldType.BIT, 17);
public static final Field UNUSEDBIT_19_18 = Field.create("UNUSEDBIT_19_18", 16, FieldType.BIT, 18);
public static final Field UNUSEDBIT_19_19 = Field.create("UNUSEDBIT_19_19", 16, FieldType.BIT, 19);
public static final Field UNUSEDBIT_19_20 = Field.create("UNUSEDBIT_19_20", 16, FieldType.BIT, 20);
public static final Field UNUSEDBIT_19_21 = Field.create("UNUSEDBIT_19_21", 16, FieldType.BIT, 21);
public static final Field UNUSEDBIT_19_22 = Field.create("UNUSEDBIT_19_22", 16, FieldType.BIT, 22);
public static final Field UNUSEDBIT_19_23 = Field.create("UNUSEDBIT_19_23", 16, FieldType.BIT, 23);
public static final Field UNUSEDBIT_19_24 = Field.create("UNUSEDBIT_19_24", 16, FieldType.BIT, 24);
public static final Field UNUSEDBIT_19_25 = Field.create("UNUSEDBIT_19_25", 16, FieldType.BIT, 25);
public static final Field UNUSEDBIT_19_26 = Field.create("UNUSEDBIT_19_26", 16, FieldType.BIT, 26);
public static final Field UNUSEDBIT_19_27 = Field.create("UNUSEDBIT_19_27", 16, FieldType.BIT, 27);
public static final Field UNUSEDBIT_19_28 = Field.create("UNUSEDBIT_19_28", 16, FieldType.BIT, 28);
public static final Field UNUSEDBIT_19_29 = Field.create("UNUSEDBIT_19_29", 16, FieldType.BIT, 29);
public static final Field UNUSEDBIT_19_30 = Field.create("UNUSEDBIT_19_30", 16, FieldType.BIT, 30);
public static final Field UNUSEDBIT_19_31 = Field.create("UNUSEDBIT_19_31", 16, FieldType.BIT, 31);
public static final Field TARGETRPMBYCLT = Field.create("TARGETRPMBYCLT", 20, FieldType.INT);
public static final Field TARGETRPMACBUMP = Field.create("TARGETRPMACBUMP", 24, FieldType.INT);
public static final Field[] VALUES = {
IDLESTATE,
CURRENTIDLEPOSITION,
@ -58,26 +60,28 @@ public class IdleState {
ISINDEADZONE,
ISBLIPPING,
USECLOSEDLOOP,
UNUSEDBIT_15_11,
UNUSEDBIT_15_12,
UNUSEDBIT_15_13,
UNUSEDBIT_15_14,
UNUSEDBIT_15_15,
UNUSEDBIT_15_16,
UNUSEDBIT_15_17,
UNUSEDBIT_15_18,
UNUSEDBIT_15_19,
UNUSEDBIT_15_20,
UNUSEDBIT_15_21,
UNUSEDBIT_15_22,
UNUSEDBIT_15_23,
UNUSEDBIT_15_24,
UNUSEDBIT_15_25,
UNUSEDBIT_15_26,
UNUSEDBIT_15_27,
UNUSEDBIT_15_28,
UNUSEDBIT_15_29,
UNUSEDBIT_15_30,
UNUSEDBIT_15_31,
BADTPS,
LOOKSLIKERUNNING,
LOOKSLIKECOASTING,
LOOKSLIKECRANKTOIDLE,
UNUSEDBIT_19_15,
UNUSEDBIT_19_16,
UNUSEDBIT_19_17,
UNUSEDBIT_19_18,
UNUSEDBIT_19_19,
UNUSEDBIT_19_20,
UNUSEDBIT_19_21,
UNUSEDBIT_19_22,
UNUSEDBIT_19_23,
UNUSEDBIT_19_24,
UNUSEDBIT_19_25,
UNUSEDBIT_19_26,
UNUSEDBIT_19_27,
UNUSEDBIT_19_28,
UNUSEDBIT_19_29,
UNUSEDBIT_19_30,
UNUSEDBIT_19_31,
TARGETRPMBYCLT,
TARGETRPMACBUMP,
};
}

View File

@ -354,8 +354,8 @@ TEST(idle_v2, closedLoopDeadzone) {
}
struct IntegrationIdleMock : public IdleController {
MOCK_METHOD(int, getTargetRpm, (float clt), (const, override));
MOCK_METHOD(ICP, determinePhase, (int rpm, int targetRpm, SensorResult tps, float vss, float crankingTaperFraction), (const, override));
MOCK_METHOD(int, getTargetRpm, (float clt), (override));
MOCK_METHOD(ICP, determinePhase, (int rpm, int targetRpm, SensorResult tps, float vss, float crankingTaperFraction), (override));
MOCK_METHOD(float, getOpenLoop, (ICP phase, float clt, SensorResult tps, float crankingTaperFraction), (override));
MOCK_METHOD(float, getClosedLoop, (ICP phase, float tps, int rpm, int target), (override));
MOCK_METHOD(float, getCrankingTaperFraction, (), (const, override));