Кусочно-линейная аппроксимация

Рейтинг пользователей: / 4
ХудшийЛучший 
Статьи - Программирование

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

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

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

Замена кривой на ломаную

Тонкая синяя линия состоит из отрезков прямых, по возможности максимально совпадающей с основной кривой. Работать с отрезками прямых в программе значительно проще, т. к. математически прямая описывается простым уравнением Y = k * X + c. Разумеется, замена гладкой кривой прямолинейными участками дает лишь приближенную картину соответствия X и Y, но тут уж надо идти на компромиссы.

Итак, мы заменили исходную кривую ломаной линией, т. е. выполнили кусочно-линейную аппроксимацию.

Аппроксимация графика
Ломаная линия определяется координатами (X;Y) точек ее изломов, т. е. для нашего случая это точки (0.8; 83), (2.2; 88), (3; 88), (4.2; 80), (5.2; 70), (7.8; 30), (10; 20) и (14.8; 12).

Теперь, прежде чем двигаться дальше, вспомним немножко школьную математику. Рассмотрим отрезок на координатной плоскости.

ОтрезокОтрезок AB задается координатами двух точек (XA; YA) и (XB; YB). Отрезок - это часть прямой, а уравнение прямой, как уже было сказано, описывается так: Y = k*X + c. Так как обе точки лежат на прямой, можно составить систему из двух уравнений:

YA = k*XA + с
YB = k*XB + с

В этой системе у нас два неизвестных k и c, следовательно, для их нахождения эту систему надо решить. Надеюсь, решение системы уравнений труда не составит, поэтому сам процесс не привожу, а привожу только готовое решение:

k = (YA - YB)/(XA - XB); с = YA - k * XA [1]

Таким образом, возвращаясь к нашей ломаной, мы по вышеприведенным формулам легко получим уравнения для каждого из отрезков ломаной по координатам точек ее излома. Остается лишь описать процесс на языке Си.

#define PT_CNT  8
 
typedef struct{
    float X, Y;
 } POINT;
 
POINT line[PT_CNT] = {{0.8, 83}, {2.2, 88}, {3, 88}, {4.2, 80}, {5.2, 70}, {7.8, 30}, {10, 20},  {14.8, 12}};
 
float function(float x){
    for(int i=0; i < (PT_CNT-1); i++){
       if((x >= line[i].X) && (x <= line[i+1].X){
          float k = (line[i].Y - line[i+1].Y)/(line[i].X - line[i+1].X);
          float с = line[i].Y - k * line[i].X;
          return k * x + c;
       }
    }
    return 0;
 }

Сначала определяем константу PT_CNT, которая указывает количество точек излома нашей ломаной. При этом начало и конец ломаной так же являются точками ее излома.

Затем определяем новый тип POINT для описания точки излома. Ну а после этого задаем массив, содержащий все точки излома. Это будут исходные данные для дальнейших расчетов.

Теперь определяем функцию function, которая получает в виде параметра x измеренное напряжение на датчике, а возвращает соответствующее ему значение измеряемого параметра, вычисленного по графику ломаной. Работает она по очень простому алгоритму.

Входной параметр функции х проверяется на принадлежность одному из отрезков ломаной, для чего в цикле перебираются соседние пары точек из массива line, и х сравнивается с координатами X этих точек. Если нужный отрезок найден, то по формулам [1] вычисляются коэффициенты уравнения прямой k и с, а затем вычисляется значение функции.

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

Как видите, все довольно просто. Код получается достаточно универсальным, т. е. переопределив константу PT_CNT и соответственно содержимое массива line вы можете реализовать кусочно-линейную аппроксимацию любой кривой с любой точностью приближения. И только об одном следует заботиться: чтобы в кривой и аппроксимирующих ее отрезках никогда не было вертикальных кусков, потому что для вертикального отрезка вычисление коэффициента k приведет к делению на 0, а значит, совершенно неверному результату. К счастью, реальные датчики в своих характеристиках такой ситуации не создают.

P.S. В вычислениях были использованы переменные типа float, что дает достаточно объемный по размеру и неторопливо исполняющийся код. Во многих случаях это не играет важной роли, но если требуется, можно легко адаптировать все для работы с целыми типами со знаком. Надеюсь, это не требует особого обсуждения.

 Обсудить на форуме (0 комментариев).

Добавить комментарий

Правила комментирования

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


Защитный код
Обновить



Темы форума

Нет сообщений для показа

Комментарии
Статистика
Просмотров:
mod_vvisit_counterСегодня2947
mod_vvisit_counterВчера7677
mod_vvisit_counterНа этой неделе16104
mod_vvisit_counterНа прошлой неделе43593
mod_vvisit_counterВ этом месяце137034
mod_vvisit_counterЗа все время9476203

Ваш IP: 54.145.11.9
 , 
28 Сен. 2016