树莓派遥控视频小车
实现功能
基于树莓派3B+的视频车:可遥控,显示小车两*度舵机支架上的摄像头视频。
1. 启动小车程序
2. 小车控制线程和摄像头线程运行
3. 使用遥控器可遥控小车行走,并控制两*度的摄像头舵机支架调整摄像头视野
硬件材料
- 树莓派3B+
- 32G及以上TF卡
- PS2手柄
- 12V(或其他)电池
- L298N电机驱动模块
- 稳压模块(12V/或其他 转 5V)用于树莓派供电及舵机供电
- 小车底盘(带舵机、电机的方便些)
- 树莓派CSI摄像头
- 其他(舵机支架、开关等)
控制程序
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 #接MOS
PS2_CMD_PIN = 9 #接MIS
PS2_SEL_PIN = 25 #接CS
PS2_CLK_PIN = 11 #接SCK
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;