Самая тоскливая часть проектирования - начало. Хочется заняться самим проектом, а вместо этого ищешь по каталогам готовую модель шкива или в сотый раз рисуешь его сам. На один шкив уходит полчаса, и таких деталей в проекте не одна. Я попробовал заменить мышку скриптом, который строит нужный вариант детали по требованиям. Ниже показываю, что получилось, на примере клиноремённого шкива.
Чего не умеет дерево построения
Разработчики CAD-систем давно решают задачу «одна модель - много вариантов». Для этого в КОМПАС-3D, SolidWorks, Inventor и других зрелых CAD есть переменные, выражения, таблицы параметров и исполнения. В грамотно построенной модели диаметры заданы переменными, число ручьёв определяется количеством экземпляров массива, а размеры канавки берутся из таблицы и перестраиваются при смене типа ремня. Пока меняются только значения, дерево построения справляется хорошо.
Но задачу «получить модель произвольного шкива» одна модель через дерево построения уже не решает. Проблемы начинаются как минимум в следующих местах.
- Таблицы со скачками. Размеры шпонки согласно ГОСТ меняются скачками по диапазонам: вал 22 - паз 6×6, вал 25 - уже 8×7. Угол канавки клинового ремня тоже зависит от диаметра шкива: 34°, 36° или 38°. В переменных это можно сделать ветвлениями или связью с Excel, но обычно конструктор просто смотрит в справочник и вводит числа руками.
- Смена структуры. Шкив со ступицей и диском или монолитный, ступица под шпонку или под втулку Taper-Lock, клиновой ремень или зубчатый. Здесь меняются уже операции в дереве. Исполнения и исключение операций позволяют это решить, но число вариантов перемножается: 2 конструкции × 2 вида посадки × 5 сечений ремня даёт уже 20 исполнений, и каждое нужно заранее построить и потом поддерживать.
- Проверки. Модель перестроилась без ошибок, но стенка над пазом стала 1 мм, а ступица упёрлась в обод. Для дерева это не ошибка: все операции выполнились. Какие условия должны выполняться, знает только конструктор.
Дерево помнит, как деталь построена. Почему паз 8×7, ступица Ø45, а угол 34°, оно не знает: это остаётся в голове конструктора и в справочнике а не в модели.
Значит, описывать деталь надо так, чтобы «почему» хранилось вместе с ней, то есть функцией. На входе требования, на выходе размеры и геометрия. Для клиноремённого шкива на входе тип ремня, число ручьёв, расчётный диаметр, диаметр вала и конструкция: со ступицей или монолитный. Всё остальное рассчитывается:
- ширина обода M = (n − 1)·e + 2·f, где e - шаг канавок, f - расстояние от крайней канавки до торца (ГОСТ 20889);
- профиль и угол канавки берутся из таблицы по сечению ремня и диаметру;
- шпонка - из таблицы по диаметру вала (ГОСТ 23360);
- размер ступицы как производная от диаметра вала.
Функция проверяет ещё и условия, без выполнения которых деталь неработоспособна, даже если геометрия строится:
- расчётный диаметр не меньше минимального для ремня (минимум 90 мм для А и 125 мм для Б);
- стенка ступицы над пазом не тоньше заданной;
- ступица помещается внутри обода с учётом глубины канавок и толщины обода, а у монолитного шкива хватает тела между пазом и дном канавки.
В такой постановке модель хранит условие задачи. Поэтому она может сама определить, что при заданных требованиях решения нет, и показать, где проблема.
Удобнее всего писать такую функцию программой, в которой стандарты шпонок лежат в простом словаре, ручьи строятся циклом, ступица задаёт ветвление, а условие на стенку превращается в проверку с понятным сообщением. Геометрию по-прежнему строит CAD-ядро; программа только говорит ему, что строить.
Шкив в 60 строках
Ниже код на CadQuery: это бесплатная библиотека Python на ядре OpenCascade (том же, что внутри FreeCAD). Она сохраняет модель в STEP, STL и других форматах. Для краткости я взял диск толщиной 8 мм, диаметр ступицы по правилу «1,8 диаметра вала», а в таблицах оставил только нужные строки.
import cadquery as cq
from math import tan, radians
# Канавки клиновых ремней, ГОСТ 20889 (фрагмент)
BELT = {
"A": dict(lp=11.0, b=3.3, h=8.7, e=15.0, f=10.0, d_min=90),
"B": dict(lp=14.0, b=4.2, h=10.8, e=19.0, f=12.5, d_min=125),
}
ANGLE = {"A": [(112, 34), (160, 36), (400, 38)], # (dp до, угол)
"B": [(160, 34), (224, 36), (500, 38)]}
# Шпонки, ГОСТ 23360: (вал до, b, h, t2 — глубина в ступице)
KEY = [(12, 4, 4, 1.8), (17, 5, 5, 2.3), (22, 6, 6, 2.8),
(30, 8, 7, 3.3), (38, 10, 8, 3.3), (44, 12, 8, 3.3)]
def pick(table, x):
for row in table:
if x <= row[0]:
return row[1:]
raise ValueError(f"{x} вне таблицы")
def pulley(dp, n, belt, d, hub=True, rim=6.0, wall=4.0):
g = BELT[belt]
if dp < g["d_min"]:
raise ValueError(f"ремень {belt}: расчётный диаметр не меньше {g['d_min']}")
(alpha,) = pick(ANGLE[belt], dp)
bk, hk, t2 = pick(KEY, d)
W = (n - 1) * g["e"] + 2 * g["f"] # ширина обода
De = dp + 2 * g["b"] # наружный диаметр
r0 = dp / 2 - g["h"] # радиус дна канавки
# профиль тела вращения: X - радиус, Z - ось
if hub: # обод + диск + ступица
Dr = dp - 2 * g["h"] - 2 * rim # внутренний диаметр обода
Dh = round(1.8 * d) # диаметр ступицы
if (Dh - d) / 2 - t2 < wall:
raise ValueError(f"стенка над пазом тоньше {wall} мм")
if Dh > Dr:
raise ValueError(f"ступица Ø{Dh} не входит в обод Ø{Dr:.0f}: "
f"увеличьте dp или задайте hub=False")
left = [(d/2, 0), (Dh/2, 0), (Dh/2, W/2 - 4), (Dr/2, W/2 - 4), (Dr/2, 0)]
right = [(Dr/2, W), (Dr/2, W/2 + 4), (Dh/2, W/2 + 4), (Dh/2, W), (d/2, W)]
else: # монолитный шкив без ступицы
if r0 - d / 2 - t2 < wall:
raise ValueError(f"стенка между пазом и дном канавки тоньше {wall} мм")
left, right = [(d/2, 0)], [(d/2, W)]
k = tan(radians(alpha / 2))
top = g["lp"] / 2 + g["b"] * k # полуширина канавки снаружи
bot = g["lp"] / 2 - g["h"] * k # полуширина канавки по дну
pts = left + [(De/2, 0)]
for i in range(n): # число вершин профиля зависит от n
z = g["f"] + i * g["e"]
pts += [(De/2, z - top), (r0, z - bot), (r0, z + bot), (De/2, z + top)]
pts += [(De/2, W)] + right
body = cq.Workplane("XZ").polyline(pts).close().revolve(360, (0, 0, 0), (0, 1, 0))
key = cq.Workplane("XY").center(0, d/2 + t2 - hk/2).rect(bk, hk).extrude(W)
return body.cut(key)
cq.exporters.export(pulley(100, 3, "A", 25), "pulley.step")
Вот что он выдаёт при разных требованиях (прогнал на CadQuery 2.8):
| Вызов | Что изменилось | Результат |
|---|---|---|
pulley(100, 3, "A", 22) | исходный вариант | паз 6×6, ступица Ø40, обод 50 мм |
pulley(100, 3, "A", 25) | вал 22 → 25 | паз стал 8×7, ступица Ø45 |
pulley(100, 5, "A", 25) | 3 → 5 ручьёв | обод 80 мм, у тела 35 граней вместо 27 |
pulley(100, 3, "B", 25) | ремень А → Б | ошибка: для ремня Б расчётный диаметр не меньше 125 |
pulley(100, 3, "A", 40) | вал 25 → 40 | ошибка: ступица Ø72 не входит в обод Ø71, увеличьте dp или задайте hub=False |
pulley(100, 3, "A", 40, hub=False) | без ступицы | строится монолитный шкив: паз 12×8, 19 граней вместо 27 |
pulley(140, 3, "A", 40) | шкив 100 → 140 | строится со ступицей Ø72: угол канавки 36° вместо 34° |
В пятом примере вал 40 мм не помещается в шкив Ø100 со ступицей, и функция возвращается с ошибкой, в которой названо нарушенное условие: ступица Ø72 не входит в обод Ø71. Дальше решает конструктор: увеличить шкив или сделать его монолитным через вызов той же функции с hub=False
pulley(100, 3, "A", 25) | pulley(100, 5, "A", 25) | pulley(100, 3, "A", 25, hub=False) |
Нативная модель вместо STEP
На выходе скрипта пока STEP. Он передаёт точную геометрию, но для конструктора это чёрный ящик. В нём нет ни дерева построения, ни переменных: поменять диаметр вала или добавить свой элемент можно только поверх импортированного тела, и связь с исходными требованиями при этом теряется.
Для работы нужна обычная параметрическая модель: с деревом, переменными и эскизами. Её можно построить через API, который есть у всех зрелых CAD-систем. Через него программа создаёт эскизы, накладывает ограничения, ставит размеры с выражениями от переменных и выполняет операции. Получается модель, которую можно дорабатывать вручную, как нарисованную мышкой.
Чтобы не описывать шкив дважды, для CadQuery и для КОМПАСа, я разделил описание и построение. Скрипт детали сам ничего не строит. Он записывает план построения: какие эскизы создать, какие в них точки, отрезки, ограничения и размеры-выражения, какие операции выполнить.
Вот сокращённый фрагмент плана клиноремённого шкива (ремень Б, dp 200, вал 35) - шпоночный паз:
{
"vars": [
{"name": "d", "value": 35.0, "note": "отверстие под вал"},
{"name": "b", "value": 10.0, "note": "ширина шпоночного паза",
"source": "ГОСТ 23360: Ø35 → 10 x 3.3", "origin": "standard", "depends": ["d"]},
{"name": "t2", "value": 3.3, "note": "глубина паза в ступице",
"source": "ГОСТ 23360: Ø35 → 10 x 3.3", "origin": "standard", "depends": ["d"]},
…
],
"rules": [{"expr": "d/2 + t2 < Dh/2 - 2", "msg": "шпоночный паз прорезает ступицу"}, …],
"ops": [
…
{"op": "sketch", "id": "S2", "plane": "XY",
"items": [
{"t": "point", "id": "S2.g1", "xy": [0.0, 0.0]},
{"t": "con", "kind": "fixed_point", "obj": "S2.g1"},
{"t": "arc", "id": "S2.g2", "c": [0.0, 0.0], "r": 17.5, …},
{"t": "line", "id": "S2.g4", "p1": [-5.0, -20.8], "p2": [5.0, -20.8]},
{"t": "con", "kind": "concentric", "obj": "S2.g2", "partner": "S2.g1"},
{"t": "con", "kind": "horizontal", "obj": "S2.g4"},
…
{"t": "dim_diam", "circle": "S2.g2", "expr": "d"},
{"t": "dim_line", "line": "S2.g4", "expr": "b"},
{"t": "dim_dist", "p": ["S2.g1", 0], "q": ["S2.g4", 1], "dir": "v", "expr": "d/2+t2"}
],
"dof": [0, 19]},
{"op": "cut_through", "id": "F2", "sketch": "S2"},
{"op": "param_test", "feature": "F2", "changes": [["d", 37.0], …],
"base": {"extents": {"xsize": 35.0, "ymax": 17.5, "ymin": -20.8}},
"new": {"extents": {"xsize": 37.0, "ymax": 18.5, "ymin": -21.8}}}
]
}
У ширины и глубины паза записаны источник (стандарт и найденное по нему значение) и зависимость от диаметра вала. Размеры эскиза заданы выражениями d, b и d/2+t2, и вместе с ограничениями полностью определяют эскиз, а координаты задают только начальное положение. Последняя запись - самопроверка: исполнитель меняет вал с 35 на 37 мм и ждёт, что вырез станет шириной 37 мм, а дно паза уйдёт с 20,8 на 21,8 мм от оси.
Схема работы: один план построения - разные CAD-системы.
План исполняет макрос конкретной системы: читает его и повторяет через API во встроенной среде макросов. Решений он не принимает. Я написал два исполнителя, для CadQuery и для КОМПАС-3D. С помощью Claude Code это заняло около 10 часов работы. Для другой CAD понадобится свой исполнитель, а описания деталей останутся теми же.
Построенную модель программа проверяет. Она считает степени свободы каждого эскиза, затем меняет переменную, перестраивает модель и сравнивает габариты с ожидаемыми. Если после смены числа ручьёв ширина обода осталась прежней или эскиз оказался недоопределён, параметризация не работает, хотя сама модель может выглядеть правильно.
Исходными данными может быть не только описание и стандарты, но и чертёж или чужая STEP-модель: размеры снимаются с них, а деталь всё равно описывается от требований.
Почему так не делали раньше
Описывать детали программой придумали давно: макросы, API и параметрические языки есть в CAD не первое десятилетие. Массовым подход не стал. Чтобы описать хотя бы один шкив, нужно освоить объектную модель конкретной системы: эскизы, ограничения, операции. Окупается это только на семействе однотипных деталей, а один-два шкива проще нарисовать мышкой за полчаса каждый, чем неделю разбираться в API. В крупных проектных организациях такие библиотеки, возможно, есть. В небольшом бюро или на производстве писать их некому.
С языковыми моделями этот расчёт меняется. Задача для ИИ удобная: план построения формализован, а результат проверяется запуском и сравнением с эталоном.
Принимать результат без проверки при этом нельзя. Я прогнал несколько моделей (Kimi, DeepSeek, Qwen) на эталонных деталях. Ошибки повторяются: объекты эскиза не связаны ограничениями, незаданные углы молча приняты равными нулю, самопроверка написана фиктивно.
Поэтому пока я работаю по такой схеме:
- Исходные данные: описание словами или чертёж.
- ИИ составляет ТЗ таблицей параметров. У каждого параметра указан источник: описание, чертёж, стандарт или допущение. Допущения и вопросы вынесены отдельно.
- Человек правит и подтверждает ТЗ.
- ИИ пишет формализованное описание детали. Размеры по стандартам он берёт из справочника.
- Проверка идёт независимо от ИИ: эскизы должны быть полностью определены, модель должна собираться, габарит и отверстия совпадать с ТЗ. Ошибка возвращается ИИ на исправление.
Такой подход применим не для всякой детали. Литьё сложной формы и детали, где форму задаёт дизайн или особый способ изготовления, проще нарисовать мышкой. Фаски и скругления в коде неудобны: нужно объяснить, какое ребро имеется в виду, а мышкой его достаточно указать. С чертежей ИИ пока читает размеры ненадёжно: пропускает строку, путает диаметр с радиусом, дописывает то, чего на чертеже нет. Текстовое описание с размерами работает лучше картинки, и именно поэтому ТЗ показывается человеку до построения.
Практическое применение
Всё это я собрал в сервис kq.matildacnc.com. Там есть шаблоны типовых деталей: шкивов, звёздочек, втулок, фланцев; задаёте параметры и получаете 3D-превью, STEP и макрос, который строит параметрическую модель в КОМПАС-3D. Детали вне шаблонов можно описать словами или чертежом в режиме чата с ИИ. Чат пока не в общем доступе, но как это работает, видно в записи.
Буду благодарен за любые комментарии: к самой идее, к сервису, к тому, какие детали стоит добавить в шаблоны, и к работе макроса на вашей версии КОМПАСа.
Используя функцию входа через сторонние сервисы, Вы соглашаетесь на обработку персональных данных в соответствии с Политикой конфиденциальности