dinast_grabber.cpp 16.3 KB
Newer Older
1 2 3 4
/*
 * Software License Agreement (BSD License)
 *
 *  Point Cloud Library (PCL) - www.pointclouds.org
5
 *  Copyright (c) 2012-, Open Perception, Inc.
6 7 8 9 10 11 12 13 14 15 16 17 18
 *
 *  All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions
 *  are met:
 *
 *   * Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *   * Redistributions in binary form must reproduce the above
 *     copyright notice, this list of conditions and the following
 *     disclaimer in the documentation and/or other materials provided
 *     with the distribution.
19
 *   * Neither the name of the copyright holder(s) nor the names of its
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
 *     contributors may be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 *  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 *  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 *  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 *
36 37
 * $Id$
 *
38 39 40 41 42
 */

#include <pcl/pcl_config.h>
#include <pcl/io/dinast_grabber.h>

43 44
using namespace std;

45
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
46
pcl::DinastGrabber::DinastGrabber (const int device_position)
47
  : image_width_ (320)
48 49 50
  , image_height_ (240)
  , sync_packet_size_ (512)
  , fov_ (64. * M_PI / 180.)
51 52 53 54
  , context_ (NULL)
  , bulk_ep_ (std::numeric_limits<unsigned char>::max ())
  , second_image_ (false)
  , running_ (false)
55
{
56 57
  image_size_ = image_width_ * image_height_;
  dist_max_2d_ = 1. / (image_width_ / 2.);
58
  onInit(device_position);
59 60
  
  point_cloud_signal_ = createSignal<sig_cb_dinast_point_cloud> ();
61
  raw_buffer_ = new unsigned char[(RGB16 * (image_size_) + sync_packet_size_) * 2];
62
}
R
Radu B. Rusu 已提交
63

64
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
R
Radu B. Rusu 已提交
65
pcl::DinastGrabber::~DinastGrabber () throw ()
66
{
67 68 69 70 71 72 73 74 75
  try
  {
    stop ();

    libusb_exit (context_);
    
    // Release the interface
    libusb_release_interface (device_handle_, 0);
    // Close it
A
Alexandru Eugen Ichim 已提交
76
    libusb_close (device_handle_); 
77 78
    delete[] raw_buffer_;
    delete[] image_;
79 80 81
  }
  catch (...)
  {
A
Alexandru Eugen Ichim 已提交
82
    // Destructor never throws
83
  }
84 85
}

86
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
87 88 89
bool
pcl::DinastGrabber::isRunning () const
{
90
  return (running_);
91 92
}

93
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
R
Radu B. Rusu 已提交
94
float
95
pcl::DinastGrabber::getFramesPerSecond () const
96
{ 
97 98
  static double last = pcl::getTime ();
  double now = pcl::getTime (); 
A
Alexandru Eugen Ichim 已提交
99
  float rate = 1 / float(now - last);
100 101 102
  last = now; 

  return (rate);
103 104
}

105
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
106
void
107 108 109
pcl::DinastGrabber::onInit (const int device_position)
{
  setupDevice (device_position);
110
  capture_thread_ = boost::thread (&DinastGrabber::captureThreadFunction, this);
111
  image_ = new unsigned char [image_size_];
112 113 114 115 116
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void
pcl::DinastGrabber::setupDevice (int device_position, const int id_vendor, const int id_product)
117
{
118
  int device_position_counter = 0;
119
  
A
Alexandru Eugen Ichim 已提交
120
  // Initialize libusb
121
  int ret=libusb_init (&context_);
122 123 124
  std::stringstream sstream;
  if (ret != 0)
  {
125
    sstream << "[pcl::DinastGrabber::setupDevice] libusb initialization failure, LIBUSB_ERROR: "<< ret;
A
Alexandru Eugen Ichim 已提交
126
    PCL_THROW_EXCEPTION (pcl::IOException, sstream.str ());
127
  }
128
  
129
  libusb_set_debug (context_, 3);
130
  libusb_device **devs = NULL;
131
  
132
  // Get the list of USB devices
133
  ssize_t number_devices = libusb_get_device_list (context_, &devs);
134

135
  if (number_devices < 0)
136
    PCL_THROW_EXCEPTION (pcl::IOException, "[pcl::DinastGrabber::setupDevice] No USB devices found!");
137 138
  
  // Iterate over all devices
139
  for (ssize_t i = 0; i < number_devices; ++i)
140 141
  {
    libusb_device_descriptor desc;
142

143 144 145 146 147
    if (libusb_get_device_descriptor (devs[i], &desc) < 0)
    {
      // Free the list
      libusb_free_device_list (devs, 1);
      // Return a NULL device
148
      return;
149 150 151 152 153 154 155 156 157
    }

    if (desc.idVendor != id_vendor || desc.idProduct != id_product)
      continue;
    libusb_config_descriptor *config;
    // Get the configuration descriptor
    libusb_get_config_descriptor (devs[i], 0, &config);

    // Iterate over all interfaces available
R
nm  
Radu B. Rusu 已提交
158
    for (int f = 0; f < int (config->bNumInterfaces); ++f)
159 160
    {
      // Iterate over the number of alternate settings
161
      for (int j = 0; j < config->interface[f].num_altsetting; ++j)
162 163
      {
        // Iterate over the number of end points present
R
nm  
Radu B. Rusu 已提交
164
        for (int k = 0; k < int (config->interface[f].altsetting[j].bNumEndpoints); ++k) 
165
        {
166
          if (config->interface[f].altsetting[j].endpoint[k].bmAttributes == LIBUSB_TRANSFER_TYPE_BULK)
167 168
          {
            // Initialize the bulk end point
169
            bulk_ep_ = config->interface[f].altsetting[j].endpoint[k].bEndpointAddress;
170 171 172 173 174
            break;
          }
        }
      }
    }
175
    device_position_counter++;
176
    if (device_position_counter == device_position)
177
      libusb_open(devs[i], &device_handle_);
178 179 180
    // Free the configuration descriptor
    libusb_free_config_descriptor (config);
  }
181
  
182 183 184
  // Free the list
  libusb_free_device_list (devs, 1);
  
A
Alexandru Eugen Ichim 已提交
185
  // Check if device founded if not notify
186
  if (device_handle_ == NULL)
187
    PCL_THROW_EXCEPTION (pcl::IOException, "[pcl::DinastGrabber::setupDevice] Failed to find any DINAST devices attached");
188
  
A
Alexandru Eugen Ichim 已提交
189 190
  // Claim device interface
  if (libusb_claim_interface(device_handle_, 0) < 0)
191
     PCL_THROW_EXCEPTION (pcl::IOException, "[pcl::DinastGrabber::setupDevice] Failed to open DINAST device");
192 193 194

}

195
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
196
std::string
197
pcl::DinastGrabber::getDeviceVersion ()
198
{
199
  unsigned char data[21];
200
  if (!USBRxControlData (CMD_GET_VERSION, data, 21))
201
     PCL_THROW_EXCEPTION (pcl::IOException, "[pcl::DinastGrabber::getDeviceVersion] Error trying to get device version");
202
 
203
  //data[21] = 0;
R
nm  
Radu B. Rusu 已提交
204
  return (std::string (reinterpret_cast<const char*> (data)));
205 206
}

207
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
208 209 210
void
pcl::DinastGrabber::start ()
{
211
  unsigned char ctrl_buf[1];
212
  if (!USBTxControlData (CMD_READ_START, ctrl_buf, 1))
213
    PCL_THROW_EXCEPTION (pcl::IOException, "[pcl::DinastGrabber::start] Could not start the USB data reading");
214
  running_ = true;
215 216
}

217
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
218
void
R
Radu B. Rusu 已提交
219
pcl::DinastGrabber::stop ()
220
{
221
  unsigned char ctrl_buf[1];
222
  if (!USBTxControlData (CMD_READ_STOP, ctrl_buf, 1))
223
    PCL_THROW_EXCEPTION (pcl::IOException, "[pcl::DinastGrabber::stop] Could not stop the USB data reading");
224
  running_ = false;
225 226
}

227
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
228
void
229
pcl::DinastGrabber::readImage ()
230 231
{
  // Do we have enough data in the buffer for the second image?
232
  if (second_image_)
233
  {
234
    for (int i = 0; i < image_size_; ++i)
235
      image_[i] = g_buffer_[i];
236

237
    g_buffer_.rerase (g_buffer_.begin (), g_buffer_.begin () + image_size_);
238
    second_image_ = false;
239
    
240
    return;
241 242 243 244 245
  }

  // Read data in two image blocks until we get a header
  bool first_image = false;
  int data_adr = -1;
246
  while (!second_image_)
247 248 249
  {
    // Read at least two images in synchronous mode
    int actual_length;
250
    int res = libusb_bulk_transfer (device_handle_, bulk_ep_, raw_buffer_,
251
                                    RGB16 * (image_size_) + sync_packet_size_, &actual_length, 1000);
252 253
    if (res != 0 || actual_length == 0)
    {
254
      memset (&image_[0], 0x00, image_size_);
255
      PCL_THROW_EXCEPTION (pcl::IOException, "[pcl::DinastGrabber::readImage] USB read error!");
256 257 258
    }

    // Copy data from the USB port if we actually read anything
259
    PCL_DEBUG ("[pcl::DinastGrabber::readImage] Read: %d, size of the buffer: %d\n" ,actual_length, g_buffer_.size ());
260
    
261
    // Copy data into the buffer
262 263
    int back = int (g_buffer_.size ());
    g_buffer_.resize (back + actual_length);
264

265
    for (int i = 0; i < actual_length; ++i)
266
      g_buffer_[back++] = raw_buffer_[i];
267

268
    PCL_DEBUG ("[pcl::DinastGrabber::readImage] Buffer size: %d\n", g_buffer_.size());
269 270 271 272 273

    // Check if the header is set already
    if (!first_image && data_adr == -1)
    {
      data_adr = checkHeader ();
274
      PCL_DEBUG ("[pcl::DinastGrabber::readImage] Searching for header at: %d", data_adr);
275 276 277 278
    }

    // Is there enough data in the buffer to return one image, and did we find the header?
    // If not, go back and read some more data off the USB port
279
    // Else, read the data, clean the g_buffer_, return to the user
280
    if (!first_image && (g_buffer_.size () >= static_cast<boost::circular_buffer<unsigned char>::size_type> (image_size_ + data_adr)) && data_adr != -1)
281 282
    {
      // An image found. Copy it from the buffer into the user given buffer
283

284
      for (int i = 0; i < image_size_; ++i)
285
        image_[i] = g_buffer_[data_adr + i];
286

287
      // Pop the data from the global buffer. 
288
      g_buffer_.rerase (g_buffer_.begin(), g_buffer_.begin() + data_adr + image_size_);
289 290 291
      first_image = true;
    }

292
    if (first_image && g_buffer_.size () >= static_cast<boost::circular_buffer<unsigned char>::size_type> (image_size_))
293 294 295 296
      break;
  }
}

297
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
298 299
pcl::PointCloud<pcl::PointXYZI>::Ptr
pcl::DinastGrabber::getXYZIPointCloud ()
300
{
301
  pcl::PointCloud<pcl::PointXYZI>::Ptr cloud (new pcl::PointCloud<pcl::PointXYZI>);
302
  
303 304 305
  cloud->points.resize (image_size_);
  cloud->width = image_width_;
  cloud->height = image_height_;
306
  cloud->is_dense = false;
307
  
308 309
  int depth_idx = 0;

310
  for (int x = 0; x < static_cast<int> (cloud->width); ++x)
311
  {
312
    for (int y = 0; y < static_cast<int> (cloud->height); ++y, ++depth_idx)
313
    {
314
      double pixel = image_[x + image_width_ * y];
315

316 317 318
      // Correcting distortion, data emprically got in a calibration test
      double xc = static_cast<double> (x - image_width_ / 2);
      double yc = static_cast<double> (y - image_height_ / 2);
319
      double r1 = sqrt (xc * xc + yc * yc);
320 321
      double r2 = r1 * r1;
      double r3 = r1 * r2;
322 323 324
      double A = -2e-5 * r3 + 0.004 * r2 + 0.1719 * r1 + 350.03;
      double B = -2e-9 * r3 + 3e-7 * r2 - 1e-5 * r1 - 0.01;

A
Alexandru Eugen Ichim 已提交
325
      // Compute distance
326 327
      if (pixel > A)
        pixel = A;
328
      double dy = y*0.1;
329 330
      double dist = (log (static_cast<double> (pixel / A)) / B - dy) * (7E-07*r3 - 0.0001*r2 + 0.004*r1 + 0.9985) * 1.5;
      double theta_colati = fov_ * r1 * dist_max_2d_;
331 332
      double c_theta = cos (theta_colati);
      double s_theta = sin (theta_colati);
333 334
      double c_ksai = static_cast<double> (x - 160) / r1;
      double s_ksai = static_cast<double> (y - 120) / r1;
335 336
      cloud->points[depth_idx].x = static_cast<float> ((dist * s_theta * c_ksai) / 500.0 + 0.5);
      cloud->points[depth_idx].y = static_cast<float> ((dist * s_theta * s_ksai) / 500.0 + 0.5);
337 338 339 340 341
      cloud->points[depth_idx].z = static_cast<float> (dist * c_theta);
      if (cloud->points[depth_idx].z < 0.01f)
        cloud->points[depth_idx].z = 0.01f;
      cloud->points[depth_idx].z /= 500.0f;
      cloud->points[depth_idx].intensity = static_cast<float> (pixel);
M
 
Marco A. Gutierrez 已提交
342

343
      
A
Alexandru Eugen Ichim 已提交
344
      // Get rid of the noise
T
tomazas 已提交
345
      if(cloud->points[depth_idx].z > 0.8f || cloud->points[depth_idx].z < 0.02f)
346 347 348 349
      {
        cloud->points[depth_idx].x = std::numeric_limits<float>::quiet_NaN ();
      	cloud->points[depth_idx].y = std::numeric_limits<float>::quiet_NaN ();
      	cloud->points[depth_idx].z = std::numeric_limits<float>::quiet_NaN ();
350
        cloud->points[depth_idx].intensity = static_cast<float> (pixel);
351
      }
352 353
    }
  }
354
  return cloud;
355 356
}

357 358 359 360 361 362
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool
pcl::DinastGrabber::USBRxControlData (const unsigned char req_code,
                                      unsigned char *buffer,
                                      int length)
{
A
Alexandru Eugen Ichim 已提交
363
  // The direction of the transfer is inferred from the requesttype field of the setup packet.
364
  unsigned char requesttype = (LIBUSB_RECIPIENT_DEVICE | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_IN);
A
Alexandru Eugen Ichim 已提交
365
  // The value field for the setup packet
366
  uint16_t value = 0x02;
A
Alexandru Eugen Ichim 已提交
367
  // The index field for the setup packet
368 369
  uint16_t index = 0x08;
  // timeout (in ms) that this function should wait before giving up due to no response being received
A
Alexandru Eugen Ichim 已提交
370
  // For an unlimited timeout, use value 0.
371 372
  uint16_t timeout = 1000;
  
373
  int nr_read = libusb_control_transfer (device_handle_, requesttype,
374
                                         req_code, value, index, buffer, static_cast<uint16_t> (length), timeout);
375
  if (nr_read != int(length))
376
    PCL_THROW_EXCEPTION (pcl::IOException, "[pcl::DinastGrabber::USBRxControlData] Control data error");
377 378 379 380 381 382 383 384 385 386

  return (true);
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool
pcl::DinastGrabber::USBTxControlData (const unsigned char req_code,
                                      unsigned char *buffer,
                                      int length)
{
A
Alexandru Eugen Ichim 已提交
387
  // The direction of the transfer is inferred from the requesttype field of the setup packet.
388
  unsigned char requesttype = (LIBUSB_RECIPIENT_DEVICE | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_OUT);
A
Alexandru Eugen Ichim 已提交
389
  // The value field for the setup packet
390
  uint16_t value = 0x01;
A
Alexandru Eugen Ichim 已提交
391
  // The index field for the setup packet
392 393
  uint16_t index = 0x00;
  // timeout (in ms) that this function should wait before giving up due to no response being received
A
Alexandru Eugen Ichim 已提交
394
  // For an unlimited timeout, use value 0.
395 396
  uint16_t timeout = 1000;
  
397
  int nr_read = libusb_control_transfer (device_handle_, requesttype,
398
                                         req_code, value, index, buffer, static_cast<uint16_t> (length), timeout);
399 400 401
  if (nr_read != int(length))
  {
    std::stringstream sstream;
402
    sstream << "[pcl::DinastGrabber::USBTxControlData] USB control data error, LIBUSB_ERROR: " << nr_read;
403
    PCL_THROW_EXCEPTION (pcl::IOException, sstream.str ());
404 405 406 407 408 409 410 411 412 413
  }

  return (true);
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
int
pcl::DinastGrabber::checkHeader ()
{
  // We need at least 2 full sync packets, in case the header starts at the end of the first sync packet to 
414
  // guarantee that the index returned actually exists in g_buffer_ (we perform no checking in the rest of the code)
415
  if (g_buffer_.size () < static_cast<boost::circular_buffer<unsigned char>::size_type> (2 * sync_packet_size_))
416 417 418 419
    return (-1);

  int data_ptr = -1;

420
  for (size_t i = 0; i < g_buffer_.size (); ++i)
421
  {
422 423 424 425
    if ((g_buffer_[i + 0] == 0xAA) && (g_buffer_[i + 1] == 0xAA) &&
        (g_buffer_[i + 2] == 0x44) && (g_buffer_[i + 3] == 0x44) &&
        (g_buffer_[i + 4] == 0xBB) && (g_buffer_[i + 5] == 0xBB) &&
        (g_buffer_[i + 6] == 0x77) && (g_buffer_[i + 7] == 0x77))
426
    {
427
      data_ptr = int (i) + sync_packet_size_;
428 429 430 431 432 433 434
      break;
    }
  }

  return (data_ptr);
}

435 436 437 438 439 440
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void 
pcl::DinastGrabber::captureThreadFunction ()
{
  while (true)
  {
A
Alexandru Eugen Ichim 已提交
441
    // Lock before checking running flag
442
    boost::unique_lock<boost::mutex> capture_lock (capture_mutex_);
443 444 445
    if(running_)
    {
      readImage ();
446
    
447 448 449 450
      // Check for point clouds slots
      if (num_slots<sig_cb_dinast_point_cloud> () > 0 )
        point_cloud_signal_->operator() (getXYZIPointCloud ());
    } 
451 452 453
    capture_lock.unlock ();
  }
}