×

自主无人机着陆开源分享

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

分享资料个

描述

在过去的几年里,我一直在StreamLogic开发工具,旨在让在边缘构建和部署计算机视觉变得更加容易。我最喜欢的目标平台之一是来自 tinyVision.ai的Vision FPGA SoM :

很小的吧?在中心,您可以看到来自 HiMax Imaging 的小型、低功耗且价格低廉的相机模块。我一直在寻找我可能针对此平台的新应用程序。

几个月前,在一位商业伙伴提到他们使用无人机的一个农业用例后,我退出了无人机。无人机是 tinyML 的一个很好的用例;它们需要大量传感器数据,并且对尺寸、重量和功率敏感。我对无人机几乎一无所知,但我整个周末都在尝试定位和研究开源无人机。在周末结束之前,我购买了一架无人机:Holybro 的QAV250 套件

这架无人机没有预先组装,并且是新手,几个月前,我拥有所有正确的零件并将其从地面上取下来。但是,让我们跳到故事的结尾。一旦我开始真正驾驶无人机,我就意识到 GPS 定位是多么的不准确。使用自动驾驶仪执行自主任务时,您可能会发现您的无人机远离计划的飞行路径。这在飞行和着陆中都非常危险。就在那时,我想出了这个项目的想法。

目标很简单:使用自动驾驶仪安全着陆无人机。这有两个部分:1)在预期的表面上着陆(在我的例子中是人行道)和 2)避免表面上的任何意外障碍物。为了实现这一点,我提出了一种计算机视觉算法来检测无人机降落时应该何时以及向哪个方向移动。

算法设计

我的解决方案在高层次上非常简单。向下的摄像头安装在无人机的底部。当无人机缓慢下降着陆时,它会捕捉图像进行分析。首先,将无人机正下方的平方米确定为着陆点。其次,还对八个方向(北、东北、东、东南、南、西南、西、西北)的附近站点进行评级。最后,如果附近任何一个站点的评分明显优于当前站点,则无人机的位置将沿相应方向调整。

这是从我的无人机拍摄的示例图像:

pYYBAGNYsDiAU3J7AABPOgdbiWc028.png
 

如果我们继续前进,中间的黑色方块就是当前的着陆点。它周围的标记方块是评估可能的方向变化的候选站点。显然,如果我们要降落在人行道上,我们想在这种情况下向东南移动。

嗯,这是高水平,但您可能仍然想知道 1)您如何知道一米的大小以及 2)您如何评价它?第一个很简单,无人机已经有一个高度传感器,所以你可以估计无人机的高度。鉴于高度、一些相机规格和一些代数,计算当前图像的每米像素数 (PPM) 非常简单。在这种情况下,它的计算结果为 PPM = 168 / H。

对于第二个问题,这就是计算机视觉的用武之地。我们需要能够对图像进行任意裁剪,并获得关于该图像是无障碍路面的可能性的定量分数。实际值在这里并不重要,只要我们可以比较两个分数来确定哪个更好。可以手工制作算法,但我选择训练卷积神经网络 (CNN) 来生成分数。

下一节将讨论我是如何训练 CNN 的,但让我们看一下上图的输出:

poYBAGNYsDqALTP4AAAmonmQKIM201.png
 

此图显示当前着陆点(标记为 C)和所有 8 个方向的候选点的得分。毫无疑问,东南部是希望的赢家。

机器学习

如上所述,我选择使用 CNN 为候选着陆点生成定量分数。具体来说,全卷积网络(FCN)。这些与其他 CNN 的不同之处在于它们不以密集或全局池化层结束。最后一个卷积层的激活图网络输出。正如您将看到的,这非常适合此应用程序。

训练 FCN 有不同的方法,但我选择先构建分类器 CNN,然后将其转换为 FCN。让我们先看看我是如何构建分类器的。

我想创建一个分类器,将小图像块分类为好或坏。但首先,我需要数据。自然地,我花了很多时间在互联网上搜索一些公共数据集。不幸的是,没有什么真正适合我的任务。我需要创建自己的数据集,但是如何创建?最后,我创建了自己的电池供电图像记录器

pYYBAGNYsEGAY0aKAAcrYEbxA2Q118.jpg
 

图像记录器每秒捕获图像并将它们写入 SD 卡。这使我能够在着陆期间从我的无人机收集图像,如下所示:

poYBAGNYsEOAelHeAAA7FiOofMI253.png
 

手头有许多图像,我创建了三个不同类别的补丁数据集:

  • 地面(非人行道)
  • 路面
  • 未知(人行道边缘/非人行道)

我最初收集了大约 2,000 个补丁。然后我花了很多时间尝试训练一个浅层网络(2 个卷积层),但没有取得多大成功。我尝试了许多数据增强和网络结构都无济于事。网络总是会在验证集上生成随机结果或做出恒定的选择 (:sigh:)。

除了获取更多数据外,别无他法。所以,我从我的图像中收集了大约 1, 800 个补丁。Bingo,大约有 4, 800 个补丁,训练表现良好,经过一些实验,我的网络达到了 88% 的准确率。这是最终的网络:

model = Sequential([
    layers.Input(shape=(tgt_width,tgt_height,3)),
    layers.Conv2D(10, 5, strides=2, padding='valid', activation='relu'),
    layers.Conv2D(3, 3, padding='valid'),
    layers.GlobalAveragePooling2D(),
])

这个模型只有 1, 033 个参数!

有关完整的训练过程,请参阅training.ipynb代码存储库中的 Jupyter 笔记本。

全卷积网络

有了地面补丁分类器,是时候将其转换为我们可以应用于整个图像的 FCN。查看上面的网络,您可以看到通过移除 GlobalAveragePooling2D 层,我们最终得到了一个仅包含卷积层的网络。它接受任何图像大小并生成比例大小的激活图。FCN 的结果实际上与以滑动窗口方式将补丁分类器应用于图像中的所有补丁相同。输出图中每个位置的激活值是输入图像中相应位置的补丁的分类器结果。

这真的有效吗?我们来看一下。这是示例图像的渲染和 FCN 的相应输出:

pYYBAGNYsEaALPseAAChIJZPiKo996.png
 

右图显示了路面类的 FCN 输出。将正确图像中的值与候选站点相加,可以得出该站点的分数。

这是呈现相同数据的另一种方法:

poYBAGNYsEmADdZoAAAwNmcSamM792.png
 

在这张图片中,我用一个红色正方形覆盖了原始图像,其透明度是路面类输出的倒数(即越有可能是路面,越透明)。查看目标着陆点,红色越多,得分越低。

创建 FCN 并将权重从训练的分类器转移到 FCN 的所有详细信息都在evaluation.ipynb代码存储库的 Python 笔记本中。

量化

我对网络进行了量化,以使用 8 位权重和 8 位激活来获得更好的运行时性能。您可以遵循 Tensorflow 文档中描述的标准程序,但我使用了我自己的方法,该方法具有更简单的量化模型。该过程在README代码存储库中进行了详细说明。

这是量化网络的输出示例:

poYBAGNYsE2AbbLWAABkO6docVE367.png
 

这与原始网络的输出几乎没有区别:

poYBAGNYsE-ATmH1AABj-ALKFpE329.png
 

硬件构建

我已经有了一个带有 Vision FPGA SoM 的硬件堆栈和一个来自Battery-powered Image Logger项目的微控制器。不幸的是,结果证明微控制器太小了。计算 FCN 中每一层的输入/输出激活图的大小,我们发现我们需要 48KB 的缓冲区空间。幸运的是,还有另一个符合要求的 Feather 板。

Feather M4 Express 不仅速度更快,而且拥有高达 192KB 的 RAM!该项目不需要 SD 卡,因此我们的构建只是堆叠在 Feather M4 Express 上的 Vision FPGA SoM 板。

pYYBAGNYsFOAZshdAAbjEjVi0N0570.jpg
 

体积小、重量轻、电池供电!

执行

从框图中您可以看到 FPGA 被用作相机模块和微控制器之间的桥梁。

poYBAGNYsFWAVJwGAABhr02-OG8327.png
 

FPGA 将捕获的图像以图像传感器的原始格式(即拜耳滤波器图像)传递给微控制器。FPGA 的预构建编程文件在代码库中提供。

这让微控制器的任务是:

  • 将图像从 Bayer 转换为 RGB 格式
  • 将图像下采样到 80x80
  • 执行 FCN 推理
  • 对目标站点区域求和

所有这些任务的源代码都包含在sketch代码存储库的文件夹中。我将在这里对代码做一些评论。

完整的原始图像超过 100KB,虽然这个 MCU 有 192KB,但我不想不必要地浪费空间。下采样的图像只需要 20KB。您将在从 FPGA 读取图像的循环中看到,它在读取时执行拜耳转换和下采样,一次一个块,因此我们只需要为下采样图像提供足够的空间。

这个网络没有使用 CNN 框架,而是非常简单,以至于我实现了自己的卷积和激活函数。这些在gndnet.cpp文件中定义。如果您有兴趣了解有关卷积如何工作的更多信息,它的编写方式非常简单。我并不太担心性能,但这些可能会被 ARM神经网络库中的函数取代以获得更好的性能。

结果

着陆算法与无人机飞行控制器的集成超出了此概念验证的范围。目标是设计和训练算法,证明其有效性并获得一些性能数据。

从质量上看,该算法看起来不错。它已在用于培训的站点以及未用于培训的站点的测试图像上进行了评估。结果非常令人鼓舞。当然,要投入生产,人们可能希望对来自多个站点的数据进行训练。

在性能方面,从图像捕获到分数计算的整个过程大约是 525 毫秒。这意味着略低于 2 FPS。这可能会起作用,因为无人机确实以相当慢的速度下降。但是,这很容易得到改善。例如,如果将 Bayer 转换和下采样功能移至 FPGA,则时间会缩短到 200 毫秒多一点。那将超过 4 FPS,这绝对足够了。


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

评论(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);