利用单片机在封闭环境下计算水下物体数量

2024-03-02 13:20

本文主要是介绍利用单片机在封闭环境下计算水下物体数量,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

目录

前言

一、架构图及流程图

二、ESP32-CAM代码

三、Web服务器

四、图像处理程序代码

五、前端代码

六、使用流程

总结



前言

随着当今社会的发展,水产品市场作为一个重要的食品供应领域,其规模庞大且竞争激烈。然而,在售卖水产品时,例如螃蟹,虾等,通常需要按照数量来计算和售卖,这个传统的方式不仅耗费了大量的人力和时间资源,还容易引发计算错误,导致盈利能力下降。

传统的水产品销售方式主要依赖于人工计算,这需要雇佣大量的工作人员来记录、计算和核对订单。这不仅增加了人力成本,还容易出现计算错误,导致产品的过多或过少售出,从而损害了盈利能力和客户满意度。此外,传统方式也会导致资源浪费。

所以传统的水产品销售方式在当今社会已经不再适用,因为它浪费了资源,容易出现错误,降低了盈利能力。本文便设计了一种基于单片机的设备,旨在在封闭环境下计算水下物体的数量,从而提高售卖过程的效率和精度。通过采用现代技术和数字化销售方式,水产品市场可以提高效率,减少成本,更好地满足客户和商户的需求,从而在竞争激烈的市场中保持竞争力。

一、架构图及流程图

架构图

流程图

二、ESP32-CAM代码

import machine
import time
import network
import ntptime
import urequests as requests
from machine import Timer, Pin# 声明引脚 13 作为LED的引脚
red_pin = Pin(13, Pin.OUT)
timer = Timer(1)  # 创建定时器对象
(ST_INIT, ST_READY, ST_BUSY) = (300, 0, 100)def toggle_led(red_pin):red_pin.value(not red_pin.value())
#指示灯
def led_blink_timed(timer, red_pin, state):#就绪if state == 'READY':timer.deinit()red_pin.value(ST_READY)#初始elif state == 'INIT':timer.init(period=ST_INIT, mode=Timer.PERIODIC, callback=lambda t: toggle_led(red_pin))#忙碌elif state == 'BUSY':timer.init(period=ST_BUSY, mode=Timer.PERIODIC, callback=lambda t: toggle_led(red_pin))else:print('not define yet')# 定时器触发
led_blink_timed(timer, red_pin, state='INIT')# 连接到 Wi-Fi 网络
def connectWiFi():wlan = network.WLAN(network.STA_IF)wlan.active(True)if not wlan.isconnected():print("连接到Wi-Fi网络...")wlan.connect('ESP32','123456789')while not wlan.isconnected():passprint('网络配置: ', wlan.ifconfig())led_blink_timed(timer, red_pin, state='INIT')
connectWiFi()# freq设置PWM频率,duty设置初始占空比为1023
led_pin = machine.PWM(machine.Pin(4), freq=1000, duty=1023)def led_control(current_brightness, led_pwm):if current_brightness == 1023:current_brightness = 100elif current_brightness == 100:current_brightness = 50elif current_brightness == 50:current_brightness = 1023else:current_brightness = 1023  # 如果当前亮度无效,将其设置为默认值1023led_pwm.duty(current_brightness)  # 更新LED亮度return current_brightness# 在主循环中初始化 LED 亮度
led_brightness = 1023import camera
# 拍照并上传图片
def image_upload():flask_url = 'http://172.20.10.2:5000/esp32_im'led_blink_timed(timer, red_pin, state='BUSY')# 初始化摄像头while not camera.init(0, format=camera.JPEG, fb_location=camera.PSRAM):time.sleep(1)frame = camera.capture()print("拍摄完成")camera.deinit()led_blink_timed(timer, red_pin, state='READY')r = requests.post(flask_url, headers={'content-type': 'image/jpeg'}, data=frame)return r.json(), 200, {'Content-Type': 'application/json'}# 初始化按钮引脚
button_pin = machine.Pin(12, machine.Pin.IN, machine.Pin.PULL_UP)
if __name__ == '__main__':button_state = button_pin.value()  # 初始化按钮状态last_trigger_time = 0  # 上次触发操作的时间last_button_state = 1  # 初始状态为按键未按下last_click_time = 0double_click_timeout = 3  # 双击的最大时间间隔min_double_click_interval = 1  # 最小双击间隔,单位为秒debounce_delay = 50  # 消抖延迟,毫秒while True:current_time = time.ticks_ms()  # 获取当前时间,以便检查是否达到了消抖延迟。# 检查消抖延迟是否满足if current_time - last_trigger_time >= debounce_delay:new_button_state = button_pin.value()  # 读取按钮引脚的当前状态if new_button_state != button_state:# 按钮状态改变button_state = new_button_stateif button_state == 0:# 根据单击或双击执行不同的操作time_since_last_click = current_time - last_click_timeprint(time_since_last_click)if (time_since_last_click > min_double_click_interval * 1000and time_since_last_click < double_click_timeout * 1000):led_brightness = led_control(led_brightness, led_pin)print("亮度:",led_brightness)else:print("单击操作")print("发送照片")image_upload()print("发送成功")last_click_time = current_timelast_trigger_time = current_time

注释:照明LED是ESP32-CAM自带的灯,引脚为4,按钮接的引脚为12。状态灯red,接的引脚为13。

三、Web服务器

from flask import Flask, request, jsonify,render_template
from flask_cors import cross_origin
import time
import os
from countstone import countstones
app = Flask(__name__)
@app.route("/esp32_im", methods=["POST","GET"])
def process_image():print("保存图片1")imageData = request.get_data(parse_form_data=False)filename = time.strftime("%m%d%H%M%S", time.localtime()) + ".jpg"path = "./images/" + str(filename)file = open(path, "wb")file.write(imageData)countstones()print("图片已处理")return jsonify({'msg': '图片已保存'})@app.route("/send_image", methods=["GET"])
def send_image1():# 获取最新的照片发送到前端显示print("开始发送图片到前端")maxnum = []picturePath = './static/finsh/'for fi in os.listdir(picturePath):if fi.endswith(".jpg"):maxnum.append(int(fi[0:10]))maxPhoto = max(maxnum)# print(max(maxnum))path = "./static/finsh/"  + str(maxPhoto) + ".jpg"#frame = open(path, 'rb')print(path)return render_template('index.html', image_url=path)
if __name__ == "__main__":app.run(host='0.0.0.0',debug=True,)   

四、图像处理程序代码

import cv2
import os
import numpy as np
from matplotlib.pyplot import figure, imshow, axis,showdef countstones():maxnum = []picturePath = './images'for fi in os.listdir(picturePath):if fi.endswith(".jpg"):maxnum.append(int(fi[0:10]))maxPhoto = max(maxnum)path = "./images/"  + str(maxPhoto) + ".jpg"img = cv2.imread(path)# Apply Gamma=0.4 on the normalised image and then multiply by scaling constant (For 8 bit, c=2 5)gamma_point_four = np.array(255*(img/255)**0.4,dtype='uint8')# Similarly, Apply Gamma=0.8gamma_point_eight = np.array(255*(img/255)**0.9,dtype='uint8')# Display the images in subplotsimg3 = cv2.hconcat([gamma_point_four,gamma_point_eight])frame = cv2.normalize(gamma_point_eight, None, alpha=0, beta=1, norm_type=cv2.NORM_MINMAX, dtype=cv2.CV_32F)# scale to uint8frame = (255*frame).astype(np.uint8)image = frame[80:450, 130:520] # 找出ROIhsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)# 转换颜色空间 BGR 到 HSV# 定义HSV中蓝色的范围color_dist = {'red': {'Lower': np.array([0, 43,46]), 'Upper': np.array([13, 255, 255])},'blue': {'Lower': np.array([75, 28, 65]), 'Upper': np.array([120, 255, 220])},'green': {'Lower': np.array([35, 43, 35]), 'Upper': np.array([90, 255, 255])},'pink': {'Lower': np.array([156, 43, 46]), 'Upper': np.array([180, 255, 255])},'yellow': {'Lower': np.array([26, 43, 46]), 'Upper': np.array([34, 255, 255])},}filterStone = 'blue'lower_blue = color_dist[filterStone]['Lower'] upper_blue = color_dist[filterStone]['Upper'] # 设置HSV的阈值使得只取蓝色mask = cv2.inRange(hsv, lower_blue, upper_blue) # 过滤蓝色石头# 将掩膜和图像逐像素相加res = cv2.bitwise_and(image,image, mask= mask)res2rgba = cv2.cvtColor(res, cv2.COLOR_BGR2RGBA)# 透过开运算取得蓝色石头所在位置erode_hsv = cv2.erode(mask, None, iterations=1)dilated_hsv = cv2.dilate(erode_hsv, None, iterations=1)# 去除小的轮廓(噪点)min_contour_area = 2 # 轮廓的最小面积,根据需要调整lunkuo_img=image.copy()#获取轮廓_,binary=cv2.threshold(erode_hsv,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)contours, hierarchy = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)filtered_contours = [contour for contour in contours if cv2.contourArea(contour) >= min_contour_area]num_filtered_contours = len(filtered_contours)print("过滤后的轮廓数量:", num_filtered_contours)# 画出轮廓image = cv2.drawContours(lunkuo_img, filtered_contours, -1, (0, 255, 0), 1) cv2.putText(image, str(num_filtered_contours), (10, 30),cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)BGRimage = image[:, :, ::-1]finshPath = "./static/finsh/" + str(maxPhoto) + ".jpg"cv2.imwrite(finshPath, image)return num_filtered_contours

注释:水下物体是用蓝色石头代替的。

五、前端代码

<!DOCTYPE html>
<html>
<head><title>用户界面</title><style>body {display: flex;flex-direction: column;justify-content: center;align-items: center;height: 100vh;margin: 0;}</style><meta http-equiv="refresh" content="5">
</head>
<body><h1>用户界面</h1><img src="{{ image_url }}" alt="Latest Image" width="700" height="700">
</body>
</html>

六、使用流程

第一步:WiFi配置

将ESP32-CAM的WiFi网络名称(SSID)设置为"ESP32",密码设置为"123456789"。

第二步:连接WiFi并启动Web服务器

启动Web服务器,确保它连接到WiFi网络"ESP32"。

打开ESP32-CAM的电源,确保它连接到WiFi网络"ESP32"。

第三步:设备准备就绪指示

启动ESP32-CAM,当ESP32-CAM上的LED慢闪时,表示设备已准备就绪。

第四步:调整LED亮度

双击:快速按两次按钮以调整LED亮度。

调整完成:调整亮度完成后,LED将短暂闪烁一次。

第五步:拍摄图片

单击:按下ESP32-CAM上的按钮,以拍摄一张照片。

成功拍摄:成功拍摄后,LED将短暂闪烁一次。

第六步:查看拍摄的图片

在浏览器中访问以下网址:http://172.20.10.2:5000/send_image 以查看拍摄的图片的处理结果。


总结

本项目实现了在封闭水下环境中计算物体数量的任务,通过深入了解计算机视觉、人工智能和单片机开发领域的核心知识,实现了这一复杂任务。巧妙地将计算机视觉和人工智能的理论应用于实际,通过图像识别、分类和分析等算法,精确计算出水下物体的数量。

在项目的执行过程中,不仅深入研究了相关技术,还通过图像数据的预处理,包括降噪、增强和边缘检测等步骤,优化了数据的特征提取。通过分类算法,能够精确地识别和分类水下物体,最终得出准确的数量计算结果。

此外,对于单片机开发能力也得到了充分提升。能够选择并配置适当的硬件设备,满足项目需求,同时编写高效而可靠的代码,实现了所需功能。掌握了与传感器和其他外部设备的有效通信,确保了图像数据的采集和处理。

总的来说,本项目成功在封闭水下环境中实现了计算水下物体数量的目标。这一项目不仅为我提供了宝贵的经验和技能,深化了对计算机视觉和人工智能领域的理解,还增强了在单片机开发和项目管理方面的能力。这些积累将在未来的研究和工作中增加自信和有能力地面对各种挑战。

这篇关于利用单片机在封闭环境下计算水下物体数量的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/766258

相关文章

ESP32 esp-idf esp-adf环境安装及.a库创建与编译

简介 ESP32 功能丰富的 Wi-Fi & 蓝牙 MCU, 适用于多样的物联网应用。使用freertos操作系统。 ESP-IDF 官方物联网开发框架。 ESP-ADF 官方音频开发框架。 文档参照 https://espressif-docs.readthedocs-hosted.com/projects/esp-adf/zh-cn/latest/get-started/index

51单片机学习记录———定时器

文章目录 前言一、定时器介绍二、STC89C52定时器资源三、定时器框图四、定时器模式五、定时器相关寄存器六、定时器练习 前言 一个学习嵌入式的小白~ 有问题评论区或私信指出~ 提示:以下是本篇文章正文内容,下面案例可供参考 一、定时器介绍 定时器介绍:51单片机的定时器属于单片机的内部资源,其电路的连接和运转均在单片机内部完成。 定时器作用: 1.用于计数系统,可

计算绕原点旋转某角度后的点的坐标

问题: A点(x, y)按顺时针旋转 theta 角度后点的坐标为A1点(x1,y1)  ,求x1 y1坐标用(x,y)和 theta 来表示 方法一: 设 OA 向量和x轴的角度为 alpha , 那么顺时针转过 theta后 ,OA1 向量和x轴的角度为 (alpha - theta) 。 使用圆的参数方程来表示点坐标。A的坐标可以表示为: \[\left\{ {\begin{ar

UnrealScriptIDE调试环境部署

先安装vs2010   再安装VSIsoShell.exe, 下载地址 https://pan.baidu.com/s/10kPNUuDGTbWXbz7Nos-1WA       fd3t   最后安装unside,下载地址 https://archive.codeplex.com/?p=uside  安装中间有一步选择Binary文件夹要选对路径。   安装好以后,启动 UDKDe

零基础STM32单片机编程入门(一)初识STM32单片机

文章目录 一.概要二.单片机型号命名规则三.STM32F103系统架构四.STM32F103C8T6单片机启动流程五.STM32F103C8T6单片机主要外设资源六.编程过程中芯片数据手册的作用1.单片机外设资源情况2.STM32单片机内部框图3.STM32单片机管脚图4.STM32单片机每个管脚可配功能5.单片机功耗数据6.FALSH编程时间,擦写次数7.I/O高低电平电压表格8.外设接口

KLayout ------ 旋转物体90度并做平移

KLayout ------ 旋转创建的物体 正文 正文 前段时间,有个小伙伴留言问我,KLayout 中如何旋转自己创建的物体,这里特来说明一下。 import pyapoly = pya.DPolygon([pya.DPoint(0, 0), pya.DPoint(0, 5), pya

力扣SQL50 每位经理的下属员工数量 join

Problem: 1731. 每位经理的下属员工数量 👨‍🏫 参考题解 Code select m.Employee_id, m.name,count(*) reports_count,round(avg(e.age),0) average_agefrom Employees ejoin Employees mon e.reports_to = m.Employee_id

API-环境对象

学习目标: 掌握环境对象 学习内容: 环境对象作用 环境对象: 指的是函数内部特殊的变量this,它代表着当前函数运行时所处的环境。 作用: 弄清楚this的指向,可以让我们代码更简洁。 函数的调用方式不同,this指代的对象也不同。【谁调用,this就是谁】是判断this指向的粗略规则。直接调用函数,其实相当于是window.函数,所以this指代window。

【云计算 复习】第1节 云计算概述和 GFS + chunk

一、云计算概述 1.云计算的商业模式 (1)软件即服务(SaaS) 有些景区给游客提供烧烤场地,游客需要自己挖坑或者砌烧烤台,然后买肉、串串、烧烤。 (2)平台即服务(PaaS) 有些景区给游客提供烧烤场地,同时搭建好烧烤台,游客只需要自己带食材和调料、串串、烧烤。 (3)基础设施即服务(IaaS) 有些景区给游客提供烧烤场地,同时搭建好烧烤台,还有专门的厨师来烧烤,用户不需要关心前面的所有

Pycharm配置conda环境(解决新版本无法识别可执行文件问题)

引言: 很多小伙伴在下载最新版本的pycharm或者更新到最新版本后为项目配置conda环境的时候,发现文件夹目录中无法显示可执行文件(一般为python.exe),以下就是本人遇到该问题后试验和解决该问题的一些方法和思路。 一般遇到该问题的人群有两种,一种是刚入门对pycharm进行conda环境配置的小白(例如我),不熟悉相关环境配置的操作和过程,还有一种是入坑pycharm有段时间的老手