$search
00001 /********************************************************************* 00002 * Software License Agreement (BSD License) 00003 * 00004 * Copyright (c) 2009-2010, Willow Garage, Inc. 00005 * All rights reserved. 00006 * 00007 * Redistribution and use in source and binary forms, with or without 00008 * modification, are permitted provided that the following conditions 00009 * are met: 00010 * 00011 * * Redistributions of source code must retain the above copyright 00012 * notice, this list of conditions and the following disclaimer. 00013 * * Redistributions in binary form must reproduce the above 00014 * copyright notice, this list of conditions and the following 00015 * disclaimer in the documentation and/or other materials provided 00016 * with the distribution. 00017 * * Neither the name of the Willow Garage nor the names of its 00018 * contributors may be used to endorse or promote products derived 00019 * from this software without specific prior written permission. 00020 * 00021 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 00022 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 00023 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 00024 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 00025 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 00026 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 00027 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 00028 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 00029 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 00030 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 00031 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 00032 * POSSIBILITY OF SUCH DAMAGE. 00033 *********************************************************************/ 00034 00035 // TODO: doxygen mainpage 00036 00037 // TODO: Timeout receiving packet. 00038 // TODO: Check that partial EOF missing frames get caught. 00039 // @todo Do the triggering based on a stream of incoming timestamps. 00040 00041 #include <ros/node_handle.h> 00042 #include <sensor_msgs/Image.h> 00043 #include <sensor_msgs/CameraInfo.h> 00044 #include <sensor_msgs/fill_image.h> 00045 #include <sensor_msgs/SetCameraInfo.h> 00046 #include <driver_base/SensorLevels.h> 00047 #include <diagnostic_updater/diagnostic_updater.h> 00048 #include <diagnostic_updater/update_functions.h> 00049 #include <diagnostic_updater/publisher.h> 00050 #include <self_test/self_test.h> 00051 #include <stdlib.h> 00052 #include <limits> 00053 #include <math.h> 00054 #include <linux/sysctl.h> 00055 #include <iostream> 00056 #include <fstream> 00057 #include <wge100_camera/BoardConfig.h> 00058 #include <boost/tokenizer.hpp> 00059 #include <boost/format.hpp> 00060 #include <driver_base/driver.h> 00061 #include <driver_base/driver_node.h> 00062 #include <timestamp_tools/trigger_matcher.h> 00063 #include <camera_calibration_parsers/parse.h> 00064 #include <image_transport/image_transport.h> 00065 #include <sstream> 00066 00067 #include <wge100_camera/WGE100CameraConfig.h> 00068 00069 #include "wge100_camera/wge100lib.h" 00070 #include "wge100_camera/host_netutil.h" 00071 #include "wge100_camera/mt9v.h" 00072 00073 #define RMEM_MAX_RECOMMENDED 20000000 00074 00075 // @todo this should come straight from camera_calibration_parsers as soon as a release happens. 00076 struct SimpleMatrix 00077 { 00078 int rows; 00079 int cols; 00080 const double* data; 00081 00082 SimpleMatrix(int rows, int cols, const double* data) 00083 : rows(rows), cols(cols), data(data) 00084 {} 00085 }; 00086 00087 std::ostream& operator << (std::ostream& out, const SimpleMatrix& m) 00088 { 00089 for (int i = 0; i < m.rows; ++i) { 00090 for (int j = 0; j < m.cols; ++j) { 00091 out << m.data[m.cols*i+j] << " "; 00092 } 00093 out << std::endl; 00094 } 00095 return out; 00096 } 00097 00098 bool writeCalibrationIni(std::ostream& out, const std::string& camera_name, 00099 const sensor_msgs::CameraInfo& cam_info) 00100 { 00101 out.precision(5); 00102 out << std::fixed; 00103 00104 out << "# Camera intrinsics\n\n"; 00106 out << "[image]\n\n"; 00107 out << "width\n" << cam_info.width << "\n\n"; 00108 out << "height\n" << cam_info.height << "\n\n"; 00109 out << "[" << camera_name << "]\n\n"; 00110 00111 out << "camera matrix\n" << SimpleMatrix(3, 3, &cam_info.K[0]); 00112 out << "\ndistortion\n" << SimpleMatrix(1, 5, &cam_info.D[0]); 00113 out << "\n\nrectification\n" << SimpleMatrix(3, 3, &cam_info.R[0]); 00114 out << "\nprojection\n" << SimpleMatrix(3, 4, &cam_info.P[0]); 00115 00116 return true; 00117 } 00118 00119 00120 00121 00122 // The FrameTimeFilter class takes a stream of image arrival times that 00123 // include time due to system load and network asynchrony, and generates a 00124 // (hopefully) more steady stream of arrival times. The filtering is based 00125 // on the assumption that the frame rate is known, and that often the time 00126 // stamp is correct. The general idea of the algorithm is: 00127 // 00128 // anticipated_time_ = previous time stamp + frame_period_ 00129 // is a good estimate of the current frame time stamp. 00130 // 00131 // Take the incoming time stamp, or the anticipated_time_, whichever one is 00132 // lower. The rationale here is that when the latency is low or zero, the 00133 // incoming time stamp is correct and will dominate. If latency occurs, the 00134 // anticipated_time_ will be used. 00135 // 00136 // To avoid problems with clock skew, max_skew indicates the maximum 00137 // expected clock skew. frame_period_ is set to correspond to the 00138 // slowest expected rate when clock skew is taken into account. If 00139 // frame_period_ was less than the actual frame rate, then 00140 // anticipated_time_ would always dominate, and the output time stamps 00141 // would slowly diverge from the actual time stamps. 00142 // 00143 // Because the frame rate may sometimes skip around in an unanticipated way 00144 // the filter detects if anticipated_time_ has dominated by more than 00145 // locked_threshold_ more than max_recovery_frames_ in a row. In that case, 00146 // it picks the lowest latency input value that occurs during the 00147 // max_recovery_frames_ to reset anticipated_time_. 00148 // 00149 // Finally, if the filter misses too many frames in a row, it assumes that 00150 // its anticipated_time is invalid and immediately resets the filter. 00151 00152 class FrameTimeFilter 00153 { 00154 public: 00155 FrameTimeFilter(double frame_rate = 1., double late_locked_threshold = 1e-3, double early_locked_threshold = 3e-3, int early_recovery_frames = 5, int max_recovery_frames = 10, double max_skew = 1.001, double max_skipped_frames = 100) 00156 { 00157 frame_period_ = max_skew / frame_rate; 00158 max_recovery_frames_ = max_recovery_frames; 00159 early_recovery_frames_ = early_recovery_frames; 00160 late_locked_threshold_ = late_locked_threshold; 00161 early_locked_threshold_ = early_locked_threshold; 00162 max_skipped_frames_ = max_skipped_frames; 00163 last_frame_number_ = 0; // Don't really care about this value. 00164 reset_filter(); 00165 } 00166 00167 void reset_filter() 00168 { 00169 relock_time_ = anticipated_time_ = std::numeric_limits<double>::infinity(); 00170 unlocked_count_ = late_locked_threshold_; 00171 } 00172 00173 double run(double in_time, int frame_number) 00174 { 00175 double out_time = in_time; 00176 int delta = (frame_number - last_frame_number_) & 0xFFFF; // Hack because the frame rate currently wraps around too early. 00177 00178 if (delta > max_skipped_frames_) 00179 { 00180 ROS_WARN_NAMED("frame_time_filter", "FrameTimeFilter missed too many frames from #%u to #%u. Resetting.", last_frame_number_, frame_number); 00181 reset_filter(); 00182 } 00183 00184 anticipated_time_ += frame_period_ * delta; 00185 relock_time_ += frame_period_ * delta; 00186 00187 if (out_time < relock_time_) 00188 relock_time_ = out_time; 00189 00190 //ROS_DEBUG("in: %f ant: %f", in_time, anticipated_time_); 00191 00192 bool early_trigger = false; 00193 if (out_time < anticipated_time_ - early_locked_threshold_ && finite(anticipated_time_)) 00194 { 00195 ROS_WARN_NAMED("frame_time_filter", "FrameTimeFilter saw frame #%u was early by %f.", frame_number, anticipated_time_ - out_time); 00196 early_trigger = true; 00197 } 00198 if (out_time < anticipated_time_) 00199 { 00200 anticipated_time_ = out_time; 00201 relock_time_ = std::numeric_limits<double>::infinity(); 00202 unlocked_count_ = 0; 00203 } 00204 else if (out_time > anticipated_time_ + late_locked_threshold_) 00205 { 00206 unlocked_count_++; 00207 if (unlocked_count_ > max_recovery_frames_) 00208 { 00209 ROS_DEBUG_NAMED("frame_time_filter", "FrameTimeFilter lost lock at frame #%u, shifting by %f.", frame_number, relock_time_ - anticipated_time_); 00210 anticipated_time_ = relock_time_; 00211 relock_time_ = std::numeric_limits<double>::infinity(); 00212 } 00213 //else 00214 // ROS_DEBUG("FrameTimeFilter losing lock at frame #%u, lock %i/%i, off by %f.", frame_number, unlocked_count_, max_recovery_frames_, anticipated_time_ - out_time); 00215 out_time = anticipated_time_; 00216 } 00217 00218 last_frame_number_ = frame_number; 00219 00220 return early_trigger ? -out_time : out_time; 00221 } 00222 00223 private: 00224 int max_recovery_frames_; 00225 int unlocked_count_; 00226 int last_frame_number_; 00227 int max_skipped_frames_; 00228 int early_recovery_frames_; 00229 double early_locked_threshold_; 00230 double late_locked_threshold_; 00231 double anticipated_time_; 00232 double relock_time_; 00233 double frame_period_; 00234 }; 00235 00239 class SlowTriggerFilter 00240 { 00241 private: 00242 bool has_warned_; 00243 int before_warn_; 00244 int before_clear_; 00245 int countdown_; 00246 00247 public: 00248 SlowTriggerFilter(int before_warn, int before_clear) 00249 { 00250 has_warned_ = false; 00251 before_clear_ = before_clear; 00252 before_warn_ = before_warn; 00253 countdown_ = before_warn_; 00254 } 00255 00256 bool badFrame() 00257 { 00258 if (!has_warned_) 00259 { 00260 if (!countdown_--) 00261 { 00262 has_warned_ = true; 00263 countdown_ = before_warn_; 00264 return true; 00265 } 00266 } 00267 else 00268 countdown_ = before_clear_; 00269 00270 return false; 00271 } 00272 00273 void goodFrame() 00274 { 00275 if (has_warned_) 00276 { 00277 if (!countdown_--) 00278 { 00279 has_warned_ = false; 00280 countdown_ = before_clear_; 00281 } 00282 } 00283 else 00284 countdown_ = before_warn_; 00285 } 00286 }; 00287 00288 class WGE100CameraDriver : public driver_base::Driver 00289 { 00290 friend class WGE100CameraNode; 00291 00292 public: 00293 typedef wge100_camera::WGE100CameraConfig Config; 00294 Config config_; 00295 00296 private: 00297 // Services 00298 ros::ServiceClient trig_service_; 00299 00300 // Video mode 00301 double desired_freq_; 00302 00303 // Statistics 00304 int rmem_max_; 00305 int missed_eof_count_; 00306 int dropped_packets_; 00307 int massive_frame_losses_; 00308 bool dropped_packet_event_; 00309 int missed_line_count_; 00310 int trigger_matcher_drop_count_; 00311 double last_image_time_; 00312 double driver_start_time_; 00313 unsigned int last_frame_number_; 00314 unsigned int last_partial_frame_number_; 00315 int lost_image_thread_count_; 00316 00317 // Timing 00318 FrameTimeFilter frame_time_filter_; 00319 timestamp_tools::TriggerMatcher trigger_matcher_; 00320 00321 // Link information 00322 sockaddr localMac_; 00323 in_addr localIp_; 00324 00325 // Camera information 00326 std::string hwinfo_; 00327 IpCamList camera_; 00328 MT9VImagerPtr imager_; 00329 MT9VImagerPtr alternate_imager_; 00330 double last_camera_ok_time_; 00331 std::string image_encoding_; 00332 bool next_is_alternate_; 00333 bool first_frame_; 00334 bool enable_alternate_; 00335 bool enable_primary_; 00336 00337 // Threads 00338 boost::shared_ptr<boost::thread> image_thread_; 00339 00340 // Frame function (gets called when a frame arrives). 00341 typedef boost::function<int(size_t, size_t, uint8_t*, ros::Time, bool)> UseFrameFunction; 00342 UseFrameFunction useFrame_; 00343 00344 struct ImagerSettings 00345 { 00346 int height; 00347 int width; 00348 int x_offset; 00349 int y_offset; 00350 int horizontal_binning; 00351 int vertical_binning; 00352 bool auto_black_level; 00353 int black_level; 00354 int black_level_filter_length; 00355 int black_level_step_size; 00356 int brightness; 00357 int gain; 00358 double exposure; 00359 double max_exposure; 00360 double imager_rate; 00361 bool companding; 00362 bool auto_gain; 00363 bool auto_exposure; 00364 bool mirror_x; 00365 bool mirror_y; 00366 bool rotate_180; 00367 }; 00368 00369 int setImagerSettings(MT9VImager &imager, ImagerSettings &cfg) 00370 { 00371 int bintable[5] = { -1, 0, 1, -1, 2 }; 00372 00373 if (imager.setMode(cfg.width, cfg.height, bintable[cfg.horizontal_binning], bintable[cfg.vertical_binning], cfg.imager_rate, cfg.x_offset, cfg.y_offset)) 00374 { 00375 setStatusMessage("Error setting video mode."); 00376 return 1; 00377 } 00378 00379 if (imager.setCompanding(cfg.companding)) 00380 { 00381 setStatusMessage("Error setting companding mode."); 00382 return 1; 00383 } 00384 00385 if (imager.setAgcAec(cfg.auto_gain, cfg.auto_exposure)) 00386 { 00387 setStatusMessage("Error setting AGC/AEC mode. Exposure and gain may be incorrect."); 00388 return 1; 00389 } 00390 00391 if (imager.setGain(cfg.gain)) 00392 { 00393 setStatusMessage("Error setting analog gain."); 00394 return 1; 00395 } 00396 00397 if (imager.setMirror(cfg.mirror_x != cfg.rotate_180, cfg.mirror_y != cfg.rotate_180)) 00398 { 00399 setStatusMessage("Error setting mirroring properties."); 00400 return 1; 00401 } 00402 00403 if (imager.setExposure(cfg.exposure)) 00404 { 00405 setStatusMessage("Error setting exposure."); 00406 return 1; 00407 } 00408 00409 if (imager.setBlackLevel(!cfg.auto_black_level, cfg.black_level, cfg.black_level_step_size, 00410 cfg.black_level_filter_length)) 00411 { 00412 setStatusMessage("Error setting black level."); 00413 return 1; 00414 } 00415 00416 if (imager.setBrightness(cfg.brightness)) 00417 { 00418 setStatusMessage("Error setting brightness."); 00419 return 1; 00420 } 00421 00422 if (imager.setMaxExposure(cfg.max_exposure ? cfg.max_exposure : 0.99 / cfg.imager_rate)) 00423 { 00424 setStatusMessage("Error setting maximum exposure."); 00425 return 1; 00426 } 00427 00428 return 0; 00429 } 00430 00431 00432 public: 00433 WGE100CameraDriver() : 00434 trigger_matcher_(5, 60), // Recovers from frame drop in 5 frames, queues at least 1 second of timestamps. 00435 no_timestamp_warning_filter_(10, 10) // 10 seconds before warning. 10 frames to clear. 00436 { 00437 // Create a new IpCamList to hold the camera list 00438 // wge100CamListInit(&camList); 00439 00440 // Clear statistics 00441 last_camera_ok_time_ = 0; 00442 last_image_time_ = 0; 00443 driver_start_time_ = ros::Time::now().toSec(); 00444 missed_line_count_ = 0; 00445 trigger_matcher_drop_count_ = 0; 00446 missed_eof_count_ = 0; 00447 dropped_packets_ = 0; 00448 lost_image_thread_count_ = 0; 00449 massive_frame_losses_ = 0; 00450 dropped_packet_event_ = false; 00451 } 00452 00453 void config_update(const Config &new_cfg, uint32_t level = 0) 00454 { 00455 config_ = new_cfg; 00456 00457 if (config_.horizontal_binning == 3) // 1, 2 and 4 are legal, dynamic reconfigure ensures that 3 is the only bad case. 00458 { 00459 config_.horizontal_binning = 2; 00460 ROS_WARN("horizontal_binning = 3 is invalid. Setting to 2."); 00461 } 00462 00463 if (config_.vertical_binning == 3) // 1, 2 and 4 are legal, dynamic reconfigure ensures that 3 is the only bad case. 00464 { 00465 config_.vertical_binning = 2; 00466 ROS_WARN("horizontal_binning = 3 is invalid. Setting to 2."); 00467 } 00468 00469 desired_freq_ = config_.ext_trig ? config_.trig_rate : config_.imager_rate; 00470 00472 00473 if (!config_.auto_exposure && config_.exposure > 0.99 / desired_freq_) 00474 { 00475 ROS_WARN("Exposure (%f s) is greater than 99%% of frame period (%f s). Setting to 99%% of frame period.", 00476 config_.exposure, 0.99 / desired_freq_); 00477 config_.exposure = 0.99 * 1 / desired_freq_; 00478 } 00479 /*if (!config_.auto_exposure && config_.exposure > 0.95 / desired_freq_) 00480 { 00481 ROS_WARN("Exposure (%f s) is greater than 95%% of frame period (%f s). You may not achieve full frame rate.", 00482 config_.exposure, 0.95 / desired_freq_); 00483 }*/ 00484 if (config_.auto_exposure && config_.max_exposure && config_.max_exposure > 0.99 / desired_freq_) 00485 { 00486 ROS_WARN("Maximum exposure (%f s) is greater than 99%% of frame period (%f s). Setting to 99%% of frame period.", 00487 config_.max_exposure, 0.99 / desired_freq_); 00488 config_.max_exposure = 0.99 * 1 / desired_freq_; 00489 } 00490 /*if (config_.auto_exposure && config_.max_exposure && config_.max_exposure > 0.95 / desired_freq_) 00491 { 00492 ROS_WARN("Maximum exposure (%f s) is greater than 95%% of frame period (%f s). You may not achieve full frame rate.", 00493 config_.max_exposure, 0.95 / desired_freq_); 00494 }*/ 00495 00496 enable_primary_ = (config_.register_set != wge100_camera::WGE100Camera_AlternateRegisterSet); 00497 enable_alternate_ = (config_.register_set != wge100_camera::WGE100Camera_PrimaryRegisterSet); 00498 } 00499 00500 ~WGE100CameraDriver() 00501 { 00502 // wge100CamListDelAll(&camList); 00503 close(); 00504 } 00505 00506 int get_rmem_max() 00507 { 00508 int rmem_max; 00509 { 00510 std::ifstream f("/proc/sys/net/core/rmem_max"); 00511 f >> rmem_max; 00512 } 00513 return rmem_max; 00514 } 00515 00516 void doOpen() 00517 { 00518 if (state_ != CLOSED) 00519 { 00520 ROS_ERROR("Assertion failing. state_ = %i", state_); 00521 ROS_ASSERT(state_ == CLOSED); 00522 } 00523 00524 ROS_DEBUG("open()"); 00525 00526 int retval; 00527 00528 // Check that rmem_max is large enough. 00529 rmem_max_ = get_rmem_max(); 00530 if (rmem_max_ < RMEM_MAX_RECOMMENDED) 00531 { 00532 ROS_WARN_NAMED("rmem_max", "rmem_max is %i. Buffer overflows and packet loss may occur. Minimum recommended value is 20000000. Updates may not take effect until the driver is restarted. See http://www.ros.org/wiki/wge100_camera/Troubleshooting", rmem_max_); 00533 } 00534 00535 ROS_DEBUG("Redoing discovery."); 00536 const char *errmsg; 00537 int findResult = wge100FindByUrl(config_.camera_url.c_str(), &camera_, SEC_TO_USEC(0.2), &errmsg); 00538 00539 if (findResult) 00540 { 00541 setStatusMessagef("Matching URL %s : %s", config_.camera_url.c_str(), errmsg); 00542 strncpy(camera_.cam_name, "unknown", sizeof(camera_.cam_name)); 00543 camera_.serial = -1; 00544 return; 00545 } 00546 00547 retval = wge100Configure(&camera_, camera_.ip_str, SEC_TO_USEC(0.5)); 00548 if (retval != 0) { 00549 setStatusMessage("IP address configuration failed"); 00550 return; 00551 } 00552 00553 ROS_INFO("Configured camera. Complete URLs: serial://%u@%s#%s name://%s@%s#%s", 00554 camera_.serial, camera_.ip_str, camera_.ifName, camera_.cam_name, camera_.ip_str, camera_.ifName); 00555 00556 camera_.serial = camera_.serial; 00557 hwinfo_ = camera_.hwinfo; 00558 00559 // We are going to receive the video on this host, so we need our own MAC address 00560 if ( wge100EthGetLocalMac(camera_.ifName, &localMac_) != 0 ) { 00561 setStatusMessagef("Unable to get local MAC address for interface %s", camera_.ifName); 00562 return; 00563 } 00564 00565 // We also need our local IP address 00566 if ( wge100IpGetLocalAddr(camera_.ifName, &localIp_) != 0) { 00567 setStatusMessagef("Unable to get local IP address for interface %s", camera_.ifName); 00568 return; 00569 } 00570 00571 if (config_.ext_trig && config_.trig_timestamp_topic.empty()) 00572 { 00573 setStatusMessage("External triggering is selected, but no \"trig_timestamp_topic\" was specified."); 00574 return; 00575 } 00576 00577 // Select data resolution 00578 if ( wge100ImagerSetRes( &camera_, config_.width, config_.height ) != 0) { 00579 setStatusMessage("Error selecting image resolution."); 00580 return; 00581 } 00582 00583 ImagerSettings primary_settings, alternate_settings; 00584 00585 primary_settings.height = config_.height; 00586 primary_settings.width = config_.width; 00587 primary_settings.x_offset = config_.x_offset; 00588 primary_settings.y_offset = config_.y_offset; 00589 primary_settings.horizontal_binning = config_.horizontal_binning; 00590 primary_settings.vertical_binning = config_.vertical_binning; 00591 primary_settings.imager_rate = config_.imager_rate; 00592 primary_settings.companding = config_.companding; 00593 primary_settings.auto_gain = config_.auto_gain; 00594 primary_settings.gain = config_.gain; 00595 primary_settings.auto_exposure = config_.auto_exposure; 00596 primary_settings.exposure = config_.exposure; 00597 primary_settings.mirror_x = config_.mirror_x; 00598 primary_settings.mirror_y = config_.mirror_y; 00599 primary_settings.rotate_180 = config_.rotate_180; 00600 primary_settings.auto_black_level = config_.auto_black_level; 00601 primary_settings.black_level_filter_length = config_.black_level_filter_length; 00602 primary_settings.black_level_step_size = config_.black_level_step_size; 00603 primary_settings.black_level = config_.black_level; 00604 primary_settings.brightness = config_.brightness; 00605 primary_settings.max_exposure = config_.max_exposure; 00606 00607 alternate_settings.height = config_.height; 00608 alternate_settings.width = config_.width; 00609 alternate_settings.x_offset = config_.x_offset; 00610 alternate_settings.y_offset = config_.y_offset; 00611 alternate_settings.horizontal_binning = config_.horizontal_binning; 00612 alternate_settings.vertical_binning = config_.vertical_binning; 00613 alternate_settings.imager_rate = config_.imager_rate; 00614 alternate_settings.companding = config_.companding_alternate; 00615 alternate_settings.auto_gain = config_.auto_gain_alternate; 00616 alternate_settings.gain = config_.gain_alternate; 00617 alternate_settings.auto_exposure = config_.auto_exposure_alternate; 00618 alternate_settings.exposure = config_.exposure_alternate; 00619 alternate_settings.mirror_x = config_.mirror_x; 00620 alternate_settings.mirror_y = config_.mirror_y; 00621 alternate_settings.rotate_180 = config_.rotate_180; 00622 alternate_settings.auto_black_level = config_.auto_black_level; 00623 alternate_settings.black_level = config_.black_level; 00624 alternate_settings.black_level_filter_length = config_.black_level_filter_length; 00625 alternate_settings.black_level_step_size = config_.black_level_step_size; 00626 alternate_settings.brightness = config_.brightness; 00627 alternate_settings.max_exposure = config_.max_exposure; 00628 00629 imager_ = MT9VImager::getInstance(camera_); 00630 if (!imager_) 00631 { 00632 setStatusMessage("Unknown imager model."); 00633 return; 00634 } 00635 alternate_imager_ = imager_->getAlternateContext(); 00636 00637 if (enable_primary_ && enable_alternate_) // Both 00638 { 00639 if (!alternate_imager_) 00640 { 00641 setStatusMessage("Unable to find alternate imager context, but enable_alternate is true."); 00642 return; 00643 } 00644 00645 // Workaround for firmware/imager bug. 00646 primary_settings.auto_exposure = config_.auto_exposure_alternate; 00647 primary_settings.gain = config_.gain_alternate; 00648 primary_settings.companding = config_.companding_alternate; 00649 alternate_settings.auto_exposure = config_.auto_exposure; 00650 alternate_settings.gain = config_.gain; 00651 alternate_settings.companding = config_.companding; 00652 00653 if (setImagerSettings(*imager_, primary_settings) || 00654 setImagerSettings(*alternate_imager_, alternate_settings)) 00655 return; 00656 } 00657 else if (enable_primary_) // Primary only 00658 { 00659 if (setImagerSettings(*imager_, primary_settings)) 00660 return; 00661 } 00662 else // Alternate only 00663 { 00664 if (setImagerSettings(*imager_, alternate_settings)) 00665 return; 00666 } 00667 00668 if (enable_alternate_ && enable_primary_ && wge100SensorSelect(&camera_, 0, 0x07)) 00669 { 00670 setStatusMessage("Error configuring camera to report alternate frames."); 00671 return; 00672 } 00673 00674 next_is_alternate_ = !enable_primary_; 00675 00676 // Initialize frame time filter. 00677 frame_time_filter_ = FrameTimeFilter(desired_freq_, 0.001, 0.5 / config_.imager_rate); 00678 trigger_matcher_.setTrigDelay(1. / config_.imager_rate); 00679 00680 // The modulo below is for cameras that got programmed with the wrong 00681 // serial number. There should only be one currently. 00682 bool is_027 = ((camera_.serial / 100000) % 100) == 27; 00683 bool is_MT9V032 = imager_->getModel() == "MT9V032"; 00684 if (camera_.serial != 0 && is_027 == is_MT9V032) 00685 { 00686 boost::recursive_mutex::scoped_lock lock(mutex_); 00687 setStatusMessagef("Camera has %s serial number, but has %s imager. This is a serious problem with the hardware.", 00688 is_027 ? "027" : "non-027", imager_->getModel().c_str()); 00689 return; 00690 } 00691 00692 if (config_.test_pattern && ( 00693 wge100ReliableSensorWrite( &camera_, 0x7F, 0x3800, NULL ) != 0 || 00694 wge100ReliableSensorWrite( &camera_, 0x0F, 0x0006, NULL ) != 0)) { 00695 setStatusMessage("Error turning on test pattern."); 00696 return; 00697 } 00698 00699 setStatusMessage("Camera is opened."); 00700 state_ = OPENED; 00701 last_camera_ok_time_ = ros::Time::now().toSec(); // If we get here we are communicating with the camera well. 00702 } 00703 00704 void doClose() 00705 { 00706 ROS_DEBUG("close()"); 00707 ROS_ASSERT(state_ == OPENED); 00708 state_ = CLOSED; 00709 setStatusMessage("Camera is closed."); 00710 } 00711 00712 void doStart() 00713 { 00714 last_frame_number_ = 0xFFFF; 00715 last_partial_frame_number_ = 0xFFFF; 00716 00717 // Select trigger mode. 00718 int trig_mode = config_.ext_trig ? TRIG_STATE_EXTERNAL : TRIG_STATE_INTERNAL; 00719 if (enable_alternate_ && enable_primary_) 00720 trig_mode |= TRIG_STATE_ALTERNATE; 00721 if (config_.rising_edge_trig) 00722 trig_mode |= TRIG_STATE_RISING; 00723 if ( wge100TriggerControl( &camera_, trig_mode ) != 0) { 00724 setStatusMessagef("Trigger mode set error. Is %s accessible from interface %s? (Try running route to check.)", camera_.ip_str, camera_.ifName); 00725 state_ = CLOSED; // Might have failed because camera was reset. Go to closed. 00726 return; 00727 } 00728 00729 trigger_matcher_.reset(); 00730 00731 /* 00732 if (0) // Use this to dump register values. 00733 { 00734 for (int i = 0; i <= 255; i++) 00735 { 00736 uint16_t value; 00737 if (wge100ReliableSensorRead(&camera_, i, &value, NULL)) 00738 exit(1); 00739 printf("usleep(50000); stcam_->setRegister(0xFF000, 0x4%02x%04x); printf(\"%08x\\n\", stcam_->getRegister(0xFF000));\n", i, value); 00740 } 00741 } */ 00742 00743 ROS_DEBUG("start()"); 00744 ROS_ASSERT(state_ == OPENED); 00745 setStatusMessage("Waiting for first frame..."); 00746 state_ = RUNNING; 00747 image_thread_.reset(new boost::thread(boost::bind(&WGE100CameraDriver::imageThread, this))); 00748 } 00749 00750 void doStop() 00751 { 00752 ROS_DEBUG("stop()"); 00753 if (state_ != RUNNING) // RUNNING can exit asynchronously. 00754 return; 00755 00757 setStatusMessage("Stopped"); 00758 00759 state_ = OPENED; 00760 00761 if (image_thread_ && !image_thread_->timed_join((boost::posix_time::milliseconds) 2000)) 00762 { 00763 ROS_ERROR("image_thread_ did not die after two seconds. Pretending that it did. This is probably a bad sign."); 00764 lost_image_thread_count_++; 00765 } 00766 image_thread_.reset(); 00767 } 00768 00769 int setTestMode(uint16_t mode, diagnostic_updater::DiagnosticStatusWrapper &status) 00770 { 00771 if ( wge100ReliableSensorWrite( &camera_, 0x7F, mode, NULL ) != 0) { 00772 status.summary(2, "Could not set imager into test mode."); 00773 status.add("Writing imager test mode", "Fail"); 00774 return 1; 00775 } 00776 else 00777 { 00778 status.add("Writing imager test mode", "Pass"); 00779 } 00780 00781 usleep(100000); 00782 uint16_t inmode; 00783 if ( wge100ReliableSensorRead( &camera_, 0x7F, &inmode, NULL ) != 0) { 00784 status.summary(2, "Could not read imager mode back."); 00785 status.add("Reading imager test mode", "Fail"); 00786 return 1; 00787 } 00788 else 00789 { 00790 status.add("Reading imager test mode", "Pass"); 00791 } 00792 00793 if (inmode != mode) { 00794 status.summary(2, "Imager test mode read back did not match."); 00795 status.addf("Comparing read back value", "Fail (%04x != %04x)", inmode, mode); 00796 return 1; 00797 } 00798 else 00799 { 00800 status.add("Comparing read back value", "Pass"); 00801 } 00802 00803 return 0; 00804 } 00805 00806 std::string getID() 00807 { 00808 if (!camera_.serial) 00809 return "unknown"; 00810 else 00811 return (boost::format("WGE100_%05u") % camera_.serial).str(); 00812 } 00813 00814 private: 00815 void imageThread() 00816 { 00817 // Start video 00818 frame_time_filter_.reset_filter(); 00819 00820 first_frame_ = true; 00821 int ret_val = wge100VidReceiveAuto( &camera_, config_.height, config_.width, &WGE100CameraDriver::frameHandler, this); 00822 00823 if (ret_val == IMAGER_LINENO_OVF) 00824 setStatusMessage("Camera terminated streaming with an overflow error."); 00825 else if (ret_val == IMAGER_LINENO_ERR) 00826 setStatusMessage("Camera terminated streaming with an error."); 00827 else if (ret_val) 00828 setStatusMessage("wge100VidReceiveAuto exited with an error code."); 00829 00830 // At this point we are OPENED with lock held, or RUNNING, or image_thread_ did not die after two seconds (which should not happen). 00831 // Thus, it is safe to switch to OPENED, except if the image_thread_ 00832 // did not die, in which case the if will save us most of the time. 00833 // (Can't take the lock because the doStop thread might be holding it.) 00834 if (state_ == RUNNING) 00835 state_ = OPENED; 00836 ROS_DEBUG("Image thread exiting."); 00837 } 00838 00839 void cameraStatus(diagnostic_updater::DiagnosticStatusWrapper& stat) 00840 { 00841 double since_last_frame_ = ros::Time::now().toSec() - (last_image_time_ ? last_image_time_ : driver_start_time_); 00842 if (since_last_frame_ > 10) 00843 { 00844 stat.summary(2, "Next frame is way past due."); 00845 } 00846 else if (since_last_frame_ > 5 / desired_freq_) 00847 { 00848 stat.summary(1, "Next frame is past due."); 00849 } 00850 else if (rmem_max_ < RMEM_MAX_RECOMMENDED) 00851 { 00852 stat.summaryf(1, "Frames are streaming, but the receive buffer is small (rmem_max=%u). Please increase rmem_max to %u to avoid dropped frames. For details: http://www.ros.org/wiki/wge100_camera/Troubleshooting", rmem_max_, RMEM_MAX_RECOMMENDED); 00853 } 00854 else if (dropped_packet_event_ && config_.packet_debug) 00855 { 00856 // Only warn in diagnostics for dropped packets if we're in debug mode, #3834 00857 stat.summary(diagnostic_msgs::DiagnosticStatus::WARN, "There have been dropped packets."); 00858 dropped_packet_event_ = 0; 00859 } 00860 else 00861 { 00862 stat.summary(diagnostic_msgs::DiagnosticStatus::OK, "Frames are streaming."); 00863 dropped_packet_event_ = 0; 00864 } 00865 00866 stat.addf("Missing image line frames", "%d", missed_line_count_); 00867 stat.addf("Missing EOF frames", "%d", missed_eof_count_); 00868 stat.addf("Dropped packet estimate", "%d", dropped_packets_); 00869 stat.addf("Frames dropped by trigger filter", "%d", trigger_matcher_drop_count_); 00870 stat.addf("Massive frame losses", "%d", massive_frame_losses_); 00871 stat.addf("Losses of image thread", "%d", lost_image_thread_count_); 00872 stat.addf("First packet offset", "%d", config_.first_packet_offset); 00873 if (isClosed()) 00874 { 00875 static const std::string not_opened = "not_opened"; 00876 stat.add("Camera Serial #", not_opened); 00877 stat.add("Camera Name", not_opened); 00878 stat.add("Camera Hardware", not_opened); 00879 stat.add("Camera MAC", not_opened); 00880 stat.add("Interface", not_opened); 00881 stat.add("Camera IP", not_opened); 00882 stat.add("Image encoding", not_opened); 00883 } 00884 else 00885 { 00886 stat.add("Camera Serial #", camera_.serial); 00887 stat.add("Camera Name", camera_.cam_name); 00888 stat.add("Camera Hardware", hwinfo_); 00889 stat.addf("Camera MAC", "%02X:%02X:%02X:%02X:%02X:%02X", camera_.mac[0], camera_.mac[1], camera_.mac[2], camera_.mac[3], camera_.mac[4], camera_.mac[5]); 00890 stat.add("Interface", camera_.ifName); 00891 stat.add("Camera IP", camera_.ip_str); 00892 stat.add("Image encoding", image_encoding_); 00893 } 00894 stat.add("Image width", config_.width); 00895 stat.add("Image height", config_.height); 00896 stat.add("Image horizontal binning", config_.horizontal_binning); 00897 stat.add("Image vertical binning", config_.vertical_binning); 00898 stat.add("Requested imager rate", config_.imager_rate); 00899 stat.add("Latest frame time", last_image_time_); 00900 stat.add("Latest frame #", last_frame_number_); 00901 stat.add("External trigger controller", config_.trig_timestamp_topic); 00902 stat.add("Trigger mode", config_.ext_trig ? "external" : "internal"); 00903 boost::shared_ptr<MT9VImager> imager = imager_; // For thread safety. 00904 if (imager) 00905 { 00906 stat.add("Imager model", imager->getModel()); 00907 stat.addf("Imager version", "0x%04x", imager->getVersion()); 00908 } 00909 } 00910 00911 double getTriggeredFrameTime(double firstPacketTime) 00912 { 00913 /*// Assuming that there was no delay in receiving the first packet, 00914 // this should tell us the time at which the first trigger after the 00915 // image exposure occurred. 00916 double pulseStartTime = 0; 00917 controller::TriggerController::getTickStartTimeSec(firstPacketTime, trig_req_); 00918 00919 // We can now compute when the exposure ended. By offsetting to the 00920 // falling edge of the pulse, going back one pulse duration, and going 00921 // forward one camera frame time. 00922 double exposeEndTime = pulseStartTime + 00923 controller::TriggerController::getTickDurationSec(trig_req_) + 00924 1 / imager_freq_ - 00925 1 / config_.trig_rate; 00926 00927 return exposeEndTime;*/ 00928 return 0; 00929 } 00930 00931 double getExternallyTriggeredFrameTime(double firstPacketTime) 00932 { 00933 // We can now compute when the exposure ended. By offsetting by the 00934 // empirically determined first packet offset, going back one pulse 00935 // duration, and going forward one camera frame time. 00936 00937 double exposeEndTime = firstPacketTime - config_.first_packet_offset + 00938 1 / config_.imager_rate - 00939 1 / config_.trig_rate; 00940 00941 return exposeEndTime; 00942 } 00943 00944 double getFreeRunningFrameTime(double firstPacketTime) 00945 { 00946 // This offset is empirical, but fits quite nicely most of the time. 00947 00948 return firstPacketTime - config_.first_packet_offset; 00949 } 00950 00951 // double nowTime = ros::Time::now().toSec(); 00952 // static double lastExposeEndTime; 00953 // static double lastStartTime; 00954 // if (fabs(exposeEndTime - lastExposeEndTime - 1 / trig_rate_) > 1e-4) 00955 // ROS_INFO("Mistimed frame #%u first %f first-pulse %f now-first %f pulse-end %f end-last %f first-last %f", eofInfo->header.frame_number, frameStartTime, frameStartTime - pulseStartTime, nowTime - pulseStartTime, pulseStartTime - exposeEndTime, exposeEndTime - lastExposeEndTime, frameStartTime - lastStartTime); 00956 // lastStartTime = frameStartTime; 00957 // lastExposeEndTime = exposeEndTime; 00958 // 00959 // static double lastImageTime; 00960 // static double firstFrameTime; 00961 // if (eofInfo->header.frame_number == 100) 00962 // firstFrameTime = imageTime; 00963 // ROS_INFO("Frame #%u time %f ofs %f ms delta %f Hz %f", eofInfo->header.frame_number, imageTime, 1000 * (imageTime - firstFrameTime - 1. / (29.5/* * 0.9999767*/) * (eofInfo->header.frame_number - 100)), imageTime - lastImageTime, 1. / (imageTime - lastImageTime)); 00964 // lastImageTime = imageTime; 00965 00966 SlowTriggerFilter no_timestamp_warning_filter_; 00967 00968 int frameHandler(wge100FrameInfo *frame_info) 00969 { 00970 boost::recursive_mutex::scoped_lock(diagnostics_lock_); 00971 00972 if (state_ != RUNNING) 00973 return 1; 00974 00975 if (frame_info == NULL) 00976 { 00977 boost::recursive_mutex::scoped_lock lock(mutex_); 00978 // The select call in the driver timed out. 00980 if (config_.ext_trig && !trigger_matcher_.hasTimestamp()) 00981 { 00982 const char *msg = "More than one second elapsed without receiving any trigger. Is this normal?"; 00983 if (no_timestamp_warning_filter_.badFrame()) 00984 ROS_WARN_NAMED("slow_trigger", "%s", msg); 00985 else 00986 ROS_DEBUG("%s", msg); 00987 return 0; 00988 } 00989 00991 // simultaneously trying to be killed? The other thread will time out 00992 // on the join, but the error message it spits out in that case is 00993 // ugly. 00994 setStatusMessagef("No data have arrived for more than one second. Assuming that camera is no longer streaming."); 00995 return 1; 00996 } 00997 00998 no_timestamp_warning_filter_.goodFrame(); 00999 01000 if (first_frame_) 01001 { 01002 ROS_INFO_COND(first_frame_, "Camera streaming."); 01003 setStatusMessage("Camera streaming."); 01004 } 01005 first_frame_ = false; 01006 01007 double firstPacketTime = frame_info->startTime.tv_sec + frame_info->startTime.tv_usec / 1e6; 01008 01009 // Use a minimum filter to eliminate variations in delay from frame 01010 // to frame. 01011 double firstPacketTimeFiltered = fabs(frame_time_filter_.run(firstPacketTime, frame_info->frame_number)); 01012 01013 // If we don't know the trigger times then use the arrival time of the 01014 // first packet run through a windowed minimum filter to estimate the 01015 // trigger times. 01016 01017 ros::Time imageTime; 01018 if (!config_.ext_trig || config_.trig_timestamp_topic.empty()) 01019 { 01020 // Suppose that the first packet arrived a fixed time after the 01021 // trigger signal for the next frame. (I.e., this guess will be used 01022 // as a timestamp for the next frame not for this one.) 01023 double triggerTimeGuess = firstPacketTimeFiltered - config_.first_packet_offset; 01024 // Send the trigger estimate to the trigger matcher. 01025 trigger_matcher_.triggerCallback(triggerTimeGuess); 01026 //ROS_INFO("Added filtered trigger: %f", triggerTimeGuess); 01027 //ROS_INFO("Using firstPacketTime: %f", firstPacketTime); 01028 imageTime = trigger_matcher_.getTimestampNoblock(ros::Time(firstPacketTime)); 01029 //ROS_INFO("Got timestamp: %f delta: %f", imageTime.toSec(), imageTime.toSec() - triggerTimeGuess); 01030 } 01031 else 01032 { 01033 //ROS_INFO("Using firstPacketTime: %f", firstPacketTime); 01034 //double pre = ros::Time::now().toSec(); 01035 imageTime = trigger_matcher_.getTimestampBlocking(ros::Time(firstPacketTime), 0.01); 01036 //double post = ros::Time::now().toSec(); 01037 //ROS_INFO("Got timestamp: %f delta: %f delay: %f", imageTime.toSec(), imageTime.toSec() - firstPacketTime, post - pre); 01038 } 01039 01040 //static double lastunfiltered; 01041 //ROS_DEBUG("first_packet %f unfilteredImageTime %f frame_id %u deltaunf: %f", frameStartTime, unfilteredImageTime, frame_info->frame_number, unfilteredImageTime - lastunfiltered); 01042 //lastunfiltered = unfilteredImageTime; 01043 01044 int16_t frame_delta = (frame_info->frame_number - last_partial_frame_number_) & 0xFFFF; 01045 last_partial_frame_number_ = frame_info->frame_number; 01046 if (frame_delta > 1) 01047 { 01048 dropped_packet_event_ = true; 01049 ROS_DEBUG_NAMED("dropped_frame", "Some frames were never seen. The dropped packet count will be incorrect."); 01050 massive_frame_losses_++; 01051 } 01052 01053 // Check for frame with missing EOF. 01054 if (frame_info->eofInfo == NULL) { 01055 missed_eof_count_++; 01056 dropped_packets_++; 01057 dropped_packet_event_ = true; 01058 ROS_DEBUG_NAMED("no_eof", "Frame %u was missing EOF", frame_info->frame_number); 01059 return 0; 01060 } 01061 01062 // Check for short packet (video lines were missing) 01063 if (frame_info->shortFrame) { 01064 missed_line_count_++; 01065 dropped_packets_ += frame_info->missingLines; 01066 dropped_packet_event_ = true; 01067 ROS_DEBUG_NAMED("short_frame", "Short frame #%u (%zu video lines were missing, last was %zu)", frame_info->frame_number, 01068 frame_info->missingLines, frame_info->lastMissingLine); 01069 return 0; 01070 } 01071 01072 if (imageTime == timestamp_tools::TriggerMatcher::RetryLater) 01073 { 01074 trigger_matcher_drop_count_++; 01075 ROS_DEBUG_NAMED("missing_timestamp", "TriggerMatcher did not get timestamp for frame #%u", frame_info->frame_number); 01076 return 0; 01077 } 01078 01079 if (imageTime == timestamp_tools::TriggerMatcher::DropData) 01080 { 01081 trigger_matcher_drop_count_++; 01082 ROS_DEBUG_NAMED("trigger_match_fail", "TriggerMatcher dropped frame #%u", frame_info->frame_number); 01083 return 0; 01084 } 01085 01086 last_image_time_ = imageTime.toSec(); 01087 last_frame_number_ = frame_info->frame_number; 01088 01089 image_encoding_ = isColor() ? sensor_msgs::image_encodings::BAYER_BGGR8 : sensor_msgs::image_encodings::MONO8; 01090 01091 bool alternate = next_is_alternate_; 01092 next_is_alternate_ = enable_alternate_ && (!enable_primary_ || (frame_info->eofInfo->i2c[0] & 0x8000)); 01093 01094 return useFrame_(frame_info->width, frame_info->height, frame_info->frameData, imageTime, alternate); 01095 } 01096 01097 bool isColor() 01098 { 01099 return imager_->getModel() == "MT9V032"; 01100 // Serial number test fails because of the exceptions I have introduced. 01101 // May want to go back to this test if we get dual register set color 01102 // imagers. 01103 // return !(camera_.serial >= 2700000 && camera_.serial < 2800000); 01104 } 01105 01106 static int frameHandler(wge100FrameInfo *frameInfo, void *userData) 01107 { 01108 WGE100CameraDriver &fa_node = *(WGE100CameraDriver*)userData; 01109 return fa_node.frameHandler(frameInfo); 01110 } 01111 01112 uint16_t intrinsicsChecksum(uint16_t *data, int words) 01113 { 01114 uint16_t sum = 0; 01115 for (int i = 0; i < words; i++) 01116 sum += htons(data[i]); 01117 return htons(0xFFFF - sum); 01118 } 01119 01120 bool loadIntrinsics(sensor_msgs::CameraInfo &cam_info) 01121 { 01122 // Retrieve contents of user memory 01123 std::string calbuff(2 * FLASH_PAGE_SIZE, 0); 01124 01125 if(wge100ReliableFlashRead(&camera_, FLASH_CALIBRATION_PAGENO, (uint8_t *) &calbuff[0], NULL) != 0 || 01126 wge100ReliableFlashRead(&camera_, FLASH_CALIBRATION_PAGENO+1, (uint8_t *) &calbuff[FLASH_PAGE_SIZE], NULL) != 0) 01127 { 01128 ROS_WARN("Error reading camera intrinsics from flash.\n"); 01129 return false; 01130 } 01131 01132 uint16_t chk = intrinsicsChecksum((uint16_t *) &calbuff[0], calbuff.length() / 2); 01133 if (chk) 01134 { 01135 ROS_WARN_NAMED("no_intrinsics", "Camera intrinsics have a bad checksum. They have probably never been set."); 01136 return false; 01137 } 01138 01139 std::string name; 01140 bool success = camera_calibration_parsers::parseCalibration(calbuff, "ini", name, cam_info); 01141 // @todo: rescale intrinsics as needed? 01142 if (success && (cam_info.width != (unsigned int) config_.width || cam_info.height != (unsigned int) config_.height)) { 01143 ROS_ERROR("Image resolution from intrinsics file does not match current video setting, " 01144 "intrinsics expect %ix%i but running at %ix%i", cam_info.width, cam_info.height, config_.width, config_.height); 01145 } 01146 01147 if (!success) 01148 ROS_ERROR("Could not parse camera intrinsics downloaded from camera."); 01149 01150 return success; 01151 } 01152 01153 bool saveIntrinsics(const sensor_msgs::CameraInfo &cam_info) 01154 { 01155 char calbuff[2 * FLASH_PAGE_SIZE]; 01156 01157 std::stringstream inifile; 01158 writeCalibrationIni(inifile, camera_.cam_name, cam_info); 01159 strncpy(calbuff, inifile.str().c_str(), 2 * FLASH_PAGE_SIZE - 2); 01160 01161 uint16_t *chk = ((uint16_t *) calbuff) + FLASH_PAGE_SIZE - 1; 01162 *chk = 0; 01163 *chk = intrinsicsChecksum((uint16_t *) calbuff, sizeof(calbuff) / 2); 01164 01165 ROS_INFO("Writing camera info:\n%s", calbuff); 01166 01167 boost::recursive_mutex::scoped_lock lock_(mutex_); // Don't want anybody restarting us behind our back. 01168 goOpened(); 01169 01170 // Retrieve contents of user memory 01171 01172 bool ret = false; 01173 if (state_ != OPENED) 01174 ROS_ERROR("Error putting camera into OPENED state to write to flash.\n"); 01175 else if(wge100ReliableFlashWrite(&camera_, FLASH_CALIBRATION_PAGENO, (uint8_t *) calbuff, NULL) != 0 || 01176 wge100ReliableFlashWrite(&camera_, FLASH_CALIBRATION_PAGENO+1, (uint8_t *) calbuff + FLASH_PAGE_SIZE, NULL) != 0) 01177 ROS_ERROR("Error writing camera intrinsics to flash.\n"); 01178 else 01179 { 01180 ROS_INFO("Camera_info successfully written to camera."); 01181 ret = true; 01182 } 01183 01184 goClosed(); // Need to close to reread the intrinsics. 01185 goRunning(); // The new intrinsics will get read here. 01186 return ret; 01187 } 01188 01189 bool boardConfig(wge100_camera::BoardConfig::Request &req, wge100_camera::BoardConfig::Response &rsp) 01190 { 01191 if (state_ != RUNNING) 01192 { 01193 ROS_ERROR("board_config sevice called while camera was not running. To avoid programming the wrong camera, board_config can only be called while viewing the camera stream."); 01194 rsp.success = 0; 01195 return 1; 01196 } 01197 01198 MACAddress mac; 01199 if (req.mac.size() != 6) 01200 { 01201 ROS_ERROR("board_config service called with %zu bytes in MAC. Should be 6.", req.mac.size()); 01202 rsp.success = 0; 01203 return 1; 01204 } 01205 for (int i = 0; i < 6; i++) 01206 mac[i] = req.mac[i]; 01207 ROS_INFO("board_config called s/n #%i, and MAC %02x:%02x:%02x:%02x:%02x:%02x.", req.serial, mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); 01208 stop(); 01209 rsp.success = !wge100ConfigureBoard( &camera_, req.serial, &mac); 01210 01211 if (rsp.success) 01212 ROS_INFO("board_config succeeded."); 01213 else 01214 ROS_INFO("board_config failed."); 01215 01216 return 1; 01217 } 01218 01219 }; 01220 01221 class WGE100CameraNode : public driver_base::DriverNode<WGE100CameraDriver> 01222 { 01223 public: 01224 WGE100CameraNode(ros::NodeHandle &nh) : 01225 driver_base::DriverNode<WGE100CameraDriver>(nh), 01226 camera_node_handle_(nh.resolveName("camera")), 01227 camera_node_handle_alt_(nh.resolveName("camera_alternate")), 01228 cam_pub_diag_(cam_pub_.getTopic(), 01229 diagnostic_, 01230 diagnostic_updater::FrequencyStatusParam(&driver_.desired_freq_, &driver_.desired_freq_, 0.05), 01231 diagnostic_updater::TimeStampStatusParam()), 01232 config_bord_service_(private_node_handle_.advertiseService("board_config", &WGE100CameraDriver::boardConfig, &driver_)), 01233 set_camera_info_service_(camera_node_handle_.advertiseService("set_camera_info", &WGE100CameraNode::setCameraInfo, this)), 01234 wge100_diagnostic_task_("WGE100 Diagnostics", boost::bind(&WGE100CameraDriver::cameraStatus, &driver_, _1)), 01235 calibrated_(false) 01236 { 01237 // Take care not to advertise the same topic twice to work around ros-pkg #4689, 01238 // advertising reconfigure services twice for image_transport plugins. 01239 // In future, image_transport should handle that transparently. 01240 std::string topic = camera_node_handle_.resolveName("image_raw"); 01241 std::string alt_topic = camera_node_handle_alt_.resolveName("image_raw"); 01242 image_transport::ImageTransport it(nh); 01243 cam_pub_ = it.advertiseCamera(topic, 1); 01244 if (topic == alt_topic) 01245 cam_pub_alt_ = cam_pub_; 01246 else 01247 cam_pub_alt_ = it.advertiseCamera(alt_topic, 1); 01248 01249 driver_.useFrame_ = boost::bind(&WGE100CameraNode::publishImage, this, _1, _2, _3, _4, _5); 01250 driver_.setPostOpenHook(boost::bind(&WGE100CameraNode::postOpenHook, this)); 01251 } 01252 01253 private: 01254 ros::NodeHandle camera_node_handle_; 01255 ros::NodeHandle camera_node_handle_alt_; 01256 01257 // Publications 01258 image_transport::CameraPublisher cam_pub_; 01259 image_transport::CameraPublisher cam_pub_alt_; 01260 diagnostic_updater::TopicDiagnostic cam_pub_diag_; 01261 sensor_msgs::Image image_; 01262 sensor_msgs::CameraInfo camera_info_; 01263 ros::ServiceServer config_bord_service_; 01264 ros::ServiceServer set_camera_info_service_; 01265 ros::Subscriber trigger_sub_; 01266 01267 diagnostic_updater::FunctionDiagnosticTask wge100_diagnostic_task_; 01268 01269 // Calibration 01270 bool calibrated_; 01271 01272 int publishImage(size_t width, size_t height, uint8_t *frameData, ros::Time t, bool alternate) 01273 { 01274 // Raw data is bayer BGGR pattern 01275 fillImage(image_, driver_.image_encoding_, height, width, width, frameData); 01276 01277 fflush(stdout); 01278 (alternate ? cam_pub_alt_ : cam_pub_).publish(image_, camera_info_, t); 01279 cam_pub_diag_.tick(t); 01280 01281 return 0; 01282 } 01283 01284 virtual void reconfigureHook(int level) 01285 { 01286 ROS_DEBUG("WGE100CameraNode::reconfigureHook called at level %x", level); 01287 01288 image_.header.frame_id = driver_.config_.frame_id; 01289 camera_info_.header.frame_id = driver_.config_.frame_id; 01290 } 01291 01292 void postOpenHook() 01293 { 01294 // Try to load camera intrinsics from flash memory 01295 calibrated_ = driver_.loadIntrinsics(camera_info_); 01296 if (calibrated_) 01297 ROS_INFO("Loaded intrinsics from camera."); 01298 else 01299 { 01300 ROS_WARN_NAMED("no_intrinsics", "Failed to load intrinsics from camera"); 01301 camera_info_ = sensor_msgs::CameraInfo(); 01302 camera_info_.width = driver_.config_.width; 01303 camera_info_.height = driver_.config_.height; 01304 } 01305 // Receive timestamps through callback 01306 if (driver_.config_.ext_trig && !driver_.config_.trig_timestamp_topic.empty()) 01307 { 01308 void (timestamp_tools::TriggerMatcher::*cb)(const roslib::HeaderConstPtr &) = ×tamp_tools::TriggerMatcher::triggerCallback; 01309 trigger_sub_ = node_handle_.subscribe(driver_.config_.trig_timestamp_topic, 100, cb, &driver_.trigger_matcher_); 01310 } 01311 else 01312 trigger_sub_.shutdown(); 01313 01314 diagnostic_.setHardwareID(driver_.getID()); 01315 } 01316 01317 virtual void addDiagnostics() 01318 { 01319 // Set up diagnostics 01320 driver_status_diagnostic_.addTask(&wge100_diagnostic_task_); 01321 } 01322 01323 virtual void addRunningTests() 01324 { 01325 self_test_.add( "Streaming Test", this, &WGE100CameraNode::streamingTest); 01326 } 01327 01328 bool setCameraInfo(sensor_msgs::SetCameraInfo::Request &req, sensor_msgs::SetCameraInfo::Response &rsp) 01329 { 01330 sensor_msgs::CameraInfo &cam_info = req.camera_info; 01331 01332 if (driver_.getState() != driver_base::Driver::RUNNING) 01333 { 01334 ROS_ERROR("set_camera_info service called while camera was not running. To avoid programming the wrong camera, set_camera_info can only be called while viewing the camera stream."); 01335 rsp.status_message = "Camera not running. Camera must be running to avoid setting the intrinsics of the wrong camera."; 01336 rsp.success = false; 01337 return 1; 01338 } 01339 01340 if ((cam_info.width != (unsigned int) driver_.config_.width || cam_info.height != (unsigned int) driver_.config_.height)) { 01341 ROS_ERROR("Image resolution of camera_info passed to set_camera_info service does not match current video setting, " 01342 "camera_info contains %ix%i but camera running at %ix%i", cam_info.width, cam_info.height, driver_.config_.width, driver_.config_.height); 01343 rsp.status_message = (boost::format("Camera_info resolution %ix%i does not match camera resolution %ix%i.")%cam_info.width%cam_info.height%driver_.config_.width%driver_.config_.height).str(); 01344 rsp.success = false; 01345 } 01346 01347 rsp.success = driver_.saveIntrinsics(cam_info); 01348 if (!rsp.success) 01349 rsp.status_message = "Error writing camera_info to flash."; 01350 01351 return 1; 01352 } 01353 01354 virtual void addOpenedTests() 01355 { 01356 } 01357 01358 virtual void addStoppedTests() 01359 { 01360 ROS_DEBUG("Adding wge100 camera video mode tests."); 01361 for (int i = 0; i < MT9V_NUM_MODES; i++) 01362 { 01363 if (MT9VModes[i].width != 640) 01364 continue; // Hack, some of the camera firmware doesn't support other modes. 01365 diagnostic_updater::TaskFunction f = boost::bind(&WGE100CameraNode::videoModeTest, this, i, _1); 01366 self_test_.add( str(boost::format("Test Pattern in mode %s")%MT9VModes[i].name), f ); 01367 } 01368 } 01369 01370 void streamingTest(diagnostic_updater::DiagnosticStatusWrapper& status) 01371 { 01372 // Try multiple times to improve test reliability by retrying on failure 01373 for (int i = 0; i < 10; i++) 01374 { 01375 cam_pub_diag_.clear_window(); 01376 sleep(1); 01377 01378 cam_pub_diag_.run(status); 01379 if (!status.level) 01380 break; 01381 driver_.start(); 01382 } 01383 } 01384 01385 class VideoModeTestFrameHandler 01386 { 01387 public: 01388 VideoModeTestFrameHandler(diagnostic_updater::DiagnosticStatusWrapper &status, int imager_id) : got_frame_(false), frame_count_(0), status_(status), imager_id_(imager_id) 01389 {} 01390 01391 volatile bool got_frame_; 01392 int frame_count_; 01393 01394 int run(size_t width, size_t height, uint8_t *data, ros::Time stamp, bool alternate) 01395 { 01396 status_.add("Got a frame", "Pass"); 01397 01398 bool final = frame_count_++ > 10; 01399 int mode; 01400 //int error_count = 0; 01401 switch (imager_id_) 01402 { 01403 case 0x1313: 01404 mode = 0; 01405 break; 01406 case 0x1324: 01407 mode = 2; 01408 break; 01409 default: 01410 ROS_WARN("Unexpected imager_id, autodetecting test pattern for this camera."); 01411 mode = -1; 01412 break; 01413 } 01414 01415 for (size_t y = 0; y < height; y++) 01416 for (size_t x = 0; x < width; x++, data++) 01417 { 01418 std::string msg; 01419 01420 // Disabled modes other than 640 for now... 01421 //if (width > 320) 01422 msg = check_expected(x, y, *data, final, mode); 01423 //else 01424 //expected = (get_expected(2 * x, 2 * y) + get_expected(2 * x, 2 * y + 1)) / 2; 01425 01426 if (msg.empty()) 01427 continue; 01428 01429 //if (error_count++ < 50) 01432 // continue; 01433 01434 if (!final) 01435 { 01436 ROS_WARN("Pattern mismatch, retrying..."); 01437 return 0; 01438 } 01439 01440 status_.summary(2, msg); 01441 status_.add("Frame content", msg); 01442 01443 got_frame_ = true; 01444 return 1; 01445 } 01446 01447 status_.addf("Frame content", "Pass"); 01448 got_frame_ = true; 01449 return 1; 01450 } 01451 01452 private: 01453 std::string check_expected(int x, int y, uint8_t actual_col, bool final, int &mode) 01454 { 01455 #define NUM_TEST_PATTERNS 3 01456 // There seem to be two versions of the imager with different 01457 // patterns. 01458 uint8_t col[NUM_TEST_PATTERNS]; 01459 col[0] = (x + 2 * y + 25) / 4; 01460 if ((x + 1) / 2 + y < 500) 01461 col[1] = 14 + x / 4; 01462 else 01463 col[1] = 0; 01464 col[2] = (x + 2 * y + 43) / 4; 01465 01466 std::string msg; 01467 01468 if (mode != -1) 01469 { 01470 if (col[mode] == actual_col) 01471 return ""; 01472 01473 msg = (boost::format("Unexpected value %u instead of %u at (x=%u, y=%u)")%(int)actual_col%(int)col[mode]%x%y).str(); 01474 } 01475 else 01476 { 01477 for (int i = 0; i < NUM_TEST_PATTERNS; i++) 01478 if (col[i] == actual_col) 01479 { 01480 mode = i; 01481 return ""; 01482 } 01483 01484 msg = (boost::format("Unexpected value in first pixel %u, expected: %u, %u or %u")%(int)actual_col%(int)col[0]%(int)col[1]%(int)col[2]).str(); 01485 } 01486 01487 ROS_ERROR("%s", msg.c_str()); 01488 01489 return msg; 01490 } 01491 diagnostic_updater::DiagnosticStatusWrapper &status_; 01492 int imager_id_; 01493 }; 01494 01495 void videoModeTest(int mode, diagnostic_updater::DiagnosticStatusWrapper& status) 01496 { 01497 if (mode < 0 || mode > MT9V_NUM_MODES) 01498 { 01499 ROS_ERROR("Tried to call videoModeTest with out of range mode %u.", mode); 01500 return; 01501 } 01502 01503 const Config old_config = driver_.config_; 01504 WGE100CameraDriver::UseFrameFunction oldUseFrame = driver_.useFrame_; 01505 01506 Config new_config = driver_.config_; 01507 new_config.width = MT9VModes[mode].width; 01508 new_config.height = MT9VModes[mode].height; 01509 new_config.x_offset = 0; 01510 new_config.y_offset = 0; 01511 new_config.imager_rate = MT9VModes[mode].fps; 01512 new_config.ext_trig = 0; 01513 new_config.register_set = wge100_camera::WGE100Camera_PrimaryRegisterSet; 01514 new_config.test_pattern = true; 01515 driver_.config_update(new_config); 01516 boost::shared_ptr<MT9VImager> imager = driver_.imager_; // For thread safety. 01517 VideoModeTestFrameHandler callback(status, imager ? driver_.imager_->getVersion() : 0); 01518 driver_.useFrame_ = boost::bind(&VideoModeTestFrameHandler::run, boost::ref(callback), _1, _2, _3, _4, _5); 01519 01520 status.name = (boost::format("Pattern Test: %ix%i at %.1f fps.")%new_config.width%new_config.height%new_config.imager_rate).str(); 01521 status.summary(0, "Passed"); // If nobody else fills this, then the test passed. 01522 01523 int oldcount = driver_.lost_image_thread_count_; 01524 for (int i = 0; i < 50 && !callback.got_frame_; i++) 01525 { 01526 driver_.start(); // In case something goes wrong. 01527 usleep(100000); 01528 } 01529 driver_.close(); 01530 if (!callback.got_frame_) 01531 { 01532 ROS_ERROR("Got no frame during pattern test."); 01533 status.summary(2, "Got no frame during pattern test."); 01534 } 01535 if (oldcount < driver_.lost_image_thread_count_) 01536 { 01537 ROS_ERROR("Lost the image_thread. This should never happen."); 01538 status.summary(2, "Lost the image_thread. This should never happen."); 01539 } 01540 01541 driver_.useFrame_ = oldUseFrame; 01542 driver_.config_update(old_config); 01543 01544 ROS_INFO("Exiting test %s with status %i: %s", status.name.c_str(), status.level, status.message.c_str()); 01545 } 01546 }; 01547 01548 int main(int argc, char **argv) 01549 { 01550 return driver_base::main<WGE100CameraNode>(argc, argv, "wge100_camera"); 01551 }