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;
}