Простые устройства
Просто об устройствах

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

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

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

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

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

Тонкая синяя линия состоит из отрезков прямых, по возможности максимально совпадающей с основной кривой. Работать с отрезками прямых в программе значительно проще, т. к. математически прямая описывается простым уравнением 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).

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

4Отрезок 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]

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

#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;
 }{/code}

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

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

{ads1}

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

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

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

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

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

Комментарии   

#1 ARV 30.09.2016 15:38
Кстати, можно заранее рассчитать (например, при помощи Excel) коэффициенты k и с для отрезков ломаной, и хранить в массиве уже их - это позволит заметно повысить быстродействие.

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

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

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

Copyright 2019 © simple-devices.ru.
При использовании материалов ссылка на simple-devices.ru обязательна.