在本教程中,我提供了足够的深度,以通知您如何使用Arduino控制伺服电机。我使用PWM软件和硬件资源,因为这是控制一个或多个伺服电机的正确方法。阅读本文之后,您应该能够控制自己的业余伺服器而不会产生噪音或振动。另外,我在文章末尾写了一个部分,描述了如何修复多个MG996R伺服器和结论。
在详细介绍之前,我想告诉您一些事情。我知道制作出色的作品然后将其发布到全世界有多么艰辛。但是,无论在论坛和博客上写了多少关于如何控制诸如MG996R之类的业余伺服电机的内容,几乎所有这些教程都提供了在不施加扭矩的情况下自由工作时进行控制的信息。伺服器在施加扭矩的情况下改变其行为。如果控制不正确,则伺服器会振动,发出噪音并随机旋转。这就是我写本教程的方式。我想向您详细介绍尝试控制伺服器的解决方案,以及在施加扭矩的同时平稳控制许多爱好伺服器(如MG996R)的解决方案–在我的情况下,伺服机构会驱动机械臂。
故事:

RoboBioca机器人手臂
MG996R伺服是用于移动伺服电机的主伺服电机。 SainSmart 6轴机械手。 MG996R从手臂的所有6个轴上驱动其中四个。仅两个轴使用SG90伺服器移动末端执行器。
我用这条机械臂搭建了一个机器人服务员,可以抓住并搬运一个小塑料杯。因为我的项目需要其他内容,所以无法使用手臂的默认配置。我仅用一台带有金属齿轮箱和爪的伺服电动机替换了SG90伺服电动机。这样,我将手臂的自由度数从6减少到5。
在这个项目中,我使用了 布林克 应用程序向机器人发送命令。运行Blynk的Android平板电脑与机器人之间的通信是通过蓝牙连接无线完成的。
而且因为我没有足够的问题要解决,所以蓝牙连接会干扰伺服电机。这些在运行蓝牙连接时发出声音并随机运行。因此,还有一个额外的问题需要解决。
电源供应
必须为伺服电机提供稳定的6V电源。我用可调 LTC3780 DC降压/升压转换器模块。另外,我有一个 电源适配器(12V– 3A) 喂转换器。我使用数字万用表为转换器设置了接近6V的输出电压。
第一次尝试
最初的尝试是使用 Arduino的传感器防护板V5。 Arduino的 UNO的最大问题是,我只有两个PWM引脚(引脚9和10)可以在运行 Servo2 图书馆。 PWM引脚用于伺服电机的控制信号。与直流电动机不同,伺服器需要PWM控制来确定位置而不是伺服轴的速度。
这是随Arduino 0016和更早版本分发的Servo库。它可以使用标准板上的引脚9和10或Mega上的11和12来驱动最多两个伺服器。其他引脚将无法正常工作。
当我用PWM控制它们时,我有两个伺服电机工作良好,’全部。我离开他们,再试一次。
第二次尝试
我可以使用五个伺服器和只有两个带PWM的引脚,尝试了另一种方式来控制伺服电动机。我放弃了Servo2库,使用电容器和Servo库。这项尝试比前一次尝试更成功。根据Internet上的解决方案,我为每个伺服器使用了470uF 50V电解电容器。它有效,但是不如我所愿。震动和振动’t出现在大多数伺服位置,但随机旋转仍然继续。
第三次尝试
第三次尝试成功。我用了 Adafruit 16通道PWM /伺服驱动器 控制所有五个伺服器该驱动器使用i2c控制的PWM驱动器,该驱动器具有内置时钟和每个伺服器12位分辨率,这意味着在60Hz更新速率下约为4us分辨率。
我用了 Adafruit_PWMServoDriver库 而编写代码的灵感来自该库提供的示例。
这是一个演示,其中机器人手臂使用Adafruit通过PWM控制’s driver.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 | #包括<Wire.h> #包括<Adafruit_PWMServoDriver.h> Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver(); #定义MIN_PULSE_WIDTH 135 #定义MAX_PULSE_WIDTH 470 #定义DEFAULT_PULSE_WIDTH 150 #定义频率50 const uint8_t 伺服A = 0; const uint8_t 伺服B = 1; const uint8_t 伺服C = 2; const uint8_t 伺服D = 3; const uint8_t 伺服E = 4; const uint8_t 延迟Servo = 25; 虚空 controlServo0(uint8_t oldPos, uint8_t newPos) { 如果 (oldPos <= newPos) 开关Servo = 向上; 其他 开关Servo = 下; 开关 (开关Servo) { 案件 向上: { 对于 (uint8_t 位置 = oldPos; 位置 <= newPos; 位置 + = 1) { 延迟(延迟Servo); pwm.setPWM(0, 0, angleToPulse(位置)); } 打破; } 案件 下: { 对于 (uint8_t 位置 = oldPos; 位置 >= newPos; 位置 -= 1) { 延迟(延迟Servo); pwm.setPWM(0, 0, angleToPulse(位置)); } 打破; } } } 虚空 controlServo1(uint8_t oldPos, uint8_t newPos) { 如果 (oldPos <= newPos) 开关Servo = 向上; 其他 开关Servo = 下; 开关 (开关Servo) { 案件 向上: { 对于 (uint8_t 位置 = oldPos; 位置 <= newPos; 位置 + = 1) { 延迟(延迟Servo); pwm.setPWM(1, 0, angleToPulse(位置)); } 打破; } 案件 下: { 对于 (uint8_t 位置 = oldPos; 位置 >= newPos; 位置 -= 1) { 延迟(延迟Servo); pwm.setPWM(1, 0, angleToPulse(位置)); } 打破; } } } 虚空 controlServo2(uint8_t oldPos, uint8_t newPos) { 如果 (oldPos <= newPos) 开关Servo = 向上; 其他 开关Servo = 下; 开关 (开关Servo) { 案件 向上: { 对于 (uint8_t 位置 = oldPos; 位置 <= newPos; 位置 + = 1) { 延迟(延迟Servo); pwm.setPWM(2, 0, angleToPulse(位置)); } 打破; } 案件 下: { 对于 (uint8_t 位置 = oldPos; 位置 >= newPos; 位置 -= 1) { 延迟(延迟Servo); pwm.setPWM(2, 0, angleToPulse(位置)); } 打破; } } } 虚空 controlServo3(uint8_t oldPos, uint8_t newPos) { 如果 (oldPos <= newPos) 开关Servo = 向上; 其他 开关Servo = 下; 开关 (开关Servo) { 案件 向上: { 对于 (uint8_t 位置 = oldPos; 位置 <= newPos; 位置 + = 1) { 延迟(延迟Servo); pwm.setPWM(3, 0, angleToPulse(位置)); } 打破; } 案件 下: { 对于 (uint8_t 位置 = oldPos; 位置 >= newPos; 位置 -= 1) { 延迟(延迟Servo); pwm.setPWM(3, 0, angleToPulse(位置)); } 打破; } } } 虚空 controlServo4(uint8_t oldPos, uint8_t newPos) { 如果 (oldPos <= newPos) 开关Servo = 向上; 其他 开关Servo = 下; 开关 (开关Servo) { 案件 向上: { 对于 (uint8_t 位置 = oldPos; 位置 <= newPos; 位置 + = 1) { 延迟(延迟Servo); pwm.setPWM(4, 0, angleToPulse(位置)); } 打破; } 案件 下: { 对于 (uint8_t 位置 = oldPos; 位置 >= newPos; 位置 -= 1) { 延迟(延迟Servo); pwm.setPWM(4, 0, angleToPulse(位置)); } 打破; } } } 虚空 robotServoExample() { controlServo0(140, 140); controlServo1(145, 145); controlServo3(85, 85); controlServo4(155, 155); } 虚空 设定() { pwm.开始(); pwm.setPWMFreq(频率); } 虚空 循环() { robotServoExample(); } |
MG996R伺服器的故事
第一个故障的伺服电机是转动机器人手臂的那个。它很容易失败–我注意到操作过程中冒出了一些烟。第二个失败的伺服器位于基座。这一人不再承认这一立场。无论特定位置的命令如何,它都会随机更改其位置。原因未使用蓝牙模块。我试图在不连接通讯模块的情况下定位伺服电机。
运行约30分钟后,第一个伺服器发生故障。第二次更友善,又经过大约2个小时的操作后失败了。在这段时间里’我们使用机械臂测试了不同的位置。
I’当我开始工作以解决实际上使我的血压升高的两个问题时,出现了两个MG996R伺服电动机出现故障的情况:随机摇动(振动)和噪音。
最初,我以为我做错了’这就是为什么伺服电机这么快损坏的原因。在Google上搜索后,看起来像我’我不是唯一与MG996R一起工作并在测试阶段将其损坏的人。最后,我点了一些 新伺服 然后我开始研究我在哪里错了,以及如何解决这个问题。
我订购了三台MG996R伺服电机。他们很快来了,我开始测试它们。它 ’我不是第一次买到非常便宜的东西(MG996R的价格大约为6欧元/ 7美元),并且没有工作。这次对我来说很特别。从三个伺服电机来看,它们全都不起作用。
对于三台无法使用的新型伺服电机,我认为最好寻找替代方案。一世’我们一直在寻找具有与SainSmart机械臂相同大小的替代产品。我找到了Power HD Servo 1501MG。它们的尺寸与MG996R相同,但扭矩更高。
机器人手臂升级版中使用的五台MG996R伺服电机(原始版本的手臂使用4台MG996R),剩下三台。 1501MG取代了两个MG996R伺服器。一个MG996R伺服器驱动肩膀偏航,另一个驱动手腕偏航,另一个驱动机器人爪。
我如何修复MG996R
以下任何方法都需要接受电子学方面的培训,并需要一定的螺钉,变速箱和润滑脂经验。在伺服器内部进行操作时,请格外小心,您将自行承担责任。
- 伺服电机中的两个’仅需拧下螺丝即可将其固定。我控制它们在不放置变速箱盖的情况下从位置30来回移动到160,然后固定变速箱盖…而且有效。听起来有些愚蠢,但这就是我的工作方式。我怀疑问题出在变速箱内的齿轮位置上。最有可能的是,直流电动机在旋转时没有全部啮合。可能会有组装错误或运输冲击。
- 一种新的伺服器存在一些更大的问题。一档不’牙齿只有一两个。除此之外,我在变速箱内发现了一块塑料。幸运的是,我从那里拿走了备用零件的伺服器烧毁了,我把所有零件装回去,并且工作正常。
MG996R缺少主题
业余伺服器的结论
- 尽量减少手动更改伺服器的位置。这会很快损坏变速箱,而伺服可能会失效;
- 爱好项目中使用的伺服电机在位置10和170之间工作良好。如果位置小于10或大于170,则会产生噪音并产生振动;
- 伺服电源必须尽可能接近6V的值。较高的电压会燃烧它,而较低的电压会使它随机运行。另外,请不要尝试直接从Arduino开发板或项目中使用的任何其他控制器馈送伺服器。您将会有很多惊喜;
- 如果同时使用多个伺服电机(例如,一个机械手),则需要对它们进行逐一编程。如果其中一个伺服器命令不正确并移动到较不理想的位置,则对其他伺服器影响很大。机械手可能会触摸工作台或周围的其他物体,从而损坏伺服器的变速箱;
- 如果没有稳定的6V电源,则在伺服电机的电源线之间必须使用电解电容器。
本文的教程部分在哪里?您特权为零
很好的基础,但我可以’无法通过代码未优化这一事实。它做的时间太长了5倍…
您好克里斯,
感谢您的评论。 Arduino的草图用于五个伺服电机,我认为它是’比仅拥有一台伺服电机的代码有用。