golang

Свой CAM для фрезеровки плат: Windows 7, станок без API и карта высот самой фрезой

  • среда, 21 октября 2026 г. в 00:00:16
https://habr.com/ru/articles/1096450/

Когда односторонняя плата нужна сегодня, а не через две недели заводского цикла, и под рукой есть фрезерный станок, её можно изготовить за один вечер. Я отладил старый станок так, чтобы он делал платы с дорожками шириной 0,2 мм, зазорами 0,2 мм и отверстиями. Сразу обозначу ограничения: метод рассчитан на один слой меди, не поддерживает переходные отверстия.

Плата после станка: дорожки, отверстия, V-канавки по краю
Плата после станка: дорожки, отверстия, V-канавки по краю

Станок

Станок находится не у меня. Доступ к нему я получаю по договорённости с ЦМР, Центром молодёжной робототехники МГТУ им. Н. Э. Баумана. Это старый станок, который поставляет компания «Станком». Управляет им NcStudio версии 5.5.60, запущенная на компьютере с 32-разрядной Windows 7. Компьютер не подключён к сети, а из флеш-накопителей он распознаёт лишь некоторые.

NcStudio предоставляет много функций, однако я задействовал лишь две. Первая функция: окно MDI, куда оператор вручную вводит команды G-кода. Вторая: стандартный диалог открытия файла управляющей программы. Ни внешнего API, ни разъёма для измерительного щупа у этой системы нет. Вся дальнейшая работа строится только на этих двух окнах.

Как работает V-фреза

Для изоляции дорожек я применяю V-образную фрезу с диаметром кончика 0,1 мм и углом 45°. Она снимает медь на глубину 0,1 мм, оставляя между дорожками разделительную канавку. Ширина этой канавки определяется глубиной погружения: чем глубже конус входит в материал, тем шире получается рез.

Разрез V-фрезы и ширина реза на трёх глубинах
Разрез V-фрезы и ширина реза на трёх глубинах

На глубине 0,1 мм ширина реза равна 0,183 мм. Если заготовка опускается на 0,05 мм ниже расчётного уровня, фреза уже почти не касается меди. Подъём на те же 0,05 мм расширяет рез до 0,224 мм, и дорожка, рассчитанная на 0,2 мм, оказывается бракованной. Одного общего значения высоты для всей платы оказалось недостаточно: станку необходимо знать рельеф медной поверхности в каждой точке.

Процесс снятия карты высот

Поскольку у станка отсутствует измерительный щуп, его роль выполняет фреза. Медный слой заготовки служит контактным датчиком. В момент касания фрезы с медью электрическая цепь замыкается. Этот сигнал фиксирует модуль ESP32-C3, размещённый прямо на заготовке. Медь подключена к его входу. Фреза, в свою очередь, соединена с общим проводом через шпиндель, корпус станка, компьютер и USB-кабель.

Стол станка: заготовка, ESP32-C3 и провода
Стол станка: заготовка, ESP32-C3 и провода

Управление станком берёт на себя моё приложение pcbscan. Оно выводит команды G-кода в окно MDI, имитируя действия оператора. Процедура одного касания выглядит так: фреза делает небольшой шаг вниз, программа опрашивает ESP32 о наличии контакта и, если его нет, повторяет шаг. Приходится использовать короткие шаги, потому что кнопка «Стоп» в NcStudio реагирует только по завершении строки, и длинное перемещение прервать в середине невозможно.

Обход выполняется в такой последовательности:

  1. В углу листа, над медью, выставляю фрезу на 1–2 мм и сбрасываю Z в NcStudio. Эта точка задаёт начало координат для всей операции.

  2. Вначале программа прикасается к углу листа, а затем по очереди к четырём углам платы. Неточность задания нуля или нарушение контакта измерительного провода дают о себе знать уже после пяти точек.

  3. Далее программа обходит сетку с шагом 10 мм, но лишь в границах плат, а не по всему листу.

  4. Перемещения по Z всегда идут в абсолютных координатах, с отсчётом от нуля, заданного в начале.

  5. В финале программа повторно касается самой первой точки. Если её координата сместилась больше чем на 0,03 мм, карта отбраковывается и не сохраняется.

Обход карты высот в окне pcbscan
Обход карты высот в окне pcbscan

Измерение короткими шагами отнимает много времени. Без прогнозирования на одну точку требовалось в среднем 14,4 команды. Поэтому программа оценивает ожидаемую высоту по соседним измерениям. В первом ряду она продлевает прямую, построенную по двум соседним точкам. В последующих рядах строит плоскость по четырём ближайшим уже снятым точкам. Затем фреза одним ходом опускается до уровня на 0,04 мм выше предсказанной высоты, после чего продолжает осторожное касание шагами по 0,01 мм. Если на протяжении 0,06 мм контакт не происходит, запускается обычная процедура поиска.

Подлёт по прогнозу соседей
Подлёт по прогнозу соседей

На реальных картах высот превышение меди над прогнозом не превышало 0,03 мм. Оценка же по ближайшей точке или линейной экстраполяции по двум точкам могла дать ошибку вверх до 0,15 мм. Ошибка в сторону завышения критична: при такой ошибке фреза вдавится в медь на ходу подлёта.

Первая полученная карта

Первая же снятая карта прояснила масштаб проблемы. На заготовке размером 73 × 88 мм перепад высот превысил миллиметр. Это на порядок больше глубины реза.

Первая карта высот в окне pcbscan
Первая карта высот в окне pcbscan

Карта высот нужна не как самоцель. Моя программа разбивает каждый проход фрезы на миллиметровые отрезки и к координате Z каждого отрезка добавляет локальную высоту меди, взятую из карты. Я протестировал этот подход в симуляции: при перепаде высот 0,4 мм глубина реза везде сохранялась равной 0,1 мм, а максимальное отклонение не вышло за 0,005 мм.

CAM без зависимостей

Мне нужно было создавать управляющие программы для станка из файлов, которые обычно отправляют на завод. Эти файлы включают Gerber-описания медных слоёв, маски и контура, а также Excellon-файлы с координатами отверстий. Первый вариант я реализовал на Python. Считывание слоёв выполняла библиотека gerbonara, предназначенная для обработки Gerber-файлов на Python. Отступы от дорожек вычисляла shapely. Это геометрическая библиотека, способная строить расширенные многоугольники на заданную величину. Собственный симулятор проверял каждую программу перед отправкой её на станок. На MacBook Air с процессором M5 всё работало.

Однако программу необходимо было запускать на компьютере станка, где установлена 32-разрядная Windows 7 без доступа к сети. Разворачивать там Python вместе с GEOS (на которой основана shapely) я не хотел. Поэтому окончательную версию я написал на Go 1.20. Это последний релиз Go, поддерживающий сборку исполняемых файлов для Windows 7. На выходе получается один exe-файл без внешних зависимостей. В нём нет геометрических библиотек наподобие shapely или Clipper. Вместо векторных операций над многоугольниками все расчёты выполняются на растровой сетке.

Как растровый CAM находит путь фрезы
Как растровый CAM находит путь фрезы

Процесс состоит из четырёх шагов:

  1. Я заливаю медный рисунок слоя в сетку с шагом 0,01 мм. Круглые контактные площадки обрабатываю отдельно, а многоугольники заливаю по правилу чёт-нечет.

  2. Для каждого пикселя вычисляю точное евклидово расстояние до ближайшего медного участка. Использую алгоритм Фельценшвальба и Хуттенлохера: один проход по столбцам, затем по строкам, оба за линейное время.

  3. Линию, на которой расстояние до меди равно половине ширины реза, нахожу алгоритмом marching squares и упрощаю алгоритмом Дугласа-Пекера с допуском 0,003 мм. По этой линии прокладываю траекторию фрезы.

  4. Второй проход делаю дальше от меди, с перекрытием первого на 30 %. Вместе оба прохода снимают полосу шириной примерно 0,31 мм.

На растре легче обнаружить участки, где две дорожки расположены на расстоянии меньше ширины реза. Я выполняю морфологическое закрытие меди на половину ширины реза и вычитаю из результата исходную медь. Область, которая после этого касается двух разных медных островов, соответствует перемычке: фреза не сможет пройти между дорожками. Найденные места программа отображает на изображении и заносит в отчёт.

Тестовая плата и проходы изоляции
Тестовая плата и проходы изоляции

На тестовой плате насчитывается 96 таких мест. Это ожидаемо: плата содержит образцы, нарушающие проектные нормы.

Скорость

Преобразование расстояний выполняется по столбцам. Если обрабатывать столбец сверху вниз, обращения к памяти происходят не последовательно, и такой проход занимает 70 % общего времени. Из-за этого проход по столбцам реализован построчно, что обеспечивает последовательное чтение памяти. Таблица корней не принесла пользы: дорого само построение нижней огибающей парабол.

Маска вскрывается через кольцевые области, и на крупной плате таких колец могут быть сотни. Алгоритм обнаруживает их за один проход растровой сетки: для каждой ячейки анализируются уровни между минимальным и максимальным расстоянием в её углах. Обработка двухсторонней платы с маской занимает от 2 до 15 секунд.

Сверка с версией на Python

В ситуациях, где shapely выдаёт точный результат, растровый подход способен давать погрешность. Я проверил обе реализации на восьми платах из открытых проектов, спроектированных в KiCad и EasyEDA. Площади медных полигонов и маски сошлись с данными gerbonara с отклонением не более 0,5 %. Число отверстий, суммарный диаметр и геометрия пазов совпали без расхождений.

Длина реза изоляции, мм

Python (shapely)

Go (растр)

плата 1

1293

1291

плата 2

563

564

плата 3

3328

3329

плата 4

5813

5813

Дополнительно я измерил зазор между фрезой и медным слоем. Медиана составила 0,0916 мм при заданной половинной ширине реза 0,0914 мм. В интервал ±0,008 мм попало 99 % точек. Минимальный зазор 0,078 мм я зафиксировал в узком промежутке между двумя контактными площадками. В этом месте карта расстояний формирует гребень, и линейная интерполяция на его вершине вносит погрешность.

Что ещё пришлось разобрать в форматах
  • KiCad описывает пазы в Excellon-файле несколькими командами, а EasyEDA одной строкой. Обе формы успешно читаются.

  • EasyEDA помещает данные сверловки в отдельные файлы с отличным префиксом имени. Из-за этого файлы, относящиеся к одной плате, нельзя отбирать по общему префиксу.

  • Шаблон имени верхнего медного слоя в проектах DipTrace совпадает с обозначением слоя шелкографии в Eagle. Поэтому служебные слои (маска, шелкография, паста) отсеиваются до того, как начнётся поиск меди.

  • Название слоя паяльной пасты в EasyEDA очень похоже на имя слоя маски. Маску необходимо искать прежде, чем сработает правило определения «это не медь».

Программа у станка

Приложение pcbscan открывается как обычное окно Windows. В нём нет сторонних библиотек: окно Win32 создаётся через системные вызовы, текст отрисовывается собственным растровым шрифтовым движком, работающим с TrueType, а трёхмерный вид вычисляется программным z-буфером. На Mac та же программа отображает окно в браузере и функционирует в эмуляции станка. Все приведённые далее снимки окна получены в эмуляции.

Работа разбита на этапы. В левой части окна расположен их список. В центре видны действия текущего этапа, справа трёхмерный вид листа.

Платы

Я кладу zip-архив, подготовленный для завода, рядом с исполняемым файлом. pcbscan размещает платы на листе, учитывая их реальный контур, допуская повороты на 90° и соблюдая зазор между контурами. Затем он генерирует программы для всех этапов и показывает ожидаемое время выполнения каждого.

Программы листа в окне pcbscan
Программы листа в окне pcbscan

Карта высот

Выполняется обход поверхности, описанный ранее.

Фрезеровка

V-образная фреза прорезает изолирующие канавки вокруг дорожек с коррекцией по карте высот.

Маска

Я наношу паяльную маску на всю поверхность листа и высушиваю её. Лист остаётся на столе, поэтому карта высот не меняется. Затем фреза удаляет маску над контактными площадками. Сначала я выполняю пробный проход: шесть квадратов размером 3 × 3 мм на глубинах от 0,01 до 0,06 мм. По этому тесту определяю глубину, на которой маска полностью снимается. На тестовой плате 103 окна, ширина следа фрезы при глубине 0,03 мм равна 0,125 мм. Дополнительно программа вскрывает круг диаметром 2 мм в точке начала координат. Поскольку маска не проводит ток, после замены фрезы нужно выполнить касание Z0 по открытому медному участку.

Сверловка

Все отверстия обрабатываются одной фрезой диаметром 1 мм. Отверстие, не превышающее диаметр фрезы, просто сверлится. Отверстие большего диаметра фреза обходит по спирали. Фреза заглубляется на 0,3 мм ниже платы, благодаря чему карта высот для отверстий не требуется.

V-рез

V-образный резец прорезает канавку шириной 0,8 мм по контуру платы за четыре прохода и доводит каждую линию до края листа. Затем плату отламывают по этим канавкам.

Четыре этапа одного листа
Четыре этапа одного листа

Для каждого файла этапа порядок действий не меняется. Программа pcbscan указывает, какой инструмент нужно поставить, и выполняет касание меди в нулевой точке, чтобы оператор мог обнулить координату Z. Затем она выравнивает траекторию по карте высот и запрашивает ввод строки yes. После подтверждения pcbscan самостоятельно открывает файл в NcStudio через диалог открытия. Обработку запускает оператор кнопкой на станке, а не программа.

Этот же подход я заложил и в аварийную остановку. Пока pcbscan передаёт команды в NcStudio, его собственное окно остаётся неактивным. Если оператор щёлкнет мышью по рабочему столу, NcStudio потеряет фокус, и pcbscan остановит передачу данных.

Время программ тестовой платы
Время программ тестовой платы

Файлы и щуп

Нужно было загрузить pcbscan на компьютер станка. Сеть отсутствовала, и компьютер распознавал не каждый флеш-накопитель. Но к нему уже был подключён ESP32 из состава щупа. Часть flash-памяти модуля я выделил под файлы. Файлы загружаются на щуп по Wi-Fi с телефона, а pcbscan считывает их через COM-порт и запускает самообновление. Сжатый pcbscan весит 1,6 МБ и передаётся со щупа по порту за 6,4 секунды. Это время я замерил на MacBook.

Wi-Fi на щупе по умолчанию выключен и активируется лишь на период передачи данных. При постоянно включённом Wi-Fi вход касания регистрировал ложные срабатывания даже при отсутствии зажима.

Прошивку щупа pcbscan обновляет через тот же кабель. Для этого внутри программы реализован протокол загрузчика ESP32, взятый из исходного кода esptool. В Windows сигнал DTR достигает чипа только одновременно с последующей установкой RTS. Перед записью программа отключает сторожевые таймеры, чтобы чип не перезагрузился в процессе.

Грабли Windows 7 и последовательного порта
  • Драйвер usbser в Windows 7 вызывает взаимную блокировку при попытке читать и писать из разных потоков через один синхронный дескриптор. Первый ответ поступает, затем порт перестаёт отвечать. Запрос и чтение выполняются в одном потоке.

  • Повторное открытие уже занятого COM-порта через раз завершается сбоем. Порт щупа остаётся открытым на протяжении всей сессии.

  • Среда выполнения Go удерживает каждый обратный вызов Windows до завершения процесса, а общее число таких вызовов составляет около двух тысяч. Поиск окна NcStudio выполняется при каждой подсказке. Если бы на каждый поиск создавался новый обратный вызов, запас исчерпался бы в течение длинной смены. Поэтому на весь процесс выделен один обратный вызов.

Тестовая плата

Для настройки режимов я сделал тестовую плату. pcbscan добавляет рядом с ней серию пробных вырезов. По этим пробам я подбирал глубину, подачу, врезание, обороты шпинделя, подачу сверла и глубину V-реза. Вся плата вместе с пробами помещается на заготовке 100×100 мм.

Проба глубины формирует шесть групп по три дорожки шириной 0,3 мм и длиной 30 мм на глубинах от 0,04 до 0,15 мм. Траектории для каждой группы рассчитывает та же функция, которая используется для реальной платы. Подходящей считается глубина самого мелкого набора, в котором дорожки не прозваниваются между собой. Коротких канавок длиной 5 мм здесь недостаточно: ошибка карты высот проявляется лишь на длинных участках.

Три платы

Первую плату я изготовил до появления тестовой платы. Дорожки прорезались, но не на всей поверхности.

Первая плата
Первая плата

Дорожки с шириной 0,2 мм и зазором 0,2 мм получились без замыканий. Лучшую изоляцию дал режим с оборотами 24 000 об/мин, подачей 300 мм/мин, глубиной 0,1 мм и коррекцией по карте высот. После этого я уменьшил в правилах проектирования минимальные дорожку и зазор до 0,2 мм. Тем не менее, в качестве рекомендованного значения сохраняется 0,3 мм с запасом на возможные ошибки карты высот.

Тестовая плата 84 × 58 мм после станка
Тестовая плата 84 × 58 мм после станка
Итоговая плата: дорожки, отверстия, V-канавки по краю
Итоговая плата: дорожки, отверстия, V-канавки по краю

На этом фото именно та плата, которую я хотел получить. На ней выполнены изоляция дорожек, сверление отверстий одной фрезой 1 мм и V-рез по контуру.