學生專題開發完全攻略:從構想到成品的實戰經驗
每年協助數十組學生完成專題,我們看過太多成功的喜悅,也見證不少走過的彎路。本文整理了最實用的專題開發經驗,希望能幫助你順利完成一個令人驚艷的作品。
第一階段:選題與規劃 (1-2 週)
好專題的三大特質
1. 實用性 > 創新性
很多同學追求「前所未有」的創新,但評審更看重「真的有人會用」。
❌ 不好的選題:
「基於區塊鏈的智慧寵物餵食器」
→ 區塊鏈在這裡沒有實際價值
✅ 好的選題:
「獨居老人生活監測系統」
→ 解決真實社會問題,有明確受眾
2. 可完成性 > 複雜度
別在大四上還想做「完整的自駕車系統」,要能在學期內完成並展示。
評估專題難度:
def estimate_project_difficulty(project_idea):
"""
專題難度評估工具
"""
factors = {
'hardware_complexity': 0, # 硬體複雜度 (1-10)
'software_complexity': 0, # 軟體複雜度 (1-10)
'integration_effort': 0, # 整合難度 (1-10)
'debugging_difficulty': 0, # 調試難度 (1-10)
'team_experience': 0, # 團隊經驗 (1-10, 分數越高越有經驗)
}
# 計算總難度分數
difficulty_score = (
factors['hardware_complexity'] * 0.3 +
factors['software_complexity'] * 0.25 +
factors['integration_effort'] * 0.25 +
factors['debugging_difficulty'] * 0.2
) / factors['team_experience']
if difficulty_score < 0.5:
return "簡單 - 可在 2-3 個月完成"
elif difficulty_score < 1.0:
return "適中 - 需要 4-5 個月"
elif difficulty_score < 1.5:
return "困難 - 需要 6 個月以上"
else:
return "⚠️ 過於困難 - 建議縮小範圍"
# 範例
smart_greenhouse = {
'hardware_complexity': 5, # ESP32 + 感測器
'software_complexity': 6, # 資料處理 + Web
'integration_effort': 5, # MQTT + 資料庫
'debugging_difficulty': 6, # 環境變數多
'team_experience': 6, # 有基礎經驗
}
print(estimate_project_difficulty(smart_greenhouse))
# 輸出: "適中 - 需要 4-5 個月"
3. 展示性 > 技術深度
展示當天只有 5-10 分鐘,要讓評審「看得到」成果。
優先順序:
1. 能動的實體 Demo > PPT 報告
2. 即時數據顯示 > 歷史紀錄
3. 互動式介面 > 純文字輸出
經典專題主題推薦
入門級 (2-3 個月)
1. 智慧植栽監測系統
硬體需求:
- Arduino Uno / ESP8266
- 土壤濕度感測器
- DHT22 溫濕度感測器
- 繼電器模組(控制水泵)
- OLED 顯示器
核心功能:
✓ 即時監測土壤濕度、溫度
✓ 自動澆水(濕度低於閾值)
✓ LINE Bot 推播通知
✓ 歷史數據記錄與圖表
技術亮點:
- IoT 雲端整合
- 自動化控制
- 行動裝置推播
完整程式碼架構:
// smart_plant_monitor.ino
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <DHT.h>
#define DHTPIN D4
#define SOIL_MOISTURE_PIN A0
#define PUMP_RELAY_PIN D1
#define MOISTURE_THRESHOLD 30
DHT dht(DHTPIN, DHT22);
WiFiClient espClient;
PubSubClient mqtt(espClient);
struct SensorData {
float temperature;
float humidity;
int soilMoisture;
unsigned long timestamp;
};
void setup() {
Serial.begin(115200);
pinMode(PUMP_RELAY_PIN, OUTPUT);
digitalWrite(PUMP_RELAY_PIN, LOW);
dht.begin();
setupWiFi();
setupMQTT();
}
void loop() {
if (!mqtt.connected()) {
reconnectMQTT();
}
mqtt.loop();
// 每 30 秒讀取一次感測器
static unsigned long lastRead = 0;
if (millis() - lastRead > 30000) {
SensorData data = readSensors();
publishData(data);
checkAutoWatering(data);
lastRead = millis();
}
}
SensorData readSensors() {
SensorData data;
data.temperature = dht.readTemperature();
data.humidity = dht.readHumidity();
data.soilMoisture = map(analogRead(SOIL_MOISTURE_PIN), 0, 1023, 0, 100);
data.timestamp = millis();
Serial.printf("溫度: %.1f°C, 濕度: %.1f%%, 土壤濕度: %d%%\n",
data.temperature, data.humidity, data.soilMoisture);
return data;
}
void checkAutoWatering(SensorData data) {
if (data.soilMoisture < MOISTURE_THRESHOLD) {
Serial.println("⚠️ 土壤過乾,啟動澆水!");
// 開啟水泵 5 秒
digitalWrite(PUMP_RELAY_PIN, HIGH);
delay(5000);
digitalWrite(PUMP_RELAY_PIN, LOW);
// 發送 LINE 通知
sendLineNotification("植物已自動澆水");
}
}
void publishData(SensorData data) {
char payload[200];
sprintf(payload,
"{\"temperature\":%.1f,\"humidity\":%.1f,\"soil\":%d,\"time\":%lu}",
data.temperature, data.humidity, data.soilMoisture, data.timestamp);
mqtt.publish("plant/sensors", payload);
}
2. 智慧門禁系統
硬體需求:
- ESP32-CAM
- RFID-RC522 模組
- 伺服馬達(門鎖)
- 蜂鳴器
核心功能:
✓ RFID 卡片識別
✓ 人臉辨識(OpenCV)
✓ 門禁記錄上傳雲端
✓ 異常入侵警報
技術亮點:
- 多重身份驗證
- 影像辨識 AI
- 安全性設計
進階級 (4-5 個月)
3. 工廠設備監測與預測維護系統
系統架構:
┌─────────────────────────────────────────┐
│ 工廠設備監測系統 │
├─────────────────────────────────────────┤
│ │
│ ┌──────────┐ ┌──────────┐ │
│ │ 震動感測器 │ │ 溫度感測器 │ │
│ └─────┬────┘ └─────┬────┘ │
│ │ │ │
│ └────────┬───────┘ │
│ ↓ │
│ ┌──────────────┐ │
│ │ ESP32 │ │
│ │ (Edge AI) │ │
│ └──────┬───────┘ │
│ │ MQTT │
│ ↓ │
│ ┌──────────────────────┐ │
│ │ Cloud Server │ │
│ │ - 資料儲存 │ │
│ │ - AI 預測模型 │ │
│ │ - Dashboard │ │
│ └──────────────────────┘ │
└─────────────────────────────────────────┘
核心功能:
✓ 多感測器即時監測
✓ 異常模式識別(機器學習)
✓ 故障預測(時序預測模型)
✓ Web Dashboard 視覺化
✓ 警報推播(Email/LINE)
技術難點:
- 感測器資料融合
- 邊緣運算實作
- 機器學習模型訓練
- 時序資料處理
機器學習異常檢測實作:
# anomaly_detection.py
import numpy as np
from sklearn.ensemble import IsolationForest
from sklearn.preprocessing import StandardScaler
import joblib
class VibrationAnomalyDetector:
def __init__(self):
self.model = IsolationForest(
contamination=0.1, # 預期異常比例 10%
random_state=42
)
self.scaler = StandardScaler()
self.is_trained = False
def train(self, normal_data):
"""
使用正常運轉數據訓練模型
normal_data: shape (n_samples, n_features)
features = [振動頻率, 振幅, 溫度, 轉速]
"""
# 標準化
X_scaled = self.scaler.fit_transform(normal_data)
# 訓練模型
self.model.fit(X_scaled)
self.is_trained = True
print(f"✓ 模型訓練完成,使用 {len(normal_data)} 筆數據")
def predict(self, sensor_data):
"""
預測是否異常
Returns:
is_anomaly: bool
anomaly_score: float (越低越異常)
"""
if not self.is_trained:
raise ValueError("模型尚未訓練")
# 標準化
X_scaled = self.scaler.transform(sensor_data.reshape(1, -1))
# 預測
prediction = self.model.predict(X_scaled)[0]
score = self.model.score_samples(X_scaled)[0]
is_anomaly = (prediction == -1)
if is_anomaly:
print(f"⚠️ 異常檢測!分數: {score:.4f}")
return is_anomaly, score
def save_model(self, filepath):
"""儲存訓練好的模型"""
joblib.dump({
'model': self.model,
'scaler': self.scaler
}, filepath)
print(f"✓ 模型已儲存至 {filepath}")
@classmethod
def load_model(cls, filepath):
"""載入訓練好的模型"""
data = joblib.load(filepath)
detector = cls()
detector.model = data['model']
detector.scaler = data['scaler']
detector.is_trained = True
return detector
# 使用範例
if __name__ == "__main__":
# 1. 收集正常運轉數據(模擬)
normal_data = np.random.randn(1000, 4) * [50, 2, 60, 1500] + [200, 5, 40, 3000]
# 2. 訓練模型
detector = VibrationAnomalyDetector()
detector.train(normal_data)
# 3. 即時監測
while True:
# 讀取感測器數據
sensor_reading = np.array([210, 5.2, 42, 3100]) # [頻率, 振幅, 溫度, 轉速]
is_anomaly, score = detector.predict(sensor_reading)
if is_anomaly:
send_alert_notification(sensor_reading, score)
4. 智慧停車場管理系統
完整功能:
✓ 超音波感測器偵測車位
✓ LED 燈號指示(紅/綠)
✓ LCD 顯示剩餘車位
✓ 手機 APP 即時查詢
✓ 車牌辨識(進階)
✓ 自動計費系統
AI/ML 專題 (5-6 個月)
5. 垃圾分類智能辨識系統
# trash_classifier.py
import tensorflow as tf
from tensorflow.keras.applications import MobileNetV2
from tensorflow.keras.layers import Dense, GlobalAveragePooling2D, Dropout
from tensorflow.keras.models import Model
from tensorflow.keras.preprocessing.image import ImageDataGenerator
class TrashClassifier:
def __init__(self, num_classes=6):
"""
垃圾分類模型
分類類別:
0: 一般垃圾
1: 資源回收(紙類)
2: 資源回收(塑膠)
3: 資源回收(金屬)
4: 廚餘
5: 有害垃圾
"""
self.num_classes = num_classes
self.model = self._build_model()
self.class_names = [
'一般垃圾', '紙類', '塑膠', '金屬', '廚餘', '有害垃圾'
]
def _build_model(self):
# 使用 MobileNetV2 作為 backbone(適合嵌入式部署)
base_model = MobileNetV2(
input_shape=(224, 224, 3),
include_top=False,
weights='imagenet'
)
# 凍結 base model 的前 100 層
for layer in base_model.layers[:100]:
layer.trainable = False
# 添加分類層
x = base_model.output
x = GlobalAveragePooling2D()(x)
x = Dense(256, activation='relu')(x)
x = Dropout(0.5)(x)
x = Dense(128, activation='relu')(x)
x = Dropout(0.3)(x)
predictions = Dense(self.num_classes, activation='softmax')(x)
model = Model(inputs=base_model.input, outputs=predictions)
model.compile(
optimizer=tf.keras.optimizers.Adam(learning_rate=0.0001),
loss='categorical_crossentropy',
metrics=['accuracy']
)
return model
def train(self, train_dir, val_dir, epochs=30, batch_size=32):
"""訓練模型"""
# 資料增強
train_datagen = ImageDataGenerator(
rescale=1./255,
rotation_range=20,
width_shift_range=0.2,
height_shift_range=0.2,
shear_range=0.2,
zoom_range=0.2,
horizontal_flip=True,
fill_mode='nearest'
)
val_datagen = ImageDataGenerator(rescale=1./255)
train_generator = train_datagen.flow_from_directory(
train_dir,
target_size=(224, 224),
batch_size=batch_size,
class_mode='categorical'
)
val_generator = val_datagen.flow_from_directory(
val_dir,
target_size=(224, 224),
batch_size=batch_size,
class_mode='categorical'
)
# 訓練
history = self.model.fit(
train_generator,
epochs=epochs,
validation_data=val_generator,
callbacks=[
tf.keras.callbacks.EarlyStopping(patience=5, restore_best_weights=True),
tf.keras.callbacks.ReduceLROnPlateau(factor=0.5, patience=3)
]
)
return history
def convert_to_tflite(self, output_path='trash_model.tflite'):
"""
轉換為 TensorFlow Lite 格式
用於 ESP32-CAM 或 Raspberry Pi 部署
"""
converter = tf.lite.TFLiteConverter.from_keras_model(self.model)
# 量化以減少模型大小
converter.optimizations = [tf.lite.Optimize.DEFAULT]
tflite_model = converter.convert()
with open(output_path, 'wb') as f:
f.write(tflite_model)
print(f"✓ TFLite 模型已儲存: {output_path}")
print(f" 模型大小: {len(tflite_model) / 1024:.2f} KB")
ESP32-CAM 整合:
// trash_classifier_esp32cam.ino
#include "esp_camera.h"
#include "tensorflow/lite/micro/all_ops_resolver.h"
#include "tensorflow/lite/micro/micro_interpreter.h"
#include "tensorflow/lite/schema/schema_generated.h"
// 模型儲存
#include "trash_model.h" // 轉換後的 TFLite 模型
const char* classNames[] = {
"一般垃圾", "紙類", "塑膠", "金屬", "廚餘", "有害垃圾"
};
void setup() {
Serial.begin(115200);
// 初始化相機
camera_config_t config;
config.ledc_channel = LEDC_CHANNEL_0;
config.ledc_timer = LEDC_TIMER_0;
config.pin_d0 = Y2_GPIO_NUM;
// ... 其他相機設定
esp_err_t err = esp_camera_init(&config);
if (err != ESP_OK) {
Serial.printf("相機初始化失敗: 0x%x", err);
return;
}
// 初始化 TensorFlow Lite
setupTFLite();
}
void loop() {
// 拍照
camera_fb_t * fb = esp_camera_fb_get();
if (!fb) {
Serial.println("拍照失敗");
return;
}
// 影像預處理 (resize to 224x224, normalize)
uint8_t* processed_image = preprocessImage(fb->buf, fb->len);
// 執行推理
int predicted_class = runInference(processed_image);
// 顯示結果
Serial.printf("辨識結果: %s\n", classNames[predicted_class]);
// 釋放記憶體
esp_camera_fb_return(fb);
delay(2000); // 每 2 秒辨識一次
}
第二階段:硬體選型與採購 (1 週)
常用開發板比較
開發板 | 價格 | 優勢 | 適用場景 |
---|---|---|---|
Arduino Uno | NT$ 200 | 入門友善、資源多 | 簡單感測器控制 |
ESP8266 | NT$ 100 | WiFi 內建、便宜 | IoT 基礎應用 |
ESP32 | NT$ 150 | 雙核、BLE+WiFi | IoT 進階應用 |
Raspberry Pi 4 | NT$ 1,500 | 完整 Linux 系統 | AI/影像處理 |
STM32 | NT$ 300 | 工業級、高穩定 | 專業應用 |
Nordic nRF52 | NT$ 500 | 低功耗藍芽 | 穿戴裝置 |
預算規劃建議
總預算 NT$ 3,000 - 5,000
基礎配置:
- 開發板: NT$ 500
- 感測器模組: NT$ 800
- 電源/線材: NT$ 300
- PCB 製作: NT$ 500
- 外殼/結構: NT$ 600
- 備用零件: NT$ 300
合計: NT$ 3,000
進階配置(+AI/影像):
+ Raspberry Pi: NT$ 1,500
+ Camera Module: NT$ 500
合計: NT$ 5,000
採購清單範本
## 智慧植栽監測系統 - 採購清單
### 主要元件
- [ ] ESP8266 NodeMCU × 1 (NT$ 120)
- [ ] DHT22 溫濕度感測器 × 1 (NT$ 150)
- [ ] 土壤濕度感測器 × 1 (NT$ 50)
- [ ] OLED 顯示器 0.96吋 × 1 (NT$ 80)
- [ ] 5V 繼電器模組 × 1 (NT$ 40)
- [ ] 小型抽水泵 × 1 (NT$ 100)
### 電源與線材
- [ ] Micro USB 線 × 1 (NT$ 50)
- [ ] 5V 2A 電源供應器 × 1 (NT$ 100)
- [ ] 杜邦線 (公對公/母對母/公對母各 20 條) (NT$ 60)
- [ ] 麵包板 × 1 (NT$ 50)
### 其他
- [ ] 專題用植栽 × 1 (NT$ 50)
- [ ] 水管與接頭 (NT$ 50)
- [ ] 外殼(壓克力或 3D 列印)(NT$ 300)
總計: NT$ 1,200
第三階段:開發與整合 (8-12 週)
開發時程規劃
Week 1-2: 硬體測試
# 測試檢查清單
hardware_tests = {
'power': {
'description': '電源供應測試',
'tests': [
'電壓穩定性測試',
'電流消耗測量',
'電池續航估算'
]
},
'sensors': {
'description': '感測器功能測試',
'tests': [
'讀值準確性驗證',
'回應時間測試',
'環境干擾測試'
]
},
'actuators': {
'description': '致動器測試',
'tests': [
'馬達/繼電器控制',
'負載能力測試',
'連續運作穩定性'
]
},
'communication': {
'description': '通訊模組測試',
'tests': [
'WiFi 連線穩定性',
'MQTT 訊息傳輸',
'斷線重連機制'
]
}
}
Week 3-6: 核心功能開發
專注於最重要的 2-3 個核心功能,確保能完整運作。
Week 7-9: 系統整合
將各個模組整合成完整系統。
Week 10-11: 測試與優化
Week 12: 文件與簡報準備
常見問題解決
問題 1: 感測器讀值不穩定
// 解決方案:多次採樣取平均值
float readStableSensorValue(int pin, int samples = 10) {
float sum = 0;
for (int i = 0; i < samples; i++) {
sum += analogRead(pin);
delay(10); // 等待穩定
}
return sum / samples;
}
// 進階:加入中位數濾波
float readWithMedianFilter(int pin, int samples = 9) {
float readings[samples];
// 收集數據
for (int i = 0; i < samples; i++) {
readings[i] = analogRead(pin);
delay(10);
}
// 排序
std::sort(readings, readings + samples);
// 返回中位數
return readings[samples / 2];
}
問題 2: WiFi 連線不穩定
// WiFi 自動重連機制
void ensureWiFiConnected() {
static unsigned long lastCheck = 0;
// 每 30 秒檢查一次
if (millis() - lastCheck < 30000) {
return;
}
lastCheck = millis();
if (WiFi.status() != WL_CONNECTED) {
Serial.println("⚠️ WiFi 斷線,嘗試重連...");
WiFi.disconnect();
delay(1000);
WiFi.begin(ssid, password);
int timeout = 0;
while (WiFi.status() != WL_CONNECTED && timeout < 20) {
delay(500);
Serial.print(".");
timeout++;
}
if (WiFi.status() == WL_CONNECTED) {
Serial.println("\n✓ WiFi 重連成功");
} else {
Serial.println("\n✗ WiFi 重連失敗");
}
}
}
問題 3: 記憶體不足
// ESP8266/ESP32 記憶體優化技巧
// 1. 使用 F() 巨集將字串存在 Flash
Serial.println(F("這個字串不會佔用 RAM"));
// 2. 釋放不用的變數
void processData() {
String largeData = downloadData();
// 處理數據...
largeData = ""; // 釋放記憶體
}
// 3. 監控記憶體使用
void printMemoryUsage() {
Serial.printf("Free Heap: %d bytes\n", ESP.getFreeHeap());
Serial.printf("Heap Fragmentation: %d%%\n", ESP.getHeapFragmentation());
}
第四階段:展示準備 (1-2 週)
Demo 成功的秘訣
1. 準備 Plan B
主要 Demo: 連線雲端即時展示
備用方案 A: 本機離線展示
備用方案 B: 錄影影片播放
備用方案 C: 簡報說明 + 照片
2. 口頭報告架構
## 5 分鐘簡報結構
[30 秒] 問題陳述
- 這個專題要解決什麼問題?
- 為什麼重要?
[1 分鐘] 系統介紹
- 系統架構圖
- 核心技術
[2 分鐘] 實機展示
- 操作流程
- 功能演示
[1 分鐘] 技術亮點
- 使用的技術
- 克服的挑戰
[30 秒] 總結與未來展望
3. 常見評審問題準備
Q: 為什麼不用現成的產品?
A: (說明客製化需求、成本考量、學習目的)
Q: 如何確保系統穩定性?
A: (展示壓力測試結果、錯誤處理機制)
Q: 遇到最大的挑戰是什麼?
A: (技術難點 + 解決方案 + 學習收穫)
Q: 這個系統可以商業化嗎?
A: (市場分析、成本估算、改進方向)
額外加分技巧
1. 製作精美的外殼
材料選擇:
✓ 3D 列印(最推薦)
✓ 雷射切割壓克力
✓ 現成電子盒修改
✗ 紙盒(看起來不夠專業)
2. 建立專案網站
<!-- 簡易專題網站範本 -->
<!DOCTYPE html>
<html>
<head>
<title>智慧植栽監測系統</title>
<style>
body { font-family: Arial; max-width: 800px; margin: 0 auto; }
.hero { background: #4CAF50; color: white; padding: 50px; text-align: center; }
.feature { padding: 20px; border-bottom: 1px solid #ddd; }
img { max-width: 100%; height: auto; }
</style>
</head>
<body>
<div class="hero">
<h1>🌱 智慧植栽監測系統</h1>
<p>讓您的植物永遠保持最佳狀態</p>
</div>
<div class="feature">
<h2>專題簡介</h2>
<p>本系統能夠即時監測植物的土壤濕度、環境溫濕度...</p>
</div>
<div class="feature">
<h2>系統架構</h2>
<img src="architecture.png" alt="系統架構圖">
</div>
<div class="feature">
<h2>展示影片</h2>
<iframe width="560" height="315" src="demo.mp4"></iframe>
</div>
</body>
</html>
3. 撰寫技術部落格
分享開發過程到 Medium、HackMD,可以:
- 建立個人品牌
- 獲得社群回饋
- 找工作時的作品集
4. 參加競賽
推薦競賽:
- 教育部資訊應用服務創新競賽
- ITSA 盃程式設計競賽
- MakeNTU 硬體黑客松
- AWS DeepRacer 自駕車競賽
- 各校校內競賽
時間管理與團隊協作
使用 Git 進行版本控制
# 專題開發 Git 工作流程
# 1. 建立專案倉庫
git init smart-plant-monitor
cd smart-plant-monitor
# 2. 建立分支策略
git checkout -b develop # 開發分支
git checkout -b feature/sensor # 功能分支
git checkout -b feature/webapp
# 3. 每日工作流程
git add .
git commit -m "feat: 新增土壤濕度感測器驅動"
git push origin feature/sensor
# 4. 合併到主分支
git checkout develop
git merge feature/sensor
# 5. 發布版本
git tag -a v1.0 -m "第一次展示版本"
git push origin v1.0
專題管理工具
## 使用 Trello/Notion 管理進度
### To Do
- [ ] 完成感測器校準
- [ ] 實作 MQTT 連線
- [ ] 設計 Web 介面
### In Progress
- [ ] 整合 LINE Bot 通知
### Done
- [x] 完成硬體組裝
- [x] 測試基本功能
最後叮嚀
✅ 要做的事
- 及早開始 - 別拖到最後兩週才動工
- 頻繁測試 - 每加一個功能就測試一次
- 寫清楚註解 - 一個月後的你會感謝現在的自己
- 備份再備份 - Git + 雲端硬碟雙重保險
- 記錄過程 - 拍照、錄影、寫筆記
❌ 避免的錯誤
- 不要追求完美 - 能動 > 完美但做不完
- 不要重造輪子 - 善用現有函式庫
- 不要熬夜趕工 - 效率低且容易出錯
- 不要單打獨鬥 - 善用教授、學長姐資源
- 不要害怕失敗 - 錯誤是最好的老師
總結
成功的專題 = 明確的目標 + 合理的規劃 + 持續的執行
記住:評審看的不只是技術,更看重你解決問題的能力和學習態度。
在 BASHCAT,我們已經協助超過 100 組學生成功完成專題,從構想、硬體選型、程式開發到最終展示,提供全方位的技術指導。如果你的專題遇到困難,歡迎與我們聯繫!