The Jerusalem PostKushner says US working toward 'largest prisoner exchange' of Russia's war in UkraineZDF heuteEntdecken Sie das ZDF-Nachrichtenstudio中国新闻网一张草原奶皮的逆袭:蒙古族老味道跑出富民新速度CBS NewsVan Hollen will challenge Schumer for Senate majority leaderLenta.ruУкраине предрекли гибель без миллионов мигрантовBBC 中文微軟系統全球癱瘓:Outlook、Teams、Copilot受影響The Sydney Morning HeraldBeau Lamarre-Condon trial live updates: Former NSW Police officer, alleged double murderer’s cross-examination continues; grilled by Crown over texts to Jesse BairdNew Straits TimesMessi finalist for third straight MLS MVP awardDaily MailSeedy Russell Brand and Diddy private jet video: As rapist comedian joins rapper behind bars, the truth about their friendship is revealedGMA NewsAustralia's spy agency, federal police hit by Microsoft outageالنهاررئاسة الجمهورية ستتصدى لمن يعمل على "سقوط دم في الشارع"Het Laatste NieuwsPaus ontmoet zes slachtoffers van misbruik binnen de kerk in Peru
The Daily Newsstand · Free, Always
Friday, November 13, 2026

Рисуем шестерни и не только, в программе OpenSCAD

Translate

Здравствуйте меня зовут Дмитрий. Недавно мне попалась на глаза программа OpenSCAD и я хочу сделать небольшой обзор и ещё мы немного порисуем.

Итак OpenSCAD это как вы наверно догадались CAD программа но рисовать в ней нужно не мышкой, а по средствам встроенного скриптового языка. То есть проектирование в ней напоминает програмирование.

И вы спросите, а зачем это нужно не проще-ли рисовать по старинке мышкой. Но я похоже разгадал замысел авторов программы. Дело в том что существует определенный круг деталей внешняя форма которых определяется формулами. И как раз для проектирования подобных деталей OpenSCAD подходит как нельзя лучше.

Шестерня с внешним зацеплением.

И первой подобной деталью является шестерня. Вот пример который я написал:

Скрытый текст
// To activate animation, select "View->Animate" from the
// menu; this will cause three fields to appear
// underneath the Preview console: Time, FPS & Steps.
// To commence animation, enter values into the FPS and Steps input
//Example FPS = 60 Steps = 120
// For set initial position uncomment
//$t=0;

//Fist gear...............................................
Teath = 20;
M = 1;
PressAng = 20;
C = 0.25;

DPeatch = Teath * M;
RPeatch = DPeatch/2;
DMain = DPeatch * cos(PressAng);

St = PI * M/2; // Teeth thickness by diameter
Stx = DPeatch * sin(St*(180/PI)/DPeatch); // Teeth thickness by chord

DIn = DPeatch - (2 + C)* M;
DOut = DPeatch + 2 * M;
ROut = DOut / 2;
d = sqrt(RPeatch^2 - (Stx/2)^2);

Steps = 90;

function ToRad(T) = T * (PI/180);
function Evolv_X(T, MD) = MD/2 * (cos(T) + ToRad(T) * sin(T));
function Evolv_Y(T, MD) = MD/2 * (sin(T) - ToRad(T) * cos(T));


FistEvolv_temp = [ for (a = [0 : Steps]) 
	if (sqrt((Evolv_X(a, DMain) + RPeatch - Evolv_X(20, DMain))^2 + (Evolv_Y(a, DMain) - Evolv_Y(20, DMain) - Stx/2)^2) < ROut)  // Cut pionts outside gear
		[Evolv_X(a, DMain) + RPeatch - Evolv_X(20, DMain), Evolv_Y(a, DMain) - Evolv_Y(20, DMain) - Stx/2]];

FistEvolv = concat([[d - (2 + 2)* M/2 , - Stx/2]], FistEvolv_temp);
	
SecondEvolv = [ for(variable = FistEvolv)  [variable.x, -variable.y]];

FistEvolvB = [ for(a =[0 : (len(FistEvolv) - 1)])  a];
	
SecondEvolvB = [ for(a =[0 : (len(SecondEvolv) - 1)])
	2 * len(SecondEvolv) - a - 1];


rotate ([0, 0, $t*360])
linear_extrude(height = 2 * M, center = true, convexity = 10, twist = 0) 
	union() 
	{
		for (i = [0 : (Teath - 1)])
			rotate([0,0,(360/Teath) * i]) 
				polygon(concat(FistEvolv, SecondEvolv),[concat(FistEvolvB, SecondEvolvB)]); 
			circle(d = DIn);
	}

//Second Gear.........................................................

Teath_2 = 30;

DPeatch_2 = Teath_2 * M;
RPeatch_2 = DPeatch_2 / 2;
DMain_2 = DPeatch_2 * cos(PressAng);


Stx2 = DPeatch_2 * sin(St*(180/PI)/DPeatch_2); // Teeth thickness by chord

DIn2 = DPeatch_2 - (2 + C)* M;
DOut2 = DPeatch_2 + 2 * M;
d2 = sqrt(RPeatch_2^2 - (Stx2/2)^2);

FistEvolv_temp2 = [ for (a = [0 : Steps]) 
	if (sqrt((Evolv_X(a, DMain_2)+ RPeatch_2 - Evolv_X(20, DMain_2))^2 + (Evolv_Y(a, DMain_2) - Evolv_Y(20, DMain_2) - Stx2/2)^2) < DOut2 / 2)  // Cut pionts outside gear
		[Evolv_X(a, DMain_2)+ RPeatch_2 - Evolv_X(20, DMain_2), Evolv_Y(a, DMain_2) - Evolv_Y(20, DMain_2) - Stx2/2]];

FistEvolv2 = concat([[d2 - (2 + 2)* M/2 , - Stx2/2]],FistEvolv_temp2);
	
SecondEvolv2 = [ for(variable = FistEvolv2)
	[variable.x, -variable.y]];

FistEvolvB2 = [ for(a =[0 : (len(FistEvolv2) - 1)]) a];
	
SecondEvolvB2 = [ for(a =[0 : (len(SecondEvolv2) - 1)])
	2 * len(SecondEvolv2) - a - 1];

k = Teath / Teath_2;
offset = (360/(2*Teath_2));

color( "Lime", 1.0 )
translate([DPeatch/2 + DPeatch_2/2, 0, 0])
	rotate([0,0,k * (-$t*360) + offset])
		linear_extrude(height = 2 * M, center = true, convexity = 10, twist = 0) 
			union() 
			{
				for (i = [0 : (Teath_2 -1)])
					rotate([0,0,(360/Teath_2) * i]) 
						polygon(concat(FistEvolv2, SecondEvolv2),[concat(FistEvolvB2, SecondEvolvB2)]); 
					circle(d = DIn2);
			}

Код нужно вставить в программу. Если выбрать "View->Animate" то можно увидеть анимацию. Правда чтобы она запустилась нужно в поля FPS и Steps ввести значения.

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

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

Steps = 90;

function ToRad(T) = T * (PI/180);
function Evolv_X(T, MD) = MD/2 * (cos(T) + ToRad(T) * sin(T));
function Evolv_Y(T, MD) = MD/2 * (sin(T) - ToRad(T) * cos(T));


FistEvolv_temp = [ for (a = [0 : Steps]) 
	if (sqrt((Evolv_X(a, DMain) + RPeatch - Evolv_X(20, DMain))^2 + (Evolv_Y(a, DMain) - Evolv_Y(20, DMain) - Stx/2)^2) < ROut)  // Cut pionts outside gear
		[Evolv_X(a, DMain) + RPeatch - Evolv_X(20, DMain), Evolv_Y(a, DMain) - Evolv_Y(20, DMain) - Stx/2]];

FistEvolv = concat([[d - (2 + 2)* M/2 , - Stx/2]], FistEvolv_temp);

Мы прокатываем наш воображаемый отрезок от нуля до 90. Чтобы избавится от лишней части эвольвенты. Я использую условный оператор который отсекает все точки, лежащие за внешним диаметром шестерни.

Эвольвента начинается от главного диаметра шестерни и уходит в бесконечность. Но нам нужно 2 эвольвенты, и чтобы получить вторую нужно отразить все точки. Но для этого нужно опустить эвольвенту вниз на половину толщины зуба по главному диаметру. А мы знаем толщину зуба только по делительному. И чтобы сдвинуть делительный диаметр эвольвенты в ноль я нахожу координаты эвольвенты при 20 градусах и вычитаю их. После этого остается только сдвинуть эвольвенту вниз на половину толшины зуба Stx.

Ну а вторую эвольвенту мы получаем просто изменив знак у координаты y.

SecondEvolv = [ for(variable = FistEvolv)  [variable.x, -variable.y]];

Шестерня с внутренним зацеплением.

Ну а если мы создали шестерню с внешним зацепление. То сделать шестерню с внутренним зацеплением не представляет большого труда. Надо просто из цилиндра вырезать форму шестерни. Вот код:

Скрытый текст
// To activate animation, select "View->Animate" from the
// menu; this will cause three fields to appear
// underneath the Preview console: Time, FPS & Steps.
// To commence animation, enter values into the FPS and Steps input
//Example FPS = 60 Steps = 120
// For set initial position uncomment
//$t=0;

//Fist gear...............................................
Teath = 30;
M = 1;
PressAng = 20;
C = 0.25;

DPeatch = Teath * M;
RPeatch = DPeatch/2;
DMain = DPeatch * cos(PressAng);

St = PI * M/2; // Teeth thickness by diameter
Stx = DPeatch * sin(St*(180/PI)/DPeatch); // Teeth thickness by chord

DIn = DPeatch - 1 * M;
DOut = DPeatch + (2 + C) * M;
ROut = DOut / 2;
d = sqrt((DPeatch/2)^2 - (Stx/2)^2);

steps = 90;

function ToRad(T) = T * (PI/180);
function Evolv_X(T, MD) = MD/2 * (cos(T) + ToRad(T) * sin(T));
function Evolv_Y(T, MD) = MD/2 * (sin(T) - ToRad(T) * cos(T));


FistEvolv_temp = [ for (a = [0 : steps]) 
	if (sqrt((Evolv_X(a, DMain) + RPeatch  - Evolv_X(20, DMain))^2 + (Evolv_Y(a, DMain) - Evolv_Y(20, DMain) - Stx/2)^2) <  ROut)  // Cut pionts outside gear
	[Evolv_X(a, DMain)+ RPeatch - Evolv_X(20, DMain),
	Evolv_Y(a, DMain) - Evolv_Y(20, DMain) - Stx/2]];

FistEvolv = concat([[d - (2 + 2)* M/2 , - Stx/2]],FistEvolv_temp);
	
SecondEvolv = [ for(variable = FistEvolv)
	[variable.x, -variable.y]];

FistEvolvB = [ for(a =[0 : (len(FistEvolv) - 1)])  a];
	
SecondEvolvB = [ for(a =[0 : (len(SecondEvolv) - 1)])
	len(SecondEvolv) - a - 1 + len(SecondEvolv)];

rotate ([0, 0, $t*360])
linear_extrude(height = 2 * M, center = true, convexity = 10, twist = 0)
	difference() 
	{
		circle(d = DPeatch + 5 * M);
		for (i = [0 : (Teath -1)])
			rotate([0,0,(360/Teath) * i]) 
				polygon(concat(FistEvolv, SecondEvolv),[concat(FistEvolvB, SecondEvolvB)]); 
		circle(d = DIn);
	}

//Second Gear.........................................................

Teath_2 = 10;

DPeatch_2 = Teath_2 * M;
RPeatch_2 = DPeatch_2 / 2;
DMain2 = DPeatch_2 * cos(PressAng);


Stx_2 = DPeatch_2 * sin(St*(180/PI)/DPeatch_2); // Teeth thickness by chord

DIn_2 = DPeatch_2 - (2 + C)* M;
DOut_2 = DPeatch_2 + 2 * M;  
ROut_2 = DOut_2 / 2;
d2 = sqrt((DPeatch_2/2)^2 - (Stx_2/2)^2);

FistEvolv_temp_2 = [ for (a = [0 : steps]) 
	if (sqrt((Evolv_X(a, DMain2)+ (DPeatch_2/ 2) - Evolv_X(20, DMain2))^2 + (Evolv_Y(a, DMain2) - Evolv_Y(20, DMain2) - Stx_2/2)^2) < DOut_2 / 2)  // Cut pionts outside gear
	[Evolv_X(a, DMain2)+ (DPeatch_2/ 2) - Evolv_X(20, DMain2), Evolv_Y(a, DMain2) - Evolv_Y(20, DMain2) - Stx_2/2]];

FistEvolv_2 = concat([[d2 - (2 + 2)* M/2 , - Stx_2/2]],FistEvolv_temp_2);
	
SecondEvolv_2 = [ for(variable = FistEvolv_2)
	[variable.x, -variable.y]];

FistEvolvB_2 = [ for(a =[0 : (len(FistEvolv_2) - 1)]) a];
	
SecondEvolvB_2 = [ for(a =[0 : (len(SecondEvolv_2) - 1)])
	2 * len(SecondEvolv_2) - a - 1];

k = Teath / Teath_2;
offset = 0;

color( "Lime", 1.0 )
translate([DPeatch/2 - DPeatch_2/2, 0, 0])
	rotate([0,0,k * ($t*360) + offset])
		linear_extrude(height = 2 * M, center = true, convexity = 10, twist = 0) 
			union() 
			{
				for (i = [0 : (Teath_2 -1)])
					rotate([0,0,(360/Teath_2) * i]) 
						polygon(concat(FistEvolv_2, SecondEvolv_2),[concat(FistEvolvB_2, SecondEvolvB_2)]); 
				circle(d = DIn_2);
			}

Циклоидный редуктор

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

Скрытый текст
// To activate animation, select "View->Animate" from the
// menu; this will cause three fields to appear
// underneath the Preview console: Time, FPS & Steps.
// To commence animation, enter values into the FPS and Steps input
//Example FPS = 60 Steps = 120
// For set initial position uncomment
//$t=0;

D = 100;
R = D/2;
Dout = 5;
Drol = 5;
Rrol = Drol/2;
E = 0.75;
I = 39;
N = I + 1;
DHols = 65;
H = 5;


Steps = 360;

function Cycloyd_X(T) = (R * cos(T)) - (Rrol * cos(T + atan(sin((1 - N) * T) / ((R / (E * N)) - cos((1 - N) * T))))) - (E * cos(N * T));
function Cycloyd_Y(T) = (-R * sin(T)) + (Rrol * sin(T + atan(sin((1 - N) * T) / ((R / (E * N)) - cos((1 - N) * T))))) + (E * sin(N * T));

//Cycloyd body.........................................

Cycloyd = [ for (a = [0 : Steps]) 
		[Cycloyd_X(a), Cycloyd_Y(a)]];

translate([E, 0, 0])
rotate([0,0,($t*360)])
linear_extrude(height = H, center = true, convexity = 10, twist = 0)
difference() 
{
    polygon(Cycloyd); 
    circle(d = 20);
    translate([DHols/2, 0, 0])
        circle(d = Dout + 2 * E);
    
    rotate([0,0,120])
        translate([DHols/2, 0, 0])
            circle(d = Dout + 2 * E);

    rotate([0,0,240])
        translate([DHols/2, 0, 0])
            circle(d = Dout + 2 * E);
}

//Rollers body.............................................................
k = I/N;
rotate([0,0, k * ($t*360)])
color( "Lime", 1.0 )
linear_extrude(height = H, center = true, convexity = 10, twist = 0)
union() 
{
    difference() 
    {
        circle(d = D  + 20);
        circle(d = D);
    }
    for (i = [0 : I])
        rotate([0,0,i * 360/N])
            translate([D/2, 0, 0])
                circle(d = Drol);
};

//Vodilo........................................
color( "Aqua", 1.0 )
rotate([0,0,($t*360)])
union() 
{
    hull() 
    {
        translate([0,0,-20]) cylinder(h = 5, d= 15);
        translate([DHols/2,0,-20])cylinder(h = 5, d= 15);
    }
    rotate([0,0,120])
    hull() 
    {
        translate([0,0,-20]) cylinder(h = 5, d= 15);
        translate([DHols/2,0,-20])cylinder(h = 5, d= 15);
    }

    rotate([0,0,240])
    hull() 
    {
        translate([0,0,-20]) cylinder(h = 5, d= 15);
        translate([DHols/2,0,-20])cylinder(h = 5, d= 15);
    }

    translate([DHols/2, 0, -20])
        cylinder(h = 20 + H,d = Dout);
    
    rotate([0,0,120])
        translate([DHols/2, 0, -20])
        
            cylinder(h = 20 + H,d = Dout );

    rotate([0,0,240])
        translate([DHols/2, 0, -20])
            cylinder(h = 20 + H ,d = Dout);
}

Здесь необходимо создать циклоиду. В обычной CAD программе это было-бы сделать очень проблематично, а в OpenSCAD нет никаких проблем.

Циклойдный редуктор с промежуточными телами качения.

Ну и конечно-же после того как мы создали редуктор с внешней циклоидой. Возникает вопрос. А существуют-ли редукторы с внутренней циклоидой. И на удивление я нашел такой редуктор, называется он циклоидный редуктор с промежуточными телами качения. Вот видео, там автор подробно расписывает все преимущества этого редуктора над просто циклоидным. Ну а мы приступаем к рисованию:

Скрытый текст
// To activate animation, select "View->Animate" from the
// menu; this will cause three fields to appear
// underneath the Preview console: Time, FPS & Steps.
// To commence animation, enter values into the FPS and Steps input
//Example FPS = 60 Steps = 120
// For set initial position uncomment
//$t=0;

Dball = 6;
Rball = Dball/2;
I = 17;
Rout = 38;
E = 0.2 * Dball;
ZG = I + 1; //gear ratio
Rin = Rout - 2 * E; 
HC = 2.2 * E; //Separator thickness
Dex = (Rin + E - Dball) * 2;
Dsep = Dex + Dball; //Separator diameter
Dcyclbody = 2 * Rout + 20;
Dsepm = Dex + Dball;
k = (I + 1)/I;
H = 5;

function S(T) = sqrt((Rball + Dex/2) ^ 2 - (E  * cos(ZG * T)) ^ 2);
function L(T) = E * cos(ZG * T) + S(T);
function XI(T) = atan2(E * ZG *  sin(ZG *T),S(T));

function Cycloyd_X(T) = L(T) * sin(T) + Rball * sin(T + XI(T));
function Cycloyd_Y(T) = L(T) * cos(T) + Rball * cos(T + XI(T));

//Cycloyd body.........................................
Steps = 360;

Cycloyd = [ for (a = [0 : Steps]) 
		[Cycloyd_X(a), Cycloyd_Y(a)]];

rotate([0,0,($t*360)])
linear_extrude(height = H, center = true, convexity = 10, twist = 0)
difference() 
{
    circle(d = Dcyclbody);
    polygon(Cycloyd);
}

//Excenric...................................................

color( "Blue", 1.0 )
translate([0, E, 0])

rotate([0,0, k * ($t*360)])
    cylinder( h = H, d = Dex);

//Balls...................................................
color( "Aqua", 1.0 )
translate([0, E, 0])
rotate([0,0, k * ($t*360)])
for (i = [0 : I - 1])
    rotate([0,0,i * 360/I])
        translate([0, Dsepm/2, 0])
            sphere(d = Dball);

//Seporator.........................................................
color( "Lime", 1.0 )
translate([0, E, 0])
rotate([0,0, k * ($t*360)])
linear_extrude(height = H, center = true, convexity = 10, twist = 0)
difference() 
{
    circle(d = Dsep + HC);
    circle(d = Dsep - HC);
    for (i = [0 : I - 1])
        rotate([0,0,i * 360/I])
            translate([0, Dsepm/2 , 0])
                square(Dball,true);
}

Вывод

Конечно есть за что программу поругать. У неё ужасный текстовый редактор. Из-за окна предпросмотра для него остается только пол экрана, поэтому работать в нем невозможно. Я все скрипты писал в VSCodium. Кроме того неудобная работа с массивами. Это проявилось при создании шестерни. Чтобы добавить точку с координатой основания зуба в начало массива пришлось создать новый массив. Кроме того хотелось-бы иметь инструмент для измерения размеров, но все что есть это сетка с координатами.

Но если не смотреть на эти недостатки, то программа просто огонь. Если вас утомили однообразные CAD программы похожие друг на друга как две капли, рекомендую попробовать. Это как глоток свежего воздуха.

View the original on Хабр →

KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.