Skip to main content

KiCad 3D PCB renderings with GitHub Actions

Documentation has always been important for the splitflap project, and a big part of that is keeping its README landing page up to date so it's easy to see what the project is about.

However, if you're not an electrical engineer or experienced hobbyist, the schematics and board layout images that were previously included in the README could be a little hard to read. So I wanted to include 3d renderings of what the PCBs actually look like in real life to make it even more approachable.

Example auto-generated 3d rendering

KiCad has a built in 3d renderer, but it's not easily scriptable (as of the current stable version, KiCad 5). Updating photo renderings in the documentation would be too tedious and error prone to do manually - especially now that the project has 7 different PCB designs - so I wanted a way to generate these automatically whenever the designs change.

I've previously described how I automated schematic rendering using UI automation - a technique which went on to inspire parts of the awesome KiBot toolchain - and this 3d rendering process is based on the same fundamentals as my previous approach.

As a quick recap, the process is roughly as follows:

  • start an Xvfb virtual display, to host the GUI even on a headless server like GitHub Actions runners
  • start recordmydesktop on the virtual display (saves a screen recording so you can debug what happened if something goes wrong in CI)
  • launch KiCad
  • interact with KiCad using xdotool

It's easy enough to launch the 3d viewer this way, but the next challenge was configuring the camera orientation to take a nice photo. The are two tricky parts:

  1. KiCad automatically centers the bounding box of all items present in the board layout, even if those elements are not visible in the rendering. For example, if you have comments or fabrication-only drawings that extend beyond the board's outline, they don't get rendered in the 3d viewer, but still affect the calculated center point, causing your PCB to appear off-center by default

    Non-visible items like comments affect centering behavior



  2. Normally you would adjust the camera by clicking and dragging, but doing this repeatably in automation is difficult (part of the issue is that camera adjustments are path dependent, that is, the start and end mouse positions of a drag don't fully describe the resulting transform; two different paths to reach the same destination cause different results)

    View transforms are path-dependent


To work around these issues, my solution was to use the view commands in the menu instead - e.g. "Zoom In" or "Rotate X Clockwise" - which apply discrete repeatable transforms to the view.

Since each PCB design will have a different preferred camera view, I gave each of those transforms a compact name, like "rx+" for "Rotate X Clockwise", so that an ordered list of transforms can be passed as a command-line argument when exporting a 3d rendering.

For example, a command in GitHub Actions might look like this:

./scripts/export_3d.py chainlinkDriver/chainlinkDriver.kicad_pcb --width 2000 --height 800 transform z+ z+ z+ z+ z+ z+ z+ z+ mr rx+ rx+ ry- ry- rz-

And the resulting export process looks like this:

 



Final little details

One thing you might have noticed from the gif above is that I'm using KiCad's ray-tracing option, for slightly more realistic rendering. The ray-tracing takes time though, which a human can see visually with the expanding circle during rendering and the subsequent image quality change once complete, but it's not easy to programmatically determine whether it's finished or not.

A naive approach would be to just hard-code a timeout, but we can do slightly better: since ray-tracing is computationally intensive, we can actually just monitor KiCad's CPU usage and wait until it goes idle!

I'm using Python's psutil library to do this - the script just waits until the "pcbnew" process drops below 5% CPU usage

def _wait_for_pcbnew_idle():
    start = time.time()
    while time.time() < start + RENDER_TIMEOUT:
        for proc in psutil.process_iter():
            if proc.name() == 'pcbnew':
                cpu = proc.cpu_percent(interval=1)
                print(f'CPU={cpu}', flush=True)
                if cpu < 5:
                    print('Render took %d seconds' % (time.time() - start))
                    return
        time.sleep(1)
    raise RuntimeError('Timeout waiting for pcbnew to go idle')

 

Finally, I wanted to adjust some rendering settings like soldermask color. This is possible to do through the GUI, but there's an easier way than clicking through a bunch of menus - KiCad stores configuration options to a plaintext file (".config/kicad/pcbnew" in your home dir on Linux) so we can just replace that file with our desired settings before launching KiCad.

However, I wanted these automation scripts to be as non-destructive as possible if you run them locally instead of on GitHub Actions, so I created a basic Python context manager that saves your existing config and replaces it with predefined settings for the duration of the inner context. Here's what it looks like in use:

    settings = {
        'canvas_type': '1',
        'SMaskColor_Red': '0.1',
        'SMaskColor_Green': '0.1',
        'SMaskColor_Blue': '0.1',
        'RenderEngine': '1' if raytrace else '0',
        'Render_RAY_ProceduralTextures': '0',
    }
    with patch_config(os.path.expanduser('~/.config/kicad/pcbnew'), settings):
        # ... do the UI automation here ...

This way, even if an exception occurs during the automation, the context manager will restore the original config before exiting.

Comments

Popular posts from this blog

OpenSCAD Rendering Tricks, Part 3: Web viewer

This is my sixth post in a series about the  open source split-flap display  I’ve been designing in my free time. Check out a  video of the prototype . Posts in the series: Scripting KiCad Pcbnew exports Automated KiCad, OpenSCAD rendering using Travis CI Using UI automation to export KiCad schematics OpenSCAD Rendering Tricks, Part 1: Animated GIF OpenSCAD Rendering Tricks, Part 2: Laser Cutting OpenSCAD Rendering Tricks, Part 3: Web viewer One of my goals when building the split-flap display was to make sure it was easy to visualize the end product and look at the design in detail without having to download the full source or install any programs. It’s hard to get excited about a project you find online if you need to invest time and effort before you even know how it works or what it looks like. I’ve previously blogged about automatically exporting the schematics, PCB layout , and even an animated gif of the 3D model to make it easier to understand the project at a glanc

Using UI automation to export KiCad schematics

This is my third post in a series about the open source split-flap display I’ve been designing in my free time. I’ll hopefully write a bit more about the overall design process in the future, but for now wanted to start with some fairly technical posts about build automation on that project. Posts in the series: Scripting KiCad Pcbnew exports Automated KiCad, OpenSCAD rendering using Travis CI Using UI automation to export KiCad schematics OpenSCAD Rendering Tricks, Part 1: Animated GIF OpenSCAD Rendering Tricks, Part 2: Laser Cutting OpenSCAD Rendering Tricks, Part 3: Web viewer Since I’ve been designing the split-flap display as an open source project, I wanted to make sure that all of the different components were easily accessible and visible for someone new or just browsing the project. Today’s post continues the series on automatically rendering images to include in the project’s README, but this time we go beyond simple programmatic bindings to get what we want: the

Scripting KiCad Pcbnew exports

This is my first post in a series about the  open source split-flap display  I’ve been designing in my free time. Check out a  video of the prototype . Posts in the series: Scripting KiCad Pcbnew exports Automated KiCad, OpenSCAD rendering using Travis CI Using UI automation to export KiCad schematics OpenSCAD Rendering Tricks, Part 1: Animated GIF OpenSCAD Rendering Tricks, Part 2: Laser Cutting OpenSCAD Rendering Tricks, Part 3: Web viewer For the past few months I’ve been designing an open source split-flap display in my free time — the kind of retro electromechanical display that used to be in airports and train stations before LEDs and LCDs took over and makes that distinctive “tick tick tick tick” sound as the letters and numbers flip into place. I designed the electronics in KiCad, and one of the things I wanted to do was include a nice picture of the current state of the custom PCB design in the project’s README file. Of course, I could generate a snapshot of the