Recommended resources
The freely available resources this lesson will direct you to.
Здравствуйте!
На прошлом уроке мы спроектировали консольные защёлки, добавив их на крышку корпуса. Они готовы к работе, но им пока не за что цепляться. Также в одном из предыдущих уроков мы создали бобышки для винтового соединения.
Сегодня мы завершим оба этих механизма, добавив ответные элементы на сопрягаемые детали. В результате у нас будет две полностью готовые к сборке версии корпуса: одна на винтах, другая на защёлках. Мы научимся создавать точные посадочные места как для винтов, используя мощную библиотеку bd_warehouse, так и для спроектированных нами защёлок.
Цель этого урока — добавить на сопрягаемые детали ответные элементы для защёлок и винтов, получив в итоге два полноценных варианта конструкции корпуса.
Давайте начнём с винтового соединения, так как оно позволит нам познакомиться с очень полезным инструментом для параметрического проектирования.
1. Ответные элементы для винтового соединения
Вместо того чтобы вручную вычислять диаметры отверстий под винты и их головки, мы воспользуемся библиотекой bd_warehouse. Это расширение для build123d, которое содержит параметрические модели стандартизированных компонентов: подшипников, винтов, гаек и т.д. Такой подход, аналогичный импорту готовых модулей в Python, значительно ускоряет разработку и уменьшает вероятность ошибок.
1.1. Установка bd_warehouse
Если вы ещё не установили библиотеку, добавьте её в ваш проект с помощью uv:
uv pip install bd_warehouse
1.2. Создание сквозных отверстий в основании
Предположим, у нас есть крышка с бобышками (lid_with_bosses) и основание (base_final). Наша задача — сделать в основании сквозные отверстия (clearance holes), через которые винты будут свободно проходить и вкручиваться в пилотные отверстия в бобышках. Головка винта должна быть утоплена в основание (countersunk).
Откройте ваш проект (рекомендую сохранить его как enclosure_v11.py) и давайте модифицируем код для создания основания.
FASTENER - PARAMETRIC THREADED FASTENERS (bd_warehouse docs)
Прежде чем мы напишем код, ознакомьтесь с документацией на bd_warehouse, чтобы понять, какие возможности она предоставляет для создания отверстий. Это поможет вам лучше понять код, который мы напишем.
Прочтите раздел 3.4 Custom Holes (стр. 15-20). Особое внимание уделите классу ClearanceHole и его параметрам, таким как fastener, fit и counter_sunk. Этот раздел подробно описывает, как библиотека автоматически создаёт правильные отверстия на основе модели крепежа.
Теперь применим эти знания. Вот как будет выглядеть код для создания основания с правильными отверстиями под винты M3. Предполагается, что у вас уже есть код, создающий base_final (монолитное основание) и boss_locations (GridLocations для размещения бобышек).
# enclosure_v11.py
# ... импорты и параметры ...
from bd_warehouse.fastener import SocketHeadCapScrew, ClearanceHole
from build123d import *
# ... (код для создания pcb_model, base_height, enclosure_dimensions и т.д.) ...
# ================== ПАРАМЕТРЫ КРЕПЛЕНИЯ (ВИНТЫ) ==================
# Определяем, какой винт мы будем использовать
# Длина винта должна быть достаточной, чтобы пройти через основание и войти в бобышку
# base_thickness + (длина резьбы в бобышке)
# Например, 10 мм + 8 мм = 18 мм. Возьмём стандартную длину 20 мм.
fastener_screw = SocketHeadCapScrew(size="M3-0.5", length=20)
# Расположение бобышек/винтов
boss_padding = 10
boss_locations = GridLocations(
enclosure_dimensions.X - boss_padding * 2,
enclosure_dimensions.Y - boss_padding * 2,
2,
2,
)
# ... (код создания base_final) ...
# ================== МОДИФИКАЦИЯ ОСНОВАНИЯ (ОТВЕРСТИЯ ПОД ВИНТЫ) ==================
with BuildPart() as base_with_holes:
add(base_final) # Начинаем с существующего основания
# Размещаем кастомные отверстия в тех же местах, что и бобышки
with Locations(boss_locations):
# ClearanceHole автоматически создаст сквозное отверстие
# с углублением под головку винта (т.к. у SocketHeadCapScrew она есть)
ClearanceHole(fastener=fastener_screw)
base_final_screws = base_with_holes.part
# ... (в блоке визуализации)
# if "show_object" in locals():
# show_object(base_final_screws, name="base_with_screws")
# show_object(lid_with_bosses, name="lid_with_bosses") # Предполагается, что эта деталь есть
Ключевые моменты:
- Мы создаём экземпляр
SocketHeadCapScrew, описывая наш винт.bd_warehouseзнает все его размеры по стандарту ISO. - Мы используем
ClearanceHole, передавая ему нашfastener_screw. Объект сам вычисляет нужный диаметр сквозного отверстия и размеры углубления под головку. - Параметр
depth=None(по умолчанию) означает, что отверстие будет сквозным. - Код получается декларативным и чистым. Мы описываем что хотим сделать (отверстие под винт), а не как (диаметры, глубины, булевы операции).
1.3. Pro-Tip: Использование закладных гаек (Captive Nuts)
Вкручивание саморезов в пластик — рабочий, но не самый надёжный метод, особенно если предполагается частая сборка/разборка. Гораздо прочнее использовать стандартный болт и гайку. Чтобы гайка не прокручивалась при затягивании, для неё делают специальное шестигранное гнездо — nut trap.
bd_warehouse делает создание таких гнёзд тривиальной задачей. Вместо бобышек на крышке мы сделаем посадочные места под гайки.
Взгляните на раздел 3.4.1 Captive Nuts (стр. 20) в той же документации LINK.
Давайте модифицируем крышку. Вместо добавления бобышек мы вычтем из неё ClearanceHole с параметром captive_nut=True.
# Пример создания крышки с гнёздами под гайки
from bd_warehouse.fastener import HexNut
# ...
fastener_nut = HexNut(size="M3-0.5", fastener_type="iso4032")
# ...
# Вместо with BuildPart() as lid_with_bosses:
with BuildPart() as lid_with_nut_traps:
add(lid_final) # Наша полая крышка с фальцем
# Переворачиваемся на внутреннюю сторону крышки
with BuildSketch(Plane.XY.offset(base_height)):
with Locations(boss_locations):
# Создаём гнездо под гайку и сквозное отверстие
ClearanceHole(fastener=fastener_nut, captive_nut=True)
lid_final_nuts = lid_with_nut_traps.part
В этом случае основание по-прежнему имеет обычные сквозные отверстия ClearanceHole под винт. Винт проходит через основание и вкручивается в гайку, надёжно зафиксированную в крышке. Это чрезвычайно прочное и профессиональное решение.
2. Ответные элементы для защёлок
Теперь вернёмся к защёлкам из предыдущего урока. У нас есть крышка lid_final_with_snaps. Нам нужно сделать в основании (base_final) пазы, за которые будут цепляться крючки защёлок.

Логика здесь проста: мы должны создать "режущий инструмент" (cutting tool) по форме крючка защёлки (с небольшим зазором) и вычесть его из стенки основания.
Продолжим работу в файле enclosure_v11.py, используя код с защёлками из прошлого урока.
2.1. Расчёт геометрии паза
Положение и размер паза полностью определяются геометрией и расположением защёлки.
- Расположение: Паз должен находиться точно там, где крючок защёлки входит в стенку основания.
- Размер: Паз должен быть чуть больше крючка, чтобы обеспечить зазор для лёгкого защёлкивания.
Добавим новый параметр для зазора:
# ... после параметров защёлок ...
snap_slot_clearance = 0.3 # Зазор для паза защёлки
2.2. Создание пазов в основании
Мы будем использовать тот же snap_locations, что и для размещения защёлок, но применим его к основанию для создания вырезов.
# ... после создания base_final и snap_part ...
# ================ СОЗДАНИЕ ПАЗОВ В ОСНОВАНИИ ================
with BuildPart() as base_with_slots:
add(base_final)
# Размеры "режущего инструмента"
cutter_width = snap_width + 2 * snap_slot_clearance
cutter_depth = snap_hook_depth + snap_slot_clearance
cutter_height = snap_hook_depth * 2 # С запасом, чтобы точно прорезать
# Создаём режущий инструмент
cutter = Box(
length=cutter_width,
width=cutter_depth,
height=cutter_height,
align=(Align.CENTER, Align.MIN, Align.MIN)
)
# Рассчитываем Y-координату для размещения на внутренней стенке основания
# Позиционируем режущий инструмент так, чтобы он "врезался" в стенку изнутри
y_pos_base = enclosure_dimensions.Y / 2 - wall_thickness
# Высота, на которой крючок входит в стенку
z_pos = base_height - snap_hook_depth / tan(radians(45))
# Создаём два положения для вырезов
slot_locations = [
Location(Vector(0, y_pos_base, z_pos), rotation=Rotation(0, 0, 90)),
Location(Vector(0, -y_pos_base, z_pos), rotation=Rotation(0, 0, -90)),
]
for loc in slot_locations:
add(cutter, location=loc, mode=Mode.SUBTRACT)
base_final_snaps = base_with_slots.part
Разбор кода:
- Мы создаём
Boxв качестве режущего инструмента. Его размеры немного больше, чем у крючка защёлки, за счётsnap_slot_clearance. - Мы вычисляем
y_pos_base, чтобы расположить режущий инструмент у внутренней стенки основания.align=(Align.CENTER, Align.MIN, Align.MIN)помогает правильно его сориентировать: центр по X, а по Y он будет "расти" наружу, прорезая стенку. z_posвычисляется так, чтобы паз оказался на той же высоте, что и зацеп на защёлке.- Мы используем два
Locationс поворотами на 90 и -90 градусов, чтобы правильно сориентироватьcutterвдоль стенок. - В цикле вычитаем
cutterиз основания.
Теперь обновите блок визуализации:
if "show_object" in locals():
show_object(base_final_snaps, name="base_with_slots")
show_object(lid_final_with_snaps, name="lid_with_snaps")
Запустив скрипт, вы увидите основание с аккуратными пазами, идеально совпадающими с защёлками на крышке.
Проверьте себя!
Представьте, что после тестовой печати защёлка входит в паз слишком туго. Какой один параметр вы бы изменили в коде, чтобы увеличить зазор и облегчить сборку?
Показать ответ
Самый простой и правильный способ — увеличить значение параметра snap_slot_clearance. Например, с 0.3 до 0.4. Это параметрически увеличит размеры режущего инструмента (cutter) и, соответственно, размер паза, не требуя изменения остальной логики.
Ключевые выводы
- Мы научились создавать ответные элементы для двух типов соединений: винтового и защёлок.
- Библиотека
bd_warehouse— мощный инструмент для работы со стандартным крепежом, который позволяет писать чистый, декларативный код и избегать ручного расчёта размеров отверстий. - Мы освоили профессиональный приём — создание гнёзд для закладных гаек (
captive_nut), что значительно повышает надёжность соединения. - Для создания ответного паза под сложный элемент (защёлку) удобно использовать простой "режущий инструмент" (
Box), размеры и положение которого параметрически связаны с исходным элементом.
Что дальше?
В этом уроке мы использовали "магические числа" для зазоров (например, snap_slot_clearance = 0.3). Хотя это сработало, в реальном мире идеального совпадения размеров не бывает из-за особенностей FDM-печати. В следующем, последнем практическом уроке этого модуля, мы системно подойдём к вопросу допусков (tolerances) и научимся проектировать детали так, чтобы они гарантированно собирались правильно после печати.