Générer une règle PCB (cm et pouces) avec un script Python pcbnew

Publié le 21 octobre 2026 par leZ

Une vraie règle PCB de 15 cm / 5 pouces dessinée par un script Python pcbnew : graduations en cuivre doré, chiffres en sérigraphie, jauges CMS et de perçage, DRC à 0.

Règle PCB de 15 cm et 5 pouces générée par script Python pcbnew : graduations en cuivre doré sur vernis noir, jauges de tailles CMS et de perçage
Le rendu 3D de KiCad : vernis noir, cuivre doré, échelle 1:1.

Une règle en PCB est le goodie classique des fabricants de circuits imprimés, et c’est un excellent exercice de script : 288 objets à placer au dixième de millimètre près, impossibles à dessiner proprement à la main, mais qui tiennent en quelques boucles Python. Ce projet produit une vraie règle fabricable : 15 cm d’un côté, 5 pouces de l’autre, une jauge des tailles de boîtiers CMS et une jauge de perçage, avec un DRC à 0 erreur.

Le projet complet en vidéo, étape par étape (6 min 24, sous-titres anglais) :

Tout repose sur trois briques déjà vues dans les tutoriels : des segments (PCB_SHAPE), des textes de sérigraphie (PCB_TEXT) et des trous dessinés sur Edge.Cuts.

Le principe : une boucle et un modulo

Chaque graduation est un segment vertical. Sa longueur dépend uniquement de sa position : un grand trait tous les centimètres, un moyen tous les 5 mm, un petit sinon. Le modulo % fait tout le travail :

for i in range(151):                                   # un trait par mm, de 0 à 15 cm
    ln = 4.5 if i % 10 == 0 else 3.0 if i % 5 == 0 else 1.8
    seg(board, (ZERO + i, Y0 + MARGIN), (ZERO + i, Y0 + MARGIN + ln))

L’échelle en pouces suit la même logique avec un pas de 25,4 / 16 mm et cinq longueurs de trait (pouce, 1/2, 1/4, 1/8, 1/16).

Ajustez la longueur ci-dessous pour voir combien d’objets le script crée :

for i in range(151):        # one tick per mm
    ln = 4.5 if i % 10 == 0 else 3.0 if i % 5 == 0 else 1.8
    seg(board, (ZERO + i, Y0 + MARGIN), (ZERO + i, Y0 + MARGIN + ln))
15
0 graduations et chiffres · carte 162 × 32 mm

Le script complet

Le script crée une carte vide, dessine tout, puis enregistre pcb_ruler.kicad_pcb. Lancez-le avec le Python fourni avec KiCad (voir l’installation), par exemple depuis la KiCad Command Prompt : python pcb_ruler.py.

import pcbnew
from pcbnew import Edge_Cuts, F_Cu, F_Mask, F_SilkS, FromMM, PCB_SHAPE, PCB_TEXT, VECTOR2I

X0, Y0, X1, Y1 = 100.0, 100.0, 262.0, 132.0   # contour de la carte, en mm
ZERO = 105.0       # x du repère 0, commun aux deux échelles
MARGIN = 0.75      # marge cuivre-bord (le DRC veut 0,5 mm + la demi-largeur du trait)
TICK_W = 0.22


def mm(x, y):
    return VECTOR2I(FromMM(x), FromMM(y))


def shape(board, kind, layer, width):
    s = PCB_SHAPE(board)
    s.SetShape(kind)
    s.SetLayer(layer)
    s.SetWidth(FromMM(width))
    board.Add(s)
    return s


def seg(board, a, b, layer=F_Cu, w=TICK_W):
    s = shape(board, pcbnew.SHAPE_T_SEGMENT, layer, w)
    s.SetStart(mm(*a))
    s.SetEnd(mm(*b))
    return s


def circle(board, c, r, layer=Edge_Cuts, w=0.1):
    s = shape(board, pcbnew.SHAPE_T_CIRCLE, layer, w)
    s.SetCenter(mm(*c))
    s.SetEnd(mm(c[0] + r, c[1]))
    return s


def rect(board, cx, cy, w, h):
    s = shape(board, pcbnew.SHAPE_T_RECTANGLE, F_Cu, 0.05)
    s.SetStart(mm(cx - w / 2, cy - h / 2))
    s.SetEnd(mm(cx + w / 2, cy + h / 2))
    s.SetFilled(True)
    return s


def text(board, s, x, y, size=1.6, bold=False):
    t = PCB_TEXT(board)
    t.SetText(s)
    t.SetPosition(mm(x, y))
    t.SetLayer(F_SilkS)
    t.SetTextSize(VECTOR2I(FromMM(size), FromMM(size)))
    t.SetTextThickness(FromMM(size * (0.2 if bold else 0.15)))
    board.Add(t)


def outline(board, r=3.0):
    for a, b in (((X0 + r, Y0), (X1 - r, Y0)), ((X1, Y0 + r), (X1, Y1 - r)),
                 ((X1 - r, Y1), (X0 + r, Y1)), ((X0, Y1 - r), (X0, Y0 + r))):
        seg(board, a, b, Edge_Cuts, 0.1)
    for c, start in (((X0 + r, Y0 + r), (X0, Y0 + r)), ((X1 - r, Y0 + r), (X1 - r, Y0)),
                     ((X1 - r, Y1 - r), (X1, Y1 - r)), ((X0 + r, Y1 - r), (X0 + r, Y1))):
        a = shape(board, pcbnew.SHAPE_T_ARC, Edge_Cuts, 0.1)
        a.SetCenter(mm(*c))
        a.SetStart(mm(*start))
        a.SetArcAngleAndEnd(pcbnew.EDA_ANGLE(90, pcbnew.DEGREES_T), True)
    circle(board, (X1 - 6, (Y0 + Y1) / 2), 2.5)        # trou d'accroche


def metric_scale(board):
    for i in range(151):                                   # un trait par mm, de 0 à 15 cm
        ln = 4.5 if i % 10 == 0 else 3.0 if i % 5 == 0 else 1.8
        seg(board, (ZERO + i, Y0 + MARGIN), (ZERO + i, Y0 + MARGIN + ln))
    for c in range(16):
        text(board, str(c), ZERO + 10 * c, Y0 + 6.9, 1.7)
    text(board, "cm", ZERO + 154, Y0 + 6.9, 1.4)


def inch_scale(board):
    for i in range(5 * 16 + 1):                            # traits au 1/16 de pouce, de 0 à 5 in
        ln = 4.5 if i % 16 == 0 else 3.2 if i % 8 == 0 else 2.4 if i % 4 == 0 else 1.7 if i % 2 == 0 else 1.1
        x = ZERO + i * 25.4 / 16
        seg(board, (x, Y1 - MARGIN), (x, Y1 - MARGIN - ln))
    for n in range(6):
        text(board, str(n), ZERO + 25.4 * n, Y1 - 6.9, 1.7)
    text(board, "inch", ZERO + 25.4 * 5 + 6, Y1 - 6.9, 1.4)


def gauges(board, ym=(Y0 + Y1) / 2):
    x = 108.0
    for name, w, h in [("0201", 0.6, 0.3), ("0402", 1.0, 0.5), ("0603", 1.6, 0.8),
                       ("0805", 2.0, 1.25), ("1206", 3.2, 1.6), ("2512", 6.3, 3.2)]:
        rect(board, x + w / 2, ym - 0.6, w, h)             # taille réelle du boîtier CMS
        text(board, name, x + w / 2, ym + 2.4, 1.0)
        x += max(w, 4.0) + 2.6
    x = 166.0
    for d in [0.5, 0.8, 1.0, 1.5, 2.0, 3.0]:
        c = (x + d / 2, ym - 0.6)
        circle(board, c, d / 2)                            # vrai trou (Edge.Cuts)
        circle(board, c, d / 2 + 0.85, F_Cu, 0.6)          # anneau de cuivre, à 0,55 mm du trou
        text(board, f"{d:g}", c[0], max(ym + 2.4, c[1] + d / 2 + 2.35), 1.0)
        x += d + 4.2
    text(board, "ScriptedPCB", 228.0, ym - 1.2, 2.2, bold=True)
    text(board, "drawn with Python", 228.0, ym + 1.9, 1.1)


def expose_copper(board):
    """Mêmes formes sur F.Mask : pas de vernis sur le cuivre, qui ressort doré (finition ENIG)."""
    for s in list(board.GetDrawings()):
        if isinstance(s, PCB_SHAPE) and s.GetLayer() == F_Cu:
            m = s.Duplicate()
            m.SetLayer(F_Mask)
            m.SetWidth(s.GetWidth() + FromMM(0.08))
            board.Add(m)


board = pcbnew.CreateEmptyBoard()
outline(board)
metric_scale(board)
inch_scale(board)
gauges(board)
expose_copper(board)
pcbnew.SaveBoard("pcb_ruler.kicad_pcb", board)
print(len(board.GetDrawings()), "objects")
Règle PCB générée par script Python dans l'éditeur de PCB de KiCad : graduations en mm et en pouces sur F.Cu, chiffres en sérigraphie, jauges CMS et de perçage
Le résultat dans l'éditeur de PCB : 541 objets, aucun clic.

Les points qui comptent pour la fabrication

Vérifiez le résultat sans ouvrir l’interface avec kicad-cli pcb drc --exit-code-violations pcb_ruler.kicad_pcb (code de sortie 0 = aucune violation), comme expliqué dans le tuto headless.

Vernis noir et rendu 3D

La couleur du vernis se règle dans Fichiers → Options CI… → Stackup du Circuit Imprimé → Couches Physiques, colonne Couleur (choisissez Black pour les deux couches de vernis et White pour la sérigraphie), et la Finition cuivre sur ENIG. Pour le rendu 3D en ligne de commande, demandez à kicad-cli d’utiliser ces couleurs :

kicad-cli pcb render --use-board-stackup-colors --quality high -o regle.png pcb_ruler.kicad_pcb
Vue 3D en perspective de la règle PCB générée en Python : vernis noir, graduations et anneaux de perçage en cuivre doré
kicad-cli pcb render avec les couleurs de l'empilement.

La couleur réelle se choisit au moment de la commande chez le fabricant ; celle de l’empilement sert surtout à l’aperçu.

Pour aller plus loin

Changez range(151) pour une règle de 10, 20 ou 30 cm (pensez à agrandir le contour), ajoutez votre logo, ou remplacez la jauge CMS par des empreintes réelles chargées avec FootprintLoad, comme dans le tuto de placement.