【2020】【机械六足蜘蛛】恐怖机器人【第1阶段】【Hexapod】

2020-02-024.4万
精华项目
AI 快速预览详细 收起
本文介绍了如何低成本制作一个六足蜘蛛机器人。项目名称为“机械六足蜘蛛”,硬件清单包括3D打印机、MG996R山寨版舵机、24路舵机控制板和Arduino Uno。最终效果是一个能够自主移动的六足机器人。难度等级适中,适合初学者和青少年。
1. 项目简介

      【机械六足蜘蛛】恐怖机器人【简短预告】【Hexapod】        注:有额外的图片,视频,和项目背景
      个小男孩/女孩
【和老男孩hhh】,或许都曾经倾慕与各种能够变形的东西,比如变形金刚,诸葛连弩,或者航天飞机之类的【不算上史莱姆?】。在这其中,仿生类的机器人总是尤其能引起我的注意,也是机器人中最重要的分支之一。模仿节肢动物的机械也是游戏最常见的素材之一,比如...


星际争霸里面的巨像
【对空弱鸡www】

战锤40K里面的Onager Dunecrawler
【机械神教万岁!荣归万机神!】

      这种类似于蜘蛛的结构分为两类,四足&六足。四足机器人比较简单易懂,运动规律也容易理解,但是相对的玩法单一,稳定性和载重也是不太行。而六足则不仅在外观上更接近蜘蛛【似不似更有威慑力了嗷?】,也没有前面提到的缺陷,但是运动复杂,每一个动作都是由18个关节同时运动完成,而且无论硬件方面还是编程方面实际做起来遇到的难题远远比想象的要多好几倍,但是毕竟帅嘛,Geek极客们当然接受挑战!【奥利给!】
      当然,能够有本项目的另外一个原因就是,现成的Hexapod实在是太贵了啊啊啊!!【╮(╯▽╰)╭】
      所以,本项目兼带另外一个目的,让每一个Geek都能低成本的体会六足的爽快感,所以尽量压缩成本,让快乐的性价比最大化~~

2. 视频演示

【2020】【机械六足蜘蛛】恐怖机器人【第1阶段】【Hexapod】图3


3. 第1阶段

      3.0.5 建模

            软件方面,用SolidWorks,或者Fusion 360都行。多看看已经成熟的商业Hexapod产品结构,自然就出来了。

      3.1 选材问题

           I:3D打印
                  这一点,基本上所有3D打印机都行,请务必把打印精度调到最高,不然部分结构耦合时会需要额外打磨。打印的耗材最好选择有一点脆的那种,否则打出来时,支撑就算用鹰嘴钳也难以取下。最后,大部分家用的3D打印机容量不足以打印主结构,有这个问题的看官可以先把模型分为两段,然后用强力胶固定为一就行。如果,没有3D打印机,如果是大学生,一般学校里的某某Lab会有,如果是初高中生,找找附近的“极客空间”有没有,实在找不到,可以找网店厂家定制。

            II:舵机
                  如果,看官矿里有家,上线可以买专业竞赛用舵机,比如,HerkuleX DRS-0101,295RMB×18=5310RMB【疯了,疯了.GIF】,或者,和穷逼UP主一样,直逼下线,选择MG996R的山寨版【是的,穷的选择山寨舵机,我@#¥~!】,10RMB×18=180RMB【还是有点小贵小贵的】。总之,上线下线都很大,根据购买能力自由选择。最后,注意一下舵机规格,按需要调整模型,而且,根据经验,最好选择270°的,180°的角度会有点受限,选择360°就完全是舵机菜鸟了【和UP主最开始选购一样,浪费了180RMB】

            III:舵机控制板
                  24路舵机控制板是最合适的,像Veyron Servo Driver 24-Channel是最顶级的一类,配有GUI和在线调试也非常方便,当然,也可以和UP主一样【又来了】,买个国产版的,国内的小厂大厂品种多样,只要注意一下规格,基本上都没问题。最需要关注的规格有,电源,输入电压范围要匹配电池,输出电压范围要匹配。最好要支持二次开发,也就是要有串口和主控板通信。不同控制板一般命令格式不太一样,但是本质相同,都是对PWM的封装,这部分可能需要稍微修改一下程序来适配你的控制板。如果用的是总线舵机,也请注意一下控制板支不支持CAN。

            IV:主控板
                  第1阶段内容简单,对硬件要求也不是很高,所以主控板可以自由选择,Arduino Uno足以。你甚至可以不要主控板,一般舵机控制器会自带命令储存功能,所以只要预先设置好动作组,连主控板也能省下。但是,如果你的目标更远大,想做点骚操作,比如蓝牙功能,云台控制,图像识别,图传监控等等,那串口一定要预先盘算好,当然,直接买个Mega无论如何都够了。

            V:电池
                  注意一下,电压和控制板匹配,容量/续航最好充足,重量不要Hexapod撑不起【笑Cry】,就行。

      3.2 硬件连接

            I:电压路线
                  电池--->舵机控制板--->主控板--->其他
                                       └-------->舵机×18
                  OR
                  
                  电池--->舵机控制板--->舵机×18
                     ---->主控板--->其他
                  
                  OR
                  
                  电池1--->舵机控制板--->舵机×18
                  电池2--->主控板----->其他
                  
            II:控制线路
                  主控板--->舵机控制板--->舵机×18
                      └------>其他


      3.3 算法思路

            I:主思路
                  采用经典的三角交替法
【2020】【机械六足蜘蛛】恐怖机器人【第1阶段】【Hexapod】图1
Si, Jianting & Chin, Cheng Siong. (2014).
An adaptable walking-skid for seabed ROV under strong current disturbance.
Journal of Marine Science and Application. 13. 305-314. 10.1007/s11804-014-1261-y.

将左上,右中,左下3点看做三角形A
右上,左中,右下3点看做三角形B
三角形AB自己不用变形,只是各自交替地不停地
抬起,移动,放下,反向移动(归位),而已

【2020】【机械六足蜘蛛】恐怖机器人【第1阶段】【Hexapod】图2

            II:前进后退

动态效果如上

                  整个机体的前进和后退只是互相镜像翻转而已

            III:原地旋转
                  这部分原理相同,只是移动变成了同时顺/逆时针旋转罢了


                  整个机体的顺时针旋转和逆时针旋转也只是互相镜像翻转而已

            IV:需要注意的细节
                  注意,前进/后退的时候,每条腿都一定要在自己的那条直线上,且所属的三角的腿移动距离相同,否则虽然相反方向上的腿上的力可能能够互相抵消,但是会有股内劲在机器人身上,增强摩擦力,影响整体运动。而对于,顺/逆时针旋转,需要每条腿在自己的那个圆上,且所属的三角的腿移动的角度相同,否则也会有上面提到的负面影响。

4. 额外的讨论
     
     六足的进阶与发展方向
     
      4.1 ROS
            目前最先进的六足机器人,都已经清一色采用ROS作为其操作系统,ROS十分复杂,对编程,硬件,物理方面的专业知识要求奇高,但是其优势在于三点
                  一,旋转统一,每个刚体关节的旋转可以说是机器人物理里面最重要和复杂的一部分,各种计算方法层出不穷,ROS做到了对各种方法的统一支持
                  二,自动规划,目前最常见于机械臂,告诉机器需要移动的位置,ROS自动计算所需的移动方案
                  三,模拟,自带模拟软件,机器人工程师可以直接在其上面建模,测试,对各种细小而关键的力进行模拟
                  四,支持好,可以与Arduino通信,也能直接部署在Raspberry上,在SLAM算法支持上简直无敌。我本人以前也用ROS做过用SLAM的无人自动规划车,其自带的很多子软件包堪称强大而同时友好,对很多常用算法进行了封装。其混合算法的封装与协调也是其强项。
      4.2 MATLAB
            专注于模拟,太出名了,有插件加持下也能自动规划【想起我手算和测试每个舵机的旋转角度的痛】。用MATLAB来模拟六足也是非常常见的方案,如果嫌ROS上手要求过于高,MATLAB就是最佳选择。

5. 制作时可能遇到的潜在问题


      以下我罗列了6个在实际制作时才可能遇到的问题,按遇到的顺序排列,也算是制作的经验分享吧
      5.1 3D打印:打磨是非常消耗时间的事情,特别是像我一样打完才发现,支撑打的太多了,材料选的韧性也过高,导致拆卸支撑和打磨表面花费了一周多时间,特别是不同部件之间耦合的部分,打磨不足摩擦力太大容易卡舵机,速度和灵活性也上不去,甚至有2个舵机就因为这个原因导致扭矩过大而报废,而打磨过度就会造成本来就难以调整的误差进一步扩大

      5.2 舵机误差:除了上面所述的可能造成误差的原因,舵机安装时本身也容易产生额外误差,一是,舵机附带的舵机盘很可能安装时无法达到完美的90°,二是,因为舵机内部自带的PID算法会微调舵机,所以如果安装时是垂直安装,而实际使用的时候重力方向与安装时不同,PID会自动调整,反倒让误差更大。所以这里有2个解决方案,一是,尽可能的安装时达到完美,因为每条腿有3个舵机,所以甚至可以考虑,如果两个舵机误差方向相反,可以抵消一部分的误差,二是,在软件层面添加舵机调整的流程代码,比如,如果某个舵机PWM理想状态下需要达到1500,而实际上1375就已经达到位置了,那只需要每次给这个舵机发送命令前减去125就行,每次运行前每个舵机误差都这样调就能达到完美状态了。

      5.3 画图算步伐:如果不想或者没能力数学建模来测算每个舵机在每组动作下需要达到的角度,那就需要手动测算,如果舵机有角度反馈功能,就可以手动将腿摆到目标位置,然后记录下舵机反馈值就行了,简化了手动测算的麻烦,而如果没有,就只能一点一点的修改每个舵机的PWM来看某个组合合不合适,这部分也比较耗时间,而且如果机器人是在悬空的状态下测算的,在实际放在地面上时肯定会发现因为重力这个组合又不合适,需要重算了。为了让每只脚都均匀分摊重力,最好是像我一样,在地上画一个圈和四条线,前进时确保都在线上,旋转是确保都在圈上,而且最好是用手去稍微抬一下每条腿来感受一下每条腿受到的压力,即时调整误差

画图示例(6个白线与黑线的交叉处即六足落脚处)

      5.4
重心:我把航模电池放在了六足内部前面,而舵机控制板放在了内部后面,在实际测试的时候才发现,整个机器人的重心极其不平衡,前进没问题,后退就会经常翻车,因此,将电池放在内容中间,把舵机控制板,Arduino,和其他传感器当成配重来合理分布在六足身上才是比较理想的设计


      5.5 受力角度:我在实际测试的时候才发现,如果让机器人真的像蜘蛛一样爬行,会让每条腿的压力进一步增大,因为现实中的蜘蛛本身很小,重量更小,密度低,所以每条腿受到的力是很小的,而六足机器人本身重量是很重的,所以,如果行走时,每条腿都是向外伸展行走(如下图),特别是3条腿抬起,只有3条腿支撑尚在运动的立足时,整个六足就会剧烈摇晃并且向下坍塌,所以要反过来,让六足的腿最好一直是垂直或者向内的位姿


原设计的走姿,但其实反倒给每条腿增加了额外的压力

      5.6
打滑:在光滑的表面运动时,很容易打滑(3D打印材料本身就很光滑),所以最好如视频中所示,在每条腿的末端加上半个橡胶球体,这样最终效果就会好很多

6. 项目仓库&博客


      项目仓库:https://github.com/AlchemicRonin/Hexapod
      个人成长博客:https://alchemicronin.github.io/

7. 3D模型

      7.1 第1关节:https://skfb.ly/6QpBx
      7.2 第2关节:
https://skfb.ly/6QpBy
      7.3 第3关节:
https://skfb.ly/6QpBz
      7.4 主结构:  
https://skfb.ly/6QpBt
      7.5 上盖:     
https://skfb.ly/6QpBw

8. 第1阶段Demo代码

        注:注释部分为正在开发的第2阶段蓝牙手柄对应代码,不用太在意

  1. //const char checkPosition1[] = "{#004P0600T0300!#007P0600T0300!#016P0600T0300!}";
  2. //const char initialPosition1[] = "{#003P1500T0300!#004P1050T0300!#005P0650T0300!#006P0775T0300!#007P1125T0300!#008P1500T0300!#015P1500T0300!#016P1050T0300!#017P0650T0300!}";
  3. //const char checkPosition2[] = "{#001P0600T0300!#010P0600T0300!#013P0600T0300!}";
  4. //const char initialPosition2[] = "{#000P0650T0300!#001P1050T0300!#002P1500T0300!#009P1500T0300!#010P1125T0300!#011P0775T0300!#012P0650T0300!#013P1050T0300!#014P1500T0300!}";
  5. const char initialPosition[] = "{#003P1500T0300!#004P1050T0300!#005P0650T0300!#006P0775T0300!#007P1125T0300!#008P1500T0300!#015P1500T0300!#016P1050T0300!#017P0650T0300!#000P0650T0300!#001P1050T0300!#002P1500T0300!#009P1500T0300!#010P1125T0300!#011P0775T0300!#012P0650T0300!#013P1050T0300!#014P1500T0300!}";
  6. //String joystickStatus;
  7. //String button;
  8. //int Joystick[4];
  9. //char text;
  10. //bool flag;
  11. //bool continual = false;
  12. void setup() {
  13.   Serial.begin(115200);
  14.   Serial1.begin(115200);
  15.   Serial2.begin(115200);
  16. }
  17. void loop() {
  18. //  joystickStatus = "";
  19. //  flag == false;
  20. //  while (Serial.read() >= 0);
  21. //  while (Serial1.available()) {
  22. //    text = char(Serial1.read());
  23. //    if (text == '#') {
  24. //      flag = true;
  25. //      continue;
  26. //    }
  27. //    if (flag == true) {
  28. //      if (text == '!') {
  29. //        break;
  30. //      }
  31. //      joystickStatus += text;
  32. //    }
  33. //    delay(1);
  34. //  }
  35. //  if (19 >= joystickStatus.length() || joystickStatus.length() >= 26) {
  36. //    joystickStatus = "";
  37. //  }
  38. //  if (joystickStatus != "") {
  39. //    Serial.println(joystickStatus);
  40. //    int i = -1;
  41. //    String temp;
  42. //    for (int j = 3; j < joystickStatus.length(); j++) {
  43. //      text = joystickStatus.charAt(j);
  44. //      if (text != ' ') {
  45. //        temp += text;
  46. //      } else {
  47. //        if (i == -1) {
  48. //          button = temp;
  49. //        } else {
  50. //          Joystick = temp.toInt();
  51. //        }
  52. //        i++;
  53. //        temp = "";
  54. //      }
  55. //    }
  56. //  }
  57. //    Serial.println(button);
  58. //    Serial.println(Joystick[0]);
  59. //    Serial.println(Joystick[1]);
  60. //    Serial.println(Joystick[2]);
  61. //    Serial.println(Joystick[3]);
  62. //  if (button == "UP") {
  63. //    forward();
  64. //  } else if (button == "DOWN") {
  65. //    backward();
  66. //  } else if (button == "RIGHT") {
  67. //    if (continual == false) {
  68. //      Serial2.println("{#000P0500T0300!#005P0500T0300!#006P0500T0300!#011P0500T0300!#012P0500T0300!#017P0500T0300!}");
  69. //    }
  70. //    continual = true;
  71. //    clockwise();
  72. //  } else if (button == "LEFT") {
  73. //    if (continual == false) {
  74. //      Serial2.println("{#000P0500T0300!#005P0500T0300!#006P0500T0300!#011P0500T0300!#012P0500T0300!#017P0500T0300!}");
  75. //    }
  76. //    continual = true;
  77. //    counterClockwise();
  78. //  } else {
  79. //    continual = false;
  80. //    check();
  81. //  }
  82.   check();
  83.   delay(2000);
  84.   for(int i=0;i<6;i++){
  85.     forward();
  86.   }
  87.   check();
  88.   delay(300);
  89.   Serial2.println("{#000P0500T0300!#005P0500T0300!#006P0500T0300!#011P0500T0300!#012P0500T0300!#017P0500T0300!}");
  90.   for(int i=0;i<6;i++){
  91.     clockwise();
  92.   }
  93.   check();
  94.   delay(300);
  95.   Serial2.println("{#000P0500T0300!#005P0500T0300!#006P0500T0300!#011P0500T0300!#012P0500T0300!#017P0500T0300!}");
  96.   for(int i=0;i<6;i++){
  97.     counterClockwise();
  98.   }
  99.   check();
  100.   delay(9999999999999999999999);
  101. }
  102. void check() {
  103.   //Serial2.println(checkPosition1);
  104.   //delay(300);
  105.   Serial2.println(initialPosition);
  106.   //delay(300);
  107.   //Serial2.println(checkPosition2);
  108.   //delay(300);
  109.   //Serial2.println(initialPosition2);
  110.   //delay(300);
  111. }
  112. void forward() {
  113.   Serial2.println("{#004P0600T0300!#007P0600T0300!#016P0600T0300!#001P1000T0300!#010P1000T0300!#013P1050T0300!#006P0500T0300!#011P0500T0300!}");
  114.   delay(300);
  115.   Serial2.println("{#003P1000T0300!#008P1675T0300!#015P1500T0300!#002P1500T0300!#009P1675T0300!#014P1000T0300!#001P1050T0300!#013P1000T0300!#000P0650T0300!#012P0500T0300!#005P0500T0300!#017P0650T0300!}");
  116.   delay(300);
  117.   Serial2.println("{#001P0600T0300!#010P0600T0300!#013P0600T0300!#004P1000T0300!#007P1000T0300!#016P1050T0300!#006P0500T0300!#011P0500T0300!}");
  118.   delay(300);
  119.   Serial2.println("{#002P2000T0300!#009P1325T0300!#014P1500T0300!#003P1500T0300!#008P1325T0300!#015P2000T0300!#004P1050T0300!#016P1000T0300!#005P0650T0300!#017P0500T0300!#000P0500T0300!#012P0650T0300!}");
  120.   delay(300);
  121. }
  122. void backward() {
  123.   Serial2.println("{#013P0600T0300!#010P0600T0300!#001P0600T0300!#016P1000T0300!#007P1000T0300!#004P1050T0300!#011P0500T0300!#006P0500T0300!}");
  124.   delay(300);
  125.   Serial2.println("{#014P1000T0300!#009P1675T0300!#002P1500T0300!#015P1500T0300!#008P1675T0300!#003P1000T0300!#016P1050T0300!#004P1000T0300!#017P0650T0300!#005P0500T0300!#012P0500T0300!#000P0650T0300!}");
  126.   delay(300);
  127.   Serial2.println("{#016P0600T0300!#007P0600T0300!#004P0600T0300!#013P1000T0300!#010P1000T0300!#001P1050T0300!#011P0500T0300!#006P0500T0300!}");
  128.   delay(300);
  129.   Serial2.println("{#015P2000T0300!#008P1325T0300!#003P1500T0300!#014P1500T0300!#009P1325T0300!#002P2000T0300!#013P1050T0300!#001P1000T0300!#012P0650T0300!#000P0500T0300!#017P0500T0300!#005P0650T0300!}");
  130.   delay(300);
  131. }
  132. void clockwise() {
  133.   Serial2.println("{#001P0600T0300!#010P0600T0300!#013P0600T0300!#004P1050T0300!#007P1125T0300!#016P1050T0300!}");
  134.   delay(300);
  135.   Serial2.println("{#002P2000T0300!#009P1675T0300!#014P1500T0300!#003P1000T0300!#008P1325T0300!#015P1500T0300!}");
  136.   delay(300);
  137.   Serial2.println("{#001P1050T0300!#010P1125T0300!#013P1050T0300!#004P0600T0300!#007P0600T0300!#016P0600T0300!}");
  138.   delay(300);
  139.   Serial2.println("{#002P1500T0300!#009P1325T0300!#014P1000T0300!#003P1500T0300!#008P1675T0300!#015P2000T0300!}");
  140.   delay(300);
  141. }
  142. void counterClockwise() {
  143.   Serial2.println("{#001P1050T0300!#010P1125T0300!#013P1050T0300!#004P0600T0300!#007P0600T0300!#016P0600T0300!}");
  144.   delay(300);
  145.   Serial2.println("{#002P2000T0300!#009P1675T0300!#014P1500T0300!#003P1000T0300!#008P1325T0300!#015P1500T0300!}");
  146.   delay(300);
  147.   Serial2.println("{#001P0600T0300!#010P0600T0300!#013P0600T0300!#004P1050T0300!#007P1125T0300!#016P1050T0300!}");
  148.   delay(300);
  149.   Serial2.println("{#002P1500T0300!#009P1325T0300!#014P1000T0300!#003P1500T0300!#008P1675T0300!#015P2000T0300!}");
  150.   delay(300);
  151. }
复制代码

第2阶段已经在紧锣密鼓的进行时了,我们很快就又会再见滴~~~
如果大家觉得项目有趣的话,就请在下方留言吧~

9.
本开源项目基于以下项目
      7.1 PhantomX AX
      7.2 黑人黑科技

10. 额外参考项目
      8.1 How to Build a Wireless Hexapod Robot
      8.2 Hexapod
      8.3 Capers II, a Hexapod Robot
      8.4 How To: Arduino Hexapod PART 1- Mechanics and Wiring
      8.5 HEXAPOD TANK FROM GHOST IN THE SHELL BROUGHT TO LIFE
      8.6 HEXDRAKE — A LOW COST 2-DOF HEXAPOD
      8.7 Phantom_Phoenix

创作许可协议

本项目采用 None(不开放任何权利,保留所有权利) 进行许可。

评论(23)
诩
高级是真的高级,
恐怖是真不恐怖
海之鱼
海之鱼
非常赞,谢谢分享。
DFBJFz5siXP
DFBJFz5siXP
几个舵机怎么同是运动呢,我的是一个一个的动
DFBJFz5siXP
DFBJFz5siXP
又来围观了
DFBJFz5siXP
DFBJFz5siXP
下了模型感觉好大啊,用什么舵机啊
若水2
若水2
求up看b站回复
炼金浪人
炼金浪人
作者
回复若水2
回了,加wx慢慢聊吧(BillMITTER)
gray6666
gray6666
炼金浪人,六足出第二部分了吗?
炼金浪人
炼金浪人
作者
回复gray6666
Em...非常抱歉,最近因为要准备打RoboMaster比赛,在学习Jetson Tx2做视觉识别,这个项目暂时搁置了,要等上一段时间了,非常抱歉!
Adolph
Adolph
这个可以
小慈
小慈
感觉像个蜘蛛,只不过只有6条腿。
代码应该敲的东
代码应该敲的东
这个感觉好难
DFSkangKjBr
DFSkangKjBr
太棒了,版主什么时候整体出......
三儿公子
三儿公子
初学者看的不太明白,是自动运行还是遥控?
kylinpoet
kylinpoet
多谢分享,学习了。
gray6666
gray6666
能实现脱机吗?
炼金浪人
炼金浪人
作者
回复gray6666
程序是运行在机器人上的Arduino上的,没有和PC连接,你是指能否自动运行嘛?还是指什么?
红通通
红通通
此篇神作让我想起了铁锈战争里的试验性战斗蜘蛛
且歌且行
且歌且行
大神,请接受我的膝盖~
滂沱
滂沱
建模大概用了多少料

炼金浪人
炼金浪人
作者
回复滂沱
不好统计,主要是这个模型对于我们学校的3D打印机有些刁钻,很容易就打废了,至少浪费了50%的总消耗
dfybc
dfybc
不错不错不错
rzyzzxw
rzyzzxw
哈哈,有互动的看帖子
7875LXXs
7875LXXs
【2020】【机械六足蜘蛛】恐怖机器人【第1阶段】【Hexapod】