3自由度并联绘图机器人实现写字功能(一)

1. 功能说明

    本文示例将实现R305样机3自由度并联绘图机器人写字的功能。

2. 电子硬件

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

主控板

Basra主控板(兼容Arduino Uno)

扩展板Bigfish2.1扩展板
电池7.4V锂电池

3. 功能实现

3.1建立坐标系

      我们需要先对3自由度并联绘图机器人进行极限位置调节,下图中为左极限位置,右极限位置与其对称。3自由度并联绘图机器人绘图主要由图中所示两个伺服电机控制。

3.2硬件连接

    ① 左侧舵机连接Bigfish扩展板D4号引脚

    ② 右侧舵机连接Bigfish扩展板D7号引脚

    ③ 抬笔舵机连接到Bigfish扩展板D11号引脚

3.3 示例程序

编程环境:Arduino 1.8.19

      将参考例程(_1_servoWrite.ino)下载到主控板,调试时可以先只安装舵机输出头,或者安装输出头及L1连杆,运行观察舵机转动状况。

/*------------------------------------------------------------------------------------

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

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

           https://opensource.org/licenses/MIT

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

  ------------------------------*/

#define SERVOLEFTNULL 500      //左侧舵机转动到0度的值

#define SERVORIGHTNULL 500     //右侧舵机转动到0度的值


#define SERVOFAKTORLEFT 603    //左侧舵机转动到0度右侧舵机转动到90度时的值

#define SERVOFAKTORRIGHT 610   //左侧舵机转动到90度右侧舵机转动到180度时的值


#define LIFT0 2000             //落笔写字时舵机转动的值

#define LIFT1 1800             //一笔写完换笔时舵机转动的值

#define LIFT2 1400             //初始及完成写字后抬笔时舵机的值


#define SERVOPINLEFT 4         //定义一些舵机引脚

#define SERVOPINRIGHT 7

#define SERVOPINLIFT 11


#define LIFTSPEED 1500         //定义抬笔落笔时舵机转动的速度


#define L1 90                  //定义与舵机相连杆的长度 mm  

#define L2 117.6               //定义与舵机相连杆的端点铰接处中心到笔中心的距离 mm

#define L3 31.6                //定义中间杆顶端铰接处中心到笔的距离 mm


#define X1 40.0                //定义左侧舵机中心轴到原点在X方向上的距离

#define Y1 -42                 //定义左侧舵机中心轴到X轴的垂直距离

#define X2 111.4               //定义右侧舵机中心轴到原点在X方向上的距离

#define Y2 -42                 //定义右侧舵机中心轴到到X轴的垂直距离


//坐标系建立第一象限,X轴方向为140mm,Y轴方向为120mm,选取坐标点时Y值建议大于70

int coAry[10][10][2] = {         


              //汉字   王

              {{25,110},{0,0},{51,110},{1,1}},

              {{26,98},{0,0},{49,98},{1,1}},

              {{25,84},{0,0},{51,84},{1,1}},

              {{37,110},{0,0},{37,84},{1,1}},


              //汉字   云

              {{61,110},{0,0},{81,110},{1,1}},

              {{58,100},{0,0},{85,100},{1,1}},

              {{68,100},{0,0},{62,85},{86,85},{1,1}},

              {{80,96},{0,0},{91,82},{1,1}},

             

              //汉字   飞

              {{99,110},{0,0},{114,110},{114,98},{115,90},{115,86},{118,84},{121,82},{126,88},{1,1}},

              {{121,103},{0,0},{116,98},{122,94},{1,1}}

                         

};


#include <Servo.h>


int servoLift = LIFT2;


Servo servo1;

Servo servo2;

Servo servo3;


volatile double lastX = 57;   //笔初始坐标值

volatile double lastY = 93;


void setup() {

  Serial.begin(9600);        //开启串口通信

 

  /*servo1.attach(SERVOPINLEFT);

  servo2.attach(SERVOPINRIGHT);

  servo3.attach(SERVOPINLIFT);

  */

 

}


void loop() {

 

  Serial.println("begin");

 

  if(1)

  {  

   

    if(!servo1.attached()) servo1.attach(SERVOPINLEFT);

    if(!servo2.attached()) servo2.attach(SERVOPINRIGHT);

    if(!servo3.attached()) servo3.attach(SERVOPINLIFT);

   

    lift(2);

    //delay(1000);


    //汉字坐标处理

    for(int i=0;i<10;i++)

    {

      for(int j=0;j<10;j++)

      {

        int x = coAry[i][j][0];

        int y = coAry[i][j][1];

       

        //Serial.println(x);

        //Serial.println(y);

       

        if(x == 0 && y == 0)

        {

          lift(0);

          continue;

        }

        else if(x == 1 && y == 1)

        {

          lift(1);

          break;

        }

       

        drawTo(x,y);

       

       //delay(10);

       

      }

    }

   


    /*

    //摆臂测试坐标,左侧舵机转动到0度,右侧舵机转动到90度;左侧舵机转动到90度,右侧舵机转动到180度,依次循环

    drawTo(0,120.0);

    delay(1000);

   

    Serial.println("----------------------");

   

    drawTo(140.0, 120.0);

    delay(1000);

    ///

    */

   

    lift(2);

   

    servo1.detach();

    servo2.detach();

    servo3.detach();   

   

   

    Serial.println("end");

   

  }

 

}


//抬笔舵机转动函数,三个动作,落笔、换笔、起笔, 0 ,1, 2

void lift(char lift)

{

  switch(lift)

  {

    case 0:

        Serial.println("lift0");

        if(servoLift >= LIFT0)

        {

          while(servoLift >= LIFT0)

          {

            servoLift--;

            servo3.writeMicroseconds(servoLift);

            delayMicroseconds(LIFTSPEED);

          }

        }

        else

        {

           while(servoLift <= LIFT0)

           {

             servoLift++;

             servo3.writeMicroseconds(servoLift);

             delayMicroseconds(LIFTSPEED);

           }

        }

    break;

    case 1:

        Serial.println("lift1");

        if(servoLift >= LIFT1)

        {

          while(servoLift >= LIFT1)

          {

            servoLift--;

            servo3.writeMicroseconds(servoLift);

            delayMicroseconds(LIFTSPEED);

          }

        }

        else

        {

          while(servoLift <= LIFT1)

          {

            servoLift++;

            servo3.writeMicroseconds(servoLift);

            delayMicroseconds(LIFTSPEED);

          }

        }

    break;

    case 2:

        Serial.println("lift2");

        if(servoLift >= LIFT2)

        {

          while(servoLift >= LIFT2)

          {

            servoLift--;

            servo3.writeMicroseconds(servoLift);

            delayMicroseconds(LIFTSPEED);

          }

        }

        else

        {

          while(servoLift <= LIFT2)

          {

            servoLift++;

            servo3.writeMicroseconds(servoLift);

            delayMicroseconds(LIFTSPEED);

          }

        }

    break;

    default:break;

  }

}


//笔移动函数,参数为点坐标值,计算两点坐标距离来控制笔移动

void drawTo(double pX, double pY) {

  double dx, dy, c;

  int i;

 

  Serial.println(pX);

  Serial.println(pY);


  // dx dy of new point

  dx = pX - lastX;

  dy = pY - lastY;

  //path lenght in mm, times 4 equals 4 steps per mm

  c = floor(1 * sqrt(dx * dx + dy * dy));


  if (c < 1) c = 1;


  for (i = 0; i <= c; i++) {

    // draw line point by point

    set_XY(lastX + (i * dx / c), lastY + (i * dy / c));


  }


  lastX = pX;

  lastY = pY;

}



double return_angle(double a, double b, double c) {

  // cosine rule for angle between c and a

  return acos((a * a + c * c - b * b) / (2 * a * c));

}



//用各种三角函数把位置坐标换算成舵机的角度,如何计算,请参考

//http://www.thingiverse.com/thing:248009/

void set_XY(double Tx, double Ty)

{

  delay(1);

  double dx, dy, c, a1, a2, Hx, Hy;


  // calculate triangle between pen, servoLeft and arm joint

  // cartesian dx/dy

  dx = Tx - X1;

  dy = Ty - Y1;


  // polar lemgth (c) and angle (a1)

  c = sqrt(dx * dx + dy * dy); //

  a1 = atan2(dy, dx); //

  a2 = return_angle(L1, L2, c);

 

  //Serial.println(floor(((M_PI-a1 - a2) * SERVOFAKTORLEFT) + SERVOLEFTNULL));


  servo1.writeMicroseconds(floor(((M_PI-a1 - a2) * SERVOFAKTORLEFT) + SERVOLEFTNULL));


  // calculate joinr arm point for triangle of the right servo arm

  a2 = return_angle(L2, L1, c);

  Hx = Tx + L3 * cos((a1 - a2 + 0.621) + M_PI); //36,5掳

  Hy = Ty + L3 * sin((a1 - a2 + 0.621) + M_PI);


  // calculate triangle between pen joint, servoRight and arm joint

  dx = Hx - X2;

  dy = Hy - Y2;


  c = sqrt(dx * dx + dy * dy);

  a1 = atan2(dy, dx);

  a2 = return_angle(L1, (L2 - L3), c);

 

  //Serial.println(floor(((M_PI - a1 + a2) * SERVOFAKTORRIGHT) + SERVORIGHTNULL));


  servo2.writeMicroseconds(floor(((M_PI - a1 + a2) * SERVOFAKTORRIGHT) + SERVORIGHTNULL));


}

      两个舵机输出头会保持一个水平的同时另一个垂直,如上述演示视频中所示,如果差距较大,可拆下输出头进行调节,如果比较接近,可以修改程序中前四行参数的值:

      SERVOFAKTORLEFT 左侧舵机水平、右侧舵机垂直位置,数值增加顺时针调整; SERVOFAKTORRIGHT 右侧舵机水平、左侧舵机垂直位置,数值减小逆时针调整;

      SERVOLEFTNULL 左侧舵机0°位置,可不调节;

      SERVORIGHTNULL 右侧舵机0°位置,可不调节。

      当调试完成后可以安装其余连杆,此测试例程中所调节的是笔架在绘图区域中的两端位置,即左侧点(0, 120),右侧点(140, 120)。

      根据上述步骤中所调节的四个参数修改_1_servoWrite.ino例程中对应参数的值。然后将该例程下载到主控板中,运行程序即可书写汉字。程序中所存储的汉字为“王云飞”坐标,如果要书写其它字体,可按照上述3.1步骤所示建立相应坐标系,将各笔画记录到程序中即可。

      坐标系可以按如下格式建立:

      根据上面图表中可知,只要记录每一笔画的坐标值到程序中即可,字体的坐标值在程序中将以数组的形式记录。如下图所示:

      以王字的第一笔:横来说,{25,110}表示横的第一个点坐标,数组中{0,0}表示当x=0&&y=0时落笔,每一笔的第一个坐标后要添加{0,0},然后{51,110}为横的第二个点坐标,因为之前笔已经落下,所以此处将绘制此横;然后{1,1}表示当x=1&&y=1时抬笔,此时一笔写完,将执行下一行坐标。每一笔坐标用大括号括起来,因此{{25,110},{0,0},{51,110},{1,1}},表示王字的第一笔。如要写其它字体,按以上所示建立坐标系,获取坐标数据,按格式记录到程序中即可,并按笔画数量修改数组大小。

4. 资料下载

①写字-例程源代码

②写字-样机3D文件

资料内容详见:3自由度并联绘图机器人-写字

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/13637.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

能量密度的必要性:城市比乡村具有更高的能量密度

文章目录 引言I 人口密度1.1 人口密度太低对于经济的发展的不利因素1.2 足够的人口密度带来的好处1.3 乌鲁克城II 农耕文明和商业文明2.1 农耕文明2.2 商业文明III 有效掌握动力的文明处于优势3.1 苏美尔人- 轮子&风能的利用3.2 英国人- 以蒸汽机为代表的工业革命引言 文明…

作业3综合练习

综合练习: 要求&#xff1a;请给openlab搭建web网站 网站需求: 1.基于域名www.openlab.com可以访问网站内容为 welcome to openlab!!! 2.给该公司创建三个子界面分别显示学生信息,教学资料和缴费网站,基于www.openlab.com/student #更改配置文件 &#xff08;虚拟主机标签…

2023好玩的解压游戏,压力大点开玩可以放松自己

你是不是经常感觉到压力大&#xff1f; 现代社会&#xff0c;竞争逐步激烈&#xff0c;不管是来自学习上&#xff0c;工作上&#xff0c;还是生活上的&#xff0c;压力都非常大&#xff01; 这时候&#xff0c;我们要学会自我减压&#xff0c;有效的放松是为了更好地前行。 …

新互联网人必学-产品经理课无密为伊消得人憔悴

新互联网人必学-产品经理课 download&#xff1a;https://www.666xit.com/3832/ 产品经理&#xff1a;连接用户需求和产品设计的重要角色 随着移动互联网的迅猛发展&#xff0c;产品经理已成为越来越多IT公司中不可或缺的职位。作为一名产品经理&#xff0c;他所扮演的角色是…

如何使用DNS实现融合CDN功能

将托管DNS解决方案与CDN配对可为您的网站提供额外的性能、可靠性和灵活性。 域名系统&#xff08;DNS&#xff09;是一种用于计算机、服务或连接到Internet或专用网络的任何资源的分层分布式命名系统&#xff0c;它将各种信息与分配给每个参与实体的域名相关联&#xff0c;它基…

【LeetCode】剑指 Offer 66. 构建乘积数组 p312 -- Java Version

题目链接&#xff1a;https://leetcode.cn/problems/gou-jian-cheng-ji-shu-zu-lcof/ 1. 题目介绍&#xff08;66. 构建乘积数组&#xff09; 给定一个数组 A[0,1,…,n-1]&#xff0c;请构建一个数组 B[0,1,…,n-1]&#xff0c;其中 B[i] 的值是数组 A 中除了下标 i 以外的元素…

System V 共享内存

System V 共享内存 共享内存是什么如何使用共享内存ftokshmgetshmatshmdtshmctl 共享内存的原理共享内存实现两个进程间通信共享内存的特点共享内存与管道配合使用两个进程间通信多个进程间通信 共享内存是什么 &#x1f680;共享内存是最快的IPC形式。一旦这样的内存映射到共…

网络工程师的水平检测1

水平测试 文章目录 水平测试填空题&#xff08;11分&#xff09;判断题&#xff08;9分&#xff09;选择题&#xff08;8分&#xff09;简答题&#xff08;26分&#xff09;子网划分&#xff08;24分&#xff09;实验拓扑&#xff08;19分&#xff09;填空题&#xff08;5分&am…

【MySQL】数据库完整性和安全性

目录 一、完整性 1.概念 2.sql语言支持的两种约束 2.1静态约束 撤销追加约束 断言 2.3动态约束 触发器 二、安全性 用DBMS对数据库实现的两个特性 一、完整性 1.概念 指dbms保证的db的一种特性&#xff0c;在任何情况下的正确性、有效性、一致性 原理图 广义完整性&…

园区路线地图指引图怎么画?园区地图三维图怎么画?

目前在园区信息化应用形式中&#xff0c;广泛缺乏专业电子地图的使用&#xff0c;因此&#xff0c;使这种高效的信息化工具的应用受到了很大限制。有些仅以图片代替&#xff0c;但图片没有空间计算、检索、路径设计的能力&#xff0c;在地图应用形式中&#xff0c;使用价值很低…

Day953.以假设驱动为指引 -遗留系统现代化实战

以假设驱动为指引 Hi&#xff0c;我是阿昌&#xff0c;今天学习记录的是关于以假设驱动为指引的内容。 很多人在做遗留系统现代化的时候呢&#xff0c;总觉得它是一个十分复杂的技术问题。 本来嘛&#xff0c;无论是代码的重构、架构的拆分&#xff0c;还是 DevOps 平台的搭…

除了学历,你更需要有能力

遥想当年&#xff0c;家里培养出一个大学生&#xff0c;是多荣耀的事&#xff01;可现今却处于一个比较尴尬的状态。 为什么大学生贬值得这么厉害&#xff1f;其实大学生之所以会不值钱不外乎三大原因&#xff1a;量大、与企业需求不匹配、质量差。 高校扩招下&#xff0c;大…

Python每日一练(20230423)

目录 1. 删除链表的倒数第 N 个结点 &#x1f31f;&#x1f31f; 2. 最小覆盖子串 &#x1f31f;&#x1f31f;&#x1f31f; 3. 二叉树的层序遍历 &#x1f31f;&#x1f31f; &#x1f31f; 每日一练刷题专栏 &#x1f31f; Golang每日一练 专栏 Python每日一练 专栏…

Java核心技术 卷1-总结-11

Java核心技术 卷1-总结-11 Java 集合框架将集合的接口与实现分离Collection接口迭代器泛型实用方法集合框架中的接口 Java 集合框架 将集合的接口与实现分离 Java集合类库将接口&#xff08;interface&#xff09;与实现&#xff08;implementation&#xff09;分离。 例如队…

小航助学答题系统编程等级考试scratch一级真题2023年3月(含题库答题软件账号)

青少年编程等级考试scratch真题答题考试系统请点击 电子学会-全国青少年编程等级考试真题Scratch一级&#xff08;2019年3月&#xff09;在线答题_程序猿下山的博客-CSDN博客_小航答题助手 1.下列说法不正确的是&#xff1f;&#xff08; &#xff09; A.可以从声音库中随机…

使用buildroot编译完整系统【IMX6ULLPRO】

目录 构建 IMX6ULL Pro 版的根文件系统 编译系统 ​编辑 镜像文件 构建 IMX6ULL Pro 版的根文件系统 配置文件说明 编译系统 下面以 100ask_imx6ull_pro_ddr512m_systemV_qt5_defconfig 配置文 件为例&#xff0c;在 ubuntu 终端上说明 Buildroot 的配置过程&#x…

抖音数字人主播app

抖音数字人主播app是指一款利用计算机生成的虚拟数字人&#xff0c;在抖音平台上进行实时音视频传输和互动的应用程序。该软件可以让用户创建自己的虚拟数字人&#xff0c;并在抖音平台上进行实时互动和交流。 抖音数字人主播app通常需要包含以下功能&#xff1a; 3D建…

前端学习之路 来自前端方向学生的总结

恭喜您&#xff01;您发现了宝藏&#xff01; 我发现很多小伙伴&#xff0c;对于前端感兴趣&#xff0c;也很想去学好&#xff0c;但是却无从下手&#xff0c;不知道如何去学习。作为一名现处于大三即将大四的学生&#xff0c;借此机会来分享分享我的前端学习之路&#xff01;…

Visual Instruction Tuning: 用LLaVA近似多模态GPT-4

©Paperweekly 原创 作者 | Chunyuan Li 使用 GPT-4 进行视觉指令学习&#xff01;Visual Instruction Tuning with GPT-4! ▲ Generated by GLIGEN (https://gligen.github.io/): A cute lava llama and glasses 我们分享了 LLaVA (Language-and-Vision Assistant)&#…

设计模式--单例模式

目录 介绍 单例模式的八种实现方式 饿汉式(静态常量) 优缺点说明: 饿汉式(静态代码块) 优缺点说明 懒汉式(线程不安全) 优缺点说明 懒汉式(线程安全 同步方法) 优缺点说明 懒汉式(线程安全 同步代码块) 优缺点说明 双重检查 优缺点说明 静态内部类 优缺点说明 …