Merge pull request #1639 from mck1117/vw-etb

VW's Whacky ETB Idle
This commit is contained in:
rusefillc 2020-07-26 10:48:38 -04:00 committed by GitHub
commit c035cd64a5
5 changed files with 93 additions and 29 deletions

View File

@ -99,7 +99,11 @@ static bool startupPositionError = false;
#define STARTUP_NEUTRAL_POSITION_ERROR_THRESHOLD 5
static SensorType indexToTpsSensor(size_t index) {
static SensorType indexToTpsSensor(size_t index, bool volkswagenEtbIdle) {
if (volkswagenEtbIdle) {
return SensorType::Tps2;
}
switch(index) {
case 0: return SensorType::Tps1;
default: return SensorType::Tps2;
@ -127,7 +131,8 @@ static percent_t currentEtbDuty;
// this macro clamps both positive and negative percentages from about -100% to 100%
#define ETB_PERCENT_TO_DUTY(x) (clampF(-ETB_DUTY_LIMIT, 0.01f * (x), ETB_DUTY_LIMIT))
void EtbController::init(DcMotor *motor, int ownIndex, pid_s *pidParameters, const ValueProvider3D* pedalMap) {
void EtbController::init(SensorType positionSensor, DcMotor *motor, int ownIndex, pid_s *pidParameters, const ValueProvider3D* pedalMap) {
m_positionSensor = positionSensor;
m_motor = motor;
m_myIndex = ownIndex;
m_pid.initPidClass(pidParameters);
@ -149,7 +154,7 @@ void EtbController::showStatus(Logging* logger) {
}
expected<percent_t> EtbController::observePlant() const {
return Sensor::get(indexToTpsSensor(m_myIndex));
return Sensor::get(m_positionSensor);
}
void EtbController::setIdlePosition(percent_t pos) {
@ -162,6 +167,12 @@ expected<percent_t> EtbController::getSetpoint() const {
return unexpected;
}
// VW ETB idle mode uses an ETB only for idle (a mini-ETB sets the lower stop, and a normal cable
// can pull the throttle up off the stop.), so we directly control the throttle with the idle position.
if (CONFIG(volkswagenEtbIdle)) {
return clampF(0, m_idlePosition, 100);
}
// If the pedal map hasn't been set, we can't provide a setpoint.
if (!m_pedalMap) {
return unexpected;
@ -736,14 +747,18 @@ void doInitElectronicThrottle(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
addConsoleActionI("etb_freq", setEtbFrequency);
#endif /* EFI_PROD_CODE */
// If you don't have a pedal, we have no business here.
if (!Sensor::hasSensor(SensorType::AcceleratorPedalPrimary)) {
// If you don't have a pedal (or VW idle valve mode), we have no business here.
if (!CONFIG(volkswagenEtbIdle) && !Sensor::hasSensor(SensorType::AcceleratorPedalPrimary)) {
return;
}
pedal2tpsMap.init(config->pedalToTpsTable, config->pedalToTpsPedalBins, config->pedalToTpsRpmBins);
engine->etbActualCount = Sensor::hasSensor(SensorType::Tps2) ? 2 : 1;
if (CONFIG(volkswagenEtbIdle)) {
engine->etbActualCount = 1;
} else {
engine->etbActualCount = Sensor::hasSensor(SensorType::Tps2) ? 2 : 1;
}
for (int i = 0 ; i < engine->etbActualCount; i++) {
auto motor = initDcMotor(i, CONFIG(etb_use_two_wires) PASS_ENGINE_PARAMETER_SUFFIX);
@ -751,7 +766,8 @@ void doInitElectronicThrottle(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
// If this motor is actually set up, init the etb
if (motor)
{
engine->etbControllers[i]->init(motor, i, &engineConfiguration->etb, &pedal2tpsMap);
auto positionSensor = indexToTpsSensor(i, CONFIG(volkswagenEtbIdle));
engine->etbControllers[i]->init(positionSensor, motor, i, &engineConfiguration->etb, &pedal2tpsMap);
INJECT_ENGINE_REFERENCE(engine->etbControllers[i]);
}
}

View File

@ -19,6 +19,7 @@
#include "engine.h"
#include "closed_loop_controller.h"
#include "expected.h"
#include "sensor.h"
class DcMotor;
class Logging;
@ -26,7 +27,7 @@ class Logging;
class IEtbController : public ClosedLoopController<percent_t, percent_t> {
public:
DECLARE_ENGINE_PTR;
virtual void init(DcMotor *motor, int ownIndex, pid_s *pidParameters, const ValueProvider3D* pedalMap) = 0;
virtual void init(SensorType positionSensor, DcMotor *motor, int ownIndex, pid_s *pidParameters, const ValueProvider3D* pedalMap) = 0;
virtual void reset() = 0;
virtual void setIdlePosition(percent_t pos) = 0;
virtual void start() = 0;
@ -35,7 +36,7 @@ public:
class EtbController : public IEtbController {
public:
void init(DcMotor *motor, int ownIndex, pid_s *pidParameters, const ValueProvider3D* pedalMap) override;
void init(SensorType positionSensor, DcMotor *motor, int ownIndex, pid_s *pidParameters, const ValueProvider3D* pedalMap) override;
void setIdlePosition(percent_t pos) override;
void reset() override;
void start() override {}
@ -74,6 +75,7 @@ protected:
private:
int m_myIndex = 0;
SensorType m_positionSensor = SensorType::Invalid;
DcMotor *m_motor = nullptr;
Pid m_pid;
bool m_shouldResetPid = false;

View File

@ -826,7 +826,7 @@ custom maf_sensor_type_e 4 bits, S32, @OFFSET@, [0:1], @@maf_sensor_type_e_enum@
bit enableInnovateLC2
bit showHumanReadableWarning
bit stftIgnoreErrorMagnitude;+If enabled, adjust at a constant rate instead of a rate proportional to the current lambda error. This mode may be easier to tune, and more tolerant of sensor noise. Use of this mode is required if you have a narrowband O2 sensor.;
bit unusedBit_251_11
bit volkswagenEtbIdle
bit unusedBit_251_12
bit unusedBit_251_13
bit unusedBit_251_14

View File

@ -10,7 +10,7 @@ public:
// IEtbController mocks
MOCK_METHOD(void, reset, (), ());
MOCK_METHOD(void, start, (), (override));
MOCK_METHOD(void, init, (DcMotor* motor, int ownIndex, pid_s* pidParameters, const ValueProvider3D* pedalMap), (override));
MOCK_METHOD(void, init, (SensorType positionSensor, DcMotor* motor, int ownIndex, pid_s* pidParameters, const ValueProvider3D* pedalMap), (override));
MOCK_METHOD(void, setIdlePosition, (percent_t pos), (override));
MOCK_METHOD(void, autoCalibrateTps, (), (override));

View File

@ -32,7 +32,6 @@ TEST(etb, initializationNoPedal) {
}
TEST(etb, initializationSingleThrottle) {
StrictMock<MockEtb> mocks[ETB_COUNT];
WITH_ENGINE_TEST_HELPER(TEST_ENGINE);
@ -45,8 +44,8 @@ TEST(etb, initializationSingleThrottle) {
Sensor::setMockValue(SensorType::AcceleratorPedal, 0);
Sensor::setMockValue(SensorType::AcceleratorPedalPrimary, 0);
// Expect mock0 to be init with index 0, and PID params
EXPECT_CALL(mocks[0], init(_, 0, &engineConfiguration->etb, Ne(nullptr)));
// Expect mock0 to be init with TPS 1, index 0, and PID params
EXPECT_CALL(mocks[0], init(SensorType::Tps1, _, 0, &engineConfiguration->etb, Ne(nullptr)));
EXPECT_CALL(mocks[0], reset);
EXPECT_CALL(mocks[0], start);
@ -71,19 +70,43 @@ TEST(etb, initializationDualThrottle) {
// The presence of a second TPS indicates dual throttle
Sensor::setMockValue(SensorType::Tps2, 25.0f);
// Expect mock0 to be init with index 0, and PID params
EXPECT_CALL(mocks[0], init(_, 0, &engineConfiguration->etb, Ne(nullptr)));
// Expect mock0 to be init with TPS 1, index 0, and PID params
EXPECT_CALL(mocks[0], init(SensorType::Tps1, _, 0, &engineConfiguration->etb, Ne(nullptr)));
EXPECT_CALL(mocks[0], reset);
EXPECT_CALL(mocks[0], start);
// Expect mock1 to be init with index 2, and PID params
EXPECT_CALL(mocks[1], init(_, 1, &engineConfiguration->etb, Ne(nullptr)));
// Expect mock1 to be init with TPS 2, index 1, and PID params
EXPECT_CALL(mocks[1], init(SensorType::Tps2, _, 1, &engineConfiguration->etb, Ne(nullptr)));
EXPECT_CALL(mocks[1], reset);
EXPECT_CALL(mocks[1], start);
doInitElectronicThrottle(PASS_ENGINE_PARAMETER_SIGNATURE);
}
TEST(etb, initializationVolkswagenEtbIdleMode) {
StrictMock<MockEtb> mocks[ETB_COUNT];
WITH_ENGINE_TEST_HELPER(TEST_ENGINE);
// Enable VW idle mode
engineConfiguration->volkswagenEtbIdle = true;
for (int i = 0; i < ETB_COUNT; i++) {
engine->etbControllers[i] = &mocks[i];
}
// No accelerator pedal configured - this mode doesn't use it
// Expect mock0 to be init with TPS 2, index 0, and PID params
EXPECT_CALL(mocks[0], init(SensorType::Tps2, _, 0, &engineConfiguration->etb, Ne(nullptr)));
EXPECT_CALL(mocks[0], reset);
EXPECT_CALL(mocks[0], start);
// We do not expect throttle #2 to be initialized
doInitElectronicThrottle(PASS_ENGINE_PARAMETER_SIGNATURE);
}
TEST(etb, idlePlumbing) {
StrictMock<MockEtb> mocks[ETB_COUNT];
@ -117,7 +140,7 @@ TEST(etb, testSetpointOnlyPedal) {
// Uninitialized ETB must return unexpected (and not deference a null pointer)
EXPECT_EQ(etb.getSetpoint(), unexpected);
etb.init(nullptr, 0, nullptr, &pedalMap);
etb.init(SensorType::Invalid, nullptr, 0, nullptr, &pedalMap);
// Check endpoints and midpoint
Sensor::setMockValue(SensorType::AcceleratorPedal, 0.0f);
@ -167,7 +190,7 @@ TEST(etb, setpointIdle) {
.WillRepeatedly([](float xRpm, float y) {
return y;
});
etb.init(nullptr, 0, nullptr, &pedalMap);
etb.init(SensorType::Invalid, nullptr, 0, nullptr, &pedalMap);
// No idle range, should just pass pedal
Sensor::setMockValue(SensorType::AcceleratorPedal, 0.0f);
@ -204,6 +227,29 @@ TEST(etb, setpointIdle) {
EXPECT_FLOAT_EQ(55, etb.getSetpoint().value_or(-1));
}
TEST(etb, idleVolkswagenMode) {
WITH_ENGINE_TEST_HELPER(TEST_ENGINE);
// In this mode the idle position should be passed thru as the setpoint directly
engineConfiguration->volkswagenEtbIdle = true;
EtbController etb;
INJECT_ENGINE_REFERENCE(&etb);
etb.setIdlePosition(0);
EXPECT_FLOAT_EQ(0, etb.getSetpoint().value_or(-1));
etb.setIdlePosition(50);
EXPECT_FLOAT_EQ(50, etb.getSetpoint().value_or(-1));
etb.setIdlePosition(100);
EXPECT_FLOAT_EQ(100, etb.getSetpoint().value_or(-1));
// Out of range should be clamped
etb.setIdlePosition(-10);
EXPECT_FLOAT_EQ(0, etb.getSetpoint().value_or(-1));
etb.setIdlePosition(110);
EXPECT_FLOAT_EQ(100, etb.getSetpoint().value_or(-1));
}
TEST(etb, etbTpsSensor) {
// Throw some distinct values on the TPS sensors so we can identify that we're getting the correct one
Sensor::setMockValue(SensorType::Tps1, 25.0f);
@ -212,14 +258,14 @@ TEST(etb, etbTpsSensor) {
// Test first throttle
{
EtbController etb;
etb.init(nullptr, 0, nullptr, nullptr);
etb.init(SensorType::Tps1, nullptr, 0, nullptr, nullptr);
EXPECT_EQ(etb.observePlant().Value, 25.0f);
}
// Test second throttle
{
EtbController etb;
etb.init(nullptr, 1, nullptr, nullptr);
etb.init(SensorType::Tps2, nullptr, 1, nullptr, nullptr);
EXPECT_EQ(etb.observePlant().Value, 75.0f);
}
}
@ -231,7 +277,7 @@ TEST(etb, setOutputInvalid) {
EtbController etb;
INJECT_ENGINE_REFERENCE(&etb);
etb.init(&motor, 0, nullptr, nullptr);
etb.init(SensorType::Invalid, &motor, 0, nullptr, nullptr);
// Should be disabled in case of unexpected
EXPECT_CALL(motor, disable());
@ -245,7 +291,7 @@ TEST(etb, setOutputValid) {
EtbController etb;
INJECT_ENGINE_REFERENCE(&etb);
etb.init(&motor, 0, nullptr, nullptr);
etb.init(SensorType::Invalid, &motor, 0, nullptr, nullptr);
// Should be enabled and value set
EXPECT_CALL(motor, enable());
@ -261,7 +307,7 @@ TEST(etb, setOutputValid2) {
EtbController etb;
INJECT_ENGINE_REFERENCE(&etb);
etb.init(&motor, 0, nullptr, nullptr);
etb.init(SensorType::Invalid, &motor, 0, nullptr, nullptr);
// Should be enabled and value set
EXPECT_CALL(motor, enable());
@ -277,7 +323,7 @@ TEST(etb, setOutputOutOfRangeHigh) {
EtbController etb;
INJECT_ENGINE_REFERENCE(&etb);
etb.init(&motor, 0, nullptr, nullptr);
etb.init(SensorType::Invalid, &motor, 0, nullptr, nullptr);
// Should be enabled and value set
EXPECT_CALL(motor, enable());
@ -293,7 +339,7 @@ TEST(etb, setOutputOutOfRangeLow) {
EtbController etb;
INJECT_ENGINE_REFERENCE(&etb);
etb.init(&motor, 0, nullptr, nullptr);
etb.init(SensorType::Invalid, &motor, 0, nullptr, nullptr);
// Should be enabled and value set
EXPECT_CALL(motor, enable());
@ -309,7 +355,7 @@ TEST(etb, setOutputPauseControl) {
EtbController etb;
INJECT_ENGINE_REFERENCE(&etb);
etb.init(&motor, 0, nullptr, nullptr);
etb.init(SensorType::Invalid, &motor, 0, nullptr, nullptr);
// Pause control - should get no output
engineConfiguration->pauseEtbControl = true;
@ -327,7 +373,7 @@ TEST(etb, closedLoopPid) {
pid.minValue = -60;
EtbController etb;
etb.init(nullptr, 0, &pid, nullptr);
etb.init(SensorType::Invalid, nullptr, 0, &pid, nullptr);
// Disable autotune for now
Engine e;