自制三维激光扫描建模

article/2025/9/16 11:43:41

看图片就是我做的东西,很炫酷是不是。

好吧,开玩笑,这是电影普罗米修斯的截图。

当初看这个电影的时候就感觉这东西好眩酷,我能不能做出来。最近借着帮做毕业设计的机会我也做了一个。

就是这个丑丑的东西啦~




首先感谢来自CSK的低成本3D scanner。http://www.csksoft.net/blog/post/lowcost_3d_laser_ranger_1.html

本制作中大部分参考了他的原理,创意。

原理:

利用激光三角测距法的原理,计算出摄像头到激光点的距离,这样很多个激光点的距离得到,再结合角度信息,我们便可以得到他在三维坐标系下的坐标,从而渲染出来。

三角测距法:

Laser是激光器,Imager是针孔摄像机的模型,Object是物体,激光器发射的激光,经过的物体的反射进入摄像头,其中β角已知,S已知,光斑在摄像头成像的坐标我们可以得到,从而我们可以得到反射光线与S的夹角,从而三角形已知,解这个三角形,我们便可以得到d,也就是距离。

不过,这样只能得到一个点的信息,转一周我们也只能得到二维坐标系下的坐标。要想得到三维的坐标,要不装置可以增加一个维度的运动,要不我们可以采用一字线激光器。

在这个制作中我们采用了一字线激光器。


效果如图。

如何根据一字线激光点坐标得到距离(图像处理过程之后再提)



这样实际就成为了一个椎体,画面中心点的距离可以很方便求出,而其他地方点的坐标我们必须知道它的仰角,我们可以通过根据距离画面中心点的距离得到。实际上就是解立体几何啦~

具体的公式,可以参见我的代码。

对于摄像头的选择

理论上来说,640*480的分辨率的画面就可以很好的精度。我选择的是OV9710摄像头,通过USB获取数据。起初我还打算用STM32F4来进行图像处理,后来实在是太吃力了,RAM根本不够用的,所以就直接在电脑上使用OpenCV来进行图像处理了。

图像处理

整个制作最关键的部分就是图像处理了,起初我是对图像直接灰度然后二值化,这种效果很不理想,一遇到亮的地方就完蛋了。

后来我参考网上别人的做法,使用不可见光808NM的100MW的激光器,摄像头加装了一个窄带滤光片,效果的确不错,可是距离我的期望感觉还差得远。


这就是加装了滤光片后使用不可见光激光拍摄的画面。

后来我苦思冥想,想出来一个大招。把激光当作一个信息处理,那么原来我直接获取激光,是根本没有经过任何处理的获取信息,但如果我把激光调制呢,例如这一帧画面有激光,下一帧画面便没有激光,两幅图像异或!便可以得到很清晰的只有激光的画面。事实证明,我的这个想法很好。非常可行。


可以看到效果很好,得到的画面很干净。

控制

根据前面说的,我们整个装置要进行旋转,我选择了舵机,舵机要用PWM控制。

激光器要能够根据我的指令关闭和开启,所以我搭建了一个简单的MOS开关电路用来对激光器进行控制,控制信号采用的是高电平低电平信号。

要能够获取我们现在转动的角度,因此我添加了MPU6050模块,模块基于I2C协议进行通讯。

要与电脑进行通信,接收电脑的指令,我选择了串口作为通讯方式。

所以,基于以上,我使用了STM32F103作为下位机,接收上位机指令,控制激光器开关,获取MPU6050数据,驱动舵机运动。


程序实现

因为大三啦~要准备考研啦~(我还选择跨考计算机就更没怎么有时间了)所以我就用了Python+OpenCV来缩短编程的时间了。

程序主要就是图像处理部分和计算部分了。

这是图像处理以及控制部分程序(Python2.7+OpenCV)

    ser.write('+\r\n'.encode()) #舵机转str=ser.readline()ser.write('ON\r\n'.encode()) #激光器打开str=ser.readline()ret,img1 = cap.read()ret,img1 = cap.read()ret,img1 = cap.read()ret,img1 = cap.read()  #多获取几次,感觉这样获得的画面会稳定点吧ser.write('OFF\r\n'.encode()) #激光器关闭str=ser.readline()ret,img2 = cap.read()ret,img2 = cap.read()ret,img2 = cap.read()ret,img2 = cap.read()  #多获取几次,感觉这样获得的画面会稳定点吧ser.write('Angle?\r\n'.encode()) #请求角度Angle=ser.readline()#获取角度img1=cv2.resize(img1,(800,600),interpolation=cv2.INTER_CUBIC)#图像缩放img2=cv2.resize(img2,(800,600),interpolation=cv2.INTER_CUBIC)#图像缩放gray1 = cv2.cvtColor(img1, 6)gray2 = cv2.cvtColor(img2, 6)#图像灰度化ret,TRE1 = cv2.threshold(gray1,215,255,cv2.THRESH_BINARY)ret,TRE2 = cv2.threshold(gray2,215,255,cv2.THRESH_BINARY)#图像二值化img=cv2.absdiff(TRE1,TRE2)#两幅图像找不同x=[]y=[]   #用来存放坐标x,y=findXY(img) #获取坐标px=[]py=[]pz=[]   #用来存放实际三维坐标for i in range(len(x)):dis,ang,pit=calcDistanceByPos(x[i],y[i],800)p_x,p_y,p_z=getPLOTXYZ(dis,ang,pit,Angle)    #计算坐标px.append(p_x)py.append(p_y)pz.append(p_z)
计算(这部分程序参考网上帖子的计算方法~)

主要是先获取极坐标系下坐标再得到直角三维坐标

def calcDistanceByPos(x,y,img_height):a=698.1       #f*baselineb=4.354c=0.006912    #PixelSized=-40.88baseline=100.0laser_angle=1.486407302rotation_r=49.2focal=a/baselinecenter_distance=a/(b-c*x)pitch_angle=math.atan(((y-img_height/2)*c)/(focal))pitch_distance=center_distance/math.cos(pitch_angle)laser_to_dist_pt = center_distance*math.tan(PI/2 - laser_angle)laser_to_current_pt = math.sqrt(pitch_distance*pitch_distance + laser_to_dist_pt*laser_to_dist_pt)laser_to_center_pt  = math.sqrt(center_distance*center_distance + laser_to_dist_pt*laser_to_dist_pt)real_center_distance = math.sqrt( (laser_to_dist_pt-rotation_r)*(laser_to_dist_pt-rotation_r)+  center_distance*center_distance)yaw_angle = PI/2 - math.acos((rotation_r*rotation_r + real_center_distance*real_center_distance- laser_to_center_pt*laser_to_center_pt)/2.0/rotation_r/real_center_distance)real_distance = math.sqrt( (laser_to_dist_pt-rotation_r)*(laser_to_dist_pt-rotation_r) +  pitch_distance*pitch_distance)return real_distance,yaw_angle,(pitch_angle-18.0*PI/180.0)
def getPLOTXYZ(distance,yaw,pitch,Angle):px = distance * math.cos(pitch) * math.sin( yaw  + Angle*PI/10.0/180.0)py = distance * math.cos(pitch) * math.cos( yaw  + Angle*PI/10.0/180.0)pz = distance * math.sin(-pitch)return px,py,pz
效果如图,还是挺好的哇~~~~~只是我还没怎么会用那些点云渲染的引擎,就直接扫了一段数据后用散点图画出来了,哈哈哈哈哈~

(这是我之前拍摄的工作视频截取得画面)


最后就祝大家万事胜意吧,嘻嘻。

http://chatgpt.dhexx.cn/article/Gs9Ynk0m.shtml

相关文章

Ansys Zemax | 使用 OpticStudio 进行闪光激光雷达系统建模(上)

前言 在消费类电子产品领域,工程师可利用激光雷达实现众多功能,如面部识别和3D映射等。尽管激光雷达系统的应用非常广泛而且截然不同,但是 “闪光激光雷达” 解决方案通常都适用于在使用固态光学元件的目标场景中生成可检测的点阵列。凭借具…

复杂建筑物三维激光扫描与室内外精细建模的科普解析

如今,随着科技的进步,复杂建筑物的三维激光扫描与室内外精细建模成为了现实。本文将通过简洁易懂的方式,介绍这一技术的原理、应用和操作过程,并引用可靠数据和研究成果进行支持,以确保准确性和可信度。 第一部分&…

真实多模激光的建模

作者:Daniel Asoubar(LightTrans) 相关文件:Tutorial_101.01,Snippet_028 需求:VirtualLab™5.11.1-基本工具箱 许可证:CC-BY-SA 3.0 摘要 1). 这个案例展示了如何在VirtualLab中对一个真…

nohup启动jar_nohup命令详解

nohup命令详解 在我们想要把SpringBoot微服务工程部署到远程服务器时,会通过java -jar springboot.jar的方式启动SpringBoot微服务。但是当我们把运行这个命令的SSH客户端退出登录就会导致SpringBoot进程也一起停止了,然后当然就没法访问我们启动的项目了…

Linux-nohup命令详解

场景 今天在linux上部署wdt程序,在SSH客户端执行./start-dishi.sh,启动成功,在关闭SSH客户端后,运行的程序也同时终止了,怎样才能保证在推出SSH客户端后程序能一直执行呢?通过网上查找资料,发现需要使用nohup命令。 …

c语言连续生成不同随机数_C语言连续生成多个随机数(可限制范围)

生成随机数 在现实中我们经常用到随机数,可怎么实现呢,且听小乔慢慢道来。 在C语言中,我们一般使用 头文件中的 rand() 函数来生成随机数 int void rand() 可是却发现生成的数字都一样。 这是因为rand() 函数产生的随机数是伪随机数&#xff…

c语言不用随机数种子,C语言的随机数与随机种子

引言: 在实际编程中,我们经常会用到随机数这个概念,其实也是一个伪随机数,实际上并不是一个真正的随机数,但是也足够我们使用了。在C语言中,编写一些关于游戏之类的程序时就需要用到随机数了。同时C语言也提…

C 语言随机数

1. 随机数概述 编写程序过程中,我们经常需要产生一些随机数。随机数在程序中分为两种: 真随机数:完全没有规则,无法预测接下来要产生的数。 伪随机数:通过一些预先设定好的规则产生不能简单预测的数。 当然&#xff0…

产生瑞利分布的随机数 C语言实现

瑞利分布的概率密度函数为 瑞丽分布的均值为,方差为 首先使用逆变换法产生参数的指数分布的随机变量,其概率密度函数为 然后通过变换,产生瑞丽分布的随机变量x,具体的方法如下: (1)产生均匀分布的随机数 &#xff…

产生(a,b)区间上均匀分布的随机数 C语言实现

uniform.h文件 #ifndef UNIFORM_H_ #define UNIFORM_H_/* 函数功能: 产生(a,b)区间上均匀分布的随机数组 输入参数说明: a 给定区间的下限 b 给定区间的上线 seed 长整型指针变量, *seed 为伪随机数的种子 */ doubl…

洛谷:明明的随机数,C语言

题目描述 明明想在学校中请一些同学一起做一项问卷调查,为了实验的客观性,他先用计算机生成了N个1到1000之间的随机整数(N≤100),对于其中重复的数字,只保留一个,把其余相同的数去掉,不同的数对应着不同的…

产生正态分布(高斯分布)随机数 C语言实现

正态分布的概率密度函数如下 通常使用表示均值为,方差为 产生正太分布的方法如下: 设为(0,1)上n个相互独立的均匀分布的随机数,由于,. 根据中心极限定理可知,当n趋向于无穷时 的分布近似于正态分布&…

产生指数分布的随机数 C语言实现

一、产生随机变量的逆变换方法 定理:设F(x)是任一连续的分布函数,如果,且那么. 证明 由于,则有 所以 此定理给出了从均匀分布随机数到给定分布的随机数的变换,根据该变换可以生分布函数为的随机数,其算法可以用下列…

C语言如何设置随机数

步骤 设置一个随机的起点 那如何设置呢&#xff1f;编写代码srand((unsigned int)time(NULL))。而该代码用到了两个库函数void srand(unsigned int seed)和longlong time(NULL)&#xff0c;对应的头文件是<stdlib.h>和<time.h>。生成随机数 生成随机数直接编写代码…

C语言基础教程:C语言随机函数

1. 随机数概述 编写程序过程中&#xff0c;我们经常需要产生一些随机数。随机数在程序中分为两种&#xff1a; 真随机数&#xff1a;完全没有规则&#xff0c;无法预测接下来要产生的数。伪随机数&#xff1a;通过一些预先设定好的规则产生不能简单预测的数。当然&#xff0c…

C语言头文件深入理解

C语言程序中&#xff0c;源文件通常分为两种&#xff1a;一种用于保存程序的声明(declaration)&#xff0c;称为头文件&#xff1b;另一种用于保存程序的实现(implementation)&#xff0c;称为定义(definition)文件。 C程序的头文件以“.h”为后缀&#xff0c;C 程序的定义文件…

第十四章 C语言头文件的编写_C语言模块化编程中的头文件

前面我们在演示多文件编程时创建了 main.c 和 module.c 两个源文件&#xff0c;并在 module.c 中定义了一个函数和一个全局变量&#xff0c;然后在 main.c 中进行了声明。 不过实际开发中很少这样做&#xff0c;一般是将函数和变量的声明放到头文件&#xff0c;再在当前源文件中…

C语言头文件路径相关问题总结说明

聊聊系统路径位置&#xff0c;绝对路径与相对路径&#xff0c;正斜杠 / 与 反斜杠 \ 使用说明 ...... by 矜辰所致目录 前言一、C语言中的头文件引用二、KEIL 中的头文件路径2.1 IncudePaths 指定的路径绝对路径和相对路径正斜杠 / 与 反斜杠 \ 与双斜杠 2.2 include < &…

c语言头文件下载大全,求C语言头文件下载?

传统 C++ #include <assert.h> //设定插入点 #include <ctype.h> //字符处理 #include <errno.h> //定义错误码 #include <float.h> //浮点数处理 #include <fstream.h> //文件输入/输出 #include <iomanip.h> //参数化输入/输出 #inclu…

python读取C语言头文件

本文测试过程使用使用的工程代码上传 python读取C语言头文件&#xff0c;参考博客&#xff0c;有测试记录、使用说明资源-CSDN文库 或者 python读取C语言头文件测试用例&#xff0c;参考博客&#xff0c;有详细说明-嵌入式文档类资源-CSDN文库 背景 在使用python编程过程中…