极客工坊

 找回密码
 注册

QQ登录

只需一步,快速开始

查看: 28579|回复: 17

自动摩斯电码接收器

[复制链接]
发表于 2012-11-15 22:36:48 | 显示全部楼层 |阅读模式
本帖最后由 树·水·风 于 2012-11-16 08:59 编辑

前一段时间看《终极审判》,潜水员利用声音发送摩斯电码给潜艇最终获救,于是突发灵感:既然普通人无法记住每个字幕的摩斯电码,何不做一个自动接收器,将白光闪烁信号解析为字母呢?于是就开始做了。
整个系统包括两个部分:发送端和接收端。
发送端,使用Arduino控制LED手电发出闪光。MorseCode库参见程晨老师的Arduino开发实战指南,我所做的是在上面加了一些扩充。
Arduino代码如下:
  1. /*
  2. @Date:2012/11/10
  3. @Author:huaweidong
  4. @Description:MorseDemo03-自定义字符串,每次接收到的新的字符串替换原先的字符串,并开始循环发送
  5. */
  6. #include "MorseCode.h"
  7. char words[20];
  8. int i = 0;
  9. MorseCode Morse(13);

  10. void setup()
  11. {
  12.   Morse.setInterval(250);
  13.   Serial.begin(9600);
  14. }

  15. void loop()
  16. {
  17.   if(Serial.available())
  18.   {
  19.     i = 0;
  20.     while(Serial.available())
  21.     {
  22.       words[i] = Serial.read();
  23.       i++;
  24.     }
  25.   }
  26.   int k;
  27.   for(k = 0; k < i; k++)
  28.   {
  29.     Serial.print(words[k]);
  30.     if(words[k] == ' ')
  31.     {
  32.        Morse.WordInterval();
  33.     }
  34.     else
  35.     {
  36.       Morse.transfor(words[k]);
  37.       Morse.CharInterval();
  38.     }
  39.   }
  40.   Serial.println();
  41.   Morse.WordInterval();
  42. }
复制代码


用户打开串口工具后,可以自定义字符串(可包含空格),发送到Arduino板中,如SOS,Arduino之后便会循环发送SOS,直到用户输入新的字符串替换到原有字符串。

我使用的设备是小手电(原本想使用Android手机的闪光灯的,但是发现Android的闪光灯无法单独调用,只能连摄像头一起开启,而一旦开启摄像头,那么闪光灯的响应速度就变得很慢,无法达到想要的效果),原想使用三极管放大电路从而让Arduino控制小手电,后来发现小手电的额定电压也不过4.5V,于是就直接把小手电插在了Arduino板上。如下图所示:
图中小手电显示这么灰暗是因为我用手机拍照的缘故,实际上小手电的亮度是足够的(14个led灯),在夜间测试20米远完全没有问题。

于是摩斯电码发送端就算做好了,小手电可以发出间歇性的"."和"_"了,信息就存在与这些间歇性的闪光中。

下面就是接收端。

接收端我用的是一个摄像头,利用计算机视觉的方法,检测视频图像的颜色信息,根据颜色变化确定"."和"_",然后进一步确定每个英文字符,最后组成一个单词。
代码如下(未经优化,有很多冗余代码,大家见谅):
  1. #include <stdio.h>
  2. #include <cv.h>
  3. #include <highgui.h>
  4. #include <cxcore.h>
  5. #include <process.h>
  6. #include <Windows.h>
  7. #include <iostream>

  8. using namespace std;

  9. IplImage* IFrameBody = 0;
  10. IplImage* gray = 0;
  11. int red_cnt;
  12. int no_cnt;
  13. bool state;
  14. bool last_state;
  15. short value = 1;
  16. int height, width;
  17. CvCapture* pcap;

  18. char Decide(short i)
  19. {
  20.         char ch = '*';
  21.         switch(i){
  22.         case 5:
  23.                 ch = 'A';
  24.                 break;
  25.         case 24:
  26.                 ch = 'B';
  27.                 break;
  28.         case 26:
  29.                 ch = 'C';
  30.                 break;
  31.         case 12:
  32.                 ch = 'D';
  33.                 break;
  34.         case 2:
  35.                 ch = 'E';
  36.                 break;
  37.         case 18:
  38.                 ch = 'F';
  39.                 break;
  40.         case 14:
  41.                 ch = 'G';
  42.                 break;
  43.         case 16:
  44.                 ch = 'H';
  45.                 break;
  46.         case 4:
  47.                 ch = 'I';
  48.                 break;
  49.         case 23:
  50.                 ch = 'J';
  51.                 break;
  52.         case 13:
  53.                 ch = 'K';
  54.                 break;
  55.         case 20:
  56.                 ch = 'L';
  57.                 break;
  58.         case 7:
  59.                 ch = 'M';
  60.                 break;
  61.         case 6:
  62.                 ch = 'N';
  63.                 break;
  64.         case 15:
  65.                 ch = 'O';
  66.                 break;
  67.         case 22:
  68.                 ch = 'P';
  69.                 break;
  70.         case 29:
  71.                 ch = 'Q';
  72.                 break;
  73.         case 10:
  74.                 ch = 'R';
  75.                 break;
  76.         case 8:
  77.                 ch = 'S';
  78.                 break;
  79.         case 3:
  80.                 ch = 'T';
  81.                 break;
  82.         case 9:
  83.                 ch = 'U';
  84.                 break;
  85.         case 17:
  86.                 ch = 'V';
  87.                 break;
  88.         case 11:
  89.                 ch = 'W';
  90.                 break;
  91.         case 25:
  92.                 ch = 'X';
  93.                 break;
  94.         case 27:
  95.                 ch = 'Y';
  96.                 break;
  97.         case 28:
  98.                 ch = 'Z';
  99.                 break;
  100.         default:
  101.                 ch = '*';
  102.                 break;
  103.         }
  104.         return ch;
  105. }

  106. int main(int argc, char* argv[])
  107. {
  108.         CvCapture* pCapBody = cvCreateCameraCapture(1);

  109.         if(pCapBody == NULL)
  110.         {
  111.                 printf("No body camera!!!!!!!!\n");
  112.         }
  113.        
  114.         red_cnt = 0;
  115.         no_cnt = 0;
  116.         state = false;        // false代表无色,true代表检测到白色
  117.         last_state = false;

  118.         while(1)
  119.         {
  120.                 IFrameBody = cvQueryFrame(pCapBody);
  121.                 if(IFrameBody == NULL)
  122.                 {
  123.                         printf("No Pictures.\n");
  124.                 }

  125.                 height = IFrameBody->height;
  126.                 width = IFrameBody->width;
  127.                 gray = cvCreateImage(cvSize(width,height),8,1);        // 创建灰度图像

  128.                 int count = 0;

  129.                 /* 检测白色光 */
  130.                 cvCvtColor(IFrameBody, gray, CV_BGR2GRAY);
  131.                 for(int i = 220; i < 320; i++)
  132.                 {
  133.                         for(int j = 300; j < 400; j++)
  134.                         {
  135.                                 int g = ((uchar*)(gray->imageData+gray->widthStep*j))[i];
  136.                                 if(g>150)
  137.                                 {
  138.                                         count += 1;
  139.                                         ((uchar*)(gray->imageData+gray->widthStep*j))[i] = 255;
  140.                                 }
  141.                                 else
  142.                                 {
  143.                                         ((uchar*)(gray->imageData+gray->widthStep*j))[i] = 0;
  144.                                 }
  145.                         }
  146.                 }

  147.                 if(count > 1500)        // 检测“白色”,1500是像素点阈值
  148.                 {
  149.                         state = true;
  150.                         if(last_state == false)
  151.                         {
  152.                                 if(no_cnt > 10 && no_cnt < 35)        // 说明是字符间间隔,no_cnt是持续检测到“无色”的次数,10和35是阈值
  153.                                 {
  154.                                         cout<<Decide(value);
  155.                                         value = 1;
  156.                                 }
  157.                                 else if(no_cnt > 35)        // 说明是单词间间隔
  158.                                 {
  159.                                         cout<<Decide(value)<<" ";
  160.                                         value = 1;
  161.                                 }
  162.                                 no_cnt = 0;
  163.                                 red_cnt = 0;
  164.                         }
  165.                         red_cnt++;
  166.                 }
  167.                 else        // 检测到“无色”
  168.                 {
  169.                         state = false;
  170.                         if(last_state == true)
  171.                         {
  172.                                 if(red_cnt >= 10)        // 检测到“白色”十次以上,判断为“_”,阈值10可调
  173.                                 {
  174.                                         value = (value << 1) + 1;
  175.                                 }
  176.                                 else if(red_cnt < 10)        // 检测到“白色”的时间很短,判断为“.”
  177.                                 {
  178.                                         value = value << 1;
  179.                                 }
  180.                                 red_cnt = 0;
  181.                                 no_cnt = 0;
  182.                         }
  183.                         no_cnt++;
  184.                 }

  185.                 last_state = state;
  186.                
  187.                 cvShowImage("gray", gray);

  188.                 cvReleaseImage(&gray);

  189.                 char c=cvWaitKey(1);
  190.                 if(c==27)break;
  191.         }

  192.         cvReleaseCapture(&pCapBody);

  193. }
复制代码


上面一些阈值(如区分"."与"_"以及空格)需要根据电脑运行速度来设定。
为了提高视频处理速度,我只是对视频整个画面的一小部分做颜色判断,做实验时也把手电和摄像头离得比较近,当距离较远时,就需要把手电正对着摄像头,这样才能检测明显。
最后,执行上面的C++程序,便可以跳出两个窗口,如下图所示

一个窗口显示视频画面,一个输出解析的字母。(细致的同学会发现出现了一个E,这是由于我用手拿着手电遮挡了所造成的,后面就稳定地输出CCNT PVC了。)

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

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

评分

参与人数 1 +2 收起 理由
幻生幻灭 + 2 赞一个!

查看全部评分

回复

使用道具 举报

发表于 2012-11-15 23:55:39 | 显示全部楼层

还以为是通常光敏电阻做的
回复 支持 反对

使用道具 举报

发表于 2012-11-16 02:17:35 | 显示全部楼层
好...摩斯码确实比较有意思...
回复 支持 反对

使用道具 举报

发表于 2012-11-16 09:00:51 | 显示全部楼层
接收端是电脑+摄像头?我还以为也是arduino呢
回复 支持 反对

使用道具 举报

 楼主| 发表于 2012-11-16 09:02:58 | 显示全部楼层
ttyp 发表于 2012-11-16 09:00
接收端是电脑+摄像头?我还以为也是arduino呢

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

使用道具 举报

发表于 2012-11-16 09:44:16 | 显示全部楼层
准备做射频版本的。。。
回复 支持 反对

使用道具 举报

发表于 2012-11-16 10:47:51 | 显示全部楼层
树·水·风 发表于 2012-11-16 09:02
嗯,感觉摄像头检测效果蛮好的。不知道光敏电阻效果如何,以后有机会可以尝试下。

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

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

使用道具 举报

 楼主| 发表于 2012-11-16 12:28:29 | 显示全部楼层
ttyp 发表于 2012-11-16 10:47
摄像头的可以近似模拟人眼吧,我是说使用pc,如果使用arduino+摄像头的的就好了

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

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

使用道具 举报

 楼主| 发表于 2012-11-16 12:28:55 | 显示全部楼层
迷你强 发表于 2012-11-16 09:44
准备做射频版本的。。。

射频?使用无线吗?
回复 支持 反对

使用道具 举报

发表于 2012-11-16 18:01:59 | 显示全部楼层
树·水·风 发表于 2012-11-16 12:28
射频?使用无线吗?

应该是的。。。目前只是构思。。。。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2012-11-20 16:54:05 | 显示全部楼层
ttyp 发表于 2012-11-16 10:47
摄像头的可以近似模拟人眼吧,我是说使用pc,如果使用arduino+摄像头的的就好了

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

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

使用道具 举报

发表于 2012-11-20 18:00:49 | 显示全部楼层
树·水·风 发表于 2012-11-20 16:54
使用光敏电阻可以做吗?假如想要检测近距离红色的或者蓝色的LED灯的话?

光敏只能检测光的强度,颜色分辨不了的。有分辨颜色的传感器
回复 支持 反对

使用道具 举报

发表于 2012-11-21 12:08:34 | 显示全部楼层
如果可以做成音频版的,再加个1602显示输出就完美了。
回复 支持 反对

使用道具 举报

发表于 2012-11-21 14:06:14 | 显示全部楼层
bg5cdu 发表于 2012-11-21 12:08
如果可以做成音频版的,再加个1602显示输出就完美了。

哈哈,这个想法不错,偷号码的利器
回复 支持 反对

使用道具 举报

发表于 2012-11-22 13:38:46 | 显示全部楼层
ttyp 发表于 2012-11-21 14:06
哈哈,这个想法不错,偷号码的利器

怎么成偷号码了,莫尔斯电码和手机脉冲音频不一样的好伐。
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则 需要先绑定手机号

Archiver|联系我们|极客工坊

GMT+8, 2024-4-27 21:26 , Processed in 0.046385 second(s), 25 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表