rawdata.cc
Go to the documentation of this file.
00001 /*
00002  *  Copyright (C) 2007 Austin Robot Technology, Patrick Beeson
00003  *  Copyright (C) 2009, 2010, 2012 Austin Robot Technology, Jack O'Quin
00004  *
00005  *  License: Modified BSD Software License Agreement
00006  *
00007  *  $Id$
00008  */
00009 
00028 #include <fstream>
00029 #include <math.h>
00030 
00031 #include <ros/ros.h>
00032 #include <ros/package.h>
00033 #include <angles/angles.h>
00034 
00035 #include <velodyne_pointcloud/rawdata.h>
00036 
00037 namespace velodyne_rawdata
00038 {
00040   //
00041   // RawData base class implementation
00042   //
00044 
00045   RawData::RawData() {}
00046   
00048   void RawData::setParameters(double min_range,
00049                               double max_range,
00050                               double view_direction,
00051                               double view_width)
00052   {
00053     config_.min_range = min_range;
00054     config_.max_range = max_range;
00055 
00056     //converting angle parameters into the velodyne reference (rad)
00057     config_.tmp_min_angle = view_direction + view_width/2;
00058     config_.tmp_max_angle = view_direction - view_width/2;
00059     
00060     //computing positive modulo to keep theses angles into [0;2*M_PI]
00061     config_.tmp_min_angle = fmod(fmod(config_.tmp_min_angle,2*M_PI) + 2*M_PI,2*M_PI);
00062     config_.tmp_max_angle = fmod(fmod(config_.tmp_max_angle,2*M_PI) + 2*M_PI,2*M_PI);
00063     
00064     //converting into the hardware velodyne ref (negative yaml and degrees)
00065     //adding 0.5 perfomrs a centered double to int conversion 
00066     config_.min_angle = 100 * (2*M_PI - config_.tmp_min_angle) * 180 / M_PI + 0.5;
00067     config_.max_angle = 100 * (2*M_PI - config_.tmp_max_angle) * 180 / M_PI + 0.5;
00068     if (config_.min_angle == config_.max_angle)
00069     {
00070       //avoid returning empty cloud if min_angle = max_angle
00071       config_.min_angle = 0;
00072       config_.max_angle = 36000;
00073     }
00074   }
00075 
00077   int RawData::setup(ros::NodeHandle private_nh)
00078   {
00079     // get path to angles.config file for this device
00080     if (!private_nh.getParam("calibration", config_.calibrationFile))
00081       {
00082         ROS_ERROR_STREAM("No calibration angles specified! Using test values!");
00083 
00084         // have to use something: grab unit test version as a default
00085         std::string pkgPath = ros::package::getPath("velodyne_pointcloud");
00086         config_.calibrationFile = pkgPath + "/params/64e_utexas.yaml";
00087       }
00088 
00089     ROS_INFO_STREAM("correction angles: " << config_.calibrationFile);
00090 
00091     calibration_.read(config_.calibrationFile);
00092     if (!calibration_.initialized) {
00093       ROS_ERROR_STREAM("Unable to open calibration file: " << 
00094           config_.calibrationFile);
00095       return -1;
00096     }
00097     
00098     ROS_INFO_STREAM("Data will be processed as a VLP-16...num_lasers: " << calibration_.num_lasers);
00099     
00100     // Set up cached values for sin and cos of all the possible headings
00101     for (uint16_t rot_index = 0; rot_index < ROTATION_MAX_UNITS; ++rot_index) {
00102       float rotation = angles::from_degrees(ROTATION_RESOLUTION * rot_index);
00103       cos_rot_table_[rot_index] = cosf(rotation);
00104       sin_rot_table_[rot_index] = sinf(rotation);
00105     }
00106    return 0;
00107   }
00108 
00114   void RawData::unpack(const velodyne_msgs::VelodynePacket &pkt,
00115                        VPointCloud &pc)
00116   {
00117     ROS_DEBUG_STREAM("Received packet, time: " << pkt.stamp);
00118     
00120     if (calibration_.num_lasers == 16)
00121     {
00122       unpack_vlp16(pkt, pc);
00123       return;
00124     }
00125     
00126     const raw_packet_t *raw = (const raw_packet_t *) &pkt.data[0];
00127 
00128     for (int i = 0; i < BLOCKS_PER_PACKET; i++) {
00129 
00130       // upper bank lasers are numbered [0..31]
00131       // NOTE: this is a change from the old velodyne_common implementation
00132       int bank_origin = 0;
00133       if (raw->blocks[i].header == LOWER_BANK) {
00134         // lower bank lasers are [32..63]
00135         bank_origin = 32;
00136       }
00137 
00138       for (int j = 0, k = 0; j < SCANS_PER_BLOCK; j++, k += RAW_SCAN_SIZE) {
00139         
00140         float x, y, z;
00141         float intensity;
00142         uint8_t laser_number;       
00143 
00144         laser_number = j + bank_origin;
00145         velodyne_pointcloud::LaserCorrection &corrections = 
00146           calibration_.laser_corrections[laser_number];
00147 
00150         union two_bytes tmp;
00151         tmp.bytes[0] = raw->blocks[i].data[k];
00152         tmp.bytes[1] = raw->blocks[i].data[k+1];
00153         /*condition added to avoid calculating points which are not
00154           in the interesting defined area (min_angle < area < max_angle)*/
00155         if ((raw->blocks[i].rotation >= config_.min_angle 
00156              && raw->blocks[i].rotation <= config_.max_angle 
00157              && config_.min_angle < config_.max_angle)
00158              ||(config_.min_angle > config_.max_angle 
00159              && (raw->blocks[i].rotation <= config_.max_angle 
00160              || raw->blocks[i].rotation >= config_.min_angle))){
00161           float distance = tmp.uint * DISTANCE_RESOLUTION;
00162           distance += corrections.dist_correction;
00163   
00164           float cos_vert_angle = corrections.cos_vert_correction;
00165           float sin_vert_angle = corrections.sin_vert_correction;
00166           float cos_rot_correction = corrections.cos_rot_correction;
00167           float sin_rot_correction = corrections.sin_rot_correction;
00168   
00169           // cos(a-b) = cos(a)*cos(b) + sin(a)*sin(b)
00170           // sin(a-b) = sin(a)*cos(b) - cos(a)*sin(b)
00171           float cos_rot_angle = 
00172             cos_rot_table_[raw->blocks[i].rotation] * cos_rot_correction + 
00173             sin_rot_table_[raw->blocks[i].rotation] * sin_rot_correction;
00174           float sin_rot_angle = 
00175             sin_rot_table_[raw->blocks[i].rotation] * cos_rot_correction - 
00176             cos_rot_table_[raw->blocks[i].rotation] * sin_rot_correction;
00177   
00178           float horiz_offset = corrections.horiz_offset_correction;
00179           float vert_offset = corrections.vert_offset_correction;
00180   
00181           // Compute the distance in the xy plane (w/o accounting for rotation)
00186           float xy_distance = distance * cos_vert_angle + vert_offset * sin_vert_angle;
00187   
00188           // Calculate temporal X, use absolute value.
00189           float xx = xy_distance * sin_rot_angle - horiz_offset * cos_rot_angle;
00190           // Calculate temporal Y, use absolute value
00191           float yy = xy_distance * cos_rot_angle + horiz_offset * sin_rot_angle;
00192           if (xx < 0) xx=-xx;
00193           if (yy < 0) yy=-yy;
00194     
00195           // Get 2points calibration values,Linear interpolation to get distance
00196           // correction for X and Y, that means distance correction use
00197           // different value at different distance
00198           float distance_corr_x = 0;
00199           float distance_corr_y = 0;
00200           if (corrections.two_pt_correction_available) {
00201             distance_corr_x = 
00202               (corrections.dist_correction - corrections.dist_correction_x)
00203                 * (xx - 2.4) / (25.04 - 2.4) 
00204               + corrections.dist_correction_x;
00205             distance_corr_x -= corrections.dist_correction;
00206             distance_corr_y = 
00207               (corrections.dist_correction - corrections.dist_correction_y)
00208                 * (yy - 1.93) / (25.04 - 1.93)
00209               + corrections.dist_correction_y;
00210             distance_corr_y -= corrections.dist_correction;
00211           }
00212   
00213           float distance_x = distance + distance_corr_x;
00218           xy_distance = distance_x * cos_vert_angle + vert_offset * sin_vert_angle ;
00220           x = xy_distance * sin_rot_angle - horiz_offset * cos_rot_angle;
00221   
00222           float distance_y = distance + distance_corr_y;
00223           xy_distance = distance_y * cos_vert_angle + vert_offset * sin_vert_angle ;
00228           y = xy_distance * cos_rot_angle + horiz_offset * sin_rot_angle;
00229   
00230           // Using distance_y is not symmetric, but the velodyne manual
00231           // does this.
00236           z = distance_y * sin_vert_angle + vert_offset*cos_vert_angle;
00237   
00239           float x_coord = y;
00240           float y_coord = -x;
00241           float z_coord = z;
00242   
00245           float min_intensity = corrections.min_intensity;
00246           float max_intensity = corrections.max_intensity;
00247   
00248           intensity = raw->blocks[i].data[k+2];
00249   
00250           float focal_offset = 256 
00251                              * (1 - corrections.focal_distance / 13100) 
00252                              * (1 - corrections.focal_distance / 13100);
00253           float focal_slope = corrections.focal_slope;
00254           intensity += focal_slope * (abs(focal_offset - 256 * 
00255             (1 - static_cast<float>(tmp.uint)/65535)*(1 - static_cast<float>(tmp.uint)/65535)));
00256           intensity = (intensity < min_intensity) ? min_intensity : intensity;
00257           intensity = (intensity > max_intensity) ? max_intensity : intensity;
00258   
00259           if (pointInRange(distance)) {
00260   
00261             // convert polar coordinates to Euclidean XYZ
00262             VPoint point;
00263             point.ring = corrections.laser_ring;
00264             point.x = x_coord;
00265             point.y = y_coord;
00266             point.z = z_coord;
00267             point.intensity = (uint8_t) intensity;
00268   
00269             // append this point to the cloud
00270             pc.points.push_back(point);
00271             ++pc.width;
00272           }
00273         }
00274       }
00275     }
00276   }
00277   
00283   void RawData::unpack_vlp16(const velodyne_msgs::VelodynePacket &pkt,
00284                              VPointCloud &pc)
00285   {
00286     float azimuth;
00287     float azimuth_diff;
00288     float last_azimuth_diff;
00289     float azimuth_corrected_f;
00290     int azimuth_corrected;
00291     float x, y, z;
00292     float intensity;
00293     uint8_t dsr;
00294     
00295     const raw_packet_t *raw = (const raw_packet_t *) &pkt.data[0];
00296 
00297     for (int block = 0; block < BLOCKS_PER_PACKET; block++) {
00298       assert(0xEEFF == raw->blocks[block].header);
00299       azimuth = (float)(raw->blocks[block].rotation);
00300       if (block < (BLOCKS_PER_PACKET-1)){
00301         azimuth_diff = (float)((36000 + raw->blocks[block+1].rotation - raw->blocks[block].rotation)%36000);
00302         last_azimuth_diff = azimuth_diff;
00303       }else{
00304         azimuth_diff = last_azimuth_diff;
00305       }
00306 
00307       for (int firing=0, k=0; firing < VLP16_FIRINGS_PER_BLOCK; firing++){
00308         for (int dsr=0; dsr < VLP16_SCANS_PER_FIRING; dsr++, k+=RAW_SCAN_SIZE){
00309           velodyne_pointcloud::LaserCorrection &corrections = 
00310             calibration_.laser_corrections[dsr];
00311 
00313           union two_bytes tmp;
00314           tmp.bytes[0] = raw->blocks[block].data[k];
00315           tmp.bytes[1] = raw->blocks[block].data[k+1];
00316           
00318           azimuth_corrected_f = azimuth + (azimuth_diff * ((dsr*VLP16_DSR_TOFFSET) + (firing*VLP16_FIRING_TOFFSET)) / VLP16_BLOCK_TDURATION);
00319           azimuth_corrected = ((int)round(azimuth_corrected_f)) % 36000;
00320           
00321           /*condition added to avoid calculating points which are not
00322             in the interesting defined area (min_angle < area < max_angle)*/
00323           if ((azimuth_corrected >= config_.min_angle 
00324                && azimuth_corrected <= config_.max_angle 
00325                && config_.min_angle < config_.max_angle)
00326                ||(config_.min_angle > config_.max_angle 
00327                && (azimuth_corrected <= config_.max_angle 
00328                || azimuth_corrected >= config_.min_angle))){
00329             float distance = tmp.uint * DISTANCE_RESOLUTION;
00330             distance += corrections.dist_correction;
00331             
00332             float cos_vert_angle = corrections.cos_vert_correction;
00333             float sin_vert_angle = corrections.sin_vert_correction;
00334             float cos_rot_correction = corrections.cos_rot_correction;
00335             float sin_rot_correction = corrections.sin_rot_correction;
00336     
00337             // cos(a-b) = cos(a)*cos(b) + sin(a)*sin(b)
00338             // sin(a-b) = sin(a)*cos(b) - cos(a)*sin(b)
00339             float cos_rot_angle = 
00340               cos_rot_table_[azimuth_corrected] * cos_rot_correction + 
00341               sin_rot_table_[azimuth_corrected] * sin_rot_correction;
00342             float sin_rot_angle = 
00343               sin_rot_table_[azimuth_corrected] * cos_rot_correction - 
00344               cos_rot_table_[azimuth_corrected] * sin_rot_correction;
00345     
00346             float horiz_offset = corrections.horiz_offset_correction;
00347             float vert_offset = corrections.vert_offset_correction;
00348     
00349             // Compute the distance in the xy plane (w/o accounting for rotation)
00354             float xy_distance = distance * cos_vert_angle + vert_offset * sin_vert_angle;
00355     
00356             // Calculate temporal X, use absolute value.
00357             float xx = xy_distance * sin_rot_angle - horiz_offset * cos_rot_angle;
00358             // Calculate temporal Y, use absolute value
00359             float yy = xy_distance * cos_rot_angle + horiz_offset * sin_rot_angle;
00360             if (xx < 0) xx=-xx;
00361             if (yy < 0) yy=-yy;
00362       
00363             // Get 2points calibration values,Linear interpolation to get distance
00364             // correction for X and Y, that means distance correction use
00365             // different value at different distance
00366             float distance_corr_x = 0;
00367             float distance_corr_y = 0;
00368             if (corrections.two_pt_correction_available) {
00369               distance_corr_x = 
00370                 (corrections.dist_correction - corrections.dist_correction_x)
00371                   * (xx - 2.4) / (25.04 - 2.4) 
00372                 + corrections.dist_correction_x;
00373               distance_corr_x -= corrections.dist_correction;
00374               distance_corr_y = 
00375                 (corrections.dist_correction - corrections.dist_correction_y)
00376                   * (yy - 1.93) / (25.04 - 1.93)
00377                 + corrections.dist_correction_y;
00378               distance_corr_y -= corrections.dist_correction;
00379             }
00380     
00381             float distance_x = distance + distance_corr_x;
00386             xy_distance = distance_x * cos_vert_angle + vert_offset * sin_vert_angle ;
00387             x = xy_distance * sin_rot_angle - horiz_offset * cos_rot_angle;
00388     
00389             float distance_y = distance + distance_corr_y;
00394             xy_distance = distance_y * cos_vert_angle + vert_offset * sin_vert_angle ;
00395             y = xy_distance * cos_rot_angle + horiz_offset * sin_rot_angle;
00396     
00397             // Using distance_y is not symmetric, but the velodyne manual
00398             // does this.
00403             z = distance_y * sin_vert_angle + vert_offset*cos_vert_angle;
00404   
00405     
00407             float x_coord = y;
00408             float y_coord = -x;
00409             float z_coord = z;
00410     
00413             float min_intensity = corrections.min_intensity;
00414             float max_intensity = corrections.max_intensity;
00415     
00416             intensity = raw->blocks[block].data[k+2];
00417     
00418             float focal_offset = 256 
00419                                * (1 - corrections.focal_distance / 13100) 
00420                                * (1 - corrections.focal_distance / 13100);
00421             float focal_slope = corrections.focal_slope;
00422             intensity += focal_slope * (abs(focal_offset - 256 * 
00423               (1 - tmp.uint/65535)*(1 - tmp.uint/65535)));
00424             intensity = (intensity < min_intensity) ? min_intensity : intensity;
00425             intensity = (intensity > max_intensity) ? max_intensity : intensity;
00426     
00427             if (pointInRange(distance)) {
00428     
00429               // convert polar coordinates to Euclidean XYZ
00430               VPoint point;
00431               point.ring = corrections.laser_ring;
00432               point.x = x_coord;
00433               point.y = y_coord;
00434               point.z = z_coord;
00435               point.intensity = (uint8_t) intensity;
00436     
00437               // append this point to the cloud
00438               pc.points.push_back(point);
00439               ++pc.width;
00440             }
00441           }
00442         }
00443       }
00444     }
00445   }  
00446 
00447 } // namespace velodyne_rawdata


velodyne_pointcloud
Author(s): Jack O'Quin, Piyush Khandelwal, Jesse Vera
autogenerated on Thu Aug 27 2015 15:37:05