Skip to main content

Someone wanted to learn this too, so Grasp built them a personal learning path.

You can keep exploring this one or create your own

"Размещение компонентов и вырезов в корпусе по размерам платы"

Recommended resources

The freely available resources this lesson will direct you to.

Здравствуйте!

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

Сегодня мы переходим от внешней формы к внутреннему содержанию. Наша цель — интегрировать монтажные стойки и вырезы для портов в модель корпуса на основе размеров платы. Мы превратим сплошной блок в функциональное основание корпуса, добавив ключевые элементы для крепления платы и подключения внешних устройств. Этот урок — важный шаг к получению готового к печати изделия.

Мы будем действовать поэтапно:

  1. Создадим полую заготовку для основания корпуса.
  2. Добавим модель печатной платы в качестве визуального и геометрического ориентира.
  3. Спроектируем и разместим монтажные стойки для фиксации платы.
  4. Прорежем отверстия в стенках корпуса для портов.
Конструкция корпуса для печатной платы
Наша сегодняшняя цель — создать основание корпуса ("Enclosure Body") с элементами для крепления печатной платы ("PCB") и вырезами для портов, как показано в этой концепции.

1. Подготовка: от монолита к полому основанию

Начнем с модификации кода из предыдущего урока. Нам больше не нужен сплошной блок. Вместо него мы создадим только нижнюю часть корпуса (основание) и сделаем ее полой.

Самый простой способ сделать полый короб — вычесть из внешнего блока внутренний, меньшего размера.

Давайте обновим нашу функцию. Теперь она будет создавать именно полое основание, а не весь блок целиком. Обратите внимание, как мы используем уже знакомые вам булевы операции и позиционирование.

from build123d import *

def create_enclosure_base(
    pcb_width: float,
    pcb_length: float,
    pcb_height: float,
    wall_thickness: float = 2.0,
    side_clearance: float = 1.0,
    bottom_clearance: float = 3.0,
) -> Part:
    """Создает параметрическое полое основание корпуса."""

    # Внутренние размеры, необходимые для размещения платы
    inner_width = pcb_width + 2 * side_clearance
    inner_length = pcb_length + 2 * side_clearance
    
    # Высота основания: зазор снизу + высота стоек + толщина дна
    # Предположим, плата будет на стойках высотой bottom_clearance
    base_height = bottom_clearance + wall_thickness

    # Внешние размеры основания
    outer_width = inner_width + 2 * wall_thickness
    outer_length = inner_length + 2 * wall_thickness
    
    # Внутренний объем, который нужно вычесть
    hollow_width = inner_width
    hollow_length = inner_length
    hollow_height = base_height # Вычитаем на всю высоту

    with BuildPart() as enclosure_base:
        # Создаем внешний блок
        Box(outer_width, outer_length, base_height)
        
        # Создаем и вычитаем внутренний блок
        # Смещаем его вверх на толщину дна
        with Locations(Vector(0, 0, wall_thickness)):
            Box(hollow_width, hollow_length, hollow_height, mode=Mode.SUBTRACT)

    return enclosure_base.part

# ... (остальная часть скрипта будет добавлена ниже)

Мы рассчитали размеры так, чтобы после вычитания у нас остался короб с дном и стенками заданной толщины wall_thickness.

2. Интеграция модели печатной платы (PCB)

Чтобы точно размещать стойки и вырезы, нам нужна сама плата в виде 3D-модели. Мы создадим простую параметрическую модель платы (Box) и разместим её внутри нашего корпуса. Это будет наш главный ориентир.

Добавим функцию для создания платы и вызовем ее в основном скрипте.

def create_pcb_model(width: float, length: float, height: float) -> Part:
    """Создает простую модель печатной платы."""
    with BuildPart() as pcb_model:
        Box(width, length, height)
    return pcb_model.part

# ... (в блоке if __name__ == "__main__")

# 1. Генерируем основание корпуса
base = create_enclosure_base(...)

# 2. Генерируем модель платы
pcb = create_pcb_model(...)

# 3. Позиционируем плату внутри корпуса
# Она должна "парить" на высоте стоек над дном
pcb_z_position = wall_thickness + bottom_clearance
with BuildPart() as final_assembly:
    add(base)
    # Делаем плату полупрозрачной для наглядности
    add(pcb, at=Location(Vector(0, 0, pcb_z_position)), a=0.5, name="pcb")

Теперь при просмотре модели вы будете видеть и корпус, и плату внутри него, что сильно упростит дальнейшее проектирование.

3. Проектирование и размещение монтажных стоек (Standoffs)

Стойки — это цилиндрические выступы на дне корпуса, на которые устанавливается плата. В них делаются отверстия для крепежных винтов.

Для их создания нам понадобятся координаты крепежных отверстий на вашей реальной плате. Их нужно измерить относительно центра платы.

Давайте определим функцию add_standoffs, которая будет добавлять стойки к основанию корпуса. Она будет использовать циклы для итерации по списку координат, что является отличной практикой для создания повторяющихся элементов.

Introductory Examples - Build123d

В официальной документации есть примеры, которые демонстрируют ключевые техники для этой задачи. Например, как создавать объекты в нескольких точках с помощью списка Locations.

Обратите внимание на раздел 'Using Point Lists'. В нем показано, как использовать Locations со списком кортежей координат для создания нескольких окружностей. Мы применим этот же принцип для создания стоек.

Вот как может выглядеть функция и ее вызов:

def add_standoffs(
    base_part: Part,
    hole_locations: list[tuple[float, float]],
    standoff_height: float,
    standoff_diameter: float,
    pilot_hole_diameter: float,
) -> Part:
    """Добавляет монтажные стойки ко дну корпуса."""
    
    with BuildPart(base_part) as part_with_standoffs:
        # Выбираем внутреннюю поверхность дна как рабочую плоскость
        bottom_face = part_with_standoffs.faces().sort_by(Axis.Z)[0]
        
        with BuildSketch(bottom_face):
            # Размещаем стойки по заданным координатам
            with Locations(*hole_locations):
                Circle(standoff_diameter / 2)
        
        # Выдавливаем стойки вверх
        extrude(amount=standoff_height)

        # Добавляем пилотные отверстия для винтов
        with BuildSketch(bottom_face):
            with Locations(*hole_locations):
                Circle(pilot_hole_diameter / 2, mode=Mode.SUBTRACT)

        # Вычитаем отверстия из стоек
        # Глубина чуть меньше высоты стойки, чтобы не просверлить дно
        extrude(amount=standoff_height * 0.8, mode=Mode.SUBTRACT)

    return part_with_standoffs.part

# ... (в блоке if __name__ == "__main__")

# Координаты крепежных отверстий относительно центра платы (X, Y)
# ЗАМЕНИТЕ ЭТИ ЗНАЧЕНИЯ НА РЕАЛЬНЫЕ ИЗМЕРЕНИЯ ВАШЕЙ ПЛАТЫ!
pcb_mounting_holes = [
    (25, 40), (-25, 40),
    (25, -40), (-25, -40)
]

# ...

# Добавляем стойки
base_with_standoffs = add_standoffs(
    base_part=base,
    hole_locations=pcb_mounting_holes,
    standoff_height=bottom_clearance,
    standoff_diameter=5.0,
    pilot_hole_diameter=2.0
)

Мы использовали BuildSketch на внутренней поверхности дна, чтобы гарантировать, что стойки начинаются именно оттуда. Затем с помощью Locations и цикла мы создали и выдавили цилиндры стоек и отверстия в них.

4. Создание вырезов для портов

Вырезы для портов (USB, Ethernet и т.д.) создаются по тому же принципу, что и стойки, но на боковых стенках корпуса. Мы определим параметры каждого порта (размеры и положение) и в цикле создадим для них вырезы.

Примеры вырезов на печатных платах
Вырезы могут иметь разную форму. Мы начнем с простых прямоугольников, но тот же метод позволит создавать и более сложные контуры.

Ключевая техника здесь — выбор нужной грани (стенки) и создание на ней эскиза.

Introductory Examples - Build123d

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

Изучите раздел 'Creating Workplanes on Faces'. Он показывает, как выбрать грань, создать на ней эскиз (BuildSketch) и затем вырезать (extrude с отрицательным значением) геометрию. Это именно то, что нам нужно для создания вырезов.

Мы можем представить порты в виде структурированного списка, что соответствует вашему опыту в data engineering. Это делает код чистым и легко расширяемым.

def add_port_cutouts(
    base_part: Part,
    ports: list[dict],
    pcb_z_pos: float,
    wall_thickness: float,
) -> Part:
    """Вырезает отверстия для портов в стенках корпуса."""
    
    with BuildPart(base_part) as part_with_cutouts:
        for port in ports:
            # Выбираем нужную стенку
            if port["wall"] == "front": # Стена по +Y
                wall_face = part_with_cutouts.faces().filter_by(Axis.Y).last
            elif port["wall"] == "back": # Стена по -Y
                wall_face = part_with_cutouts.faces().filter_by(Axis.Y).first
            # Добавьте 'left' и 'right' по аналогии, если нужно
            else:
                continue

            with BuildSketch(wall_face):
                # Рассчитываем центр выреза на стенке
                # port["center"][0] -> X-координата центра порта на плате
                # port["center"][1] -> Z-координата центра порта на плате (относительно низа платы)
                cutout_center = (
                    port["center"][0], 
                    pcb_z_pos + port["center"][1] - part_with_cutouts.part.bounding_box().min.Z
                )
                Rectangle(*port["size"], at=cutout_center)
            
            # Вырезаем отверстие "насквозь"
            extrude(amount=-wall_thickness, mode=Mode.SUBTRACT)

    return part_with_cutouts.part

# ... (в блоке if __name__ == "__main__")

# Описание портов на вашей плате
# ЗАМЕНИТЕ НА РЕАЛЬНЫЕ ДАННЫЕ!
# 'wall': на какой стенке корпуса порт ('front' -> +Y, 'back' -> -Y)
# 'center': (X, Z) координаты центра порта относительно центра низа платы
# 'size': (width, height) размер выреза
ports_definitions = [
    {
        "wall": "back",
        "center": (0, 6), # по центру оси Х, на 6 мм выше низа платы
        "size": (20, 10) # для USB-C, например
    }
]

# ...

# Добавляем вырезы
final_base = add_port_cutouts(
    base_part=base_with_standoffs,
    ports=ports_definitions,
    pcb_z_pos=pcb_z_position,
    wall_thickness=WALL_THICKNESS # Используем глобальную константу
)

Итоговый код

Вот как будет выглядеть ваш основной файл проекта после всех сегодняшних добавлений.

# full_script.py

from build123d import *

# ----------------- ПАРАМЕТРЫ ПРОЕКТА -----------------
# (Эти значения вы будете менять под свою плату и нужды)

# Размеры печатной платы
PCB_WIDTH = 65.0
PCB_LENGTH = 100.0
PCB_HEIGHT = 12.0 # Высота с учетом компонентов

# Параметры корпуса
WALL_THICKNESS = 1.8
SIDE_CLEARANCE = 1.5
BOTTOM_CLEARANCE = 5.0 # Это же будет высота стоек

# Параметры стоек
STANDOFF_DIAMETER = 5.0
PILOT_HOLE_DIAMETER = 2.0

# Координаты крепежных отверстий на плате (относительно центра)
PCB_MOUNTING_HOLES = [
    (25, 40), (-25, 40),
    (25, -40), (-25, -40)
]

# Описание портов
PORTS = [
    {
        "wall": "back", # Стена в направлении -Y
        "center": (0, 6.0), # (X, Z) от центра низа платы
        "size": (12.0, 8.0)
    }
]

# ----------------- ФУНКЦИИ-КОНСТРУКТОРЫ -----------------

def create_enclosure_base(...) -> Part:
    # ... (код функции, как описано выше) ...

def add_standoffs(...) -> Part:
    # ... (код функции, как описано выше) ...

def add_port_cutouts(...) -> Part:
    # ... (код функции, как описано выше) ...
    
def create_pcb_model(width: float, length: float, height: float) -> Part:
    """Создает простую модель печатной платы."""
    with BuildPart() as pcb_model:
        Box(width, length, height)
    return pcb_model.part


# ----------------- ОСНОВНОЙ СКРИПТ СБОРКИ -----------------

if __name__ == "__main__":
    # 1. Создаем полое основание
    base = create_enclosure_base(
        pcb_width=PCB_WIDTH,
        pcb_length=PCB_LENGTH,
        pcb_height=PCB_HEIGHT,
        wall_thickness=WALL_THICKNESS,
        side_clearance=SIDE_CLEARANCE,
        bottom_clearance=BOTTOM_CLEARANCE,
    )

    # 2. Добавляем монтажные стойки
    base_with_standoffs = add_standoffs(
        base_part=base,
        hole_locations=PCB_MOUNTING_HOLES,
        standoff_height=BOTTOM_CLEARANCE,
        standoff_diameter=STANDOFF_DIAMETER,
        pilot_hole_diameter=PILOT_HOLE_DIAMETER,
    )

    # 3. Рассчитываем положение платы для следующих шагов
    pcb_z_position = WALL_THICKNESS + BOTTOM_CLEARANCE
    
    # 4. Добавляем вырезы для портов
    final_base = add_port_cutouts(
        base_part=base_with_standoffs,
        ports=PORTS,
        pcb_z_pos=pcb_z_position,
        wall_thickness=WALL_THICKNESS,
    )

    # 5. Собираем все для визуализации (опционально)
    pcb = create_pcb_model(PCB_WIDTH, PCB_LENGTH, PCB_HEIGHT)
    
    # Чтобы видеть результат в OCP CAD Viewer
    show_object(final_base, name="base")
    show_object(pcb, name="pcb", options={"alpha": 0.6, "color": (0.1, 0.8, 0.2)}, at=Location(Vector(0,0,pcb_z_position)))

    # Экспорт финальной модели
    final_base.export_stl("enclosure_base.stl")
Проверьте себя!

Вам нужно добавить еще один порт (например, для питания) на переднюю стенку ("front") корпуса. Ваши действия в блоке if __name__ == "__main__"?

Показать ответ

Нужно просто добавить еще один словарь в список PORTS. Например:

PORTS = [
    {
        "wall": "back",
        "center": (0, 6.0),
        "size": (12.0, 8.0)
    },
    {
        "wall": "front",
        "center": (20.0, 5.0), # Смещен вправо, на 5 мм от низа платы
        "size": (7.0, 7.0) # Круглый разъем питания в квадратном вырезе
    }
]

Никаких других изменений в коде не потребуется. Функция add_port_cutouts автоматически обработает новый элемент списка и создаст соответствующий вырез на передней стенке. Это и есть мощь параметрического и декларативного подхода.

Ключевые выводы

  • Мы успешно интегрировали в нашу модель два важнейших функциональных элемента: монтажные стойки и вырезы для портов.
  • Мы использовали данные (списки координат и словари) для управления геометрией, что делает код чистым, читаемым и легко расширяемым.
  • Мы активно применяли изученные ранее техники: выбор граней (.faces().filter_by()), создание эскизов на них (BuildSketch(face)), итерацию для создания множественных объектов и булевы операции.
  • Наш проект теперь состоит из нескольких логических функций (create_..., add_...), что является хорошей инженерной практикой.

Что дальше?

У нас есть готовое основание корпуса. Но ему нужна крышка, а также механизм их соединения. В следующем уроке мы займемся именно этим: реализуем механизм соединения (начнем с винтового) для основания и крышки. Мы спроектируем бобышки под винты и соответствующие им элементы на крышке.

Can't find a good explanation? Sign up and we'll make it for you

Sign up