Spaces:
Running
Running
File size: 4,355 Bytes
96a5049 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 |
#include "fuel_learning.h"
#include <Preferences.h>
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;
}
|