
- •Содержание
- •Введение
- •1. Морфологический анализ экг
- •2. Методы обработки экг
- •3. Исследование существующих методов выделения r – зубца
- •4. Разработка алгоритма выделения r – зубца
- •4.1. Определение требований к алгоритму выделения r – зубца
- •5. Реализация алгоритма выделения r – зубца
- •6. Тестирование
- •6.1. Оценка точности выделения r – зубца
4.1. Определение требований к алгоритму выделения r – зубца
Частота дискретизации 500 Гц
Частота АЦП не меньше 16 бит
Алгоритм разрабатывался на базе микроконтроллера STM32F373C8
5. Реализация алгоритма выделения r – зубца
Реализация алгоритма выделения R-зубца выполнена на языке «C»; графический интерфейс приложения выполнен на языке высокого уровня «Python 3.4».
1.
Рисунок 5. Исходный сигнал ЭКГ
Рисунок 6. Увеличенный масштаб исходного сигнала ЭКГ
2.
Рисунок 7. Применение фильтров верхних и нижних частот на исходный сигнал ЭКГ
Рисунок 8. Увеличенный масштаб применения фильтров верхних и нижних частот на исходный сигнал ЭКГ
3.
Рисунок 9. Операция дифференцирования
Рисунок 10. Увеличенный масштаб операции дифференцирования
4.
Рисунок 11. Операция возведения в квадрат
Рисунок 12. Увеличенный масштаб операции возведения в квадрат
5.
Рисунок 12. Операция интегрирования с вычислением R-зубцов
6. Тестирование
Проведем тестирование на пяти различных сигналах ЭКГ.
1.
Рисунок 13. Сигнал ЭКГ №1
2.
Рисунок 14. Сигнал ЭКГ №2
3.
Рисунок 15. Увеличенный сигнал ЭКГ №2
Рисунок 16. Сигнал ЭКГ №3
4.
Рисунок 17. Сигнал ЭКГ №4
5.
Рисунок 18. Сигнал ЭКГ №5
6.1. Оценка точности выделения r – зубца
При тестировании программного обеспечения ни один R-зубец не был найден неверно.
Следовательно, на выбранном отрезке времени, точность выделения R-зубца равняется 100%.
Однако при тестировании в режиме реального времени ожидается погрешность около 1%.
Выводы
В результате проделанной работы был предложен метод неинвазивной регистрации и анализа ЭКГ человека. В основе метода лежит алгоритм Пан-Томпкинса, основанный на анализе QRS-комплекса в режиме реального времени. Используя метод Пан – Томпкинса можно определить амплитудные показатели R-зубца, а также значения R-R интервалов. Рассчитанные показатели позволяют определить наличие патологий сердечной мышцы у пациентов.
По полученным данным исследования, сформулируем требования к АЦП обработки ЭКГ сигнала.
Выходная частота дискретизации сигнала составляет 500Гц. Требуемая разрядность АЦП для данной системы не менее 16 бит, с общим коэффициентом усиления K=500.
В данном курсовом проекте была написана программа на языке СИ, реализующая метод выделения QRS комплекса, а также была произведена визуализация полученных данных на языке программирования Python. Алгоритм разработан на базе микроконтроллера STM32F373C8
Список используемой литературы
1. Рангайан Р.Н. Анализ биомедицинских сигналов. Практический подход, 2007 – 404 с.
2. Барановский А.Л. Кардиомониторы. Аппаратура непрерывного контроля ЭКГ, 1993 –248 с.
3. Мельник О.В., Михеев А.А., Нечаев Г.И. Выделение дрейфа изолинии электрокардиосигнала // Биомедицинские технологии и радиоэлектроника, 2005, № 1-2 С.26-30.
4. Васильев В.П. Основы теории и расчета цифровых фильтров: учеб. Пособие для высш. учеб. Заведений – М.: Академия, 2007 – 272 с.
Приложение А. Листинг №1
#include "algo.h"
#define st_arr_l(__arr__) \
sizeof(__arr__) / sizeof(*__arr__)
#define EXP 10
#define INTER_STEP 4
static PyObject *err_str(const char *msg) {
// Бросает исключение в питон
raise_exception(msg);
return NULL;
}
// Дифференциация
static void differ(float *signal, Py_ssize_t size, float dx) {
Py_ssize_t i = 1;
float last_val = *signal;
for (; i < size; i++) {
float tmp = signal[i];
signal[i] = (signal[i] - last_val) / dx; // tan(alpha)
last_val = tmp;
}
*signal = signal[1];
}
// Возведение переданных значений в квадрат
static void sig_sqr(float *signal, Py_ssize_t size) {
Py_ssize_t i = 0;
for (; i < size; i++, signal++)
*signal *= *signal;
}
// Алгоритм скользящего окна
void moving_window(const float *signal, float *out, Py_ssize_t size) {
const int N = 38;
Py_ssize_t i = N;
int j = 0;
for (; i < size; i++) {
out[i] = 0.0f;
for (j = 0; j < N; j++) out[i] += signal[i - N + j];
out[i] /= (float)N;
}
}
// Применение высокочастотного фильтра
static void band_filters_impl_hi(const float *src, float *dst, Py_ssize_t size) {
Py_ssize_t i = 1;
*dst = *src;
for (; i < size; i++) {
dst[i] = dst[i - 1] - src[i] / 32;
if (i > 15)
dst[i] += src[i - 16];
if (i > 16)
dst[i] -= src[i - 17];
if (i > 31)
dst[i] += src[i - 32] / 32;
}
}
// Применение низкочастотного фильтра
static void band_filters_impl_lo(const float *src, float *dst, Py_ssize_t size) {
Py_ssize_t i = 0;
for (; i < size; i++) {
if (i < 2)
dst[i] = src[i];
else {
dst[i] = 2 * dst[i - 1] - dst[i - 2] + src[i] / 32;
if (i > 5)
dst[i] -= src[i - 6] / 16;
if (i > 11)
dst[i] += src[i - 12] / 32;
}
}
}
// Вызывается ф-ия напрямую из питона
PyObject *band_filter(PyObject *self, PyObject *args) {
PyObject *data, *result = NULL;
PyObject *x, *y, *ry;
Py_ssize_t size;
if (!PyArg_ParseTuple(args, "O!", &PyTuple_Type, &data)) // Проверка ввода
return err_str("Incorrect input");
size = PyTuple_Size(data); // Проверка ввода
if (!size)
return err_str("Incorrect tuple given");
if (size != 2)
return err_str("Tuple must contain 2 tuples (x & y)");
PyArg_Parse(PyTuple_GetItem(data, 0), "O!", &PyTuple_Type, &x); // Вытаскивание значений по х, у из питона
PyArg_Parse(PyTuple_GetItem(data, 1), "O!", &PyTuple_Type, &y);
size = PyTuple_Size(x);
if (!size) // Проверка размеров массивов данных
return err_str("Incorrect coordinates given (length == 0)");
if (size != PyTuple_Size(y))
return err_str("Incorrect coordinates given (x_len != y_len)");
float *xvec = (float *)PyMem_RawMalloc(sizeof(float) * size); // Выделение памяти для локальных буфферных переменных
float *yvec = (float *)PyMem_RawMalloc(sizeof(float) * size);
if (!xvec || !yvec) {
PyMem_RawFree(xvec ?: yvec); // Ошибка при выделении памяти
return err_str("Error allocating memory");
}
Py_ssize_t i = 0;
for (; i < size; i++)
PyArg_Parse(PyTuple_GET_ITEM(y, i), "f", xvec + i); // Копирование входного сигнала в локальные переменные
float *ptr = yvec;
band_filters_impl_lo(xvec, yvec, size); // Применение низкочастотного фильтра
band_filters_impl_hi(yvec, xvec, size); // Примененеи высокочастотного фильтра
float *x_ptr = xvec == ptr ? yvec : xvec;
for (i = 0; i < size; i++) // Копирование временного массива из питона
PyArg_Parse(PyTuple_GET_ITEM(x, i), "f", x_ptr + i);
float dx = x_ptr[1] - x_ptr[0];
differ(ptr, size, dx); // дифференцирование
sig_sqr(ptr, size); // Возведение в квадрат
moving_window(ptr, x_ptr, size); // ПРименение алгоритма скользящего окна
memcpy(ptr, x_ptr, size * sizeof(*ptr)); // Перемещение значений в другую буфферную переменную
for (i = 0; i < size; i++) // Перемещение значений времени из питона в массив
PyArg_Parse(PyTuple_GET_ITEM(x, i), "f", x_ptr + i);
_PyTuple_Resize(&x, size);
ry = PyTuple_New(size);
if (!ry)
return err_str("Error allocating memory");
// Подготовка переменных для вывода обратно в питон
result = PyTuple_New(2);
if (result) {
for (i = 0; i < size; i++) {
PyTuple_SET_ITEM(ry, i, Py_BuildValue("f", ptr[i])); // Копируем в питон массив сигнала
PyTuple_SET_ITEM(x, i, Py_BuildValue("f", x_ptr[i])); // время
}
PyTuple_SET_ITEM(result, 0, Py_BuildValue("O", x)); // [ [ x_data ... ], [ y_data ... ] ]
PyTuple_SET_ITEM(result, 1, Py_BuildValue("O", ry));
} else
err_str("Error allocating memory");
// Освобождение памяти
PyMem_RawFree(xvec);
PyMem_RawFree(yvec);
return result;
}
#include "algo_ex.h"
static PyObject *exception;
// Модуль помогает кинуть исключение в питон в случае неверного ввода
int init_exception(PyObject *parent) {
exception = PyErr_NewException(LIB_NAME ".Exception", NULL, NULL);
Py_INCREF(exception);
PyModule_AddObject(parent, "exception", exception);
return exception != NULL;
}
void del_exception() {
// Освободить выделенную исключениями память
Py_CLEAR(exception);
}
void raise_exception_n(const char *msg, const char *file, int line) {
// Послать исключение в питон
char str[1024];
snprintf(str, sizeof(str), "Exception came from %s:%d: %s\n", file, line, msg);
PyErr_SetString(exception, str);
}
#include <Python.h>
#include "algo.h"
#include "algo_ex.h"
// Модуль инициализации С библиотеки при старте программы
static PyMethodDef module_methods[] = {
{ "band_filter", (PyCFunction)band_filter, METH_VARARGS }, // Определение методов, доступных из питона
{ NULL, NULL }
};
static int module_traverse(PyObject *m, visitproc visit, void *arg) {
/* Memory allocations */
return 0;
}
static int module_clear(PyObject *m) {
/* Memory clear */
del_exception();
return 0;
}
static struct PyModuleDef moduledef = { // Структура характеризует модуль С для питона (****)
PyModuleDef_HEAD_INIT,
LIB_NAME, // Имя модуля
NULL,
0,
module_methods, // Методы модуля
NULL,
module_traverse, // Ф-ия инициализации (ничего не делает)
module_clear, // Функция деинициализации
NULL
};
PyObject *PyInit_algo() {
// Ф-ия вызывается при старте программы
PyObject *module = PyModule_Create(&moduledef); // Инициализация параметров модуля см (****)
// Обработка ошибок инициализации
if (module == NULL)
return NULL;
if (!init_exception(module)) {
Py_DECREF(module);
return NULL;
}
return module;
}