optimizer.cpp
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2014-2015 Fetch Robotics Inc.
00003  * Copyright (C) 2013-2014 Unbounded Robotics Inc.
00004  *
00005  * Licensed under the Apache License, Version 2.0 (the "License");
00006  * you may not use this file except in compliance with the License.
00007  * You may obtain a copy of the License at
00008  *
00009  *     http://www.apache.org/licenses/LICENSE-2.0
00010  *
00011  * Unless required by applicable law or agreed to in writing, software
00012  * distributed under the License is distributed on an "AS IS" BASIS,
00013  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00014  * See the License for the specific language governing permissions and
00015  * limitations under the License.
00016  */
00017 
00018 // Author: Michael Ferguson
00019 
00020 #include <robot_calibration/ceres/optimizer.h>
00021 
00022 #include <ceres/ceres.h>
00023 
00024 #include <urdf/model.h>
00025 #include <kdl_parser/kdl_parser.hpp>
00026 #include <robot_calibration_msgs/CalibrationData.h>
00027 
00028 #include <robot_calibration/calibration_offset_parser.h>
00029 #include <robot_calibration/ceres/camera3d_to_arm_error.h>
00030 #include <robot_calibration/ceres/ground_plane_error.h>
00031 #include <robot_calibration/ceres/data_functions.h>
00032 #include <robot_calibration/ceres/outrageous_error.h>
00033 #include <robot_calibration/models/camera3d.h>
00034 #include <robot_calibration/models/chain.h>
00035 #include <boost/shared_ptr.hpp>
00036 #include <string>
00037 #include <map>
00038 
00039 namespace robot_calibration
00040 {
00041 
00042 Optimizer::Optimizer(const std::string& robot_description)
00043 {
00044   if (!model_.initString(robot_description))
00045     std::cerr << "Failed to parse URDF." << std::endl;
00046 }
00047 
00048 Optimizer::~Optimizer()
00049 {
00050 }
00051 
00052 // Determine if a sample of data has an observation from
00053 // the desired sensor
00054 bool hasSensor(
00055   const robot_calibration_msgs::CalibrationData& msg,
00056   const std::string& sensor)
00057 {
00058   for (size_t i = 0; i < msg.observations.size(); i++)
00059   {
00060     if (msg.observations[i].sensor_name == sensor)
00061       return true;
00062   }
00063   return false;
00064 }
00065 
00066 int Optimizer::optimize(OptimizationParams& params,
00067                         std::vector<robot_calibration_msgs::CalibrationData> data,
00068                         bool progress_to_stdout)
00069 {
00070   // Load KDL from URDF
00071   if (!kdl_parser::treeFromUrdfModel(model_, tree_))
00072   {
00073     std::cerr << "Failed to construct KDL tree" << std::endl;
00074     return -1;
00075   }
00076 
00077   // Create models
00078   for (size_t i = 0; i < params.models.size(); ++i)
00079   {
00080     if (params.models[i].type == "chain")
00081     {
00082       ROS_INFO_STREAM("Creating chain '" << params.models[i].name << "' from " <<
00083                                             params.base_link << " to " <<
00084                                             params.models[i].params["frame"]);
00085       ChainModel* model = new ChainModel(params.models[i].name, tree_, params.base_link, params.models[i].params["frame"]);
00086       models_[params.models[i].name] = model;
00087     }
00088     else if (params.models[i].type == "camera3d")
00089     {
00090       ROS_INFO_STREAM("Creating camera3d '" << params.models[i].name << "' in frame " <<
00091                                                params.models[i].params["frame"]);
00092       Camera3dModel* model = new Camera3dModel(params.models[i].name, tree_, params.base_link, params.models[i].params["frame"]);
00093       models_[params.models[i].name] = model;
00094     }
00095     else
00096     {
00097       // ERROR unknown
00098     }
00099   }
00100 
00101   // Setup  parameters to calibrate
00102   offsets_.reset(new CalibrationOffsetParser());
00103   for (size_t i = 0; i < params.free_params.size(); ++i)
00104   {
00105     offsets_->add(params.free_params[i]);
00106   }
00107   for (size_t i = 0; i < params.free_frames.size(); ++i)
00108   {
00109     offsets_->addFrame(params.free_frames[i].name,
00110                        params.free_frames[i].x,
00111                        params.free_frames[i].y,
00112                        params.free_frames[i].z,
00113                        params.free_frames[i].roll,
00114                        params.free_frames[i].pitch,
00115                        params.free_frames[i].yaw);
00116   }
00117 
00118   // Allocate space
00119   double* free_params = new double[offsets_->size()];
00120   for (int i = 0; i < offsets_->size(); ++i)
00121     free_params[i] = 0.0;
00122 
00123   double z_ = 0;
00124   // Houston, we have a problem...
00125   ceres::Problem* problem = new ceres::Problem();
00126 
00127   // For each sample of data:
00128   for (size_t i = 0; i < data.size(); ++i)
00129   {
00130     for (size_t j = 0; j < params.error_blocks.size(); ++j)
00131     {
00132       if (params.error_blocks[j].type == "camera3d_to_arm")
00133       {
00134         // This error block can process data generated by the LedFinder,
00135         // CheckboardFinder, or any other finder that can sample the pose
00136         // of one or more data points that are connected at a constant offset
00137         // from a link a kinematic chain (the "arm").
00138 
00139 
00140         std::string camera_name = static_cast<std::string>(params.error_blocks[j].params["camera"]);
00141         std::string arm_name = static_cast<std::string>(params.error_blocks[j].params["arm"]);
00142 
00143         // Check that this sample has the required features/observations
00144         if (!hasSensor(data[i], camera_name) || !hasSensor(data[i], arm_name))
00145           continue;
00146 
00147         // Create the block
00148         ceres::CostFunction * cost = Camera3dToArmError::Create(
00149             dynamic_cast<Camera3dModel*>(models_[camera_name]),
00150             models_[arm_name],
00151             offsets_.get(), data[i]);
00152 
00153         int index = -1;
00154         for (size_t k = 0; k < data[i].observations.size(); k++)
00155         {
00156            if (data[i].observations[k].sensor_name == camera_name)
00157           {
00158             index = k;
00159             break;
00160           }
00161         }
00162 
00163         if (index == -1)
00164         {
00165           std::cerr << "Sensor name doesn't exist" << std::endl;
00166           return 0;
00167         }
00168 
00169         if (progress_to_stdout)
00170         {
00171           double ** params = new double*[1];
00172           params[0] = free_params;
00173           double * residuals = new double[data[i].observations[index].features.size() * 3];  // TODO: should check that all features are same length?
00174 
00175           cost->Evaluate(params, residuals, NULL);
00176           std::cout << "INITIAL COST (" << i << ")" << std::endl << "  x: ";
00177           for (size_t k = 0; k < data[i].observations[index].features.size(); ++k)
00178             std::cout << "  " << std::setw(10) << std::fixed << residuals[(3*k + 0)];
00179           std::cout << std::endl << "  y: ";
00180           for (size_t k = 0; k < data[i].observations[index].features.size(); ++k)
00181             std::cout << "  " << std::setw(10) << std::fixed << residuals[(3*k + 1)];
00182           std::cout << std::endl << "  z: ";
00183           for (size_t k = 0; k < data[i].observations[index].features.size(); ++k)
00184             std::cout << "  " << std::setw(10) << std::fixed << residuals[(3*k + 2)];
00185           std::cout << std::endl << std::endl;
00186         }
00187 
00188         problem->AddResidualBlock(cost,
00189                                   NULL,  // squared loss
00190                                   free_params);
00191       }
00192       else if (params.error_blocks[j].type =="camera3d_to_ground")
00193       {
00194         std::string camera_name = static_cast<std::string>(params.error_blocks[j].params["camera"]);
00195         std::string ground_name = static_cast<std::string>(params.error_blocks[j].params["ground"]);
00196 
00197         // Check that this sample has the required features/observations
00198         if (!hasSensor(data[i], camera_name) || !hasSensor(data[i], ground_name))
00199           continue;
00200 
00201         // Create the block
00202         ceres::CostFunction * cost = GroundPlaneError::Create(
00203           dynamic_cast<Camera3dModel*>(models_[camera_name]),
00204           z_,
00205           offsets_.get(), data[i]);
00206 
00207         int index = -1;
00208         for (size_t k =0; k < data[i].observations.size(); k++)
00209         {
00210           if (data[i].observations[k].sensor_name == camera_name)
00211           {
00212             index = k;
00213             break;
00214           }
00215         }
00216         
00217         if(index == -1)
00218         {
00219           std::cerr << "Sensor name doesn't exist" << std::endl;
00220           return 0;
00221         }
00222 
00223         if (progress_to_stdout)
00224         {
00225           double ** params = new double*[1];
00226           params[0] = free_params;
00227           double * residuals = new double[data[i].observations[index].features.size()];
00228 
00229           cost->Evaluate(params, residuals, NULL);
00230 
00231           std::cout << std::endl << "  z: ";
00232           for (size_t k = 0; k < data[i].observations[index].features.size(); ++k)
00233             std::cout << "  " << std::setw(10) << std::fixed << residuals[(k)];
00234           std::cout << std::endl << std::endl;
00235         }
00236 
00237         problem->AddResidualBlock(cost,
00238                                   NULL /* squared loss */,
00239                                   free_params);
00240       }
00241       else if (params.error_blocks[j].type == "outrageous")
00242       {
00243         // Outrageous error block requires no particular sensors, add to every sample
00244         problem->AddResidualBlock(
00245           OutrageousError::Create(offsets_.get(),
00246                                   params.error_blocks[j].name,
00247                                   static_cast<double>(params.error_blocks[j].params["joint_scale"]),
00248                                   static_cast<double>(params.error_blocks[j].params["position_scale"]),
00249                                   static_cast<double>(params.error_blocks[j].params["rotation_scale"])),
00250           NULL, // squared loss
00251           free_params);
00252       }
00253       else
00254       {
00255         // ERROR unknown
00256       }
00257     }
00258   }
00259 
00260   // Setup the actual optimization
00261   ceres::Solver::Options options;
00262   options.use_nonmonotonic_steps = true;
00263   options.function_tolerance = 1e-10;
00264   options.linear_solver_type = ceres::DENSE_QR;
00265   options.max_num_iterations = 1000;
00266   options.minimizer_progress_to_stdout = progress_to_stdout;
00267   //  options.use_nonmonotonic_steps = true;
00268 
00269   if (progress_to_stdout)
00270     std::cout << "\nSolver output:" << std::endl;
00271   summary_.reset(new ceres::Solver::Summary());
00272   ceres::Solve(options, problem, summary_.get());
00273   if (progress_to_stdout)
00274     std::cout << "\n" << summary_->BriefReport() << std::endl;
00275 
00276   // TODO output stats
00277   /*if (progress_to_stdout)
00278     {
00279     CalibrationOffsetParser no_offsets;
00280     offsets_->update(free_params);
00281     for (size_t i = 0; i < data.size(); ++i)
00282     {
00283     std::cout << "Sample " << i << std::endl;
00284     printSimpleDistanceError(arm_model_, camera_model_, &no_offsets, offsets_, data[i]);
00285     printComparePoints(arm_model_, camera_model_, &no_offsets, offsets_, data[i]);
00286     }
00287     }*/
00288 
00289   // Note: the error blocks will be managed by scoped_ptr in cost functor
00290   //       which takes ownership, and so we do not need to delete them here
00291 
00292   // Done with our free params
00293   delete[] free_params;
00294   delete problem;
00295 
00296   return 0;
00297 }
00298 
00299 }  // namespace robot_calibration


robot_calibration
Author(s): Michael Ferguson
autogenerated on Sat May 20 2017 02:35:31