rawdata.cc 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867
  1. /*
  2. * Copyright (C) 2007 Austin Robot Technology, Patrick Beeson
  3. * Copyright (C) 2009, 2010, 2012 Austin Robot Technology, Jack O'Quin
  4. * Copyright (C) 2019, Kaarta Inc, Shawn Hanna
  5. *
  6. * License: Modified BSD Software License Agreement
  7. *
  8. * $Id$
  9. */
  10. /**
  11. * @file
  12. *
  13. * Velodyne 3D LIDAR data accessor class implementation.
  14. *
  15. * Class for unpacking raw Velodyne LIDAR packets into useful
  16. * formats.
  17. *
  18. * Derived classes accept raw Velodyne data for either single packets
  19. * or entire rotations, and provide it in various formats for either
  20. * on-line or off-line processing.
  21. *
  22. * @author Patrick Beeson
  23. * @author Jack O'Quin
  24. * @author Shawn Hanna
  25. *
  26. * HDL-64E S2 calibration support provided by Nick Hillier
  27. */
  28. #include <fstream>
  29. #include <math.h>
  30. #include "angles.h"
  31. #include "rawdata.h"
  32. namespace velodyne_rawdata
  33. {
  34. inline float SQR(float val) { return val * val; }
  35. ////////////////////////////////////////////////////////////////////////
  36. //
  37. // RawData base class implementation
  38. //
  39. ////////////////////////////////////////////////////////////////////////
  40. RawData::RawData() {}
  41. /** Update parameters: conversions and update */
  42. void RawData::setParameters(double min_range,
  43. double max_range,
  44. double view_direction,
  45. double view_width)
  46. {
  47. config_.min_range = min_range;
  48. config_.max_range = max_range;
  49. //converting angle parameters into the velodyne reference (rad)
  50. config_.tmp_min_angle = view_direction + view_width / 2;
  51. config_.tmp_max_angle = view_direction - view_width / 2;
  52. //computing positive modulo to keep theses angles into [0;2*M_PI]
  53. config_.tmp_min_angle = fmod(fmod(config_.tmp_min_angle, 2 * M_PI) + 2 * M_PI, 2 * M_PI);
  54. config_.tmp_max_angle = fmod(fmod(config_.tmp_max_angle, 2 * M_PI) + 2 * M_PI, 2 * M_PI);
  55. //converting into the hardware velodyne ref (negative yaml and degrees)
  56. //adding 0.5 perfomrs a centered double to int conversion
  57. config_.min_angle = 100 * (2 * M_PI - config_.tmp_min_angle) * 180 / M_PI + 0.5;
  58. config_.max_angle = 100 * (2 * M_PI - config_.tmp_max_angle) * 180 / M_PI + 0.5;
  59. if (config_.min_angle == config_.max_angle)
  60. {
  61. //avoid returning empty cloud if min_angle = max_angle
  62. config_.min_angle = 0;
  63. config_.max_angle = 36000;
  64. }
  65. }
  66. int RawData::scansPerPacket() const
  67. {
  68. if (calibration_.num_lasers == 16)
  69. {
  70. return BLOCKS_PER_PACKET * VLP16_FIRINGS_PER_BLOCK *
  71. VLP16_SCANS_PER_FIRING;
  72. }
  73. else
  74. {
  75. return BLOCKS_PER_PACKET * SCANS_PER_BLOCK;
  76. }
  77. }
  78. /**
  79. * Build a timing table for each block/firing. Stores in timing_offsets vector
  80. */
  81. bool RawData::buildTimings()
  82. {
  83. // vlp16
  84. if (config_.model == "VLP16")
  85. {
  86. // timing table calculation, from velodyne user manual
  87. timing_offsets.resize(12);
  88. for (size_t i = 0; i < timing_offsets.size(); ++i)
  89. {
  90. timing_offsets[i].resize(32);
  91. }
  92. // constants
  93. double full_firing_cycle = 55.296 * 1e-6; // seconds
  94. double single_firing = 2.304 * 1e-6; // seconds
  95. double dataBlockIndex, dataPointIndex;
  96. bool dual_mode = false;
  97. // compute timing offsets
  98. for (size_t x = 0; x < timing_offsets.size(); ++x)
  99. {
  100. for (size_t y = 0; y < timing_offsets[x].size(); ++y)
  101. {
  102. if (dual_mode)
  103. {
  104. dataBlockIndex = (x - (x % 2)) + (y / 16);
  105. }
  106. else
  107. {
  108. dataBlockIndex = (x * 2) + (y / 16);
  109. }
  110. dataPointIndex = y % 16;
  111. //timing_offsets[block][firing]
  112. timing_offsets[x][y] = (full_firing_cycle * dataBlockIndex) + (single_firing * dataPointIndex);
  113. }
  114. }
  115. }
  116. // vlp32
  117. else if (config_.model == "32C")
  118. {
  119. // timing table calculation, from velodyne user manual
  120. timing_offsets.resize(12);
  121. for (size_t i = 0; i < timing_offsets.size(); ++i)
  122. {
  123. timing_offsets[i].resize(32);
  124. }
  125. // constants
  126. double full_firing_cycle = 55.296 * 1e-6; // seconds
  127. double single_firing = 2.304 * 1e-6; // seconds
  128. double dataBlockIndex, dataPointIndex;
  129. bool dual_mode = false;
  130. // compute timing offsets
  131. for (size_t x = 0; x < timing_offsets.size(); ++x)
  132. {
  133. for (size_t y = 0; y < timing_offsets[x].size(); ++y)
  134. {
  135. if (dual_mode)
  136. {
  137. dataBlockIndex = x / 2;
  138. }
  139. else
  140. {
  141. dataBlockIndex = x;
  142. }
  143. dataPointIndex = y / 2;
  144. timing_offsets[x][y] = (full_firing_cycle * dataBlockIndex) + (single_firing * dataPointIndex);
  145. }
  146. }
  147. }
  148. // hdl32
  149. else if (config_.model == "32E")
  150. {
  151. // timing table calculation, from velodyne user manual
  152. timing_offsets.resize(12);
  153. for (size_t i = 0; i < timing_offsets.size(); ++i)
  154. {
  155. timing_offsets[i].resize(32);
  156. }
  157. // constants
  158. double full_firing_cycle = 46.080 * 1e-6; // seconds
  159. double single_firing = 1.152 * 1e-6; // seconds
  160. double dataBlockIndex, dataPointIndex;
  161. bool dual_mode = false;
  162. // compute timing offsets
  163. for (size_t x = 0; x < timing_offsets.size(); ++x)
  164. {
  165. for (size_t y = 0; y < timing_offsets[x].size(); ++y)
  166. {
  167. if (dual_mode)
  168. {
  169. dataBlockIndex = x / 2;
  170. }
  171. else
  172. {
  173. dataBlockIndex = x;
  174. }
  175. dataPointIndex = y / 2;
  176. timing_offsets[x][y] = (full_firing_cycle * dataBlockIndex) + (single_firing * dataPointIndex);
  177. }
  178. }
  179. }
  180. else if (config_.model == "VLS128")
  181. {
  182. timing_offsets.resize(3);
  183. for (size_t i = 0; i < timing_offsets.size(); ++i)
  184. {
  185. timing_offsets[i].resize(17); // 17 (+1 for the maintenance time after firing group 8)
  186. }
  187. double full_firing_cycle = VLS128_SEQ_TDURATION * 1e-6; //seconds
  188. double single_firing = VLS128_CHANNEL_TDURATION * 1e-6; // seconds
  189. double offset_paket_time = VLS128_TOH_ADJUSTMENT * 1e-6; //seconds
  190. double sequenceIndex, firingGroupIndex;
  191. // Compute timing offsets
  192. for (size_t x = 0; x < timing_offsets.size(); ++x)
  193. {
  194. for (size_t y = 0; y < timing_offsets[x].size(); ++y)
  195. {
  196. sequenceIndex = x;
  197. firingGroupIndex = y;
  198. timing_offsets[x][y] = (full_firing_cycle * sequenceIndex) + (single_firing * firingGroupIndex) - offset_paket_time;
  199. LOG(INFO) << " firing_seque" << x << " firing_group" << y << "offset" << timing_offsets[x][y];
  200. }
  201. }
  202. }
  203. else
  204. {
  205. timing_offsets.clear();
  206. LOG(WARNING) << "Timings not supported for model" << config_.model.c_str();
  207. }
  208. if (timing_offsets.size())
  209. {
  210. // ROS_INFO("VELODYNE TIMING TABLE:");
  211. for (size_t x = 0; x < timing_offsets.size(); ++x)
  212. {
  213. for (size_t y = 0; y < timing_offsets[x].size(); ++y)
  214. {
  215. printf("%04.3f ", timing_offsets[x][y] * 1e6);
  216. }
  217. printf("\n");
  218. }
  219. return true;
  220. }
  221. else
  222. {
  223. LOG(WARNING) << "NO TIMING OFFSETS CALCULATED. ARE YOU USING A SUPPORTED VELODYNE SENSOR?";
  224. }
  225. return false;
  226. }
  227. /** Set up for on-line operation. */
  228. bool RawData::setup(std::string model, std::string calib_file, double max_range, double min_range, int min_angle, int max_angle)
  229. {
  230. config_.model = model;
  231. config_.calibrationFile = calib_file;
  232. config_.max_range = max_range;
  233. config_.min_range = min_range;
  234. config_.min_angle = min_angle*100;
  235. config_.max_angle = max_angle*100;
  236. buildTimings();
  237. // // get path to angles.config file for this device
  238. // if (!private_nh.getParam("calibration", config_.calibrationFile))
  239. // {
  240. // ROS_ERROR_STREAM("No calibration angles specified! Using test values!");
  241. // // have to use something: grab unit test version as a default
  242. // std::string pkgPath = ros::package::getPath("velodyne_pointcloud");
  243. // config_.calibrationFile = pkgPath + "/params/64e_utexas.yaml";
  244. // }
  245. // ROS_INFO_STREAM("correction angles: " << config_.calibrationFile);
  246. if (!loadCalibration())
  247. {
  248. mb_initialized = false;
  249. return false;
  250. }
  251. // ROS_INFO_STREAM("Number of lasers: " << calibration_.num_lasers << ".");
  252. setupSinCosCache();
  253. setupAzimuthCache();
  254. mb_initialized = true;
  255. return true;
  256. }
  257. /** Set up for offline operation */
  258. int RawData::setupOffline(std::string calibration_file, double max_range_, double min_range_)
  259. {
  260. config_.max_range = max_range_;
  261. config_.min_range = min_range_;
  262. LOG(INFO) << "data ranges to publish: ["
  263. << config_.min_range << ", "
  264. << config_.max_range << "]";
  265. config_.calibrationFile = calibration_file;
  266. LOG(INFO) << "correction angles: " << config_.calibrationFile;
  267. if (!loadCalibration())
  268. {
  269. return -1;
  270. }
  271. LOG(INFO) << "Number of lasers: " << calibration_.num_lasers << ".";
  272. setupSinCosCache();
  273. setupAzimuthCache();
  274. return 0;
  275. }
  276. bool RawData::loadCalibration()
  277. {
  278. calibration_.read(config_.calibrationFile);
  279. if (!calibration_.initialized)
  280. {
  281. LOG(ERROR) << "Unable to open calibration file: " << config_.calibrationFile;
  282. return false;
  283. }
  284. return true;
  285. }
  286. void RawData::setupSinCosCache()
  287. {
  288. // Set up cached values for sin and cos of all the possible headings
  289. for (uint16_t rot_index = 0; rot_index < ROTATION_MAX_UNITS; ++rot_index)
  290. {
  291. float rotation = angles::from_degrees(ROTATION_RESOLUTION * rot_index);
  292. cos_rot_table_[rot_index] = cosf(rotation);
  293. sin_rot_table_[rot_index] = sinf(rotation);
  294. }
  295. if (config_.model == "VLS128")
  296. {
  297. for (uint8_t i = 0; i < 16; i++)
  298. {
  299. vls_128_laser_azimuth_cache[i] =
  300. (VLS128_CHANNEL_TDURATION / VLS128_SEQ_TDURATION) * (i + i / 8);
  301. }
  302. }
  303. }
  304. void RawData::setupAzimuthCache()
  305. {
  306. if (config_.model == "VLS128")
  307. {
  308. for (uint8_t i = 0; i < 16; i++)
  309. {
  310. vls_128_laser_azimuth_cache[i] =
  311. (VLS128_CHANNEL_TDURATION / VLS128_SEQ_TDURATION) * (i + i / 8);
  312. }
  313. }
  314. else
  315. {
  316. LOG(WARNING) << "No Azimuth Cache configured for model " << config_.model.c_str();
  317. }
  318. }
  319. /** @brief convert raw packet to point cloud
  320. *
  321. * @param pkt raw packet to unpack
  322. * @param pc shared pointer to point cloud (points are appended)
  323. */
  324. void RawData::unpack(const unsigned char *pkt, std::vector<Lidar_point> &data)
  325. {
  326. using velodyne_pointcloud::LaserCorrection;
  327. // LOG(INFO)<<"Received packet";
  328. /** special parsing for the VLS128 **/
  329. if (pkt[1205] == VLS128_MODEL_ID)
  330. { // VLS 128
  331. unpack_vls128(pkt, data);
  332. return;
  333. }
  334. /** special parsing for the VLP16 **/
  335. if (calibration_.num_lasers == 16)
  336. {
  337. unpack_vlp16(pkt, data);
  338. return;
  339. }
  340. // float time_diff_start_to_this_packet = (pkt.stamp - scan_start_time).toSec();
  341. const raw_packet_t *raw = (const raw_packet_t *)&pkt[0];
  342. for (int i = 0; i < BLOCKS_PER_PACKET; i++)
  343. {
  344. // upper bank lasers are numbered [0..31]
  345. // NOTE: this is a change from the old velodyne_common implementation
  346. int bank_origin = 0;
  347. if (raw->blocks[i].header == LOWER_BANK)
  348. {
  349. // lower bank lasers are [32..63]
  350. bank_origin = 32;
  351. }
  352. for (int j = 0, k = 0; j < SCANS_PER_BLOCK; j++, k += RAW_SCAN_SIZE)
  353. {
  354. float x, y, z;
  355. float intensity;
  356. const uint8_t laser_number = j + bank_origin;
  357. float time = 0;
  358. const LaserCorrection &corrections = calibration_.laser_corrections[laser_number];
  359. /** Position Calculation */
  360. const raw_block_t &block = raw->blocks[i];
  361. union two_bytes tmp;
  362. tmp.bytes[0] = block.data[k];
  363. tmp.bytes[1] = block.data[k + 1];
  364. /*condition added to avoid calculating points which are not
  365. in the interesting defined area (min_angle < area < max_angle)*/
  366. if ((block.rotation >= config_.min_angle && block.rotation <= config_.max_angle && config_.min_angle < config_.max_angle) || (config_.min_angle > config_.max_angle && (raw->blocks[i].rotation <= config_.max_angle || raw->blocks[i].rotation >= config_.min_angle)))
  367. {
  368. if (timing_offsets.size())
  369. {
  370. time = timing_offsets[i][j]; // +time_diff_start_to_this_packet;
  371. }
  372. if (tmp.uint == 0) // no valid laser beam return
  373. {
  374. // call to addPoint is still required since output could be organized
  375. data.push_back(Lidar_point(0.0f, 0.0f, 0.0f, corrections.laser_ring, raw->blocks[i].rotation, 0.0f, 0.0f, time));
  376. continue;
  377. }
  378. float distance = tmp.uint * calibration_.distance_resolution_m;
  379. distance += corrections.dist_correction;
  380. float cos_vert_angle = corrections.cos_vert_correction;
  381. float sin_vert_angle = corrections.sin_vert_correction;
  382. float cos_rot_correction = corrections.cos_rot_correction;
  383. float sin_rot_correction = corrections.sin_rot_correction;
  384. // cos(a-b) = cos(a)*cos(b) + sin(a)*sin(b)
  385. // sin(a-b) = sin(a)*cos(b) - cos(a)*sin(b)
  386. float cos_rot_angle =
  387. cos_rot_table_[block.rotation] * cos_rot_correction +
  388. sin_rot_table_[block.rotation] * sin_rot_correction;
  389. float sin_rot_angle =
  390. sin_rot_table_[block.rotation] * cos_rot_correction -
  391. cos_rot_table_[block.rotation] * sin_rot_correction;
  392. float horiz_offset = corrections.horiz_offset_correction;
  393. float vert_offset = corrections.vert_offset_correction;
  394. // Compute the distance in the xy plane (w/o accounting for rotation)
  395. /**the new term of 'vert_offset * sin_vert_angle'
  396. * was added to the expression due to the mathemathical
  397. * model we used.
  398. */
  399. float xy_distance = distance * cos_vert_angle - vert_offset * sin_vert_angle;
  400. // Calculate temporal X, use absolute value.
  401. float xx = xy_distance * sin_rot_angle - horiz_offset * cos_rot_angle;
  402. // Calculate temporal Y, use absolute value
  403. float yy = xy_distance * cos_rot_angle + horiz_offset * sin_rot_angle;
  404. if (xx < 0)
  405. xx = -xx;
  406. if (yy < 0)
  407. yy = -yy;
  408. // Get 2points calibration values,Linear interpolation to get distance
  409. // correction for X and Y, that means distance correction use
  410. // different value at different distance
  411. float distance_corr_x = 0;
  412. float distance_corr_y = 0;
  413. if (corrections.two_pt_correction_available)
  414. {
  415. distance_corr_x =
  416. (corrections.dist_correction - corrections.dist_correction_x) * (xx - 2.4) / (25.04 - 2.4) + corrections.dist_correction_x;
  417. distance_corr_x -= corrections.dist_correction;
  418. distance_corr_y =
  419. (corrections.dist_correction - corrections.dist_correction_y) * (yy - 1.93) / (25.04 - 1.93) + corrections.dist_correction_y;
  420. distance_corr_y -= corrections.dist_correction;
  421. }
  422. float distance_x = distance + distance_corr_x;
  423. /**the new term of 'vert_offset * sin_vert_angle'
  424. * was added to the expression due to the mathemathical
  425. * model we used.
  426. */
  427. xy_distance = distance_x * cos_vert_angle - vert_offset * sin_vert_angle;
  428. ///the expression wiht '-' is proved to be better than the one with '+'
  429. x = xy_distance * sin_rot_angle - horiz_offset * cos_rot_angle;
  430. float distance_y = distance + distance_corr_y;
  431. xy_distance = distance_y * cos_vert_angle - vert_offset * sin_vert_angle;
  432. /**the new term of 'vert_offset * sin_vert_angle'
  433. * was added to the expression due to the mathemathical
  434. * model we used.
  435. */
  436. y = xy_distance * cos_rot_angle + horiz_offset * sin_rot_angle;
  437. // Using distance_y is not symmetric, but the velodyne manual
  438. // does this.
  439. /**the new term of 'vert_offset * cos_vert_angle'
  440. * was added to the expression due to the mathemathical
  441. * model we used.
  442. */
  443. z = distance_y * sin_vert_angle + vert_offset * cos_vert_angle;
  444. /** Use standard ROS coordinate system (right-hand rule) */
  445. float x_coord = y;
  446. float y_coord = -x;
  447. float z_coord = z;
  448. /** Intensity Calculation */
  449. float min_intensity = corrections.min_intensity;
  450. float max_intensity = corrections.max_intensity;
  451. intensity = raw->blocks[i].data[k + 2];
  452. float focal_offset = 256 * (1 - corrections.focal_distance / 13100) * (1 - corrections.focal_distance / 13100);
  453. float focal_slope = corrections.focal_slope;
  454. intensity += focal_slope * (std::abs(focal_offset - 256 *
  455. SQR(1 - static_cast<float>(tmp.uint) / 65535)));
  456. intensity = (intensity < min_intensity) ? min_intensity : intensity;
  457. intensity = (intensity > max_intensity) ? max_intensity : intensity;
  458. data.push_back(Lidar_point(x_coord, y_coord, z_coord, corrections.laser_ring, raw->blocks[i].rotation, distance, intensity, time));
  459. }
  460. }
  461. // data.newLine();
  462. }
  463. }
  464. /** @brief convert raw VLS128 packet to point cloud
  465. *
  466. * @param pkt raw packet to unpack
  467. * @param pc shared pointer to point cloud (points are appended)
  468. */
  469. void RawData::unpack_vls128(const unsigned char *pkt, std::vector<Lidar_point> &data)
  470. {
  471. float azimuth_diff, azimuth_corrected_f;
  472. float last_azimuth_diff = 0;
  473. uint16_t azimuth, azimuth_next, azimuth_corrected;
  474. float x_coord, y_coord, z_coord;
  475. float distance;
  476. const raw_packet_t *raw = (const raw_packet_t *)&pkt[0];
  477. union two_bytes tmp;
  478. float cos_vert_angle, sin_vert_angle, cos_rot_correction, sin_rot_correction;
  479. float cos_rot_angle, sin_rot_angle;
  480. float xy_distance;
  481. // float time_diff_start_to_this_packet = (pkt.stamp - scan_start_time).toSec();
  482. uint8_t laser_number, firing_order;
  483. bool dual_return = (pkt[1204] == 57);
  484. for (int block = 0; block < BLOCKS_PER_PACKET - (4 * dual_return); block++)
  485. {
  486. // cache block for use
  487. const raw_block_t &current_block = raw->blocks[block];
  488. float time = 0;
  489. int bank_origin = 0;
  490. // Used to detect which bank of 32 lasers is in this block
  491. switch (current_block.header)
  492. {
  493. case VLS128_BANK_1:
  494. bank_origin = 0;
  495. break;
  496. case VLS128_BANK_2:
  497. bank_origin = 32;
  498. break;
  499. case VLS128_BANK_3:
  500. bank_origin = 64;
  501. break;
  502. case VLS128_BANK_4:
  503. bank_origin = 96;
  504. break;
  505. default:
  506. // ignore packets with mangled or otherwise different contents
  507. // Do not flood the log with messages, only issue at most one
  508. // of these warnings per minute.
  509. LOG_EVERY_N(INFO, 10000) << "skipping invalid VLS-128 packet: block "
  510. << block << " header value is "
  511. << raw->blocks[block].header;
  512. return; // bad packet: skip the rest
  513. }
  514. // Calculate difference between current and next block's azimuth angle.
  515. if (block == 0)
  516. {
  517. azimuth = current_block.rotation;
  518. }
  519. else
  520. {
  521. azimuth = azimuth_next;
  522. }
  523. if (block < (BLOCKS_PER_PACKET - (1 + dual_return)))
  524. {
  525. // Get the next block rotation to calculate how far we rotate between blocks
  526. azimuth_next = raw->blocks[block + (1 + dual_return)].rotation;
  527. // Finds the difference between two successive blocks
  528. azimuth_diff = (float)((36000 + azimuth_next - azimuth) % 36000);
  529. // This is used when the last block is next to predict rotation amount
  530. last_azimuth_diff = azimuth_diff;
  531. }
  532. else
  533. {
  534. // This makes the assumption the difference between the last block and the next packet is the
  535. // same as the last to the second to last.
  536. // Assumes RPM doesn't change much between blocks
  537. azimuth_diff = (block == BLOCKS_PER_PACKET - (4 * dual_return) - 1) ? 0 : last_azimuth_diff;
  538. }
  539. // condition added to avoid calculating points which are not in the interesting defined area (min_angle < area < max_angle)
  540. if ((config_.min_angle < config_.max_angle && azimuth >= config_.min_angle && azimuth <= config_.max_angle) || (config_.min_angle > config_.max_angle))
  541. {
  542. for (int j = 0, k = 0; j < SCANS_PER_BLOCK; j++, k += RAW_SCAN_SIZE)
  543. {
  544. // distance extraction
  545. tmp.bytes[0] = current_block.data[k];
  546. tmp.bytes[1] = current_block.data[k + 1];
  547. distance = tmp.uint * VLS128_DISTANCE_RESOLUTION;
  548. if (pointInRange(distance))
  549. {
  550. laser_number = j + bank_origin; // Offset the laser in this block by which block it's in
  551. firing_order = laser_number / 8; // VLS-128 fires 8 lasers at a time
  552. if (timing_offsets.size())
  553. {
  554. time = timing_offsets[block / 4][firing_order + laser_number / 64]; // + time_diff_start_to_this_packet;
  555. }
  556. velodyne_pointcloud::LaserCorrection &corrections = calibration_.laser_corrections[laser_number];
  557. // correct for the laser rotation as a function of timing during the firings
  558. azimuth_corrected_f = azimuth + (azimuth_diff * vls_128_laser_azimuth_cache[firing_order]);
  559. azimuth_corrected = ((uint16_t)round(azimuth_corrected_f)) % 36000;
  560. // convert polar coordinates to Euclidean XYZ
  561. cos_vert_angle = corrections.cos_vert_correction;
  562. sin_vert_angle = corrections.sin_vert_correction;
  563. cos_rot_correction = corrections.cos_rot_correction;
  564. sin_rot_correction = corrections.sin_rot_correction;
  565. // cos(a-b) = cos(a)*cos(b) + sin(a)*sin(b)
  566. // sin(a-b) = sin(a)*cos(b) - cos(a)*sin(b)
  567. cos_rot_angle =
  568. cos_rot_table_[azimuth_corrected] * cos_rot_correction +
  569. sin_rot_table_[azimuth_corrected] * sin_rot_correction;
  570. sin_rot_angle =
  571. sin_rot_table_[azimuth_corrected] * cos_rot_correction -
  572. cos_rot_table_[azimuth_corrected] * sin_rot_correction;
  573. // Compute the distance in the xy plane (w/o accounting for rotation)
  574. xy_distance = distance * cos_vert_angle;
  575. data.push_back(Lidar_point(xy_distance * cos_rot_angle,
  576. -(xy_distance * sin_rot_angle),
  577. distance * sin_vert_angle,
  578. corrections.laser_ring,
  579. azimuth_corrected,
  580. distance,
  581. current_block.data[k + 2],
  582. time));
  583. }
  584. }
  585. // data.newLine();
  586. }
  587. }
  588. }
  589. /** @brief convert raw VLP16 packet to point cloud
  590. *
  591. * @param pkt raw packet to unpack
  592. * @param data vector of self-defined point cloud (points are appended)
  593. */
  594. void RawData::unpack_vlp16(const unsigned char *pkt, std::vector<Lidar_point> &data)
  595. {
  596. float azimuth;
  597. float azimuth_diff;
  598. int raw_azimuth_diff;
  599. float last_azimuth_diff = 0;
  600. float azimuth_corrected_f;
  601. int azimuth_corrected;
  602. float x, y, z;
  603. float intensity;
  604. // float time_diff_start_to_this_packet = (pkt.stamp - scan_start_time).toSec();
  605. const raw_packet_t *raw = (const raw_packet_t *)&pkt[0];
  606. float packet_timestamp = ((unsigned long)(raw->revolution & 0xff) << 8 | (unsigned long)(raw->revolution & 0xff00) | (unsigned long)(raw->status[0]) << 16 | (unsigned long)(raw->status[1]) << 24) * 1e-6;
  607. // LOG_EVERY_N(INFO, 10) << "packet timestamp: " << packet_timestamp;
  608. for (int block = 0; block < BLOCKS_PER_PACKET; block++)
  609. {
  610. // ignore packets with mangled or otherwise different contents
  611. if (UPPER_BANK != raw->blocks[block].header)
  612. {
  613. // Do not flood the log with messages, only issue at most one
  614. // of these warnings per minute.
  615. LOG_EVERY_N(INFO, 1000) << "skipping invalid VLP-16 packet: block "
  616. << block << " header value is "
  617. << raw->blocks[block].header;
  618. return; // bad packet: skip the rest
  619. }
  620. // Calculate difference between current and next block's azimuth angle.
  621. azimuth = (float)(raw->blocks[block].rotation);
  622. if (block < (BLOCKS_PER_PACKET - 1))
  623. {
  624. raw_azimuth_diff = raw->blocks[block + 1].rotation - raw->blocks[block].rotation;
  625. azimuth_diff = (float)((36000 + raw_azimuth_diff) % 36000);
  626. // some packets contain an angle overflow where azimuth_diff < 0
  627. if (raw_azimuth_diff < 0) //raw->blocks[block+1].rotation - raw->blocks[block].rotation < 0)
  628. {
  629. LOG_EVERY_N(INFO, 1) << "Packet containing angle overflow, first angle: " << raw->blocks[block].rotation << " second angle: " << raw->blocks[block + 1].rotation;
  630. // if last_azimuth_diff was not zero, we can assume that the velodyne's speed did not change very much and use the same difference
  631. if (last_azimuth_diff > 0)
  632. {
  633. azimuth_diff = last_azimuth_diff;
  634. }
  635. // otherwise we are not able to use this data
  636. // TODO: we might just not use the second 16 firings
  637. else
  638. {
  639. continue;
  640. }
  641. }
  642. last_azimuth_diff = azimuth_diff;
  643. }
  644. else
  645. {
  646. azimuth_diff = last_azimuth_diff;
  647. }
  648. for (int firing = 0, k = 0; firing < VLP16_FIRINGS_PER_BLOCK; firing++)
  649. {
  650. for (int dsr = 0; dsr < VLP16_SCANS_PER_FIRING; dsr++, k += RAW_SCAN_SIZE)
  651. {
  652. velodyne_pointcloud::LaserCorrection &corrections = calibration_.laser_corrections[dsr];
  653. /** Position Calculation */
  654. union two_bytes tmp;
  655. tmp.bytes[0] = raw->blocks[block].data[k];
  656. tmp.bytes[1] = raw->blocks[block].data[k + 1];
  657. /** correct for the laser rotation as a function of timing during the firings **/
  658. azimuth_corrected_f = azimuth + (azimuth_diff * ((dsr * VLP16_DSR_TOFFSET) + (firing * VLP16_FIRING_TOFFSET)) / VLP16_BLOCK_TDURATION);
  659. azimuth_corrected = ((int)round(azimuth_corrected_f)) % 36000;
  660. /*condition added to avoid calculating points which are not
  661. in the interesting defined area (min_angle < area < max_angle)*/
  662. if ((azimuth_corrected >= config_.min_angle && azimuth_corrected <= config_.max_angle && config_.min_angle < config_.max_angle) || (config_.min_angle > config_.max_angle && (azimuth_corrected <= config_.max_angle || azimuth_corrected >= config_.min_angle)))
  663. {
  664. // convert polar coordinates to Euclidean XYZ
  665. float distance = tmp.uint * calibration_.distance_resolution_m;
  666. distance += corrections.dist_correction;
  667. // added by yct, range limit
  668. if(!pointInRange(distance))
  669. continue;
  670. float cos_vert_angle = corrections.cos_vert_correction;
  671. float sin_vert_angle = corrections.sin_vert_correction;
  672. float cos_rot_correction = corrections.cos_rot_correction;
  673. float sin_rot_correction = corrections.sin_rot_correction;
  674. // cos(a-b) = cos(a)*cos(b) + sin(a)*sin(b)
  675. // sin(a-b) = sin(a)*cos(b) - cos(a)*sin(b)
  676. float cos_rot_angle =
  677. cos_rot_table_[azimuth_corrected] * cos_rot_correction +
  678. sin_rot_table_[azimuth_corrected] * sin_rot_correction;
  679. float sin_rot_angle =
  680. sin_rot_table_[azimuth_corrected] * cos_rot_correction -
  681. cos_rot_table_[azimuth_corrected] * sin_rot_correction;
  682. float horiz_offset = corrections.horiz_offset_correction;
  683. float vert_offset = corrections.vert_offset_correction;
  684. // Compute the distance in the xy plane (w/o accounting for rotation)
  685. /**the new term of 'vert_offset * sin_vert_angle'
  686. * was added to the expression due to the mathemathical
  687. * model we used.
  688. */
  689. float xy_distance = distance * cos_vert_angle - vert_offset * sin_vert_angle;
  690. // Calculate temporal X, use absolute value.
  691. float xx = xy_distance * sin_rot_angle - horiz_offset * cos_rot_angle;
  692. // Calculate temporal Y, use absolute value
  693. float yy = xy_distance * cos_rot_angle + horiz_offset * sin_rot_angle;
  694. if (xx < 0)
  695. xx = -xx;
  696. if (yy < 0)
  697. yy = -yy;
  698. // Get 2points calibration values,Linear interpolation to get distance
  699. // correction for X and Y, that means distance correction use
  700. // different value at different distance
  701. float distance_corr_x = 0;
  702. float distance_corr_y = 0;
  703. if (corrections.two_pt_correction_available)
  704. {
  705. distance_corr_x =
  706. (corrections.dist_correction - corrections.dist_correction_x) * (xx - 2.4) / (25.04 - 2.4) + corrections.dist_correction_x;
  707. distance_corr_x -= corrections.dist_correction;
  708. distance_corr_y =
  709. (corrections.dist_correction - corrections.dist_correction_y) * (yy - 1.93) / (25.04 - 1.93) + corrections.dist_correction_y;
  710. distance_corr_y -= corrections.dist_correction;
  711. }
  712. float distance_x = distance + distance_corr_x;
  713. /**the new term of 'vert_offset * sin_vert_angle'
  714. * was added to the expression due to the mathemathical
  715. * model we used.
  716. */
  717. xy_distance = distance_x * cos_vert_angle - vert_offset * sin_vert_angle;
  718. x = xy_distance * sin_rot_angle - horiz_offset * cos_rot_angle;
  719. float distance_y = distance + distance_corr_y;
  720. /**the new term of 'vert_offset * sin_vert_angle'
  721. * was added to the expression due to the mathemathical
  722. * model we used.
  723. */
  724. xy_distance = distance_y * cos_vert_angle - vert_offset * sin_vert_angle;
  725. y = xy_distance * cos_rot_angle + horiz_offset * sin_rot_angle;
  726. // Using distance_y is not symmetric, but the velodyne manual
  727. // does this.
  728. /**the new term of 'vert_offset * cos_vert_angle'
  729. * was added to the expression due to the mathemathical
  730. * model we used.
  731. */
  732. z = distance_y * sin_vert_angle + vert_offset * cos_vert_angle;
  733. /** Use standard ROS coordinate system (right-hand rule) */
  734. float x_coord = y;
  735. float y_coord = -x;
  736. float z_coord = z;
  737. /** Intensity Calculation */
  738. float min_intensity = corrections.min_intensity;
  739. float max_intensity = corrections.max_intensity;
  740. intensity = raw->blocks[block].data[k + 2];
  741. float focal_offset = 256 * SQR(1 - corrections.focal_distance / 13100);
  742. float focal_slope = corrections.focal_slope;
  743. intensity += focal_slope * (std::abs(focal_offset - 256 *
  744. SQR(1 - tmp.uint / 65535)));
  745. intensity = (intensity < min_intensity) ? min_intensity : intensity;
  746. intensity = (intensity > max_intensity) ? max_intensity : intensity;
  747. float time = 0;
  748. if (timing_offsets.size())
  749. time = timing_offsets[block][firing * 16 + dsr]; // + time_diff_start_to_this_packet;
  750. data.push_back(Lidar_point(x_coord, y_coord, z_coord, corrections.laser_ring, azimuth_corrected, distance, intensity, time+packet_timestamp));
  751. }
  752. }
  753. // data.newLine();
  754. }
  755. }
  756. }
  757. } // namespace velodyne_rawdata