擴充版 PyQt5 IMU 顯示器 的功能設計與實作規劃,支援以下三大功能:
✅ 擴充功能目標
功能項目 | 說明 |
---|---|
🎬 即時播放(滑動窗格) | 自動每 100ms 顯示下一個視窗(例如 1 秒=100 筆) |
🏷️ 顯示實際類別 (type ) | 每個時間窗顯示對應的原始標籤 |
🤖 疊加分類預測結果 | 使用 LSTM 模型做預測並與實際類別一同顯示 |
📦 完整 Python 程式碼
此版本含有:
-
滑動視窗控制 (
QTimer
) -
實際
type
與 LSTM 預測結果顯示 -
時域資料繪圖更新
pythonimport sys
import pandas as pd
import numpy as np
from PyQt5.QtWidgets import (
QApplication, QWidget, QVBoxLayout, QPushButton,
QFileDialog, QComboBox, QLabel
)
from PyQt5.QtCore import QTimer
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
import torch
import torch.nn as nn
# 模擬用的簡易 LSTM 分類模型(請換成你訓練好的模型)
class DummyLSTM(nn.Module):
def __init__(self, input_dim=6, hidden_dim=64, num_layers=1, num_classes=4):
super().__init__()
self.lstm = nn.LSTM(input_dim, hidden_dim, num_layers, batch_first=True)
self.fc = nn.Linear(hidden_dim, num_classes)
def forward(self, x):
out, _ = self.lstm(x)
out = self.fc(out[:, -1, :])
return out
class IMUGUI(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle('IMU 時域顯示器(即時播放+預測)')
self.resize(900, 700)
self.data = None
self.index = 0
self.fs = 100
self.seq_len = 100
self.timer = QTimer()
self.timer.timeout.connect(self.next_frame)
self.model = DummyLSTM().eval()
self.device = torch.device('cpu')
layout = QVBoxLayout()
self.load_button = QPushButton('載入 CSV')
self.play_button = QPushButton('▶️ 播放')
self.combo = QComboBox()
self.combo.addItems(['1 秒', '2 秒', '3 秒'])
self.label = QLabel('目前未載入檔案')
self.canvas = FigureCanvas(Figure())
self.ax = self.canvas.figure.add_subplot(111)
layout.addWidget(self.load_button)
layout.addWidget(self.play_button)
layout.addWidget(self.combo)
layout.addWidget(self.label)
layout.addWidget(self.canvas)
self.setLayout(layout)
self.load_button.clicked.connect(self.load_csv)
self.combo.currentIndexChanged.connect(self.set_seq_len)
self.play_button.clicked.connect(self.toggle_play)
def load_csv(self):
fname, _ = QFileDialog.getOpenFileName(self, '選擇 CSV 檔', '', 'CSV files (*.csv)')
if fname:
self.data = pd.read_csv(fname)
self.label.setText(f'已載入:{fname.split("/")[-1]}')
self.set_seq_len()
self.index = 0
self.update_plot()
def set_seq_len(self):
self.seq_len = self.fs * (self.combo.currentIndex() + 1)
def toggle_play(self):
if self.timer.isActive():
self.timer.stop()
self.play_button.setText("▶️ 播放")
else:
self.timer.start(100)
self.play_button.setText("⏸️ 暫停")
def next_frame(self):
if self.data is None:
return
self.index += 1
if self.index + self.seq_len > len(self.data):
self.timer.stop()
self.play_button.setText("▶️ 播放")
self.index = 0
self.update_plot()
def update_plot(self):
if self.data is None or self.index + self.seq_len > len(self.data):
return
segment = self.data.iloc[self.index:self.index + self.seq_len]
time = [i * 0.01 for i in range(self.seq_len)]
self.ax.clear()
colors = ['red', 'blue', 'green']
for i in range(3): # b1~b3
self.ax.plot(time, segment[f'b{i+1}'], color=colors[i], label=f'b{i+1}')
for i in range(3): # b4~b6
self.ax.plot(time, segment[f'b{i+4}'], linestyle='--', color=colors[i], label=f'b{i+4}')
# 顯示實際類別與預測類別
actual_label = segment['type'].iloc[-1]
imu_values = segment[[f'b{i+1}' for i in range(6)]].values
imu_tensor = torch.tensor(imu_values, dtype=torch.float32).unsqueeze(0)
with torch.no_grad():
pred = self.model(imu_tensor).argmax().item()
self.ax.set_title(f'第 {self.index} ~ {self.index+self.seq_len} 筆 | 實際: {actual_label} | 預測: {pred}')
self.ax.set_xlabel('時間 (秒)')
self.ax.set_ylabel('IMU 數值')
self.ax.legend()
self.canvas.draw()
if __name__ == '__main__':
app = QApplication(sys.argv)
gui = IMUGUI()
gui.show()
sys.exit(app.exec_())
🧪 進階功能你可以加入:
-
✅ 使用你訓練好的
LSTM
模型(替換DummyLSTM
) -
✅ 使用
LabelEncoder.inverse_transform()
顯示類別名稱 -
✅ 支援匯出預測結果、顯示混淆矩陣
沒有留言:
張貼留言