Самодельный 3-осевой станок с ЧПУ (VMC): постройте точную фрезерную машину
Компоненты и материалы
- Arduino Nano R3
- Raspberry Pi 3 Model B
- Драйвер шагового двигателя A4988 (SparkFun)
- Шилд Adafruit Arduino Nano 4-шаговый 1-DC RepRap
- Алюминиевые профили OpenBuilds (2020 T-slot), L-образные уголки, 스페йсеры, M5 T-гайки, винты M5 низкого профиля, M3 цилиндрические, зажимные винты, шайбы для T-слотов 15×5×2 мм
- Антибэклеш гайка для трапецеидального винта 8 мм
- Плита крепления трапецеидального винта под NEMA 17
- Трапецеидальный винт 8 мм метрический
- Шаровые подшипники 625 2RS 5×16×5
- Гладкие валы (smooth rod)
- BLDC вентилятор охлаждения
- DC двигатель (шпиндель)
- Сервопривод Tower Pro MG996R
- Драйвер двигателей L293D (Texas Instruments)
- Блок питания 12В/5А
Необходимые инструменты
- Отвёртки
- Дрель
- Дремель / ротационный резак
- Клеевой пистолет
- Паяльник
- Угловая шлифмашина (болгарка)
Программное обеспечение и сервисы
- Linux
- ArtCAM (генерация G-кода)
- GRBL 0.9 (прошивка управления)
- Autodesk Fusion 360 (CAD/CAM)
- Arduino IDE
- Google Android Things
- VNC (удалённый доступ)
О проекте
Наш прототип основан на IoT-архитектуре, обеспечивая масштабируемость и управление практически из любой точки с доступом в интернет. Это 3-осевой станок с ЧПУ рабочей зоной 250×240 мм (общая рама 350×350 мм), работающий на открытом ПО GRBL. Аппаратная часть также преимущественно открыта, что сильно снижает стоимость. Станок полностью собран из локально закупленных материалов и способен обрабатывать дерево, пластики, твёрдую резину, эпоксидные смолы и т.д. Основная цель — низкобюджетное производство печатных плат. Также возможна гравировка по дереву. В планах — добавка лазерной обработки.
Управление сейчас возможно по локальной беспроводной сети, далее планируется распределённая веб-модель для создания экосистемы из множества таких устройств. Возможности неограничены.
Рама выполнена из алюминиевых профилей 2020 с T-образными пазами и L-соединителями. Мощность шпинделя ~80–85 Вт при 2000 об/мин (DC с регулировкой). Движение по всем осям — трапецеидальные винты с наводящими гладкими валами и радиальными шарикоподшипниками для плавности. Минимальный шаг винта 0.8 мм/об, при 2000 шагов/об (микрошаг) разрешение 1600 шагов/мм по всем осям. Габариты ~430×430×330 мм, рабочая зона ~270×170×65 мм, позиционная точность 0.04 мм. Портал Z-оси 3D-печатан с двойными наводящими стержнями, ход 4.8 см с установленной фрезой. Используются V-образные резаки 45° для травировки плат и концевые фрезы 0.2–1.8 мм. Двигатели NEMA 23 с током до 3А на ось, драйверы A4988 2.5А с микрошагом 1/16 — высокая точность по низкой цене.
IoT-часть: управление с мобильного/планшетного экрана (HMI) из любой точки с интернетом. Raspberry Pi + роутер (безопасность) + веб-сервер для беспроводного интерфейса. Планируется сбор телеметрии: крутящий момент, ток, вибрация, шум — для реального времени аналитики на веб-платформе (Industrial IoT / Industry 4.0).
Код
Пример G-кода (VHDL)
(Scribbled version of C:\Users\ABDERR~1\AppData\Local\Temp\ink_ext_XXXXXX.svgISF45X @ 3000.00)
( unicorn.py --tab="plotter_setup" --pen-up-angle=50 --pen-down-angle=30 --start-delay=160 --stop-delay=150 --xy-feedrate=3000 --z-feedrate=150 --z-height=0 --finished-height=0 --register-pen=true --x-home=0 --y-home=0 --num-copies=1 --continuous=false --pause-on-layer-change=true C:\Users\ABDERR~1\AppData\Local\Temp\ink_ext_XXXXXX.svgISF45X )
G21 (metric ftw)
G90 (absolute mode)
G92 X0.00 Y0.00 Z0.00 (you are here)
M300 S30 (pen down)
G4 P160 (wait 160ms)
M300 S50 (pen up)
G4 P150 (wait 150ms)
M18 (disengage drives)
M01 (Was registration test successful?)
M17 (engage drives if YES, and continue)
M01 (Plotting layer 'Calque 1')
(Polyline consisting of 29 segments.)
G1 X16.85 Y4.97 F3000.00
M300 S30.00 (pen down)
G4 P160 (wait 160ms)
G1 X14.07 Y5.98 F3000.00
G1 X13.33 Y6.40 F3000.00
... (файл усечён, полная версия доступна для скачивания)
Код управления шаговыми двигателями (Arduino)
//AMIT
#include <Servo.h>
#include <AFMotor.h>
#define LINE_BUFFER_LENGTH 512
char STEP = MICROSTEP;
const int penZUp = 115;
const int penZDown = 83;
const int penServoPin = 10;
const int stepsPerRevolution = 48;
Servo penServo;
AF_Stepper myStepperY(stepsPerRevolution,1);
AF_Stepper myStepperX(stepsPerRevolution,2);
struct point { float x; float y; float z; };
struct point actuatorPos;
float StepInc = 1;
int StepDelay = 0;
int LineDelay = 0;
int penDelay = 50;
float StepsPerMillimeterX = 100.0;
float StepsPerMillimeterY = 100.0;
float Xmin = 0, Xmax = 40, Ymin = 0, Ymax = 40, Zmin = 0, Zmax = 1;
float Xpos = Xmin, Ypos = Ymin, Zpos = Zmax;
boolean verbose = false;
void setup() {
Serial.begin(9600);
penServo.attach(penServoPin);
penServo.write(penZUp);
delay(100);
myStepperX.setSpeed(600);
myStepperY.setSpeed(600);
Serial.println("Mini CNC Plotter alive and kicking!");
Serial.print("X range is from "); Serial.print(Xmin); Serial.print(" to "); Serial.print(Xmax); Serial.println(" mm.");
Serial.print("Y range is from "); Serial.print(Ymin); Serial.print(" to "); Serial.print(Ymax); Serial.println(" mm.");
}
void loop() {
delay(100);
char line[LINE_BUFFER_LENGTH];
char c;
int lineIndex;
bool lineIsComment, lineSemiColon;
lineIndex = 0; lineSemiColon = false; lineIsComment = false;
while (1) {
while (Serial.available()>0) {
c = Serial.read();
if ((c == '\n') || (c == '\r')) {
if (lineIndex > 0) {
line[lineIndex] = '\0';
if (verbose) { Serial.print("Received : "); Serial.println(line); }
processIncomingLine(line, lineIndex);
lineIndex = 0;
} else { }
lineIsComment = false; lineSemiColon = false;
Serial.println("ok");
} else {
if ((lineIsComment) || (lineSemiColon)) {
if (c == ')') lineIsComment = false;
} else {
if (c <= ' ') { }
else if (c == '/') { }
else if (c == '(') { lineIsComment = true; }
else if (c == ';') { lineSemiColon = true; }
else if (lineIndex >= LINE_BUFFER_LENGTH-1) { Serial.println("ERROR - lineBuffer overflow"); lineIsComment = false; lineSemiColon = false; }
else if (c >= 'a' && c <= 'z') { line[lineIndex++] = c-'a'+'A'; }
else { line[lineIndex++] = c; }
}
}
}
}
}
void processIncomingLine(char* line, int charNB) {
int currentIndex = 0;
char buffer[64];
struct point newPos; newPos.x = 0.0; newPos.y = 0.0;
while (currentIndex < charNB) {
switch (line[currentIndex++]) {
case 'U': penUp(); break;
case 'D': penDown(); break;
case 'G':
buffer[0] = line[currentIndex++];
buffer[1] = '\0';
switch (atoi(buffer)) {
case 0: case 1:
char* indexX = strchr(line+currentIndex, 'X');
char* indexY = strchr(line+currentIndex, 'Y');
if (indexY <= 0) { newPos.x = atof(indexX + 1); newPos.y = actuatorPos.y; }
else if (indexX <= 0) { newPos.y = atof(indexY + 1); newPos.x = actuatorPos.x; }
else { newPos.y = atof(indexY + 1); indexY = '\0'; newPos.x = atof(indexX + 1); }
drawLine(newPos.x, newPos.y);
actuatorPos.x = newPos.x; actuatorPos.y = newPos.y;
break;
}
break;
case 'M':
buffer[0] = line[currentIndex++];
buffer[1] = line[currentIndex++];
buffer[2] = line[currentIndex++];
buffer[3] = '\0';
switch (atoi(buffer)) {
case 300:
char* indexS = strchr(line+currentIndex, 'S');
float Spos = atof(indexS + 1);
if (Spos == 30) penDown();
if (Spos == 50) penUp();
break;
case 114:
Serial.print("Absolute position : X = "); Serial.print(actuatorPos.x);
Serial.print(" - Y = "); Serial.println(actuatorPos.y);
break;
default:
Serial.print("Command not recognized : M"); Serial.println(buffer);
}
}
}
}
void drawLine(float x1, float y1) {
if (verbose) { Serial.print("fx1, fy1: "); Serial.print(x1); Serial.print(","); Serial.print(y1); Serial.println(""); }
if (x1 >= Xmax) x1 = Xmax;
if (x1 <= Xmin) x1 = Xmin;
if (y1 >= Ymax) y1 = Ymax;
if (y1 <= Ymin) y1 = Ymin;
if (verbose) { Serial.print("Xpos, Ypos: "); Serial.print(Xpos); Serial.print(","); Serial.print(Ypos); Serial.println(""); }
if (verbose) { Serial.print("x1, y1: "); Serial.print(x1); Serial.print(","); Serial.print(y1); Serial.println(""); }
x1 = (int)(x1*StepsPerMillimeterX);
y1 = (int)(y1*StepsPerMillimeterY);
float x0 = Xpos; float y0 = Ypos;
long dx = abs(x1-x0); long dy = abs(y1-y0);
int sx = x0<x1 ? StepInc : -StepInc;
int sy = y0<y1 ? StepInc : -StepInc;
long i; long over = 0;
if (dx > dy) {
for (i=0; i<dx; ++i) {
myStepperX.onestep(sx,STEP);
over+=dy;
if (over>=dx) { over-=dx; myStepperY.onestep(sy,STEP); }
delay(StepDelay);
}
} else {
for (i=0; i<dy; ++i) {
myStepperY.onestep(sy,STEP);
over+=dx;
if (over>=dy) { over-=dy; myStepperX.onestep(sx,STEP); }
delay(StepDelay);
}
}
if (verbose) { Serial.print("dx, dy:"); Serial.print(dx); Serial.print(","); Serial.print(dy); Serial.println(""); }
if (verbose) { Serial.print("Going to ("); Serial.print(x0); Serial.print(","); Serial.print(y0); Serial.println(")"); }
delay(LineDelay);
Xpos = x1; Ypos = y1;
}
void penUp() {
penServo.write(penZUp); delay(penDelay); Zpos=Zmax;
digitalWrite(15, LOW); digitalWrite(16, HIGH);
if (verbose) Serial.println("Pen up!");
}
void penDown() {
penServo.write(penZDown); delay(penDelay); Zpos=Zmin;
digitalWrite(15, HIGH); digitalWrite(16, LOW);
if (verbose) Serial.println("Pen down.");
}
Библиотека AFMotor (Arduino)
// Adafruit Motor shield library
// copyright Adafruit Industries LLC, 2009
// this code is public domain, enjoy!
#if (ARDUINO >= 100)
#include "Arduino.h"
#else
#if defined(__AVR__)
#include <avr/io.h>
#endif
#include "WProgram.h"
#endif
#include "AFMotor.h"
static uint8_t latch_state;
#if (MICROSTEPS == 8)
uint8_t microstepcurve[] = {0, 50, 98, 142, 180, 212, 236, 250, 255};
#elif (MICROSTEPS == 16)
uint8_t microstepcurve[] = {0, 25, 50, 74, 98, 120, 141, 162, 180, 197, 212, 225, 236, 244, 250, 253, 255};
#endif
// ... (полный код библиотеки доступен в исходнике)
Детали для 3D печати / фрезеровки
Файлы: cnc_contour_SNa09InGkY.gm1, cnc_coppertop_PIxEVyoMWI.gtl
Схемы
Несколько изображений схем доступны в разделе Schematics.
Производственный процесс
- Сделай свой LUMAZOID: DIY Arduino музыкальный визуализатор
- Умный вольтметр на Arduino и смартфоне — простое DIY-проект
- Сборка DIY VR-скейтборда на Arduino, Google Cardboard и Bluetooth
- Трехфункциональный станок с ЧПУ — Премиум-апгрейд
- Умный столик-журнальный с CNC-zen-садом: Создайте свою интеллектуальную мебель
- DIY Arduino Radionics EMR Treatment Device — радионическое устройство
- Построение полнофункционального станка с ЧПУ на базе Arduino Nano R3
- DIY 2D гоночный симулятор движения на Arduino Nano и сервоприводах SG90
- Вертикальная обработка и ЦНЦ ВМЦ: подробное введение
- Мини VMC станок: компактное ЧПУ фрезерование для малых проектов