[TSG开发]法如扫描仪SDK探幽-1.旧版SDK采集流程、问题解析、常见参数

做什么

法如扫描仪是一个三维的激光扫描仪,可以通过特定的作业模式将空间以三维激光点云的形式保存下来,并且通过特定的算法得出一些想要的具体参数。

这个SDK探幽日志主要是对目前SDK开发中遇到的一些问题做个记录,以及对未来开发的一些指导,只是在业余时间简单写写,之后还会深入探索与合作。算是一个把FARO官方文档吃进去再吐出来的一个过程。

简单看了一下官方文档,稍微记录一下FARO旧版SDK的开发指南。

首先我们要知道法如的SDK是什么东西,我们可以看一下官方示例:

我们可以看到,法如执行一次扫描任务,代码量并不大,但是这到底是个什么鬼?

没错,就是在C++上的#import命令,看上去很奇葩,而且不需要#include 一个头文件,为什么就突然多一个类出来了?我们来巨硬的官方文档看看,这是什么意思?

#import 指令 (C++)

#imprtC++:专用。过去一直合并类型库中的信息。 类型库的内容将转换为 C++ 类,主要描述 COM 接口。

我们可以简单看一下FARO的官方文档

如上很明显可以看出FARO的SDK实际上就是通过注册COM组件的形式将自己的DLL注册进了window系统内,这也难怪FARO一直没有开发linux下的SDK。。。。毕竟这是要推倒重做的事,不过最近又出了新的API,之后需要参考一下这个API。.

怎么做

从目前的代码出发,我会简单聊聊FARO扫描仪的螺旋CAN式扫描仪的用法及流程。

实际上从目前的作业流程来说,和官方文档给出的示例文档差别不大,大概流程:

1.初始化法如SDK,获取COM对象

2.设置IP,证书,密钥等信息,调用接口连接至法如扫描仪

3.设置扫描参数

4.启动法如扫描仪预热,开始旋转

5.准备完毕后让法如扫描仪进入数据记录状态,这个时候才开始生成数据:

6.采集完毕,停止扫描仪

7.脱离控制,当然了也可以直接退出进程,COM组件消失后就自动放开控制。

sdk中常用接口及其作用见下表,更完整请看官方文档:

CoInitialize(NULL)	初始化SDK服务
BSTR licenseCode = ... /* FARO LS license code */;	插入扫描仪的秘钥
IiQLibIfPtr libRef = static_cast<IiQLibIfPtr>(liPtr);	拿到提供服务的指针
scanCtrl->ScannerIP = L"132.154.24.13";	设置扫描仪ip
connect()	与扫描仪建立连接
syncParam()	设置完参数后用于同步参数至扫描仪
startScan()	启动镜头(镜头旋转,但没正式记录)
recordScan()	开始记录数据
pauseScan()	暂停数据记录,镜头保持旋转
stopScan()	停止记录,停止镜头
CoUninitialize()	卸载服务

sdk中常用参数及其含义见下表,更完整请看官方文档:

ScanMode	枚举值。移动式扫描一般使用HelicalCANGrey,架站式可选StationaryGrey、StationaryColor。
StorageMode	枚举值。SMLocal:数据保存在扫描仪的sd卡中或是内置硬盘;SMRemote:数据保存在于扫描仪建立连接的远程电脑;SMAuto:自动选择保存位置,优先保存在连接的远程电脑。
ScanFileNumber	设置扫描文件的起始文件名。e.g.设置为1,则以后的文件会自动命名为2、3、4...
ScanBaseName	设置扫描文件的文件名前缀。e.g.设置为Test,则扫描文件名会变成Test001.fls、Test002.fls、Test003.fls...
RemoteScanStoragePath	若选择了将数据保存在远程电脑,此属性就是设置具体保存的文件夹路径
Resolution	设置扫描分辨率,有多个档位设置,此值与最终扫描成果的精度有关
MeasurementRate	设置测量速率,有多个档位设置,此值与最终扫描成果的精度有关
NoiseCompression	设置噪声压缩,有多个档位设置,一般设置为1,即不压缩
VerticalAngleMax	竖直方向最大角度,一般为90
VerticalAngleMin	竖直方向最小角度,一般为-60
NumCols	最大扫描线数,当扫描过程达到此值,会自动结束扫描,一般设为2000000
SplitAfterLines	分块线数,扫描文件中的线数达到此值,会自动将数据保存在下一个文件。可理解为单文件最大容量属性。一般设置为5000

什么问题?

目前最严重的问题就是,法如提供的这几个COM接口的返回值并不可靠,就比如说,我调用了startScan()之后,可能上方的灯闪烁,但是并不会开始旋转,但是这个时候COM口返回给我的值却没有任何问题,这是非常奇怪的,但是也有办法可以解决。几个比较常见的问题解决方案如下:

1.无法从connect()函数返回值判断扫描仪是否完成连接

答:需要通过查询Connected属性(bool类型)判断,当然了最简单的方法是直接看法如扫描仪上的界面,但是这显然是不合适的,怎么还能让用户亲自去看Connected属性,这不是开玩笑吗?

2.无法从startScan()函数返回值判断扫描仪镜头是否启动成功,灯闪蓝灯了,但是没有转起来?

以下回答显然是不可接受的,但是目前没有好办法解决,之后深入研究SDK后需要解决这个问题

答:1.人工观察扫描仪顶部的灯由蓝色常量变为红色闪烁,即镜头启动成功。2.据说还有一种方式,镜头启动之后(电子组)能从扫描仪的CAN口获取到镜头启动信息,具体的消息形式可找他们探讨一下,确定可用的话,软件这边从串口获取消息即可。

3.无法从stopScan()函数返回值判断扫描仪是否已正式结束记录

答:当拿到返回值之后直接卸载服务,会出现最后一个文件损坏的情况。据观察,stopScan之后要过一会才会将文件的最后一部分完整写入。可通过ScanProgress属性来查询扫描进度,当返回值是0时,一般就正式结束了。

4.开始记录数据后,数据有可能不生成

答:这个原因很复杂,但是主要出现这个问题的原因就是目前VTM软件的代码有问题,开始任务后扫描仪不一定真启动了,可能没有调用记录数据的这个接口。

实际上可以通过法如的SDK调用他们读取fls文件的接口,可以检查是否有数据生成,如果没有数据生成再报错。

配上一段Faro SDK读取fls文件的代码

define _SCL_SECURE_NO_WARNINGS

define _CRT_SECURE_NO_WARNINGS

include

//#include <atlsafe.h>
//#include <windows.h>
//#include

include <pcl/point_cloud.h>

include <pcl/io/pcd_io.h>

include <pcl/visualization/pcl_visualizer.h>

using namespace std;
typedef pcl::PointXYZRGBA PointT;
typedef pcl::PointCloud PointCloudT;

ifdef _WIN64

// Yes - type is 'win32' even on WIN64!

pragma comment(linker, ""/manifestdependency:type='win32' name='FARO.LS' version='1.1.701.2' processorArchitecture='amd64' publicKeyToken='1d23f5635ba800ab'"")

else

pragma comment(linker, ""/manifestdependency:type='win32' name='FARO.LS' version='1.1.701.2' processorArchitecture='amd64' publicKeyToken='1d23f5635ba800ab'"")

endif

// These imports just defines the types - they don't determine which DLLs are actually loaded!
// You can choose whatever version you have installed on your system - as long as the interface is compatible

import "C:\Windows\WinSxS\amd64_faro.ls_1d23f5635ba800ab_1.1.701.2_none_3592adf9356a0308\iQopen.dll" no_namespace

//...

int main()
{
CoInitialize(NULL); //应用程序调用com库函数(除CoGetMalloc和内存分配函数)之前必须初始化com库
// FARO LS Licensing
// Please note: The cryptic key is only a part of the complete license
// string you need to unlock the interface. Please
// follow the instructions in the 2nd line below

BSTR licenseCode = L"FARO Open Runtime License\n"
    //#include "../FAROOpenLicense"    // Delete this line, uncomment the following line, and enter your own license key here:
    L"Key:39CELNPKTCTXXJ7TN3ZYSPVPL\n"
    L"\n"
    L"The software is the registered property of FARO Scanner Production GmbH, Stuttgart, Germany.\n"
    L"All rights reserved.\n"
    L"This software may only be used with written permission of FARO Scanner Production GmbH, Stuttgart, Germany.";

IiQLicensedInterfaceIfPtr liPtr(__uuidof(iQLibIf));
liPtr->License = licenseCode;
IiQLibIfPtr libRef = static_cast<IiQLibIfPtr>(liPtr);
if (libRef->load("C:\\Users\\18148\\Desktop\\Scan_az001.fls\\Scan_az001.fls") != 0)   //读取文件的全路径  切记
{
    std::cout << "load  ScanData errer !!!" << std::endl;
    libRef = NULL;
    liPtr = NULL;
    return -1;
}

libRef->scanReflectionMode = 2;            //黑白灰度展示图像
cout << libRef->scanReflectionMode << endl;
//libRef->scanReflectionMode = 1;    //默认为1  可以为0 1 2三个模式。

int ret = libRef->setAttribute("#app/ScanLoadColor", "2");    //设置为彩色 Load grey information instead of color
cout << "setAttribute" << ret << endl;

//if (int ret = libRef->saveAs("C:\\Users\\18148\\Desktop\\img\\ddb123.fws") != 0)    //可以存储为  fls,fws
//{
//    std::cout << "saveAs  ScanData errer !!!" << std::endl;
//    return -1;
//}
//ret = libRef->extractStream("C:\\Users\\18148\\Desktop\\Scan_az001.fls\\Scan_az001.fls", "ScanDataStream0", "C:\\Users\\18148\\Desktop\\img.fls");

double x, y, z;
double rx, ry, rz, angle;
libRef->getScanPosition(0, &x, &y, &z);
libRef->getScanOrientation(0, &rx, &ry, &rz, &angle);
int numRows = libRef->getScanNumRows(0);
int numCOls = libRef->getScanNumCols(0);

std::cout << "numRows::" << numRows << std::endl;
std::cout << "numCOls::" << numCOls << std::endl;
std::cout << x << "," << y << "," << z << std::endl;
std::cout << rx << "," << ry << "," << rz << "," << angle << std::endl;

PointCloudT::Ptr cloud_pointsPtr(new PointCloudT());
// Access all points points by point
for (int col = 0; col < numCOls; col++)
{
    for (int row = 0; row<numRows; row++)
    {
        double x, y, z;
        int refl;
        int result = libRef->getScanPoint(0, row, col, &x, &y, &z, &refl);

        //Use getXYZScanPoints or getXYZScanPoints2 instead to read multiple scan points in a single call. For example, you can read the scan points column by column with these two methods.
        PointT points;
        points.x = x;
        points.y = y;
        points.z = z;
        //int rgb = ((int)r) << 16 | ((int)g) << 8 | ((int)b);
        points.a = 255;
        points.r = (refl >> 16) & 0x0000ff; //uint8_t r = (rgb >> 16) & 0x0000ff;
        points.g = (refl >> 8) & 0x0000ff;
        points.b = (refl)& 0x0000ff;
        points.rgba = points.a << 24 | points.r << 16 | points.g << 8 | points.b;
        cloud_pointsPtr->points.push_back(points);

        /*        double **array2D = new double *[numRows];    //二维数组分配内存
        for (int i = 0; i<numRows; ++i)
        {
        array2D[i] = new double[3];
        }
        double *pos = *array2D;
        */
        //double *pos = new double[3 * numRows];    //利用一维数组
        //int *refl = new int[numRows];
        //int result = libRef->getXYZScanPoints(0, row, col, numRows, pos, refl);

        std::cout << x<<","<<y<<","<<z<< std::endl;

        //delete[] pos;
        //delete[] refl;
        //pos = NULL; refl = NULL;
    }
}

// --------------------------------------------
// -----Open 3D viewer and add point cloud-----
// --------------------------------------------
boost::shared_ptr<pcl::visualization::PCLVisualizer> viewer(new pcl::visualization::PCLVisualizer("3D Viewer"));
viewer->setBackgroundColor(0, 0, 0);
pcl::visualization::PointCloudColorHandlerRGBField<PointT> rgb(cloud_pointsPtr);
//pcl::visualization::PointCloudColorHandlerCustom<PointT> red(cloud_pointsPtr, 0, 0, 255);
viewer->addPointCloud<PointT>(cloud_pointsPtr, "sample cloud");
viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 1, "sample cloud");
viewer->addCoordinateSystem(1.0);
viewer->initCameraParameters();

// Access all points column per column in polar coordinates
//double* positions = new double[numRows*3];
//int* reflections = new int[numRows];
//for (int col = 0; col<numCOls; col++)
//{
//    int result = libRef->getPolarScanPoints(0, 0, col, numRows, positions, reflections);
//    for (int row=0 ; row<numRows ; row++)
//    {
//        double r, phi, theta;
//        int refl;
//        r = positions[3*row+0];
//        phi = positions[3*row + 1];
//        theta = positions[3*row+2];
//        refl = reflections[row];     // ...
//    }
//}
//delete[] positions; delete[] reflections;

libRef = NULL; liPtr = NULL;
CoUninitialize();
//--------------------
// -----Main loop-----
//--------------------

while (!viewer->wasStopped())
{
    viewer->spinOnce(100);
    boost::this_thread::sleep(boost::posix_time::microseconds(100000));
}
system("pause");
return 0;

}


标签: c++

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
根据你提供的代码信息 `foreach(mo_env.mo_regmodel.ulch.CPU_TSG_CONFIG[i,j])`,它是使用 `foreach` 遍历多维数组 `mo_env.mo_regmodel.ulch.CPU_TSG_CONFIG` 的语法。然而,根据 Verilog 的语法规则,使用逗号 `,` 来分隔多维数组的索引是无效的。 Verilog 中,多维数组的索引应该使用方括号 `[]` 来表示各个维度的索引。在你的代码中,应该修改为 `foreach(mo_env.mo_regmodel.ulch.CPU_TSG_CONFIG[i][j])`,以正确遍历二维数组 `mo_env.mo_regmodel.ulch.CPU_TSG_CONFIG` 的元素。 请注意,根据你的代码,`mo_env.mo_regmodel.ulch.CPU_TSG_CONFIG` 应该是一个二维数组,`i` 和 `j` 是循环变量,分别表示当前元素的行和列索引。你可以在 `foreach` 循环体内部使用 `i` 和 `j` 来访问当前元素,并进行相应的操作。 修正后的示例代码如下: ```verilog module example; reg [7:0] CPU_TSG_CONFIG [3:0][3:0]; // 4x4 的二维数组 initial begin // 初始化二维数组 for (int i = 0; i < 4; i = i + 1) begin for (int j = 0; j < 4; j = j + 1) begin CPU_TSG_CONFIG[i][j] = i*4 + j; end end // 使用 foreach 语句遍历二维数组 foreach (CPU_TSG_CONFIG[i][j]) begin $display("CPU_TSG_CONFIG[%0d][%0d] = %0d", i, j, CPU_TSG_CONFIG[i][j]); end $finish; end endmodule ``` 在这个修正后的示例代码中,我们定义了一个 4x4 的二维数组 `CPU_TSG_CONFIG`,使用 `foreach` 遍历数组中的每个元素。循环变量 `i` 和 `j` 分别表示当前元素的行和列索引。在循环体内部,我们可以使用 `i` 和 `j` 来访问当前元素的索引,并进行相应的操作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值