三维扫描仪[9]——如何设计一台云台式扫描仪(初步软件设计)

我们有无数种方法可以让扫描仪进行360°扫描
拿我导师的项目来举例
这里写图片描述
这里写图片描述
在被测物体上贴上标定点,先扫一次点,然后多次扫描拼接时,保证任意一次扫描均能看见3个以上的点,即可根据点,拼接整个被测物体。


再说一个商业扫描仪
这是一个手持扫描仪
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述
扫描时不需要贴任何的标定点 是的完全不需要!
扫描仪是线激光扫描仪,投射一个红十字和蓝激光,由一个摄像头、激光发射器还有其他传感器组成。
这个扫描仪是极其强大的,我这里只说拼接。
它是如何实现在不贴点的情况下拼接的呢?
可以发现,这个扫描仪后面跟了一个机械臂一样的东西。
没错!这台扫描仪正是利用这个像机械臂一样的装置,获取空间位置,而且精度极高。
我顿时觉得人生好黑暗…怎么什么玩意都做出来了…要我何用…


回来看看我们的破烂转盘扫描仪
这里写图片描述
为了从不同的镜头重建整个物体,我们需要把定义物体的所有的点设定在同一个坐标系。把初始拍摄点P的坐标定义为P(X, Y, Z),当转盘旋转的时候P点坐标发生改变,新坐标为P(X,Y, Z)。
这里写图片描述
具体的机械结构设计请参考我的上一篇博客


Arduino

Arduino代码是最简单的
不过分为两种~(我Arduino比较多)

一、任意角度任意次数扫描代码

int potPin = A0;
int Pos = 0;

void setup() {
  // put your setup code here, to run once:
  pinMode(potPin, INPUT);
  Serial.begin(9600);
}

void loop() {
  // put your main code here, to run repeatedly:
  Pos = analogRead(potPin);
  delay(250);
  Serial.println(Pos);
}

setup 、 loop 和 analogRead请参考我之前的博客
代码详解
第一行给我们看中的端口起一个新名字,只是起一个新名字而已,我们看中的这个端口原来叫做A0,现在叫做potPin,我们还需要知道它是一个模拟口。
第二行初始化一个整型变量Pos,用来保存读到的数据,将来还要把这个Pos告诉电脑上的Processing。这个读数就是Arduino读到的电位器的数值,范围是0-1023。
pinMode定义的是用哪个口,它用来读入还是输出。我们希望A0口也就是potPin这个口是输入口。

所以:pinMode ——我要设置端口 (potPin ——我要用这个端口了 , INPUT ——这个端口负责读入 ); ——一行代码到此为止。
Serial.begin(9600);是设置波特率
其实想设置多少波特率很随意的,只不过我设置为9600而已,当然你可以设置为115200。
!!!!!!!!!!!!!!!!!!!!!!!!!!!
但是!如果你并不了解波特率!那就千万不要动这个参数
!!!!!!!!!!!!!!!!!!!!!!!!!!!
这个参数需要和电脑上Processing上的波特率设置一致。

Pos = analogRead(potPin);
analogRead 、delay请参考我之前的博客
首先执行“=”右边的代码,从potPin也就是A0这个端口读入传感器的数据。然后将这个数据保存在Pos这变量中。
delay(250);
delay是延时函数,也就是说,程序运行到这里之后会暂停,就像等红绿灯一样,等多久呢?250毫秒,就是0.25秒。更深的知识会涉及到晶振等硬件,这里不做阐述,本项目中我们知道它能延时就可以了。
Serial.println(Pos);
现在从传感器中读出的数据被装在Pos中,我们现在将这个参数发送到电脑。
使用println发送,会自动发送\n,叫做转义字符,效果就是,每发一个数据,都会自动换一行。

二、简单步进电机运动代码

int xdir = 13;
int Step = 12;
int xen = 9;

void setup() {
  // put your setup code here, to run once:
  pinMode(xen, OUTPUT);
  pinMode(xdir, OUTPUT);
  pinMode(Step, OUTPUT);
  digitalWrite(xen, LOW);
  digitalWrite(xdir, LOW);
}

void loop() {
  // put your main code here, to run repeatedly:
  digitalWrite(Step, LOW);
  delay(10);
  digitalWrite(Step, HIGH);
  delay(10);
}

使用的电机驱动是:ZUM SCAN Shield
这里写图片描述
使用4线步进电机,这里不对步进电机做详细阐述。
digitalWrite(Step, LOW);
delay(10);
digitalWrite(Step, HIGH);
delay(10);
是给脉冲的意思。如果想加快转速,只需调低delay的参数。
那么digitalWrite又是什么意思呢?
在配置pinMode之后,它可以输出数字信号。那么数字信号和模拟信号又有什么区别?
可以自己百度两个关键词,一个就是数字信号模拟信号,还有一个是占空比。


Processing

一、导包&全局变量初始化

import processing.serial.*;
import processing.opengl.*;
import SimpleOpenNI.*;
import kinectOrbit.*;
//Init Orbit and OpenNI Class.
KinectOrbit myOrbit;
SimpleOpenNI kinect;
//Serial data.
Serial myPort;
boolean serial = true;
//!
String turnTableAngle = "0";
int turnAngle = 0;
float Angle = 0;
//!
//Init pointClouds and ArrayList with clolors.
ArrayList<PVector> scanPoints = new ArrayList<PVector>();//pointCloud
ArrayList<PVector> scanColors = new ArrayList<PVector>();//obj color
ArrayList<PVector> objectPoints = new ArrayList<PVector>();//pointCloud
ArrayList<PVector> objectColors = new ArrayList<PVector>();//obj color

//Height
float baseHeight  = -180;
float modelWidth  = 1000;
float modelHeight = 1000;
PVector axis = new PVector(0, baseHeight, 1200);

int scanLines = 300;
int scanRes   = 1;  //high ppx
boolean scanning = false;
boolean arrived  = false;
float[] shotNumber = new float[30];
int currentShot = 0;

int dataNum = 1;

我们需要4个库,分别是Processing自带的serial和opengl库,以及Kinect需要的SimpleOpenNI库,Kinect摄像机库kinectOrbit。

KinectOrbit myOrbit;
SimpleOpenNI kinect;
Serial myPort;
声明三个类。

boolean serial = true;
是否允许和Arduino通信的boolean型变量。

String turnTableAngle = “0”;
int turnAngle = 0;
float Angle = 0;
用于获得转盘旋转角度的变量。

ArrayList< PVector > scanPoints = new ArrayList< PVector >();
ArrayList< PVector > scanColors = new ArrayList< PVector >();
开辟动态数组,用于储存当前扫描到的点云,实时的。
ArrayList< PVector > objectPoints = new ArrayList< PVector >();
ArrayList< PVector > objectColors = new ArrayList< PVector >();
开辟动态数组,用于储存已经保存的点云,不动的。

float baseHeight = -180;
float modelWidth = 1000;
float modelHeight = 1000;
PVector axis = new PVector(0, baseHeight, 1200);
限定Kinect的扫描范围。以kinect下方180的地方为底,宽1000,高1000,深1200。

int scanLines = 300;
扫描宽度。可以理解为有300个线激光排排坐。

int scanRes = 1;
不跳过任何一个点(高精度)。

boolean scanning = false;
boolean arrived = false;
开始扫描。

float[] shotNumber = new float[30];
int currentShot = 0;
最大扫描次数。
当前扫描次数。

int dataNum = 1;
导出的文件名。

二、setup()函数

public void setup()
{
  size(800, 600, OPENGL);

  //Init orbit
  myOrbit = new KinectOrbit(this, 0, "kinect");
  myOrbit.drawCS(true);
  myOrbit.drawGizmo(true);
  myOrbit.setCSScale(200);
  myOrbit.drawGround(true);

  //Init SimpleOpenNI
  kinect = new SimpleOpenNI(this);
  kinect.setMirror(false);
  kinect.enableDepth();
  kinect.enableRGB();
  kinect.alternativeViewPointDepthToImage();

  //Serial
  if(serial) {
    String portName = Serial.list()[0];
    myPort = new Serial(this, portName, 9600);
    //!
    myPort.bufferUntil('\n');
    //!
  }
}

size(800, 600, OPENGL);
设置窗口x y是800*600像素,启用OPENGL深度显示。

myOrbit = new KinectOrbit(this, 0, “kinect”);
myOrbit.drawCS(true);
myOrbit.drawGizmo(true);
myOrbit.setCSScale(200);
myOrbit.drawGround(true);
初始化Kinect Orbit库。

kinect = new SimpleOpenNI(this);
kinect.setMirror(false);
kinect.enableDepth();
kinect.enableRGB();
kinect.alternativeViewPointDepthToImage();
初始化SimpleOpenNI库。
特别说一下最后一个:alternativeViewPointDepthToImage()方法。
这个方法会调用Kinect自带的标定功能,让彩色图像和深度图像完美结合

if(serial) {
String portName = Serial.list()[0];
myPort = new Serial(this, portName, 9600);
//!
myPort.bufferUntil(‘\n’);
//!
}
和Arduino进行通信,接收角度值。

String portName = Serial.list()[0];
获得PC——USB——Arduino的端口。

myPort = new Serial(this, portName, 9600);
波特率9600。

myPort.bufferUntil(‘\n’);
当接收到\n这个转义字符时,标志着一个数据的收发结束。

三、draw()函数

public void draw()
{
  kinect.update();
  background(0);
  myOrbit.pushOrbit(this);  //Start Orbit
  drawPointCloud(1);
  //!!!!!
  updateObject(scanLines, scanRes);
  //!!!!!
  drawObjects();
  drawBoundingBox();
  kinect.drawCamFrustum();
  myOrbit.popOrbit(this);
}

kinect.update();
background(0);
刷新一下Kinect再刷新一下整个窗口。

myOrbit.pushOrbit(this);
开始绘制摄像机模型。

drawPointCloud(1);
显示实时点云,不跳过点。跳过的话,把1改大。

updateObject(scanLines, scanRes);
核心。
选择点云模型。保证被扫描到的点在限制范围之内。

drawObjects();
同时显示已经保存的点和实时情况下的点。

drawBoundingBox();
绘制扫描范围

myOrbit.popOrbit(this);
关闭Orbit

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值