1.树莓派、Python、STM32、上位机、局域网、PC智能遥控小车(含源码)

整体功能:电脑上观看小车前方画面,通过电脑方向键控制小车前后运动、左右转弯,如前进后退键,按下前进或后退,松开停车,左右同理

关键技术部分:PC端:使用pygame编写上位机,作为服务器

                         树莓派端:图像获取、客户端编程、IO电平控制

                         STM32端:IO输入、PWM输出

                         电路端:STM32电路设计、H桥驱动电路设计

1.树莓派、Python、STM32、上位机、局域网、PC智能遥控小车(含源码)_第1张图片1.树莓派、Python、STM32、上位机、局域网、PC智能遥控小车(含源码)_第2张图片

       

写在前面:局域网超视距遥控小车是笔者大四时的心愿,无奈当时没有时间和能力,研二才有机会继续完成,从车上的电路设计到树莓派程序,到局域网通信,到电脑上的UI设计到近期才全部能串起来,借这一DIY项目彩排一下老师的项目和实践近期所学。电脑端本计划写个像样的上位机,但没找的理想的键盘事件算法,但可以用pyqt5的按钮做控制,对使用Python写软件有兴趣的小伙伴移步专栏https://blog.csdn.net/qq_36071362/category_9697208.html

 

环境陈述:

1.老年时代的飞思卡尔c车,双电机,舵机转向

2.STM32C8T6单片机,keil,配双H桥驱动电路

3.树莓派4B,16G卡,Raspbian系统

4.Windows10,pycharm,Python3.7

 

功能陈述:

1.stm32制做底层控制板,H桥驱动,可直接接航模遥控器遥控

2.树莓派做小车端的图像获取和WiFi连接工具,运行socket的客户端

3.电脑端(Windows10)运行socket的服务端,显示树莓派传回的画面,检测电脑键盘方向键

 

 

控制流程:

电脑的服务端程序获取到方向键按下

→方向和前后信息传输至树莓派

→树莓派将画面实时传给电脑(一直传)将方向信息传给STM32

→32控制H桥控制电机正反转、控制舵机左右转

 

通信方式:

1.树莓派与电脑:WiFi,电脑和树莓派连接到同一路由器

2.树莓派与STM32:4个IO(因为作烧了树莓派的串口。。。。)

3.STM32与电机舵机:pwm,频率不同的脉宽调制信号,电机:10000hz,舵机:50hz

 

相关资料:

1.code1:STM32的小车控制程序-keil,c语言

2.code2:树莓派上的Python写的client程序,通过socket传输画面给电脑

3.code3:电脑上的Python写的server程序,通过socket接收画面,检测键盘按键,发送小车动作指令

4.schdoc、pcbdoc:电路设计文件

 

所做工作:

1.底层控制电路设计、焊接,作者前几年干过。。这版借师弟学习机会由他设计焊接板子,板子集成STM32与双H桥电机驱动电路

2.树莓派程序编写调试,树莓派端作为客户端需要从CSI摄像头获取画面通过WiFi传输至电脑,同时获取来自电脑的控制信息

3.电脑上编写服务器程序,使用pygame获取方向按键(没找的别的合适的包),显示树莓派的摄像头画面,发送方向控制信息

 

未来功能规划:

1.人脸跟踪人脸识别

2.目标识别

3.语音识别(语音控制)

4.xxxxx

 

下面贴出源码及介绍:

电脑端(服务端):

实现功能:

(1)通过socket实时接收树莓派的相机画面图片,并使用pygame显示

(2)在pygame的画面中,检测键盘方向键按下弹起情况,按下时分别通过socket发送“sta1”,“sta2”, “fta1”,“fta2”,弹起时若是前后键(即方向上下键),发送“sta0”,若是左右键,发送“fta0”

import socket
import cv2
import numpy
import pygame
# 服务器端的IP地址
address = ("192.168.2.240", 8088)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(address)
s.listen(True)
conn, addr = s.accept()

sta = '0'
fta = '0'


def check_keydown_events(event, sock):
    if event.key == pygame.K_UP:
        sta = "sta1"
        print(sta)
        sock.send(sta.encode())
    elif event.key == pygame.K_DOWN:
        sta = "sta2"
        print(sta)
        sock.send(sta.encode())
    elif event.key == pygame.K_RIGHT:
        fta = "fta1"
        print(fta)
        sock.send(fta.encode())
    elif event.key == pygame.K_LEFT:
        fta = "fta2"
        print(fta)
        sock.send(fta.encode())
    #print(sta)
    #print(fta)


def check_keyup_events(event, sock):
    if event.key == pygame.K_UP:
        sta = "sta0"
        print(sta)
        sock.send(sta.encode())
    elif event.key == pygame.K_DOWN:
        sta = "sta0"
        print(sta)
        sock.send(sta.encode())
    elif event.key == pygame.K_RIGHT:
        fta = "fta0"
        print(fta)
        sock.send(fta.encode())
    elif event.key == pygame.K_LEFT:
        fta = "fta0"
        print(fta)
        sock.send(fta.encode())
    #
    #


def check_events(sock):
    for event in pygame.event.get():
        # print("oooooo")
        if event.type == pygame.KEYDOWN:
            check_keydown_events(event, sock) #检测按键,发送数据
            print("1111111")
        elif event.type == pygame.KEYUP:
            check_keyup_events(event, sock)
            print("2222222")


def recvall(sock, count):
    buf = b''
    start = "okok"
    start = start.encode()
    start = start.decode()
    start = start.encode()

    while count:
        newbuf = sock.recv(count)
        check_events(sock)
        #sock.send(start)
        if not newbuf: return None
        buf += newbuf
        count -= len(newbuf)
    return buf


pygame.init()
screen = pygame.display.set_mode((640, 480))
pygame.display.set_caption("遥控平台")

while 1:
    length = recvall(conn, 16)
    stringData = recvall(conn, int(length))
    # stringData = stringData.encode()
    data = numpy.fromstring(stringData, dtype='uint8')
    decimg = cv2.imdecode(data, 1)
    #cv2.imshow('SERVER', decimg)

    cv2.imwrite('messigray.bmp', decimg)
    decimg1 = pygame.image.load('messigray.bmp')

    rect = decimg1.get_rect()
    screen.blit(decimg1, (0,0))
    pygame.display.update()

    # key = cv2.waitKeyEx(0)
    # print('key =', key)
    if cv2.waitKey(10) == 27:
        break

s.close()
cv2.destroyAllWindows()

树莓派端(客户端):

实现功能:

(1)通过socket发送摄像头的画面图片

(2)接收socket中的方向和前后动作信息,即sta0-2和fta0-2,1和2分别是前进后退,左转右转,0是恢复,即停车或舵机回正

(3)通过IO传输从WiFi(socket)接收到的动作信息,因为串口占用,故使用4个IO,通过不同电平状态传输小车动作,因为转向动作和前后动作各包含三种状态(前后左右,回正和停车,不调速),故各分配两个引脚控制方向和前后

import socket
import cv2,time
import numpy

import RPi.GPIO as GPIO
import pigpio

GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)

# 设置引脚
data1 = 4
data2 = 17
speed1 = 27
speed2 = 22

address = ('192.168.2.240', 8088)
sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
sock.connect(address)

capture = cv2.VideoCapture(0)
ret, frame = capture.read()
encode_param=[int(cv2.IMWRITE_JPEG_QUALITY),90]


GPIO.setup(data1, GPIO.OUT, initial=GPIO.LOW)
GPIO.setup(data2, GPIO.OUT, initial=GPIO.LOW)
GPIO.setup(speed1, GPIO.OUT, initial=GPIO.LOW)
GPIO.setup(speed2, GPIO.OUT, initial=GPIO.LOW)

def forward():
    GPIO.setup(data1, GPIO.OUT, initial=GPIO.LOW)
    GPIO.setup(data2, GPIO.OUT, initial=GPIO.HIGH)

def back():
    GPIO.setup(data1, GPIO.OUT, initial=GPIO.HIGH)
    GPIO.setup(data2, GPIO.OUT, initial=GPIO.LOW)
    
def stop():
    GPIO.setup(data1, GPIO.OUT, initial=GPIO.LOW)
    GPIO.setup(data2, GPIO.OUT, initial=GPIO.LOW)


def right():
    GPIO.setup(speed1, GPIO.OUT, initial=GPIO.LOW)
    GPIO.setup(speed2, GPIO.OUT, initial=GPIO.HIGH)

def left():
    GPIO.setup(speed1, GPIO.OUT, initial=GPIO.HIGH)
    GPIO.setup(speed2, GPIO.OUT, initial=GPIO.LOW)
    
def retu():
    GPIO.setup(speed1, GPIO.OUT, initial=GPIO.LOW)
    GPIO.setup(speed2, GPIO.OUT, initial=GPIO.LOW)
    
    str222 = "123"
    str222 = str222.encode
    #sock.send(str222);
    
while ret:

    ret, frame = capture.read()
    #frame = frame[200:480,0:640]
    frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    result, imgencode = cv2.imencode('.jpg', frame, encode_param)
    data = numpy.array(imgencode)
    stringData = data.tostring()
    strrr = str(len(stringData)).ljust(16)
    strrr = strrr.encode()
    sock.send(strrr);
    sock.send(stringData);
    
    # 用来非阻塞机制通信,0x40参数非常重要
    try:
       sdata = sock.recv(4, 0x40)
       sdata=sdata.decode()
    except BlockingIOError as e:
       sdata = "nnn"
    
    print(sdata)
    #print("35553")

    if sdata == "fta0":
      retu()
    if sdata == "fta1":
      right()
    if sdata == "fta2":
      left()
    if sdata == "sta0":
      stop()
    if sdata == "sta1":
      forward()
    if sdata == "sta2":
      back()
    #sdata = sock.recv(4)
    
    #if len(sdata)>0:
    print(sdata)
    
    #time.sleep(0.5)
    #sdata = sock.recv(2)
    #if len(sdata)>0:
    #   print(sdata)

    #print(frame.shape)
    #frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    #decimg=cv2.imdecode(data,1)
    #cv2.imshow('CLIENT',frame)
    
sock.close()
cv2.destroyAllWindows()

STM32端(底盘控制端):

实现功能:

(1)检测树莓派输出引脚的电平状态,获取目标动作

(2)从(1)的信息中根据动作控制舵机转动

(3)从(1)的信息中根据动作控制电机动作

#include "led.h"
#include "delay.h"
#include "sys.h"
#include "key.h"
#include "usart.h"
#include "exti.h"
#include "wdg.h"
#include "timer.h"
#include "control.h"
#include "pwm_in.h"
#include "LQ12864.h"

float angle_x;
float angle_y;
float angle_z;

char stxt[20];

int angle;
int speed;

int sta = 0;
int FXsta = 0;
int default_speed = 500;

int main(void)
 {
  SystemInit();
	delay_init(72);	     //ÑÓʱ³õʼ»¯
	NVIC_Configuration();//ÅäÖÃÖжÏ
	//KEY_Init();          //°´¼ü¶Ë¿Ú³õʼ»¯ 
  TIM2_PWM_Init();     //ÐγÉPWM²¨ÐÎ
  TIM1_PWM_Init();     //ÐγÉPWM²¨ÐÎ

	//Timerx_Init(1000-1,72-1);//10KhzµÄ¼ÆÊýƵÂÊ£¬¼ÆÊýµ½5000Ϊ1ms 		
	TIM4_Cap_Init(0xffff,71); //ÒÔ1MhzµÄƵÂʼÆÊý
	 
	LED_Init();          //LCDÒý½Å³õʼ»¯ÔÚÕâÀïÃæ
	 
	LCD_Init();  //A0,A1ÓëÒ£¿ØÆ÷´«³åÍ»
	
	speed = 600;
	angle = 8570;
	TIM_SetCompare2(TIM1, angle);//TIM1µÄÕ¼¿Õ±È·´ÏòÊä³ö   8650Öмä ×î×ó8500 ×îÓÒ8800

	while(1)
	{										
   if(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_9) == 0 && GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_10) == 0) speed=0; //Í£³µ
   if(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_9) == 0 && GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_10) == 1) {sta=1;speed=default_speed;} //Ç°½ø
   if(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_9) == 1 && GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_10) == 0) {sta=2;speed=default_speed;} //ºóÍË
//   if(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_9) == 1 && GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_10) == 1) sta=4;   //±¸ÓÃ---

   if(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_11) == 0 && GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_12) == 0) angle=8555; //»ØÕý
   if(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_11) == 0 && GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_12) == 1) angle=8755; //ÓÒת
   if(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_11) == 1 && GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_12) == 0) angle=8395; //×óת
//   if(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_11) == 1 && GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_12) == 1) speed=500; //±¸ÓÃ----


	 //Èí¼þÒ£¿Ø
	 if(sta == 1)//Ç°½ø
	 {
		   TIM_SetCompare1(TIM2, speed);
			 TIM_SetCompare2(TIM2, 0);
			 TIM_SetCompare3(TIM2, speed);
			 TIM_SetCompare4(TIM2, 0);
	 }
	 if(sta == 2)//µ¹³µ
	 {
		   TIM_SetCompare1(TIM2, 0);
			 TIM_SetCompare2(TIM2, speed);
			 TIM_SetCompare3(TIM2, 0);
			 TIM_SetCompare4(TIM2, speed);
	 }
	 if(speed == 0)//Í£³µ
	 {
	     TIM_SetCompare1(TIM2, 0);
			 TIM_SetCompare2(TIM2, 0);
			 TIM_SetCompare3(TIM2, 0);
			 TIM_SetCompare4(TIM2, 0);
	 }
    
	 	TIM_SetCompare2(TIM1, angle);//TIM1µÄÕ¼¿Õ±È·´ÏòÊä³ö   8650Öмä ×î×ó8500 ×îÓÒ8800

	 
	}
}

 

你可能感兴趣的:(树莓派,jeston,NX,STM32专栏,python,人工智能,socket,物联网)