自动摩斯电码接收器
本帖最后由 树·水·风 于 2012-11-16 08:59 编辑前一段时间看《终极审判》,潜水员利用声音发送摩斯电码给潜艇最终获救,于是突发灵感:既然普通人无法记住每个字幕的摩斯电码,何不做一个自动接收器,将白光闪烁信号解析为字母呢?于是就开始做了。
整个系统包括两个部分:发送端和接收端。
发送端,使用Arduino控制LED手电发出闪光。MorseCode库参见程晨老师的Arduino开发实战指南,我所做的是在上面加了一些扩充。
Arduino代码如下:
/*
@Date:2012/11/10
@Author:huaweidong
@Description:MorseDemo03-自定义字符串,每次接收到的新的字符串替换原先的字符串,并开始循环发送
*/
#include "MorseCode.h"
char words;
int i = 0;
MorseCode Morse(13);
void setup()
{
Morse.setInterval(250);
Serial.begin(9600);
}
void loop()
{
if(Serial.available())
{
i = 0;
while(Serial.available())
{
words = Serial.read();
i++;
}
}
int k;
for(k = 0; k < i; k++)
{
Serial.print(words);
if(words == ' ')
{
Morse.WordInterval();
}
else
{
Morse.transfor(words);
Morse.CharInterval();
}
}
Serial.println();
Morse.WordInterval();
}
用户打开串口工具后,可以自定义字符串(可包含空格),发送到Arduino板中,如SOS,Arduino之后便会循环发送SOS,直到用户输入新的字符串替换到原有字符串。
我使用的设备是小手电(原本想使用Android手机的闪光灯的,但是发现Android的闪光灯无法单独调用,只能连摄像头一起开启,而一旦开启摄像头,那么闪光灯的响应速度就变得很慢,无法达到想要的效果),原想使用三极管放大电路从而让Arduino控制小手电,后来发现小手电的额定电压也不过4.5V,于是就直接把小手电插在了Arduino板上。如下图所示:
图中小手电显示这么灰暗是因为我用手机拍照的缘故,实际上小手电的亮度是足够的(14个led灯),在夜间测试20米远完全没有问题。
于是摩斯电码发送端就算做好了,小手电可以发出间歇性的"."和"_"了,信息就存在与这些间歇性的闪光中。
下面就是接收端。
接收端我用的是一个摄像头,利用计算机视觉的方法,检测视频图像的颜色信息,根据颜色变化确定"."和"_",然后进一步确定每个英文字符,最后组成一个单词。
代码如下(未经优化,有很多冗余代码,大家见谅):
#include <stdio.h>
#include <cv.h>
#include <highgui.h>
#include <cxcore.h>
#include <process.h>
#include <Windows.h>
#include <iostream>
using namespace std;
IplImage* IFrameBody = 0;
IplImage* gray = 0;
int red_cnt;
int no_cnt;
bool state;
bool last_state;
short value = 1;
int height, width;
CvCapture* pcap;
char Decide(short i)
{
char ch = '*';
switch(i){
case 5:
ch = 'A';
break;
case 24:
ch = 'B';
break;
case 26:
ch = 'C';
break;
case 12:
ch = 'D';
break;
case 2:
ch = 'E';
break;
case 18:
ch = 'F';
break;
case 14:
ch = 'G';
break;
case 16:
ch = 'H';
break;
case 4:
ch = 'I';
break;
case 23:
ch = 'J';
break;
case 13:
ch = 'K';
break;
case 20:
ch = 'L';
break;
case 7:
ch = 'M';
break;
case 6:
ch = 'N';
break;
case 15:
ch = 'O';
break;
case 22:
ch = 'P';
break;
case 29:
ch = 'Q';
break;
case 10:
ch = 'R';
break;
case 8:
ch = 'S';
break;
case 3:
ch = 'T';
break;
case 9:
ch = 'U';
break;
case 17:
ch = 'V';
break;
case 11:
ch = 'W';
break;
case 25:
ch = 'X';
break;
case 27:
ch = 'Y';
break;
case 28:
ch = 'Z';
break;
default:
ch = '*';
break;
}
return ch;
}
int main(int argc, char* argv[])
{
CvCapture* pCapBody = cvCreateCameraCapture(1);
if(pCapBody == NULL)
{
printf("No body camera!!!!!!!!\n");
}
red_cnt = 0;
no_cnt = 0;
state = false; // false代表无色,true代表检测到白色
last_state = false;
while(1)
{
IFrameBody = cvQueryFrame(pCapBody);
if(IFrameBody == NULL)
{
printf("No Pictures.\n");
}
height = IFrameBody->height;
width = IFrameBody->width;
gray = cvCreateImage(cvSize(width,height),8,1); // 创建灰度图像
int count = 0;
/* 检测白色光 */
cvCvtColor(IFrameBody, gray, CV_BGR2GRAY);
for(int i = 220; i < 320; i++)
{
for(int j = 300; j < 400; j++)
{
int g = ((uchar*)(gray->imageData+gray->widthStep*j));
if(g>150)
{
count += 1;
((uchar*)(gray->imageData+gray->widthStep*j)) = 255;
}
else
{
((uchar*)(gray->imageData+gray->widthStep*j)) = 0;
}
}
}
if(count > 1500) // 检测“白色”,1500是像素点阈值
{
state = true;
if(last_state == false)
{
if(no_cnt > 10 && no_cnt < 35) // 说明是字符间间隔,no_cnt是持续检测到“无色”的次数,10和35是阈值
{
cout<<Decide(value);
value = 1;
}
else if(no_cnt > 35) // 说明是单词间间隔
{
cout<<Decide(value)<<" ";
value = 1;
}
no_cnt = 0;
red_cnt = 0;
}
red_cnt++;
}
else // 检测到“无色”
{
state = false;
if(last_state == true)
{
if(red_cnt >= 10) // 检测到“白色”十次以上,判断为“_”,阈值10可调
{
value = (value << 1) + 1;
}
else if(red_cnt < 10) // 检测到“白色”的时间很短,判断为“.”
{
value = value << 1;
}
red_cnt = 0;
no_cnt = 0;
}
no_cnt++;
}
last_state = state;
cvShowImage("gray", gray);
cvReleaseImage(&gray);
char c=cvWaitKey(1);
if(c==27)break;
}
cvReleaseCapture(&pCapBody);
}
上面一些阈值(如区分"."与"_"以及空格)需要根据电脑运行速度来设定。
为了提高视频处理速度,我只是对视频整个画面的一小部分做颜色判断,做实验时也把手电和摄像头离得比较近,当距离较远时,就需要把手电正对着摄像头,这样才能检测明显。
最后,执行上面的C++程序,便可以跳出两个窗口,如下图所示
一个窗口显示视频画面,一个输出解析的字母。(细致的同学会发现出现了一个E,这是由于我用手拿着手电遮挡了所造成的,后面就稳定地输出CCNT PVC了。)
发送端和接收端做好后,就可以拿到空旷的场地试验了。 强
还以为是通常光敏电阻做的 好...摩斯码确实比较有意思... 接收端是电脑+摄像头?我还以为也是arduino呢 ttyp 发表于 2012-11-16 09:00 static/image/common/back.gif
接收端是电脑+摄像头?我还以为也是arduino呢
嗯,感觉摄像头检测效果蛮好的。不知道光敏电阻效果如何,以后有机会可以尝试下。 :L准备做射频版本的。。。 树·水·风 发表于 2012-11-16 09:02 static/image/common/back.gif
嗯,感觉摄像头检测效果蛮好的。不知道光敏电阻效果如何,以后有机会可以尝试下。
摄像头的可以近似模拟人眼吧,我是说使用pc,如果使用arduino+摄像头的的就好了
另外光敏的距离有要求,方向性也差,如果使用激光,距离不错,方向性更差 ttyp 发表于 2012-11-16 10:47 static/image/common/back.gif
摄像头的可以近似模拟人眼吧,我是说使用pc,如果使用arduino+摄像头的的就好了
另外光敏的距离有要求 ...
假如能够不使用电脑,直接使用Arduino或者其他嵌入式PC,加上摄像头,的确会便携很多,惟一的问题是嵌入式平台上能否支持视频处理。 迷你强 发表于 2012-11-16 09:44 static/image/common/back.gif
准备做射频版本的。。。
射频?使用无线吗? 树·水·风 发表于 2012-11-16 12:28 static/image/common/back.gif
射频?使用无线吗?
应该是的。。。目前只是构思。。。。 ttyp 发表于 2012-11-16 10:47 static/image/common/back.gif
摄像头的可以近似模拟人眼吧,我是说使用pc,如果使用arduino+摄像头的的就好了
另外光敏的距离有要求 ...
使用光敏电阻可以做吗?假如想要检测近距离红色的或者蓝色的LED灯的话? 树·水·风 发表于 2012-11-20 16:54 static/image/common/back.gif
使用光敏电阻可以做吗?假如想要检测近距离红色的或者蓝色的LED灯的话?
光敏只能检测光的强度,颜色分辨不了的。有分辨颜色的传感器 如果可以做成音频版的,再加个1602显示输出就完美了。 bg5cdu 发表于 2012-11-21 12:08 static/image/common/back.gif
如果可以做成音频版的,再加个1602显示输出就完美了。
哈哈,这个想法不错,偷号码的利器 ttyp 发表于 2012-11-21 14:06 static/image/common/back.gif
哈哈,这个想法不错,偷号码的利器
怎么成偷号码了,莫尔斯电码和手机脉冲音频不一样的好伐。
页:
[1]
2