Spaces:
Running
Running
FuelLearningTable::FuelLearningTable() : | |
learningRate(0.01), | |
maxCorrection(1.2), | |
minCorrection(0.8) | |
{ | |
// Инициализация точек нагрузки (кПа) | |
for (int i = 0; i < LOAD_POINTS; i++) { | |
loadPoints[i] = 20.0 + (i * 6.0); // 20-110 кПа | |
} | |
// Инициализация точек оборотов | |
for (int i = 0; i < RPM_POINTS; i++) { | |
rpmPoints[i] = 800.0 + (i * 400.0); // 800-6800 RPM | |
} | |
// Инициализация ячеек коррекции | |
reset(); | |
} | |
void FuelLearningTable::begin() { | |
load(); // Загрузка сохраненных данных | |
} | |
void FuelLearningTable::reset() { | |
for (int i = 0; i < LOAD_POINTS; i++) { | |
for (int j = 0; j < RPM_POINTS; j++) { | |
cells[i][j] = 1.0; // Нейтральная коррекция | |
} | |
} | |
} | |
int FuelLearningTable::findNearestIndex(float value, const float* points, int size) { | |
if (value <= points[0]) return 0; | |
if (value >= points[size-1]) return size - 2; | |
for (int i = 0; i < size - 1; i++) { | |
if (value >= points[i] && value < points[i+1]) { | |
return i; | |
} | |
} | |
return 0; | |
} | |
float FuelLearningTable::interpolate2D(float load, float rpm) { | |
// Находим ближайшие индексы | |
int loadIdx = findNearestIndex(load, loadPoints, LOAD_POINTS); | |
int rpmIdx = findNearestIndex(rpm, rpmPoints, RPM_POINTS); | |
// Находим веса для интерполяции | |
float loadWeight = (load - loadPoints[loadIdx]) / | |
(loadPoints[loadIdx+1] - loadPoints[loadIdx]); | |
float rpmWeight = (rpm - rpmPoints[rpmIdx]) / | |
(rpmPoints[rpmIdx+1] - rpmPoints[rpmIdx]); | |
// Билинейная интерполяция | |
float c00 = cells[loadIdx][rpmIdx]; | |
float c10 = cells[loadIdx+1][rpmIdx]; | |
float c01 = cells[loadIdx][rpmIdx+1]; | |
float c11 = cells[loadIdx+1][rpmIdx+1]; | |
float c0 = c00 * (1 - loadWeight) + c10 * loadWeight; | |
float c1 = c01 * (1 - loadWeight) + c11 * loadWeight; | |
return c0 * (1 - rpmWeight) + c1 * rpmWeight; | |
} | |
void FuelLearningTable::updateCell(int loadIdx, int rpmIdx, float correction) { | |
// Применяем коррекцию с учетом скорости обучения | |
cells[loadIdx][rpmIdx] += correction * learningRate; | |
// Ограничиваем значение коррекции | |
cells[loadIdx][rpmIdx] = constrain(cells[loadIdx][rpmIdx], | |
minCorrection, maxCorrection); | |
} | |
float FuelLearningTable::getCorrection(float load, float rpm) { | |
return interpolate2D(load, rpm); | |
} | |
void FuelLearningTable::learn(float load, float rpm, float lambdaError) { | |
// Находим ближайшие ячейки | |
int loadIdx = findNearestIndex(load, loadPoints, LOAD_POINTS); | |
int rpmIdx = findNearestIndex(rpm, rpmPoints, RPM_POINTS); | |
// Обновляем основную ячейку | |
updateCell(loadIdx, rpmIdx, lambdaError); | |
// Обновляем соседние ячейки с меньшим весом | |
if (loadIdx > 0) { | |
updateCell(loadIdx-1, rpmIdx, lambdaError * 0.5); | |
} | |
if (loadIdx < LOAD_POINTS-1) { | |
updateCell(loadIdx+1, rpmIdx, lambdaError * 0.5); | |
} | |
if (rpmIdx > 0) { | |
updateCell(loadIdx, rpmIdx-1, lambdaError * 0.5); | |
} | |
if (rpmIdx < RPM_POINTS-1) { | |
updateCell(loadIdx, rpmIdx+1, lambdaError * 0.5); | |
} | |
} | |
bool FuelLearningTable::save() { | |
Preferences prefs; | |
prefs.begin("fuel_learn", false); | |
// Сохраняем таблицу коррекции | |
size_t written = prefs.putBytes("cells", cells, | |
sizeof(float) * LOAD_POINTS * RPM_POINTS); | |
prefs.end(); | |
return written == sizeof(float) * LOAD_POINTS * RPM_POINTS; | |
} | |
bool FuelLearningTable::load() { | |
Preferences prefs; | |
prefs.begin("fuel_learn", true); | |
// Загружаем таблицу коррекции | |
size_t readSize = prefs.getBytes("cells", cells, | |
sizeof(float) * LOAD_POINTS * RPM_POINTS); | |
prefs.end(); | |
return readSize == sizeof(float) * LOAD_POINTS * RPM_POINTS; | |
} | |