#include <cstdio>
#include <math.h>
#include <vector>
#include "ceres/ceres.h"
#include "ceres/dynamic_autodiff_cost_function.h"
#include "gflags/gflags.h"
#include "glog/logging.h"
#include "random.h"
using ceres::AutoDiffCostFunction;
using ceres::DynamicAutoDiffCostFunction;
using ceres::CauchyLoss;
using ceres::CostFunction;
using ceres::LossFunction;
using ceres::Problem;
using ceres::Solve;
using ceres::Solver;
using ceres::examples::RandNormal;
using std::min;
using std::vector;
DEFINE_double(corridor_length, 30.0, "Length of the corridor that the robot is "
"travelling down.");
DEFINE_double(pose_separation, 0.5, "The distance that the robot traverses "
"between successive odometry updates.");
DEFINE_double(odometry_stddev, 0.1, "The standard deviation of "
"odometry error of the robot.");
DEFINE_double(range_stddev, 0.01, "The standard deviation of range readings of "
"the robot.");
static const int kStride = 10;
struct OdometryConstraint {
typedef AutoDiffCostFunction<OdometryConstraint, 1, 1> OdometryCostFunction;
OdometryConstraint(double odometry_mean, double odometry_stddev) :
odometry_mean(odometry_mean), odometry_stddev(odometry_stddev) {}
template <typename T>
bool operator()(const T* const odometry, T* residual) const {
*residual = (*odometry - T(odometry_mean)) / T(odometry_stddev);
return true;
}
static OdometryCostFunction* Create(const double odometry_value) {
return new OdometryCostFunction(
new OdometryConstraint(odometry_value, FLAGS_odometry_stddev));
}
const double odometry_mean;
const double odometry_stddev;
};
struct RangeConstraint {
typedef DynamicAutoDiffCostFunction<RangeConstraint, kStride>
RangeCostFunction;
RangeConstraint(
int pose_index,
double range_reading,
double range_stddev,
double corridor_length) :
pose_index(pose_index), range_reading(range_reading),
range_stddev(range_stddev), corridor_length(corridor_length) {}
template <typename T>
bool operator()(T const* const* relative_poses, T* residuals) const {
T global_pose(0);
for (int i = 0; i <= pose_index; ++i) {
global_pose += relative_poses[i][0];
}
residuals[0] = (global_pose + T(range_reading) - T(corridor_length)) /
T(range_stddev);
return true;
}
static RangeCostFunction* Create(const int pose_index,
const double range_reading,
vector<double>* odometry_values,
vector<double*>* parameter_blocks) {
RangeConstraint* constraint = new RangeConstraint(
pose_index, range_reading, FLAGS_range_stddev, FLAGS_corridor_length);
RangeCostFunction* cost_function = new RangeCostFunction(constraint);
parameter_blocks->clear();
for (int i = 0; i <= pose_index; ++i) {
parameter_blocks->push_back(&((*odometry_values)[i]));
cost_function->AddParameterBlock(1);
}
cost_function->SetNumResiduals(1);
return (cost_function);
}
const int pose_index;
const double range_reading;
const double range_stddev;
const double corridor_length;
};
void SimulateRobot(vector<double>* odometry_values,
vector<double>* range_readings) {
const int num_steps = static_cast<int>(
ceil(FLAGS_corridor_length / FLAGS_pose_separation));
double robot_location = 0.0;
for (int i = 0; i < num_steps; ++i) {
const double actual_odometry_value = min(
FLAGS_pose_separation, FLAGS_corridor_length - robot_location);
robot_location += actual_odometry_value;
const double actual_range = FLAGS_corridor_length - robot_location;
const double observed_odometry =
RandNormal() * FLAGS_odometry_stddev + actual_odometry_value;
const double observed_range =
RandNormal() * FLAGS_range_stddev + actual_range;
odometry_values->push_back(observed_odometry);
range_readings->push_back(observed_range);
}
}
void PrintState(const vector<double>& odometry_readings,
const vector<double>& range_readings) {
CHECK_EQ(odometry_readings.size(), range_readings.size());
double robot_location = 0.0;
printf("pose: location odom range r.error o.error\n");
for (int i = 0; i < odometry_readings.size(); ++i) {
robot_location += odometry_readings[i];
const double range_error =
robot_location + range_readings[i] - FLAGS_corridor_length;
const double odometry_error =
FLAGS_pose_separation - odometry_readings[i];
printf("%4d: %8.3f %8.3f %8.3f %8.3f %8.3f\n",
static_cast<int>(i), robot_location, odometry_readings[i],
range_readings[i], range_error, odometry_error);
}
}
int main(int argc, char** argv) {
google::InitGoogleLogging(argv[0]);
CERES_GFLAGS_NAMESPACE::ParseCommandLineFlags(&argc, &argv, true);
CHECK_GT(FLAGS_corridor_length, 0.0);
CHECK_GT(FLAGS_pose_separation, 0.0);
CHECK_GT(FLAGS_odometry_stddev, 0.0);
CHECK_GT(FLAGS_range_stddev, 0.0);
vector<double> odometry_values;
vector<double> range_readings;
SimulateRobot(&odometry_values, &range_readings);
printf("Initial values:\n");
PrintState(odometry_values, range_readings);
ceres::Problem problem;
for (int i = 0; i < odometry_values.size(); ++i) {
vector<double*> parameter_blocks;
RangeConstraint::RangeCostFunction* range_cost_function =
RangeConstraint::Create(
i, range_readings[i], &odometry_values, ¶meter_blocks);
problem.AddResidualBlock(range_cost_function, NULL, parameter_blocks);
problem.AddResidualBlock(OdometryConstraint::Create(odometry_values[i]),
NULL,
&(odometry_values[i]));
}
ceres::Solver::Options solver_options;
solver_options.minimizer_progress_to_stdout = true;
Solver::Summary summary;
printf("Solving...\n");
Solve(solver_options, &problem, &summary);
printf("Done.\n");
std::cout << summary.FullReport() << "\n";
printf("Final values:\n");
PrintState(odometry_values, range_readings);
return 0;
}