
- •Аннотация
- •Содержание
- •Введение
- •1 Теоретический обзор темы
- •1.1 Описание и анализ исследуемых процессов и сигналов.
- •1.2 Патентная проработка предлагаемых технических решений
- •1.3 Функциональная и структурная схема объекта проектирования
- •2 Моделирование проектируемого устройства
- •2.1 Расчет и конструирование проектируемого объекта. Моделирование и экспериментальная апробация принципиальной схемы устройства в сапр Micro-Cap
- •2.2 Моделирование печатной платы устройства в сапр Circuit Maker
- •3 Обработка сигналов, получаемых в процессе работы устройства
- •3.1 Разработка и описание алгоритма работы программного обеспечения
- •3.2 Программная реализация разработанного алгоритма
- •Заключение
- •Список использованных источников
- •Приложение а код разработанной программы
Заключение
Задачи поставленные в начале работы были достигнуты. Для разработанного устройства, предназначенного для усиления электрокардиосигнала, была разработана принципиальная электрическая схема, произведено моделирование работы схемы в различных режимах анализа, спроектирована модель печатной платы, а также программное обеспечение для обработки получаемых сигналов. Работа позволила закрепить навыки, полученные на дисциплинах, изученных за период обучения, что может быть полезно в дальнейшей разработке более крупных и технически сложных проектов.
Список использованных источников
1. Кипенский А.В., Шамардина В.Н., Дейнеко Д.М. Электрокардиография: Учебно-методическое пособие. – Харьков, НТУ «ХПИ», 2002. – 52 с.
2. ГОСТ 19687-89. Приборы для измерения биоэлектрических потенциалов сердца. Общие технические требования и методы испытаний.
3. ГОСТ Р МЭК 60601-2-51-2008 Изделия медицинские электрические. Часть 2-51. Частные требования безопасности с учетом основных функциональных характеристик к регистрирующим и анализирующим одноканальным и многоканальным электрокардиографам.
4. Р 50.2.009-2011. Государственная система обеспечения единства измерений. Электрокардиографы, электрокардиоскопы и электрокардио-анализаторы. Методика поверки.
5. Пат. RU 2011124845/14. Устройство регистрации биопотенциалов / Красичков А.С.,Малых Ю.А., Пычко С.А., Ульяницкий Ю.Д. Опубл. 27.11.2011. Бюл. № 33.
6. Пат. RU 2017112520. Усилитель биопотенциалов с диагностированием обрыва электродов / Давыдов Д.В., Егоров А.И., Филимонов П.В. Опубл. 13.11.2017. Бюл. № 32.
7. Пат. USA 1000005421870. Surface electromyography system, recorder and method / Koninklijke Philips N.V. Опубл. 15.12.2016
8. Стивен В. Смит. The Scientist and Engineer’s Guide to Digital Signal Processing. — Second Edition. — Сан-Диего: California Technical Publishing, 1999. — ISBN 0-9660176-4-1.
9. Б.Х. Кривицкий. Справочник по теоретическим основам радиоэлектроники. — М.: Энергия, 1977. – 420 c.
10. Веб-Ресурс Nikolak / PyQt: Getting started with PyQt and Qt Designer / URL: https://nikolak.com/pyqt-qt-designer-getting-started/ (дата обращения 25.03.2021)
Приложение а код разработанной программы
import sys # sys нужен для передачи argv в QApplication import matplotlib from PyQt5.QtWidgets import QMessageBox import numpy as np from numpy import genfromtxt from scipy.fft import rfft, rfftfreq from scipy.signal import butter, lfilter matplotlib.use('Qt5Agg') from PyQt5 import QtWidgets from PyQt5.QtWidgets import * from PyQt5.uic import loadUi class ExampleApp(QMainWindow): # Инициализация графического окна def __init__(self): QMainWindow.__init__(self) loadUi("design.ui", self) self.plotBtn.clicked.connect(self.update_graph) self.FileBtn.clicked.connect(self.open_sheet) self.filterCB.addItem('Нет') self.filterCB.addItem('Да') def update_graph(self): # Кнопка обновления интерфейса def butter_bandpass(lowcut, highcut, fs, order): # Полосовой фильтр Баттерворта nyq = 0.5 * fs low = lowcut / nyq high = highcut / nyq b, a = butter(order, [low, high], btype='band') return b, a if self.fsLineEdit.text().isdigit() and float(self.fsLineEdit.text()) > 0: # Если в fs корректное значение f = self.FileN data = genfromtxt(f, delimiter=',') N = len(data) fs = int(self.fsLineEdit.text()) T = 1.0 / fs tmax = T * N x = np.linspace(0.0, N * T, N) if self.radioTime.isChecked(): # Временная область self.MplWidget.canvas.axes.clear() self.MplWidget.canvas.axes.plot(x, data) self.MplWidget.canvas.axes.set_title('Исходный ЭКГ сигнал') self.MplWidget.canvas.axes.set_xlabel('t, c') self.MplWidget.canvas.axes.set_ylabel('U, В') self.MplWidget.canvas.draw() if self.filterCB.currentText() == 'Нет': self.MplWidget2.canvas.axes.clear() self.MplWidget2.canvas.axes.plot(x, data) self.MplWidget2.canvas.axes.set_title('Неотфильтрованный ЭКГ сигнал') self.MplWidget2.canvas.axes.set_xlabel('t, c') self.MplWidget2.canvas.axes.set_ylabel('U, В') self.MplWidget2.canvas.draw() elif self.filterCB.currentText() == 'Да': # Если заданы корректные параметры фильтрации if self.lowFrEdit.text().isdigit() and ( float(self.lowFrEdit.text()) > 0) and self.highFrEdit.text().isdigit() and ( float(self.highFrEdit.text()) > 0) and self.orderEdit.text().isdigit() and ( float(self.highFrEdit.text()) > float(self.lowFrEdit.text())) and ( float(self.orderEdit.text()) > 0) and (float(self.highFrEdit.text()) < fs / 2): lowFr = float(self.lowFrEdit.text()) hiFr = float(self.highFrEdit.text()) order = float(self.orderEdit.text()) b, a = butter_bandpass(lowFr, hiFr, fs, order=order) fy = lfilter(b, a, data) self.MplWidget2.canvas.axes.clear() self.MplWidget2.canvas.axes.plot(x, fy) self.MplWidget2.canvas.axes.set_title('Отфильтрованный ЭКГ сигнал') self.MplWidget2.canvas.axes.set_xlabel('t, c') self.MplWidget2.canvas.axes.set_ylabel('U, В') self.MplWidget2.canvas.draw() else: msg = QMessageBox() msg.setIcon(QMessageBox.Critical) msg.setText("Ошибка") msg.setInformativeText( 'Введите корректные значения частот (положительные числа, верхняя частота больше нижней, но меньше частоты Найквиста (1/2 fs)) и порядок фильтра (больше нуля)') msg.setWindowTitle("Ошибка") msg.exec_() if self.radioSpectrum.isChecked(): if self.filterCB.currentText() == 'Да': if self.lowFrEdit.text().isdigit() and ( float(self.lowFrEdit.text()) > 0) and self.highFrEdit.text().isdigit() and ( float(self.highFrEdit.text()) > 0) and self.orderEdit.text().isdigit() and ( float(self.highFrEdit.text()) > float(self.lowFrEdit.text())) and ( float(self.orderEdit.text()) > 0) and (float(self.highFrEdit.text()) < fs / 2): lowFr = float(self.lowFrEdit.text()) hiFr = float(self.highFrEdit.text()) order = float(self.orderEdit.text()) b, a = butter_bandpass(lowFr, hiFr, fs, order=order) fy = lfilter(b, a, data) self.MplWidget.canvas.axes.clear() self.MplWidget.canvas.axes.plot(x, fy) self.MplWidget.canvas.axes.set_title('Отфильтрованный ЭКГ сигнал') self.MplWidget.canvas.axes.set_xlabel('t, c') self.MplWidget.canvas.axes.set_ylabel('U, В') self.MplWidget.canvas.draw() yf = rfft(fy) xf = rfftfreq(len(fy), 1 / fs) self.MplWidget2.canvas.axes.clear() self.MplWidget2.canvas.axes.plot(xf, np.abs(yf)) self.MplWidget2.canvas.axes.set_title('Спектр отфильтрованного ЭКГ сигнала') self.MplWidget2.canvas.axes.set_xlabel('f, Гц') self.MplWidget2.canvas.axes.set_ylabel('|U|^2, В^2/Гц') self.MplWidget2.canvas.draw() else: msg = QMessageBox() msg.setIcon(QMessageBox.Critical) msg.setText("Ошибка") msg.setInformativeText( 'Введите корректные значения частот (положительные числа, верхняя частота больше нижней, но меньше частоты Найквиста (1/2 fs)) и порядок фильтра (больше нуля)') msg.setWindowTitle("Ошибка") msg.exec_() elif self.filterCB.currentText() == 'Нет': self.MplWidget.canvas.axes.clear() self.MplWidget.canvas.axes.plot(x, data) self.MplWidget.canvas.axes.set_title('Исходный ЭКГ сигнал') self.MplWidget.canvas.axes.set_xlabel('t, c') self.MplWidget.canvas.axes.set_ylabel('U, В') self.MplWidget.canvas.draw() yf = rfft(data) xf = rfftfreq(len(data), 1 / fs) self.MplWidget2.canvas.axes.clear() self.MplWidget2.canvas.axes.plot(xf, np.abs(yf)) self.MplWidget2.canvas.axes.set_title('Спектр исходного ЭКГ сигнала') self.MplWidget2.canvas.axes.set_xlabel('f, Гц') self.MplWidget2.canvas.axes.set_ylabel('|U|^2, В^2/Гц') self.MplWidget2.canvas.draw() if self.radioACF.isChecked(): if self.filterCB.currentText() == 'Да': if self.lowFrEdit.text().isdigit() and ( float(self.lowFrEdit.text()) > 0) and self.highFrEdit.text().isdigit() and ( float(self.highFrEdit.text()) > 0) and self.orderEdit.text().isdigit() and ( float(self.highFrEdit.text()) > float(self.lowFrEdit.text())) and ( float(self.orderEdit.text()) > 0) and (float(self.highFrEdit.text()) < fs / 2): lowFr = float(self.lowFrEdit.text()) hiFr = float(self.highFrEdit.text()) order = float(self.orderEdit.text()) b, a = butter_bandpass(lowFr, hiFr, fs, order=order) fy = lfilter(b, a, data) self.MplWidget.canvas.axes.clear() self.MplWidget.canvas.axes.plot(x, fy) self.MplWidget.canvas.axes.set_title('Отфильтрованный ЭКГ сигнал') self.MplWidget.canvas.axes.set_xlabel('t, c') self.MplWidget.canvas.axes.set_ylabel('U, В') self.MplWidget.canvas.draw() corr_data = np.correlate(fy, fy, "full") tcf = np.arange(-tmax + T, tmax, T) self.MplWidget2.canvas.axes.clear() self.MplWidget2.canvas.axes.plot(tcf, corr_data) self.MplWidget2.canvas.axes.set_title('АКФ отфильтрованного ЭКГ сигнала') self.MplWidget2.canvas.axes.set_xlabel('Сдвиг t, c') self.MplWidget2.canvas.axes.set_ylabel('Значение') self.MplWidget2.canvas.draw() else: msg = QMessageBox() msg.setIcon(QMessageBox.Critical) msg.setText("Ошибка") msg.setInformativeText( 'Введите корректные значения частот (положительные числа, верхняя частота больше нижней, но меньше частоты Найквиста (1/2 fs)) и порядок фильтра (больше нуля)') msg.setWindowTitle("Ошибка") msg.exec_() elif self.filterCB.currentText() == 'Нет': self.MplWidget.canvas.axes.clear() self.MplWidget.canvas.axes.plot(x, data) self.MplWidget.canvas.axes.set_title('Исходный ЭКГ сигнал') self.MplWidget.canvas.axes.set_xlabel('t, c') self.MplWidget.canvas.axes.set_ylabel('U, В') self.MplWidget.canvas.draw() corr_data = np.correlate(data, data, "full") tcf = np.arange(-tmax + T, tmax, T) self.MplWidget2.canvas.axes.clear() self.MplWidget2.canvas.axes.plot(tcf, corr_data) self.MplWidget2.canvas.axes.set_title('АКФ исходного ЭКГ сигнала') self.MplWidget2.canvas.axes.set_xlabel('Сдвиг t, c') self.MplWidget2.canvas.axes.set_ylabel('Значение') self.MplWidget2.canvas.draw() else: msg = QMessageBox() msg.setIcon(QMessageBox.Critical) msg.setText("Ошибка") msg.setInformativeText('Введите корректную частоту дискретизации (целочисленное положительное значение)') msg.setWindowTitle("Ошибка") msg.exec_() def open_sheet(self): # Выбор и загрузка файла сигнала path = QFileDialog.getOpenFileName(self, "Open", "", "txt Files (*.txt);;All Files (*)") if path[0] != '': self.FileN = path[0] self.plotBtn.setEnabled(True) self.MplWidget.canvas.axes.clear() self.MplWidget2.canvas.axes.clear() self.MplWidget.canvas.draw() self.MplWidget2.canvas.draw() def main(): app = QtWidgets.QApplication(sys.argv) # Новый экземпляр QApplication window = ExampleApp() # Создаём объект класса ExampleApp window.show() # Показываем окно app.exec_() # и запускаем приложение if __name__ == '__main__': # Если мы запускаем файл напрямую, а не импортируем main() # то запускаем функцию main()