CSK.Blog--个人原创Weblog

已完成服务器迁移,新的服务器位于美国

由于不再国内,可能网络性能上会有差异。不过目前看来似乎比之前国内的服务器还快...

不会或许有不稳定的地方

本周末进行网站迁移

近来越来越多的悲观消息,看来还是要早做准备了。虽然这个网站近来我也很少有时间更新,不过毕竟已经维护了5年多,不舍得就这样消失了。
今天服务商有短暂的无辜断网,我不想考证是什么原因了。这次的迁移将是彻底的,今后不会再被这类匪夷所思的事件干扰。当然,也有可能在国内永远的无法访问。
以前一直担心转移后的速度问题,目前看来还是可以接受的。
特此通告一声,我网站会在周末迁移到一个相对安全的地方。如果需要下载资料麻烦等待一会儿。

说说我从去年9月份开始的AVR单片机学习和使用

原本是想把这篇文章的内容作为一个前言背景部分,不过发现写了不少,就干脆单独提出来一片。好给后续的文章做铺垫。

我会在明后几天发布一篇“ArduinoLite,高效且易用的AVR单片机运行库”的文章,来把这半年来我的一些工作成果推荐和分享给大家。

我从去年(2009)9月起开始接触AVR单片机。其实也就是利用业余时间基于他制作一些电子制作,当然也有将来会比较大规模的项目。或许大家也从之前几篇凑数性质的文章中看到了。

在正式开始本文前,我打算先介绍下一些背景知识,好让对这块领域暂时陌生的朋友有个了解。

AVR单片机是Atmel(atmel.com)公司推出的一款RISC指令集的8位单片机。因为它的高性价比(片上集成PWM, ADC, I2C, SPI,etc)和高运算效率(16MIPS@16Mhz),所以目前被广泛应用。
同时,另一个使得AVR受欢迎的原因是他的开发工具链很丰富。所谓丰富就是由商业的收费编译器(如ICC),也有开源免费的编译器gcc-avr。这使得对AVR的开发几乎是0成本的,并且gcc的强大也吸引着不少人。
我之前对单片机的了解仅是大二学校的一门电子科技创新课上接触过89S51单片机,从去年9月份开始打算利用AVR单片机来做些事情。于是就和其他人一样,我也开始去学习使用这个系列的芯片。这里就介绍下我再这个过程中的一些体会和心得,如果有朋友想开始涉足这个领域,不妨听我说说。顺便也是给后面的文章做铺垫...
首先我并没有去买市面上各类单片机的教程,而只是下载了AVR芯片的datasheet。毕竟一切的细节和资料都是来自datasheet的。不过如果光看着datasheet开始AVR编程实在有点痛苦,需要有一个容易上手的途径。
之前我已经从Makezine.org上听说有一个意大利的开源硬件项目Arduino(arduino.cc)是基于AVR芯片的(Atmega168),这个项目的特点是他定义了一个基于Atmega168的标准PCB版以及标准的引脚接口,并且提供了一套运行库和IDE环境方便开发者对AVR编程。

Arduino Board

图:标准Arduino板(图片来自arduino.cc首页)

 

虽然在很多专门从事单片机开发的人眼里看来这个东西基本和玩具无异(其实的确有自身的问题),但是作为一个没有接触过AVR芯片甚至完全没有单片机开发经验的人来说,Arduino大大降低了入门门槛,同时因为全世界有许多人都贡献了基于Arduino的一些应用,比如简单的如驱动马达转动到复杂的去实现一个机器人、基于Arduino的webserver等。丰富的外围代码和各类扩展硬件使得即使一个完全没有硬件设计背景的人也可以轻松的实现很复杂的应用。关于Arduino我就介绍到此,如果有兴趣可以去google了解下。

我当时就直接在taobao上购买了一块Arduino版正式开始AVR的使用。目标是一方面可以立刻基于Arduino带来的易用性来实现出我的想法,同时开始看Arduino运行库的代码,作为我对AVR芯片学习的开始。

Arduino的确是相当易用,首先它提供的IDE已经自包含了几乎所有单片机开发的工具链(avr-gcc, 烧录程序, 串口调试),同时也包含了不少的例子程序可以让使用者立刻解决一些现实问题,比如要实现控制LED很柔和的明暗渐变交替,有一个fading的现成例子就可以实现,如果略加改动,开发者还可以实现如通过PC来控制LED亮度这样的实验。

下面举个具体点的例子:

上图中可以看到PCB版上上下各有一排插口,并标有着数字。实际上这些插口是直接和AVR的芯片引脚连接的。那么,如果我将一个LED连接在上面标号为#9的针脚,希望做到的效果是让AVR控制这个LED从0%的亮度逐渐变成100%亮度,用Arduino我只要写入下的代码即可轻松实现。

pinMode(9, OUTPUT);

unsigned char led_brightness = 0;

for (led_brightness=0; led_brightness = 255; led_brightness++)

{

   analogWrite(9, led_brightness);

}

代码相信不需要我解释大家也能知道原理了,analogWrite就是设置一个引脚的模拟值(可以认为是#9的电压,自然数值越大LED就越亮),不过这里其实并不是真的设置了模拟的电压量,而是PWM占空比(wiki:Pulse-width modulation

这个analogWrite就是由Arduino所提供的函数。他已经将许多冗长的硬件寄存器配置操作包装了起来,提供给使用者易用的接口。这的确给初学者减轻了不少负担。如果不使用Arduino,仅依靠gcc提供的标准函数来实现这个看似简单的功能,则需要编写如下的代码。

//Set PWM mode: fast mode for timer 0
sbi(TCCR0A, WGM01);
sbi(TCCR0A, WGM00);

//set timer 0 prescale factor to 64
sbi(TCCR0B, CS01);
sbi(TCCR0B, CS00);

sbi(DDRB, PB1);

sbi(TCCR, COM11);

 

unsigned char led_brightness = 0;

for (led_brightness=0; led_brightness = 255; led_brightness++)

{

   OCR01 = led_brightness;

}

上面代码其实也不长,但一堆寄存器的设置相信还是会让许多第一次接触AVR的人头晕的。并且相信大部分人并不可能记住要实现这样一个功能需要设置哪些寄存器,更多的时候还是会去参考datasheet或者从已有的代码里复制过来。其实这些都是无谓的劳动,也容易增加出错的概率。

不过,作为对AVR的学习,还是必须了解其中底层的运行机制的。同时Arduino也有一些缺陷所以并不是所有情况下都能使用。所以之后我就边使用Arduino来做事情,同时对他的运行库代码作了许多修改并且进行大规模优化。这个将在后续的文章中介绍了。半年过去,我觉得至少在AVR这个领域,我应该算是精通了。

 

接下来我来分析下Arduino的一些限制和缺陷,也为衔接后续文章:-P

正如前面所说的,在很多专门从事单片机开发的人眼里,Arduino就像玩具一样。那么为何那么易用的东西会被如此看待?

当然一方面,正是因为宜用所以有人认为这是玩具,这种逻辑很多见:C#很易用,有人认为是玩具,Mac很易用,有人认识为玩具, etc... 当然这是不理性的想法,对于完全没有调查研究就下这样的结论自然是没有道理的。

撇开这个,我觉得主要还是因为Arduino自身的限制和缺陷造成的。

首先,Arduino这个概念其实有2个含义

a. Arduino PCB电路板

b. Arduino IDE,运行库

其实很多时候提到Arduino是2个含义兼有的,比如看到Arduino的标准电路板,自然就是a含义,而开发起来所谓的Arduino就是运行库。

那么这里先看Arduino作为一块硬件电路时候的情况:我想成熟产品中总不止于把一块Arduino板放进去吧...自然在这个含义下,那个就是“玩具”。这块版的用途很明显,就是给业余爱好者使用的。

那么作为运行库(软件)的情况下,Arduino也面临一些问题:

1). 仅支持Atmega8/Atmega168等/Atmega1280

2). 程序体积大,代码效率低

3). 对外围硬件存在一定假设,不能自由运用在任何外围电路中

1)和3)其实可以认为是一个问题,就是Arduino的运行库是专门为Arduino板或者兼容硬件设计的。Arduino板不可能用于产品,那么自然这个Arduino运行库也是没法用的(至少不加修改是不可能了)。同时还有别的问题,比如AVR的芯片种类很多,有不少是基于特定应用的。例如要采用AVR开发锂电池充电器,对于这样的需求,Attiny系列的芯片可能更加合适(价格便宜,充电器逻辑简单,过于强大的芯片资源也是浪费)。Arduino仅支持那些“中高端”部分的AVR芯片显然是不实用的。对于3),一个具体的例子是Arduino板上AVR普遍工作在16Mhz和8Mhz,所以Arduino运行库的代码实际上也仅支持16Mhz和8Mhz。但实际应用中可能要求AVR运行在很低的主频(1Mhz或者更低)。

其实上面这2个问题通过简单的修改Arduino运行库代码就可以解决了,应该说相对arduino库带来的好处相比还是很划得来的。当时关键就出在2)上。

程序体积大和代码效率低是关联的。再举一个很实际的例子:前面我提到的让LED渐变显示的功能,如果用Arduino,编译出来的程序需要消耗1-2Kb。如果用x86上的情况来看,1-2kb非常小。但其实情况是单片机都只有8KB左右的存储空间。有些型号,比如前面提过的Attiny系列只有2Kb。仅仅是实现一个LED渐变,或者说PWM输出,就消耗了Attiny的全部程序空间,这个肯定是不合算也没有人愿意的。就算是Arduino板采用的16KAVR,也意味着消耗了1/8的空间。这几乎都没做别的事情。做一个对比,用前面我给出的直接调用标准库的实现方式,虽然代码看上去啰嗦,但是编译产生的程序只有100字节左右。100:2048,这个差距实在是太大了。如此大的程序体积,自然效率就会低很多。

这几乎也是为什么Arduino不会被专门从事开发的人接受的根本原因了。

其实看过Arduino实现就知道出现2)的根本原因了:a.采用C++,b.过分的注重灵活性。

关于a.其实也不是核心问题,但是C++在默认情况下的确会产生加大的代码。而b.这里就非常明显。

这里再举前面的例子,我们来研究下analogWrite这个函数:

void analogWrite(uint8_t pin, uint8_t val)
{
 pinMode(pin, OUTPUT);
 if (digitalPinToTimer(pin) == TIMER1A) {
  // connect pwm to pin on timer 1, channel A
  sbi(TCCR1A, COM1A1);
  // set pwm duty
  OCR1A = val;
 } else if (digitalPinToTimer(pin) == TIMER1B) {
  // connect pwm to pin on timer 1, channel B
  sbi(TCCR1A, COM1B1);
  // set pwm duty
  OCR1B = val;

 } else if (digitalPinToTimer(pin) == TIMER0A) {
  if (val == 0) {
   digitalWrite(pin, LOW);
  } else {
   // connect pwm to pin on timer 0, channel A
   sbi(TCCR0A, COM0A1);
   // set pwm duty
   OCR0A = val;     
  }
 } else if (digitalPinToTimer(pin) == TIMER0B) {
  if (val == 0) {
   digitalWrite(pin, LOW);
  } else {
   // connect pwm to pin on timer 0, channel B
   sbi(TCCR0A, COM0B1);
   // set pwm duty
   OCR0B = val;
  }
 } else if (digitalPinToTimer(pin) == TIMER2A) {
  // connect pwm to pin on timer 2, channel A
  sbi(TCCR2A, COM2A1);
  // set pwm duty
  OCR2A = val; 
 } else if (digitalPinToTimer(pin) == TIMER2B) {
  // connect pwm to pin on timer 2, channel B
  sbi(TCCR2A, COM2B1);
  // set pwm duty
  OCR2B = val;
 } else if (val < 128)
  digitalWrite(pin, LOW);
 else
  digitalWrite(pin, HIGH);
}

实在太长了,我把其中的注释已经删去。可以看出arduino在实现上很讲究灵活性(这算是一种比较客气的说法)。那么,真的需要这么多代码吗?看看不用arduino时对他的等效实现: OCR01 = led_brightness;

仅仅是一行简单的赋值语句,即使是编译成了AVR机器码,也只有1条outb指令(2字节),而前者会产生多少代码就不用具体给出了。而这个的运行效率差别有多可怕也可以看出来了。

那么,这么多代码可否精简,但又能保持和以前完全一样的功能呢?

这个几乎是不可能的,如果仔细分析代码,可以看出那么多代码都只是在做一件事情:将Arduino采用的数字引脚的标号通过查表的手段映射回对应的AVR引脚。而只要这个函数允许接受任何Arduino引脚作为参数,它就必须有这样的动作存在。

但是实际情况中,PCB已经是做好的,比如LED和AVR的连接时固定不变的,自然也没有必要每次运行中都去查表。如果这个函数所操作的引脚是固定的话,自然就可以去除那些查表的操作了。

对Arduino运行库的改进:

针对上面这些分析,如果把Arduino运行库存在的这些问题都加以克服,并且仍旧继承Arduino易用的特点。那样相信会有很多人乐于使用它。从这里开始,Arduino所表示的含义将是他的运行库。硬件(Arduino板)自然是无法有什么改进的。

下面是我对arduino的改进,我将这个改进后的库称作ArduinoLite。

和Arduino运行库相比,ArduinoLite有如下增强

1. 增加了Attiny2313和Attiny26的支持

2. 支持1Mhz至20Mhz所有频率

3. 没有外部硬件假设,也就是说ArduinoLite可以用于任何支持的AVR芯片中,不需要对Arduino板兼容

4. 代码体积非常小,运行效率高

5. 新增加了一些工具函数

6. 全部改用C实现

具体的介绍和代码以及参考我将在后续的文章中给出,下面给一个直观的例子:

要在Attiny2313芯片上实现LED灯渐变点亮:假设LED连接在用ArduinoLite编号法则#9的引脚。

代码如下

#define LED_PIN 9

PIN_MODE(LED_PIN, OUTPUT);

PWM_ENABLE(LED_PIN);

unsigned char led_brightness = 0;

for (led_brightness=0; led_brightness = 255; led_brightness++)

{

   PWM_SET(LED_PIN, led_brightness);

}

 

这代码是否和用Arduino非常相似呢?编写者也不用去关心具体的寄存器配置状况。而且产生的代码是和之前直接去操作寄存器的版本完全一致的。像PIN_MODE() PWM_ENABLE() PWM_SET()最终均只产生一条指令。而且,和直接去操作寄存器相比,也有很大的优势:Attiny2313寄存器配置情况和Atmega168存在比较大的不同,采用ArduinoLite接口就完全不必考虑这种细节。自然也提高了代码的可移植性。

 

关于ArduinoLite的具体细节,请见我的后续文章:-)

贴个充数,不太成功的无线供电试验

这半年都没写过什么有技术含量的,实在对不起各位。而且形势也不容乐观,我也有点悲观。
这篇同样没有技术含量,是之前做过的一个不太成功到无线电能传输的试验。所谓不太成功就是我认为还没到应用价值。

目前做到的是1-2cm,最大5cm范围点亮LED发光,最大功率3V@16mA。

细节就不说了,实在感兴趣的可以单独问我,其实很多信息都在图片里。以后有空且有机会具体说说好了。

基于共振电感耦合,示波器上分别是发射和接收电感线圈的波形。频率调谐在12M。






人都是有惰性的,但是有时候还是要逼着去做点事情,否则恐怕什么都做不好了。

通告下

之前网站已经两次因为相关IDC机房被封而受到牵连,看形势今后可能更加不妙。所以在各位还能看到这里的时候先通知下,今后的某一天起如果我网站出现无法访问、404、403或者出现了不可能是我网站的内容时,别怀疑。那一定是因为我网站被关停了。 但愿这只是我自己杞人忧天吧,不过还是事先做好这个准备。

临时凑数一片

好久没有更新自己Blog。一方面是忙,另一方面是暂时没啥好说的。不过为了证明这个Blog没有废弃,提醒自己我还有个网站,所以就发一篇来凑数。

主要是最近做的一些事情拍的录像,各位有兴趣就看看哈。不多作解释。

Youtube国内不能访问,就放youku上了...




截获网页客户端的IPCAM视频用于OpenCV等程序

请点击标题进入正文下载相关附件

好久没有更新自己Blog了。长时间不更新这次又是一篇讲技术的,下次要平衡下了。

这个标题应该没有表达清楚,其实也蛮难靠标题说清的。

情况是这样的,最近从网上订购了如图所示的无线网络摄像机,为了避开广告嫌疑,我不给出任何具体介绍链接了。

 

他的功能还不错,可以将拍摄的画面通过Wifi无线或者100Mbps以太网发送出来供远程观看,同时自带了近360度自由度的X-Y轴云台供远程操作移动画面。不过在使用的时候发现了个麻烦:他只提供Web客户端进行视频浏览和控制。无法像传统摄像头那样通过VFW或者DirectShow捕捉它的视频信号,也没有提供专业网络摄像机提供的mms/rtsp视频协议供我自己写的程序或者一些视觉计算库如OpenCV使用。同时对云台的操作也只能在浏览器中进行,没有直接提供供使用者调用的编程接口。

 

因为这类摄像机基本都是小作坊工厂出产的,我在包装上连他的厂家都没找到是谁,所以问厂家索要开发包无望,且网上也没有解决办法,所以就花了一天时间实现了我要的需求。特此将思路和代码给出,希望对有同样需求的人有所帮助。要注意的是似乎生产这类Ipcam的厂家有很多,他们虽然生产出外形和功能都类似的产品,但内部的机制多少有些不同。所以这里给的方法可能不能完全适用于你的机器,但思路应该都一样。

 

为了给个直观印象方便我说明问题,这里给个这类IpCam的Web客户端的样子:

如图所示,对这类摄像机的一切操作/访问都是通过浏览器访问位于摄像机上的web服务器实现的。在具体介绍实现我的需求前,我先介绍这类摄像机的技术细节。

 

该类Ipcam的硬件和客户端

当收到这个摄像机不久我就把它拆了一遍,因此对他的硬件有了个大致的了解。这类摄像机一般采用ARM的解决方案,里边烧录了Linux OS作为固件。而Wifi很出我意料的直接使用了块usb接口的802.11b/g网卡,当然usb接口是直接用电线连接的。看来将来即使自己修改个Linux内核烧录进去也很容易搞定,同时我也怀疑他的摄像头也是usb接口连到Arm主板上的... 至于云台电机,他采用了国内某厂的一套小功率步进电机驱动。

硬件就是如此了,再说说他的通讯协议。通过sniffer他的数据包分析,我发现他所有的通讯都是基于HTTP协议的,包括视频信号和云台控制信号的传输。原先以为视频信号可能会用某些传统的协议,看来这条捷径走不通了。虽然基于HTTP传输视频信号,但也只是利用HTTP做了层wrapper,我没有时间去分析它本身的传输协议。所以也不打算通过逆向他的传输格式来直接编写客户端。

然后再分析它的Web客户端,也就上上图的那个画面中的网页。从前面协议分析已经看出,上图中的视频画面不太可能是用Mediaplayer或者FlashPlayer这类插件实现的,而是厂家自己编写的一个ActiveX控件(也就是说只能用于windows了)。同时又发现了他网页客户端中所有的操作,如云台的控制,也是直接调用这个ActiveX控件所提供的方法来实现的。

换句话说,对这个摄像机的所有访问和操作,厂家都已经完全封装在所提供的ActiveX控件当中了。那样其实也好办,我们可以尝试在自己的程序中调用该控件,这样云台的控制就解决了,或许该控件也提供了某个接口供我们直接获得视频数据,如getCurrentFrame()?

 

利用厂家自带的ActiveX控件实现摄像机云台控制和画面获取

通过分析该客户端网页代码,得知他使用的控件文件名为:DVM_IPCam2.ocx

接下来就是获得这个控件所提供的接口和调用方法了,下面是用Visual Studio的对象浏览器列出他提供的接口:

图上可以看出,所有对该摄像机的操作都已经集成在该控件当中了,当遗憾的是我除了PlayVideo(void)外并没有找到任何其他和获取视频数据有关的接口。看来要达到我们获得每一帧视频信息的目标还有点距离。但无论如何,先尝试将这个控件加入我们的程序调用再说。

从之前网页代码的分析看出,这个控件的使用并非容易,首先需要调用接口完成登陆操作。但好在Javescript都是被迫开源的...对于这个控件接口的使用完全都有代码可以参考。我第一步先将他的网页客户端进行裁减,做了个仅仅包含登陆至该摄像机并显示画面的简易版本,如下图所示,代码在文末给出,供大家参考。

接下来就是将同样的代码用C++来实现了,这个没有什么好说的,我用WTL调用这个控件实现了将画面放置于自己程序当中,同时可以调用控件的接口来控制云台,程序的代码在文末提供,下面是效果图:

简单地写了下控制界面,图上那个白色方块里边9个橙色方块可以控制云台的移动。

到此控件算是已经可以被调用了,但最重要是如何获得我们需要的视频的每帧图像数据。下面将具体介绍:

 

首先厂商提供的控件并没有提供能够获得视频数据的接口,因此我们无法简单的通过调用函数来解决这个问题。不过分析了该控件(DVM_IPCam2.ocx)的导入表后,发现他调用了VFW的DibDrawDib函数,因此猜测该控件是通过VFW将画面绘制到屏幕上的。DibDrawDib的定义如下:

The DrawDibDraw function draws a DIB to the screen.

BOOL DrawDibDraw(
  HDRAWDIB hdd,             
  HDC hdc,                  
  int xDst,                 
  int yDst,                 
  int dxDst,                
  int dyDst,                
  LPBITMAPINFOHEADER lpbi,  
  LPVOID lpBits,            
  int xSrc,                 
  int ySrc,                 
  int dxSrc,                
  int dySrc,                
  UINT wFlags               
);

对于视频的每一帧画面,该控件应该都会调用DrawDibDraw函数将图像传送至屏幕,而该函数的参数则直接包含了每一帧图像的尺寸、RGB数据。那么,如果我们能截获对此函数的调用,并从中获取数据就完全可以达到我们的需求了。

要实现导入函数的截获很方便,直接修改对应模块的IAT表即可,具体原理在Windows核心编程中介绍。再上面介绍的自制客户端程序中,每一帧的视频画面都会出发我们自己写的myDrawDibDraw_func函数,该函数拥有和DrawDibDraw一样的参数,可以通过参数来获得当前收到的这一桢画面,我们可以在此处添加视觉处理的代码。下面是一个用这个办法实现的将ipcam视频画面进行边缘查找处理的效果:

 

 

好了。在文末给出这里所涉及到的代码:

飞信(Fetion)的Perl命令行版本

请点击标题进入正文下载附加脚本

飞信我就不多介绍了,随着网上出现了libFetion后,各类第三方飞信客户端程序就纷纷涌现,对于飞信的协议分析资料也不少。不过一直比较遗憾的是用于命令行的客户端版本不多,目前只有基于PHP的实现,同时libFetion似乎不开源,无法基于它作开发。

其实纯命令行的实现有很多优势,比如可以写一个自动天气预报脚本,每天定时运行,采集到天气信息后就可以用命令行的飞信程序发送到手机了。

我原本打算用bash script实现一个飞信客户端,不过考虑到perl几乎每个linux发行版本都自带,且处理文本更加方便,所以这个周末就参照着网上一些PHP版的飞信实现,写了一个perl版本的飞信客户端脚本。同时因为之前从没有写过perl脚本也完全不会,所以也称趁这个机会速成一把。所谓实践出真知,写了这个脚本觉得自己的perl水平应该能算中等水平:-P

因为我写这个脚本主要就是为了发送短信用,所以功能很单纯,就是用飞信给一个手机号码发送指定的文本短信息。至于例如查询好友、加好友等复杂功能我没有考虑,各位可以发挥各自想象力,在以后的脚本基础上添加即可。本程序采用GPL作为许可证。

不明真相的截图

脚本的使用和要求:

按照飞信的协议要求,脚本中需要通过HTTP协议也要通过TCP socket发送SIPC文报。后者perl提供了POSIX一致的socket规范,前者我采用了curl命令,所以请确保自身系统中安装了合适的curl。对于win32,我没有做过测试,不过相信只要有相应的win32版程序,还是可以工作的。

如果要发送中文短信,请确保自己的shell环境是采用UTF8编码的。否则请用iconv自己转换。

要发送短信,请使用下面的命令:

perlfetion.pl <绑定飞信的手机号码> <飞信密码> <发送目标手机号码> <发送的文本>

这里要注意的是参数中所有手机号码都是应当绑定了飞信帐号的,且已经和发送手机建立了飞信好友关系。否则将发送失败。

如果发送成功,将有如下回应:

$ ./perlfetion.pl 138***** ***** 138***** "Hello World!"
Retrieving the config xml...
Retrieving the SSIAPP URL...
Retrieving the SIPC Address...
Trying to get the fetion number of the current account...
Connecting to the SIPC via TCP socket...
Login OK
Send SMS succeed

为了各位调试或者了解协议详情,本脚本支持dump模式,请修改脚本,在执行前调用函数:fetion_verbose();脚本会将所有通讯文报打印至stdout。

 

Autostereogram(立体画)的原理、观看以及制作

请点击文章标题进入正文下载附加讲稿

这里来炒下冷饭... 我在大一的时候曾经对立体画(Auto-Stereogram)研究过一段时间,并且开发过一个可视化的设计软件用于产生此类立体画。时间过得很快,转眼已经毕业了,这里分享一个我最近给目前组里边做的一个presentation:Auto-Stereogram Introduction。

立体画/立体图(Auto-Stereogram)相信各位从前都应该在不同地方看过,不过有可能并不知道他的真正学名:Auto-stereogram(wiki介绍)。如果你对他的实现原理感兴趣,或者希望了解如何去观看它,或者尝试自己制作(自己写程序或者基于已有软件),都可以来看看我这篇文章。同时,既然是炒冷饭,我Blog自然从前也提过此类主题。下面是我Blog以往的文章以及我开发的制作软件Stereoic的下载和介绍:

Stereogram的原理与制作

http://www.csksoft.net/blog/post/21.html

Stereoic的功能介绍

http://www.csksoft.net/blog/post/16.html

 

Stereoic的下载(位于主网站的程序作品区,需要Flash插件)

http://www.csksoft.net/index_mainsite.asp#SubView%3D2%26SubSection%3Dproducts%26ViewPos%3D23%26ViewType%3D2%26UID%3Dmainsite.site_data.ID53

 

因为是分享讲稿,文章本上就不过多介绍细节了,下面给出一些截图。本讲稿链接可以在文章末尾找到。对于想学习如何观看的朋友,在Stereoic软件的帮助文档中我曾经写过详细的观看提示。

 

自制的山寨版Sunjar

不知山寨这个词何时已经变成DIY的代名词了... Sunjar是什么呢?它是由英国的一家叫做Suck UK公司(这什么名字)设计的可以存储阳光并在夜间释放的玻璃罐。有兴趣的可以自行google下(关键词:sunjar 淘宝)。

虽然这个玻璃罐并非真正是将阳光“存储”了起来,不过创意还是十分的不错。尽管就是一个太阳能灯,但是通过罐子这个概念一包装,就给人新奇感。

但是,撇开如此好的创意,看到其正品的售价(¥200 + )实在上我觉得买它太不值得了。同时,不出我预料的国内已经有不少仿品了,且开价都低于60。同时还声称拥有多种其它功能。后来,那些卖正品的卖家估计是自身利益受损,就想尽办法在商品描述中“诋毁”那些山寨货。我觉得最经典的一句是:“那些仿品罐子还允许随意切换黄色或者蓝色的光线,这个主意看似很妙,实则很糟糕。要知道这是一个存储阳光的罐子,不是地摊上的变色灯……”原文大致如此,看得我实在觉得好笑...

好了,不胡扯了。与其花大价钱买这个玻璃罐,还不如自己做一个吧。实用价值还是有一点的,比如当小夜灯。于是,我花了2个周末作了2个山寨版Sunjar:

白天将它们至于阳光处(阳台上即可),到了晚上(光线变暗时),它们就开始发光了:

像不像Diablo里边的Mana药剂瓶...

 以上便是这山寨Sunjar的效果。自认为很不错。Sunjar这样优秀的创意,再加之自己DIY,其中的乐趣绝对要比在Taobao上花大价钱买一个好!同时,我认为它的电气特性要远远高于那些200多地“正规军”。下面我主要介绍制作过程以及原理和成本与性能分析。欢迎各位仿制,此为开源硬件项目。

原理和性能

Sunjar其实原理十分简单,就是一个太阳能储能装置+LED。或者说就是一个太阳能灯。不过实际作起来,并不是简单的将光伏电池、充电电池与LED接在一起如此简单。需要考虑几个问题:

  1. 玻璃罐限制了太阳能电池的功率,输出电压也不高
  2. 需要有光控电路控制LED仅在夜间发光

一般能装进玻璃罐的太阳能电池板一般开路电压都在1.5-5V左右,且短路电流一般不会超过200mA。同时考虑成本因素,一般3-4V 40-180mA的太阳能电池板是比较合理的选择。同时这里的数值是需要在室外晴天阳光照射下才能实现的。同时单晶硅或者多晶硅太阳能电池的弱光性差,在阴天和市内基本就不会有什么电流了。因此这样的配置下,充电电池一般考虑使用单节或双节的(1.2-2.4V)。这样的选择能确保在白天大多数情况下,太阳能电池输出的能量能尽可能充入电池。

但一般LED都需要2V以上电压驱动,尤其是蓝色和白色LED需要的驱动电压更高,即使2.4V的电压下白色LED的发光亮度也不高。因此很有必要设计驱动LED用的升压电路。将LED端的电压提升到3-5V。

对于上述第二个问题很好理解,实现起来就是一个简单的开关电路。

下面是本山寨Sunjar采用的电路图:

该电路并不复杂,所用元件也很少,且基本都能从已有的废旧设备上拆下。这里我就简要的介绍下原理,后文会提到材料的来源和选择问题。

该电路主要分为2部分,一为驱动LED的升压电路,它可以整体上看作一个负载。当图中9012三极管集电极上施加0.6-3V的电压时,图中的8050以及电感构成了一个振荡电路,通过电磁感应会在输出端(LED两侧)产生峰值约为5V的脉冲电压。经过电解电容的滤波可近似看成直流。此时驱动LED将得到耀眼的光线。足以驱动玻璃罐发光。

电路另一部分为光控电路,其主要靠两个9013管的饱和/截至特性构成了非门,并经过末端的9012驱动升压电路工作。

经我测量,在使用单节镍氢电池(1.2V)供电时,电池出的输出电流在35mA。如果使用600mAh的充电电池供电的话,大致可以在整个晚上持续发光了。电路中采用了最高4V-180mA输出的单晶硅太阳能电池。虽然理论上存在将电池过充的风险。但是考虑到白天日照是不持续且不稳定的。因此该风险其实大部分情况是不存在的。且较高功率的太阳能电池也允许在室内将充电电池充满,并不一定需要在阳光下暴晒。可以说如此的山寨Sunjar性能应当是很强悍的。

成本、选材与制作

这里先介绍选材的问题。我想大部分人手头是不会留有什么元件盒这类东西的,因此这里我主要介绍如何从身边废弃物中取出有用的元件来制作电路。

首先是太阳能电池板,该组件直接关系到Sunjar的性能,前文已经提到了,我使用的是4V/180mA的单晶圆形太阳能电池,直径为80mm。采用的是压层封装,寿命在10-15年。太阳能电池板很多时候只能自己购买。目前taobao上卖太阳能电池板的商家十分多。要挑选自己合适的太阳能板是很方便的,这里要注意的是应当注重其寿命以及输出电流。我购买的单片价格约为¥9。如果不在乎寿命,使用滴胶封装的板子,价格应当会很便宜。

其次是充电电池,因为LED耗电并不好,一般选用600mAh的镍氢电池即可。没必要使用高容量的。我采用了GP AAA的600mAh镍氢电池。如果使用锂电池要注意充电电路,避免过充爆炸。

其次是电路中电感用到的磁芯,我推荐用环形的磁芯。也可用柱状的。环形的磁芯其实很好找,可以从废弃的电子镇流器或者节能灯灯泡底座(需要拆除)取出。然后使用0.5mm的漆包线或者单股导线分别绕9匝以及15匝。如果没有环形的磁芯,也可采用中波收音机内的磁棒。

上图中绕有漆包线的黑色环就是磁环

白色部分为用收音机磁棒天线制作的电感

其余元件基本都能从电子玩具废旧的电子设备里找到。或者可以替代。图中第一级9013集级处的可变电阻的目的是调节光控电路的灵敏度,也可忽略该电阻。

LED只要选用自己喜欢的颜色即可,不过最好采用高亮度,散色类型的。

如果能顺利搞到这些材料,细心的焊接好基本就能工作了。这里就不介绍元器件的常识了。

那么实在不想或者不会焊接电路怎么办?这里有2个办法:

  1. 其中的升压电路可以从地摊买到的单节电池驱动的LED手电,或者那种用单节五号电驱动的手机应急充电器中拆出。效果是一样的。
  2. 可以在taobao购买一个草坪太阳能灯,将所有电路拆出。

其实,如果你选择了第二个方案,其成本已经高出直接购买一个山寨版Sunjar了。但DIY的乐趣还是很值得的。

我先是做了一个原形电路来验证这个电路图,十分的原始,没有用PCB板,完全架空焊接。且用了2节充电电池供电/储能,因此驱动的LED比较亮。之后又在万用PCB上作了一个“精美版”,采用单节电池供电/储能。

对于罐子的选择,为了充分发挥山寨本色,外形上要和正版的类似。我用了宜家中卖的那种玻璃罐。其内径也正好是80mm。正好将太阳能板装入。不过宜家的罐子是全透明的,最好在内壁贴上磨砂纸这类的实现漫反射效果。

好了,下面就算算材料成本。

一个宜家的玻璃罐 ¥11

太阳能电池板 ¥9

充电电池 ¥10

电路部分 ¥0-10 (如果完全废物利用,就不算成本了)

这样算来总共花费还是很低的,虽然做工可能没有正品精美(其实有本事完全可以超越),但是性能上是远远超过正品的。并且我怀疑市面上销售的Sunjar无论是否正品,都采用的是滴胶工艺的太阳能电池,其寿命一般只有2-3年。进一步说,DIY的乐趣绝对不是花钱能买来的。如果做的很好拿去送人也很有面子(骗小姑娘?)。

如果你也对此感兴趣,欢迎仿制,有什么问题也欢迎交流。

分页:[«]1[2][3][4][5][6][7][8][9][10][11][12][13][14][15][»]

日历

<< 2010-2 >>

Sun

Mon

Tue

Wed

Thu

Fri

Sat

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

MainSite

友情链接

最近引用

Search

站点统计

  • 文章总数:220
  • 评论总数:617
  • 引用总数:1
  • 浏览总数:65125
  • 留言总数:61
  • 当前样式:HeavenBlue
  • 当前语言:zh-CN

Copyright CSKSOFT 2000-2008. Powered By Z-Blog(CSK Modified)