Обеспечение надежности измерений
Мультиплексирование датчиков
Как обеспечить надежность измерений?
Во многих областях требуется обеспечить надежные измерения физических параметров. Такая задача не может быть решена при помощи одного датчика, пусть и сверхнадежного. Для обеспечения надежности измерений применяют подход избыточности датчиков - измерения осуществляются сразу несколькими датчиками, и их измерения подлежат арбитражу. В этом проекте будет показан пример алгоритма арбитража.
Описание алгоритма
В самом простом случае, мы могли бы рассчитать значение как среднее по сумме всех показаний датчиков:
Однако, требуется учитывать насколько мы "доверяем" каждому датчику. Действительно, если произошел сбой датчика, то мы должны учитывать что его надежность снизилась. Поэтому у каждому датчику назначается вес , который учитывается при расчете:
Моделирование датчика
Создадим модель датчика как зашумленную синусоиду:
Общая модель системы будет содержать четыре датчика, фиксирующих один "внешний" сигнал - медленно колеблющуюся синусоиду. У датчиков изменяем параметр собственных аддитивных шумов. Помимо этого для одного из датчиков есть возможность моделировать отказ в виде добавляемых кратковременных импульсов большой амплитуды.
Фильтрация сигнала
В первую очередь требуется подавить собственные шумы цифровым фильтром нижних частот (ФНЧ). Построим спектрограмму нашего сигнала при помощи опции "Сигналы в частотной области" раздела Визуализации сигналов:
Мы наблюдаем "полезный" низкочастотный сигнал, а также шумовое "дно", что ожидаемо для аддитивного белого гауссовского шума. Необходимо подавлять частоты примерно выше 2 Гц. Воспользуемся графическим приложением "Редактор цифровых фильтров" и создадим в нём прототип цифрового фильтра с бесконечной импульсной характеристикой (БИХ) и указанными ниже параметрами:

Мы убедились, что амплитудно-частотная характеристика (АЧХ) полученного фильтра подходит для решаемой задачи очищения синусоиды от белого шума. Помимо этого, приложение "Редактор цифровых фильтров" позволяет автоматически получать структуру фильтра с синтезированными коэффициентами. Так выглядит автоматически сгенерированная подсистема БИХ-фильтра:
Ниже представлен исходный сигнал одного из датчиков и результат работы фильтра:
А также сравнение входного и выходного спектров:

Впрочем, фильтр не может справиться с сглаживанием формы сигнала с отказами - выбросами высокой амплитуды. Форма синусоиды отсаётся сильно искаженной:
А значит необходимо опираться на измерения датчиков без отказов.
Валидация показаний датчиков
Необходимо проводить контроль датчиков - можем ли мы доверять измерению. Контроль будем осуществлять по двум критериям:
- Находится ли измерение в заданом диапазоне?
- Насколько быстро изменяется сигнал?
Сочетание этих методов контроля позволяет отследить большинство отказов датчиков.
Посмотрим на реализацию этого метода в виде модели Engee:
Контроль скорости измерения сигнала будет осуществляться оценкой скользящего среднего. Если этот показатель выйдет за допустимые пределы, то это явно ошибка измерения.
Исключение выбросов
Датчик может быть исправен с точки зрения алгоритма контроля, но выдавать неадекватное значение (например, после аварии). Такие измерения необходимо обнаруживать и исключать из расчетов.
В качестве примера, измерим температуру 4 датчиками, и получим следующие измерения:
t = [22.3 22.1 21.9 68.8]
68.8 - явный выброс! Его надо исключить из расчета. Для этого воспользуемся следующим алгоритмом: посчитаем среднее по измерениям, и найдем отклонение от среднего. Затем найдем стандартное отклонение по выборке и исключим те измерения у которых отклонение от среднего больше, чем стандартное отклонение по выборке. Ниже дана реализация этого алгоритма:
import Pkg
Pkg.add("Statistics")
using Statistics
meandev = abs(t .- mean(t))
stddev = std(t)
valid_nums = meandev .< stddev
t_valid = t[valid_nums]
Данный алгоритм может быть промоделирован как:
На выходе у нас будет битовая маска, в которой 0 означает, что датчик должен быть исключен из расчета.
Управление весами
Ранее, в описании алгоритма, мы ввели понятие веса у датчика. Напомню, что вес - это мера доверия к датчику. Будем считать, что после отказа датчика и его последующего восстановления мы начинаем меньше доверять датчику. Это означает, что мы должны уменьшить его вес. Более того, можно ввести порог доверия к датчику и исключать датчики, чей вес ниже некоторого порога из расчета.
Сборка итоговой модели
Итоговая модель алгоритма имеет следующий вид:
Здесь:
- Подсистема Filter отвечает за фильтрацию сигнала и была сгенерирована приложением "Редактор Цифровых Фильтров".
- Подсистема OutlierExclusion отвечает за исключение выбросов
- Подсистема Sensor Validation отвечает за валидацию датчиков
Сам алгоритм реализован с помощью блока Engee Function:
Посмотреть код
mutable struct Block <: AbstractCausalComponent
sensor_weights::Vector{Float64}
function Block()
dims = INPUT_SIGNAL_ATTRIBUTES[1].dimensions;
new(vec(ones(1,prod(dims))))
#new(vec(ones(1,4)))
end
end
function (c::Block)(t::Real, TrustedSensors, sensor_data, SensorValid)
if t<=0
return(0.0,c.sensor_weights)
end
total_weight = sum(c.sensor_weights[TrustedSensors])
sum_weight = sum(sensor_data[TrustedSensors].*c.sensor_weights[TrustedSensors])
ret = sum_weight/total_weight;
if isnan(ret)
warning("Nan detected with values: TW $total_weight, SW: $sum_weight")
pause_simulation()
end
return (ret,c.sensor_weights)
end
function update!(c::Block, t::Real, TrustedSensors, sensor_data, SensorValid)
if !isempty(c.sensor_weights[.!SensorValid])
c.sensor_weights[.!SensorValid] .-= 0.001
end
c.sensor_weights[c.sensor_weights .< 0] .= 0
return c
end
Обратите внимание, что модель уже векторизована и может обрабатывать любое количество датчиков. Также интерес представляет подсистема Sensor Validation. Это подсистема типа ForEach Subsystem, который позволяет выполнить содержимое подсистемы для каждого элемента векторного сигнала.
Дополнительно, было введено ограничение для весов.
Тестирование алгоритма
Проверим, что наш алгоритм работает. Соберем тестовую модель:

Моделируются 4 датчика (модель датчика описана выше). Также моделируется отказ четвертого датчика. Сама модель алгоритма вставлена как модель-ссылка.
Посмотрим на поведение алгоритма при отсутствии отказов:
mdl = engee.load(joinpath(@__DIR__,"FusionTestbed.engee"));
engee.set_param!("FusionTestbed/Fault","PortValue"=>false);
simResult = engee.run(mdl);
using Plots
gr()
sensors = collect(simResult["SigGlue.1"]);
fusion = collect(simResult["SensorAlgo.FusedSignal"]);
p1 = plot(sensors.time,stack(sensors.value,dims=1),title="Raw Data",label=["Датчик 1" "Датчик 2" "Датчик 3" "Датчик 4"]);
p2 = plot(fusion.time,fusion.value,title="Fusion",label="Fusion");
plot(p1, p2, layout=(2, 1), link=:x, legend=:outerright)
А теперь сымитируем отказы четвертого датчика и посмотрим на результаты:
engee.set_param!("FusionTestbed/Fault","PortValue"=>true)
simResult = engee.run(mdl)
sensors = collect(simResult["SigGlue.1"]);
fusion = collect(simResult["SensorAlgo.FusedSignal"]);
p1 = plot(sensors.time,stack(sensors.value,dims=1),title="Raw Data",label=["Датчик 1" "Датчик 2" "Датчик 3" "Датчик 4"]);
p2 = plot(fusion.time,fusion.value,title="Fusion",label="Fusion");
plot(p1, p2, layout=(2, 1), link=:x, legend=:outerright)
Видно, что отказ датчика не повлиял на работу алгоритма. Таким образом, мы получили отказоустойчивый алгоритм измерений некоторой величины с помощью нескольких датчиков.
Вывод
В проекте рассмотрен отказоустойчивый алгоритм измерений и продемонстрирован методологически правильный подход к организации измерений множественными датчиками. Как развитие данного проекта следует рассмотреть более подробную модель датчика, учитывающую, например, класс точности.

