树·水·风 发表于 2012-11-15 22:36:48

自动摩斯电码接收器

本帖最后由 树·水·风 于 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了。)

发送端和接收端做好后,就可以拿到空旷的场地试验了。

test01 发表于 2012-11-15 23:55:39


还以为是通常光敏电阻做的

ninjiafan 发表于 2012-11-16 02:17:35

好...摩斯码确实比较有意思...

ttyp 发表于 2012-11-16 09:00:51

接收端是电脑+摄像头?我还以为也是arduino呢

树·水·风 发表于 2012-11-16 09:02:58

ttyp 发表于 2012-11-16 09:00 static/image/common/back.gif
接收端是电脑+摄像头?我还以为也是arduino呢

嗯,感觉摄像头检测效果蛮好的。不知道光敏电阻效果如何,以后有机会可以尝试下。

迷你强 发表于 2012-11-16 09:44:16

:L准备做射频版本的。。。

ttyp 发表于 2012-11-16 10:47:51

树·水·风 发表于 2012-11-16 09:02 static/image/common/back.gif
嗯,感觉摄像头检测效果蛮好的。不知道光敏电阻效果如何,以后有机会可以尝试下。

摄像头的可以近似模拟人眼吧,我是说使用pc,如果使用arduino+摄像头的的就好了

另外光敏的距离有要求,方向性也差,如果使用激光,距离不错,方向性更差

树·水·风 发表于 2012-11-16 12:28:29

ttyp 发表于 2012-11-16 10:47 static/image/common/back.gif
摄像头的可以近似模拟人眼吧,我是说使用pc,如果使用arduino+摄像头的的就好了

另外光敏的距离有要求 ...

假如能够不使用电脑,直接使用Arduino或者其他嵌入式PC,加上摄像头,的确会便携很多,惟一的问题是嵌入式平台上能否支持视频处理。

树·水·风 发表于 2012-11-16 12:28:55

迷你强 发表于 2012-11-16 09:44 static/image/common/back.gif
准备做射频版本的。。。

射频?使用无线吗?

迷你强 发表于 2012-11-16 18:01:59

树·水·风 发表于 2012-11-16 12:28 static/image/common/back.gif
射频?使用无线吗?

应该是的。。。目前只是构思。。。。

树·水·风 发表于 2012-11-20 16:54:05

ttyp 发表于 2012-11-16 10:47 static/image/common/back.gif
摄像头的可以近似模拟人眼吧,我是说使用pc,如果使用arduino+摄像头的的就好了

另外光敏的距离有要求 ...

使用光敏电阻可以做吗?假如想要检测近距离红色的或者蓝色的LED灯的话?

ttyp 发表于 2012-11-20 18:00:49

树·水·风 发表于 2012-11-20 16:54 static/image/common/back.gif
使用光敏电阻可以做吗?假如想要检测近距离红色的或者蓝色的LED灯的话?

光敏只能检测光的强度,颜色分辨不了的。有分辨颜色的传感器

bg5cdu 发表于 2012-11-21 12:08:34

如果可以做成音频版的,再加个1602显示输出就完美了。

ttyp 发表于 2012-11-21 14:06:14

bg5cdu 发表于 2012-11-21 12:08 static/image/common/back.gif
如果可以做成音频版的,再加个1602显示输出就完美了。

哈哈,这个想法不错,偷号码的利器

bg5cdu 发表于 2012-11-22 13:38:46

ttyp 发表于 2012-11-21 14:06 static/image/common/back.gif
哈哈,这个想法不错,偷号码的利器

怎么成偷号码了,莫尔斯电码和手机脉冲音频不一样的好伐。
页: [1] 2
查看完整版本: 自动摩斯电码接收器