用fischertechnik(慧鱼创意组合模型)和Arduino制作的极坐标绘图机,使用G-code,它可以绘制任何位图或矢量图形。
APP与在线服务
ARDUINO IDE Microsoft Visual Studio 2015
关于这个项目:
基本思路:
“正常”绘图仪有两个线性轴,并用笛卡尔坐标控制。与这些机器不同,极坐标绘图机只具有一个线性轴,第二个轴用旋转板代替。因此,这个绘图机是用极坐标控制的。纸上的任何点都可以用一个角度和一个半径来描述,即点与旋转板的中心之间的距离。
从技术上讲,这个特性会带来一些困难,因为几乎没有任何CNC软件可以处理极坐标。
然而,我解决了困难,开发了我自己的极坐标绘图机:
正在绘制老虎的极坐标绘图机
主要部分:
- 旋转板调整角度φ
- 线性轴到半径R
- 钢笔升降机构
- 基于Arduino Mega的可用微控制器
旋转板
画纸卡在转盘上,通过旋转板,可以调整角度φ,旋转板由NEMA 17步进电机驱动,允许快速准确定位。
旋转板分解视图
直线轴
线性轴主要由fischertechnik组成。这一措施节省了3D打印成本和时间。此外,模型还可以在其他项目中重复使用。
直线轴由NEMA 14步进电机驱动。与NEMA 17电机不同,它具有较小的扭矩,较高的转速,这是必不可少的,因为在螺纹上进行高速齿轮传动。虽然步进电机允许精确定位,但需要一个限位开关来设置校准点的参考点。
控制器
控制器本身可以是一个项目,因为它是一个具有集成电机驱动器的即插即用控制器。它专注于控制fischertechnik硬件,如直流电机和各种不同的传感器。
fischertechnik硬件做的即插即用控制器
软件
如何用位图绘图?
步骤1:在Inkskape中打开位图,对其进行矢量化,并将其导出为 .dxf文件
步骤2:用CAM处理生成G-Code
步骤3:由笛卡尔坐标组成,G-Code必须被传输到极坐标。因此,我写了一个java小程序。
步骤4:将极性G-Code传到绘图机。为了执行命令,Arduino运行G-Code解释器。它控制步进电机,使绘图机在纸上进行绘制。
更多视频
相关代码
plotter_firmaware_3_1.ino (下载22 )#include <MegaDueShield.h>
#define BUFFER_SIZE 1200
#define RAXIS_ZERO 2822 // Kugelschreiber
/* Plotter Firmware 3_1
March, 2017
requires a G-code preprocessor
requires C# App to load G-code
G-code is transferred while drawing
optimized parallelization of serial communication and drawing
optimized serial communication
*/
StepperMotor & nema14 = *shield.getStepper(1);
StepperMotor & nema17 = *shield.getStepper(2);
DCMotor & stift = *shield.getDCMotor(1);
long posn14 = 0;
long posn17 = 0;
uint8_t lastCommand = 0;
boolean complete = false;
uint8_t commands[BUFFER_SIZE];
int commandPos[BUFFER_SIZE][2];
/* G-Code commands:
G00: Rapid positioning
G01: Linear interpolation
G02: Circular interpolation, clockwise
G03: Circular interpolation, counterclockwise
M03: Spindle on: Pen down
M05: Spindle off: Pen up
*/
/* 11 Umdrehungen = 52,3mm
2200 Schritte = 52,3mm
1mm = 41,35338 Schritte
r = 0mm: 2843 Schritte
pos (r) = 2843 - r * 41,35338
*/
long rToPos(float r)
{
return RAXIS_ZERO - (r * 41.885);
}
int rStepsToPos(int steps)
{
return RAXIS_ZERO - steps;
}
/* 2Pi = 1800 Schritte
pos (phi) = phi / 2Pi * 1800
*/
long phiToPos(float phi)
{
while (posn17 >= 1800)
{
posn17 -= 1800;
}
while (posn17 < 0)
{
posn17 += 1800;
}
float posf = phi / 2.0 / PI * 1800;
long pos = (long) posf;
if (pos - posn17 > 900)
{
pos -= 1800;
}
if (posn17 - pos > 900)
{
pos += 1800;
}
return pos;
}
int phiStepsToPos(int steps)
{
while (posn17 >= 1800)
{
posn17 -= 1800;
}
while (posn17 < 0)
{
posn17 += 1800;
}
if (steps - posn17 > 900)
{
steps -= 1800;
}
if (posn17 - steps > 900)
{
steps += 1800;
}
return steps;
}
void homeR()
{
int counter = 0;
while (!digitalRead(D4))
{
long pos = posn14 - 1;
bool t = false;
while (!t) {
t = nema14.stepping(posn14, pos);
}
counter++;
}
posn14 = 0;
//Serial.println("Referenzpunkt");
//Serial.println(counter);
}
void penZ(bool value)
{
if (value)
{
// Pen up
while (digitalRead(C4) != 1)
{
stift.ccw(200);
}
stift.stop();
}
else
{
// Pen down
stift.cw(200);
delay(500);
stift.stop();
}
}
bool findCharacter(char character)
{
if (Serial.available() > 0)
{
char serialCharacter = Serial.read();
if (serialCharacter == character)
{
return true;
}
}
return false;
}
int parseInteger()
{
bool delimiter = false;
int returnInt = 0;
while (!delimiter)
{
while(Serial.available() < 1) {}
int character = (int) Serial.read();
character -= 48;
if(character >= 0 && character <= 9)
{
returnInt *= 10;
returnInt += character;
}
else
{
delimiter = true;
}
}
return returnInt;
}
bool readGCodeLine(uint8_t & command, int arguments[])
{
//int arguments[2];
Serial.print('n');
Serial.print('\n');
Serial.flush();
while (!findCharacter('G')) {}
command = parseInteger();
switch (command) {
case 0:
while (!findCharacter('P')) {}
arguments[0] = parseInteger();
while (!findCharacter('R')) {}
arguments[1] = parseInteger();
break;
case 8:
complete = true;
break;
case 9:
while (!findCharacter('Z')) {}
arguments[0] = parseInteger();
break;
}
}
bool stepwiseReadGCodeLine(uint8_t & milestone, uint8_t & command, int arguments[])
{
switch (milestone)
{
case 0:
Serial.print('n');
Serial.print('\n');
Serial.flush();
milestone++;
return false;
case 1:
if (Serial.available() > 0) {
milestone++;
}
return false;
case 2:
if (findCharacter('G'))
{
milestone++;
}
return false;
case 3:
command = parseInteger();
milestone++;
return false;
case 4:
switch (command)
{
case 0:
milestone = 5;
break;
case 8:
complete = true;
milestone = 8;
break;
case 9:
milestone = 7;
break;
}
return false;
case 5:
if (findCharacter('P'))
{
arguments[0] = parseInteger();
milestone++;
}
return false;
case 6:
if (findCharacter('R'))
{
arguments[1] = parseInteger();
milestone = 8;
}
return false;
case 7:
if (findCharacter('Z'))
{
arguments[0] = parseInteger();
milestone = 8;
}
return false;
case 8:
return true;
}
}
void processGCode()
{
uint8_t command;
int arguments[2];
int nextArguments[2];
uint8_t milestone = 0;
while(!findCharacter('s')) {}
readGCodeLine(command, arguments); // get first G-code line
nema14.wakeUp();
delay(10);
while (!complete)
{
bool t = false;
milestone = 0;
switch (command)
{
case 0:
while (!t) {
t = nema14.stepping(posn14, rStepsToPos(arguments[1]));
t &= nema17.stepping(posn17, phiStepsToPos(arguments[0]));
t &= stepwiseReadGCodeLine(milestone, command, nextArguments);
}
arguments[0] = nextArguments[0];
arguments[1] = nextArguments[1];
break;
case 9:
penZ(arguments[0]);
readGCodeLine(command, arguments);
break;
case 8:
break;
}
}
}
void setup() {
Serial.begin(115200);
delay(1000);
nema14.motorConfig(200, 190, 600, 400);
nema17.motorConfig(200, 120, 280, 400);
// Stift
pinMode(C4, INPUT_PULLUP);
penZ(1);
// R-Achse
pinMode(D4, INPUT_PULLUP);
homeR();
while (!nema17.stepping(posn17, 900)) {}
posn17 = 0;
nema14.release();
delay(400);
//fahren();
complete = false;
processGCode();
int p = posn17 + 900;
bool t = false;
while (!t)
{
t = nema17.stepping(posn17, p);
t &= nema14.stepping(posn14, 50);
}
nema14.release();
}
void loop() {
// put your main code here, to run repeatedly:
}
定制零件和外壳
gear_wENW3QXap7.stl (下载29 )https://skfb.ly/6yyQP
翻译: DaStudio