×

光摄影机器人开源

消耗积分:0 | 格式:zip | 大小:0.00 MB | 2023-07-03

麻酱

分享资料个

描述

你知道你可以用长曝光相机制作光绘吗?光绘是一种摄影技术,其中通过移动手持光源同时拍摄长时间曝光照片进行曝光。

 

这是完成的漫游车的照片。

 
pYYBAGOYQ2KAL9g0AAGyNFmXHiM389.jpg
 

构建机箱

为了创建光摄影,我们将使用这个可编程的 8x8 LED 矩阵。我们之前在LED Hat项目中使用过灵活版本。社区中的大多数人通常使用 Arduino 来控制这些矩阵,但我们决定使用 Raspberry Pi,以便我们可以为软件存储更多图像数据并轻松从 Internet 下载新图像。

 
 
 
 
pYYBAGOYQ2mAb-OzAAHZdPKrTJc022.jpg
 
1 / 2
 

机器人底盘由几个不同的 3D 打印部件组成。不幸的是,零件的数量(以及这些零件的公差)太高了,所以我们决定不制定 MDF 版本的计划。如果您想自己制作并且没有 3D 打印机,您通常可以在公共图书馆/学校找到一台或使用在线3D 打印服务

底盘有一个主底座,我们可以在其中连接所有零件和支架。从底座开始,我们在后面插入了一个小型便携式手机电池,并用一些 2.5 毫米的螺栓和螺母将我们的 Raspberry Pi 连接到前面。顶部连接了一个小支架,以便我们可以在需要时轻松移除 Raspberry Pi。

 
 
 
 
poYBAGOYQ2yAYWpKAAFLr8WWebA178.jpg
 
1 / 3
 

车轮由 2 个 NEMA 17 200 rev 步进电机驱动。使用了步进电机,以便我们可以做出精确的动作来绘制光绘图像。我们在这个项目中使用了两个步进电机分线板,而不是我们用于自动气枪炮塔的步进电机 HAT,因为我们发现它们在并行移动两个电机时抖动较小。两块板都用一个小的 3D 打印夹子和一些 2.5 毫米的螺栓连接到火星车的前部。

 
 
 
 
poYBAGOYQ3OAQ2QGAAFU2HS_ics605.jpg
 
1 / 2
 

四个母跳线延长线焊接到每个步进电机。在另外两个支架的帮助下,它们安装在底盘下方。一些 3 毫米螺钉用于连接电机。由于我们的螺钉稍长,我们在螺钉头下方添加了几个机器人作为垫片。电线在 Raspberry Pi 下方布线并直接连接到分线板上。可以在此处找到这些分线板的接线指南。

 
 
 
 
poYBAGOYQ3WAd7MwAAGftrmKsBk660.jpg
 
1 / 3
 

为了让步进电机与我们的电池一起工作,我们需要使用升压适配器将电压从 5v 放大到 12v。将两根电线焊接到放大器的接地/热销上,然后连接到步进电机分线板上。

 
pYYBAGOYQ3mAK9nOAAGd8lI7XiA602.jpg
 

接下来我们安装了 3D 打印的轮子。每个轮子都连接到步进电机的轴上,并用一个小的 3mm 固定螺钉固定到位。在喷漆过程中,将几条小橡皮筋固定在车轮上以增加抓地力。

 
 
 
 
poYBAGOYQ3-AB6iWAAFyp4FGB_o247.jpg
 
1 / 2
 

升压适配器和 Raspberry Pi 已插入 5v 电池。我们将 LED 矩阵与 3D 打印支架连接,并将信号线插入 Raspberry Pi 上的引脚 18。使用烙铁将小型 USB 电缆连接到 LED 矩阵上的 +/- 端子。这个 USB 也插入了 5v 电池。

 
 
 
 
poYBAGOYQ4OAdgeSAAIgu_NlNus303.jpg
 
1 / 3
 

运行代码

确保安装以下依赖项:

Pillow
RPi.GPIO
numpy

我们还使用了一个自定义的 Raspberry Pi Neopixel 库,在这里可以找到:https ://github.com/jgarff/rpi_ws281x

按照 Raspberry Pi 安装说明进行操作。

在 light_rover.py 的底部,我们为所有组件配置了引脚。确保这些引脚正确。

stepper1 = Stepper(2, 3, 4, 17) 
stepper2 = Stepper(27, 22, 10, 9)
led_matrix = create_strip(64, led_pin=18)

绘图向量

矢量图像用于表示计算机图形中的图像。它们由具有大小和角度的几条线组成。您可以通过对代码进行一些修改来让您的轻型机器人绘制矢量图形。

确保以下部分在末尾被注释掉light_rover.py:

#if imageFile:         
#    rover.paint_image(imageFile)     
#else:         
#    print "No image file provided!" exit(1)

取消注释以下行:

rover.paint_vector(dog, single_value_affects_pixels=[27, 28, 35, 36])

vector_drawings.py 文件中选择一个向量并在函数中更改该值paint_vector

要运行,请确保您在项目目录中并执行:

sudo python light_rover.py

绘图图像

您还可以使用 LED 矩阵逐像素绘制图像。小图像(~100px x 100px)通常效果最好。确保以下部分在末尾被注释掉light_rover.py:

# rover.paint_vector(dog, single_value_affects_pixels=[27, 28, 35, 36])

取消注释以下行:

if imageFile:         
    rover.paint_image(imageFile)     
else:         
    print "No image file provided!" exit(1)

要运行,请确保您在项目目录中并执行:

sudo python light_rover.py images/sb.png

它是如何工作的?

连接到漫游车车轮上的两个步进电机可以进行微调转弯,在相机快门打开时将漫游车移动到指定的路径上。矢量图像是通过将漫游车发送任意距离,然后转动任意角度以排队等待下一次运动来创建的。使用这种技术,我们能够绘制几个不同的直线图像。

 
poYBAGOYQ4iAQWxuAAHFrS8eGWA125.jpg
此图像说明了将用于绘制矢量框的运动
 

如果要创建自己的矢量绘图,请在以下位置添加一个新的矢量数组:

vector_drawings.py

如:

rectangle = [LightVector(1000, 90, [[0, 255, 0]]),
            LightVector(800, 90, [[0, 255, 255]]),
            LightVector(1000, 90, [[0, 255, 0]]),
            LightVector(800, 90, [[0, 255, 255]])]

LightVector 采用以下参数:

def __init__(self, steps, angle, pixel_data):
       """
       :param steps: number of steps to move
       :param angle: angle to turn after the movement
       :param pixel_data: a 2d array of pixel data [[r, g, b]...n] where n is the number of pixels in the matrix
       """

图像的绘制方式略有不同。我们没有将流动站移动到一系列矢量上,而是分割出图像的 8x8 部分,并在流动站从图像的右上角逐行移动到左下角时,在 LED 矩阵上闪烁这些像素。每次闪光后,流动站的移动刚好足以排列图像中的下一组像素。

 
pYYBAGOYQ5CAXrPAAAHKMrfl2mg560.jpg
这张图片说明了用于复制光绘图片的动作
 

通常,小于 120x120 像素的图像最适合光绘。我们的程序会自动为您分割图像。

拍照

要拍照,请确保将相机放置在足够高的位置以捕捉整个图像。关闭所有灯并确保您的相机处于灯泡定时器模式。

在某些相机上,您必须按住快门按钮才能捕捉图像,但如果可以的话,最好设置一个自动定时器。

我们的相机在低 ISO 设置下使用 5 分钟曝光,镜头光圈降至 f/10。我们发现这样可以产生最好的光绘图像,而不会让房间中的一些环境光泄漏。

 
poYBAGOYQ5SAS1nZAAJbCzxkKas654.jpg
 

测试一下!

现在您已经构建了您的漫游车,继续尝试吧!如果您制作了任何有趣的新图片,请务必将它们发布在这里,以便我们查看。

 


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

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