💡 系統運作架構圖
Camera
→
OpenCV Lane Detection
YOLO Detection
→
PID Controller
→
Motor Control
Robot Car
Web UI (Control Panel)
↔
Flask Backend
📷 影像處理管道:OpenCV 追線原理
原始影像
BGR
→
HSV 轉換
色彩穩定
→
顏色遮罩
過濾膠帶色
→
ROI 限制
只看路面
→
形態學處理
去噪點
→
輪廓偵測
找形狀
→
車道分析
單/雙線
→
計算 Error
中心偏移
關鍵步驟說明:
- HSV 轉換:比起 BGR,HSV 受光照影響較小,利於鎖定特定顏色。
- ROI (Region of Interest):裁切掉天空與背景,避免背景雜訊干擾追線。
- 形態學 (Morphology):使用 Erode/Dilate 填補遮罩中的破洞或去除細小亮點。
# OpenCV 核心片段
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
mask = cv2.inRange(hsv, lower_yellow, upper_yellow)
roi = mask[height//2:height, 0:width] # 只看下半部
contours, _ = cv2.findContours(roi, cv2.RETR_TREE,
cv2.CHAIN_APPROX_SIMPLE)
if contours:
c = max(contours, key=cv2.contourArea)
M = cv2.moments(c)
cx = int(M['m10']/M['m00'])
error = cx - width/2
⚙️ PID 控制邏輯:如何修正路徑?
Lane Error
(偏差值)
(偏差值)
→
PID Controller
(比例/積分/微分)
(比例/積分/微分)
→
Correction
(修正量)
(修正量)
→
Left Motor Speed
Right Motor Speed
→
Robot Moves
(差速轉向)
(差速轉向)
// 差速控制公式
correction = pid.compute(error) left_speed = base_speed - correction right_speed = base_speed + correction pwm.set(left_speed, right_speed)
運作原理說明:
- ● 偏差獲取:從 OpenCV 得到的中心點與畫面中心相減。
- ● PID 計算:透過 Kp, Ki, Kd 轉化為修正強度。
- ● 差速轉向:修正量加減馬達轉速,實現流暢轉向。
01 參數改了卻沒生效?
原因分析:
前端使用了狀態輪詢 (Polling),導致在編輯輸入時,後端傳回的舊狀態覆蓋了正在修改的表單內容。
優化方案:
- 設置
isEditing旗標鎖定 - 編輯期間暫停 UI 更新
- 按下「套用」後才恢復同步
程式邏輯對照 (JavaScript)
// 鎖定機制範例
let isEditing = false;
function updateStatus() {
if (isEditing) return;
fetch('/status').then(res => res.json()).then(data => {
document.getElementById('kp').value = data.kp;
});
}
document.getElementById('kp').onfocus = () => isEditing = true;
document.getElementById('kp').onblur = () => isEditing = false;
02 Kp / 速度不能輸入小數點
原因分析:
後端 API 接收資料時,使用了 int() 進行解析,或是 HTML 的 step 屬性未正確設置。
優化方案:
- 後端解析改用
float() - HTML Input 設置
step="0.01"
程式邏輯對照 (Flask Python)
@app.route('/set_param')
def set_param():
try:
# 使用 float() 解析
kp = float(request.args.get('kp', 0.0))
speed = float(request.args.get('speed', 0.0))
return "OK"
except ValueError:
return "Invalid format", 400
03 手動按鈕按了沒反應?
原因分析:
瀏覽器或裝置(手機觸控)對事件支援差異,導致回呼未觸發。
優化方案:
- 使用 HTML Inline 事件備援
- 註冊為全域 Window 函式
互動事件對照 (HTML)
<button onmousedown="carMove('forward')"
onmouseup="carStop()"
ontouchstart="carMove('forward')"
ontouchend="carStop()">
前進
</button>
04 API 回 200,但車子不動
重點觀念:
HTTP 200 僅代表伺服器有收到請求,不保證硬體執行成功。
硬體檢測清單:
- 🔍 I2C:
i2cdetect -y 1檢查 0x40 是否在線 - 🔍 PCA9685: 檢查外部供電是否正常
- 🔍 PWM: 設定值是否太低推不動馬達
硬體層邏輯 (Python)
MIN_PWM = 8000
def move_motor(val):
if val < MIN_PWM:
print("警告: PWM 太低,馬達推不動")
# 設定 PCA9685...
05 自動駕駛過彎就停住?
原因分析:
過彎角度大於鏡頭視野時導致丟線,舊程式因「沒線就停」陷入死鎖。
邏輯優化:
- Lane Memory: 丟線時沿用上一次轉向值
- Slow Exploration: 找不到線時緩慢前行搜尋
進階邏輯 (Pseudo)
if cx is None:
# 丟線時優化: 減弱轉向但不停車
steering = last_error * 0.8
return steering, LOW_SPEED
真實場域與理論的差異
💡 光線與反光
環境反光會讓 HSV 閾值失效,建議導入動態亮度補償或邊緣偵測。
📐 透視變形
鏡頭夾角導致近大遠小,需做俯視轉換 (Bird's Eye View) 才能準確測距。
🏎️ 慣性影響
硬體反應有延遲,高速行駛需計算「預判距離」提前發送修正命令。
結語:工程師的修煉
「數據是完美的,但物理世界是有雜訊的。好的系統不是永遠不出錯,而是出錯後能優雅地修正。」