树莓派读取舵机数据与驱动

舵机数据读取

Read.py

#!/usr/bin/python3
#幻尔科技总线舵机通信#
import serial
import pigpio
import time

LOBOT_SERVO_FRAME_HEADER         = 0x55
LOBOT_SERVO_MOVE_TIME_WRITE      = 1
LOBOT_SERVO_MOVE_TIME_READ       = 2
LOBOT_SERVO_MOVE_TIME_WAIT_WRITE = 7
LOBOT_SERVO_MOVE_TIME_WAIT_READ  = 8
LOBOT_SERVO_MOVE_START           = 11
LOBOT_SERVO_MOVE_STOP            = 12
LOBOT_SERVO_ID_WRITE             = 13
LOBOT_SERVO_ID_READ              = 14
LOBOT_SERVO_ANGLE_OFFSET_ADJUST  = 17
LOBOT_SERVO_ANGLE_OFFSET_WRITE   = 18
LOBOT_SERVO_ANGLE_OFFSET_READ    = 19
LOBOT_SERVO_ANGLE_LIMIT_WRITE    = 20
LOBOT_SERVO_ANGLE_LIMIT_READ     = 21
LOBOT_SERVO_VIN_LIMIT_WRITE      = 22
LOBOT_SERVO_VIN_LIMIT_READ       = 23
LOBOT_SERVO_TEMP_MAX_LIMIT_WRITE = 24
LOBOT_SERVO_TEMP_MAX_LIMIT_READ  = 25
LOBOT_SERVO_TEMP_READ            = 26
LOBOT_SERVO_VIN_READ             = 27
LOBOT_SERVO_POS_READ             = 28
LOBOT_SERVO_OR_MOTOR_MODE_WRITE  = 29
LOBOT_SERVO_OR_MOTOR_MODE_READ   = 30
LOBOT_SERVO_LOAD_OR_UNLOAD_WRITE = 31
LOBOT_SERVO_LOAD_OR_UNLOAD_READ  = 32
LOBOT_SERVO_LED_CTRL_WRITE       = 33
LOBOT_SERVO_LED_CTRL_READ        = 34
LOBOT_SERVO_LED_ERROR_WRITE      = 35
LOBOT_SERVO_LED_ERROR_READ       = 36


pi = pigpio.pi()  #初始化 pigpio库
serialHandle = serial.Serial("/dev/ttyAMA0", 115200)  #初始化串口, 波特率为115200

def portInit(): ##配置用到的IO口
    pi.set_mode(17, pigpio.OUTPUT)  #配置RX_CON 即 GPIO17 为输出
    pi.write(17, 0)
    pi.set_mode(27, pigpio.OUTPUT)  #配置TX_CON 即 GPIO27 为输出
    pi.write(27, 1)

def portWrite():  ##配置单线串口为输出
    pi.write(27, 1)  #拉高TX_CON 即 GPIO27
    pi.write(17, 0)  #拉低RX_CON 即 GPIO17
    

def portRead():   ##配置单线串口为输入
    pi.write(17, 1) #拉高RX_CON 即 GPIO17
    pi.write(27, 0) #拉低TX_CON 即 GPIO27




def checksum(buf):
    # 计算校验和
    sum = 0x00
    for b in buf:  # 求和
        sum += b
    sum = sum - 0x55 - 0x55  # 去掉命令开头的两个 0x55
    sum = ~sum  # 取反
    return sum & 0xff

def serial_serro_wirte_cmd(id=None, w_cmd=None, dat1=None, dat2=None):
    '''
    写指令
    :param id:
    :param w_cmd:
    :param dat1:
    :param dat2:
    :return:
    '''
    portWrite()
    buf = bytearray(b'\x55\x55')  # 帧头
    buf.append(id)
    # 指令长度
    if dat1 is None and dat2 is None:
        buf.append(3)
    elif dat1 is not None and dat2 is None:
        buf.append(4)
    elif dat1 is not None and dat2 is not None:
        buf.append(7)

    buf.append(w_cmd)  # 指令
    # 写数据
    if dat1 is None and dat2 is None:
        pass
    elif dat1 is not None and dat2 is None:
        buf.append(dat1 & 0xff)  # 偏差
    elif dat1 is not None and dat2 is not None:
        buf.extend([(0xff & dat1), (0xff & (dat1 >> 8))])  # 分低8位 高8位 放入缓存
        buf.extend([(0xff & dat2), (0xff & (dat2 >> 8))])  # 分低8位 高8位 放入缓存
    # 校验和
    buf.append(checksum(buf))
    # for i in buf:
    #     print('%x' %i)
    serialHandle.write(buf)  # 发送

def serial_servo_read_cmd(id=None, r_cmd=None):
    '''
    发送读取命令
    :param id:
    :param r_cmd:
    :param dat:
    :return:
    '''
    portWrite()
    buf = bytearray(b'\x55\x55')  # 帧头
    buf.append(id)
    buf.append(3)  # 指令长度
    buf.append(r_cmd)  # 指令
    buf.append(checksum(buf))  # 校验和
    serialHandle.write(buf)  # 发送
    time.sleep(0.00034)#小延时,等命令发送完毕。不知道是否能进行这么精确的延时的,但是修改这个值的确实会产生影响。
                         #这个值效果最好,网上查值的跟这个值也接近

#读取ID
def readID(cmd):
    portWrite() #将单线串口配置为输出
    serialHandle.flushInput() #清空接收缓存
    serial_servo_read_cmd(cmd, LOBOT_SERVO_ID_READ)
    #time.sleep(0.00034)  
    portRead() #将单线串口配置为输入
    time.sleep(0.005)  #稍作延时,等待接收完毕
    count = serialHandle.inWaiting() #获取接收缓存中的字节数
    id = None
    if count != 0: #如果接收到的数据不空
        recv_data = serialHandle.read(count) #读取接收到的数据
        if count == 7: #如果接收到的数据是8个字节(符合位置读取命令的返回数据的长度)
            if recv_data[0] == 0x55 and recv_data[1] == 0x55 and recv_data[4] == LOBOT_SERVO_ID_READ:
                #第一第二个字节等于0x55, 第5个字节是0x1C 就是 28 就是 位置读取命令的命令号
                id=0xffff & (recv_data[5] | (0xff00 & (0x00 << 8)))  
                 #上面这小段代码简化了操作没有进行和校验,只要帧头和命令对了就认为数据无误
            
    

        return id  #返回读取到的位置
 
#dve读偏差
def readDve(cmd):
    portWrite() #将单线串口配置为输出
    serialHandle.flushInput() #清空接收缓存
    serial_servo_read_cmd(cmd, LOBOT_SERVO_ANGLE_OFFSET_READ)
    #time.sleep(0.00034)  
    portRead() #将单线串口配置为输入
    time.sleep(0.005)  #稍作延时,等待接收完毕
    count = serialHandle.inWaiting() #获取接收缓存中的字节数
    dve = None
    if count != 0: #如果接收到的数据不空
        recv_data = serialHandle.read(count) #读取接收到的数据
        if count == 7: #如果接收到的数据是8个字节(符合位置读取命令的返回数据的长度)
            
            if recv_data[0] == 0x55 and recv_data[1] == 0x55 and recv_data[4] == LOBOT_SERVO_ANGLE_OFFSET_READ:
                #第一第二个字节等于0x55, 第5个字节是0x1C 就是 28 就是 位置读取命令的命令号
                
                dve=0xffff & (recv_data[5] | (0xff00 & (0x00 << 8))) #将接收到的字节数据拼接成完整的位置数据  
                #上面这小段代码简化了操作没有进行和校验,只要帧头和命令对了就认为数据无误
            
    

        return dve  #返回读取到的位置



##pos读位置
##
def readPos(cmd):
    portWrite() #将单线串口配置为输出
    serialHandle.flushInput() #清空接收缓存
    serial_servo_read_cmd(cmd, LOBOT_SERVO_POS_READ)
    #time.sleep(0.00034)  
    portRead() #将单线串口配置为输入
    time.sleep(0.005)  #稍作延时,等待接收完毕
    count = serialHandle.inWaiting() #获取接收缓存中的字节数
    pos = None
    if count != 0: #如果接收到的数据不空
        recv_data = serialHandle.read(count) #读取接收到的数据
        if count == 8: #如果接收到的数据是8个字节(符合位置读取命令的返回数据的长度)
            if recv_data[0] == 0x55 and recv_data[1] == 0x55 and recv_data[4] == LOBOT_SERVO_POS_READ:
                #第一第二个字节等于0x55, 第5个字节是0x1C 就是 28 就是 位置读取命令的命令号
                 pos= 0xffff & (recv_data[5] | (0xff00 & (recv_data[6] << 8))) #将接收到的字节数据拼接成完整的位置数据
                 #上面这小段代码简化了操作没有进行和校验,只要帧头和命令对了就认为数据无误
                 
   
      
    return pos  #返回读取到的位置


##vin读电压
def readVin(cmd):
    portWrite() #将单线串口配置为输出
    serialHandle.flushInput() #清空接收缓存
    serial_servo_read_cmd(cmd, LOBOT_SERVO_VIN_READ)
    #time.sleep(0.00034)  
    portRead() #将单线串口配置为输入
    time.sleep(0.005)  #稍作延时,等待接收完毕
    count = serialHandle.inWaiting() #获取接收缓存中的字节数
    vin = None
    if count != 0: #如果接收到的数据不空
        recv_data = serialHandle.read(count) #读取接收到的数据
        if count == 8: #如果接收到的数据是8个字节(符合位置读取命令的返回数据的长度)
            if recv_data[0] == 0x55 and recv_data[1] == 0x55 and recv_data[4] == LOBOT_SERVO_VIN_READ:
                #第一第二个字节等于0x55, 第5个字节是0x1C 就是 28 就是 位置读取命令的命令号
                 vin= 0xffff & (recv_data[5] | (0xff00 & (recv_data[6] << 8))) #将接收到的字节数据拼接成完整的位置数据
                 #上面这小段代码简化了操作没有进行和校验,只要帧头和命令对了就认为数据无误
                 

    return vin 


serial_servo_read_cmd(254, LOBOT_SERVO_LOAD_OR_UNLOAD_WRITE)#所有舵机掉电
while True:
    try:
        
        readID(254)
        id = readID(254) #读取id
        print('id:',id) #打印id
        time.sleep(0.5)
        
        readPos(1)
        pos = readPos(1) #获取1号舵机的位置
        print('pos:',pos) #打印位置
        time.sleep(0.5)
        
        readDve(1)#读取偏差
        dve= readDve(1)
        print('dve:',dve) #打印偏差
        time.sleep(0.5)
        
        readVin(1)#读取电压
        vin=readVin(1)
        print('vin(mv):',vin)#打印偏差
        
        time.sleep(1) #延时1秒
         
        
    except Exception as e:
        print(e)
        break

        ###!!!!!因为多个数据一起读取,读取间隔不好控制,会容易出现数据混乱,读取时数据不对会返回None

现象:

舵机驱动

Move.py

import time
import ctypes
import serial
import pigpio
#幻尔科技总线舵机通信#

LOBOT_SERVO_FRAME_HEADER         = 0x55
LOBOT_SERVO_MOVE_TIME_WRITE      = 1
LOBOT_SERVO_MOVE_TIME_READ       = 2
LOBOT_SERVO_MOVE_TIME_WAIT_WRITE = 7
LOBOT_SERVO_MOVE_TIME_WAIT_READ  = 8
LOBOT_SERVO_MOVE_START           = 11
LOBOT_SERVO_MOVE_STOP            = 12
LOBOT_SERVO_ID_WRITE             = 13
LOBOT_SERVO_ID_READ              = 14
LOBOT_SERVO_ANGLE_OFFSET_ADJUST  = 17
LOBOT_SERVO_ANGLE_OFFSET_WRITE   = 18
LOBOT_SERVO_ANGLE_OFFSET_READ    = 19
LOBOT_SERVO_ANGLE_LIMIT_WRITE    = 20
LOBOT_SERVO_ANGLE_LIMIT_READ     = 21
LOBOT_SERVO_VIN_LIMIT_WRITE      = 22
LOBOT_SERVO_VIN_LIMIT_READ       = 23
LOBOT_SERVO_TEMP_MAX_LIMIT_WRITE = 24
LOBOT_SERVO_TEMP_MAX_LIMIT_READ  = 25
LOBOT_SERVO_TEMP_READ            = 26
LOBOT_SERVO_VIN_READ             = 27
LOBOT_SERVO_POS_READ             = 28
LOBOT_SERVO_OR_MOTOR_MODE_WRITE  = 29
LOBOT_SERVO_OR_MOTOR_MODE_READ   = 30
LOBOT_SERVO_LOAD_OR_UNLOAD_WRITE = 31
LOBOT_SERVO_LOAD_OR_UNLOAD_READ  = 32
LOBOT_SERVO_LED_CTRL_WRITE       = 33
LOBOT_SERVO_LED_CTRL_READ        = 34
LOBOT_SERVO_LED_ERROR_WRITE      = 35
LOBOT_SERVO_LED_ERROR_READ       = 36

serialHandle = serial.Serial("/dev/ttyAMA0", 115200)  # 初始化串口, 波特率为115200

pi = pigpio.pi()  #初始化 pigpio库


def portInit(): ##配置用到的IO口
    pi.set_mode(17, pigpio.OUTPUT)  #配置RX_CON 即 GPIO17 为输出
    pi.write(17, 0)
    pi.set_mode(27, pigpio.OUTPUT)  #配置TX_CON 即 GPIO27 为输出
    pi.write(27, 1)

def portWrite():  ##配置单线串口为输出
    pi.write(27, 1)  #拉高TX_CON 即 GPIO27
    pi.write(17, 0)  #拉低RX_CON 即 GPIO17
    

def portRead():   ##配置单线串口为输入
    pi.write(17, 1) #拉高RX_CON 即 GPIO17
    pi.write(27, 0) #拉低TX_CON 即 GPIO27


def checksum(buf):
    # 计算校验和
    sum = 0x00
    for b in buf:  # 求和
        sum += b
    sum = sum - 0x55 - 0x55  # 去掉命令开头的两个 0x55
    sum = ~sum  # 取反
    return sum & 0xff

def serial_serro_wirte_cmd(id=None, w_cmd=None, dat1=None, dat2=None):
    '''
    写指令
    :param id:
    :param w_cmd:
    :param dat1:
    :param dat2:
    :return:
    '''
    portWrite()
    buf = bytearray(b'\x55\x55')  # 帧头
    buf.append(id)
    # 指令长度
    if dat1 is None and dat2 is None:
        buf.append(3)
    elif dat1 is not None and dat2 is None:
        buf.append(4)
    elif dat1 is not None and dat2 is not None:
        buf.append(7)

    buf.append(w_cmd)  # 指令
    # 写数据
    if dat1 is None and dat2 is None:
        pass
    elif dat1 is not None and dat2 is None:
        buf.append(dat1 & 0xff)  # 偏差
    elif dat1 is not None and dat2 is not None:
        buf.extend([(0xff & dat1), (0xff & (dat1 >> 8))])  # 分低8位 高8位 放入缓存
        buf.extend([(0xff & dat2), (0xff & (dat2 >> 8))])  # 分低8位 高8位 放入缓存
    # 校验和
    buf.append(checksum(buf))
    # for i in buf:
    #     print('%x' %i)
    serialHandle.write(buf)  # 发送

def serial_servo_read_cmd(id=None, r_cmd=None):
    '''
    发送读取命令
    :param id:
    :param r_cmd:
    :param dat:
    :return:
    '''
    portWrite()
    buf = bytearray(b'\x55\x55')  # 帧头
    buf.append(id)
    buf.append(3)  # 指令长度
    buf.append(r_cmd)  # 指令
    buf.append(checksum(buf))  # 校验和
    serialHandle.write(buf)  # 发送
    time.sleep(0.00034)




def setBusServoPulse(id, pulse, use_time):
    """
    驱动串口舵机转到指定位置
    :param id: 要驱动的舵机id
    :pulse: 位置
    :use_time: 转动需要的时间
    """

    pulse = 0 if pulse < 0 else pulse
    pulse = 1000 if pulse > 1000 else pulse
    use_time = 0 if use_time < 0 else use_time
    use_time = 30000 if use_time > 30000 else use_time
    serial_serro_wirte_cmd(id, LOBOT_SERVO_MOVE_TIME_WRITE, pulse, use_time)
    return pulse  #x新增反馈可以删除
  
while True:
      setBusServoPulse(id=254, pulse=1000, use_time=1000)
      time.sleep(1) # 延时1s
      setBusServoPulse(id=254, pulse=0, use_time=1000)
      time.sleep(1) # 延时1s

树莓派舵机驱动

树莓派GPIO(General Purpose Input/Output,通用输入/输出)接口可以用来控制电机、继电器等电子设备,包括舵机。要通过GPIO驱动舵机,首先需要了解舵机的工作原理,它通常使用4线连接:电源、接地、脉冲信号(PWM,Pulse Width Modulation)和方向信号。 以下是步骤: 1. **连接硬件**:将舵机的红色(VCC,5V电源)、黑色(GND)接至树莓派的对应引脚。黄线接至GPIO的PWM输出口,如GPIO18或GPIO19,棕色线接至GPIO的一个数字输入口,用于检测舵机的方向。 2. **软件配置**:在树莓派上安装必要的库,例如`RPi.GPIO`库,这可以帮助管理和操作GPIO。运行命令`sudo apt-get install rpi.gpio` 安装该库。 3. **编写Python代码**:使用Python编写一个简单的程序,通过调整发送给PWM引脚的脉冲宽度值(0~100%)来改变舵机的角度。例如: ```python import RPi.GPIO as GPIO from time import sleep GPIO.setmode(GPIO.BCM) GPIO.setup(18, GPIO.OUT) # PWM输出 GPIO.setup(24, GPIO.IN) # 方向检测 def set_servo_angle(angle): pulse_width = angle / 18 + 2.5 GPIO.output(18, True) GPIO.output(18, False) GPIO.add_event_detect(18, GPIO.RISING, callback=lambda x: None, bouncetime=200) GPIO.output(18, True) sleep(pulse_width/1000000) GPIO.output(18, False) while True: direction = GPIO.input(24) if direction == GPIO.HIGH: set_servo_angle(90) # 顺时针 else: set_servo_angle(-90) # 逆时针 sleep(0.5) # 触发频率不宜过高防止损坏舵机 ``` 4. **安全注意事项**:在编写代码时确保处理好GPIO异常,并在结束时清理GPIO资源,以防资源泄露。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值