Линейная интерполяция

Glinka0
Offline
Зарегистрирован: 13.03.2017

Доброго времени суток.

Нужна ваша помощь.

Разрабатываю систему автоподсоса для автомобиля.

Основой этому будет являться стандартный датчик температуры авто,т.е. обычный термистор.

После этого сервоприд будет поворачиваться в зависимости от температуры.С получение значений сопротивления проблем нет и поворотом сервопривода проблем нет.

Собственно вопрос, как перевести значение сопротивления в температуру, в соответствии с значениями справа (линейной интерполяцией?).Принципиально хочется знать температуру,т.к. в дальнейшем планирую собирать бортовой компьютер.

Просьба ссаными тряпками не кидатся,я только учусь)

 

#define THERMISTORPIN A0

// сколько показаний берется для определения среднего значения

// чем больше значений, тем дольше проводится калибровка,

// но и показания будут более точными

#define NUMSAMPLES 5



#define SERIESRESISTOR 215 // емкость резистора "подтяжки"

int samples[NUMSAMPLES];

void setup(void) {

Serial.begin(9600); // соединяемся с компьютером и задаём скорость передачи 



}

void loop(void) {

uint8_t i;

float average;

// формируем вектор из N значений с небольшой задержкой между считыванием данных

for (i=0; i< NUMSAMPLES; i++) {

samples[i] = analogRead(THERMISTORPIN);

delay(10);

}

// определяем среднее значение в сформированном векторе

average = 0;

for (i=0; i< NUMSAMPLES; i++) {

average += samples[i];

}

average /= NUMSAMPLES;



// конвертируем значение в сопротивление

average = 1023 / average - 1;

average = SERIESRESISTOR / average;


Serial.println(average);
Serial.println("Ом ");


delay(1000);

}

 

 

Condensator
Offline
Зарегистрирован: 02.06.2017

Glinka0 пишет:
Собственно вопрос, как перевести значение сопротивления в температуру, в соответствии с значениями справа (линейной интерполяцией?).

В вашем случае линейная апроксимация, а не линейная интерполяция.

1) Измерить текущее значение сопротивления R

2) Определить в какой из участков линейной апроксимации  попадает его текущее значение:  R1MIN <  R  < R1MAX, где R1MIN R1MAX начало и конец участка

3) Вычислить температуру по формуле T=R*k1+k0, где k1 и k0 коэффициенты взятые  из графика линейной апроксимации

 

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

Думаю, кусочно-линейная аппроксимация здесь не очень подходит. График похож на экспоненту, соответственно, ею и следует аппроксимировать. Если не получится, взять степенную функцию или полином.

Barny
Offline
Зарегистрирован: 23.01.2015

Можно подобрать апроксимирующую функцию. На сколько помню есть онлайн калькуляторы или используйте в экселе Регрессионный анализ.

Logik
Offline
Зарегистрирован: 05.08.2014

Condensator пишет:

Glinka0 пишет:
Собственно вопрос, как перевести значение сопротивления в температуру, в соответствии с значениями справа (линейной интерполяцией?).

В вашем случае линейная апроксимация, а не линейная интерполяция.

1) Измерить текущее значение сопротивления R

2) Определить в какой из участков линейной апроксимации  попадает его текущее значение:  R1MIN <  R  < R1MAX, где R1MIN R1MAX начало и конец участка

3) Вычислить температуру по формуле T=R*k1+k0, где k1 и k0 коэффициенты взятые  из графика линейной апроксимации

 

Скорей всеже интерполяция, т.к. определяем значения между точками по табличным значениям точек. А так по сути почти согласен. В общем заводим массив P записей c парами  значений температура T-сопротивление R с константами из графика, типа P[0].T=-40; P[0].R=45314;  P[1].T=-35...... Измерили Rизм - в цикле проходим масив в цикле с индексом I покак не находим P[I].R>Rизм && P[I+1].R<=Rизм Как нашли - считаем T=P[I].T+(P[I+1].T-P[I].T)*(Rизм-P[I].R)/(P[I+1].R-P[I].R). Можно оптимизировать за счет того что температура идет с фиксированым шагом 5 градусов и вычислять ей T[I]=-40+5*I.

 

Сравнивая эту кусочно-ломаную интреполяцию и аппроксимацию експонентой - я бы делал интерполяцию. Не факт что фактически измереные пары R-T совпадут с заданым графиком. При интерполяции просто значения поправите а аппроксимирующую функцию прийдется пересчитать. Да и нет гарантии что експонентой впринципе удастся. Внешне конечно похоже, но не факт что ляжет. 

ПС. Вспомнил что есть у меня пример кусочно ломаной интерполяции http://arduino.ru/forum/otvlechennye-temy/v-pomoshch-samogonshchiku?page=2#comment-193782 По температуре кипения крепость самогона расчитывает (жидкости и пара). Только он заоптимизирован вконец.  Шаг таблицы по температуре5 градусов и значения выбираем от температуры, что позволяет убрать цикл поиска и т.д.

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

Logik, если не ляжет на экспоненту (кстати, это вполне обычное явление, и я предложил альтернативы), хотя похоже, то разумнее уж если и искать аппроксимацию, то использовать не кусочно-линейеую, а кусочно-экспоненциальную (либо кусочно линейную в логарифмическом масштабе.)

И еще, даже в случае непостоянного шага нужный интервал выбирать не линейным поиском, а бинарным.

Glinka0
Offline
Зарегистрирован: 13.03.2017

Честно говоря не понял и половины от того,что вы написали.

Сделал в эксэле через апроксимацию взял 5 полиномов ( для точности ),если брать один ,то после сорока валит значения в минус

С этим вроде разобрался, в коде впишу  уравнение через If else ,но как возвести число в степень, в интернете нашёл функцию pow ,но не нашёл примера её использования .

Как например в коде будет выглядеть  y=-6.453*x^3?

 

 

Glinka0
Offline
Зарегистрирован: 13.03.2017

Всё,разобоался

При измерении температуры тела погрешность 2-3 градуса,при кипячении погрешность в градус(возможно дополнительно греется от пара).

Если кому-то нужен код,то ниже

#define THERMISTORPIN A0

// сколько показаний берется для определения среднего значения

// чем больше значений, тем дольше проводится калибровка,

// но и показания будут более точными

#define NUMSAMPLES 5
#define SERIESRESISTOR 215 // емкость резистора "подтяжки"
double Temp; 

int samples[NUMSAMPLES];

void setup(void) {

Serial.begin(9600); // соединяемся с компьютером и задаём скорость передачи 



}

void loop(void) {

uint8_t i;

double average;

// формируем вектор из N значений с небольшой задержкой между считыванием данных

for (i=0; i< NUMSAMPLES; i++) {

samples[i] = analogRead(THERMISTORPIN);

delay(10);

}

// определяем среднее значение в сформированном векторе

average = 0;

for (i=0; i< NUMSAMPLES; i++) {

average += samples[i];

}

average /= NUMSAMPLES;



// конвертируем значение в сопротивление

average = 1023 / average - 1; 

average = SERIESRESISTOR / average;


Serial.print(average);
Serial.print("-Ом ");



int Temp=0;

  if (average>34280)                                       //Если температура меньше 35
  {Serial.println("Температура менее 35 градусов");} 
 
  else if (average<=342380 && average > 15460){            // Если температура от -35 до -20
  Temp = -631*pow(10,-15) * pow(average,3) + 653144932*pow(10,-16) * pow(average,2) - 28190624735131*pow(10,-16) * average + 103033637805441*pow(10,-13); //y = -0,00000000x3 + 0,00000007x2 - 0,00281906x + 10,30336378   x всегда в степени,а не умножить
  }
 
  else if (average>3790 && average<=15460){                //Если температура от -20 до +10
  Temp =  10*pow(10,-16)* pow(average,4) - 544488* pow(10,-16)*pow(average,3) + 11147159921*pow(10,-16)*pow(average,2) - 121399687828754*pow(10,-16)*average + 427488576477714000*pow(10,-16);  //y = 0,0000000000000010x4 - 0,0000000000544488x3 + 0,0000011147159921x2 - 0,0121399687828754x + 42,7488576477714000
  }
  else if (average>1370 && average<=3790){                //Если температура от +10 до +40
  Temp = -22995361*pow(10,-16)*pow(average,3) + 203802704428*pow(10,-16)*pow(average,2) - 682722421167996*pow(10,-16)*average + 1011941612141160000*pow(10,-16); //y = -0,0000000022995361x3 + 0,0000203802704428x2 - 0,0682722421167996x + 101,1941612141160000
  }
  else if (average>450 && average<=1370){                //Если температура от +40 до +70
  Temp = -602377383*pow(10,-16)*pow(average,3) + 1900064578101*pow(10,-16)*pow(average,2) - 2160255302543600*pow(10,-16)*average + 1342243448081070000*pow(10,-16);  //y = -0,0000000602377383x3 + 0,0001900064578101x2 - 0,2160255302543600x + 134,2243448081070000
  }
  else if (average>110 && average<=450){                //Если температура от +70 до +120
  Temp =  -157038*pow(10,-16)*pow(average,5) + 309294646*pow(10,-16)*pow(average,4) - 234553365547*pow(10,-16)*pow(average,3) + 84028462404499*pow(10,-16)*pow(average,2) - 15235314450979400*pow(10,-16)*average + 2128576001940600000*pow(10,-16); //y = -0,0000000000157038x5 + 0,0000000309294646x4 - 0,0000234553365547x3 + 0,0084028462404499x2 - 1,5235314450979400x + 212,8576001940600000
  }
  if (Temp>100)
  {Serial.println("Перегрев антифриза");
  }
  Serial.print(Temp);
  Serial.println("-Температура");
 

  
delay(2000);

}

 

kalapanga
Offline
Зарегистрирован: 23.10.2016

Если уж очень хочется формулой, то Википедия рекомендует уравнение Стейнхарта-Харта:

T = 1 / (A + B*ln(R) + C*(ln(R))^3)

Для этого термистора получается A=1.3e-3; B=2.6e-4; C=1.6485e-7. Температура в уравнении в кельвинах, так что ещё вычесть 273 нужно, чтобы родные градусы цельсия получились.

Glinka0
Offline
Зарегистрирован: 13.03.2017

Значения в таблице верны для конкретного датчика,у моего уже другие.

Если будет время,попробую расчитать.

Как я понял там нужно измерить реальную температуру и сопротивление которое выдаёт теморезистор в трёх точках и потом решить систему.

Верно?

 

uragan
Offline
Зарегистрирован: 23.02.2015

Матрица из примерно 10 значений. Между точками функцией map.  Точность примерно один - два градуса.

матрица двумерная: значения АЦП - температура.

Glinka0
Offline
Зарегистрирован: 13.03.2017

Тогда в моём случае этим заниматься смысла нет,у моего отклонения 2-3 градуса

Logik
Offline
Зарегистрирован: 05.08.2014

)))

Если устраивает "отклонения 2-3 градуса"  - нахрен вобще огород городить? Искать ближайщее табличное и отклонение не превысит 2,5 градусов.

//Матрица из примерно 10 значений.

Это шутка была? На графике 9 точек, ровно девять и матрица соответственно тоже.

//Точность примерно один - два градуса.

Снова шутите? Даже простая линейная для такой гладкой кривой даст точность до 0.2-0.3 градуса. Если получилось 1-2 то искать ошибку.

 

// Между точками функцией map. 

Дак она и есть линейная интерполяция :) Только делает все медлено и кривовато. То же самое но написаное самостоятельно, а это алгебра 5 или 6 класс, формула прямой, как правило лучше. 

///Значения в таблице верны для конкретного датчика,у моего уже другие.

Я предупреждал что так будет.

Glinka0, код вы зря выложили, такое людям не показывают ;) 

2128576001940600000*pow(10,-16) - унес сюда http://arduino.ru/forum/otvlechennye-temy/narochno-ne-pridumaesh-originalnye-tsitaty-vyskazyvaniya-i-tupizmy-foruma#new 

Logik
Offline
Зарегистрирован: 05.08.2014

andriano пишет:

 использовать не кусочно-линейеую, а кусочно-экспоненциальную (либо кусочно линейную в логарифмическом масштабе.)

Нет смысла, линейная  и так даст точность до пару десятых градуса. Куда уж выше? А все остальное заметно сложней. Что ТС и продемонстрировал )))  Были бы точки реже или точек больше или функция сложней или требования к результату выше (например САУ перегибов не любит и т.д.) - ну тогда еще разные подходы можна искать. А тут нет смысла.

А при поиске подходов в общем случае надо понимать, что почти все требуют работы с плавающей точкой, что конкретно для ардуины на 328р будет медлено и печально. А линейный расчет позволит избежать плавающей точки.

andriano пишет:

И еще, даже в случае непостоянного шага нужный интервал выбирать не линейным поиском, а бинарным.

Опять же смысла нет. Бинарный найдет за 3-4 итерации, перебор в среднем за 4-5. Но бинарный сложней. 

Glinka0
Offline
Зарегистрирован: 13.03.2017

Logik пишет:

 

 

Glinka0, код вы зря выложили, такое людям не показывают ;) 

2128576001940600000*pow(10,-16) - унес сюда http://arduino.ru/forum/otvlechennye-temy/narochno-ne-pridumaesh-originalnye-tsitaty-vyskazyvaniya-i-tupizmy-foruma#new 

Пожалуйста,мне не жалко.Это мой первый проект на ардуино и он работает.

Да,вы правы насчёт шеснадцати значных цифр я переборщил, но опять-же, это работает.Возможно проще было закинуть терморезистор совместно с Ds18b20 в чайник и из показаний нарисовать таблицу, но до меня это дошло несколько позже.

Glinka0
Offline
Зарегистрирован: 13.03.2017

Logik пишет:

 

 

Glinka0, код вы зря выложили, такое людям не показывают ;) 

2128576001940600000*pow(10,-16) - унес сюда http://arduino.ru/forum/otvlechennye-temy/narochno-ne-pridumaesh-originalnye-tsitaty-vyskazyvaniya-i-tupizmy-foruma#new 

Пожалуйста,мне не жалко.Это мой первый проект на ардуино и он работает.

Да,вы правы насчёт шеснадцати значных цифр я переборщил, но опять-же, это работает.Возможно проще было закинуть терморезистор совместно с Ds18b20 в чайник и из показаний нарисовать таблицу, но до меня это дошло несколько позже.