该示例演示了如何通过 API 接口对点云数据进行一些基础操作。
如何在Visual Studio、QT、CLion中使用示例工程请参考示例工程使用说明。
// std
#include <iostream>
// alson
#include <alson/classic_client.h>
int main(int argc, char** argv) {
try {
// 开启客户端日志,需要指定日志配置文件。
ALSON::Client::initLog("../../../LogConfig-Client.yaml");
// 检测所有服务端(即可用的设备),并获取其中第一个服务端的信息。
std::vector<ALSON::ServerInfo> serverInfoList = ALSON::Client::discovery();
if (serverInfoList.empty()) {
std::cerr << "can not find any server or device" << std::endl;
return -1;
}
ALSON::ServerInfo serverInfo = serverInfoList.front();
// 构造客户端并建立通信连接
ALSON::Client client;
client.connect(serverInfo.getServerNetworkCardInfo().getIp(), serverInfo.getServerNetworkCardInfo().getBindPort());
std::cout << "is connected: " << client.isConnected() << std::endl;
// 心跳时间通常设置为 2000ms 至 3000ms。在开发调试过程中,可适当设置大些。
// 如果心跳时间很大,那么当通信意外断开时,客户端和服务端都需要经历很长的时间才能感知到。
// 在此期间,客户端将阻塞您的进程,服务端不能被新的客户端连接。
client.setHeartbeatTimeout(3000);
// 创建设备控制器对象
ALSON::ClassicDeviceController deviceController = client.createDeviceController<ALSON::ClassicDeviceController>();
// 打开设备。注意无论设备是否关闭,在调用 DeviceController 中其他接口之前都必须调用 open 接口。
deviceController.open();
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// 注意:请在采集数据之前设置合适的参数
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 采集纹理图像并保存,支持多种图像格式
ALSON::AlsonMat textureImage = deviceController.grabTextureImage();
textureImage.save("./PointCloudProcess_Basic_TextureImage.bmp");
textureImage.save("./PointCloudProcess_Basic_TextureImage.png");
textureImage.save("./PointCloudProcess_Basic_TextureImage.jpg");
// 采集点云数据并保存,支持多种点云格式
ALSON::PointCloud pointCloud = deviceController.grabPointCloud();
pointCloud.save("./PointCloudProcess_Basic_PointCloud.pcd");
pointCloud.save("./PointCloudProcess_Basic_PointCloud.ply");
pointCloud.save("./PointCloudProcess_Basic_PointCloud.txt");
// 执行点云贴图,图像为未对齐的原始纹理图像。
ALSON::PointCloud pointCloudWithRGB = deviceController.addRGBForPointCloud(pointCloud, textureImage);
pointCloudWithRGB.save("./PointCloudProcess_Basic_PointCloudWithRGB.pcd");
// 执行点云贴图并计算法向。
// 注意:可以根据计算机硬件环境和性能需求来设置线程数,线程设置越小运行速度越慢。
// 较小的半径可能导致噪声敏感的法向计算,而较大的半径可能导致法向过度平滑。
double radiusSearch = 3;
int totalThreads = 6;
ALSON::PointCloud pointCloudWithRGBAndNormal = deviceController.addRGBAndNormalForPointCloud(pointCloud, textureImage, radiusSearch, totalThreads);
pointCloudWithRGBAndNormal.save("./PointCloudProcess_Basic_PointCloudWithRGBAndNormal.pcd");
// 实现根据纹理图上的区域分割点云并贴图,分割后的点云宽高都在指定的纹理图宽高范围内,下面分割出纹理图[0,0]至[1000,1000]之间的点云。
// 示例工程中设置的分割范围只适用于演示用,实际工程中可在保证参数正确的前提下灵活调整。
int totalRows = pointCloud.height;
int totalCols = pointCloud.width;
ALSON::PointCloud segmentPointCloud(pointCloud.width, pointCloud.height, ALSON::PointType::ALSON_POINT_XYZRGB);
for (int row = 0; row < totalRows; ++row) {
for (int col = 0; col < totalCols; ++col) {
auto& point = pointCloud.at<ALSON::PointXYZ>(col, row);
float x = point.x;
float y = point.y;
float z = point.z;
if (std::isnan(point.x) || std::isnan(point.y) || std::isnan(point.z)) {
segmentPointCloud.at<ALSON::PointXYZRGB>(col, row) = ALSON::PointXYZRGB(std::nanf(""), std::nanf(""), std::nanf(""), 0, 0, 0);
continue;
}
// 计算三维点对应到纹理图上的坐标
ALSON::PointUV pointUV = deviceController.pointXYZToUV(x, y, z);
int index = int(pointUV.v) * textureImage.cols + int(pointUV.u);
// 如果xyz坐标转成uv坐标在指定范围内则处理,否则置为无效值
if (pointUV.u >= 0 && pointUV.u < 1000 && pointUV.v >= 0 && pointUV.v < 1000) {
if (textureImage.channels == 3) {
// 获取指定行列的纹理图的rgb值
uint8_t* rgb = textureImage.data() + (index * 3);
segmentPointCloud.at<ALSON::PointXYZRGB>(col, row) = ALSON::PointXYZRGB(point.x, point.y, point.z, rgb[0], rgb[1], rgb[2]);
}
else if (textureImage.channels == 1) {
// 获取指定行列的纹理图的灰度值
uint8_t* rgb = textureImage.data() + index;
segmentPointCloud.at<ALSON::PointXYZRGB>(col, row) = ALSON::PointXYZRGB(point.x, point.y, point.z, *rgb, *rgb, *rgb);
}
}
else {
segmentPointCloud.at<ALSON::PointXYZRGB>(col, row) = ALSON::PointXYZRGB(std::nanf(""), std::nanf(""), std::nanf(""), 0, 0, 0);
}
}
}
segmentPointCloud.save("./PointCloudProcess_Basic_SegmentPointCloud.pcd");
// 关闭设备并断开与服务端的连接,注意 close 接口一般情况下不需要调用关闭设备,如果每次都调用,下次调用 open 的时候会比较耗时
deviceController.close();
// 如果不需要再连接设备,需要及时调用 disconnect 断开连接
client.disconnect();
}
catch (ALSON::CommonException& cause) {
// API中的所有接口都有可能抛出异常,需要按照这种方式进行捕获
std::cout << cause.getStackTrace() << std::endl;
}
return 0;
}