Код для отображения напряжения на датчике и вывода значения в литрах

map

Я нашел в Интернете скетч резистивного датчика уровня топлива, который я адаптировал. Резистивный датчик представляет собой плавающий магнит, который активирует переключатели считывания для изменения сопротивления. Используя стабильный источник питания 5 В и резистор 100 Ом, у меня есть мост напряжения, который соединен с датчиком и выводом А1. Оригинальный скетч использует это, чтобы получить показания в литрах:

TankValue0 = map(sensorTankValue0, 352, 824, 0, 100);
if (TankValue0 < 0) {
  TankValue0 = 0;
}
if (TankValue0 > 100) {
  TankValue0 = 100;

Нижний диапазон не слишком далеко, но когда вы доберетесь до вершины, он выйдет на обед.

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

V L
4.04 94
3.981 88
3.902 84
3.81 81
3.703 77
3.637 72
3.564 69
3.483 65
3.393 61
3.293 57
3.18 53
3.053 50
2.908 46
2.546 42
2.192 38
1.726 35

Как я получу эту информацию в скетче, чтобы дать мне считывание L в моем резервуаре?

На самом деле я не знаю программирования. Я много работаю с Arduino IDE, но только с помощью скетчей, написанных другими людьми. Итак, у меня огромный пробел в базовых знаниях. Поэтому, пожалуйста, огласите это в уме.

Вот весь скетч:

#include <LiquidCrystal_I2C.h>


int TankValue0;
int TankValue1;

LiquidCrystal_I2C lcd(0x27, 16, 2);   // установите адрес ЖК-дисплея на 0x27 для 16 символов и 2-строчного дисплея


void setup() {
  lcd.init();                          // инициализация lcd
  lcd.init();
  lcd.backlight();
  lcd.setCursor(1, 0);
  lcd.clear();
  lcd.print("WORD");
  lcd.setCursor(0, 1);
  lcd.print("TO");
  delay(1500);
  lcd.clear();
  lcd.print("YO ");
  lcd.setCursor(0, 1);
  lcd.print("MOMMA");        
  delay(2000);
  lcd.clear();
  pinMode(A0, INPUT);
  Serial.begin(9600);               // запуск последовательного монитора
 
}
void loop() {
  int sensorTankValue0 = analogRead(A0);
  int sensorTankValue1 = analogRead(A1);
  TankValue0 = map(sensorTankValue0, 352, 824, 34, 94);
  TankValue1 = map(sensorTankValue1, 785, 310, 0, 100);
  if (TankValue0 < 0) {
    TankValue0 = 0;
  }
  if (TankValue1 < 0) {
    TankValue1 = 0;
  }
  if (TankValue0 > 100) {
    TankValue0 = 100;
  }
  if (TankValue1 > 100) {
    TankValue1 = 100;
  }
  lcd.print("S.V. SAVANNAH");
  lcd.setCursor(0, 1);
  delay(100);
  lcd.print("DAY TANK   ");
  lcd.print(TankValue0);
  lcd.print("%");
  lcd.setCursor(0, 0);

  delay(1000);
  lcd.clear();
}       

, 👍0

Обсуждение

Кстати, этот резервуар квадратный, и держите 100 л, поплавок не достаточно длинный, чтобы достичь дна. Позже я хочу использовать этот же Arduino для 2 других резервуаров, которые имеют странную форму и содержат разное количество., @SV Savannah

начните с рисования графика, @jsotola

пожалуйста, опубликуйте таблицу данных как текст, а не как изображение, @jsotola

V L 4.04 94 3.981 88 3.902 84 3.81 81 3.703 77 3.637 72 3.564 69 3.483 65 3.393 61 3.293 57 3.18 53 3.053 50 2.908 46 2.546 42 2.192 38 1.726 35, @SV Savannah

Я бы использовал таблицу поиска для таких задач. Вероятно, это не стоит усилий моделирования., @Sim Son


1 ответ


1

Подгонка кривой

Ну, ленивый способ сделать это-просто подогнать к нему кривую, используя Excel, Libreoffice, Google Docs или что-то еще. Вот gnuplot используется таким образом, чтобы подогнать к нему кубический многочлен:

"points.txt" содержит ваши данные, разделенные табуляцией:

4.04    94
3.981   88
3.902   84
3.81    81
3.703   77
3.637   72
3.564   69
3.483   65
3.393   61
3.293   57
3.18    53
3.053   50
2.908   46
2.546   42
2.192   38
1.726   35

Сессия Gnuplot:

gnuplot> f(x) = A*(x**3) + B*(x**2) + C*x + D
gnuplot> fit f(x) "points.txt" via A, B, C, D
iter      chisq       delta/lim  lambda   A             B             C             D            
   0 1.8323200047e+01   0.00e+00  9.28e+01    2.818423e+00  -1.100151e+01   1.447650e+01   2.873579e+01
   1 1.8323200047e+01  -4.65e-10  9.28e+00    2.818423e+00  -1.100151e+01   1.447650e+01   2.873579e+01
iter      chisq       delta/lim  lambda   A             B             C             D            

После 1 итерации подгонка сходилась.
конечная сумма квадратов невязок : 18.3232
отн. изменение за последнюю итерацию : -4.6534 e-15

степени свободы  (FIT_NDF)                           : 12
rms остатков      (FIT_STDFIT) = sqrt(WSSR/ndf)      : 1.23569
дисперсия остатков  (reduced chisquare) = WSSR/ndf   : 1.52693

Конечный набор параметров          Асимптотическая стандартная ошибка
=========================          ==========================
A               = 2.81842          +/- 1.156        (41.02%)
B               = -11.0015         +/- 10.22        (92.86%)
C               = 14.4765          +/- 29.11        (201.1%)
D               = 28.7358          +/- 26.52        (92.27%)

корреляционная матрица параметров подгонки:
                A      B      C      D      
A               1.000 
B              -0.998  1.000 
C               0.990 -0.997  1.000 
D              -0.974  0.987 -0.996  1.000 

И вот что выглядит на фоне фактических данных с сюжетом "points.txt" с точками линий, f(x): Curve fitting with data.

Итак, у вас есть функция:

float f(float x) {
  static const float A =   2.81842;
  static const float B = -11.0015 ;    
  static const float C =  14.4765 ;
  static const float D =  28.7358 ;
  const float x_squared = x * x;
  const float x_cubed   = x * x_squared;

  return A * x_cubed + B * x_squared + C * x + D;
}

Если вас не волнует размер кода и медлительность арифметики с плавающей запятой, то это может быть нормально.

В противном случае вы можете преобразовать эту функцию в нечто вроде арифметики с фиксированной точкой.

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

Peicewise Линейный

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

float f(float x) {
  static const struct pt {
    float v;
    int l;
  } points[] = {
    {0.0  ,  0},  // Я поставил это здесь для тестирования, но поставил лучшее значение здесь.
    {1.726, 35},
    {2.192, 38},
    {2.546, 42},
    {2.908, 46},
    {3.053, 50},
    {3.18 , 53},
    {3.293, 57},
    {3.393, 61},
    {3.483, 65},
    {3.564, 69},
    {3.637, 72},
    {3.703, 77},
    {3.81 , 81},
    {3.902, 84},
    {3.981, 88},
    {4.04 , 94}
    // у вас, вероятно, должна быть еще одна запись здесь, чтобы показания до 5 В работали.
  };
  static const size_t table_len = sizeof points / sizeof points[0];

  for (size_t i = 0; i < table_len - 1; ++i) {
    if (x >= points[i].v && x < points[i + 1].v) {
      return 
            (x - points[i].v) 
          / (points[i + 1].v - points[i].v) 
          * (points[i + 1].l - points[i].l)
          + points[i].l;
    }
  }

  return -1; 
}

Сидя в центре этого в операторе return , в основном сырая реализация map () Arduino, но для плавающей точки, которая опять же, вам действительно не нужна плавающая точка, даже если вы хотите использовать этот кусочный подход.

Короче говоря, вы находите, между какими двумя записями таблицы вы сидите, и применяете то, что составляет map() на отрезке линии, образованном двумя записями таблицы.

PROGMEM

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

Если вы делаете это на Arduino на базе AVR, то таблица для кусочно-линейного подхода или подход с чистой таблицей поиска могут использовать PROGMEM для массива.

Что касается float и фиксированной точки

Любой из этих методов может быть адаптирован к математике с фиксированной точкой.
Если вы используете Arduino на базе AVR, имейте в виду, что аппаратной поддержки плавающей точки вообще нет, и хотя double существует как тип, он реализован в avr-gcc как одинарная точность float.

Если вы используете один из не основанных на AVR ардуино, он может иметь поддержку float (и, возможно, для double), в этом случае сгенерированный код может быть не так плох, хотя все еще может быть медленнее, чем при использовании целых чисел.

Если вы можете делать все с целыми числами, то результирующий код должен быть намного меньше и быстрее. Я предполагаю, что для того, что вы делаете, если кто-то из них и более важен, то, вероятно, он меньше.

При использовании целых чисел вы можете сохранить свое входное "напряжение" в качестве счетчика АЦП, не имея промежуточного расчета напряжения. И даже, если хотите в одной цифре литров, когда вы закончите, промежуточной интерполяцией кривой или таблицы подстановки может потребовать от вас работать в чем-то вроде centiliters, миллилитров, так что значения как на постоянной кривой сторона, например, может быть представлен как целое число, например, 2818 мл, а не грубее 3 л. Если вы не используете простой таблицы поиска и нахождения ближайшего въезда, то есть если вы используете кривой или кусочно-линейное приближение, делаем все в целочисленной арифметике от графа АЦП мл. Хотя вам нужно будет выполнить еще одну подгонку кривой, так как A, Bи т. Д. Будут представлять собой mL-per-ADC-count, а не L-per-Volt. Или масштабировать их от заданного значения.

И есть другие варианты, кусочно-кубические сплайны и т. Д.

,

Большое спасибо @timemage. Это большой ресурс. Должен признаться, что я не совсем все это понимаю;-) Я буду заниматься своими исследованиями и самообразованием. тем временем я попытался вставить код "Float" в свой скетч, но получил ошибку "lcd'does not name a type"., @SV Savannah

Я не знаю, насколько вы буквальны в верхнем регистре "F" в "Float", но это нижний регистр в фактическом использовании. Если вам нужно что-то более интерактивное, чем stackexchange, на freenode есть несколько сотен пользователей Arduino, включая меня. Есть и официальные форумы. Если у вас есть конкретные вопросы о том, что я написал, я могу попытаться обратиться к ним., @timemage