progress
This commit is contained in:
parent
38cb52b77a
commit
45b9355640
|
@ -63,4 +63,13 @@ public class AfrDataPoint {
|
||||||
public double getEngineLoad() {
|
public double getEngineLoad() {
|
||||||
return engineLoad;
|
return engineLoad;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "AfrDataPoint{" +
|
||||||
|
"afr=" + afr +
|
||||||
|
", rpm=" + rpm +
|
||||||
|
", engineLoad=" + engineLoad +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,5 +8,5 @@ import java.util.Collection;
|
||||||
*/
|
*/
|
||||||
public interface FuelAutoLogic {
|
public interface FuelAutoLogic {
|
||||||
// void MainWindow::calckGBC(double STEP)
|
// void MainWindow::calckGBC(double STEP)
|
||||||
Result process(boolean smooth, Collection<AfrDataPoint> dataECU, double STEP, double targetAFR, float[][] kgbcINIT);
|
Result process(boolean smooth, Collection<AfrDataPoint> dataECU, double STEP, double targetAFR, double[][] kgbcINIT);
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,21 +17,18 @@ public enum FuelAutoTune implements FuelAutoLogic {
|
||||||
// Fields.FUEL_RPM_COUNT
|
// Fields.FUEL_RPM_COUNT
|
||||||
// Fields.FUEL_LOAD_COUNT
|
// Fields.FUEL_LOAD_COUNT
|
||||||
public static final int SIZE = 16;
|
public static final int SIZE = 16;
|
||||||
|
public static final double _14_7 = 14.7;
|
||||||
|
|
||||||
private static boolean isLogEnabled() {
|
private static boolean isLogEnabled() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// private static final int TEMP_CORR = 39;
|
|
||||||
|
|
||||||
|
|
||||||
// void MainWindow::calckGBC(double STEP)
|
// void MainWindow::calckGBC(double STEP)
|
||||||
@Override
|
@Override
|
||||||
public Result process(boolean smooth, Collection<AfrDataPoint> dataECU, double STEP, double targetAFR, float[][] kgbcINIT) {
|
public Result process(boolean smooth, Collection<AfrDataPoint> dataECU, double STEP, double targetAFR, double[][] kgbcINIT) {
|
||||||
double kgbcSQ[][] = new double[FUEL_LOAD_COUNT][FUEL_RPM_COUNT];
|
double cellDeviation[][] = new double[FUEL_LOAD_COUNT][FUEL_RPM_COUNT];
|
||||||
double kgbcSQsum = 0;
|
double totalDeviation = 0;
|
||||||
double kgbcSQsumLast;
|
double minSQtotal = Double.MAX_VALUE;
|
||||||
double minSQtotal = 1e+15;
|
|
||||||
double kgbcSQsumLastTotal = 1e+16;
|
double kgbcSQsumLastTotal = 1e+16;
|
||||||
double ksq = 1000; //???? ??????????????????? ??????????
|
double ksq = 1000; //???? ??????????????????? ??????????
|
||||||
double ke = 100; //???? ??????????
|
double ke = 100; //???? ??????????
|
||||||
|
@ -43,7 +40,7 @@ public enum FuelAutoTune implements FuelAutoLogic {
|
||||||
bkGBC[data.PRESS_RT_32()][data.RPM_RT_32()]++;
|
bkGBC[data.PRESS_RT_32()][data.RPM_RT_32()]++;
|
||||||
}
|
}
|
||||||
|
|
||||||
float result[][] = MathUtil.deepCopy(kgbcINIT);
|
double result[][] = MathUtil.deepCopy(kgbcINIT);
|
||||||
|
|
||||||
double ktgbcRES[][] = new double[FUEL_LOAD_COUNT][FUEL_RPM_COUNT];
|
double ktgbcRES[][] = new double[FUEL_LOAD_COUNT][FUEL_RPM_COUNT];
|
||||||
double ktgbcINIT[][] = new double[FUEL_LOAD_COUNT][FUEL_RPM_COUNT];
|
double ktgbcINIT[][] = new double[FUEL_LOAD_COUNT][FUEL_RPM_COUNT];
|
||||||
|
@ -54,88 +51,97 @@ public enum FuelAutoTune implements FuelAutoLogic {
|
||||||
int COUNT_THRESHOLD = 20; // minimal number of measurements in cell to be considered
|
int COUNT_THRESHOLD = 20; // minimal number of measurements in cell to be considered
|
||||||
|
|
||||||
int minK = 0; // todo: what is this?
|
int minK = 0; // todo: what is this?
|
||||||
while (true) {
|
while (minK <= 4) {
|
||||||
for (int loadIndex = 0; loadIndex < FUEL_LOAD_COUNT; loadIndex++) {
|
for (int loadIndex = 0; loadIndex < FUEL_LOAD_COUNT; loadIndex++) {
|
||||||
for (int rpmIndex = 0; rpmIndex < FUEL_RPM_COUNT; rpmIndex++) {
|
for (int rpmIndex = 0; rpmIndex < FUEL_RPM_COUNT; rpmIndex++) {
|
||||||
if (bkGBC[loadIndex][rpmIndex] < COUNT_THRESHOLD)
|
if (bkGBC[loadIndex][rpmIndex] < COUNT_THRESHOLD)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
//log("Processing " + r + "/c" + c);
|
//log("Processing " + r + "/c" + c);
|
||||||
|
totalDeviation = getTotalDeviationAfterCellOptimization(smooth, dataECU, STEP, targetAFR, kgbcINIT, cellDeviation, ksq, ke, kg, result, loadIndex, rpmIndex);
|
||||||
double minSQ = 1e+16;
|
|
||||||
kgbcSQsum = 1e+16;
|
|
||||||
|
|
||||||
double step = STEP;
|
|
||||||
double mink = 0;
|
|
||||||
while (true) {
|
|
||||||
////////////////////////////////////
|
|
||||||
//????????? ?????????? ? ????????
|
|
||||||
MathUtil.setArray2D(kgbcSQ, 0);
|
|
||||||
|
|
||||||
kgbcSQsumLast = kgbcSQsum;
|
|
||||||
|
|
||||||
countDeviation(dataECU, kgbcSQ, result, kgbcINIT, targetAFR);
|
|
||||||
|
|
||||||
kgbcSQsum = MathUtil.sumArray(kgbcSQ);
|
|
||||||
|
|
||||||
if (smooth) {
|
|
||||||
kgbcSQsum = smooth(kgbcSQsum, ksq, ke, kg, result);
|
|
||||||
}
|
|
||||||
////////////////////////////////////
|
|
||||||
if (kgbcSQsum >= kgbcSQsumLast)
|
|
||||||
step = -step;
|
|
||||||
//???? ?????? ?? ??????? ????? ???, ?? ? ?? ?????????? ??
|
|
||||||
/*if(bkGBC[r][c]) */
|
|
||||||
|
|
||||||
// log("Adjusting " + step);
|
|
||||||
result[loadIndex][rpmIndex] += step;
|
|
||||||
if (kgbcSQsum < minSQ)
|
|
||||||
minSQ = kgbcSQsum;
|
|
||||||
|
|
||||||
if (Math.abs(minSQ - kgbcSQsumLast) < 1e-10)
|
|
||||||
mink++;
|
|
||||||
if (mink > 4) {
|
|
||||||
// updateTablekGBC();
|
|
||||||
// ui -> statusBar -> showMessage(QString::number (kgbcSQsum), 500);
|
|
||||||
log("break " + rpmIndex + "/" + loadIndex);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
if (kgbcSQsum < minSQtotal)
|
if (totalDeviation < minSQtotal)
|
||||||
minSQtotal = kgbcSQsum;
|
minSQtotal = totalDeviation;
|
||||||
if (Math.abs(minSQtotal - kgbcSQsumLastTotal) < 1e-10)
|
if (Math.abs(minSQtotal - kgbcSQsumLastTotal) < 1e-10)
|
||||||
minK++;
|
minK++;
|
||||||
if (minK > 4) {
|
kgbcSQsumLastTotal = totalDeviation;
|
||||||
//updateTablekGBC();
|
|
||||||
//ui->statusBar->showMessage(QString::number(kgbcSQsum), 500);
|
|
||||||
log("return " + minK);
|
|
||||||
return new Result(result);
|
|
||||||
}
|
|
||||||
kgbcSQsumLastTotal = kgbcSQsum;
|
|
||||||
//ui->statusBar->showMessage(QString::number(gbcSQsum));
|
//ui->statusBar->showMessage(QString::number(gbcSQsum));
|
||||||
//updateTableGBC();
|
//updateTableGBC();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log("return " + minK);
|
||||||
|
return new Result(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void countDeviation(Collection<AfrDataPoint> dataECU, double[][] kgbcSQ, float[][] kgbcRES, float[][] kgbcINIT, double targetAFR) {
|
/**
|
||||||
|
* Optimize one table cell
|
||||||
|
* @return total deviation of the whole map
|
||||||
|
*/
|
||||||
|
private static double getTotalDeviationAfterCellOptimization(
|
||||||
|
boolean smooth, Collection<AfrDataPoint> dataECU, double step,
|
||||||
|
double targetAFR, double[][] kgbcINIT, double[][] cellDeviation,
|
||||||
|
double ksq, double ke, double kg, double[][] result, int loadIndex, int rpmIndex) {
|
||||||
|
double totalDeviation = Double.MAX_VALUE;
|
||||||
|
double minTotalDeviation = Double.MAX_VALUE;
|
||||||
|
double prevTotalDeviation;
|
||||||
|
|
||||||
|
double currentStep = step;
|
||||||
|
int goodValueCounter = 0;
|
||||||
|
while (goodValueCounter <= 4) {
|
||||||
|
prevTotalDeviation = totalDeviation;
|
||||||
|
|
||||||
|
// todo: since we are only adjusting one cell there is not point to recalculate all deviations
|
||||||
|
// todo: potential CPU usage optimization here
|
||||||
|
countDeviation(dataECU, cellDeviation, result, kgbcINIT, targetAFR);
|
||||||
|
|
||||||
|
totalDeviation = MathUtil.sumArray(cellDeviation);
|
||||||
|
|
||||||
|
if (smooth) {
|
||||||
|
totalDeviation = smooth(totalDeviation, ksq, ke, kg, result);
|
||||||
|
}
|
||||||
|
////////////////////////////////////
|
||||||
|
if (totalDeviation >= prevTotalDeviation)
|
||||||
|
currentStep = -currentStep;
|
||||||
|
//???? ?????? ?? ??????? ????? ???, ?? ? ?? ?????????? ??
|
||||||
|
/*if(bkGBC[r][c]) */
|
||||||
|
|
||||||
|
// log("Adjusting " + currentStep);
|
||||||
|
result[loadIndex][rpmIndex] += currentStep;
|
||||||
|
|
||||||
|
minTotalDeviation = Math.min(minTotalDeviation, totalDeviation);
|
||||||
|
|
||||||
|
if (Math.abs(minTotalDeviation - prevTotalDeviation) < 1e-10)
|
||||||
|
goodValueCounter++;
|
||||||
|
}
|
||||||
|
return totalDeviation;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculated all per-cell deviations of all data points over whole table
|
||||||
|
*/
|
||||||
|
private static void countDeviation(Collection<AfrDataPoint> dataECU, double[][] cellDeviation, double[][] result, double[][] kgbcINIT, double targetAFR) {
|
||||||
|
MathUtil.setArray2D(cellDeviation, 0);
|
||||||
|
double normalizedTargetAfr = targetAFR / _14_7;
|
||||||
|
|
||||||
for (AfrDataPoint dataPoint : dataECU) {
|
for (AfrDataPoint dataPoint : dataECU) {
|
||||||
double ALF = targetAFR / 14.7;
|
double normalizedAfr = dataPoint.getAfr() / _14_7;
|
||||||
double tmp = (dataPoint.getAfr() / 14.7 - ALF *
|
|
||||||
(kgbcRES[dataPoint.PRESS_RT_32()][dataPoint.RPM_RT_32()]) /
|
double tmp = (normalizedAfr - normalizedTargetAfr *
|
||||||
|
(result[dataPoint.PRESS_RT_32()][dataPoint.RPM_RT_32()]) /
|
||||||
(kgbcINIT[dataPoint.PRESS_RT_32()][dataPoint.RPM_RT_32()]));
|
(kgbcINIT[dataPoint.PRESS_RT_32()][dataPoint.RPM_RT_32()]));
|
||||||
|
|
||||||
// if (isLogEnabled())
|
// if (isLogEnabled())
|
||||||
// log("r=" + r + "/c=" + c + ": tmp=" + tmp);
|
// log("r=" + r + "/c=" + c + ": tmp=" + tmp);
|
||||||
|
|
||||||
// kgbcSQ[dataPoint.PRESS_RT_32()][dataPoint.RPM_RT_32()] += tmp * tmp; todo: what is this deviation called?
|
// cellDeviation[dataPoint.PRESS_RT_32()][dataPoint.RPM_RT_32()] += tmp * tmp; todo: what is this deviation called?
|
||||||
kgbcSQ[dataPoint.PRESS_RT_32()][dataPoint.RPM_RT_32()] += Math.abs(tmp); // todo: what is this deviation called?
|
cellDeviation[dataPoint.PRESS_RT_32()][dataPoint.RPM_RT_32()] += Math.abs(tmp); // todo: what is this deviation called?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static double smooth(double kgbcSQsum, double ksq, double ke, double kg, float[][] kgbcRES) {
|
private static double smooth(double kgbcSQsum, double ksq, double ke, double kg, double[][] kgbcRES) {
|
||||||
double e = 0;
|
double e = 0;
|
||||||
kgbcSQsum = ksq * kgbcSQsum;
|
kgbcSQsum = ksq * kgbcSQsum;
|
||||||
|
|
||||||
|
|
|
@ -13,8 +13,8 @@ public enum FuelAutoTune2 implements FuelAutoLogic {
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Result process(boolean smooth, Collection<AfrDataPoint> dataECU, double GRAD, double targetAFR, float[][] VEcur) {
|
public Result process(boolean smooth, Collection<AfrDataPoint> dataECU, double GRAD, double targetAFR, double[][] VEcur) {
|
||||||
float result[][] = new float[SIZE][SIZE];
|
double result[][] = new double[SIZE][SIZE];
|
||||||
|
|
||||||
// proverka na statichnost' rezhimnoy tochki
|
// proverka na statichnost' rezhimnoy tochki
|
||||||
boolean fl_static = true;
|
boolean fl_static = true;
|
||||||
|
|
|
@ -10,10 +10,10 @@ public class MathUtil {
|
||||||
private MathUtil() {
|
private MathUtil() {
|
||||||
}
|
}
|
||||||
|
|
||||||
static float[][] deepCopy(float[][] input) {
|
static double[][] deepCopy(double[][] input) {
|
||||||
if (input == null)
|
if (input == null)
|
||||||
return null;
|
return null;
|
||||||
float[][] result = new float[input.length][];
|
double[][] result = new double[input.length][];
|
||||||
for (int r = 0; r < input.length; r++) {
|
for (int r = 0; r < input.length; r++) {
|
||||||
result[r] = input[r].clone();
|
result[r] = input[r].clone();
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,7 @@ public class MathUtil {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void setArray2D(double[][] array, double value) {
|
public static void setArray2D(double[][] array, double value) {
|
||||||
for (double[] a : array)
|
for (double[] a : array)
|
||||||
Arrays.setAll(a, i -> value);
|
Arrays.setAll(a, i -> value);
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,13 +5,13 @@ package com.rusefi.autotune;
|
||||||
* 2/23/2016.
|
* 2/23/2016.
|
||||||
*/
|
*/
|
||||||
public class Result {
|
public class Result {
|
||||||
private final float[][] kgbcRES;
|
private final double[][] kgbcRES;
|
||||||
|
|
||||||
public Result(float[][] kgbcRES) {
|
public Result(double[][] kgbcRES) {
|
||||||
this.kgbcRES = kgbcRES;
|
this.kgbcRES = kgbcRES;
|
||||||
}
|
}
|
||||||
|
|
||||||
public float[][] getKgbcRES() {
|
public double[][] getKgbcRES() {
|
||||||
return kgbcRES;
|
return kgbcRES;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,19 +1,62 @@
|
||||||
package com.rusefi.autotune.test;
|
package com.rusefi.autotune.test;
|
||||||
|
|
||||||
import com.rusefi.autotune.FuelAutoTune;
|
import com.rusefi.autotune.*;
|
||||||
import com.rusefi.autotune.Result;
|
|
||||||
import com.rusefi.autotune.AfrDataPoint;
|
|
||||||
import com.rusefi.config.Fields;
|
import com.rusefi.config.Fields;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 1/5/2016
|
* 1/5/2016
|
||||||
* (c) Andrey Belomutskiy 2013-2017
|
* (c) Andrey Belomutskiy 2013-2017
|
||||||
*/
|
*/
|
||||||
public class FuelAutoTuneTest {
|
public class FuelAutoTuneTest {
|
||||||
|
private static final double EPS = 0.00001;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAlreadyTuned() {
|
||||||
|
double value = 0.7;
|
||||||
|
double[][] veTable = createVeTable(value);
|
||||||
|
List<AfrDataPoint> dataPoints = new ArrayList<>();
|
||||||
|
|
||||||
|
for (int i = 0; i < 200; i++)
|
||||||
|
dataPoints.add(AfrDataPoint.valueOf(FuelAutoTune._14_7, 1500 + i, 50));
|
||||||
|
Result r = FuelAutoTune.INSTANCE.process(false, dataPoints, 0.1, FuelAutoTune._14_7, veTable);
|
||||||
|
|
||||||
|
assertEquals(0, countNotEqual(r.getKgbcRES(), value));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOptimizeOneCell() {
|
||||||
|
|
||||||
|
double value = 0.7;
|
||||||
|
double[][] veTable = createVeTable(value);
|
||||||
|
List<AfrDataPoint> allSamePoints = new ArrayList<>();
|
||||||
|
|
||||||
|
for (int i = 0; i < 200; i++)
|
||||||
|
allSamePoints.add(AfrDataPoint.valueOf(10, 1500, 50));
|
||||||
|
|
||||||
|
Result r = FuelAutoTune.INSTANCE.process(false, allSamePoints, 0.1, FuelAutoTune._14_7, veTable);
|
||||||
|
|
||||||
|
printNotDefault(r.getKgbcRES(), value);
|
||||||
|
assertEquals(0.5, r.getKgbcRES()[6][3], EPS);
|
||||||
|
assertEquals(1, countNotEqual(r.getKgbcRES(), value));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private int countNotEqual(double[][] array2D, double value) {
|
||||||
|
int result = 0;
|
||||||
|
for (double array[] : array2D) {
|
||||||
|
for (double v : array)
|
||||||
|
if (v != value)
|
||||||
|
result++;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAutoTune() {
|
public void testAutoTune() {
|
||||||
|
@ -54,26 +97,22 @@ public class FuelAutoTuneTest {
|
||||||
/**
|
/**
|
||||||
* this method prints all values which do not equal default value
|
* this method prints all values which do not equal default value
|
||||||
*/
|
*/
|
||||||
static void printNotDefault(float[][] array, double defaultValue) {
|
static void printNotDefault(double[][] array, double defaultValue) {
|
||||||
for (int i = 0; i < array.length; i++) {
|
for (int i = 0; i < array.length; i++) {
|
||||||
printNotDefault(array[i], i, defaultValue);
|
printNotDefault(array[i], i, defaultValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void printNotDefault(float[] array, int index, double defaultValue) {
|
private static void printNotDefault(double[] array, int index, double defaultValue) {
|
||||||
for (int i = 0; i < array.length; i++) {
|
for (int i = 0; i < array.length; i++) {
|
||||||
if (array[i] != defaultValue)
|
if (array[i] != defaultValue)
|
||||||
System.out.println("Found value: x=" + index + " y=" + i + ": " + array[i]);
|
System.out.println("Found value: x=" + index + " y=" + i + ": " + array[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static float[][] createVeTable(int value) {
|
static double[][] createVeTable(double value) {
|
||||||
float veMap[][] = new float[Fields.FUEL_LOAD_COUNT][Fields.FUEL_RPM_COUNT];
|
double veMap[][] = new double[Fields.FUEL_LOAD_COUNT][Fields.FUEL_RPM_COUNT];
|
||||||
for (int engineLoadIndex = 0; engineLoadIndex < Fields.FUEL_LOAD_COUNT; engineLoadIndex++) {
|
MathUtil.setArray2D(veMap, value);
|
||||||
for (int rpmIndex = 0; rpmIndex < Fields.FUEL_RPM_COUNT; rpmIndex++) {
|
|
||||||
veMap[engineLoadIndex][rpmIndex] = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return veMap;
|
return veMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue