基于yolov7与arduino的眼睛跟随模块

整个模块的介绍

我们首先需要一个图片收集的模块来对当前的图片进行收集然后将图片传至服务端对图片中的眼睛利用YOLO进行检测最后将数据传至arduino使其控制动力模块来进行位置调整使目标一直与眼睛处于一个水平线

摄像模块

这里我们使用ESP32-cam来获取照片并通过无线网络来上传至服务器
ESP32-CAM
要使用它我们首先需要下载支持esp32的库
下载教程: https://blog.csdn.net/qq_62975494/article/details/132539804
烧录时我们选择AI Thinker
在这里插入图片描述

图片传输模块

我们通过esp32的wifi模式对拍摄到的图片进行传输
与服务端建立连接->发送第一张图片->等待服务端指令->收到指令后继续发送->等待指令
客户端代码(esp32-cam)

/*
网络调试助手
https://soft.3dmgame.com/down/213757.html
*/
#include <Arduino.h>
#include <WiFi.h>
#include "esp_camera.h"
#include <vector>
 
const char *ssid = "xxo";   wifi id
const char *password = "12345678";  wifi密码
const IPAddress serverIP(10,218,19,53); //欲访问的地址
uint16_t serverPort = 8080;         //服务器端口号
 
#define maxcache 1430
 
WiFiClient client; //声明一个客户端对象,用于与服务器进行连接
 
//CAMERA_MODEL_AI_THINKER类型摄像头的引脚定义
#define PWDN_GPIO_NUM     32
#define RESET_GPIO_NUM    -1
#define XCLK_GPIO_NUM      0
#define SIOD_GPIO_NUM     26
#define SIOC_GPIO_NUM     27
 
#define Y9_GPIO_NUM       35
#define Y8_GPIO_NUM       34
#define Y7_GPIO_NUM       39
#define Y6_GPIO_NUM       36
#define Y5_GPIO_NUM       21
#define Y4_GPIO_NUM       19
#define Y3_GPIO_NUM       18
#define Y2_GPIO_NUM        5
#define VSYNC_GPIO_NUM    25
#define HREF_GPIO_NUM     23
#define PCLK_GPIO_NUM     22
 
static camera_config_t camera_config = {
    .pin_pwdn = PWDN_GPIO_NUM,
    .pin_reset = RESET_GPIO_NUM,
    .pin_xclk = XCLK_GPIO_NUM,  
    .pin_sscb_sda = SIOD_GPIO_NUM,
    .pin_sscb_scl = SIOC_GPIO_NUM,
    
    .pin_d7 = Y9_GPIO_NUM,
    .pin_d6 = Y8_GPIO_NUM,
    .pin_d5 = Y7_GPIO_NUM,
    .pin_d4 = Y6_GPIO_NUM,
    .pin_d3 = Y5_GPIO_NUM,
    .pin_d2 = Y4_GPIO_NUM,
    .pin_d1 = Y3_GPIO_NUM,
    .pin_d0 = Y2_GPIO_NUM,
    .pin_vsync = VSYNC_GPIO_NUM,
    .pin_href = HREF_GPIO_NUM,
    .pin_pclk = PCLK_GPIO_NUM,
    
    .xclk_freq_hz = 20000000,  //帧率
    .ledc_timer = LEDC_TIMER_0,
    .ledc_channel = LEDC_CHANNEL_0,
    
    .pixel_format = PIXFORMAT_JPEG,
    .frame_size = FRAMESIZE_VGA,    //图片格式
    .jpeg_quality = 12, //PEG图片质量(jpeg_quality),0-63,数字越小质量越高
    .fb_count = 1,
};
void wifi_init()
{
    WiFi.mode(WIFI_STA);
    WiFi.setSleep(false); //关闭STA模式下wifi休眠,提高响应速度
    WiFi.begin(ssid, password);
    while (WiFi.status() != WL_CONNECTED)
    {
        delay(500);
        Serial.print(".");
    }
    Serial.println("WiFi Connected!");
    Serial.print("IP Address:");
    Serial.println(WiFi.localIP());
}
esp_err_t camera_init() {
    //initialize the camera
    esp_err_t err = esp_camera_init(&camera_config);
    if (err != ESP_OK) {
        Serial.println("Camera Init Failed");
        return err;
    }
    sensor_t * s = esp_camera_sensor_get();
    //initial sensors are flipped vertically and colors are a bit saturated
    if (s->id.PID == OV2640_PID) {
    //        s->set_vflip(s, 1);//flip it back
    //        s->set_brightness(s, 1);//up the blightness just a bit
    //        s->set_contrast(s, 1);
    }
    Serial.println("Camera Init OK!");
    return ESP_OK;
}
 
void setup()
{
    Serial.begin(115200);
    wifi_init();
    camera_init();
}
 
void loop()
{
    Serial.println("Try To Connect TCP Server!");
    if (client.connect(serverIP, serverPort)) //尝试访问目标地址
    {
        Serial.println("Connect Tcp Server Success!");
        //client.println("Frame Begin");  //46 72 61 6D 65 20 42 65 67 69 6E // 0D 0A 代表换行  //向服务器发送数据
        while (1){       
          camera_fb_t * fb = esp_camera_fb_get();
          uint8_t * temp = fb->buf; //这个是为了保存一个地址,在摄像头数据发送完毕后需要返回,否则会出现板子发送一段时间后自动重启,不断重复
          if (!fb)
          {
              Serial.println( "Camera Capture Failed");
          }
          else
          { 
            //先发送Frame Begin 表示开始发送图片 然后将图片数据分包发送 每次发送1430 余数最后发送 
            //完毕后发送结束标志 Frame Over 表示一张图片发送完毕 
            client.print("Frame Begin"); //一张图片的起始标志
            // 将图片数据分段发送
            int leng = fb->len;
            int timess = leng/maxcache;
            int extra = leng%maxcache;
            for(int j = 0;j< timess;j++)
            {
              client.write(fb->buf, maxcache); 
              for(int i =0;i< maxcache;i++)
              {
                fb->buf++;
              }
            }
            client.write(fb->buf, extra);
            client.print("Frame Over");      // 一张图片的结束标志
            Serial.print("This Frame Length:");
            Serial.print(fb->len);
            Serial.println(".Succes To Send Image For TCP!");
            //return the frame buffer back to the driver for reuse
            fb->buf = temp; //将当时保存的指针重新返还
            esp_camera_fb_return(fb);  //这一步在发送完毕后要执行,具体作用还未可知。        
          }
          //等待服务端回应
          while (1) //如果已连接或有收到的未读取的数据
        {
            if (client.available()) //如果有数据可读取
            {
                String line = client.readStringUntil('\n'); //读取数据到换行符
                Serial.print("读取到数据:");
                Serial.println(line);
                break;
            }
        }
        }
        
        while (client.connected() || client.available()) //如果已连接或有收到的未读取的数据
        {
            if (client.available()) //如果有数据可读取
            {
                String line = client.readStringUntil('\n'); //读取数据到换行符
                Serial.print("ReceiveData:");
                Serial.println(line);
                client.print("--From ESP32--:Hello Server!");    
            }
        }
        Serial.println("close connect!");
        //client.stop(); //关闭客户端
        
    }
    else
    {
        Serial.println("Connect To Tcp Server Failed!After 10 Seconds Try Again!");
        client.stop(); //关闭客户端
    }
    delay(10000);
}

服务端我们使用python来进行编写

import socket
import threading
import time

import bluetooth

begin_data = b'Frame Begin'
end_data = b'Frame Over'

v=0

def handle_sock(sock, addr):

    global v
    print('----------开始接收-------')
    temp_data = b''

    while 1:

        if 1:
            
            print(11111111111111111111111111111111)
            data = sock.recv(1430)
            # 如果这一帧数据包的开头是 b'Frame Begin' 则是一张图片的开始
            if data[0:len(begin_data)] == begin_data:
                # 将这一帧数据包的开始标志信息(b'Frame Begin')清除   因为他不属于图片数据
                data = data[len(begin_data):len(data)]
                # 判断这一帧数据流是不是最后一个帧 最后一针数据的结尾时b'Frame Over'
                while data[-len(end_data):] != end_data:
                    temp_data = temp_data + data  # 不是结束的包 讲数据添加进temp_data
                    data = sock.recv(1430)  # 继续接受数据 直到接受的数据包包含b'Frame Over' 表示是这张图片的最后一针
                # 判断为最后一个包 将数据去除 结束标志信息 b'Frame Over'
                temp_data = temp_data + data[0:(len(data) - len(end_data))]  # 将多余的(\r\nFrame Over)去掉 其他放入temp_data
                
                with open(f'./eyes/{v}.jpg', 'wb') as fp:
                    fp.write(temp_data)
                v=v+1


                print("接收到的数据包大小:" + str(len(temp_data)))  # 显示该张照片数据大小

                print('------接收下一张图片--------')
                            #cv2.imshow('server_frame', r_img)
                            #print("接收到的数据包大小:" + str(len(temp_data)))  # 显示该张照片数据大小
                temp_data = b''  # 清空数据 便于下一章照片使用
                            #处理数据
                a=input()

                sock.send("1".encode('utf-8'))
                
        time.sleep(1)
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(('10.218.19.53', 8080))
server.listen(5)
CONNECTION_LIST = []

# 主线程循环接收客户端连接
while True:
    sock, addr = server.accept()
    CONNECTION_LIST.append(sock)
    print('Connect--{}'.format(addr))
    # 连接成功后开一个线程用于处理客户端
    client_thread = threading.Thread(target=handle_sock, args=(sock, addr))
    client_thread.start()

通过传输模块我们就可以将esp32获取的图片存至服务端的指定位置等待检测模块的目标检测
使用esp32-cam连接热点时我们需要将热点的网络频带调制2.4因为它不支持5G频带

在这里插入图片描述

图像检测模块

此模块中我们将使用yolov7对传输模块传输来的图片数据进行检测然后通过蓝牙将检测到的数据发送至控制模块
数据集的训练请参考

数据集训练: https://blog.csdn.net/qq_62975494/article/details/129786717
云GPU的使用: https://blog.csdn.net/qq_62975494/article/details/136565413

import torch
# 加载本地模型
device = torch.device("cuda")
model = torch.hub.load('D:/AI/yolov7-main', 'custom',
                       'D:\AI\yolov7-main\weights\last2.pt',
                       source='local', force_reload=False)
while 1:

    if 1:
        # 使用模型
        model = model.to(device)
        # 开始推理
        results = model('./eyes.jpg')
        # 过滤模型
        xmins = results.pandas().xyxy[0]['xmin']
        ymins = results.pandas().xyxy[0]['ymin']
        xmaxs = results.pandas().xyxy[0]['xmax']
        ymaxs = results.pandas().xyxy[0]['ymax']
        class_list = results.pandas().xyxy[0]['class']
        confidences = results.pandas().xyxy[0]['confidence']
        newlist = []
        for xmin, ymin, xmax, ymax, classitem, conf in zip(xmins, ymins, xmaxs, ymaxs, class_list, confidences):
            if classitem == 0 and conf > 0.5:
                newlist.append([int(xmin), int(ymin), int(xmax), int(ymax), conf])
        print(newlist)
#图片格式640x480   240

newlist中储存的就是图片中检测到的目标
然后我们需要通过计算将所要移动的距离通过蓝牙发送给控制模块(arduino)

python使用蓝牙发送数据

import time
import bluetooth

sock = bluetooth.BluetoothSocket(bluetooth.RFCOMM)
sock.connect(("98:DA:20:04:C1:67", 1))
time.sleep(3)
while 1:
    a = input()
    sock.send(a)
    time.sleep(2)

控制模块

控制模块我们使用arduino控制模块主要负责动力模块的控制和接收检测模块得到的结果
arduino蓝牙模块使用: https://blog.csdn.net/catzhaojia/article/details/119243058
arduino超声波测距: https://blog.csdn.net/TonyIOT/article/details/103232332
控制模块模块图
在这里插入图片描述

#include <SoftwareSerial.h> 
// Pin10接HC05的TXD
// Pin1接HC05的RXD

String comdata = "";
int timeb=0;
SoftwareSerial BT(10, 11); 
char val;
// 设定SR04连接的数字引脚
const int trigPin = 7; //设置接受引脚
const int echoPin = 8; //设置发射引脚


float sound_spd=343;//声速初始值
float distance; //距离


void setup() {
  Serial.begin(115200); 
  Serial.println("bluetooth is ready!蓝牙准备就绪");
  BT.begin(9600);
   pinMode(trigPin, OUTPUT); 
  // 要检测引脚上输入的脉冲宽度,需要先设置为输入状态
  pinMode(echoPin, INPUT);   
pinMode(6, OUTPUT);
pinMode(3, OUTPUT);
pinMode(4, OUTPUT);
pinMode(5, OUTPUT);
digitalWrite(5, LOW);
  Serial.println("HC-SR04-2019.7.14测距开始:");
digitalWrite(23, HIGH);
digitalWrite(24, LOW);
digitalWrite(25, LOW);
}
 
void loop() {
// 产生一个10us的高脉冲去触发TrigPin 
  digitalWrite(trigPin, LOW); 
  delayMicroseconds(2); 
  //产生高脉冲前线产生2us低脉冲,确保高脉冲的纯净
  digitalWrite(trigPin, HIGH); 
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW); 
    // 检测脉冲宽度,并计算出距离
  distance = pulseIn(echoPin, HIGH)/ 58.00;
 
        Serial.print(distance);                                //把得到的距离值通过串口通信返回给电脑,通过串口监视器显示出来
        Serial.println("cm"); 

 if(distance<=20){
  digitalWrite(6, HIGH);
 }
else{
  digitalWrite(6, LOW);
}
 delay(300);                                  //500mS测量一次


  while (BT.available() > 0)  
    {
        comdata += char(BT.read());
        delay(2);
    }
    if (comdata.length() > 0)
    {
        Serial.println(comdata);
        timeb=comdata.toFloat();
        if(timeb<0){
          下降
        }
        else if(timeb>0&&timeb<998){
          上升
        }
        else if(timeb==999){
          不动
        }
        comdata = "";
    }
}

动力模块

动力模块用来控制升降平台的升降由于需要24v电源无法由主板直接供电所以我们使用继电器用主板的5v电压来控制更高的电压
arduino继电器使用: https://blog.csdn.net/TonyIOT/article/details/82875925

在这里插入图片描述

继电器1用来控制上升接IN1连接arduino 4号引脚
继电器2用来控制下降接IN2连接arduino 5号引脚
继电器1来控制整个动力模块的电源通断接IN3连接arduino 3号引脚
电源与升降台连接图
在这里插入图片描述

#include <SoftwareSerial.h> 
// Pin10接HC05的TXD
// Pin1接HC05的RXD

String comdata = "";
int timeb=0;
SoftwareSerial BT(10, 11); 
char val;
// 设定SR04连接的数字引脚
const int trigPin = 7; //设置接受引脚
const int echoPin = 8; //设置发射引脚


float sound_spd=343;//声速初始值
float distance; //距离


void setup() {
  Serial.begin(115200); 
  Serial.println("bluetooth is ready!蓝牙准备就绪");
  BT.begin(9600);
   pinMode(trigPin, OUTPUT); 
  // 要检测引脚上输入的脉冲宽度,需要先设置为输入状态
  pinMode(echoPin, INPUT);   
pinMode(6, OUTPUT);
pinMode(3, OUTPUT);
pinMode(4, OUTPUT);
pinMode(5, OUTPUT);
digitalWrite(5, LOW);
  Serial.println("HC-SR04-2019.7.14测距开始:");
digitalWrite(23, HIGH);
digitalWrite(24, LOW);
digitalWrite(25, LOW);
}
 
void loop() {




// 产生一个10us的高脉冲去触发TrigPin 
  digitalWrite(trigPin, LOW); 
  delayMicroseconds(2); 
  //产生高脉冲前线产生2us低脉冲,确保高脉冲的纯净

  digitalWrite(trigPin, HIGH); 
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW); 


    // 检测脉冲宽度,并计算出距离
  distance = pulseIn(echoPin, HIGH)/ 58.00;
 
        Serial.print(distance);                                //把得到的距离值通过串口通信返回给电脑,通过串口监视器显示出来
        Serial.println("cm"); 

 if(distance<=20){
  digitalWrite(6, HIGH);
 }
else{
  digitalWrite(6, LOW);
}
 delay(300);                                  //500mS测量一次







  while (BT.available() > 0)  
    {
        comdata += char(BT.read());
        delay(2);
    }
    if (comdata.length() > 0)
    {
        Serial.println(comdata);
        timeb=comdata.toFloat();
        if(timeb<0){
          digitalWrite(3, LOW); 
          digitalWrite(5, HIGH);
          delay(timeb*1000*-1);
          digitalWrite(3, HIGH); 
          digitalWrite(5, LOW);
        }
        else if(timeb>0&&timeb<998){
          digitalWrite(3, LOW); 
          digitalWrite(4, HIGH);
          Serial.println(timeb);
          delay(timeb*1000);
          digitalWrite(3, HIGH); 
          digitalWrite(4, LOW);
        }
        else if(timeb==999){
          digitalWrite(3, LOW); 
          digitalWrite(5, HIGH);
          delay(1000);
          
          digitalWrite(5, LOW);
          
          digitalWrite(4, HIGH);
          delay(1000);
          digitalWrite(3, HIGH); 
          digitalWrite(4, LOW);
        }
        comdata = "";
    }

   
 
  
}
  • 29
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

我把把C

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

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

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

打赏作者

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

抵扣说明:

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

余额充值