KiCad 插件教學 2026 | 過孔 Stitching Vias 一鍵搞定自動填滿鋪銅平面
接地過孔(Stitching Vias)是 PCB 佈局中常見的工作,尤其當你處理接地平面、RF 屏蔽與散熱管理時。手動放置幾十顆、甚至上百顆把鋪銅區域「縫合」在一起的小接地過孔,很快就會變得重複且容易出錯——然而接地參考穩定、EMI 受控、熱通路涼爽,全都依賴它們。KiCad 插件開發提供了一種自動化這項工作的方法:
在本教學中,我們要介紹 KiCad Via Stitcher,一個免費的開源 KiCad 10 插件,把重複性的過孔放置工作自動化。選一個鋪銅區域或一個封裝,KiCad Via Stitcher 會在選取內容的包含框內填出規則網格的接地過孔(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 編輯器就能一鍵觸發。
它的運作流程如下:
- 讀取開啟的板材:透過
pcbnew.GetBoard()取得目前開啟的 PCB。若沒有開啟板材,就跳出明確警告。 - 收集你的選取:收集每一顆被選中的鋪銅區(
board.Zones())與被選中的封裝(board.GetFootprints())。若什麼都沒選,就出示實用提示並停止。 - 提示參數:開啟原生的 wxPython 對話框,詢問四個數值——過孔間距、過孔直徑、鑽孔大小與目標線網名稱。
- 驗證輸入:每個尺寸必須為正數,且鑽孔必須小於過孔直徑,否則會以錯誤訊息拒絕。
- 找到目標線網:在板上搜尋該線網(例如
GND)。如果不存在,插件會自動建立。 - 填入過孔:對每一選中區域,計算包含框,然後在 F.Cu / B.Cu 層對上填滿間距均勻的穿透孔。
- 回報結果:重新整理畫面並顯示摘要——總過孔數、間距、過孔尺寸與線網名稱。
就這些了——沒有雲端呼叫,不需要任何第三方 Python 套件;它只使用 KiCad 內建的 pcbnew 與 wx 模組。整個插件就是一個獨立的 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 內建的 pcbnew 與 wx 模組。
- 安裝最新版 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
- 開啟你的設計,進入 KiCad 10 PCB 編輯器。
- 選取一或多個鋪銅區或封裝——兩者都行,也可以混用,這告訴插件放置過孔的位置。
- 啟動插件:點 Tools → External Tools → Via Stitcher(或工具列圖示)。
- 輸入參數,在對話框填入間距、直徑、鑽孔與線網名稱。
- 按 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__.py、via_stitcher.py 與 icon.png——複製到上方外掛目錄。重新啟動 KiCad(或重新整理外掛),Via Stitcher 就會出現在 Tools → External Tools。
編譯檔部署(.so / .pyd):若要發行又不揭露原始碼,可用 Cython(內附 setup.py)把插件編譯成平台專屬的共用模組。把 __init__.py 與 icon.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)。









