树莓派4B-Python-控制舵机

树莓派4B-Python-控制舵机

  • SG90舵机
  • 参数介绍
  • 工作原理
  • 与树莓派4B连接
  • 使用gpiozero库的代码
  • 使用RPI.GPIO库的代码
    • 再补充一下

本文采用的是SG90型号、转动角度为90°/180°的舵机和树莓派4B4G版,主要是实现对舵机的控制、问题的解决。

SG90舵机

SG90舵机,常用在小型机器人、智能小车、机械臂、固定翼等小型模型上,因为内部的齿轮多数为塑料的,承受不了太大的扭力。
树莓派4B-Python-控制舵机_第1张图片

舵机类型又分为两种,一种是模拟舵机,另一种是数字舵机,像SG90就是模拟舵机。
模拟舵机和数字舵机的区别

转动角度为360°的舵机与转动角度为90°/180°的舵机工作的方式又是不一样的,360°的舵机相当于是无极变速的减速电机,只能控制转动的速度和转动的方向,不能控制转动到想要的角度,但控制的方式和一般舵机的控制信号相同。

参数介绍

型号 SG90
重量 9g
工作扭矩 1.6kg/cm
反应速度 0.12-0.13s/60°
建议使用温度 -30° ~ +60°
死区设定 5us
转动角度 90°/180°(与程序的写法有关)
舵机类型 模拟舵机
使用电压 3 ~ 7.2V(推荐用5V)
结构材质 塑料齿

工作原理

舵机的工作原理

控制电路板接受来自信号线的控制信号,控制电机转动,电机带动一系列齿轮组,减速后传动至输出舵盘。舵机的输出轴和位置反馈电位计是相连的,舵盘转动的同时,带动位置反馈电位计,电位计将输出一个电压信号到控制电路板,进行反馈,然后控制电路板根据所在位置决定电机的转动方向和速度,从而达到目标停止。

舵机的控制信号

舵机的控制信号为周期是20ms的脉宽调制(PWM)信号,其中脉冲宽度从0.5ms-2.5ms,相对应舵盘的位置为0-180度,呈线性变化。也就是说,给它提供一定的脉宽,它的输出轴就会保持在一个相对应的角度上,无论外界转矩怎样改变,直到给它提供一个另外宽度的脉冲信号,它才会改变输出角度到新的对应的位置上。舵机内部有一个基准电路,产生周期20ms,宽度1.5ms的基准信号,有一个比较器,将外加信号与基准信号相比较,判断出方向和大小,从而产生电机的转动信号。

与树莓派4B连接

树莓派4B-Python-控制舵机_第2张图片
红色:5V---- +
黑色:GND---- -
黄色:GPIO14----信号端

另外说明一下:如果舵机不是由树莓派供电的话,需要将该电源与树莓派共地,也就是说电源的负极必须与树莓派任意一个GND连接,否则会出现舵机控制失常等现象。

使用gpiozero库的代码

以下为使用gpiozero库写的,转动的范围为90°

from gpiozero import Servo
from time import sleep
 
myGPIO = 14
myCorrection = 0
maxPW = (2.0 + myCorrection) / 1000
minPW = (1.0 - myCorrection) / 1000
 
servo = Servo(myGPIO, min_pulse_width=minPW, max_pulse_width=maxPW)
 
while True:
    print("Set value range -1.0 to +0.0")
    for value in range(0,11,1):
        value2 = (float(value) - 10) / 10
        servo.value = value2
        print(value2)
        sleep(0.1)
 
    print("Set value range +0.0 to -0.9")
    for value in range(11,20,1):
        value2 = (float(value) - 10) / 10
        servo.value = value2
        print(value2)
        sleep(0.1)

使用RPI.GPIO库的代码

以下用RPI.GPIO库写的四自由度云台控制(两个舵机),转动的范围可为180°。

该程序全为本人参考其他程序后转变而来的,自身技术还些欠缺,所以此程序多多少少会出现一些BUG,还请各位大佬见谅,例如:
1.不能太频繁的发送信号给舵机,因为舵机内部转动和调整都是需要时间的;
2.有时候会像接收不到控制信号似的,发送了两三次信号才转动(有点像信号不好那样),但转动的角度应该是正确;
3.有时候可能存在一下子的抖动

所以呢,以下代码可以作为参考

import RPi.GPIO as GPIO
from time import sleep

def tonum(num):  # 用于处理角度转换的函数
    fm = 10.0 / 180.0
    num = num * fm + 2.5
    num = int(num * 10) / 10.0
    return num

servopin1 = 15   #舵机1,方向为左右转
servopin2 = 18   #舵机2,方向为上下转

GPIO.setmode(GPIO.BCM)
GPIO.setup(servopin1, GPIO.OUT, initial=False)
GPIO.setup(servopin2, GPIO.OUT, initial=False)
p1 = GPIO.PWM(servopin1,50) #50HZ
p2 = GPIO.PWM(servopin2,50) #50HZ

p1.start(tonum(85)) #初始化角度
p2.start(tonum(40)) #初始化角度
sleep(0.5)
p1.ChangeDutyCycle(0) #清除当前占空比,使舵机停止抖动
p2.ChangeDutyCycle(0) #清除当前占空比,使舵机停止抖动
sleep(0.1)

a = 0  #云台舵机1的执行次数
c = 9  #云台舵机1初始化角度:90度
b = 0  #云台舵机2的执行次数
d = 4  #云台舵机2初始化角度:40度

q = [0, 10, 20, 30, 40, 50, 60, 70, 80, 90,
 100, 110, 120, 130, 140, 150, 160, 170, 180]  #旋转角度列表

def left():
    global a, c   #引入全局变量
    a += 1
    if c > 2:  #判断角度是否大于20度
        c = c-1
        g = q[c]  #调用q列表中的第c位元素
        print('当前角度为',g)
        p1.ChangeDutyCycle(tonum(g))  #执行角度变化,跳转到q列表中对应第c位元素的角度
        sleep(0.1)
        p1.ChangeDutyCycle(0)  #清除当前占空比,使舵机停止抖动
        sleep(0.01)
    else:
        print('\n**超出范围**\n')
        c = 9
        g = 85  #调用q列表中的第c位元素
        p1.ChangeDutyCycle(tonum(g)) #执行角度变化,跳转到q列表中对应第c位元素的角度
        sleep(0.1)
        p1.ChangeDutyCycle(0)  #清除当前占空比,使舵机停止抖动
        sleep(0.01)
       
def right():
    global a, c    #引入全局变量
    if c < 16:
        c = c+1
        g = q[c]  #调用q列表中的第c位元素
        print('当前角度为',g)
        p1.ChangeDutyCycle(tonum(g)) #执行角度变化,跳转到q列表中对应第c位元素的角度
        sleep(0.1)
        p1.ChangeDutyCycle(0) #清除当前占空比,使舵机停止抖动
        sleep(0.01)
    else:
        print('\n****超出范围****\n')
        c = 9
        g = 85  #调用q列表中的第c位元素
        p1.ChangeDutyCycle(tonum(g)) #执行角度变化,跳转到q列表中对应第c位元素的角度
        sleep(0.1)
        p1.ChangeDutyCycle(0) #清除当前占空比,使舵机停止抖动
        sleep(0.01)

def up():
    global b, d    #引入全局变量
    b += 1
    if d > 2:
        d = d-1
        g = q[d]  #调用q列表中的第d位元素
        print('当前角度为',g)
        p2.ChangeDutyCycle(tonum(g)) #执行角度变化,跳转到q列表中对应第d位元素的角度
        sleep(0.1)
        p2.ChangeDutyCycle(0) #清除当前占空比,使舵机停止抖动
        sleep(0.01)
    else:
        print('\n**超出范围**\n')
        d = 4
        g = q[d]  #调用q列表中的第d位元素
        p2.ChangeDutyCycle(tonum(g)) #执行角度变化,跳转到q列表中对应第d位元素的角度
        sleep(0.1)
        p2.ChangeDutyCycle(0) #清除当前占空比,使舵机停止抖动
        sleep(0.01)

def down():
    global b, d    #引入全局变量
    if d < 11:
        d = d+1
        g = q[d]  #调用q列表中的第d位元素
        print('当前角度为',g)
        p2.ChangeDutyCycle(tonum(g)) #执行角度变化,跳转到q列表中对应第d位元素的角度
        sleep(0.1)
        p2.ChangeDutyCycle(0) #清除当前占空比,使舵机停止抖动
        sleep(0.01)
    else:
        print('\n****超出范围****\n')
        d = 4
        g = q[d]  #调用q列表中的第d位元素
        p2.ChangeDutyCycle(tonum(g)) #执行角度变化,跳转到q列表中对应第d位元素的角度
        sleep(0.1)
        p2.ChangeDutyCycle(0) #清除当前占空比,使舵机停止抖动
        sleep(0.01)

if __name__ == '__main__':
	while True:
		a = input('输入:')
	    if a == 'a':
	        left()
	    elif a == 'd':
	        right()
	    elif a == 'w':
	        up()
	    elif a == 's':
	        down()

再补充一下

补充一下关于舵机抖动和消抖的方法(仅是个人观点哈):
舵机会发生抖动是因为它自身认为自己还没有真正转到要求的角度,所以会不断的左右纠正,最终产生抖动。一般抖动是因为占空比没有清零,所以当舵机转到指定的角度后,稍微给一端很短的时间停留后,就将当前的占空比清零,如:

p2.ChangeDutyCycle(0)
sleep(0.01)

##2020.8.22
有写得不好、写得不对的地方还请各位指出,谢谢!

你可能感兴趣的:(树莓派4B,python)