Mech-Eye API 2.4.0
API reference documentation for Mech-Eye 3D Laser Profiler
Loading...
Searching...
No Matches
api_util.h
1/*******************************************************************************
2 *BSD 3-Clause License
3 *
4 *Copyright (c) 2016-2024, Mech-Mind Robotics
5 *All rights reserved.
6 *
7 *Redistribution and use in source and binary forms, with or without
8 *modification, are permitted provided that the following conditions are met:
9 *
10 *1. Redistributions of source code must retain the above copyright notice, this
11 * list of conditions and the following disclaimer.
12 *
13 *2. Redistributions in binary form must reproduce the above copyright notice,
14 * this list of conditions and the following disclaimer in the documentation
15 * and/or other materials provided with the distribution.
16 *
17 *3. Neither the name of the copyright holder nor the names of its
18 * contributors may be used to endorse or promote products derived from
19 * this software without specific prior written permission.
20 *
21 *THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 *AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 *IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24 *DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
25 *FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 *DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27 *SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 *CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29 *OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 *OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 ******************************************************************************/
32
33#pragma once
34#include <fstream>
35#include <iomanip>
36#include <iostream>
37#include <regex>
38#include <set>
39#include <utility>
40#include "ErrorStatus.h"
41#include "CommonTypes.h"
42#include "profiler/parameters/ScanParameters.h"
43#include "Profiler.h"
44#include "ProfilerInfo.h"
45
46constexpr double kPitch = 1e-3;
47constexpr long long kInitEncoderValue = 0x0FFFFFFF;
48
52inline void printProfilerInfo(const mmind::eye::ProfilerInfo& profilerInfo)
53{
54 std::cout << "........................................." << std::endl;
55 std::cout << "Profiler Model Name: " << profilerInfo.model << std::endl;
56 std::cout << "Controller Serial Number: " << profilerInfo.controllerSN << std::endl;
57 std::cout << "Sensor Serial Number: " << profilerInfo.sensorSN << std::endl;
58 std::cout << "Profiler IP Address: " << profilerInfo.ipAddress << std::endl;
59 std::cout << "Profiler IP Subnet Mask: " << profilerInfo.subnetMask << std::endl;
60 std::cout << "Profiler IP Assignment Method: "
61 << mmind::eye::ipAssignmentMethodToString(profilerInfo.ipAssignmentMethod)
62 << std::endl;
63 std::cout << "Hardware Version: "
64 << "V" << profilerInfo.hardwareVersion.toString() << std::endl;
65 std::cout << "Firmware Version: "
66 << "V" << profilerInfo.firmwareVersion.toString() << std::endl;
67 std::cout << "........................................." << std::endl;
68 std::cout << std::endl;
69}
70
71inline void printProfilerStatus(const mmind::eye::ProfilerStatus& profilerStatus)
72{
73 std::cout << ".....Profiler temperatures....." << std::endl;
74 std::cout << "Controller CPU: " << std::setprecision(4)
75 << profilerStatus.temperature.controllerCpuTemperature << "°C" << std::endl;
76 std::cout << "Sensor CPU: " << std::setprecision(4)
77 << profilerStatus.temperature.sensorCpuTemperature << "°C" << std::endl;
78 std::cout << "..............................." << std::endl;
79 std::cout << std::endl;
80}
81
86inline bool findAndConnect(mmind::eye::Profiler& profiler)
87{
88 std::cout << "Find Mech-Eye 3D Laser Profilers..." << std::endl;
89 std::vector<mmind::eye::ProfilerInfo> profilerInfoList =
91
92 if (profilerInfoList.empty()) {
93 std::cout << "No Mech-Eye 3D Laser Profiler found." << std::endl;
94 return false;
95 }
96
97 for (int i = 0; i < profilerInfoList.size(); i++) {
98 std::cout << "Mech-Eye 3D Laser profiler index : " << i << std::endl;
99 printProfilerInfo(profilerInfoList[i]);
100 }
101
102 std::cout << "Please enter the profiler index you want to connect: ";
103 unsigned inputIndex = 0;
104
105 while (true) {
106 std::string str;
107 std::cin >> str;
108 if (std::regex_match(str.begin(), str.end(), std::regex{"[0-9]+"}) &&
109 atoi(str.c_str()) < profilerInfoList.size()) {
110 inputIndex = atoi(str.c_str());
111 break;
112 }
113 std::cout << "Input invalid! Please enter the profiler index you want to connect: ";
114 }
115
117 status = profiler.connect(profilerInfoList[inputIndex]);
118
119 if (!status.isOK()) {
120 showError(status);
121 return false;
122 }
123
124 std::cout << "Connect Mech-Eye 3D Laser Profiler Successfully." << std::endl;
125 return true;
126}
127
128inline std::vector<mmind::eye::Profiler> findAndConnectMultiProfiler()
129{
130 std::cout << "Find Mech-Eye 3D Laser Profilers..." << std::endl;
131 std::vector<mmind::eye::ProfilerInfo> profilerInfoList =
133
134 if (profilerInfoList.empty()) {
135 std::cout << "No Mech-Eye 3D Laser Profilers found." << std::endl;
136 return {};
137 }
138
139 for (int i = 0; i < profilerInfoList.size(); i++) {
140 std::cout << "Mech-Eye 3D Laser Profiler index : " << i << std::endl;
141 printProfilerInfo(profilerInfoList[i]);
142 }
143
144 std::string str;
145 std::set<unsigned> indices;
146
147 while (true) {
148 std::cout << "Please enter the device index you want to connect: " << std::endl;
149 std::cout << "Enter the character 'c' to terminate adding devices" << std::endl;
150
151 std::cin >> str;
152 if (str == "c")
153 break;
154 if (std::regex_match(str.begin(), str.end(), std::regex{"[0-9]+"}) &&
155 atoi(str.c_str()) < profilerInfoList.size())
156 indices.insert(atoi(str.c_str()));
157 else
158 std::cout << "Input invalid. Please enter the device index you want to connect: ";
159 }
160
161 std::vector<mmind::eye::Profiler> profilerList{};
162
163 auto iter = indices.cbegin();
164 for (int i = 0; i < indices.size(); ++i, ++iter) {
165 mmind::eye::Profiler profiler;
166 auto status = profiler.connect(profilerInfoList[*iter]);
167 if (status.isOK())
168 profilerList.push_back(profiler);
169 else
170 showError(status);
171 }
172
173 return profilerList;
174}
175
176inline bool confirmCapture()
177{
178 std::cout << "Do you want the profiler to capture image? Please input y/n to confirm: "
179 << std::endl;
180 while (true) {
181 std::string confirmStr;
182 std::cin >> confirmStr;
183 if (confirmStr == "y") {
184 return true;
185 } else if (confirmStr == "n") {
186 std::cout << "program ends!" << std::endl;
187 return false;
188 } else {
189 std::cout << "Please input y/n again!" << std::endl;
190 }
191 }
192}
193
194int shiftEncoderValsAroundZero(unsigned int oriVal, long long initValue = kInitEncoderValue)
195{
196 return static_cast<int>(oriVal - initValue);
197}
198
199bool saveDataToPly(float* data, int* yValues, int captureLineCount, int dataWidth, float xUnit,
200 float yUnit, const std::string& fileName, bool isOrganized)
201{
202 FILE* fp = fopen(fileName.c_str(), "w");
203
204 if (!fp)
205 return false;
206
207 unsigned validPointCount{0};
208 if (!isOrganized) {
209 for (int y = 0; y < captureLineCount; ++y) {
210 for (int x = 0; x < dataWidth; ++x) {
211 if (!std::isnan(data[y * dataWidth + x]))
212 validPointCount++;
213 }
214 }
215 }
216
217 fprintf(fp, "ply\n");
218 fprintf(fp, "format ascii 1.0\n");
219 fprintf(fp, "comment File generated\n");
220 fprintf(fp, "comment x y z data unit in mm\n");
221 fprintf(fp, "element vertex %u\n",
222 isOrganized ? static_cast<unsigned>(captureLineCount * dataWidth) : validPointCount);
223 fprintf(fp, "property float x\n");
224 fprintf(fp, "property float y\n");
225 fprintf(fp, "property float z\n");
226 fprintf(fp, "end_header\n");
227
228 for (int y = 0; y < captureLineCount; ++y) {
229 for (int x = 0; x < dataWidth; ++x) {
230 if (!std::isnan(data[y * dataWidth + x]))
231 fprintf(fp, "%f %f %f\n", static_cast<float>(x * xUnit * kPitch),
232 static_cast<float>(yValues[y] * yUnit * kPitch), data[y * dataWidth + x]);
233 else if (isOrganized)
234 fprintf(fp, "nan nan nan\n");
235 }
236 }
237
238 fclose(fp);
239 return true;
240}
241
242bool saveDataToCsv(float* data, int* yValues, int captureLineCount, int dataWidth, float xUnit,
243 float yUnit, const std::string& fileName, bool isOrganized)
244{
245 FILE* fp = fopen(fileName.c_str(), "w");
246
247 if (!fp)
248 return false;
249
250 fprintf(fp, "X,Y,Z\n");
251
252 for (int y = 0; y < captureLineCount; ++y) {
253 for (int x = 0; x < dataWidth; ++x) {
254 if (!std::isnan(data[y * dataWidth + x]))
255 fprintf(fp, "%f,%f,%f\n", static_cast<float>(x * xUnit * kPitch),
256 static_cast<float>(yValues[y] * yUnit * kPitch), data[y * dataWidth + x]);
257 else if (isOrganized)
258 fprintf(fp, "nan,nan,nan\n");
259 }
260 }
261
262 fclose(fp);
263 return true;
264}
265
266void savePointCloud(const mmind::eye::ProfileBatch& batch, const mmind::eye::UserSet& userSet,
267 bool savePLY = true, bool saveCSV = true, bool isOrganized = true)
268{
269 if (batch.isEmpty())
270 return;
271
272 // Get the X-axis resolution
273 double xUnit{};
274 auto status =
275 userSet.getFloatValue(mmind::eye::point_cloud_resolutions::XAxisResolution::name, xUnit);
276 if (!status.isOK()) {
277 showError(status);
278 return;
279 }
280
281 // Get the Y resolution
282 double yUnit{};
283 status = userSet.getFloatValue(mmind::eye::point_cloud_resolutions::YResolution::name, yUnit);
284 if (!status.isOK()) {
285 showError(status);
286 return;
287 }
288 // // Uncomment the following lines for custom Y Unit
289 // // Prompt to enter the desired encoder resolution, which is the travel distance corresponding
290 // // to
291 // // one quadrature signal.
292 // std::cout << "Please enter the desired encoder resolution (integer, unit: μm, min: "
293 // "1, max: 65535): ";
294 // while (true) {
295 // std::string str;
296 // std::cin >> str;
297 // if (std::regex_match(str.begin(), str.end(), std::regex{"[0-9]+"})) {
298 // yUnit = atoi(str.c_str());
299 // break;
300 // }
301 // std::cout << "Input invalid! Please enter the desired encoder resolution (integer, unit:
302 // "
303 // "μm, min: 1, max: 65535): ";
304 // }
305
306 int lineScanTriggerSource{};
307 status = userSet.getEnumValue(mmind::eye::trigger_settings::LineScanTriggerSource::name,
308 lineScanTriggerSource);
309 if (!status.isOK()) {
310 showError(status);
311 return;
312 }
313
314 bool useEncoderValues =
315 lineScanTriggerSource ==
316 static_cast<int>(mmind::eye::trigger_settings::LineScanTriggerSource::Value::Encoder);
317
318 int triggerInterval{};
319 status = userSet.getIntValue(mmind::eye::trigger_settings::EncoderTriggerInterval::name,
320 triggerInterval);
321 if (!status.isOK()) {
322 showError(status);
323 return;
324 }
325
326 // Shift the encoder values around zero
327 std::vector<int> encoderVals;
328 encoderVals.reserve(batch.height());
329 auto encoder = batch.getEncoderArray();
330 for (int r = 0; r < batch.height(); ++r)
331 encoderVals.push_back(
332 useEncoderValues ? shiftEncoderValsAroundZero(encoder[r], encoder[0]) / triggerInterval
333 : r);
334
335 std::cout << "Save the point cloud." << std::endl;
336 if (saveCSV)
337 saveDataToCsv(batch.getDepthMap().data(), encoderVals.data(), batch.height(), batch.width(),
338 xUnit, yUnit, "PointCloud.csv", isOrganized);
339 if (savePLY)
340 saveDataToPly(batch.getDepthMap().data(), encoderVals.data(), batch.height(), batch.width(),
341 xUnit, yUnit, "PointCloud.ply", isOrganized);
342}
const ElementData * data() const
Returns the pointer to the element data.
Definition BatchArray.h:96
Represents a batch of profiles, which can be obtained by calling Profiler::retrieveBatchData()....
Definition ProfileData.h:30
bool isEmpty() const
Checks if the ProfileBatch object has no elements.
EncoderArray getEncoderArray() const
Gets an array of encoder values of all profiles in the batch. Each profile data corresponds to an enc...
size_t width() const
Returns the width of the ProfileBatch object (the number of data points per profile).
size_t height() const
Returns the height of the ProfileBatch object (the number of profiles in the batch).
DepthMap getDepthMap() const
Gets the depth map data in the batch. Each point in DepthMap contains the Z information in the laser ...
Operates the laser profiler. Use Profiler::connect to connect an available laser profiler,...
Definition Profiler.h:68
static std::vector< ProfilerInfo > discoverProfilers()
Discovers all available laser profilers, and returns the laser profiler information list....
ErrorStatus connect(const ProfilerInfo &info, unsigned int timeoutMs=5000)
Connects to a laser profiler via ProfilerInfo.
ErrorStatus getIntValue(const std::string &parameterName, int &value) const
Gets the current value of an _Int-type device parameter. See Parameter for details.
ErrorStatus getFloatValue(const std::string &parameterName, double &value) const
Gets the current value of a _Float-type device parameter. See Parameter for details.
ErrorStatus getEnumValue(const std::string &parameterName, int &value) const
Gets the current value of an _Enum-type device parameter in the form of the integer value....
std::string toString() const
Converts a Version object to a string.
Definition Version.h:71
Describes the types of errors.
Definition ErrorStatus.h:12
bool isOK() const
Returns true if the operation succeeded.
Definition ErrorStatus.h:84
Describes the laser profiler information.
Version hardwareVersion
The version of the hardware. The hardware cannot be upgraded.
Version firmwareVersion
The version of the firmware. The firmware can be upgraded.
std::string subnetMask
The IP subnet mask of the laser profiler.
IpAssignmentMethod ipAssignmentMethod
The IP address assignment method of the laser profiler.
std::string model
The laser profiler model.
std::string controllerSN
The controller serial number.
std::string sensorSN
The sensor serial number.
std::string ipAddress
The IP address of the laser profiler.
Describes the laser profiler's statuses.
Definition Profiler.h:58
float controllerCpuTemperature
The temperature (in °C) of the controller CPU.
Definition Profiler.h:50
float sensorCpuTemperature
The temperature (in °C) of the FPGA.
Definition Profiler.h:51