×

机械设计开源分享

消耗积分:2 | 格式:zip | 大小:0.04 MB | 2023-01-03

王银喜

分享资料个

描述

简介 :: 2017 年秋季

连大家都喜欢分享成功的作品和惊艳的原型机,这里我要换个思路:几乎所有的业余级机械臂制造商都专注于工业型机械臂,我打算用机械和功能几乎与人体相同,但尽可能以最便宜的方式。但由于材料和组件的能力,它变成了一个失败的故事,然而失败的故事也是一个吸取教训的故事。

起初,我开始注意和研究我的手的动作及其尺寸,甚至在四处走动时,所以我冒了很大的风险,因为我附近的几乎所有人都可能认为我有很强的心理素质疾病 :)

第一次设计在2-3个月内完成,然后使用纸板和牙签制作原型。很高兴看到机械方法是否令人满意并能够达到基本要求。

 
 
 
 
pYYBAGOuOCyAe3QwAAGJ_sWD3us982.jpg
 
1 / 6第一个设计 - 将其与 v.2.0 和 2.1 进行比较 :)
 

然后我休息了很长时间以思考更好的意见。

最初的设计在机械方面有很多部分,应该对其进行优化。所以,我决定从头开始,一开始就选择了材料:Balsa Wood,但这个选择会迫使我比我想象的更早结束项目。无论如何,我可以说它运作良好。

2018 年秋季 :: 从零开始

决定材料后,我重新开始了机械设计,尽可能使用单体零件。所有掌骨/近端/内侧/远端“骨骼”、梯形和手腕部分都经过设计、虚拟组装并在计算机上相互交互。在那段时间里,我也注意到我现在的设计有些东西是撑不住的。为什么?

因为在初始阶段,手掌是完全平坦的,与大多数其他可用的机器人手一样。所以它会导致物体滑出,即使手指是闭合的。

 
poYBAGOuOC6AG84-AAB6dLy6umg795.jpg
版本 2.0
 

然而,人的手掌却并非如此。如果你把手指伸直,从侧面看,你可能会认为它是平的。但是当你直接面向指尖看时(你也可以触摸你的手的顶部),很明显你的手掌有一个由掌骨形成的凹陷结构,由一个凹陷的梯形引导。顺便说一句,当您合上手指时,它们会从四面八方和您的手掌覆盖一个球形体积。

所以我将其反映到我的设计中:梯形被重塑为凹面,因此由于掌骨与梯形对齐,手掌变得凹陷。只需要通过调整角度进行几次试验,是的,它在理论上是有效的:)

 
pYYBAGOuODKAeGMbAAE0rtkGCks118.jpg
版本 2.1
 

那时,我也想超越单纯的机械,开始思考驱动。但是怎么办?我不得不使用伺服电机,但即使我使用最小的类型,我也没有地方在包括手腕在内的手部找到它们。我注意到,我只需要 12 个伺服器用于手部和手腕部分来模仿它们的所有动作。同时,我也决定了如何模仿“筋”和“筋”:经过尝试几种不同的材料,我发现鱼绳是最好的“筋”,因为它的强度和柔韧性,而将橡胶包装成“筋” (我们还将讨论它们)。在这些试验中,我还使用 6 毫米 PVC 管实现了“肌肉导向”。

 
 
 
 
poYBAGOuOEKAbZ1oABsyu-Rxpbo930.jpg
 
1 / 2v.2.1 的第一个组件
 

很明显,还应该设计手臂部分。但是由于前臂具有扭转和杠杆运动,所以只剩下一个可能的地方用于伺服系统:上臂。

也可以驱动上臂,但在上臂-肩部连接上建立万向接头将极其复杂和困难。无论如何,上臂可能只是杠杆,整个结构可能会被肩部转动,所以我决定将机械结构限制在肩部。

第一个问题:扭腕

拼好Hand和Wrist部分后,开始研究Front Arm,立马问题来了:我的手怎么扭?

或许直到现在你也从未想过这一点。从机械上讲,扭转动作需要枢轴连接。但是它是如何组装到人体上的呢?我寻求的答案在 Stan Prokopenko 的网页上:https ://www.proko.com/types-of-joints/

(非常感谢他的指导。与此同时,我还了解到扭转运动在医学上被称为“旋前”和“旋后”。)

所以很明显尺骨和桡骨之间存在枢轴连接。但为什么那个支点位于后端,肘部?这不是很难做的事吗?我该如何模仿这种机制?

从 Prokopenko 的动画中可以看出,尺骨和桡骨围绕枢轴缠绕在一起。当专门的肌肉伸展时,桡骨会旋转到尺骨上,因此手腕会扭曲。

所以我一开始也是这样做的:我准备了一个原型,将枢轴放在肘端,将尺骨和半径都固定在手腕上,最后用包装橡胶将它们包裹在肘端。但是,这种配置可以即使手动也不能平稳移动。在仔细研究了机制之后,我采取了完全不同的方式,完全重塑尺骨和桡骨:尺骨设计为仅用于杠杆,并且与手腕完全分离。Ulna-Radius 枢轴重新定位到前臂的中间部分。最后,Wrist 仅固定在 Radius 上。所以,尺骨和桡骨的形状也终于变得与真骨相似。

 
pYYBAGOuOE2AOMg2ABnCH-_FRvk402.jpg
尺骨和桡骨的初步研究(枢轴在肘端,与您的相同)
 

(在您的前臂上,该枢轴位于肘端,因为这是保护枢轴免受任何损坏的最佳位置。如果前臂骨骼因任何冲击而骨折,完全愈合后您可以继续正常使用它。但是,如果枢轴放在中间,受到机械冲击就会损坏,所以很可能你的手即使痊愈也再也无法扭动了。)

解决这个问题后,现在我必须决定“肌肉”的路由。路线不应受到尺骨和桡骨运动的影响,因此很明显,所有肌肉绳索都应通过尺骨-桡骨枢轴到达上臂的执行器。所以我牺牲了一支笔来使用它的身体作为一个空心关节。

最后,整个 Humanoid Arm + Hand Mechanicals 几乎可以执行真人的所有动作:打开和关闭手指以及举起东西、在手腕处左右转动和上下转动、扭转前部的能力手臂将近 270 度并杠杆化,杠杆化上臂,并在肩部将整个手臂结构旋转近 180 度。

 

 

 

请注意,如果我计算正确,真人手臂从肩部到手部有 27 个运动轴:

  • 上臂的转动、杠杆和盘旋 = 3 轴
  • 杠杆和扭转前臂 = 2 轴
  • 水平(右-左)和垂直(上下)旋转手腕 = 2 个轴
  • 盘旋和杠杆拇指掌骨 = 2 个轴
  • 杠杆拇指近端和远端 = 2 轴
  • 杠杆食指、中指、无名指和小指的近端、内侧和远端 = 3 x 4 = 12 个轴
  • 在水平面上闭合和打开食指、中指、无名指和小指,同时保持所有直线 = 1 x 4 = 4 个轴 - 尝试显示 Vulkan Salute :)

因此,完成的机械原型能够在 22 个轴上完成所有这些操作,除了绕上臂旋转和为 Vulkan Salute 移动手指。

 
 
 
 
poYBAGOuOF6AVv6CABRHRBEisnU021.jpg
 
1 / 2比例 - 1:1
 

通过完成前臂力学,第 13 个伺服器的必要性出现了“旋前”和“旋后”,第 14 个伺服器用于杠杆整个前臂。

与前臂机械相比,上臂和肩部设计更容易。但由于轻木的弱点,上臂的肩端应该从内部得到很好的支撑,40 毫米直径的 PVC 管完美地做到了。并打入一部分25mm的PVC管,这样上臂的杠杆轴就完成了。

 
 
 
 
poYBAGOuOGeAH7fZABZwtfU099s857.jpg
 
1 / 4切除上臂的一部分
 

(在这里我写得很快,但事实并非如此。在意识到某些事情之前,所有部分都被建模、匹配并与其他部分交互,并在计算机上检查其适用性。实施在工程之后进行。)

不过,自从有了驱动的想法之后,我的脑子里还有一个疑问……

“好吧,到现在为止我们都做得很好,但是……我们要怎么支撑那东西呢,宝贝?”

支持问题不仅针对机械结构。还需要将控制器固定在某处,但由于跳线的长度有限,因此靠近机械结构。几天来,我一直在想象一个由木头、管子和其他材料专门制成的支撑结构……但我不喜欢这些想法。

一天晚上,我发现一个很久以前就挂在我桌边的完美解决方案:我的相机三脚架。只需将其螺丝换成螺栓来固定肩部的转动轴,它就可以完美地工作,并且可以正常工作。

所以我最终只为 Shoulder 设计了一个轴和底板,并且只使用了我多余的家具部件、塑料废水管盖和轻木。然后所有东西都放在三脚架上。

现在我可以开始安装肌肉、执行器等等,但在此之前发生了另一件事……

“肯定还有一个操控者……”

就在决定启动整个手臂后,我计算了我必须使用的信号并不可避免地面对坏消息:我只有一个 Arduino Due,即使与其他模型相比它有很多输入/输出,但它仍然是不够。起初我必须定义进一步的必要性,然后我发现 Arduino Leonardo 是辅助控制器的最佳解决方案。在准备输入/输出列表并对其进行一些研究后,手部和手腕部分留在 Due 的范围内;而前臂、上臂和肩部的控制则由莱昂纳多负责。

然后,又提出了一个问题……

“哪个控制器应该是主人,他们应该如何相互‘对话’?”

因为容量大,速度快,所以最好让Due做master。然而,“说话”出现了问题:Due 运行在 3.3V,而 Leonardo 处于 5V 电平。由于它们的 I2C 通信也使用与控制器相同的电压电平,直接连接会烧毁 Due。

我必须使该接口尽可能简单,最好的方法是使用模拟(电压)信号。只是莱昂纳多应该“理解”;

  • 应启动哪个部分,以及,
  • 它应该移动到哪个位置。

所以我决定在 Due 上使用 DAC0 和 DAC1 模拟(电压)输出,并在 Leonardo 上相应地使用 A0 和 A1 来定义它们。

然而,电压输出的稳定需要一点时间。所以 Leonardo 可能会误解 Due 的请求,所以它也应该知道命令是否已准备好读取。第三个通讯信号出现:“执行”。好消息是,它可以直接从 Due 的 Ch.50(数字输出)连接到 Leonardo 的 Ch.8(数字输入)。

最后,Due 是如何理解它的命令被执行的呢?第四个信号应该从 Leonardo 发送到 Due:“命令已执行”反馈,但由于上述电压水平存在问题。如果确认信号直接从 Leonardo 的 Ch.12(数字输出)连接到 Due 的 Ch.51(数字输入),那么我应该为可怜的 Due 组织一场葬礼 :)

无论如何,这个问题是整个项目中最简单的:Leonardo 的 5V 输出可能仅被两个千欧级电阻分压,然后它可能会以 ~3.3V 的形式发送到 Due。在实施阶段,只有 270k 和 470k 效果很好。

肌肉和肌腱

你驱动肌肉采取一些动作,但是当相关肌肉被释放时,身体部分必须回到原来的位置。所以你的身体上有肌腱,与肌肉相反。但是肌腱由于其使用目的而不如相应的肌肉强壮。

我已经在上面写下了我决定用作肌肉和肌腱的材料。然而,在试验过程中,我注意到钓鱼绳很容易卡在机械接头之间。所以最好使用某种“指南”,而 6mm 直径的 PVC 管件正好适合这个目的。

尺骨和上臂没有使用任何肌腱,因为重力会很好地发挥作用。

 
 
 
 
pYYBAGOuOHCAbI44ABfy298w78A684.jpg
 
1 / 2穿过腕管和中空枢轴的“肌肉”
 

限位开关和位置传感器

当我决定启动手臂时,限位开关的必要性直接出现了。没有它们,伺服系统很容易损坏机械部件。但不幸的是,舵机并没有我想象的那么强大。

一开始我认为限位开关是最简单的部件之一,但事实并非如此,尤其是在手指上。实际上,这个问题的出现有两个主要原因:首先是轻木,由于它的弱点。第二个是我的设计规模。正如您在上面的一些照片中看到的,我的意图是以 1:1 的比例制作人形手和手臂。

不管怎样,我只用电线和铝带做了接触点。即使它们对于保护机械装置必不可少,但由于轻木零件的磨损,它们会及时失去功能,尤其是在手指上。

位置传感器是通过仅在几个点上使用电位器来实现的,它们几乎可以正常工作。

 
poYBAGOuOHuAPalWABfKsHMWnTw020.jpg
手指上的“高”限位开关(连同用于引导“肌肉”的管子)
 

 

 
pYYBAGOuOIWASIvNABkUwrHBbzE578.jpg
典型位置传感器
 

伺服执行器及其局限性

由于空间有限,我只有一个选择来驱动(也许我应该说医学术语中的“伸展”或“屈曲/伸展”)专用于前臂、手腕和手部的“肌肉”,包括手指: -转动微型伺服电机。所有这些都应该位于 Upper Arm 上,因此 Upper Arm 的设计也通过考虑绳索布线来完成。

起初,我用轻木制作了直径约 10 毫米的滑轮。然而,微型舵机的输出扭矩非常低。然后我修改了他们的轮角:通过研磨减小直径,在喇叭和舵机之间使用垫圈,并在靠近中心的位置钻孔以连接绳索。这样,舵机可以拉更高的负载。

 
 
 
 
poYBAGOuOIiAHZKlAABm46BXehw914.jpg
 
1 / 3
 

在后期阶段,还需要另一个滑轮组来杠杆前臂和上臂,这样就完成了。即使是微伺服也可以处理前臂的杠杆,上臂需要标准伺服,因为它应该处理上臂、前臂、手以及它们上面的所有接线的重量。

另一个标准伺服也用于转动肩部。

 
 
 
 
poYBAGOuOI-ADmrwAAlv1ZY3o2I059.jpg
 
1 / 2前臂滑轮(此处还显示了 Radius 的位置传感器组件,就在电线前面)
 

“所有那些导线都必须组织起来……哪怕是一点……”

最好的方法是制作某种“终端组件”。经过几天的研究,我发现只需2块8x2厘米的预制PCB就可以制作3个端子组件。

  • TB01 设计用于处理专用于手、手腕和桡骨(扭转前臂)的所有信号,以及专用于手腕和桡骨(扭转前臂)的伺服系统
  • TB02 设计用于处理专用于肩部、上臂和尺骨(杠杆前臂)的所有信号和电机
  • TB03 设计用于处理 Fingers 的舵机

当然,所有端子组件都应在首次“制造”之后和焊接电线期间进行测试。

 
 
 
 
pYYBAGOuOJmAcsguABQeWTcWeQY727.jpg
 
1 / 3端子组件
 

舵机的早期测试

在几乎安装完所有东西之后,我只带了 Arduino Leonardo 并开始通过一个简单的例程单独测试每个伺服器,以检查它是否能够按要求运行。该例程非常基础,仅用于以数字输入选择的最低速度和方向运行一个伺服。那个时候可以一个一个操作。但是当所有舵机一起通电时,意想不到的惊喜就来了。稍后我们将讨论那个麻烦。

 
pYYBAGOuOJ6AcCW4AAh4vLOVTCA459.jpg
早期测试
 

第一个问题继续 :: 扭腕

即使我修改了伺服臂以用作极小直径的滑轮,但它还不足以“旋前”和“旋后”Radius。所以我做了一个微型滑轮组。

 
pYYBAGOuOKmAYtMrABP_bE55XFI206.jpg
 

 

 

第一个效果很好,但它太大了。所以它在下一步被最小化。

 
poYBAGOuOLGAHQBWABN9aNelMH4544.jpg
 

 

 

不幸的是,各个伺服上的扭矩仍然太大。我尝试调整,但是当前臂处于水平位置并且伺服将桡骨完全带到尺骨上然后试图释放时,由于微型滑轮上的摩擦和反作用力,相应的“肌腱”橡胶未能恢复原状。我拉紧了肌腱,但是舵机无法再拉动了……经过几次尝试,舵机变得不稳定,很快它的电机控制器就烧毁了。那是Project的第一个受害者。请为无名英雄致敬和祈祷 - 不,对不起它的标签是“11FR”- :)

(当然,每个点/组件都有自己独特的标签。否则不可能进行所有的构造、布线和编程。耐心点,我们还将讨论工程的重要性。)

所以我决定停止扭转前臂的试验。它需要很大的扭矩,但与此同时,进一步的试验可能会导致整个前臂损坏。

其他试验 :: 手指

没什么特别要说的。一开始,一切看起来都还不错……直到伺服系统坏了,轻木部件磨损了。

 

 

 

 

 

First Codes :: Due<->莱昂纳多互通

原理上面我已经讲过了,在编程阶段就是第一节。在模拟通道上,Due/DAC0 和 Leonardo/A0 分配给设置值寄存器以发送和接收百分比设置值。Due/DAC1 和 Leonardo/A1 分配给 Section Selector。

但它只是需要做一些范围:由于可以使用全范围,但由于 DACx 输出会在 0.75-2.55V 范围内产生相应的电压,因此应该在 Leonardo 上调整匹配比例。因此,Leonardo 被配置为读取 Set Value between (整数)168-846对应0-100%。对于部分选择器,整个范围在 Due-side 上分为 5 个部分,并为 Leonardo 计算相应的值。但在那个实现中,也有必要使用死区。因此,即使是在 Due 上使用的确切值,它们在 Leonardo 上也被解释为(值 +/- 死区)以确保选择部分。

最后,相互通信程序在两个控制器上完成如下并且运行良好。

到期日:

   const int   _FA_Radius  =  840;    // Front Arm Radius Section
   const int   _FA_Ulna    = 1530;    // Front Arm Ulna Section
   const int   _UpperArm   = 2220;    // Upper Arm Section
   const int   _Shoulder   = 2910;    // Front Arm Radius Section
void CommandToLeonardo(String Section, int SetValue) {
   /* SetValue is given as Percentage */
   int Section_Num=0;
   if (Section=="_11FR") Section_Num = _FA_Radius;
      else if (Section=="_11FU") Section_Num = _FA_Ulna;
           else if (Section=="_21UA") Section_Num = _UpperArm;
               else if (Section=="_31SH") Section_Num = _Shoulder;
   if (Section_Num==0)
      Serial.println("Section cannot be recognized.");
   if ((SetValue<0) || (SetValue>100))
      Serial.println("Set Value should be between 0-100).");
   if ((Section_Num!=0) && (SetValue>=0) && (SetValue<=100) )
      {
       Serial.print(String(SetValue) + "% -> ");
       switch(Section_Num){
             case _FA_Radius: Serial.println("Front Arm-Radius"); 
                              break;
             case _FA_Ulna  : Serial.println("Front Arm-Ulna"); 
                              break;
             case _UpperArm : Serial.println("Upper Arm"); break;
             case _Shoulder : Serial.println("Shoulder"); break;
          }
       analogWrite (_00XXCM, Section_Num);
       analogWrite (_00XXSP, map(SetValue, 0, 100, 0, 4095));
       delay(50);        // Delay for stabilization of outputs
       digitalWrite(_00XXXC, HIGH);  // Execute Command
       do { // Wait until getting OK from Leonardo
          } while(!digitalRead(_00XXOK)); 
              
       Serial.println("ARD LNRD = OK.");
       analogWrite (_00XXCM, 0); 
       analogWrite (_00XXSP, 0);  
                    // Resetting Section and Set Value outputs
       delay(50);   // Delay for stabilization of outputs
       digitalWrite(_00XXXC, LOW);   // Resetting Command
      } /* End of "if (Section_Num!=0)" */
} /* End of "void CommandToLeonardo()" */
void loop() {
    if (Serial.available())
       {
        String Command=ReadUserInputOverSerial();
        Serial.println(Command); 
        if (Command.substring(0,1)=="?") /* Help */
           {
            Serial.println("Commands:");
            Serial.println("  CTL [Section] [SetValue (0-100)]");
            Serial.println("      CommandToLeonardo - Section: First 5 characters of PWM Tag (e.g. _10WH for _10WHCM).");
            Serial.end(); Serial.begin(_BAUDRATE);
            while(!Serial) { };
           }
        if (Command.substring(0,3)=="CTL")
           CommandToLeonardo(Command.substring(4,9),
                             Command.substring(10).toInt());
        CommandPromptOverSerial("[ARD DUE/] > ");
       } /* End of "if (Serial.available())..." */
} /* End of "void loop()": ARDUINO DUE */

关于莱昂纳多:

   const int   _FA_Radius  = 310;    // Front Arm Radius Section
   const int   _FA_Ulna    = 425;    // Front Arm Ulna Section
   const int   _UpperArm   = 540;    // Upper Arm Section
   const int   _Shoulder   = 655;    // Front Arm Radius Section
   const int   _ICDB       =  40;    // Deadband
   /* Since the signals cannot generate exact integer values, 
    * Section shall be identified by [ (SectionValue) +/- _ICDB ]
    * reading.
   */
void setup() {
  /* Initial Feedback to Due */
  digitalWrite(_00XXOK, LOW); 
}
void CommandFromDue() {  
   CommandFromDue_Section = analogRead(_00XXCM);
   if ( ((_FA_Radius-_ICDB)<=CommandFromDue_Section) && 
        (CommandFromDue_Section<=(_FA_Radius+_ICDB)) )
      CommandFromDue_Section=_FA_Radius;
      else if ( ((_FA_Ulna-_ICDB)<=CommandFromDue_Section) && 
                (CommandFromDue_Section<=(_FA_Ulna+_ICDB)) )
              CommandFromDue_Section=_FA_Ulna;
      else if ( ((_UpperArm-_ICDB)<=CommandFromDue_Section) &&
              (CommandFromDue_Section<=(_UpperArm+_ICDB)) )
              CommandFromDue_Section=_UpperArm;
      else if ( ((_Shoulder-_ICDB)<=CommandFromDue_Section) &&
                (CommandFromDue_Section<=(_Shoulder+_ICDB)) )
               CommandFromDue_Section=_Shoulder;
      else
               CommandFromDue_Section=0;
   CommandFromDue_SetValue = 
               map(analogRead(_00XXSP),168,846,0,100);
   /* Due generates DAC outputs between 0.55-2.75 V and it 
    * corresponds (int) 168-846 on Arduino Leonardo.
    * Here analog reading is converted to Percentage.
    */
   boolean CommandFromDue_Execute = digitalRead(_00XXXC);
   String  Section = "";
   int     Speed   = 0;
   if ((CommandFromDue_Section!=0) && CommandFromDue_Execute)
      {
       Serial.println();
       Serial.print("Command by ARD DUE: "+
                     String(CommandFromDue_SetValue) + "% -> ");
       switch(CommandFromDue_Section){
             case _FA_Radius: Serial.println("Front Arm-Radius");
                              Section = "_11FR";
                              Speed = 20;
                              break;
             case _FA_Ulna  : Serial.println("Front Arm-Ulna");
                              Section = "_11FU";
                              Speed = 20;
                              break;
             case _UpperArm : Serial.println("Upper Arm");
                              Section = "_21UA";
                              Speed = 90;
                              break;
             case _Shoulder : Serial.println("Shoulder");
                              Section = "_31SH";
                              Speed = 30;
                              break;
           }
       Serial.println(" -action-");
       MoveToPosition(Section, Speed, CommandFromDue_SetValue);
       Serial.println("OK -> ARD DUE.");
       CommandPromptOverSerial("[ARD LNRD/] > ");
       digitalWrite(_00XXOK, HIGH); delay(100);
       digitalWrite(_00XXOK, LOW); // OK Feedback to Due
      } /* End of "if (Section_Num!=0)" */
} /* End of "void CommandFromDue()" */

 

 
 
 
 
pYYBAGOuOLeAQ4kWAAHPbKXNIPQ215.jpg
 
1 / 2互通:初级测试
 

编码::进一步

除了互通之外,这里我不会解释更多代码的深层细节,以免让读者感到无聊,因为在最后你可以找到完整的程序,如 Due 的 800 多行和 Leonardo 的 500 多行,以及解释性说明. 正如您在程序中看到的那样,已经存在具有相同名称的函数。原则上,这些功能在原理/算法上完全相同,但只是由于控制范围而进行了一些更改。

void CommandPromptOverSerial(提示符)

它的创建只是为了制作那个花哨的命令行界面。除此之外,它还重新初始化串行通信以确保刷新。

字符串 ReadUserInputOverSerial()

在串行监视器上读取用户输入,并将其作为字符串返回。

无效阅读(标签)

空循环测试()

这两个函数都是为测试单个循环(输入/输出通道)而创建的。

LoopTest() 已经有专门用于模拟输入、模拟输出、数字输入、数字输出和 PWM 输出类型的小节;但 Read(Tag) 仅用于直接从顶层读取输入,而无需转到 LoopTest()。

 
poYBAGOuOLqARv8GAAIGzq0lNgU344.jpg
循环测试
 

void CommandToServo(标签,值)

正如您到目前为止所认识到的,我将所有内容都称为标签。这也是这个功能的目的:它将设定值(0-180)发送到分配给“标签”的伺服电机。

void FullTravelDurationMeasurement (PWM_Tag, LimitSw_CCW, LimitSw_CW, int Speed)

编写此函数是为了检查手指的完整行程持续时间,因为它们无法配备实际位置传感器。根据结果​​,可以定时拉或放。即使它在理论上通过手动强制输入/输出工作,它也不能用于实际系统,因为开关由于机械磨损而无法正常工作。因此,完整的行程持续时间只能通过手动试验来确定,而不是使用此功能。

 
pYYBAGOuOL2AXV3TAAHCHs8brrY433.jpg
 

void MoveDuringMillis(标记、方向、速度、毫秒)

该函数也被编写为在指定的方向和指定的毫秒内驱动由标签定义的手指。

void MoveToPosition (Section, Speed, SetValue)

此函数用于将一个部分移动到 SetValue 给定的位置并以指定的速度移动。注意Section还应装有位置传感器。

void Fingers_Hold()

void Fingers_Release()

这些功能被编写为几乎同时关闭(握住某物)和释放手指。不幸的是,手指只能部分地工作一次……

void Final_Demo()

顾名思义:最后只能使用前臂和上臂的杠杆机构,以及肩部的旋转。所以写这个函数就是为了演示它们。

在成功测试所有单独的信号和功能后,我完成了伺服系统和机械部分之间的机械连接。现在,arm 已准备好测试真正的功能,但是……

通电 :: 疯狂伺服

在一个接一个地测试和调整所有伺服系统之后,是时候为所有伺服系统一次性供电了。但结果并不如我所料。

我最初的设计在上臂有 14 个微型伺服系统来处理轻负载,而在肩部有 2 个标准伺服系统用于杠杆上臂和转动肩部。他们都能够接受一个共同的电压水平,6V,所以我做到了。

但是一旦我操作了一个标准的舵机,一些微型舵机就没有任何命令就启动了。幸运的是,我在电源线上放了一个开关,所以我可以一次全部切断,但无论如何,由于瞬间和极端的加速,一些手指用力过大。我再次拧下所有微型伺服系统的滑轮,然后重新通电。结果是一样的:不稳定的微舵机永远无法调整,或者即使调整也无法保持稳定,甚至卸载。

 

我的第一个疑问是电源的适用性:我使用了一个 6V、3Ah 的适配器。

根据可用数据,每个微伺服在空闲时消耗 6 mA,在空载运行时消耗 120 mA,在失速条件下消耗 800 mA。对于标准舵机,只给出一个参数:100 mA 作为工作电流,我认为它是空载运行,并假设其余参数与微型舵机类似。

所以,我的电源就足够了,因为此刻只有一个舵机运行。那么,失败的原因可能是什么?

我仍然在考虑电源,并开始通过示波器对其进行监控。结果很有趣,而只有一个标准伺服正在运行:

 
pYYBAGOuOMGAZrBlAAckt7u0qJ4797.jpg
操作标准伺服时的电源电压波动
 

当我看到这些变化时,我有一个理论:可能微伺服对电压波动更敏感,而那些被它们误解为“命令”。然后我决定分别为微型和标准伺服系统供电。标准伺服系统与一个微型伺服系统(专用于杠杆前臂的微型伺服系统)保持连接在当前电源上,而所有其他微型伺服系统都单独连接到其他电源。这种修改奏效了。

手指 :: 最后一次

正如我在上面已经写过的,最后由于磨损我无法操作所有部分。无论如何,它只能打开和关闭手指一次,如果我继续尝试,我可能会杀死剩下的 6 个微型伺服系统。所以最好在这里停下来。从理论上讲,如果轻木部件没有磨损,您会看到顺序但更顺畅的工作。

 

最终演示

最后,经过约 4.5 个月的设计和组装,可以仅操作前臂和上臂的杠杆功能以及肩部的转动功能。

 

工程 :: 重新审视工业实践

如果您的项目在很小的区域内并且只包含很少或几个设备,也许您可​​以通过眼睛和手来跟踪一切。

但是,如果您必须在 ~1 米长的设备上工作,该设备包含 140 多种不同类型的 500 多个零件、~60 种电气和电子设备(开关、电位器、伺服电机)、两个不同控制器上的 60 多个信号以及将两者互连,以及数百条连接线;然后在计算机和论文上工作成为必须,并且是解决问题的最佳选择。

所以一切都是从在计算机上设计机械开始的。在后期阶段;模型中添加了伺服电机、开关、位置传感器和端子组件。

正如我在前面部分提到的,专用的唯一标签也分配给所有单独的部分。你也会在节目中看到这些标签,它们让生活变得更轻松。最后,有“3D模型”、“滑轮绳布线草图”、“仪表列表”、“I/O列表”、“仪表布局”、“接线板布局”和“接线图”作为文档包项目。

 

 

 
 
 
 
poYBAGOuOMiAHZLMAAD0LXVCYy0998.jpg
 
1 / 83D 模型
 

对于未来 :: 瓶颈

我同意我的设计非常原始。我最初的意图只是机械地模仿人的手和手臂,但后来我决定尝试驱动整个结构。

在机械设计方面,一切几乎都符合预期:所有机械部件都完美匹配并通过手动方式相互交互。

无论如何,第一个瓶颈是选择的材料,轻木:我选择它只是因为它可以广泛用于业余爱好作品的预成型/预切割部件,用于原型制作非常便宜,易于通过简单的手工工具成型,容易只需使用强力胶水即可修复。通过使用轻木,我可以避免购买相当昂贵的 CNC 或 3D 打印机及其配件和耗材。

然而,轻木也是一种非常脆弱的材料,led 比我想象的更早磨损手指。

除此之外,我设计的 1:1 比例“放大”了轻木的弱点。在搭建和尝试运行期间,我不得不对许多地方进行维修。作为最后一个例子,Upper Arm 上的伺服电机支架板也是由轻木制成的,经过几次拆卸后,它们开始开裂。

而且也不可能使用任何轴承,尤其是手部,因为有刻度。

尽管如此,我还是推荐使用轻木以最便宜的方式制作机械原型。

另一个瓶颈是伺服系统的尺寸和功率。由于规模,我只能使用微型和标准伺服系统,但它们的能力有限。这也是滑轮组考虑用于桡骨和上臂的旋前和屈曲的原因,因此整个手臂具有起重机式结构而不是真正的机器人结构。

为了未来 :: 教训

模仿自然机制是一个非常困难和复杂的项目,因为它们的一些功能是由机械部分(=骨骼)之间的灵活(几乎通用)连接提供的,这些连接由执行器(=肌肉)和反向弹簧(=肌腱)保持在一起。肌腱也可以被拉伸——即使是一点点,也没有肌肉那么多。

但这还不是全部。当您启动一个“部分”时,您还可以实时了解它的实际位置,以及在达到移动限制时联锁启动。这意味着,每个部分应使用一个位置传感器和至少 2 个限位开关。

正如您所注意到的,我不得不使用伺服电机,但如果您想避免使用位置传感器,则可以使用步进电机。然而,在这种情况下,每个步进器将需要 4 个数字输出,因此 2 个 Arduino 级控制器是不够的。

你认为我们完成了吗?还没有。

让我们做一个简单的实验:握住右手,手掌向上。然后右手食指弯曲,“悬”在左手掌侧。现在,试着用左手食指的指尖握住右手,同时将右手向上推。你看到了吗?达到一定限度,你可以抵抗手臂上的肌肉,仅靠控制食指骨的肌肉。

如果你试图通过模仿机制来做到这一点,几乎可以肯定它们会被破坏。即使在简单的试验中,我也烧毁了 7 个微型伺服系统。

和更多:

在自然机制上,“执行器”放置在非常靠近机械部分的位置。今天,在机械仿制品上做到这一点的最好方法可能是使用液压缸。但在那种情况下,还应使用强大的液压泵系统、储油器、止回阀、电磁阀……这将带来更大的空间需求和控制器+输入/输出能力的必要性。

那些最新的 2 段意味着,需要更小和更强大的执行器。

完成的?不,还有一个问题:

让你试着动动手指……

现在尝试同时移动 2 或 3 个手指……很好。

尝试移动所有手指,同时在手腕处上下移动手,然后左右移动……完美。

在杠杆前臂的同时转动手腕,移动所有手指……

[ 什么?你还好吗?看着我,我们带你去诊所好吗?:))]

如果你没有任何严重的健康问题,你可以做所有的事情,对吧?现在我们如何用机械模仿来做到这一点?

正如我已经提到的,人的手臂和手总共有大约 30 种不同的运动(轴),因此每个都必须使用一个致动器,以便能够平滑地进行逼真的模仿。

如果我们想通过使用像 Arduino 类这样的简单控制器来做到这一点,我们只需要一个子控制器用于每个轴,一个主控制器来命令它们。子控制器应该有其特定的例程,仅定义各个部分的特定运动,以用于整个系统的预定义整体运动。Master 只需将设定点和请求的整体运动信息发送给子控制器,然后向 all in once 发送“执行”命令,并等待确认。

例如,每个子控制器分别专用于食指、中指、无名指和小指的近端、内侧和远端部分(因此我们谈论的是 12 个子控制器以及致动器、位置传感器、限位开关和根据需要等等)应该有 - 例如 - 一个“ Hold_a_Circular_Plate()”子例程仅为相应部分定义特定运动。当主控制器发送“ Command_to_Hold_Circular_Plate() ”时,它应该只触发一个输出同时到达所有子控制器。但另一个重要问题是在微秒内同步子控制器,否则所有操作都会失败。

第二种选择是使用功能更强大的控制器和响应速度更快的执行器,这样不同执行器之间的切换将无法识别。

第三种选择是最好的,如果可能的话:使用有史以来最强大的控制器,能够以极快的速度执行绝对多处理:有机大脑和神经系统,最好是人脑用于人形手臂(虚构它是在 Robocop 系列中完成的)。

:: 结尾 ::

即便是现在也有一些公司在为残疾人制造人形假手,即使设计的再好,为人们的生活带来便利,也不能说是完美的模仿。

我想我们几乎所有人都可能见过波士顿动力公司设计和建造的最神奇的机器人“阿特拉斯”。这样的研究和开发消耗大量的金钱、时间,需要长时间的研究、试验、无数次的失败等等。例如,波士顿动力公司成立于 1992 年,从那时起他们就一直在研究机器人。这意味着,到今天为止,Atlas 背后有近 30 年的研究、试验、失败、重试……等等。

在预算方面,Atlas 的“父亲”和“母亲”由美国国防高级研究计划局资助。是的,不幸的是,军事机构是这些惊人项目的主要支持者,人类仍然非常善于寻找最好的自相残杀方式:(

市场上的消费品无法制造出如此惊人的东西。所有机械、电子、执行器、传感器……都应根据项目量身定制的定制参数进行专门和单独设计。

也许最近发明的人造肌肉可能有助于开发更好的假肢。

在所有情况下,我希望你能从机械和控制的角度发现所有那些冗长的故事,还有你的身体……


声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

评论(0)
发评论

下载排行榜

全部0条评论

快来发表一下你的评论吧 !

'+ '

'+ '

'+ ''+ '
'+ ''+ ''+ '
'+ ''+ '' ); $.get('/article/vipdownload/aid/'+webid,function(data){ if(data.code ==5){ $(pop_this).attr('href',"/login/index.html"); return false } if(data.code == 2){ //跳转到VIP升级页面 window.location.href="//m.obk20.com/vip/index?aid=" + webid return false } //是会员 if (data.code > 0) { $('body').append(htmlSetNormalDownload); var getWidth=$("#poplayer").width(); $("#poplayer").css("margin-left","-"+getWidth/2+"px"); $('#tips').html(data.msg) $('.download_confirm').click(function(){ $('#dialog').remove(); }) } else { var down_url = $('#vipdownload').attr('data-url'); isBindAnalysisForm(pop_this, down_url, 1) } }); }); //是否开通VIP $.get('/article/vipdownload/aid/'+webid,function(data){ if(data.code == 2 || data.code ==5){ //跳转到VIP升级页面 $('#vipdownload>span').text("开通VIP 免费下载") return false }else{ // 待续费 if(data.code == 3) { vipExpiredInfo.ifVipExpired = true vipExpiredInfo.vipExpiredDate = data.data.endoftime } $('#vipdownload .icon-vip-tips').remove() $('#vipdownload>span').text("VIP免积分下载") } }); }).on("click",".download_cancel",function(){ $('#dialog').remove(); }) var setWeixinShare={};//定义默认的微信分享信息,页面如果要自定义分享,直接更改此变量即可 if(window.navigator.userAgent.toLowerCase().match(/MicroMessenger/i) == 'micromessenger'){ var d={ title:'机械设计开源分享',//标题 desc:$('[name=description]').attr("content"), //描述 imgUrl:'https://'+location.host+'/static/images/ele-logo.png',// 分享图标,默认是logo link:'',//链接 type:'',// 分享类型,music、video或link,不填默认为link dataUrl:'',//如果type是music或video,则要提供数据链接,默认为空 success:'', // 用户确认分享后执行的回调函数 cancel:''// 用户取消分享后执行的回调函数 } setWeixinShare=$.extend(d,setWeixinShare); $.ajax({ url:"//www.obk20.com/app/wechat/index.php?s=Home/ShareConfig/index", data:"share_url="+encodeURIComponent(location.href)+"&format=jsonp&domain=m", type:'get', dataType:'jsonp', success:function(res){ if(res.status!="successed"){ return false; } $.getScript('https://res.wx.qq.com/open/js/jweixin-1.0.0.js',function(result,status){ if(status!="success"){ return false; } var getWxCfg=res.data; wx.config({ //debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。 appId:getWxCfg.appId, // 必填,公众号的唯一标识 timestamp:getWxCfg.timestamp, // 必填,生成签名的时间戳 nonceStr:getWxCfg.nonceStr, // 必填,生成签名的随机串 signature:getWxCfg.signature,// 必填,签名,见附录1 jsApiList:['onMenuShareTimeline','onMenuShareAppMessage','onMenuShareQQ','onMenuShareWeibo','onMenuShareQZone'] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2 }); wx.ready(function(){ //获取“分享到朋友圈”按钮点击状态及自定义分享内容接口 wx.onMenuShareTimeline({ title: setWeixinShare.title, // 分享标题 link: setWeixinShare.link, // 分享链接 imgUrl: setWeixinShare.imgUrl, // 分享图标 success: function () { setWeixinShare.success; // 用户确认分享后执行的回调函数 }, cancel: function () { setWeixinShare.cancel; // 用户取消分享后执行的回调函数 } }); //获取“分享给朋友”按钮点击状态及自定义分享内容接口 wx.onMenuShareAppMessage({ title: setWeixinShare.title, // 分享标题 desc: setWeixinShare.desc, // 分享描述 link: setWeixinShare.link, // 分享链接 imgUrl: setWeixinShare.imgUrl, // 分享图标 type: setWeixinShare.type, // 分享类型,music、video或link,不填默认为link dataUrl: setWeixinShare.dataUrl, // 如果type是music或video,则要提供数据链接,默认为空 success: function () { setWeixinShare.success; // 用户确认分享后执行的回调函数 }, cancel: function () { setWeixinShare.cancel; // 用户取消分享后执行的回调函数 } }); //获取“分享到QQ”按钮点击状态及自定义分享内容接口 wx.onMenuShareQQ({ title: setWeixinShare.title, // 分享标题 desc: setWeixinShare.desc, // 分享描述 link: setWeixinShare.link, // 分享链接 imgUrl: setWeixinShare.imgUrl, // 分享图标 success: function () { setWeixinShare.success; // 用户确认分享后执行的回调函数 }, cancel: function () { setWeixinShare.cancel; // 用户取消分享后执行的回调函数 } }); //获取“分享到腾讯微博”按钮点击状态及自定义分享内容接口 wx.onMenuShareWeibo({ title: setWeixinShare.title, // 分享标题 desc: setWeixinShare.desc, // 分享描述 link: setWeixinShare.link, // 分享链接 imgUrl: setWeixinShare.imgUrl, // 分享图标 success: function () { setWeixinShare.success; // 用户确认分享后执行的回调函数 }, cancel: function () { setWeixinShare.cancel; // 用户取消分享后执行的回调函数 } }); //获取“分享到QQ空间”按钮点击状态及自定义分享内容接口 wx.onMenuShareQZone({ title: setWeixinShare.title, // 分享标题 desc: setWeixinShare.desc, // 分享描述 link: setWeixinShare.link, // 分享链接 imgUrl: setWeixinShare.imgUrl, // 分享图标 success: function () { setWeixinShare.success; // 用户确认分享后执行的回调函数 }, cancel: function () { setWeixinShare.cancel; // 用户取消分享后执行的回调函数 } }); }); }); } }); } function openX_ad(posterid, htmlid, width, height) { if ($(htmlid).length > 0) { var randomnumber = Math.random(); var now_url = encodeURIComponent(window.location.href); var ga = document.createElement('iframe'); ga.src = 'https://www1.elecfans.com/www/delivery/myafr.php?target=_blank&cb=' + randomnumber + '&zoneid=' + posterid+'&prefer='+now_url; ga.width = width; ga.height = height; ga.frameBorder = 0; ga.scrolling = 'no'; var s = $(htmlid).append(ga); } } openX_ad(828, '#berry-300', 300, 250);