Python 自動化
EP.01

OpenCV + 機械手臂
電腦視覺自動定位系統實戰

如何用 Python + OpenCV 讓機械手臂「看到」目標並自動移動到正確位置,
從影像辨識到 XY 軸座標轉換的完整技術拆解

Joseph Chen

2025
10 min read
工廠實戰

在鴻海深圳廠,有一個需求:讓機械手臂自動找到 iPhone 主板上的某個特定點位並精準觸碰。 人工操作太慢、容易出錯,而且品質不穩定。

解法是:視覺引導(Vision-guided)系統。 用攝影機拍下目標,用 OpenCV 算出目標的像素座標,再轉換成機械手臂的實際 XY 軸座標,自動移動過去。

這篇文章把這個系統的核心技術拆解清楚。

系統架構概覽

整個系統分三個主要模組:

01
📷

影像擷取

USB 攝影機或工業相機擷取即時影像,固定在機台上方,與機械手臂的座標系統對齊校準。

02
🔍

OpenCV 影像處理

對影像做預處理(灰階、模糊、邊緣偵測),再用輪廓偵測或模板比對找到目標的像素座標(cx, cy)。

03
🤖

座標轉換 + 機械手臂控制

將像素座標轉換成機械手臂的實際毫米座標,透過 Serial 或 TCP 發送移動指令到控制器。

Step 1:用 OpenCV 找到目標位置

最常用的兩種方法:輪廓偵測(找形狀)和模板比對(找特定圖案)。 以下以輪廓偵測為例,找到影像中最大的圓形目標:

find_target.py
import cv2
import numpy as np

def find_circle_target(frame):
    """
    在影像中找到圓形目標,返回中心座標 (cx, cy)。
    """
    # 1. 灰階化
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

    # 2. 高斯模糊(降噪)
    blurred = cv2.GaussianBlur(gray, (11, 11), 0)

    # 3. Canny 邊緣偵測
    edges = cv2.Canny(blurred, 50, 150)

    # 4. 找輪廓
    contours, _ = cv2.findContours(
        edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE
    )

    if not contours:
        return None

    # 5. 找最大輪廓(假設目標是最顯眼的物體)
    largest = max(contours, key=cv2.contourArea)

    # 6. 計算最小外接圓
    (cx, cy), radius = cv2.minEnclosingCircle(largest)

    # 過濾太小的目標(可能是雜訊)
    if radius < 10:
        return None

    return int(cx), int(cy), int(radius)

# 測試
cap = cv2.VideoCapture(0)
while True:
    ret, frame = cap.read()
    result = find_circle_target(frame)
    if result:
        cx, cy, r = result
        cv2.circle(frame, (cx, cy), r, (0, 255, 0), 2)
        cv2.circle(frame, (cx, cy), 3, (0, 0, 255), -1)
        print(f"Target found at pixel: ({cx}, {cy})")
    cv2.imshow("Vision", frame)
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break
💡實際踩坑:工廠光線複雜,直接用 Canny 很容易誤偵測。 建議加上 HSV 色彩過濾,先把目標的顏色範圍濾出來再做輪廓偵測,準確率從 60% 提升到 95%。

Step 2:像素座標 → 機械手臂座標

影像的像素座標和機械手臂的實際毫米座標是不同的座標系,需要做仿射變換(Affine Transform)校準。

校準原理

  1. 1.讓機械手臂移到 3–4 個已知點位(例如四個角)
  2. 2.記錄每個點的機械手臂座標(mm)和對應的像素座標(px)
  3. 3.用 cv2.getAffineTransform 計算變換矩陣
  4. 4.之後任意像素座標都可以轉換成機械手臂座標
calibration.py
import numpy as np
import cv2

# 校準點:[機械手臂座標(mm), 對應像素座標(px)]
# 需要手動校準一次,之後存成設定檔
CALIBRATION_POINTS = [
    # (robot_x, robot_y), (pixel_x, pixel_y)
    ((100, 100), (120, 95)),
    ((200, 100), (240, 95)),
    ((100, 200), (120, 200)),
]

def build_transform_matrix(calibration_points):
    """
    從校準點計算仿射變換矩陣。
    需要至少 3 個不共線的對應點。
    """
    robot_pts = np.float32([p[0] for p in calibration_points[:3]])
    pixel_pts = np.float32([p[1] for p in calibration_points[:3]])

    # 從像素座標到機械手臂座標的變換矩陣
    M = cv2.getAffineTransform(pixel_pts, robot_pts)
    return M

def pixel_to_robot(pixel_x, pixel_y, M):
    """
    將像素座標轉換為機械手臂座標(mm)。
    """
    pt = np.array([[[float(pixel_x), float(pixel_y)]]], dtype=np.float32)
    robot_pt = cv2.transform(pt, M)
    robot_x = robot_pt[0][0][0]
    robot_y = robot_pt[0][0][1]
    return robot_x, robot_y

# 使用方式
M = build_transform_matrix(CALIBRATION_POINTS)
target_pixel = (180, 150)
robot_x, robot_y = pixel_to_robot(*target_pixel, M)
print(f"Robot should move to: ({robot_x:.2f}mm, {robot_y:.2f}mm)")

Step 3:控制機械手臂移動

算出目標座標後,透過序列埠(Serial)或 TCP Socket 發送指令到機械手臂控制器。 不同廠牌的指令格式不同,這裡以通用的 ASCII 指令格式示範:

robot_control.py
import serial
import time

class RobotArm:
    def __init__(self, port='/dev/ttyUSB0', baudrate=115200):
        self.ser = serial.Serial(port, baudrate, timeout=1)
        time.sleep(2)  # 等待連線穩定

    def send_command(self, cmd: str):
        """發送指令並等待回應"""
        self.ser.write((cmd + '\n').encode())
        response = self.ser.readline().decode().strip()
        return response

    def move_to(self, x: float, y: float, z: float = 50.0, speed: int = 50):
        """
        移動到指定 XYZ 座標(mm)。
        z 預設 50mm 安全高度,避免撞到工件。
        """
        # 先提高 Z 軸(安全移動)
        self.send_command(f"MOVZ {z:.2f} {speed}")
        time.sleep(0.5)
        # 移動 XY
        self.send_command(f"MOVXY {x:.2f} {y:.2f} {speed}")
        time.sleep(1.0)
        return True

    def close(self):
        self.ser.close()

# 完整整合:找目標 → 轉座標 → 移動
def auto_targeting(camera_index=0):
    cap = cv2.VideoCapture(camera_index)
    robot = RobotArm('/dev/ttyUSB0')
    M = build_transform_matrix(CALIBRATION_POINTS)

    ret, frame = cap.read()
    result = find_circle_target(frame)

    if result:
        cx, cy, _ = result
        robot_x, robot_y = pixel_to_robot(cx, cy, M)
        print(f"Target at pixel ({cx}, {cy}) → robot ({robot_x:.1f}, {robot_y:.1f})")
        robot.move_to(robot_x, robot_y)
        print("✅ Movement complete")
    else:
        print("❌ Target not found")

    cap.release()
    robot.close()
⚠️安全注意事項:在生產環境操控機械手臂,一定要設定速度限制和行程限制(Software Limit)。 建議所有自動移動前先手動確認一次,加入緊急停止按鈕,並在低速模式下先跑一輪驗證座標正確。

實際成果與優化

🎯

精度

❌ 之前:±2mm(手動)

✅ 之後:±0.3mm(自動)

速度

❌ 之前:30 sec/次

✅ 之後:5 sec/次

成功率

❌ 之前:約 85%

✅ 之後:約 97%

主要優化點:

  • 1加入 HSV 色彩過濾:排除工廠環境的光線干擾,只保留目標顏色範圍
  • 2多幀平均:連拍 5 幀,取目標位置的平均值,降低單幀誤差
  • 3增量移動:不直接跳到目標,用小步驟移動 + 重新偵測,類似閉迴路控制
  • 4校準檔案:每次開機載入上次的校準矩陣,不需要每次重新校準

這篇學到什麼

📷視覺引導系統的核心三步驟:影像擷取 → OpenCV 偵測目標像素座標 → 仿射變換轉成機械手臂座標
🔍OpenCV 工廠場景建議:用 HSV 色彩過濾再做輪廓偵測,比直接用 Canny 更穩定
📐仿射變換校準只需要 3 個對應點,之後任意位置都能自動轉換,是整個系統最重要的一步
🔒生產環境一定要加速度限制 + 行程限制 + 緊急停止,安全永遠優先於效率
Python 自動化
OpenCV
電腦視覺
機械手臂
工廠自動化
仿射變換
EP.01