【DigiKey“智造万物,快乐不停”创意大赛】工地安全检测移动装置

发布时间 2023-12-29 18:39:08作者: wuboy19

【DigiKey“智造万物,快乐不停”创意大赛】工地安全检测移动装置

项目背景

    随着工业化进程的加速和建筑业的发展,机器人在工业和建筑领域的应用越来越广泛。对于建筑工地,开发一种高效、准确、安全的巡检技术,在工业和建筑现场,员工经常需要佩戴安全帽以防止头部受伤。安全帽识别技术可以帮助监测工作人员是否正确佩戴安全帽,以确保他们在危险环境中的安全。许多国家和地区都有法规和规范要求在特定环境中佩戴安全帽。安全帽识别技术有助于确保企业和个人遵守这些法规,降低事故风险。在大型工地或生产场所,实时监测员工是否佩戴安全帽对于管理人员来说是一项重要任务。安全帽识别系统可以自动执行这一任务,提高监控效率。机器人采用麦克拉姆轮,方便机器人能够实现多方位的平移,方便巡检识别工作的进行,以实现更加智能、高效的工作方式。在巡检识别方面,机器人可以通过搭载摄像头,实现对工作人员是否佩戴安全帽进行检测。
    机器人巡检技术的发展,可以有效地提高工业和建筑领域的生产效率和质量,降低人工成本和安全风险。具体来说,机器人巡检可以代替人工进行工业设备和建筑物的巡检,提高巡检效率和准确性,降低人工巡检的安全风险。安全帽识别技术有助于预防意外事故,特别是那些可能导致头部受伤的事故。通过及时发现并纠正未佩戴安全帽的情况,可以降低事故发生的可能性。自动化的安全帽识别系统可以替代人工监测,提高工作效率。这对于大型工地或需要频繁监测的场所尤为重要。安全帽识别技术有助于确保企业和员工遵守安全规定,提高工作场所的合规性。此外,对于一些特定行业,未佩戴安全帽可能导致责任问题,因此及时的安全帽识别有助于规遍责任。通过防止事故的发生,安全帽识别技术可以帮助降低企业的人身伤害成本,包括医疗费用、赔偿金和法律费用。安全帽识别技术在提高工作场所安全性、遵守法规、降低事故风险以及提高管理效率方面具有重要意义。

作品简介

    本项目利用视觉检测人是否佩戴安全帽,如果未佩戴安全帽将记录下来,上传到onenet云平台,通过TCP协议图传到电脑实时图像,装置利用麦克拉姆轮便于移动,控制通过esp_now协议控制,低延迟控制。本项目利用solidworks设计了二维云台结构连接上maixduino视觉模块,用户通过按红外遥控让红外遥控接收器接收到后,二维云台能够进行两个自由度的运动,让视觉处理器能够运动去检测安全帽。用户通过按红外遥控让红外遥控接收器接收到,控制小车的前进后退,当视觉处理器识别到是否佩戴安全帽会显示在lcd显示屏、电脑显示屏,当识别到佩戴安全帽后会上传到onenet云平台,本项目所有器件构成都来自得捷商城,以及全部应用到上面。

picture 0

图1 作品图

作品项目用到的板卡

ESP32-S3-DevKitC-1

    ESP32-S3-DevKitC-1 是一款入门级开发板,搭载 Wi-Fi + Bluetooth® LE 模组 ESP32-S3-WROOM-1、ESP32-S3-WROOM-1U 或 ESP32-S3-WROOM-2。

参考链接:乐鑫ESP32-S3-DevKitC-1

picture 0

图2 ESP32-S3-DevKitC-1

picture 1

图3 ESP32-S3-DevKitC-1实物图

ESP32-C3-LCDkit

    ESP32-C3-LCDkit 是一款基于 ESP32-C3 芯片和 SPI 接口显示屏的评估开发板,不仅通过旋转编码器开关实现屏幕交互,还具有音频播放和红外无线控制功能。由于 ESP32-C3 具有成本低、功耗低、性能强的特点,能够满足用户基本的 GUI 交互需求,其在小尺寸屏幕的应用场景中占据优势。

picture 2

图4 ESP32-C3-LCDkit

picture 3

图5 ESP32-C3-LCDkit实物图

参考链接:乐鑫ESP32-C3-LCDkit

MaixDuino 开发板

    Maixduino开发板以M1Al模块作为核心单元,功能非常很强大,模块内置64位双核处理器芯片,拥有8M的片上SRAM,在Al机器视觉、听觉性能方便表现突出,内置多种硬件加速单元(KPU、FPU,FFT等),总算力最高可达1TOPS,可以方便地实现各类应用场景的机器视觉/听觉算法,也可以进行语音方向扫描和语音数据输出的前置处理工作。此外,开发板还配置了ESP32模块(WiFi+蓝牙一体),简单的操作即可轻松联网。

picture 4

图6 MaixDuino 开发板

picture 6

图7 MaixDuino 开发板实物图

系统方案及功能实现

    本项目是以esp32s3为主控制器,esp32c3为遥控装置,k210作为yolov2算法安全帽识别部署硬件,通过两个TB6612控制四路n20电机以及二维云台构成通过两个MG90s构成,系统框图如下图所示,其中esp32s3负责电机控制,舵机控制以及接收esp32c3的信号,两个esp32信息的传输通过低延迟、低功耗的esp-now协议。k210是负责视觉部分,通过数据集标注、在maixhub中训练模型得到最后的模型。esp32c3是自带红外接收,然后接收红外发射器做出相应的指令然后控制esp32s3主控制器。
picture 1

图8 系统框图

小车硬件设计

    使用立创eda进行设计,设计需要关注一下esp32s3的尺寸,然后设置好间距,方便打板子后,焊接排座直接插入到排座,然后使用的78m05将7.2转5v,以及使用ams117转3.3v来给esp32s3供电,为什么留出电阻口,是方便测试,电机驱动使用的是得捷商城购买的tb6612。
picture 2

图9 原理图

picture 3

图10 PCB图

picture 4

图11 渲染图

小车软件设计

    小车的基本运动控制的话主要是提供四路pwm加上8个GPIO引脚控制正反转

例如控制pwm和限幅:

void Set_Pwm(int moto1, int moto2,int moto3,int moto4)
{
    int Amplitude = 4000;  //===PWM满幅是1024 限制在950

    if (moto1 > 0)
    {
        digitalWrite(IN1_A, HIGH),      digitalWrite(IN1_B, LOW);  //TB6612的电平控制
    }
    else if(moto1 < 0)
    {
        digitalWrite(IN1_A, LOW),       digitalWrite(IN1_B, HIGH); //TB6612的电平控制
    }
    else
    {
        digitalWrite(IN1_A, LOW),       digitalWrite(IN1_B, LOW); //TB6612的电平控制
    }

    if (moto2 > 0)
    {
        digitalWrite(IN2_A, HIGH),      digitalWrite(IN2_B, LOW);  //TB6612的电平控制
    }
    else if(moto2 < 0)
    {
        digitalWrite(IN2_A, LOW),       digitalWrite(IN2_B, HIGH); //TB6612的电平控制
    }
    else
    {
        digitalWrite(IN2_A, LOW),       digitalWrite(IN2_B, LOW); //TB6612的电平控制
    }

    if (moto3 > 0)
    {
        digitalWrite(IN3_A, HIGH),      digitalWrite(IN3_B, LOW);  //TB6612的电平控制
    }
    else if(moto3 < 0)
    {
        digitalWrite(IN3_A, LOW),       digitalWrite(IN3_B, HIGH); //TB6612的电平控制
    }
    else
    {
        digitalWrite(IN3_A, LOW),       digitalWrite(IN3_B, LOW); //TB6612的电平控制
    }

    if (moto4 > 0)
    {
        digitalWrite(IN4_A, HIGH),      digitalWrite(IN4_B, LOW);  //TB6612的电平控制
    }
    else if(moto4 < 0)
    {
        digitalWrite(IN4_A, LOW),       digitalWrite(IN4_B, HIGH); //TB6612的电平控制
    }
    else
    {
        digitalWrite(IN4_A, LOW),       digitalWrite(IN4_B, LOW); //TB6612的电平控制
    }


    //功能:限制PWM赋值
    if (moto1 < -Amplitude)  moto1 = -Amplitude;
    if (moto1 >  Amplitude)  moto1 =  Amplitude;
    if (moto2 < -Amplitude)  moto2 = -Amplitude;
    if (moto2 >  Amplitude)  moto2 =  Amplitude;
    if (moto3 < -Amplitude)  moto3 = -Amplitude;
    if (moto3 >  Amplitude)  moto3 =  Amplitude;
    if (moto4 < -Amplitude)  moto4 = -Amplitude;
    if (moto4 >  Amplitude)  moto4 =  Amplitude;
    //赋值给PWM寄存器
    ledcWrite(pwm_Channel_1,abs(moto1));
    ledcWrite(pwm_Channel_2,abs(moto2));
    ledcWrite(pwm_Channel_3,abs(moto3));
    ledcWrite(pwm_Channel_4,abs(moto4));
}

产生pwm:

void MOTOR_PWM_Init()
{
    ledcSetup(pwm_Channel_1, freq, resolution);	//PWM通道一开启设置
    ledcAttachPin(PWM1, pwm_Channel_1);			//PWM通道一和引脚PWMA关联
    ledcWrite(pwm_Channel_1, 0);				//PWM通道一占空比设置为零

    ledcSetup(pwm_Channel_2, freq, resolution); //PWM通道二开启设置
    ledcAttachPin(PWM2, pwm_Channel_2);			//PWM通道二和引脚PWMB关联
    ledcWrite(pwm_Channel_2, 0);				//PWM通道二占空比设置为零
//
    ledcSetup(pwm_Channel_3, freq, resolution); //PWM通道二开启设置
    ledcAttachPin(PWM3, pwm_Channel_3);			//PWM通道二和引脚PWMB关联
    ledcWrite(pwm_Channel_3, 0);				//PWM通道二占空比设置为零

    ledcSetup(pwm_Channel_4, freq, resolution); //PWM通道二开启设置
    ledcAttachPin(PWM4, pwm_Channel_4);			//PWM通道二和引脚PWMB关联
    ledcWrite(pwm_Channel_4, 0);				//PWM通道二占空比设置为零
}

二维云台设计

    软件使用的是solidworks,因为我是创维的3d打印机,所以使用的是创维的切片软件,在后面会提供stl文件。
材料清单:

铜柱
m2.5*10 3
m2.5*5 3
螺丝
m2.5*3 5
螺母
m2.5 5
两个舵机:mg90s(或者sg90)都行

picture 0

图12 实物图

picture 1

图13 实物图

picture 2

图14 实物图

安全帽识别实现

    在 MaixHub 创建一个检测训练项目,然后采集数据并标注(可以在线标注),创建一个训练任务,参数选择你有的硬件平台,如果没有开发板可以使用手机,选择 yolov2 进行训练即可,训练后会得到模型文件以及曲线情况。
picture 5

图16 页面图

picture 6

图17 曲线关系

picture 7

图18 验证效果图

采集数据集可以用下面的代码,采集到的图像会保存到sd卡中,通过读卡器将图片导入maixhub在线标注即可:

import sensor, lcd
from Maix import GPIO
from fpioa_manager import fm
from board import board_info
import os, sys
import time
import image

#### image size ####
set_windowing = (224, 224)

#### sensor config ####

sensor.reset(freq=22000000, dual_buff=False)
sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.QVGA) # 320x240
try:
    sensor.set_jb_quality(95)         # for IDE display quality
except Exception:
    pass # no IDE support
if set_windowing:
    sensor.set_windowing(set_windowing)
# sensor.set_auto_gain(False)
# sensor.set_auto_whitebal(False, rgb_gain_db=(0x52,0x40,0x4d))
# sensor.set_saturation(0)
# sensor.set_brightness(4)
# sensor.set_contrast(0)
# sensor.set_hmirror(True)        # image horizonal mirror
# sensor.set_vflip(True)          # image vertical flip
# sensor.set_auto_whitebal(False)

sensor.skip_frames()

#### lcd config ####
lcd.init(type=1, freq=15000000)
lcd.rotation(2)

#### boot key ####
boot_pin = 16 # board_info.BOOT_KEY
fm.register(boot_pin, fm.fpioa.GPIOHS0)
key = GPIO(GPIO.GPIOHS0, GPIO.PULL_UP)

######################################################

#### main ####
def capture_main(key):
    def draw_string(img, x, y, text, color, scale, bg=None , full_w = False):
        if bg:
            if full_w:
                full_w = img.width()
            else:
                full_w = len(text)*8*scale+4
            img.draw_rectangle(x-2,y-2, full_w, 16*scale, fill=True, color=bg)
        img = img.draw_string(x, y, text, color=color,scale=scale)
        return img

    def del_all_images():
        os.chdir("/sd")
        images_dir = "cap_images"
        if images_dir in os.listdir():
            os.chdir(images_dir)
            types = os.listdir()
            for t in types:
                os.chdir(t)
                files = os.listdir()
                for f in files:
                    os.remove(f)
                os.chdir("..")
                os.rmdir(t)
            os.chdir("..")
            os.rmdir(images_dir)

    # del_all_images()
    os.chdir("/sd")
    dirs = os.listdir()
    images_dir = "cap_images"
    last_dir = 0
    for d in dirs:
        if d.startswith(images_dir):
            if len(d) > 11:
                n = int(d[11:])
                if n > last_dir:
                    last_dir = n
    images_dir = "{}_{}".format(images_dir, last_dir+1)
    print("save to ", images_dir)
    if images_dir in os.listdir():
        img = image.Image()
        img = draw_string(img, 2, 200, "please del cap_images dir", color=lcd.WHITE,scale=1, bg=lcd.RED)
        lcd.display(img)
        sys.exit(1)
    os.mkdir(images_dir)
    last_cap_time = 0
    last_btn_status = 1
    save_dir = 0
    save_count = 0
    os.mkdir("{}/{}".format(images_dir, save_dir))
    while(True):
        img0 = sensor.snapshot()
        if set_windowing:
            img = image.Image()
            img = img.draw_image(img0, (img.width() - set_windowing[0])//2, img.height() - set_windowing[1])
        else:
            img = img0.copy()
        # img = img.resize(320, 240)
        if key.value() == 0:
            time.sleep_ms(30)
            if key.value() == 0 and (last_btn_status == 1) and (time.ticks_ms() - last_cap_time > 500):
                last_btn_status = 0
                last_cap_time = time.ticks_ms()
            else:
                if time.ticks_ms() - last_cap_time > 5000:
                    img = draw_string(img, 2, 200, "release to change type", color=lcd.WHITE,scale=1, bg=lcd.RED)
                else:
                    img = draw_string(img, 2, 200, "release to capture", color=lcd.WHITE,scale=1, bg=lcd.RED)
                    if time.ticks_ms() - last_cap_time > 2000:
                        img = draw_string(img, 2, 160, "keep push to change type", color=lcd.WHITE,scale=1, bg=lcd.RED)
        else:
            time.sleep_ms(30)
            if key.value() == 1 and (last_btn_status == 0):
                if time.ticks_ms() - last_cap_time > 5000:
                    img = draw_string(img, 2, 200, "change object type", color=lcd.WHITE,scale=1, bg=lcd.RED)
                    lcd.display(img)
                    time.sleep_ms(1000)
                    save_dir += 1
                    save_count = 0
                    dir_name = "{}/{}".format(images_dir, save_dir)
                    os.mkdir(dir_name)
                else:
                    draw_string(img, 2, 200, "capture image {}".format(save_count), color=lcd.WHITE,scale=1, bg=lcd.RED)
                    lcd.display(img)
                    f_name = "{}/{}/{}.jpg".format(images_dir, save_dir, save_count)
                    img0.save(f_name, quality=95)
                    save_count += 1
                last_btn_status = 1
        img = draw_string(img, 2, 0, "will save to {}/{}/{}.jpg".format(images_dir, save_dir, save_count), color=lcd.WHITE,scale=1, bg=lcd.RED, full_w=True)
        lcd.display(img)
        del img
        del img0


def main():
    try:
        capture_main(key)
    except Exception as e:
        print("error:", e)
        import uio
        s = uio.StringIO()
        sys.print_exception(e, s)
        s = s.getvalue()
        img = image.Image()
        img.draw_string(0, 0, s)
        lcd.display(img)
main()

上传图片到onenet

    上传图片到onenet可以通过http的方式进行上传到onenet。

picture 8

图19 onenet方法图

例子如下:

url = "https://iot-api.heclouds.com/device/file-upload"

payload = {'device_name': '',
'product_id': ''}
files=[
  ('file',('logo1.jpg',open('tmp.jpg','rb'),'你的类型))
]
headers = {
  'Authorization': ''
}

response = requests.request("POST", url, headers=headers, data=payload, files=files)

print(response.text)

picture 9

图20 上传效果图

项目源码

安全帽模型下载链接如下:http://download.eeworld.com.cn/detail/wuboy/630433
esp32红外接收端下载链接如下:http://download.eeworld.com.cn/detail/wuboy/630432
小车底板下载链接如下:http://download.eeworld.com.cn/detail/wuboy/630431
esp32控制端下载链接如下:http://download.eeworld.com.cn/detail/wuboy/630430

作品功能演示视频

视频链接:https://www.bilibili.com/video/BV1m64y1J7q9/?spm_id_from=333.999.0.0&vd_source=b1fff0f773136d7d05331087929c7739

项目总结

    整个项目将得捷购买的材料全部使用上了,锻炼了自己换板子,以及调试能力,
最后实现了功能,本项目利用视觉检测人是否佩戴安全帽,如果未佩戴安全帽将记录下来,上传到onenet云平台,通过TCP协议图传到电脑实时图像,装置利用麦克拉姆轮便于移动,控制通过esp_now协议控制,低延迟控制。最后十分感谢得捷官方,得捷的工作人员。

帖子分享:http://bbs.eeworld.com.cn/thread-1266467-1-1.html
帖子分享:http://bbs.eeworld.com.cn/thread-1266054-1-1.html

其他