/* * Copyright (C) 2007 Austin Robot Technology, Patrick Beeson * Copyright (C) 2009, 2010, 2012 Austin Robot Technology, Jack O'Quin * Copyright (C) 2019, Kaarta Inc, Shawn Hanna * * License: Modified BSD Software License Agreement * * $Id$ */ /** * @file * * Velodyne 3D LIDAR data accessor class implementation. * * Class for unpacking raw Velodyne LIDAR packets into useful * formats. * * Derived classes accept raw Velodyne data for either single packets * or entire rotations, and provide it in various formats for either * on-line or off-line processing. * * @author Patrick Beeson * @author Jack O'Quin * @author Shawn Hanna * * HDL-64E S2 calibration support provided by Nick Hillier */ #include #include #include "angles.h" #include "rawdata.h" namespace velodyne_rawdata { inline float SQR(float val) { return val * val; } //////////////////////////////////////////////////////////////////////// // // RawData base class implementation // //////////////////////////////////////////////////////////////////////// RawData::RawData() {} /** Update parameters: conversions and update */ void RawData::setParameters(double min_range, double max_range, double view_direction, double view_width) { config_.min_range = min_range; config_.max_range = max_range; //converting angle parameters into the velodyne reference (rad) config_.tmp_min_angle = view_direction + view_width / 2; config_.tmp_max_angle = view_direction - view_width / 2; //computing positive modulo to keep theses angles into [0;2*M_PI] config_.tmp_min_angle = fmod(fmod(config_.tmp_min_angle, 2 * M_PI) + 2 * M_PI, 2 * M_PI); config_.tmp_max_angle = fmod(fmod(config_.tmp_max_angle, 2 * M_PI) + 2 * M_PI, 2 * M_PI); //converting into the hardware velodyne ref (negative yaml and degrees) //adding 0.5 perfomrs a centered double to int conversion config_.min_angle = 100 * (2 * M_PI - config_.tmp_min_angle) * 180 / M_PI + 0.5; config_.max_angle = 100 * (2 * M_PI - config_.tmp_max_angle) * 180 / M_PI + 0.5; if (config_.min_angle == config_.max_angle) { //avoid returning empty cloud if min_angle = max_angle config_.min_angle = 0; config_.max_angle = 36000; } } int RawData::scansPerPacket() const { if (calibration_.num_lasers == 16) { return BLOCKS_PER_PACKET * VLP16_FIRINGS_PER_BLOCK * VLP16_SCANS_PER_FIRING; } else { return BLOCKS_PER_PACKET * SCANS_PER_BLOCK; } } /** * Build a timing table for each block/firing. Stores in timing_offsets vector */ bool RawData::buildTimings() { // vlp16 if (config_.model == "VLP16") { // timing table calculation, from velodyne user manual timing_offsets.resize(12); for (size_t i = 0; i < timing_offsets.size(); ++i) { timing_offsets[i].resize(32); } // constants double full_firing_cycle = 55.296 * 1e-6; // seconds double single_firing = 2.304 * 1e-6; // seconds double dataBlockIndex, dataPointIndex; bool dual_mode = false; // compute timing offsets for (size_t x = 0; x < timing_offsets.size(); ++x) { for (size_t y = 0; y < timing_offsets[x].size(); ++y) { if (dual_mode) { dataBlockIndex = (x - (x % 2)) + (y / 16); } else { dataBlockIndex = (x * 2) + (y / 16); } dataPointIndex = y % 16; //timing_offsets[block][firing] timing_offsets[x][y] = (full_firing_cycle * dataBlockIndex) + (single_firing * dataPointIndex); } } } // vlp32 else if (config_.model == "32C") { // timing table calculation, from velodyne user manual timing_offsets.resize(12); for (size_t i = 0; i < timing_offsets.size(); ++i) { timing_offsets[i].resize(32); } // constants double full_firing_cycle = 55.296 * 1e-6; // seconds double single_firing = 2.304 * 1e-6; // seconds double dataBlockIndex, dataPointIndex; bool dual_mode = false; // compute timing offsets for (size_t x = 0; x < timing_offsets.size(); ++x) { for (size_t y = 0; y < timing_offsets[x].size(); ++y) { if (dual_mode) { dataBlockIndex = x / 2; } else { dataBlockIndex = x; } dataPointIndex = y / 2; timing_offsets[x][y] = (full_firing_cycle * dataBlockIndex) + (single_firing * dataPointIndex); } } } // hdl32 else if (config_.model == "32E") { // timing table calculation, from velodyne user manual timing_offsets.resize(12); for (size_t i = 0; i < timing_offsets.size(); ++i) { timing_offsets[i].resize(32); } // constants double full_firing_cycle = 46.080 * 1e-6; // seconds double single_firing = 1.152 * 1e-6; // seconds double dataBlockIndex, dataPointIndex; bool dual_mode = false; // compute timing offsets for (size_t x = 0; x < timing_offsets.size(); ++x) { for (size_t y = 0; y < timing_offsets[x].size(); ++y) { if (dual_mode) { dataBlockIndex = x / 2; } else { dataBlockIndex = x; } dataPointIndex = y / 2; timing_offsets[x][y] = (full_firing_cycle * dataBlockIndex) + (single_firing * dataPointIndex); } } } else if (config_.model == "VLS128") { timing_offsets.resize(3); for (size_t i = 0; i < timing_offsets.size(); ++i) { timing_offsets[i].resize(17); // 17 (+1 for the maintenance time after firing group 8) } double full_firing_cycle = VLS128_SEQ_TDURATION * 1e-6; //seconds double single_firing = VLS128_CHANNEL_TDURATION * 1e-6; // seconds double offset_paket_time = VLS128_TOH_ADJUSTMENT * 1e-6; //seconds double sequenceIndex, firingGroupIndex; // Compute timing offsets for (size_t x = 0; x < timing_offsets.size(); ++x) { for (size_t y = 0; y < timing_offsets[x].size(); ++y) { sequenceIndex = x; firingGroupIndex = y; timing_offsets[x][y] = (full_firing_cycle * sequenceIndex) + (single_firing * firingGroupIndex) - offset_paket_time; LOG(INFO) << " firing_seque" << x << " firing_group" << y << "offset" << timing_offsets[x][y]; } } } else { timing_offsets.clear(); LOG(WARNING) << "Timings not supported for model" << config_.model.c_str(); } if (timing_offsets.size()) { // // ROS_INFO("VELODYNE TIMING TABLE:"); // for (size_t x = 0; x < timing_offsets.size(); ++x) // { // for (size_t y = 0; y < timing_offsets[x].size(); ++y) // { // printf("%04.3f ", timing_offsets[x][y] * 1e6); // } // printf("\n"); // } return true; } else { LOG(WARNING) << "NO TIMING OFFSETS CALCULATED. ARE YOU USING A SUPPORTED VELODYNE SENSOR?"; } return false; } /** Set up for on-line operation. */ bool RawData::setup(std::string model, std::string calib_file, double max_range, double min_range, int min_angle, int max_angle) { config_.model = model; config_.calibrationFile = calib_file; config_.max_range = max_range; config_.min_range = min_range; config_.min_angle = min_angle*100; config_.max_angle = max_angle*100; if(!buildTimings()) { return false; } // // get path to angles.config file for this device // if (!private_nh.getParam("calibration", config_.calibrationFile)) // { // ROS_ERROR_STREAM("No calibration angles specified! Using test values!"); // // have to use something: grab unit test version as a default // std::string pkgPath = ros::package::getPath("velodyne_pointcloud"); // config_.calibrationFile = pkgPath + "/params/64e_utexas.yaml"; // } // ROS_INFO_STREAM("correction angles: " << config_.calibrationFile); if (!loadCalibration()) { mb_initialized = false; return false; } // ROS_INFO_STREAM("Number of lasers: " << calibration_.num_lasers << "."); setupSinCosCache(); setupAzimuthCache(); mb_initialized = true; return true; } /** Set up for offline operation */ int RawData::setupOffline(std::string calibration_file, double max_range_, double min_range_) { config_.max_range = max_range_; config_.min_range = min_range_; LOG(INFO) << "data ranges to publish: [" << config_.min_range << ", " << config_.max_range << "]"; config_.calibrationFile = calibration_file; LOG(INFO) << "correction angles: " << config_.calibrationFile; if (!loadCalibration()) { return -1; } LOG(INFO) << "Number of lasers: " << calibration_.num_lasers << "."; setupSinCosCache(); setupAzimuthCache(); return 0; } bool RawData::loadCalibration() { calibration_.read(config_.calibrationFile); if (!calibration_.initialized) { LOG(ERROR) << "Unable to open calibration file: " << config_.calibrationFile; return false; } return true; } void RawData::setupSinCosCache() { // Set up cached values for sin and cos of all the possible headings for (uint16_t rot_index = 0; rot_index < ROTATION_MAX_UNITS; ++rot_index) { float rotation = angles::from_degrees(ROTATION_RESOLUTION * rot_index); cos_rot_table_[rot_index] = cosf(rotation); sin_rot_table_[rot_index] = sinf(rotation); } if (config_.model == "VLS128") { for (uint8_t i = 0; i < 16; i++) { vls_128_laser_azimuth_cache[i] = (VLS128_CHANNEL_TDURATION / VLS128_SEQ_TDURATION) * (i + i / 8); } } } void RawData::setupAzimuthCache() { if (config_.model == "VLS128") { for (uint8_t i = 0; i < 16; i++) { vls_128_laser_azimuth_cache[i] = (VLS128_CHANNEL_TDURATION / VLS128_SEQ_TDURATION) * (i + i / 8); } } else { LOG(WARNING) << "No Azimuth Cache configured for model " << config_.model.c_str(); } } /** @brief convert raw packet to point cloud * * @param pkt raw packet to unpack * @param pc shared pointer to point cloud (points are appended) */ void RawData::unpack(const unsigned char *pkt, std::vector &data) { using velodyne_pointcloud::LaserCorrection; // LOG(INFO)<<"Received packet"; /** special parsing for the VLS128 **/ if (pkt[1205] == VLS128_MODEL_ID) { // VLS 128 unpack_vls128(pkt, data); return; } /** special parsing for the VLP16 **/ if (calibration_.num_lasers == 16) { unpack_vlp16(pkt, data); return; } // float time_diff_start_to_this_packet = (pkt.stamp - scan_start_time).toSec(); const raw_packet_t *raw = (const raw_packet_t *)&pkt[0]; for (int i = 0; i < BLOCKS_PER_PACKET; i++) { // upper bank lasers are numbered [0..31] // NOTE: this is a change from the old velodyne_common implementation int bank_origin = 0; if (raw->blocks[i].header == LOWER_BANK) { // lower bank lasers are [32..63] bank_origin = 32; } for (int j = 0, k = 0; j < SCANS_PER_BLOCK; j++, k += RAW_SCAN_SIZE) { float x, y, z; float intensity; const uint8_t laser_number = j + bank_origin; float time = 0; const LaserCorrection &corrections = calibration_.laser_corrections[laser_number]; /** Position Calculation */ const raw_block_t &block = raw->blocks[i]; union two_bytes tmp; tmp.bytes[0] = block.data[k]; tmp.bytes[1] = block.data[k + 1]; /*condition added to avoid calculating points which are not in the interesting defined area (min_angle < area < max_angle)*/ 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))) { if (timing_offsets.size()) { time = timing_offsets[i][j]; // +time_diff_start_to_this_packet; } if (tmp.uint == 0) // no valid laser beam return { // call to addPoint is still required since output could be organized data.push_back(Lidar_point(0.0f, 0.0f, 0.0f, corrections.laser_ring, raw->blocks[i].rotation, 0.0f, 0.0f, time)); continue; } float distance = tmp.uint * calibration_.distance_resolution_m; distance += corrections.dist_correction; float cos_vert_angle = corrections.cos_vert_correction; float sin_vert_angle = corrections.sin_vert_correction; float cos_rot_correction = corrections.cos_rot_correction; float sin_rot_correction = corrections.sin_rot_correction; // cos(a-b) = cos(a)*cos(b) + sin(a)*sin(b) // sin(a-b) = sin(a)*cos(b) - cos(a)*sin(b) float cos_rot_angle = cos_rot_table_[block.rotation] * cos_rot_correction + sin_rot_table_[block.rotation] * sin_rot_correction; float sin_rot_angle = sin_rot_table_[block.rotation] * cos_rot_correction - cos_rot_table_[block.rotation] * sin_rot_correction; float horiz_offset = corrections.horiz_offset_correction; float vert_offset = corrections.vert_offset_correction; // Compute the distance in the xy plane (w/o accounting for rotation) /**the new term of 'vert_offset * sin_vert_angle' * was added to the expression due to the mathemathical * model we used. */ float xy_distance = distance * cos_vert_angle - vert_offset * sin_vert_angle; // Calculate temporal X, use absolute value. float xx = xy_distance * sin_rot_angle - horiz_offset * cos_rot_angle; // Calculate temporal Y, use absolute value float yy = xy_distance * cos_rot_angle + horiz_offset * sin_rot_angle; if (xx < 0) xx = -xx; if (yy < 0) yy = -yy; // Get 2points calibration values,Linear interpolation to get distance // correction for X and Y, that means distance correction use // different value at different distance float distance_corr_x = 0; float distance_corr_y = 0; if (corrections.two_pt_correction_available) { distance_corr_x = (corrections.dist_correction - corrections.dist_correction_x) * (xx - 2.4) / (25.04 - 2.4) + corrections.dist_correction_x; distance_corr_x -= corrections.dist_correction; distance_corr_y = (corrections.dist_correction - corrections.dist_correction_y) * (yy - 1.93) / (25.04 - 1.93) + corrections.dist_correction_y; distance_corr_y -= corrections.dist_correction; } float distance_x = distance + distance_corr_x; /**the new term of 'vert_offset * sin_vert_angle' * was added to the expression due to the mathemathical * model we used. */ xy_distance = distance_x * cos_vert_angle - vert_offset * sin_vert_angle; ///the expression wiht '-' is proved to be better than the one with '+' x = xy_distance * sin_rot_angle - horiz_offset * cos_rot_angle; float distance_y = distance + distance_corr_y; xy_distance = distance_y * cos_vert_angle - vert_offset * sin_vert_angle; /**the new term of 'vert_offset * sin_vert_angle' * was added to the expression due to the mathemathical * model we used. */ y = xy_distance * cos_rot_angle + horiz_offset * sin_rot_angle; // Using distance_y is not symmetric, but the velodyne manual // does this. /**the new term of 'vert_offset * cos_vert_angle' * was added to the expression due to the mathemathical * model we used. */ z = distance_y * sin_vert_angle + vert_offset * cos_vert_angle; /** Use standard ROS coordinate system (right-hand rule) */ float x_coord = y; float y_coord = -x; float z_coord = z; /** Intensity Calculation */ float min_intensity = corrections.min_intensity; float max_intensity = corrections.max_intensity; intensity = raw->blocks[i].data[k + 2]; float focal_offset = 256 * (1 - corrections.focal_distance / 13100) * (1 - corrections.focal_distance / 13100); float focal_slope = corrections.focal_slope; intensity += focal_slope * (std::abs(focal_offset - 256 * SQR(1 - static_cast(tmp.uint) / 65535))); intensity = (intensity < min_intensity) ? min_intensity : intensity; intensity = (intensity > max_intensity) ? max_intensity : intensity; data.push_back(Lidar_point(x_coord, y_coord, z_coord, corrections.laser_ring, raw->blocks[i].rotation, distance, intensity, time)); } } // data.newLine(); } } /** @brief convert raw VLS128 packet to point cloud * * @param pkt raw packet to unpack * @param pc shared pointer to point cloud (points are appended) */ void RawData::unpack_vls128(const unsigned char *pkt, std::vector &data) { float azimuth_diff, azimuth_corrected_f; float last_azimuth_diff = 0; uint16_t azimuth, azimuth_next, azimuth_corrected; float x_coord, y_coord, z_coord; float distance; const raw_packet_t *raw = (const raw_packet_t *)&pkt[0]; union two_bytes tmp; float cos_vert_angle, sin_vert_angle, cos_rot_correction, sin_rot_correction; float cos_rot_angle, sin_rot_angle; float xy_distance; // float time_diff_start_to_this_packet = (pkt.stamp - scan_start_time).toSec(); uint8_t laser_number, firing_order; bool dual_return = (pkt[1204] == 57); for (int block = 0; block < BLOCKS_PER_PACKET - (4 * dual_return); block++) { // cache block for use const raw_block_t ¤t_block = raw->blocks[block]; float time = 0; int bank_origin = 0; // Used to detect which bank of 32 lasers is in this block switch (current_block.header) { case VLS128_BANK_1: bank_origin = 0; break; case VLS128_BANK_2: bank_origin = 32; break; case VLS128_BANK_3: bank_origin = 64; break; case VLS128_BANK_4: bank_origin = 96; break; default: // ignore packets with mangled or otherwise different contents // Do not flood the log with messages, only issue at most one // of these warnings per minute. LOG_EVERY_N(INFO, 10000) << "skipping invalid VLS-128 packet: block " << block << " header value is " << raw->blocks[block].header; return; // bad packet: skip the rest } // Calculate difference between current and next block's azimuth angle. if (block == 0) { azimuth = current_block.rotation; } else { azimuth = azimuth_next; } if (block < (BLOCKS_PER_PACKET - (1 + dual_return))) { // Get the next block rotation to calculate how far we rotate between blocks azimuth_next = raw->blocks[block + (1 + dual_return)].rotation; // Finds the difference between two successive blocks azimuth_diff = (float)((36000 + azimuth_next - azimuth) % 36000); // This is used when the last block is next to predict rotation amount last_azimuth_diff = azimuth_diff; } else { // This makes the assumption the difference between the last block and the next packet is the // same as the last to the second to last. // Assumes RPM doesn't change much between blocks azimuth_diff = (block == BLOCKS_PER_PACKET - (4 * dual_return) - 1) ? 0 : last_azimuth_diff; } // condition added to avoid calculating points which are not in the interesting defined area (min_angle < area < max_angle) if ((config_.min_angle < config_.max_angle && azimuth >= config_.min_angle && azimuth <= config_.max_angle) || (config_.min_angle > config_.max_angle)) { for (int j = 0, k = 0; j < SCANS_PER_BLOCK; j++, k += RAW_SCAN_SIZE) { // distance extraction tmp.bytes[0] = current_block.data[k]; tmp.bytes[1] = current_block.data[k + 1]; distance = tmp.uint * VLS128_DISTANCE_RESOLUTION; if (pointInRange(distance)) { laser_number = j + bank_origin; // Offset the laser in this block by which block it's in firing_order = laser_number / 8; // VLS-128 fires 8 lasers at a time if (timing_offsets.size()) { time = timing_offsets[block / 4][firing_order + laser_number / 64]; // + time_diff_start_to_this_packet; } velodyne_pointcloud::LaserCorrection &corrections = calibration_.laser_corrections[laser_number]; // correct for the laser rotation as a function of timing during the firings azimuth_corrected_f = azimuth + (azimuth_diff * vls_128_laser_azimuth_cache[firing_order]); azimuth_corrected = ((uint16_t)round(azimuth_corrected_f)) % 36000; // convert polar coordinates to Euclidean XYZ cos_vert_angle = corrections.cos_vert_correction; sin_vert_angle = corrections.sin_vert_correction; cos_rot_correction = corrections.cos_rot_correction; sin_rot_correction = corrections.sin_rot_correction; // cos(a-b) = cos(a)*cos(b) + sin(a)*sin(b) // sin(a-b) = sin(a)*cos(b) - cos(a)*sin(b) cos_rot_angle = cos_rot_table_[azimuth_corrected] * cos_rot_correction + sin_rot_table_[azimuth_corrected] * sin_rot_correction; sin_rot_angle = sin_rot_table_[azimuth_corrected] * cos_rot_correction - cos_rot_table_[azimuth_corrected] * sin_rot_correction; // Compute the distance in the xy plane (w/o accounting for rotation) xy_distance = distance * cos_vert_angle; data.push_back(Lidar_point(xy_distance * cos_rot_angle, -(xy_distance * sin_rot_angle), distance * sin_vert_angle, corrections.laser_ring, azimuth_corrected, distance, current_block.data[k + 2], time)); } } // data.newLine(); } } } /** @brief convert raw VLP16 packet to point cloud * * @param pkt raw packet to unpack * @param data vector of self-defined point cloud (points are appended) */ void RawData::unpack_vlp16(const unsigned char *pkt, std::vector &data) { float azimuth; float azimuth_diff; int raw_azimuth_diff; float last_azimuth_diff = 0; float azimuth_corrected_f; int azimuth_corrected; float x, y, z; float intensity; // float time_diff_start_to_this_packet = (pkt.stamp - scan_start_time).toSec(); const raw_packet_t *raw = (const raw_packet_t *)&pkt[0]; 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; // LOG_EVERY_N(INFO, 10) << "packet timestamp: " << packet_timestamp; for (int block = 0; block < BLOCKS_PER_PACKET; block++) { // ignore packets with mangled or otherwise different contents if (UPPER_BANK != raw->blocks[block].header) { // Do not flood the log with messages, only issue at most one // of these warnings per minute. LOG_EVERY_N(INFO, 1000) << "skipping invalid VLP-16 packet: block " << block << " header value is " << raw->blocks[block].header; return; // bad packet: skip the rest } // Calculate difference between current and next block's azimuth angle. azimuth = (float)(raw->blocks[block].rotation); if (block < (BLOCKS_PER_PACKET - 1)) { raw_azimuth_diff = raw->blocks[block + 1].rotation - raw->blocks[block].rotation; azimuth_diff = (float)((36000 + raw_azimuth_diff) % 36000); // some packets contain an angle overflow where azimuth_diff < 0 if (raw_azimuth_diff < 0) //raw->blocks[block+1].rotation - raw->blocks[block].rotation < 0) { LOG_EVERY_N(INFO, 10000) << "Packet containing angle overflow, first angle: " << raw->blocks[block].rotation << " second angle: " << raw->blocks[block + 1].rotation; // 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 if (last_azimuth_diff > 0) { azimuth_diff = last_azimuth_diff; } // otherwise we are not able to use this data // TODO: we might just not use the second 16 firings else { continue; } } last_azimuth_diff = azimuth_diff; } else { azimuth_diff = last_azimuth_diff; } for (int firing = 0, k = 0; firing < VLP16_FIRINGS_PER_BLOCK; firing++) { for (int dsr = 0; dsr < VLP16_SCANS_PER_FIRING; dsr++, k += RAW_SCAN_SIZE) { velodyne_pointcloud::LaserCorrection &corrections = calibration_.laser_corrections[dsr]; /** Position Calculation */ union two_bytes tmp; tmp.bytes[0] = raw->blocks[block].data[k]; tmp.bytes[1] = raw->blocks[block].data[k + 1]; /** correct for the laser rotation as a function of timing during the firings **/ azimuth_corrected_f = azimuth + (azimuth_diff * ((dsr * VLP16_DSR_TOFFSET) + (firing * VLP16_FIRING_TOFFSET)) / VLP16_BLOCK_TDURATION); azimuth_corrected = ((int)round(azimuth_corrected_f)) % 36000; /*condition added to avoid calculating points which are not in the interesting defined area (min_angle < area < max_angle)*/ 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))) { // convert polar coordinates to Euclidean XYZ float distance = tmp.uint * calibration_.distance_resolution_m; distance += corrections.dist_correction; // added by yct, range limit if(!pointInRange(distance)) continue; float cos_vert_angle = corrections.cos_vert_correction; float sin_vert_angle = corrections.sin_vert_correction; float cos_rot_correction = corrections.cos_rot_correction; float sin_rot_correction = corrections.sin_rot_correction; // cos(a-b) = cos(a)*cos(b) + sin(a)*sin(b) // sin(a-b) = sin(a)*cos(b) - cos(a)*sin(b) float cos_rot_angle = cos_rot_table_[azimuth_corrected] * cos_rot_correction + sin_rot_table_[azimuth_corrected] * sin_rot_correction; float sin_rot_angle = sin_rot_table_[azimuth_corrected] * cos_rot_correction - cos_rot_table_[azimuth_corrected] * sin_rot_correction; float horiz_offset = corrections.horiz_offset_correction; float vert_offset = corrections.vert_offset_correction; // Compute the distance in the xy plane (w/o accounting for rotation) /**the new term of 'vert_offset * sin_vert_angle' * was added to the expression due to the mathemathical * model we used. */ float xy_distance = distance * cos_vert_angle - vert_offset * sin_vert_angle; // Calculate temporal X, use absolute value. float xx = xy_distance * sin_rot_angle - horiz_offset * cos_rot_angle; // Calculate temporal Y, use absolute value float yy = xy_distance * cos_rot_angle + horiz_offset * sin_rot_angle; if (xx < 0) xx = -xx; if (yy < 0) yy = -yy; // Get 2points calibration values,Linear interpolation to get distance // correction for X and Y, that means distance correction use // different value at different distance float distance_corr_x = 0; float distance_corr_y = 0; if (corrections.two_pt_correction_available) { distance_corr_x = (corrections.dist_correction - corrections.dist_correction_x) * (xx - 2.4) / (25.04 - 2.4) + corrections.dist_correction_x; distance_corr_x -= corrections.dist_correction; distance_corr_y = (corrections.dist_correction - corrections.dist_correction_y) * (yy - 1.93) / (25.04 - 1.93) + corrections.dist_correction_y; distance_corr_y -= corrections.dist_correction; } float distance_x = distance + distance_corr_x; /**the new term of 'vert_offset * sin_vert_angle' * was added to the expression due to the mathemathical * model we used. */ xy_distance = distance_x * cos_vert_angle - vert_offset * sin_vert_angle; x = xy_distance * sin_rot_angle - horiz_offset * cos_rot_angle; float distance_y = distance + distance_corr_y; /**the new term of 'vert_offset * sin_vert_angle' * was added to the expression due to the mathemathical * model we used. */ xy_distance = distance_y * cos_vert_angle - vert_offset * sin_vert_angle; y = xy_distance * cos_rot_angle + horiz_offset * sin_rot_angle; // Using distance_y is not symmetric, but the velodyne manual // does this. /**the new term of 'vert_offset * cos_vert_angle' * was added to the expression due to the mathemathical * model we used. */ z = distance_y * sin_vert_angle + vert_offset * cos_vert_angle; /** Use standard ROS coordinate system (right-hand rule) */ float x_coord = y; float y_coord = -x; float z_coord = z; /** Intensity Calculation */ float min_intensity = corrections.min_intensity; float max_intensity = corrections.max_intensity; intensity = raw->blocks[block].data[k + 2]; float focal_offset = 256 * SQR(1 - corrections.focal_distance / 13100); float focal_slope = corrections.focal_slope; intensity += focal_slope * (std::abs(focal_offset - 256 * SQR(1 - tmp.uint / 65535))); intensity = (intensity < min_intensity) ? min_intensity : intensity; intensity = (intensity > max_intensity) ? max_intensity : intensity; float time = 0; if (timing_offsets.size()) time = timing_offsets[block][firing * 16 + dsr]; // + time_diff_start_to_this_packet; data.push_back(Lidar_point(x_coord, y_coord, z_coord, corrections.laser_ring, azimuth_corrected, distance, intensity, time+packet_timestamp)); } } // data.newLine(); } } } } // namespace velodyne_rawdata