KiCad 插件教學 2026 | 過孔 Stitching Vias 一鍵搞定自動填滿鋪銅平面


接地過孔(Stitching Vias)是 PCB 佈局中常見的工作,尤其當你處理接地平面、RF 屏蔽與散熱管理時。手動放置幾十顆、甚至上百顆把鋪銅區域「縫合」在一起的小接地過孔,很快就會變得重複且容易出錯——然而接地參考穩定、EMI 受控、熱通路涼爽,全都依賴它們。KiCad 插件開發提供了一種自動化這項工作的方法:

在本教學中,我們要介紹 KiCad Via Stitcher,一個免費的開源 KiCad 10 插件,把重複性的過孔放置工作自動化。選一個鋪銅區域或一個封裝,KiCad Via Stitcher 會在選取內容的包含框內填出規則網格的接地過孔(Stitching Vias)。從此不再重複點擊、不再手動計算、不再漏掉任何一顆過孔。讓我們從零開始動手打造它!

Stitching Vias

什麼是接地過孔?

接地過孔(Stitching Vias),就是在大型銅區(尤其是接地鋪銅與接地平面)上,佈置一組規則排列的接地過孔,把頂層與底層銅箔在電氣上「縫合」在一起。它有三個至關重要的用途:

  • EMC 與 RF 屏蔽
    密集的接地過孔(Stitching Vias)能降低層與層之間的迴路電感,並形成有效屏蔽「圍欄」,包圍敏感電路,降低輻射發射與串音。
  • 散熱管理
    過孔能把高溫銅區(例如功率元件下方)的熱量,透過電路板導到另一層,達到良好的熱擴散效果。
  • 穩定的接地參考
    接地過孔(Stitching Vias)能維持接地平面的電位穩定,降低高速信號迴流路徑的阻抗,避免接地彈跳。

對 RF via 圍欄(RF via fence),常見的起步作法是依相關介質,把過孔間距控制在 λ/10(或 λ/20)以下。實務上,設計者往往會依據疊層、幾何形狀、回流路徑與屏蔽需求,使用更密的間距——在一大片接地鋪銅上放好幾百顆過孔是常有的事。

認識 Via Stitcher

Via Stitcher 是一個原生的 KiCad 10 ActionPlugin,用 Python 撰寫。它不像手寫指令碼,而是直接把自己註冊進 KiCad 的 Tools → External Tools 選單(也可選工具列),讓你在 PCB 編輯器就能一鍵觸發。

它的運作流程如下:

  1. 讀取開啟的板材:透過 pcbnew.GetBoard() 取得目前開啟的 PCB。若沒有開啟板材,就跳出明確警告。
  2. 收集你的選取:收集每一顆被選中的鋪銅區(board.Zones())與被選中的封裝(board.GetFootprints())。若什麼都沒選,就出示實用提示並停止。
  3. 提示參數:開啟原生的 wxPython 對話框,詢問四個數值——過孔間距、過孔直徑、鑽孔大小與目標線網名稱。
  4. 驗證輸入:每個尺寸必須為正數,且鑽孔必須小於過孔直徑,否則會以錯誤訊息拒絕。
  5. 找到目標線網:在板上搜尋該線網(例如 GND)。如果不存在,插件會自動建立
  6. 填入過孔:對每一選中區域,計算包含框,然後在 F.Cu / B.Cu 層對上填滿間距均勻的穿透孔。
  7. 回報結果:重新整理畫面並顯示摘要——總過孔數、間距、過孔尺寸與線網名稱。

就這些了——沒有雲端呼叫,不需要任何第三方 Python 套件;它只使用 KiCad 內建的 pcbnewwx 模組。整個插件就是一個獨立的 Python 檔,在規則網格中放置你的接地過孔。

四個參數說明

參數預設值作用
Via spacing(間距mm)2.54網路中相鄰過孔中心的距離。間距越小 = 縫合越密集(屏蔽更好、過孔多)。
Via diameter(過孔直徑mm)0.60每個過孔外圈銅徑。
Via drill(鑽孔mm)0.30每個過孔的圓孔直徑,必須小於過孔直徑。
Net name(線網名稱)GND所有放置過孔要接的線網,適合 GND、AGND、DGND 或任何自訂線網。

對大部分雙層板,好的起步配置是 2.54 mm 間距、0.6 mm 過孔(0.3 mm 鑽孔)、接 GND。若為 RF 重要或高電流區域,可把間距縮到 1.27 mm 左右。

事前準備工作

在動手寫任何 code 前,先把開發環境準備好。好消息是:Via Stitcher 不需要任何額外的 Python 套件——只要 KiCad 內建的 pcbnewwx 模組。

  • 安裝最新版 KiCad 10.0
    本篇基於 KiCad 10.0,它提供成熟,透過 SWIG 的 Python API 綁定,以及完整的 pcbnew.ActionPlugin 支援。
  • 準備好的程式碼編輯器
    推薦使用 VS Code 搭配 Python 擴充,提供語法高亮與自動補全,方便檢視原始碼。
  • 知道你的外掛目錄
    KiCad 會自動從特定作業系統的路徑載入 action plugin,確切路徑會在下方「安裝指南」說明。

插件程式碼

以下是完整原始碼(via_stitcher.py)。它把前述功能全部實作到位——安全的模組載入、原生 wxPython 參數驗證對話框、自動建立線網、網格過孔放置,以及最後的統計摘要。請注意:它是在選取內容的包含框內放置過孔,而非沿著銅區的實際形狀,所以執行後請記得跑一次 DRC:

import sys
import os
import traceback
import math

if 'pcbnew' in sys.modules:
    pcbnew = sys.modules['pcbnew']
else:
    import pcbnew

try:
    import wx
except ImportError:
    wx = None


class ViaStitcherPlugin(pcbnew.ActionPlugin):

    def defaults(self):
        self.name = "Via Stitcher"
        self.category = "Routing Tools"
        self.description = "Auto-fill copper areas with stitching vias"
        self.show_toolbar_button = True
        try:
            self.icon_file_name = os.path.join(os.path.dirname(__file__), 'icon.png')
        except NameError:
            self.icon_file_name = ""

    def show_msg(self, message, is_error=False):
        if not wx:
            print(message)
            return
        try:
            parent = wx.GetActiveWindow()
        except Exception:
            parent = None
        title = "Via Stitcher - Error" if is_error else "Via Stitcher"
        style = wx.OK | (wx.ICON_ERROR if is_error else wx.ICON_INFORMATION)
        try:
            wx.MessageBox(message, title, style, parent)
        except Exception:
            print(f"[{title}] {message}")

    def get_params(self):
        if not wx:
            return None
        parent = wx.GetActiveWindow()
        dlg = wx.Dialog(parent, title="Via Stitcher")
        panel = wx.Panel(dlg)
        sizer = wx.BoxSizer(wx.VERTICAL)

        grid = wx.FlexGridSizer(0, 2, 10, 10)
        grid.AddGrowableCol(1, 1)

        grid.Add(wx.StaticText(panel, label="Via spacing (mm):"), 0, wx.ALIGN_CENTER_VERTICAL)
        sp_ctrl = wx.TextCtrl(panel, value="2.54")
        grid.Add(sp_ctrl, 0, wx.EXPAND)

        grid.Add(wx.StaticText(panel, label="Via diameter (mm):"), 0, wx.ALIGN_CENTER_VERTICAL)
        dia_ctrl = wx.TextCtrl(panel, value="0.6")
        grid.Add(dia_ctrl, 0, wx.EXPAND)

        grid.Add(wx.StaticText(panel, label="Via drill (mm):"), 0, wx.ALIGN_CENTER_VERTICAL)
        dr_ctrl = wx.TextCtrl(panel, value="0.3")
        grid.Add(dr_ctrl, 0, wx.EXPAND)

        grid.Add(wx.StaticText(panel, label="Net name:"), 0, wx.ALIGN_CENTER_VERTICAL)
        net_ctrl = wx.TextCtrl(panel, value="GND")
        grid.Add(net_ctrl, 0, wx.EXPAND)

        sizer.Add(grid, 1, wx.ALL | wx.EXPAND, 20)
        btn_sizer = dlg.CreateButtonSizer(wx.OK | wx.CANCEL)
        sizer.Add(btn_sizer, 0, wx.ALL | wx.EXPAND, 10)

        panel.SetSizer(sizer)
        dlg_main_sizer = wx.BoxSizer(wx.VERTICAL)
        dlg_main_sizer.Add(panel, 1, wx.EXPAND)
        dlg.SetSizerAndFit(dlg_main_sizer)
        dlg.Center()

        if dlg.ShowModal() == wx.ID_OK:
            try:
                sp = float(sp_ctrl.GetValue().strip())
                dia = float(dia_ctrl.GetValue().strip())
                dr = float(dr_ctrl.GetValue().strip())
                net_name = net_ctrl.GetValue().strip()
                if sp <= 0 or dia <= 0 or dr <= 0:
                    raise ValueError("All dimensions must be positive")
                if dr >= dia:
                    raise ValueError("Drill must be smaller than diameter")
                return sp, dia, dr, net_name
            except ValueError as e:
                self.show_msg(f"Invalid input: {e}", is_error=True)
        return None

    def Run(self):
        try:
            board = pcbnew.GetBoard()
            if not board:
                self.show_msg("No open PCB board.", is_error=True)
                return

            result = self.get_params()
            if not result:
                return
            spacing_mm, dia_mm, drill_mm, net_name = result

            spacing_nm = int(spacing_mm * 1_000_000)
            dia_nm = int(dia_mm * 1_000_000)
            drill_nm = int(drill_mm * 1_000_000)

            zones = [z for z in board.Zones() if z.IsSelected()]
            footprints = [f for f in board.GetFootprints() if f.IsSelected()]

            if not zones and not footprints:
                self.show_msg("Select a copper zone or footprint first.", is_error=True)
                return

            target_net = None
            for n in board.GetNetsByName().values():
                if n.GetNetname().upper() == net_name.upper():
                    target_net = n
                    break

            if target_net is None:
                target_net = pcbnew.NETINFO_ITEM(board, net_name)
                board.Add(target_net)

            via_net_code = target_net.GetNetCode()

            areas = []
            if zones:
                for z in zones:
                    bbox = z.GetBoundingBox()
                    areas.append(bbox)
            if footprints:
                for fp in footprints:
                    bbox = fp.GetBoundingBox()
                    areas.append(bbox)

            created = 0
            for area in areas:
                x0 = area.GetX()
                y0 = area.GetY()
                x1 = x0 + area.GetWidth()
                y1 = y0 + area.GetHeight()

                x = x0 + spacing_nm // 2
                while x < x1:
                    y = y0 + spacing_nm // 2
                    while y < y1:
                        via = pcbnew.PCB_VIA(board)
                        via.SetPosition(pcbnew.VECTOR2I(int(x), int(y)))
                        via.SetWidth(dia_nm)
                        via.SetDrill(drill_nm)
                        via.SetNetCode(via_net_code)
                        try:
                            via.SetLayerPair(pcbnew.F_Cu, pcbnew.B_Cu)
                        except Exception:
                            pass
                        board.Add(via)
                        created += 1
                        y += spacing_nm
                    x += spacing_nm

            try:
                canvas = board.GetViewControl()
                if canvas:
                    canvas.Refresh()
            except Exception:
                try:
                    pcbnew.Refresh()
                except Exception:
                    pass

            self.show_msg(
                f"Done.\n\n"
                f"  - Vias created: {created}\n"
                f"  - Spacing: {spacing_mm:.2f} mm\n"
                f"  - Via size: {dia_mm:.2f} / {drill_mm:.2f} mm\n"
                f"  - Net: {net_name}"
            )

        except Exception as e:
            self.show_msg(
                f"Uncaught error.\n\n"
                f"Type: {type(e).__name__}\n"
                f"Message: {e}\n\n"
                f"Traceback:\n{traceback.format_exc()}",
                is_error=True,
            )


if __name__ != '__main__':
    ViaStitcherPlugin().register()
else:
    print("[Via Stitcher] Code loaded. Run ViaStitcherPlugin().Run() in the console.")

如何使用 Via Stitcher

  1. 開啟你的設計,進入 KiCad 10 PCB 編輯器。
  2. 選取一或多個鋪銅區或封裝——兩者都行,也可以混用,這告訴插件放置過孔的位置。
  3. 啟動插件:Tools → External Tools → Via Stitcher(或工具列圖示)。
  4. 輸入參數,在對話框填入間距、直徑、鑽孔與線網名稱。
  5. 按 OK。插件會填滿選取區域的接地過孔,並顯示總數。

小秘訣:你不需要先畫鋪銅區。直接選中封裝的包圍框也行——可快速在功率墊下方加散熱過孔。KiCad Via Stitcher 對兩者都使用同一套網格填孔邏輯。

安裝指南

要讓 KiCad 啟動時自動載入你的外掛,需把檔案放到官方指定的目錄之一:

  • macOS ★ 官方最推薦路徑:
    ~/Documents/KiCad/10.0/scripting/plugins/
    (不隱藏的個人資料夾,易於管理與清理,強烈推薦!)
  • Windows 路徑:
    %APPDATA%\kicad\10.0\plugins\
  • Linux 路徑:
    ~/.local/share/kicad/10.0/plugins/

原始碼安裝(推薦):把整個 via_stitcher/ 資料夾——包含 __init__.pyvia_stitcher.pyicon.png——複製到上方外掛目錄。重新啟動 KiCad(或重新整理外掛),Via Stitcher 就會出現在 Tools → External Tools

編譯檔部署(.so / .pyd):若要發行又不揭露原始碼,可用 Cython(內附 setup.py)把插件編譯成平台專屬的共用模組。把 __init__.pyicon.png 留在 via_stitcher/ 資料夾,直接用編譯後的模組替換 via_stitcher.py

實戰建議

  • EMC 屏蔽:對 RF via 圍欄,常見的起步作法是讓接地過孔間距低於最高工作頻率的 λ/10。對 2.4 GHz 的自由空間,這大約是 12.5 mm——但實務上設計者會依疊層與回流路徑選擇更密的間距。
  • 散熱過孔:對 QFN 或 PowerPAD 封裝,0.3 mm 鑽孔既可防止焊錫吸暘,又能最大程度把熱傳到內層/底層。
  • DRC 檢查:執行插件後,跑一次 KiCad 的 DRC,確認過孔沒有跟現有走線或禁止區碰撞,特別是在緊密的 1.27 mm 間距下。
  • 自動建網:如果你輸入了不存在的線網名(例如打錯字),插件會自動替你建立——所以務必再確認名稱,避免意外產生新線網。

結論

接地過孔正是這樣一種「把『可以用』變成『用得很棒』」的任務——在專業 PCB 設計裡尤其關鍵。有了 KiCad Via Stitcher,重複性的過孔放置變成簡單、可重複的工作流程:選一個鋪銅區或封裝、設定間距與過孔尺寸、讓插件自動生成網格。

準備好一試了嗎?到 SaludPCB 商店下載 KiCad Via Stitcher 插件,開啟 KiCad,幾秒鐘內為鋪銅平面填入接地過孔(Stitching Vias)。