树莓派遥控视频小车(附代码)

实现功能

基于树莓派3B+的视频车:可遥控,显示小车两自由度舵机支架上的摄像头视频。

  1. 启动小车程序
  2. 小车控制线程和摄像头线程运行
  3. 使用遥控器可遥控小车行走,并控制两自由度的摄像头舵机支架调整摄像头视野
  4. 树莓派的初次使用、opencv安装及远程连接等可以参考另一篇文章:
    链接: 树莓派的使用网线及无线连接方法及手机连接树莓派_opencv镜像.
    在这里插入图片描述

硬件材料

  1. 树莓派3B+
  2. 32G及以上TF卡
  3. PS2手柄
  4. 12V(或其他)电池
  5. L298N电机驱动模块
  6. 稳压模块(12V/或其他 转 5V)用于树莓派供电及舵机供电
  7. 小车底盘(带舵机、电机的方便些)
  8. 树莓派CSI摄像头
  9. 其他(舵机支架、开关等)

控制程序

PS2手柄驱动程序

PS2手柄实现功能如图:
在这里插入图片描述

import RPi.GPIO as GPIO
import time
import spidev#linux环境下用于spi通讯
import io
import cv2
import os
os.environ['SDL_VIDEODRIVE'] = 'x11'
from time import ctime,sleep,time

#手柄按键定义
PSB_SELECT = 1
PSB_L3 =  2
PSB_R3 =  3
PSB_START = 4
PSB_PAD_UP = 5
PSB_PAD_RIGHT = 6
PSB_PAD_DOWN  = 7
PSB_PAD_LEFT  = 8
PSB_L2 = 9
PSB_R2 = 10
PSB_L1 = 11
PSB_R1 = 12
PSB_TRIANGLE = 13
PSB_CIRCLE = 14
PSB_CROSS  = 15
PSB_SQUARE = 16

#摇杆的数据
PSS_RX = 5    #右边摇杆X轴数据
PSS_RY = 6    #右边摇杆Y轴数据
PSS_LX = 7    #左边摇杆X轴数据
PSS_LY = 8    #右边摇杆Y轴数据

#PS2引脚设置,接树莓派引脚
PS2_DAT_PIN = 10 #接DAT
PS2_CMD_PIN = 9  #接CMD
PS2_SEL_PIN = 25 #接CS  
PS2_CLK_PIN = 11 #接CLK

global Handkey
scan=[0x01,0x42,0x00,0x00,0x00,0x00,0x00,0x00,0x00]
Data=[0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00]
MASK = [PSB_SELECT,PSB_L3,PSB_R3,PSB_START,PSB_PAD_UP,PSB_PAD_RIGHT,PSB_PAD_DOWN,PSB_PAD_LEFT,PSB_L2,PSB_R2,PSB_L1,PSB_R1,PSB_TRIANGLE,PSB_CIRCLE,PSB_CROSS,PSB_SQUARE]

#设置GPIO口为BCM编码方式
GPIO.setmode(GPIO.BCM)
#忽略警告信息
GPIO.setwarnings(False)

#spi初始化 
def spi_init():
    spi = spidev.SpiDev()
    spi.open(0,0)
    GPIO.setup(PS2_CMD_PIN,GPIO.OUT,initial=GPIO.HIGH)
    GPIO.setup(PS2_CLK_PIN,GPIO.OUT,initial=GPIO.HIGH)
    GPIO.setup(PS2_DAT_PIN, GPIO.IN)
    GPIO.setup(PS2_SEL_PIN,GPIO.OUT,initial=GPIO.HIGH)
    
#读取PS2摇杆的模拟值
def PS2_AnologaData(button):
    return Data[button] 
    
#清空接受PS2的数据
def PS2_ClearData():
    Data[:]=[]
    
#读取PS2的数据
def PS2_ReadData(command):
    res = 0
    j = 1 
    i = 0
    for i in range(8):
        if command & 0x01:
            GPIO.output(PS2_CMD_PIN, GPIO.HIGH)
        else:
            GPIO.output(PS2_CMD_PIN, GPIO.LOW)
        command = command >> 1
        sleep(0.000008)
        GPIO.output(PS2_CLK_PIN, GPIO.LOW)
        sleep(0.000008)
        if GPIO.input(PS2_DAT_PIN):
            res = res + j
        j = j << 1
        GPIO.output(PS2_CLK_PIN, GPIO.HIGH)
        sleep(0.000008)
    GPIO.output(PS2_CMD_PIN, GPIO.HIGH)
    sleep(0.00004)
    return res
    
#PS2获取按键类型
def PS2_Datakey():
    global Data
    global scan
    index = 0
    i = 0
    PS2_ClearData()
    GPIO.output(PS2_SEL_PIN, GPIO.LOW)
    for i in range(9):
        Data.append( PS2_ReadData(scan[i]))
    GPIO.output(PS2_SEL_PIN, GPIO.HIGH)
    
    Handkey = (Data[4] << 8) | Data[3]
    for index in range(16):
        if Handkey & (1 << (MASK[index] - 1)) == 0:
            return index+1
    return 0

运动控制程序

接线图如下:
在这里插入图片描述

#初始化就GPIO口
def init():
    #直流电机pwm
    global pwm_ENA
    #转向舵机pwm
    global pwm_servo
    #舵机支架pwm
    global p1
    global p2
    GPIO.setup(ENA,GPIO.OUT,initial=GPIO.HIGH)
    GPIO.setup(IN1,GPIO.OUT,initial=GPIO.LOW)
    GPIO.setup(IN2,GPIO.OUT,initial=GPIO.LOW)
    GPIO.setup(ServoPin, GPIO.OUT)
    GPIO.setup(cmotor1,GPIO.OUT)
    GPIO.setup(cmotor2,GPIO.OUT)
    pwm_ENA = GPIO.PWM(ENA, 100)
    p1=GPIO.PWM(cmotor1,50)
    p2=GPIO.PWM(cmotor2,50)
    pwm_servo = GPIO.PWM(ServoPin, 50)
    pwm_servo.start(0)
    p1.start(0)
    p2.start(0)
    pwm_ENA.start(0)

#摄像头显示视频线程
def pi_capture():
    global is_capture_running
    print("Start capture") 
    cap = cv2.VideoCapture(0)
    is_capture_running = True
    while is_capture_running:
        ret,frame = cap.read()
        cv2.imshow("img",frame)
        if cv2.waitKey(1) & 0xFF == ord("q"):
            break
    is_capture_running = False
    
def run():
    GPIO.output(IN1, GPIO.HIGH)
    GPIO.output(IN2, GPIO.LOW)
    pwm_ENA.ChangeDutyCycle(CarSpeedControl)
    pwm_servo.ChangeDutyCycle(2.5+10*138/180)
    time.sleep(0.02)
    pwm_servo.ChangeDutyCycle(0)
#小车后退
def back():
    GPIO.output(IN1, GPIO.LOW)
    GPIO.output(IN2, GPIO.HIGH)
    pwm_ENA.ChangeDutyCycle(CarSpeedControl)
    pwm_servo.ChangeDutyCycle(2.5+10*138/180)
    time.sleep(0.02)
    pwm_servo.ChangeDutyCycle(0)

#小车左转   
def left():
    GPIO.output(IN1, GPIO.HIGH)
    GPIO.output(IN2, GPIO.LOW)
    pwm_ENA.ChangeDutyCycle(CarSpeedControl)
    pwm_servo.ChangeDutyCycle(2.5+10*96/180)
    time.sleep(0.02)
    pwm_servo.ChangeDutyCycle(0)

#小车右转
def right():
    GPIO.output(IN1, GPIO.HIGH)
    GPIO.output(IN2, GPIO.LOW)
    pwm_ENA.ChangeDutyCycle(CarSpeedControl)
    pwm_servo.ChangeDutyCycle(2.5+10*180/180)
    time.sleep(0.02)
    pwm_servo.ChangeDutyCycle(0)
   #小车沿左后方后退
def downleft():
    GPIO.output(IN1, GPIO.LOW)
    GPIO.output(IN2, GPIO.HIGH)
    pwm_ENA.ChangeDutyCycle(CarSpeedControl)
    pwm_servo.ChangeDutyCycle(2.5+10*96/180)
    time.sleep(0.02)
    pwm_servo.ChangeDutyCycle(0)
#小车沿右下方后退
def downright():
    GPIO.output(IN1, GPIO.LOW)
    GPIO.output(IN2, GPIO.HIGH)
    pwm_ENA.ChangeDutyCycle(CarSpeedControl)
    pwm_servo.ChangeDutyCycle(2.5+10*180/180)
    time.sleep(0.02)
    pwm_servo.ChangeDutyCycle(0)
    
#小车停止   
def brake():
   GPIO.output(IN1, GPIO.LOW)
   GPIO.output(IN2, GPIO.LOW)
   pwm_ENA.ChangeDutyCycle(0)
   pwm_servo.ChangeDutyCycle(2.5+10*138/180)
   time.sleep(0.02)
   pwm_servo.ChangeDutyCycle(0)
def cmotor11():
    p1.ChangeDutyCycle(2.5+10*angle1/180)
    time.sleep(0.02)
    p1.ChangeDutyCycle(0)
def cmotor22():
    p2.ChangeDutyCycle(2.5+10*angle2/180)
    time.sleep(0.02)
    #p2.ChangeDutyCycle(0)
def control():
    global CarSpeedControl,angle1,angle2
    global PS2_KEY
    global CarSpeedControl
    print("Start control!")
    try:
        init()
        ps.spi_init()
        while True:
            PS2_KEY = ps.PS2_Datakey()
            print('state is:',PS2_KEY)
            #PSB_SELECT键按下
            if PS2_KEY == PSB_SELECT:
                print ("PSB_SELECT")
            #PSB_R3键按下,舵机归位
            elif PS2_KEY == PSB_R3:
                print ("PSB_R3")
            #PSB_START键按下
            elif PS2_KEY == PSB_START:
                print ("PSB_START")
                #g_Carstate=enDOWNRIGHT
            #PSB_PAD_UP键按下,小车前进
            elif PS2_KEY == PSB_PAD_UP:
                g_Carstate = enRUN
                print ("PSB_PAD_UP")
            #PSB_PAD_RIGHT键按下,小车右转
            elif PS2_KEY == PSB_PAD_RIGHT:
                g_Carstate = enRIGHT
                print ("PSB_PAD_RIGHT") 
     
            #PSB_PAD_DOWN键按下,小车后退
            elif PS2_KEY == PSB_PAD_DOWN:
                g_Carstate = enBACK
                print ("PSB_PAD_DOWN")
            #PSB_PAD_LEFT键按下,小车左转
            elif PS2_KEY == PSB_PAD_LEFT:
                g_Carstate = enLEFT
                print ("PSB_PAD_LEFT")             
            #L2键按下,小车每次加速
            elif PS2_KEY == PSB_L2:
                print ("PSB_L2_SPEED+")
                CarSpeedControl += 10
                if CarSpeedControl > 100:
                    CarSpeedControl = 100
            #R2键按下,小车每次减速
            elif PS2_KEY == PSB_R2:
                print ("PSB_R2_SPEED-")
                CarSpeedControl -= 10
                if CarSpeedControl < 0:
                    CarSpeedControl = 0
            #L1键按下
            elif PS2_KEY == PSB_L1:
                g_Carstate=enDOWNRIGHT
                print ("PSB_L1")
                
                #downright()
            #R1键按下
            elif PS2_KEY == PSB_R1:
                g_Carstate=enDOWNLEFT
                print ("PSB_R1")
                
                #downleft()
            #按下按键或右摇杆控制摄像头舵机支架调整视野
            elif PS2_KEY ==PSB_TRIANGLE:
                print('camera_down')
                angle1+=10
                if angle1>180:
                    angle1=180
                cmotor11()
            elif PS2_KEY ==PSB_CROSS:
                print('camera_up')
                angle1-=10
                if angle1<0:
                    angle1=0
                cmotor11()
            elif PS2_KEY ==PSB_SQUARE:
                print('camera_left')
                angle2+=10
                if angle2>180:
                    angle2=180
                cmotor22()
            elif PS2_KEY ==PSB_CIRCLE:
                print('camera_right')
                angle2-=10
                if angle2<-0:
                    angle2=-0
                cmotor22()
            
            else:
                g_Carstate = enSTOP

            time.sleep(0.2)
    except KeyboardInterrupt:
        pass

源码链接

完整代码链接: 源码Gitee.

扩展功能

去年用这个底盘玩了一下Github上的深度学习视觉巡线小车项目,已经试过可以用这个树莓派小车可以跑起来,挺有意思的各位有兴趣可以参考一下,附上大佬的项目地址:链接: link

  • 8
    点赞
  • 114
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
这是一个简单的使用socket实现树莓派遥控小车Python代码: ```python import RPi.GPIO as GPIO import socket # 初始化GPIO GPIO.setmode(GPIO.BOARD) GPIO.setup(11, GPIO.OUT) GPIO.setup(13, GPIO.OUT) GPIO.setup(15, GPIO.OUT) GPIO.setup(16, GPIO.OUT) # 建立socket连接 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.bind(('0.0.0.0', 8888)) sock.listen(1) print('等待连接...') while True: # 等待客户端连接 conn, addr = sock.accept() print('已连接:', addr) while True: # 接收客户端发送的指令 data = conn.recv(1024) if not data: break # 处理指令 cmd = data.decode() if cmd == 'F': GPIO.output(11, GPIO.HIGH) GPIO.output(13, GPIO.LOW) GPIO.output(15, GPIO.HIGH) GPIO.output(16, GPIO.LOW) elif cmd == 'B': GPIO.output(11, GPIO.LOW) GPIO.output(13, GPIO.HIGH) GPIO.output(15, GPIO.LOW) GPIO.output(16, GPIO.HIGH) elif cmd == 'L': GPIO.output(11, GPIO.LOW) GPIO.output(13, GPIO.HIGH) GPIO.output(15, GPIO.HIGH) GPIO.output(16, GPIO.LOW) elif cmd == 'R': GPIO.output(11, GPIO.HIGH) GPIO.output(13, GPIO.LOW) GPIO.output(15, GPIO.LOW) GPIO.output(16, GPIO.HIGH) elif cmd == 'S': GPIO.output(11, GPIO.LOW) GPIO.output(13, GPIO.LOW) GPIO.output(15, GPIO.LOW) GPIO.output(16, GPIO.LOW) # 关闭连接 conn.close() # 清理GPIO GPIO.cleanup() ``` 这个代码使用了GPIO库来控制树莓派GPIO口,使用socket库来建立与客户端的连接,并通过接收到的指令来控制小车的运动。具体指令包括: - F:前进 - B:后退 - L:左转 - R:右转 - S:停止 客户端可以使用任意支持TCP连接的工具发送指令,比如使用Python的socket库、使用Telnet工具等。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

RambOoO_l

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值