Python 物联网入门指南(八)(1)https://developer.aliyun.com/article/1507342
寻找极限
机械臂套件在 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')
使用前面的代码行,我们正在为用户打印从 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
对应的代码。在这段代码中,第一行将打印选择要旋转的值
。完成后,程序将等待用户输入。一旦用户输入任何值,它将被存储在一个名为I
的变量中。然后,使用cvt_angle(i)
函数,用户输入的值将被转换为相应的占空比值。这个占空比值将被获取到pwm1.ChangeDutyCycle()
参数中,从而给予机器人你想要的特定关节角度。由于time.sleep(2)
函数,舵机将等待到下一行。之后,我们使用pwm1.ChangeDutyCycle(cvt_angle(90))
这一行,这将把它带回到 90 度。
你可能会问,为什么我们要这样做?这是一个非常重要的原因。假设您已经给它一个超出其物理极限的命令。如果是这种情况,那么舵机将继续尝试朝那个方向移动,不管发生什么。然而,由于物理限制,它将无法继续前进。一旦发生这种情况,然后在几秒钟内,您将看到蓝烟从舵机中冒出,表明它的损坏。问题在于,制造这种类型的错误非常容易,损失是非常明显的。因此,为了防止这种情况,我们迅速将其带回到中心位置,这样它就不会有任何烧毁的可能性。
现在,根据前面的代码,通过机器人对舵机 1-6 执行相同的操作。现在你知道发生了什么,是时候拿起笔和纸开始给舵机赋予角度值了。请记住,这段代码的最终目标是找出最大限制。因此,让我们从 90 度开始做起。在每一侧给它一个值,直到你能够接受的值。在纸上列出清单,因为我们将需要它用于下一段代码。
使机器人安全
在本章的前一部分中,通过我们的多次尝试,我们已经能够找到每个舵机的最大位置。现在是时候使用这些值了。在本章中,我们将为舵机编程其绝对最大值。在这个程序中,我们将确保舵机永远不需要超出两侧的定义参数。如果用户给出超出它的值,那么它将选择忽略用户输入,而不是造成自身损坏。
那么,让我们看看如何完成它。在程序的某些部分,数字值已经用粗体标出。这些是您需要用本章前面记录的值替换的值。例如,对于舵机 1,记录下的值是23
和170
,作为每一侧的最大值。因此,代码的更改将从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
语句传递给舵机的最终值,必须检查为舵机移动编写的所有代码;因此,对代码的基本可视化是非常必要的。
现在解释完毕,是时候给出不同的命令并查看它们是否在安全工作限制内工作了。
编写多个帧
在上一章中,我们已经学习了如何确保机器人在安全限制下工作的基础知识。在本章中,我们将看看如何使机器人能够在点击按钮的同时执行不同的活动,而不是逐个输入值。
为了做到这一点,我们需要了解一些高级运动概念。每当您观看任何视频或玩任何视频游戏时,您一定会遇到“每秒帧数”(FPS)这个术语。如果您还没有听说过这个术语,那么让我为您解释一下。现在制作的每个视频实际上都是由静止图像制成的。这些静止图像是由摄像机捕捉的,每秒点击 25-30 次。当这些图像以与它们被捕捉的速率相同的速率在屏幕上播放时,它形成了一个平滑的视频。
同样,在机器人中,我们也有帧的概念。然而,这些帧不是图像,而是机器人必须遵循的多个步骤。在一个简单的机器人程序中,可能只有两个帧,即初始帧和最终帧。这两个帧将对应于初始位置或最终位置。
然而,在现实世界中,这并不总是可能的,因为当机器人直接从初始位置到最终位置时,它会沿着特定的路径运动,并具有特定的曲率。然而,在这条路径上可能会有障碍物,或者这条路径可能不是所需的,因为需要遵循的路径可能是另一条。因此,我们需要帧。这些帧不仅定义了机器人从初始位置到最终位置的运动,而且将这两个位置之间的过渡分解为多个步骤,使机器人遵循所需的路径。
这可以被称为帧编程,在本章中我们将介绍。要记住的一件事是,帧数越多,机器人的运行就越平稳。你还记得我们看到的闭路电视录像吗?我们可以说它不够平滑,而且有很多抖动。这是由于闭路电视摄像头的低帧率造成的。它们不是以 30FPS 工作,而是以 15FPS 工作。这是为了减少视频的存储空间。然而,如果你看到最新的视频,有一些游戏和视频的帧率比正常的要高得多。我们最新的摄像头有 60FPS 的工作,使视频更加平滑和愉快。机器人也是如此。帧数越多,运动就越平滑和可控。但是,请确保不要过度使用。
现在,要从一个位置移动到另一个位置,我们将不得不在一开始就放入每个舵机的角度值。一旦获取,它将自动开始逐个执行这些值。为此,请继续编写以下代码:
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
;类似地,对于舵机 2,将使用b
,依此类推直到f
。在代码的前面几行中,机器人将提示用户填写电机 1
的六个帧值。然后,它将要求电机 2
的六个值,以此类推直到电机 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
的值将增加,从而使程序读取列表中提取的不同值。列表中舵机的每个值将对应一个不同的帧,从而使机器人通过它。
所以继续玩一些有趣的东西,让你的机器人做一些很棒的动作。只要小心对待它!
Python 物联网入门指南(八)(3)https://developer.aliyun.com/article/1507344