极客工坊

 找回密码
 注册

QQ登录

只需一步,快速开始

查看: 2755|回复: 0

三轴XYZ平台绘制空心字

[复制链接]
发表于 2023-4-18 09:30:25 | 显示全部楼层 |阅读模式
本帖最后由 机器谱 于 2023-4-18 09:30 编辑

1. 功能说明
      本文示例将实现R312三轴XYZ平台绘制“机器时代”空心字的功能。

2. 电子硬件
       在这个示例中,采用了以下硬件,请大家参考:

主控板
Basra主控板(兼容Arduino Uno)
扩展板
Bigfish2.1扩展板
SH-ST步进电机扩展板
电池
11.1V动力电池
传感器
触碰传感器
其它
笔架×1(自制,可根据文末资料提供的3D文件打印)

3. 功能实现
       在这里我们采用了一种算法,该算法的思路是:先建立一个平面坐标系,将我们所需要画的图形放置在该坐标系中,这样就可以确定该图形每个顶点的坐标,两个相邻的顶点之间确定一条直线,直线上各点坐标通过插补计算得到,然后画笔依次沿着这些坐标进行移动,完成绘制。所以在这个过程中,我们需要知道如何建立一个图形的坐标系,以及什么是插补计算。插补计算方法可参考 【R311】双轴XY平台-绘制斜向多边形 。

建立坐标系:
      三轴XYZ平台绘图仪,即通过X, Y, Z三轴的步进电机协调控制绘图笔来进行图形的绘制。通过上位机(PC)来发送gcode代码;下位机(三轴XYZ平台绘图仪)通过对接收到的gcode坐标代码进行解析,并通过插补算法来控制各个轴的步进电机进行图形绘制。


本实验将基于三轴XYZ平台利用processing软件处理gcode文件后,进行绘制文字“机器时代”。


3.1硬件连接
     ① 各轴步进电机与SH-ST步进电机扩展板的接线顺序如下(从上至下):
          X:红蓝黑绿
          Y:红蓝黑绿
          Z:黑绿红蓝
     ② 各个轴的限位传感器(触碰)与Bigfish扩展板的接线如下:
  
        X:A0
         Y:A4
          Z:A2
3.2 示例程序
编程环境:Arduino 1.8.19
       将参考例程代码(_4_smile.ino)下载到主控板中,烧录完成后打开电源,三轴XYZ平台绘图仪各轴步进电机将进行复位,复位完成后,绘图笔将到达绘图区域中心,本实验中三轴XYZ平台绘图仪绘图面积为80*80mm。
  1. /*------------------------------------------------------------------------------------

  2.   版权说明:Copyright 2023 Robottime(Beijing) Technology Co., Ltd. All Rights Reserved.

  3.            Distributed under MIT license.See file LICENSE for detail or copy at

  4.            https://opensource.org/licenses/MIT

  5.            by 机器谱 2023-03-30 https://www.robotway.com/

  6.   ------------------------------*/

  7. #define EN        8       //步进电机使能端,低电平有效

  8. #define X_DIR     5       //X轴 步进电机方向控制

  9. #define Y_DIR     6       //y轴 步进电机方向控制

  10. #define Z_DIR     7       //z轴 步进电机方向控制

  11. #define X_STP     2       //x轴 步进控制

  12. #define Y_STP     3       //y轴 步进控制

  13. #define Z_STP     4       //z轴 步进控制

  14. boolean DIR;              //boolean类型变量 DIR,控制步进电机方向,true为正向,false为反向,根据接线做调整

  15. int stepper_pulse = 40;   //定义步进电机脉冲发送的时间间隔


  16. #define LINE_BUFFER_LENGTH 512  


  17. const int SENSOR_X = 14;   //定义X,Y,Z轴复位传感器引脚

  18. const int SENSOR_Y = 18;

  19. const int SENSOR_Z = 16;


  20. const int stepsPerRevolution = 3200;   //定义步进电机每圈转动的步数,细分为16


  21. float LEAD = 8;   //定义丝杠导程,即步进电机转动一圈,丝杠前进8mm


  22. struct point {   

  23.   float x;

  24.   float y;

  25.   float z;

  26. };


  27. // Current position of plothead

  28. struct point actuatorPos;


  29. float Xmin = -40;   //定义绘图区域范围

  30. float Xmax = 40;

  31. float Ymin = -40;

  32. float Ymax = 40;


  33. float Xpos = 0;

  34. float Ypos = 0;


  35. boolean verbose = false;


  36. void setup()

  37. {

  38.   Serial.begin(9600);      //开启串口通信,波特率为9600

  39.   pinMode(X_DIR, OUTPUT); pinMode(X_STP, OUTPUT);

  40.   pinMode(Y_DIR, OUTPUT); pinMode(Y_STP, OUTPUT);

  41.   pinMode(Z_DIR, OUTPUT); pinMode(Z_STP, OUTPUT);

  42.   pinMode(EN, OUTPUT);

  43.   digitalWrite(EN, LOW);  

  44.   resetStepper();

  45.   digitalWrite(EN, HIGH);

  46.   delay(1000);

  47. }


  48. void loop()

  49. {           

  50.   delay(200);

  51.   char line[ LINE_BUFFER_LENGTH ];

  52.   char c;

  53.   int lineIndex;

  54.   bool lineIsComment, lineSemiColon;


  55.   lineIndex = 0;

  56.   lineSemiColon = false;

  57.   lineIsComment = false;


  58.   while (1) {


  59.     // 接受来自Grbl的串口数据

  60.     while ( Serial.available()>0 ) {

  61.       c = Serial.read();

  62.       if (( c == '\n') || (c == '\r') ) {             // End of line reached

  63.         if ( lineIndex > 0 ) {                        // Line is complete. Then execute!

  64.           line[ lineIndex ] = '\0';                   // Terminate string

  65.           if (verbose) {

  66.             Serial.print( "Received : ");

  67.             Serial.println( line );

  68.           }

  69.           processIncomingLine( line, lineIndex );

  70.           lineIndex = 0;

  71.         }

  72.         else {

  73.           // Empty or comment line. Skip block.

  74.         }

  75.         lineIsComment = false;

  76.         lineSemiColon = false;

  77.         Serial.println("ok");   

  78.       }

  79.       else {

  80.         if ( (lineIsComment) || (lineSemiColon) ) {   // Throw away all comment characters

  81.           if ( c == ')' )   lineIsComment = false;     // End of comment. Resume line.

  82.         }

  83.         else {

  84.           if ( c <= ' ' ) {                           // Throw away whitepace and control characters

  85.           }

  86.           else if ( c == '/' ) {                    // Block delete not supported. Ignore character.

  87.           }

  88.           else if ( c == '(' ) {                    // Enable comments flag and ignore all characters until ')' or EOL.

  89.             lineIsComment = true;

  90.           }

  91.           else if ( c == ';' ) {

  92.             lineSemiColon = true;

  93.           }

  94.           else if ( lineIndex >= LINE_BUFFER_LENGTH-1 ) {

  95.             Serial.println( "ERROR - lineBuffer overflow" );

  96.             lineIsComment = false;

  97.             lineSemiColon = false;

  98.           }

  99.           else if ( c >= 'a' && c <= 'z' ) {        // Upcase lowercase

  100.             line[ lineIndex++ ] = c-'a'+'A';

  101.           }

  102.           else {

  103.             line[ lineIndex++ ] = c;

  104.           }

  105.         }

  106.       }

  107.     }

  108.   }

  109. }


  110. void processIncomingLine( char* line, int charNB ) {

  111.   int currentIndex = 0;

  112.   char buffer[ 64 ];          // Hope that 64 is enough for 1 parameter

  113.   stepper_pulse = 40;         //设置Z轴抬笔落笔时步进电机脉冲间隔

  114.   struct point newPos;


  115.   newPos.x = 0.0;

  116.   newPos.y = 0.0;


  117.   //   Needs to interpret

  118.   //   G1 for moving

  119.   //   G4 P300 (wait 150ms)

  120.   //   G1 X60 Y30

  121.   //   G1 X30 Y50

  122.   //   M300 S30 (pen down)

  123.   //   M300 S50 (pen up)

  124.   //   Discard anything with a (

  125.   //   Discard any other command!


  126.   while( currentIndex < charNB ) {

  127.     switch ( line[ currentIndex++ ] ) {           // Select command, if any

  128.     case 'U':

  129.       step(Z_DIR, Z_STP, 2000);

  130.       break;

  131.     case 'D':

  132.       step(Z_DIR, Z_STP, -2000);

  133.       break;

  134.     case 'G':

  135.       buffer[0] = line[ currentIndex++ ];          // /!\ Dirty - Only works with 2 digit commands

  136.       //      buffer[1] = line[ currentIndex++ ];

  137.       //      buffer[2] = '\0';

  138.       buffer[1] = '\0';


  139.       switch ( atoi( buffer ) ){                   // Select G command

  140.       case 0:                                      // G00 & G01 - Movement or fast movement. Same here

  141.       case 1:

  142.         // /!\ Dirty - Suppose that X is before Y

  143.         char* indexX = strchr( line+currentIndex, 'X' );    // Get X/Y position in the string (if any)

  144.         char* indexY = strchr( line+currentIndex, 'Y' );

  145.         if ( indexY <= 0 ) {

  146.           newPos.x = atof( indexX + 1);

  147.           newPos.y = actuatorPos.y;

  148.         }

  149.         else if ( indexX <= 0 ) {

  150.           newPos.y = atof( indexY + 1);

  151.           newPos.x = actuatorPos.x;

  152.         }

  153.         else {

  154.           newPos.y = atof( indexY + 1);

  155.           indexY = '\0';

  156.           newPos.x = atof( indexX + 1);

  157.         }

  158.         drawLine(newPos.x, newPos.y );

  159.         //        Serial.println("ok");

  160.         actuatorPos.x = newPos.x;

  161.         actuatorPos.y = newPos.y;

  162.         break;

  163.       }

  164.       break;

  165.     case 'M':

  166.       buffer[0] = line[ currentIndex++ ];     // /!\ Dirty - Only works with 3 digit commands

  167.       buffer[1] = line[ currentIndex++ ];

  168.       buffer[2] = line[ currentIndex++ ];

  169.       buffer[3] = '\0';

  170.       switch ( atoi( buffer ) ){

  171.       case 300:

  172.         {

  173.           char* indexS = strchr( line+currentIndex, 'S' );

  174.           float Spos = atof( indexS + 1);

  175.           //          Serial.println("ok");

  176.           if (Spos == 30) {

  177.             step(Z_DIR, Z_STP, -2000);

  178.           }

  179.           if (Spos == 50) {

  180.             step(Z_DIR, Z_STP, 2000);

  181.           }

  182.           break;

  183.         }

  184.       case 114:                                      // M114 - Repport position

  185.         Serial.print( "Absolute position : X = " );

  186.         Serial.print( actuatorPos.x );

  187.         Serial.print( "   -   Y = " );

  188.         Serial.println( actuatorPos.y );

  189.         break;

  190.       default:

  191.         Serial.print( "Command not recognized : M");

  192.         Serial.println( buffer );

  193.       }

  194.     }

  195.   }



  196. }


  197. //直线插补函数,参数为点坐标值

  198. void drawLine(float x1, float y1)

  199. {

  200.   int dx, dy, n, k, i, f, stepInc;

  201.   stepper_pulse = 150;              //设置步进电机写字时脉冲间隔



  202.   if (x1 >= Xmax) {

  203.     x1 = Xmax;

  204.   }

  205.   if (x1 <= Xmin) {

  206.     x1 = Xmin;

  207.   }

  208.   if (y1 >= Ymax) {

  209.     y1 = Ymax;

  210.   }

  211.   if (y1 <= Ymin) {

  212.     y1 = Ymin;

  213.   }



  214.   x1 = (int)(x1/LEAD*stepsPerRevolution);

  215.   y1 = (int)(y1/LEAD*stepsPerRevolution);

  216.   float x0 = Xpos;

  217.   float y0 = Ypos;



  218.   //Serial.println(Xpos);

  219.   //Serial.println(Ypos);



  220.   dx = abs(x1-x0);

  221.   dy = abs(y1-y0);

  222.   n = abs(dx+dy);



  223.   if(dx==0||dy==0)

  224.   {

  225.     stepInc = 1;

  226.   }

  227.   else

  228.   {

  229.     stepInc = 10;

  230.   }


  231.   if(x1 >= x0)

  232.   {

  233.     k = y1 >= y0 ? 1:4;

  234.   }

  235.   else

  236.   {

  237.     k = y1 >= y0 ? 2:3;

  238.   }



  239.   for(i=0,f=0;i<n;i+=stepInc)

  240.   {

  241.     if(f>=0)

  242.     {

  243.       switch(k)

  244.       {

  245.          case 1:

  246.          step(X_DIR, X_STP, stepInc);

  247.          f = f - dy;

  248.          //Serial.println("+x");

  249.          break;

  250.          case 2:

  251.          step(X_DIR, X_STP, -stepInc);

  252.          f = f - dy;

  253.          //Serial.println("-x");

  254.          break;

  255.          case 3:

  256.          step(X_DIR, X_STP, -stepInc);

  257.          f = f - dy;

  258.          //Serial.println("-x");

  259.          break;

  260.          case 4:

  261.          step(X_DIR, X_STP, stepInc);

  262.          f = f - dy;

  263.          //Serial.println("+x");

  264.          break;

  265.          default:break;

  266.       }

  267.     }

  268.     else

  269.     {

  270.       switch(k)

  271.       {

  272.         case 1:

  273.         step(Y_DIR, Y_STP, stepInc);

  274.         f = f + dx;

  275.         //Serial.println("+y");

  276.         break;

  277.         case 2:

  278.         step(Y_DIR, Y_STP, stepInc);

  279.         f = f + dx;

  280.         //Serial.println("+y");

  281.         break;

  282.         case 3:

  283.         step(Y_DIR, Y_STP, -stepInc);

  284.         f = f + dx;

  285.         //Serial.println("-y");

  286.         break;

  287.         case 4:

  288.         step(Y_DIR, Y_STP, -stepInc);

  289.         f = f +dx;

  290.         //Serial.println("-y");

  291.         break;

  292.         default:break;

  293.       }

  294.     }

  295.   }

  296.   Xpos = x1;

  297.   Ypos = y1;

  298. }


  299. /*

  300. //函数:step    功能:控制步进电机方向,步数。

  301. //参数:dirPin对应步进电机的DIR引脚,stepperPin 对应步进电机的step引脚, steps 步进的步数

  302. //无返回值

  303. */

  304. void step(byte dirPin, byte stepperPin, int steps)

  305. {

  306.   digitalWrite(EN, LOW);

  307.   boolean DIR = steps>0 ? true : false;  

  308.   digitalWrite(dirPin,DIR);

  309.   for(int i=0;i<abs(steps); i++)

  310.   {

  311.     digitalWrite(stepperPin, HIGH);

  312.     delayMicroseconds(stepper_pulse);

  313.     digitalWrite(stepperPin, LOW);

  314.     delayMicroseconds(stepper_pulse);

  315.   }

  316.   digitalWrite(EN, HIGH);

  317. }


  318. //步进电机复位函数

  319. void resetStepper()

  320. {

  321.     stepper_pulse = 40; //设置步进电机复位脉冲间隔

  322.    

  323.     while(digitalRead(SENSOR_Z))

  324.         step(Z_DIR,Z_STP,10);

  325.     step(Z_DIR,Z_STP,-15);

  326. while(digitalRead(SENSOR_X))

  327.         step(X_DIR,X_STP,-10);

  328.     step(X_DIR,X_STP,15);

  329. while(digitalRead(SENSOR_Y))

  330.     step(Y_DIR,Y_STP,10);

  331.     step(Y_DIR,Y_STP,-15);


  332.     //复位笔至平台中间位置,步数根据中间位置距离复位传感器的距离计算

  333.     step(X_DIR, X_STP, 28000);

  334.     step(Y_DIR, Y_STP, -16000);

  335.     step(Z_DIR, Z_STP, -30000);

  336. }
复制代码

3.3 图形绘制
       接下来我们将通过上位机的processing软件发送生成文字“机器时代”的 gcode文件给三轴XYZ平台绘图仪进行图形绘制。
       首先将 软件资料包\processing-2.0b8.zip 文件解压到电脑上任意磁盘,然后打开processing.exe来启动 Processing 软件,之后按下图所示步骤进行操作:


       此时打开绘图仪电源开关,在英文输入法状态下按键盘P键,选择端口号,等待三轴XYZ平台绘图仪复位完毕,进入接收上位机指令状态;然后英文输入法状态下按键盘G键,选择之前生成的 gcode文件,点击确定,开始发送gcode文件代码,三轴XYZ平台绘图仪开始绘图;三轴XYZ平台绘图仪在绘图过程中,可以按X键来停止发送gcode文件代码。

注意事项:

       ① 关于绘图笔的安装,可以让绘图仪进入工作状态后关闭电源,此时安装绘图笔使其与纸面相接即可。
       ② 程序中步进电机使用的细分数为16细分,无细分时200步/圈,16细分即 3200步/圈。
       ③ 生成gcode坐标文件后,使用windows的笔记本或者Notepad++软件打开gcode文件,然后删除第一行和第二行,如下图所示:


4. 资料下载

资料内容:
​①绘制空心字-例程源代码
​②绘制空心字-样机3D文件
​③软件资料包
资料下载地址:https://www.robotway.com/h-col-202.html

想了解更多机器人开源项目资料请关注 机器谱网站 https://www.robotway.com

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则 需要先绑定手机号

Archiver|联系我们|极客工坊

GMT+8, 2024-4-28 17:06 , Processed in 0.038873 second(s), 17 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表