打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
简易激光测距
 抽空做了个简单的激光测距的小东西,这个方案简单易实现,而且可以用现有的opencv库很容易作进一步性能改进。
之前在一个老外的网上看到介绍,现在找不到了。
基本原理:




这个图很清晰的解释了测距的基本原理,假设激光投射到目标物体上,同时位于同一平面的摄像头画面内能够捕捉到激光照射点,(所以要求摄像头和激光头之间的距离h不能太远),激光光线和摄像头成像轴完全平行。这样,照射到目标物体的激光点和摄像头成像平面连线和摄像头轴线之间会有一个夹角theta,通过在摄像帧图像里面对这个激光点相对于轴心的像素点的判断,可以确定出theta的大小,从而就能得到距离D。D越远,画面中激光点离中心越近,D越小,激光点离中心越远。画面的激光点判断可以简单的认为最亮的点就是激光点,逐像素扫描。当然可以使用opencv的API实现更复杂更精确的激光点确定方法。
整个算法比较简单但是很实用。

D很容易由下式得出:


所以只有一个未知量theta,theta由几个因素决定,一个是画面中激光点离轴心的距离pfc,这个可以通过帧画面的处理得到,
另外两个是固定量,和具体摄像头有关,每个摄像头都不一样,rpc表示每个像素的弧度(其实就是与pfc想乘的因子),还有一个是弧度偏移补偿量,用于矫正。


这样我们可以得到下式:


经过上面的分析,只有pfc一个变量,其他的都算已知量。那怎么得到rpc和ro呢?
可以通过实验得到:
在实际试验中先得到两组数据,分别是激光点离中心的距离和真实的距离D

矫正数据
光点离中心距离D (cm)
61137
13764

代入
137 =7.350/tan(61x+y)
64 =7.350/tan(137x+y)

这样得到

Offset (ro)= 0.0053867 radians

Gain (rpc)= 0.000795302 radians/pixel


采用的摄像头,很普通的那种,640*480像素


做好了的实验板:




代码:

#include <iostream>

usingnamespacestd;
#include"opencv/cv.h"
#include"opencv2/highgui/highgui.hpp"

intmain( int argc, char** argv ) {
   cvNamedWindow( "Example", CV_WINDOW_AUTOSIZE );
   CvCapture* capture;
   if (argc==1) {
      capture= cvCreateCameraCapture( 0 );
   } else{
      capture= cvCreateFileCapture( argv[1] );
   }
   assert( capture != NULL );

   IplImage* frame;
   while(1){
      frame= cvQueryFrame( capture );
      if( !frame ) break;

      //对取到的图像做处理

      unsignedint   W, H;
      unsignedint   row, col;
      unsignedlong  i;
      unsignedint   max_row;
      unsignedint   max_col;
      unsignedchar  max_val = 0;

      constdouble   gain = 0.000795302;
      constdouble   offset = 0.0053867;
      constdouble   h_cm = 7.350;           //我的摄像头和激光头距离7.35cm
      double        range;
      unsignedint   pixels_from_center;

      W= frame->width;
      H= frame->height;
      unsignedchar*img_d = (unsignedchar*)frame->imageData;
      
      //假设激光点是最亮点,最简单的实现,一般来说确实是这样,但是在背景干扰大的情况下会误判,还有很大的提高空间

      for(row = 0; row < H; row++) {
         for(col = 0; col < W; col++) {
             i = (unsignedlong)(row*3*W+ 3*col);

             if (*(img_d + i) >= max_val)
             {
                max_val= *(img_d + i);
                max_row= row;
                max_col= col;
             }
         }
      }

      max_val= 0;

      //找到了画十字线标注
      for(row = 0; row < H; row++) {
         for(col = 0; col < W; col++) {
             i = (unsignedlong)(row*3*W+ 3*col);
             if((row == max_row) || (col == max_col))
                       *(img_d + i) =
                       *(img_d + i + 1) =
                       *(img_d + i + 2) = 255;
         }
      }

      pixels_from_center = max_row - H/2;

      //算出距离并打印出来
      range= h_cm / tan(pixels_from_center * gain + offset);

      cout<<"W= " <<W <<", H= " <<H <<", Max Value atx="
          <<max_col <<", y= " <<max_row <<", range= "<<range <<endl;


      cvShowImage( "Example", frame );
      charc = cvWaitKey(10);
      if( c == 27 ) break;
   }
   cvReleaseCapture( &capture );
   cvDestroyWindow( "Example" );
}



实际测试中误差还算可以,在5%以内,最主要是激光点的判断还有很大提高空间,opencv提供了不少API,网上也有一些文章
距离64cm:





距离152cm:



本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
精巧的4*4键盘程序
1小时C语言入门(转帖续六)
数字图像处理算法实现
8*8全彩点阵的使用
干货 | 6个按键能有多少组合?这篇文章告诉你
人脸检测【dlib源码分析】
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服