×

太阳能喂鸟器相机开源分享

消耗积分:0 | 格式:zip | 大小:0.07 MB | 2022-10-27

分享资料个

描述

圣诞节,我女儿收到了她祖父的“建立你自己的喂鸟器”。我认为制作太阳能喂鸟器相机会是一个有趣的项目,这样她就可以看到喂鸟器中的鸟儿!我以前在这里看到过一些图像识别喂鸟器项目,但它们带有相当大的 Raspberry Pi SBC 和硬接线供电。我想我会试一试带有低功率 MCU 的太阳能版本。随着我对 Edge Impulse 越来越满意,我想我也可以添加一些图像识别功能!像往常一样,我遇到了一些小问题,但总的来说,我对项目的结果感到满意。

该项目从我 5 年的建筑开始,绘制和装饰鸟屋。你可以在上面的照片中看到她的成品。一旦她的杰作完成,我就接手了。

我有一块 1W 的太阳能电池板,还有太阳能充电器、Powerboost 和一块 2500 mAh 的 3.7V 电池。我也有一个 ESP32 摄像头。这是一个相当便宜的微控制器(USB 适配器约 10 美元),但用于图像识别模型的内存不多(约 520 kB),但是,它确实有 Wifi 与 ESP32-S!

因为我要把喂鸟器放在离我家很远的地方,所以我想我需要一个外部 WiFi 天线来为 MCU 增加 WiFi 范围。ESP-32 Cam 已经有一个 uFL 连接器,所以它只是即插即用,对吧?好吧,没那么多……原来天线有 2 种设置:默认的板载天线(带 0 欧姆电阻)和外部天线。

poYBAGNYrZCAehT4AACYAc3aWUg221.png
ESP-32 CAM 的 2 个设置。这是真的很小。
 

Random Nerd Tutorials 有一个很棒的教程,介绍了我遵循的如何设置外部天线。使用我的基本设备(带标准尖端的烙铁,无放大倍数),卸下电阻器并放入外部天线的两个焊盘之间的焊桥是一件很痛苦的事情。上图是放大的,那些垫子很小。但是我搞定了!我建议你为你的烙铁和一个放大镜准备一个很好的尖端来做到这一点。我发现大多数威廉希尔官方网站 板都将板载天线作为默认设置,因此如果您打算使用外部天线,则必须这样做。

一旦我在 MCU 上配置和测试了外部天线,我在喂鸟器的侧面钻了一个孔,将天线电缆穿过食物储存区,我将把相机放在那里。我使用我找到的 3D 打印外壳安装了相机(并且已经在我当地的图书馆打印了!)和双面胶带安装到喂鸟器的塑料外壳之一。然后我将太阳能电池板安装在朝南的喂鸟器的屋顶上(因为我在北半球)。

poYBAGNYrZeAQDE-ABFQmcRhvEE929.jpg
太阳能电池板安装在屋顶上。你可以看到右边的天线
 

我找到一个旧塑料容器,将太阳能充电器、Power boost 和电池放入一个整洁的包装中。我在侧面切了一些孔,以便将太阳能电池板的连接器和 USB 电缆连接到喂鸟器中的 ESP-32 凸轮。完成设置后,我使用一些双面胶带将其安装到喂鸟器的底部。

pYYBAGNYrZyARzHIAAjnnmeypZA089.jpg
我使用了一个旧塑料容器作为电池、Power Boost 和太阳能充电器的外壳。
 

然后是时候开始编码了!我从 Arduino IDE 中的 ESP-32 库附带的 CameraWebServer 示例开始。我花了一些时间来了解它是如何工作的,然后我对其进行了精简以从网络服务器中删除许多功能。基本上,我希望能够看到视频的实时流,使用 Edge Impulse 模型对该视频进行推理,然后将任何鸟类照片保存到 ESP32 Cam 上的 SD 卡中。

在大多数情况下,编码进行得很好。了解 app_httpd.cpp 文件与 Arduino 草图的关系有点棘手,但你花在其中的时间越多,它就越有意义。我也花了一点时间才知道网络服务器的网页是一个编码数组。我使用Cyber​​ Chef将数组解码为 html,进行更改,然后再次编码回十六进制格式。它让我慢了一点,但能够弄清楚并继续前进。

poYBAGNYraCAA8gnAAH6yb1un7U725.png
我使用 Cyber​​ chef 对 webserver html 进行解码和编码
 

现在我已经启动并运行了网络服务器,是时候制作Edge Impulse模型了!我已经公开了我的项目,所以你可以在这里看到它我保持模型非常简单。我从 kaggle下载了一个鸟类数据集,并过滤了我所在地区常见的鸟类。我在 Edge Impulse 中创建了一个由鸟类而非鸟类组成的数据集,每个类别大约有 1400 张图像。由于 MCU 上的内存限制,我必须保持模型简单,因此必须使用二进制分类器。我保持颜色 (RGB) 的脉冲,但我确实将分辨率降低到 48x48 以保持模型更小。不理想,但我必须做出权衡。

pYYBAGNYraWAatCAAADekm_D0gw218.png
鸟类图像分类器脉冲
 

然后我训练了 50 个 epoch,结果还不错:

poYBAGNYraiAPfaJAACinvu7G_s288.png
训练鸟类分类器数据集的结果
 

为了快速将模型组合在一起,这些是我可以接受的结果。还要注意 RAM 和闪存大小。足够小,可以在 MCU 上运行。

然后我将模型部署到 Arduino 库。这是我真正欣赏 Arduino 生态系统的地方。Edge Impulse 可以轻松地以 Arduino 库的形式导入模型。您只需像添加任何库一样添加它,然后在 Arduino 草图中包含您的推理头文件。Edge Impulse 的 Louis Moreau 有一个很好的教程,介绍了如何将 Edge Impulse 模型集成到 ESP-32 Cam 中。我花了很多时间研究他的代码,看看它是如何工作的。

我的代码有点长,所以我不会在文章中放很多片段,但你可以在我的 github上看到它但一般流程是从 ESP-32 CAM 接收图像(也称为帧缓冲区)。该帧缓冲区被转换为 RGB888 格式,然后调整为 48 x 48 像素(边缘脉冲模型所期望的大小)。一旦调整大小,缓冲区就会被分类。Edge Impulse 分类代码实际上非常简单:

//Perform Edge Impulse classification
void classify()
{
  signal_t signal;
  signal.total_length = EI_CLASSIFIER_INPUT_WIDTH * EI_CLASSIFIER_INPUT_WIDTH;
  signal.get_data = &raw_feature_get_data;

  // Feed signal to the classifier
  EI_IMPULSE_ERROR res = run_classifier(&signal, &result, false /* debug */);

  // Returned error variable "res" while data object.array in "result"
  if (res != 0) {
    Serial.printf("Error....returning\n");
    return;
  }

  //bird found!
  if(result.classification[0].value > 0.8)
  {
    ei_printf("**** Bird found with %f probability! ****\n", result.classification[0].value);
    jpg_sent = true;
  }
}

如果找到一只鸟,则会设置一个标志,以便代码可以将图像保存到 SD 卡中。并且这个过程会重复!我让这段代码运行了 6 个小时,然后我让 MCU 进入深度睡眠状态,这样我就可以给电池充电一点。我认为 ESP32 Cam 的运行功率约为 1W(~ 310 mA @ 3.3V),而我拥有的太阳能电池板只有 1W(并且由于损失和不完美的太阳,它可能是输入的一半)所以我需要一些停机时间让电池充电. 下面是在 Arduino 草图的 loop() 方法中运行的深度睡眠代码。

//plan would be to wakeup during daylight, stay on for a certain period of time (6 hours), then go into deep sleep at night
//wakeTime is the amount of time that the camera is awake, if this time is exceeded by the run time, go to sleep
unsigned long wakeTime = 6 * ms_TO_S_FACTOR * s_TO_HOUR_FACTOR;
if(millis() > wakeTime) {
  Serial.print("Time in ms: ");
  Serial.print(String(millis()));
  Serial.println(". Going to sleep now");
  Serial.flush(); 
  esp_deep_sleep_start();
}

我遇到麻烦的下一个领域是,将照片存储到 SD 卡后如何查看它们?我看到的大多数教程只是从 ESP32 Cam 中取出 SD 卡并将其放入计算机中查看照片。每次我想看照片时,我都不想去喂鸟器并尝试取出 SD 卡。我认为必须有一种方法可以使用 Wifi 来查看网络服务器中的图像。下班后我花了两个晚上试图找出一种方法,用我微薄的编码技能来做这件事。我终于偶然发现了jameszah的 ESP32 SD 文件管理器。它很简单,只需几行就可以集成到我的代码中,而且效果很好。它让我在尝试创建自己的时候省去了很多麻烦,所以谢谢詹姆斯!

一旦我设置了网络服务器,我就可以从 SD 卡下载照片。成功!

poYBAGNYrauATTGTAAGgY5Qjprk963.png
我实现 jameszah 的 ESP32 SD 文件管理器的快照
 

当我第一次使用它时,我只是使用 millis() 来获取文件名的唯一 ID。后来我了解到您可以使用 NTP 服务器来获取当前时间,因此我可以将日期时间添加到我的文件中。更容易阅读和知道照片的拍摄时间!

相机在行动!

以下是相机拍摄的一些示例快照。非常令人兴奋!

pYYBAGNYra2ASMUEAAA9BwQLB9o941.jpg
你在看什么?
 
poYBAGNYrbCAZM_4AABFLzWWRt4302.jpg
为它奔跑(飞行?)
 
poYBAGNYrbSADyp5AAA6_wYUbnE403.jpg
“等等,那是照相机吗?”
 

总的来说,这是一个真正考验我的伟大项目。我喜欢它是一个独立的系统,它从太阳中获取能量并将其存储到电池中。这测试了我的系统工程技能,并迫使我考虑围绕一个已经建成的喂鸟器进行设计。它在资源相当有限的 MCU 上使用 Edge AI 模型对我进行了测试。如果我能再做一次,如果可以的话,我会使用更大的太阳能电池板(可能 1.5-2W)和更大的电池(3 Ah)。

总的来说工作量很大,但我对结果很满意。继续我的下一个项目!!!


声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

评论(0)
发评论

下载排行榜

全部0条评论

快来发表一下你的评论吧 !

'+ '

'+ '

'+ ''+ '
'+ ''+ ''+ '
'+ ''+ '' ); $.get('/article/vipdownload/aid/'+webid,function(data){ if(data.code ==5){ $(pop_this).attr('href',"/login/index.html"); return false } if(data.code == 2){ //跳转到VIP升级页面 window.location.href="//m.obk20.com/vip/index?aid=" + webid return false } //是会员 if (data.code > 0) { $('body').append(htmlSetNormalDownload); var getWidth=$("#poplayer").width(); $("#poplayer").css("margin-left","-"+getWidth/2+"px"); $('#tips').html(data.msg) $('.download_confirm').click(function(){ $('#dialog').remove(); }) } else { var down_url = $('#vipdownload').attr('data-url'); isBindAnalysisForm(pop_this, down_url, 1) } }); }); //是否开通VIP $.get('/article/vipdownload/aid/'+webid,function(data){ if(data.code == 2 || data.code ==5){ //跳转到VIP升级页面 $('#vipdownload>span').text("开通VIP 免费下载") return false }else{ // 待续费 if(data.code == 3) { vipExpiredInfo.ifVipExpired = true vipExpiredInfo.vipExpiredDate = data.data.endoftime } $('#vipdownload .icon-vip-tips').remove() $('#vipdownload>span').text("VIP免积分下载") } }); }).on("click",".download_cancel",function(){ $('#dialog').remove(); }) var setWeixinShare={};//定义默认的微信分享信息,页面如果要自定义分享,直接更改此变量即可 if(window.navigator.userAgent.toLowerCase().match(/MicroMessenger/i) == 'micromessenger'){ var d={ title:'太阳能喂鸟器相机开源分享',//标题 desc:$('[name=description]').attr("content"), //描述 imgUrl:'https://'+location.host+'/static/images/ele-logo.png',// 分享图标,默认是logo link:'',//链接 type:'',// 分享类型,music、video或link,不填默认为link dataUrl:'',//如果type是music或video,则要提供数据链接,默认为空 success:'', // 用户确认分享后执行的回调函数 cancel:''// 用户取消分享后执行的回调函数 } setWeixinShare=$.extend(d,setWeixinShare); $.ajax({ url:"//www.obk20.com/app/wechat/index.php?s=Home/ShareConfig/index", data:"share_url="+encodeURIComponent(location.href)+"&format=jsonp&domain=m", type:'get', dataType:'jsonp', success:function(res){ if(res.status!="successed"){ return false; } $.getScript('https://res.wx.qq.com/open/js/jweixin-1.0.0.js',function(result,status){ if(status!="success"){ return false; } var getWxCfg=res.data; wx.config({ //debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。 appId:getWxCfg.appId, // 必填,公众号的唯一标识 timestamp:getWxCfg.timestamp, // 必填,生成签名的时间戳 nonceStr:getWxCfg.nonceStr, // 必填,生成签名的随机串 signature:getWxCfg.signature,// 必填,签名,见附录1 jsApiList:['onMenuShareTimeline','onMenuShareAppMessage','onMenuShareQQ','onMenuShareWeibo','onMenuShareQZone'] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2 }); wx.ready(function(){ //获取“分享到朋友圈”按钮点击状态及自定义分享内容接口 wx.onMenuShareTimeline({ title: setWeixinShare.title, // 分享标题 link: setWeixinShare.link, // 分享链接 imgUrl: setWeixinShare.imgUrl, // 分享图标 success: function () { setWeixinShare.success; // 用户确认分享后执行的回调函数 }, cancel: function () { setWeixinShare.cancel; // 用户取消分享后执行的回调函数 } }); //获取“分享给朋友”按钮点击状态及自定义分享内容接口 wx.onMenuShareAppMessage({ title: setWeixinShare.title, // 分享标题 desc: setWeixinShare.desc, // 分享描述 link: setWeixinShare.link, // 分享链接 imgUrl: setWeixinShare.imgUrl, // 分享图标 type: setWeixinShare.type, // 分享类型,music、video或link,不填默认为link dataUrl: setWeixinShare.dataUrl, // 如果type是music或video,则要提供数据链接,默认为空 success: function () { setWeixinShare.success; // 用户确认分享后执行的回调函数 }, cancel: function () { setWeixinShare.cancel; // 用户取消分享后执行的回调函数 } }); //获取“分享到QQ”按钮点击状态及自定义分享内容接口 wx.onMenuShareQQ({ title: setWeixinShare.title, // 分享标题 desc: setWeixinShare.desc, // 分享描述 link: setWeixinShare.link, // 分享链接 imgUrl: setWeixinShare.imgUrl, // 分享图标 success: function () { setWeixinShare.success; // 用户确认分享后执行的回调函数 }, cancel: function () { setWeixinShare.cancel; // 用户取消分享后执行的回调函数 } }); //获取“分享到腾讯微博”按钮点击状态及自定义分享内容接口 wx.onMenuShareWeibo({ title: setWeixinShare.title, // 分享标题 desc: setWeixinShare.desc, // 分享描述 link: setWeixinShare.link, // 分享链接 imgUrl: setWeixinShare.imgUrl, // 分享图标 success: function () { setWeixinShare.success; // 用户确认分享后执行的回调函数 }, cancel: function () { setWeixinShare.cancel; // 用户取消分享后执行的回调函数 } }); //获取“分享到QQ空间”按钮点击状态及自定义分享内容接口 wx.onMenuShareQZone({ title: setWeixinShare.title, // 分享标题 desc: setWeixinShare.desc, // 分享描述 link: setWeixinShare.link, // 分享链接 imgUrl: setWeixinShare.imgUrl, // 分享图标 success: function () { setWeixinShare.success; // 用户确认分享后执行的回调函数 }, cancel: function () { setWeixinShare.cancel; // 用户取消分享后执行的回调函数 } }); }); }); } }); } function openX_ad(posterid, htmlid, width, height) { if ($(htmlid).length > 0) { var randomnumber = Math.random(); var now_url = encodeURIComponent(window.location.href); var ga = document.createElement('iframe'); ga.src = 'https://www1.elecfans.com/www/delivery/myafr.php?target=_blank&cb=' + randomnumber + '&zoneid=' + posterid+'&prefer='+now_url; ga.width = width; ga.height = height; ga.frameBorder = 0; ga.scrolling = 'no'; var s = $(htmlid).append(ga); } } openX_ad(828, '#berry-300', 300, 250);