Python 制造机械臂详解

最后,我们是我们大多数人自本书开始以来一直想要的地方。做一个机械手臂!在本章中,我们将学习机械臂工作背后的概念。毫无疑问,我们也将制造一个供我们个人使用的机械臂,它可以为我们做无限的事情。

如果你看到一个人的身体,那么使我们能够与大多数其他物种不同的最独特的部位之一就是手臂。这是我们用来做大部分工作的身体部位。

人类的手臂是一个由关节和肌肉组成的非常复杂的机构,它们协同工作,赋予它我们所知道的灵巧性。以我们的肩关节为例。如果你注意的话,你会注意到它有上下左右移动的能力,甚至可以绕自己的轴旋转,而所有这些都是因为它只有一个关节,我们称之为球关节。

当我们谈论机器人上的机械臂时,毫无疑问,我们谈论的是一个复杂的执行器与身体的排列,或者称为底盘,以在三维空间中获得所需的运动。

现在,让我们了解一下机械臂的一些基本部件。第一部分是执行器。我们可以用马达来控制机械臂;然而,正如我们之前所研究的,使用我们以前使用过的电机将不是理想的解决方案,因为它无法保持其位置,也没有反馈机制。所以我们只有一个选择,那就是使用伺服电机。正如我们所知,他们有一把扭矩,有能力知道它在哪里,并保持它的位置,只要我们想。

机器人的第二部分是底盘,即将所有电机固定在一起并为机器人提供结构支撑的部分。这必须以这样的方式进行,即它向任何给定的关节提供所有期望轴上的运动。这一点很重要,因为单个伺服只能在一个轴上提供运动。然而,在多个地方,复杂的排列可以用来使机器人在多个轴上移动。此外,底盘应具有刚性,这一点非常重要。我们都知道,这个星球上的所有物质都有一定程度的灵活性。此外,材料的构造取决于材料的不合规程度。这是重复性的一个非常重要的目的。

现在,什么是可重复性?正如你可能在工业或任何制造单位看到的那样,机器人已经安装好,它们一遍又一遍地完成同样的任务。这是可能的,因为机器人被编程为在特定情况下执行一组特定功能。现在,让我们假设机器人的底盘不是刚性的。在这种情况下,即使伺服是 100%精确的,并且一次又一次地到达完全相同的位置,机器人仍然可能实际上与其实际目标位置不同。这是因为底盘可能具有一定的灵活性,这就是最终位置可能不同的原因。因此,必须安装正确的机箱。当我们谈论大型机器人时,它变得更加重要,因为即使是最轻微的变形也会导致手臂最终位置发生非常大的变化。

我们在谈论机器人手臂时使用的一个非常常见的术语是末端执行器。这基本上是机器人手臂的末端,它将为我们完成所有的最后工作。在真实人类手臂的情况下,末端执行器可以被视为手。这是在手臂的顶部,手臂的所有运动基本上是在三维空间中表达手的位置。此外,是手拿起物体或做必要的身体动作。因此,术语“末端效应器”。

现在,当机械臂在三维空间中运动时,定义运动发生的轴就成了一个真正的大问题。因此,我们通常使用正在执行的运动类型,而不是使用轴来定义运动,这使我们能够真实地了解运动是什么以及运动可能位于哪个轴上。为了分析运动,我们使用了偏航俯仰和横滚YPR的概念)。

上图将澄清关于 YPR 的大部分疑问。这一概念通常用于飞机;然而,它也是机械手的重要组成部分。如上图所示,当飞机机头上升或下降时,它将被视为俯仰运动。同样,如果飞机改变航向,偏航可被视为相应改变偏航只不过是飞机在y轴上的运动。最后,我们有一个叫做的东西。它用于理解旋转角度。如您所见,所有这三个实体都是相互独立的,追逐其中任何一个实体都不会对另一个实体产生任何影响。这一概念也很有用,因为无论飞机的方向是什么,YPR 都将保持不变,并且非常容易理解。因此,我们把这个概念直接从飞机上带到我们的机器人上。

最后,我们怎么能忘记处理单元呢?它是指挥所有执行机构并进行协调和决策的单元。在我们的例子中,这个处理单元是 Raspberry Pi,它将命令所有的执行器。所有这些前面的组件组成了一个机械臂。

并不是每个机械臂都是一样的。它们具有不同的额定载荷,即末端效应器可以承受的最大载荷、速度和到达距离,即末端效应器可以到达的距离。然而,机械臂的一个非常重要的部分是它的电机数量。因此,对于每个轴,您至少需要一个电机使机器人在该轴上移动。例如,人的手臂在肩关节中具有三维自由度。因此,要模拟该关节,每个轴都需要一个电机,也就是说,手臂在所有三个轴上独立移动至少需要三个电机。类似地,当我们谈论手的肘关节时,它只能在二维中移动。这就是手臂的闭合和打开,最后是手臂的旋转,肘部在三维空间中不移动。因此,为了复制它的运动,我们至少需要两个电机,这样我们就可以在w轴上移动机器人。

根据我们目前所了解的情况,我们可以有把握地假设,电机的数量越多,机器人也会越灵巧。大多数情况下都是这样;但是,您可以使用多个电机使机器人在单轴上转动。在这种情况下,计算执行器数量以确定机器人灵巧度的基本概念将不起作用。那么,我们如何确定机器人的灵巧程度呢?

我们有一个称为自由度自由度的概念)。如果我按照标准定义去做,那么我可以非常肯定的是,你会对它的实际含义感到困惑。如果你不相信,那就自己在谷歌上试试看。DOF,用非常简单明了的英语来说,是一个可以在任何给定轴上独立移动的关节。例如,如果我们谈论的是肩关节,那么我们在所有三个轴上都有运动。因此,自由度将是三个。现在,让我们考虑一下我们手臂的肘关节。因为它只能在俯仰和滚动中移动,所以我们最终有两个自由度。如果我们将肩关节和肘关节连接起来,那么自由度将相加,整个系统将被称为有六个自由度。请记住,这是一个非常简单的定义。如果你选择深入挖掘,你会遇到多种复杂情况。

现在,您将遇到的大多数机械臂都有接近六个自由度。虽然你可能会说它比人的手臂少,但实际上,它完成了大部分工作,而且显然,自由度少意味着电机数量少,从而降低了成本,明显降低了编程的复杂性。因此,我们尝试使用尽可能少的自由度。

现在,在上图中,您可以看到一个典型的机械臂,它有六个自由度。编号为1的基座执行机构可自由滚动和改变节距。编号为2的肘部执行器仅向机器人添加一个俯仰自由度。此外,接头编号3is 具有在节距&辊中移动的能力。最后,我们将末端执行器作为夹持器;夹持器本身有一个自由度。所以,累积起来,我们可以说这个机器人是一个六自由度的机器人。

我们在所有项目中都使用了一个单元,但我想在本章中强调它。这个装置就是动力装置。我们之所以讨论它,是因为在本章中,我们将控制多个伺服系统。当我们谈论多个伺服时,我们自然会谈论大量的功耗。在机器人手臂中,我们有六个伺服电机。现在,根据电机的品牌和型号,功耗会有所不同。但让自己站在更安全的一边,并假设每个伺服的功耗在 1 安培左右将是一个好主意。您将使用的大多数电源可能无法提供这么大的突发电流。那我们该怎么办呢?

我们可以采取简单的方法,获得更高的功率输出。但是,我们可以走非传统路线。我们可以有一个电池,可以在需要时提供这么多的电力。但是,问题是,任何电池都能解决我们的目的吗?显然,答案是否定的。

存在多种类型的电池。这些电池可根据以下参数进行区分:

这些将在接下来的小节中详细介绍。

电压是电池可以产生的总电位差。每个电池都有一个特定的电压。需要记住的一件事是,该电压将根据蓄电池的充电条件略有变化。也就是说,当 12V 电池充满电时,其输出可能为 12.6V。然而,当它完全放电时,它可能达到 11.4V。所以,电池电压是指电池提供的标称电压。

现在,第二个参数是容量。一般来说,当你购买电池时,你会看到它的容量以毫安时毫安时)或安培时)为单位。这是一个非常简单的术语。让我用一个例子来解释这个术语。假设您有一个容量为 5 Ah 的电池。现在,如果我连续 1 小时消耗 5 安培,那么电池将完全放电。相反,如果我连续消耗 10 安培,电池将在半小时内放电。由此,我们还可以使用以下简单公式得出电池的总功率:总功率(瓦特)=电池标称电压 x 电池总容量(安培)

因此,如果您的电池为 12V,容量为 10Ah,则以瓦特为单位的总容量为 120 瓦特。

重量在机器人技术中起着非常关键的作用,如果我们增加机器人的重量,那么移动机器人所需的力就会成倍增加。因此,功率重量比的概念开始发挥作用。我们总是更喜欢电池,它是非常轻,并提供了一个大数额的权力相对于重量。功率重量比公式可定义如下:功率重量比(瓦时/kg)=最大功率(瓦特)/电池总重量(kg)。

现在,假设一个电池提供 500 瓦的功率,重量为 5 千克,那么功率重量比将为 100 Wh/kg。功率重量比越高,电池就越好。

这可能是电池最关键的部分之一。电池通常能够使机器人运行 1 小时。然而,机器人的功耗并不是恒定的。假设 90%的时间里,我们的机械臂消耗 2 安培的电力,因此电池容量为 2 Ah。然而,在操作过程中的某些时间点,机器人需要所有电机以峰值功率工作。机器人的峰值功耗约为 6 安培。现在的问题是,2 Ah 的电池能否为机器人提供 6 安培的电力?

这是一个非常实际的挑战。您可能会说,最好使用比 2 Ah 电池大得多的电池。但是,如你所知,它会显著增加重量。那么解决办法是什么呢?

有一种叫做峰值放电电流的东西。这由C等级表示。因此,如果我们的电池额定值为 1 C,那么在任何给定时间,2 Ah 电池最多只能为我们提供 2 Ah 的电源。但是,如果电池额定值为 10 C,则应能够提供高达 20 安培的突发电源。现在,你可以找到能提供高达 100 摄氏度甚至更高的突发电源的电池。我们这样做的原因是,机器人的峰值功耗可能比其恒定功耗高出一倍。如果电池在任何时候都不能提供足够的电力,那么机器人就会出现错误,甚至会关机。

这个故事的第二部分是收费率。这是您可以提供给蓄电池的最大充电电流。它也由相同的 C 等级表示。因此,如果 C 额定值为 0.5,则可以为 2 Ah 电池提供最大 1 安培的充电。

换句话说,你能给电池充电的最快时间是 2 小时。

你可以在市场上找到不同类型的电池,它们的化学成分不同。所有这些电池都有各自的优缺点。因此,我们不能说一个比另一个好。这始终是各种因素之间的权衡。以下是您可以在市场上找到的电池列表及其优缺点:

| 电池 | 峰值功率输出 | 功率重量比 | 价格 | | 湿电池 | 低的 | 极低 | 最便宜的 | | 金属氢化物镍 | 中等的 | 低的 | 花钱少的 | | 锂离子 | 高的 | 好的 | 高的 | | 锂聚合物 | 极高 | 非常好 | 极高 |

从这个表中可以看出,峰值功率输出是我们非常想要的,良好的功率重量比也是如此;因此,花大量的钱在锂聚合物电池上是有意义的。

这些电池的额定值至少为 20℃,功率重量比大约是普通湿电池的五倍。然而,它们的价格可能是普通湿电池的 10 倍。

现在我们知道了要选择哪些电池来满足更高的电流要求。11.1V 和 2200 毫安时的锂聚合物电池的价格不会超过 20 美元,并将为您提供您可能永远不需要的巨大电力。所以,我们已经解决了供电问题。现在是时候让机器人手开始工作了。

从 eBay 或亚马逊购买机械臂套件相当容易。这不是很难组装,需要几个小时来准备。一些机械臂套件可能没有随附伺服电机,在这种情况下,您可能需要单独订购。我想说的是,购买随伺服系统捆绑的套件,因为如果您选择单独订购伺服系统,可能会存在兼容性问题。

如你所知,这些伺服将使用 PWM 工作,控制它们也不难。那么,让我们直接开始,看看我们能做些什么。组装好机械臂套件后,按如下方式连接伺服系统的电线:

现在,首先,我们需要知道机器人上连接的每个伺服的最大物理极限是多少。有多种技术可以做到这一点。最基本的方法是物理测量。这种方法可能很好,但你永远无法充分利用伺服电机的潜力,因为你在测量时会有一定程度的误差。因此,您在伺服中输入的值将略小于您认为它可以达到的值。第二种方法是手动输入数据并找出准确的角度。那么,让我们继续第二种方法,并上传以下代码:

import RPi.GPIO as GPIO
import time

GPIO.setmode(GPIO.BCM)
GPIO.setup(14,GPIO.OUT)
GPIO.setup(16,GPIO.OUT)
GPIO.setup(18,GPIO.OUT)
GPIO.setup(20,GPIO.OUT)
GPIO.setup(21,GPIO.OUT)
GPIO.setup(22,GPIO.OUT)

GPIO.setwarnings(False)

pwm1 = GPIO.PWM(14, 50)
pwm2 = GPIO.PWM(16, 50)
pwm3 = GPIO.PWM(18, 50)
pwm4 = GPIO.PWM(20, 50)
pwm5 = GPIO.PWM(21, 50)
pwm6 = GPIO.PWM(22, 50)

pwm1.start(0)
pwm2.start(0)
pwm3.start(0)
pwm4.start(0)
pwm5.start(0)
pwm6.start(0)

def cvt_angle(angle):
    dc = float(angle/90) + 0.5
    return dc

while 1:

 j = input('select servo')

 if j == 1:

  i = input('select value to rotate')
  pwm1.ChangeDutyCycle(cvt_angle(i))
  time.sleep(2)
  pwm1.ChangeDutyCycle(cvt_angle(90))

 elif j ==2:

  i = input('select value to rotate')
  pwm2.ChangeDutyCycle(cvt_angle(i))
  time.sleep(2)
  pwm2.ChangeDutyCycle(cvt_angle(90))

 elif j ==3:

 i = input('select value to rotate')
 pwm3.ChangeDutyCycle(cvt_angle(i))
 time.sleep(2)
 pwm3.ChangeDutyCycle(cvt_angle(90))

 elif j ==4: 

 i = input('select value to rotate')
 pwm4.ChangeDutyCycle(cvt_angle(i))
 time.sleep(2)
 pwm4.ChangeDutyCycle(cvt_angle(90))

 elif j ==5:

 i = input('select value to rotate')
 pwm5.ChangeDutyCycle(cvt_angle(i))
 time.sleep(2)
 pwm5.ChangeDutyCycle(cvt_angle(90))

 elif j ==6:

 i = input('select value to rotate')
 pwm6.ChangeDutyCycle(cvt_angle(i))
 time.sleep(2)
 pwm6.ChangeDutyCycle(cvt_angle(90)) }

现在,让我们看看这段代码在做什么。这段代码看起来可能非常复杂,但它所做的事情非常简单。

    j = input('select servo from 1-6')

使用前一行代码,我们正在为用户select servo from 1-6打印语句。当用户输入伺服值时,该值存储在变量j中:

 if j == 1:

  i = input('select value to rotate')
  pwm1.ChangeDutyCycle(cvt_angle(i))
  time.sleep(2)
  pwm1.ChangeDutyCycle(cvt_angle(90)) 

此处的if条件检查j的值。如果在此行中为j=1,则会运行伺服编号1对应的代码。在此代码内,第一行将打印select value to rotate。完成后,程序将等待用户输入。一旦用户输入任何值,它将被存储在一个名为I的变量中。此后,使用cvt_angle(i)功能,用户输入到系统中的值将转换为相应的占空比值。此占空比值将被提取到pwm1.ChangeDutyCycle()参数,从而为机器人提供您想要的特定关节的特定角度。由于time.sleep(2)功能,伺服将等待转到下一行。此后,我们将使用线pwm1.ChangeDutyCycle(cvt_angle(90)),它将使其返回到 90 度。

你可能会问,我们为什么要这样做?这是一个非常重要的原因。假设你已经给了它一个超越物理极限的命令。如果是这样的话,那么不管发生什么,伺服都会继续朝那个方向移动。但是,由于物理约束,它将无法继续。一旦发生这种情况,那么,在几秒钟内,您将看到蓝烟从伺服中冒出,表明其已死亡。问题是,犯此类错误非常容易,损失也相当明显。因此,为了防止这种情况发生,我们迅速将其带回中心位置,在那里它没有任何燃烧的可能性。

现在,根据前面的代码,通过机器人对伺服 1-6 也进行了同样的操作。现在你知道发生了什么,是时候拿一支笔和一张纸开始给伺服提供角度值了。请记住,此代码的最终目标是找出最大限制。那么,让我们从 90 度开始。在任意一侧给它一个值,直到你可以取哪个值为止,不要向下。在纸上列出一个清单,因为我们将在下一个代码中需要它。

在本章的前一部分,通过我们的多次尝试,我们已经能够找到每个伺服的最大位置。现在是使用这些值的时候了。在本章中,我们将为伺服系统的绝对最大值编程。在本程序中,我们将确保伺服系统永远不需要移动,即使超出两侧定义的参数。如果用户给出了一个超出它的值,那么它将简单地选择忽略用户输入,而不是造成自我伤害。

那么,让我们看看如何完成它。该程序的某些部分的数值是粗体的。这些是您需要用我们在本章上一节中提到的值替换的值。例如,对于伺服 1,记下的值为23170作为任意一侧的最大值。因此,代码将从if a[0] < 160 and a[0] > 30更改为ifa[0] < 170 and a[0] > 23。同样,对于每个伺服,必须遵循相同的程序:

import RPi.GPIO as GPIO
import time

GPIO.setmode(GPIO.BCM)
GPIO.setup(14,GPIO.OUT)
GPIO.setup(16,GPIO.OUT)
GPIO.setup(18,GPIO.OUT)
GPIO.setup(20,GPIO.OUT)
GPIO.setup(21,GPIO.OUT)
GPIO.setup(22,GPIO.OUT)

GPIO.setwarnings(False)

pwm1 = GPIO.PWM(14, 50)
pwm2 = GPIO.PWM(16, 50)
pwm3 = GPIO.PWM(18, 50)
pwm4 = GPIO.PWM(20, 50)
pwm5 = GPIO.PWM(21, 50)
pwm6 = GPIO.PWM(22, 50)

pwm1.start(cvt_angle(90))
pwm2.start(cvt_angle(90))
pwm3.start(cvt_angle(90))
pwm4.start(cvt_angle(90))
pwm5.start(cvt_angle(90))
pwm6.start(cvt_angle(90))

def cvt_angle(angle):
    dc = float(angle/90) + 0.5
    return dc

while True:

    a = raw_input("enter a list of 6 values")

    if a[0] < 160 and  a[0] > 30:
        pwm1.ChangeDutyCycle(cvt_angle(a[0]))

    if a[1] < 160 and  a[1] > 30:
        pwm2.ChangeDutyCycle(cvt)angle(a[1]))

    if a[0] < 160 and  a[0] > 30:
        pwm3.ChangeDutyCycle(cvt_angle(a[2]))

    if a[0] < 160 and  a[0] > 30:
        pwm4.ChangeDutyCycle(cvt_angle(a[3]))

    if a[0] < 160 and  a[0] > 30:
        pwm5.ChangeDutyCycle(cvt_angle(a[4]))

    if a[0] < 160 and  a[0] > 30:
        pwm6.ChangeDutyCycle(cvt_angle(a[5]))}

现在,在这段代码中,我们做了一些非常基本的事情。您可以放心地说,我们所做的只是将ChangeDutyCycle()函数放入if语句中。此if语句将控制伺服是在其自身位置移动还是保持静止。对一些人来说,把这个程序放在一个特别的部分似乎很幼稚。但是,相信我,事实并非如此。从现在起,此语句将作为每个程序的一部分使用。为伺服装置的运动编写的所有代码都必须检查通过本if语句发送至伺服装置的最终值;因此,代码的基本可视化是非常必要的。

现在,解释已经完成,是时候给出不同的命令,看看它们是否在安全工作限制内工作了。

在上一章中,我们学习了如何确保机器人在安全极限下工作的基本知识。在本章中,我们将研究如何让机器人在点击按钮时执行不同的活动,而不是逐个键入值。

要做到这一点,我们需要了解一些先进的运动概念。每当您观看任何视频或玩任何视频游戏时,您一定遇到过术语每秒帧数每秒帧数。如果你没听过这个词,那么让我给你解释一下。现在制作的每个视频实际上都是由静止图像制作的。这些静止图像是由每秒点击 25-30 次的相机拍摄的。当这些图像在屏幕上以捕获它们的相同速率播放时,就会形成平滑的视频。

同样,在机器人中,我们也有框架的概念。然而,这些帧不是图像,而是机器人实现特定运动所必须遵循的多个步骤。在一个简单的机器人程序中,可能只有两个帧,即初始帧和最终帧。这两个框架将对应于初始位置或最终位置。

然而,在现实世界中,这并不总是可能的,因为每当机器人直接从初始位置移动到最终位置时,它都会倾向于具有特定曲率的特定路径。然而,在这条道路上可能会有障碍,或者这条道路并不理想,因为需要遵循的道路可能是另一条。因此,我们需要框架。这些框架不仅定义了机器人从初始位置到最终位置的运动,还将从这两个位置的过渡分解为多个步骤,使机器人沿着所需的路径移动。

这可以称为框架编程,我们将在本章中介绍。需要记住的一件事是,帧数越多,机器人的功能就越顺畅。你还记得我们看到的闭路电视录像吗?我们可以说它不平滑,而且有很多急动。这是由于闭路电视摄像机的低帧速率造成的。他们不是以 30 FPS 的速度工作,而是以 15 FPS 的速度工作。这样做是为了减少视频的存储空间。但是,如果您看到最新的视频,有些游戏和视频的帧速率要比正常情况高得多。我们的一些最新相机以每秒 60 帧的速度拍摄,使视频更流畅,观看起来更愉快。机器人也是如此。帧数越多,运动就越平滑和受控。但是,请确保不要过度使用。

现在,要从一个位置移动到另一个位置,我们必须在一开始就把每个伺服系统的角度值放进去。一旦获取,它将自动开始逐个执行这些值。为此,请继续编写以下代码:

import RPi.GPIO as GPIO
import time

GPIO.setmode(GPIO.BCM)
GPIO.setup(14,GPIO.OUT)
GPIO.setup(16,GPIO.OUT)
GPIO.setup(18,GPIO.OUT)
GPIO.setup(20,GPIO.OUT)
GPIO.setup(21,GPIO.OUT)
GPIO.setup(22,GPIO.OUT)

GPIO.setwarnings(False)

pwm1 = GPIO.PWM(14, 50)
pwm2 = GPIO.PWM(16, 50)
pwm3 = GPIO.PWM(18, 50)
pwm4 = GPIO.PWM(20, 50)
pwm5 = GPIO.PWM(21, 50)
pwm6 = GPIO.PWM(22, 50)

pwm1.start(0)
pwm2.start(0)
pwm3.start(0)
pwm4.start(0)
pwm5.start(0)
pwm6.start(0)

def cvt_angle(angle):
    dc = float(angle/90) + 0.5
    return dc

prev0 = 90
prev1 = 90
prev2 = 90
prev3 = 90
prev4 = 90
prev5 = 90 

while True:

    a = raw_input("enter a list of 6 values for motor 1")
    b = raw_input("enter a list of 6 values for motor 2")
    c = raw_input("enter a list of 6 values for motor 3")
    d = raw_input("enter a list of 6 values for motor 4")
    e = raw_input("enter a list of 6 values for motor 5")
    f = raw_input("enter a list of 6 values for motor 6")

    for i in range(6):

        if a[i] > 10 and a[i]< 180 :  
            pwm1.ChangeDutyCycle(cvt_angle(a[i]))

        if b[i] > 10 and b[i] < 180:
            pwm2.ChangeDutyCycle(cvt_angle(b[i]))

        if c[i] > 10 and c[i] < 180:
            pwm3.ChangeDutyCycle(cvt_angle(c[i]))

        if d[i] > 10 and d[i] < 180:
            pwm4.ChangeDutyCycle(cvt_angle(d[i]))

        if e[i] > 10 and e[i] < 180:
           pwm5.ChangeDutyCycle(cvt_angle(e[i]))

        if f[i] > 10 and f[i] < 180:
           pwm6.ChangeDutyCycle(cvt_angle(f[i]))

在这个程序中,您可以看到我们复制了以前的程序,只做了一些非常小的更改。那么,让我们看看这些变化是什么:

    a = raw_input("enter a list of 6 values for motor 1")
    b = raw_input("enter a list of 6 values for motor 2")
    c = raw_input("enter a list of 6 values for motor 3")
    d = raw_input("enter a list of 6 values for motor 4")
    e = raw_input("enter a list of 6 values for motor 5")
    f = raw_input("enter a list of 6 values for motor 6")

这里,我们获取每个伺服的输入值,并将其存储在不同的列表中。伺服 1 使用列表a;同样地,b将用于伺服 2,依此类推直到f,在前面的代码行中,机器人会提示用户填写motor 1的六帧值。然后,它将询问motor 2的六个值,类似地,直到motor 6

    for i in range(6):

为伺服提供 PWM 的整个程序都集中在这个 for 回路中。该循环将检查i的值,并每次递增。i的值将从1开始,循环将运行并增加i的值,直到达到6

        if a[i] > 10 and a[i]< 180 :  
            pwm1.ChangeDutyCycle(cvt_angle(a[i]))

在程序的这一行中,列表中包含的值以1的值为标题。因此,它将第一次读取a[1]的值,该值将对应于列表a[]的第一个值。该值应在安全工作极限之间,因此为if回路。如果在安全工作范围内,则处于if状态的程序将工作,否则将不工作。在if循环中,我们有一个简单的语句:pwm1.ChangeDutyCycle(cvt_angle(a[I]))。这将简单地获取a[1]的值,并将其转换为相应的 PWM 值,然后将其提取到ChangeDutyCycle()功能,该功能将改变伺服 1 的 PWM。

对于其他伺服系统,以及从伺服 1 到伺服 6 的伺服系统,也制定了类似的程序。因此,所有这些都将逐个读取相应列表的值,并按照用户编程的方式更改伺服角度。此外,随着循环的执行,i的值将增加,从而使程序读取列表中获取的不同值。列表中伺服的每个值将对应于不同的帧,因此通过它解析机器人。

所以,让你的机器人做一些很棒的动作吧,尽情享受吧。小心点,对它温柔点!

能如此轻松地制造出一个机器人手臂真是太神奇了,只需一点代码,我们现在就可以按我们想要的方式控制它了。然而,有一个问题你可能已经注意到了,那就是,机器人正在以我们想要的方式移动,但不是以我们想要的速度移动。这是使用基于数字 PWM 的伺服系统时的一个非常常见的问题。

这些伺服没有内置的速度控制。他们的控制系统被编程为尽可能快地移动伺服以达到目标位置。因此,为了控制速度,我们必须对程序本身进行处理,并给它一个平滑的线性进程。

速度控制可以通过几种不同的技术来实现。所以,不用多说,让我们去看看代码。在编写它之前,请先阅读并浏览代码一次,然后查看下面的解释。此后,您将对我们正在做的事情有更好的了解。这将使编写代码更快更容易。那么,让我们来看一看:

import RPi.GPIO as GPIO
import time

GPIO.setmode(GPIO.BCM)
GPIO.setup(14,GPIO.OUT)
GPIO.setup(16,GPIO.OUT)
GPIO.setup(18,GPIO.OUT)
GPIO.setup(20,GPIO.OUT)
GPIO.setup(21,GPIO.OUT)
GPIO.setup(22,GPIO.OUT)

GPIO.setwarnings(False)

pwm1 = GPIO.PWM(14, 50)
pwm2 = GPIO.PWM(16, 50)
pwm3 = GPIO.PWM(18, 50)
pwm4 = GPIO.PWM(20, 50)
pwm5 = GPIO.PWM(21, 50)
pwm6 = GPIO.PWM(22, 50)

pwm1.start(0)
pwm2.start(0)
pwm3.start(0)
pwm4.start(0)
pwm5.start(0)
pwm6.start(0)

def cvt_angle(angle):
    dc = float(angle/90) + 0.5
    return dc

prev0 = 90
prev1 = 90
prev2 = 90
prev3 = 90
prev4 = 90
prev5 = 90 

pwm1.ChangeDutyCycle(cvt_angle(prev0))
pwm2.ChangeDutyCycle(cvt_angle(prev1))
pwm3.ChangeDutyCycle(cvt_angle(prev2))
pwm4.ChangeDutyCycle(cvt_angle(prev3))
pwm5.ChangeDutyCycle(cvt_angle(prev4))
pwm6.ChangeDutyCycle(cvt_angle(prev5))

while True:

 a = raw_input("enter a list of 6 values for motor 1")
 b = raw_input("enter a list of 6 values for motor 2")
 c = raw_input("enter a list of 6 values for motor 3")
 d = raw_input("enter a list of 6 values for motor 4")
 e = raw_input("enter a list of 6 values for motor 5")
 f = raw_input("enter a list of 6 values for motor 6")

    speed = raw_input("enter one of the following speed 0.1, 0.2, 0.5, 1")

 for i in range(6):

   while prev0 =! a[i] and prev1 =! b[i] and prev2 =! c[i] and prev3 =! d[i] and prev4 =! e[i] and prev 5 =! f[i]

     if a[i] > 10 and a[i]< 180 : 

        if prev0 > a[i]
            prev0 = prev0 - speed

         if prev0 < a[i]
             prev0 = prev0 + speed

         if prev0 = a[i]
             prev0 = prev0 

         pwm1.ChangeDutyCycle(cvt_angle(prev0))

    if b[i] > 10 and b[i] < 180:

        if prev2 > b[i]
            prev2 = prev2 - speed

         if prev2 < b[i]
             prev2 = prev2 + speed

         if prev2 = b[i]
            prev2 = prev2

         pwm2.ChangeDutyCycle(cvt_angle(b[i]))

    if c[i] > 10 and c[i] < 180:

        if prev3 > c[i]
             prev3 = prev3 - speed

        if prev3 < c[i]
            prev3 = prev3 + speed

        if prev3 = c[i]
             prev3 = prev3

         pwm3.ChangeDutyCycle(cvt_angle(c[i]))

    if d[i] > 10 and d[i] < 180:

        if prev4 > d[i]
             prev4 = prev4 - speed

        if prev4 < d[i]
            prev4 = prev4 + speed

        if prev4 = d[i]
             prev4 = prev4

    pwm4.ChangeDutyCycle(cvt_angle(d[i]))

     if e[i] > 10 and e[i] < 180:
         if prev5 > e[i]
             prev5 = prev5 - speed

        if prev0 < e[i]
            prev5 = prev5 + speed

        if prev5 = e[i]
             prev5 = prev5

     pwm5.ChangeDutyCycle(cvt_angle(e[i]))

     if f[i] > 10 and f[i] < 180:

         if prev6 > f[i]
            prev6 = prev6 - speed

         if prev6 < f[i]
            prev6 = prev6 + speed

        if prev6 = f[i]
            prev6 = prev6

     pwm6.ChangeDutyCycle(cvt_angle(f[i]))

 flag = 0 

在这个节目中,有很多东西。我们应该一个一个地看一看,了解一下。那么,让我们看看我们在做什么:

prev0 = 90
prev1 = 90
prev2 = 90
prev3 = 90
prev4 = 90
prev5 = 90 

在这里,我们定义了六个名为prev0prev5的新变量,并且所有变量的值都被允许为90。此处的术语prev代表 previous,因此它将在此处表示 previous 值。

        while prev0 =! a[i] and prev1 =! b[i] and prev2 =! c[i] and prev3 =! d[i]   and prev4 =! e[i] and prev 5 =! f[i]

在代码行for i in range 6之后,我们有代码的前一行,它基本上是用prev0检查a[i]的值。类似地,它正在检查b[i]prev1的值,以此类推。在所有这些都不为真之前,while循环将为真,并将在其中循环程序,直到条件不为假。也就是说,所有的prev值与列表中相应值的值完全相等。

同样,这对您来说可能有点奇怪,但是,相信我,它将非常有用,我们稍后会看到:

     if a[i] > 10 and a[i]< 180 : 

         if prev0 > a[i]
             prev0 = prev0 - speed

         if prev0 < a[i]
             prev0 = prev0 + speed

         if prev0 = a[i]
             prev0 = prev0 

         pwm1.ChangeDutyCycle(cvt_angle(prev0))

现在,真正的交易来了。这是控制伺服速度的主程序。在这方面,第一行很简单;它将检查给定给它的值是否有效,即在安全极限之间。完成后,它将检查a[Ii]的值是否小于或大于先前的值。如果大于a[i],则取上一个值,按用户指定的速度递减。如果小于a[i]的值,则将以规定的速度增加先前的值。

因此,如果您查看它,代码只是在每次while循环运行时递增或递减上一个值。现在,while循环将运行,直到prev的值等于相应的列表值。也就是说,循环将不断增加值,直到达到指定位置。

因此,速度值越低,每次的增量就越小,从而一起降低速度。

这是同样的过程,这将发生在所有其他伺服以及。听起来可能很复杂,但事实并非如此!编程很简单,每次你把它分解成小块并逐一理解它们时,它将继续保持简单!

在本章中,我们了解了机械臂的基本知识、其电源和编程。通过一个非常简单的程序,我们能够找出伺服的极限,然后应用这些极限来确保伺服不会损坏自身。我们对框架有了一个基本的概念,并基于框架进行了一些编程。最后,我们还继续使用我们自己的基本程序控制伺服的速度。

教程来源于Github,感谢apachecn大佬的无私奉献,致敬!

技术教程推荐

软件测试52讲 -〔茹炳晟〕

Go语言核心36讲 -〔郝林〕

Python核心技术与实战 -〔景霄〕

.NET Core开发实战 -〔肖伟宇〕

互联网人的英语私教课 -〔陈亦峰〕

手机摄影 -〔@随你们去〕

徐昊 · TDD项目实战70讲 -〔徐昊〕

零基础学Python(2023版) -〔尹会生〕

零基础GPT应用入门课 -〔林健(键盘)〕