Пример #1
0
 py::EigenDMap<Eigen::Matrix2d> corners() { return py::EigenDMap<Eigen::Matrix2d>(mat.data(),
             py::EigenDStride(mat.outerStride() * (mat.outerSize()-1), mat.innerStride() * (mat.innerSize()-1))); }
Пример #2
0
// OUT' = bcsr * B' (for matrices)
void A_mul_Bt(Eigen::MatrixXd & out, CSR & csr, Eigen::MatrixXd & B) {
  if (csr.nrow != out.cols()) {throw std::runtime_error("csr.nrow must equal out.cols()");}
  if (csr.ncol != B.cols())   {throw std::runtime_error("csr.ncol must equal b.cols()");}
  if (out.rows() != B.rows()) {throw std::runtime_error("out.rows() must equal B.rows()");}
  csr_A_mul_Bn( out.data(), & csr, B.data(), B.rows() );
}
Пример #3
0
bool ChompPlanner::solve(const planning_scene::PlanningSceneConstPtr& planning_scene,
                         const moveit_msgs::MotionPlanRequest& req, const chomp::ChompParameters& params,
                         moveit_msgs::MotionPlanDetailedResponse& res) const
{
  if (!planning_scene)
  {
    ROS_ERROR_STREAM_NAMED("chomp_planner", "No planning scene initialized.");
    res.error_code.val = moveit_msgs::MoveItErrorCodes::FAILURE;
    return false;
  }

  if (req.start_state.joint_state.position.empty())
  {
    ROS_ERROR_STREAM_NAMED("chomp_planner", "Start state is empty");
    res.error_code.val = moveit_msgs::MoveItErrorCodes::INVALID_ROBOT_STATE;
    return false;
  }

  if (not planning_scene->getRobotModel()->satisfiesPositionBounds(req.start_state.joint_state.position.data()))
  {
    ROS_ERROR_STREAM_NAMED("chomp_planner", "Start state violates joint limits");
    res.error_code.val = moveit_msgs::MoveItErrorCodes::INVALID_ROBOT_STATE;
    return false;
  }

  ros::WallTime start_time = ros::WallTime::now();
  ChompTrajectory trajectory(planning_scene->getRobotModel(), 3.0, .03, req.group_name);

  jointStateToArray(planning_scene->getRobotModel(), req.start_state.joint_state, req.group_name,
                    trajectory.getTrajectoryPoint(0));

  if (req.goal_constraints.empty())
  {
    ROS_ERROR_STREAM_NAMED("chomp_planner", "No goal constraints specified!");
    res.error_code.val = moveit_msgs::MoveItErrorCodes::INVALID_GOAL_CONSTRAINTS;
    return false;
  }

  if (req.goal_constraints[0].joint_constraints.empty())
  {
    ROS_ERROR_STREAM("Only joint-space goals are supported");
    res.error_code.val = moveit_msgs::MoveItErrorCodes::INVALID_GOAL_CONSTRAINTS;
    return false;
  }

  int goal_index = trajectory.getNumPoints() - 1;
  trajectory.getTrajectoryPoint(goal_index) = trajectory.getTrajectoryPoint(0);
  sensor_msgs::JointState js;
  for (const moveit_msgs::JointConstraint& joint_constraint : req.goal_constraints[0].joint_constraints)
  {
    js.name.push_back(joint_constraint.joint_name);
    js.position.push_back(joint_constraint.position);
    ROS_INFO_STREAM_NAMED("chomp_planner", "Setting joint " << joint_constraint.joint_name << " to position "
                                                            << joint_constraint.position);
  }
  jointStateToArray(planning_scene->getRobotModel(), js, req.group_name, trajectory.getTrajectoryPoint(goal_index));

  const moveit::core::JointModelGroup* model_group =
      planning_scene->getRobotModel()->getJointModelGroup(req.group_name);
  // fix the goal to move the shortest angular distance for wrap-around joints:
  for (size_t i = 0; i < model_group->getActiveJointModels().size(); i++)
  {
    const moveit::core::JointModel* model = model_group->getActiveJointModels()[i];
    const moveit::core::RevoluteJointModel* revolute_joint =
        dynamic_cast<const moveit::core::RevoluteJointModel*>(model);

    if (revolute_joint != nullptr)
    {
      if (revolute_joint->isContinuous())
      {
        double start = (trajectory)(0, i);
        double end = (trajectory)(goal_index, i);
        ROS_INFO_STREAM("Start is " << start << " end " << end << " short " << shortestAngularDistance(start, end));
        (trajectory)(goal_index, i) = start + shortestAngularDistance(start, end);
      }
    }
  }

  const std::vector<std::string>& active_joint_names = model_group->getActiveJointModelNames();
  const Eigen::MatrixXd goal_state = trajectory.getTrajectoryPoint(goal_index);
  moveit::core::RobotState goal_robot_state = planning_scene->getCurrentState();
  goal_robot_state.setVariablePositions(
      active_joint_names, std::vector<double>(goal_state.data(), goal_state.data() + active_joint_names.size()));

  if (not goal_robot_state.satisfiesBounds())
  {
    ROS_ERROR_STREAM_NAMED("chomp_planner", "Goal state violates joint limits");
    res.error_code.val = moveit_msgs::MoveItErrorCodes::INVALID_ROBOT_STATE;
    return false;
  }

  // fill in an initial trajectory based on user choice from the chomp_config.yaml file
  if (params.trajectory_initialization_method_.compare("quintic-spline") == 0)
    trajectory.fillInMinJerk();
  else if (params.trajectory_initialization_method_.compare("linear") == 0)
    trajectory.fillInLinearInterpolation();
  else if (params.trajectory_initialization_method_.compare("cubic") == 0)
    trajectory.fillInCubicInterpolation();
  else if (params.trajectory_initialization_method_.compare("fillTrajectory") == 0)
  {
    if (!(trajectory.fillInFromTrajectory(res)))
    {
      ROS_ERROR_STREAM_NAMED("chomp_planner", "Input trajectory has less than 2 points, "
                                              "trajectory must contain at least start and goal state");
      return false;
    }
  }
  else
    ROS_ERROR_STREAM_NAMED("chomp_planner", "invalid interpolation method specified in the chomp_planner file");

  ROS_INFO_NAMED("chomp_planner", "CHOMP trajectory initialized using method: %s ",
                 (params.trajectory_initialization_method_).c_str());

  // optimize!
  moveit::core::RobotState start_state(planning_scene->getCurrentState());
  moveit::core::robotStateMsgToRobotState(req.start_state, start_state);
  start_state.update();

  ros::WallTime create_time = ros::WallTime::now();

  int replan_count = 0;
  bool replan_flag = false;
  double org_learning_rate = 0.04, org_ridge_factor = 0.0, org_planning_time_limit = 10;
  int org_max_iterations = 200;

  // storing the initial chomp parameters values
  org_learning_rate = params.learning_rate_;
  org_ridge_factor = params.ridge_factor_;
  org_planning_time_limit = params.planning_time_limit_;
  org_max_iterations = params.max_iterations_;

  std::unique_ptr<ChompOptimizer> optimizer;

  // create a non_const_params variable which stores the non constant version of the const params variable
  ChompParameters params_nonconst = params;

  // while loop for replanning (recovery behaviour) if collision free optimized solution not found
  while (true)
  {
    if (replan_flag)
    {
      // increase learning rate in hope to find a successful path; increase ridge factor to avoid obstacles; add 5
      // additional secs in hope to find a solution; increase maximum iterations
      params_nonconst.setRecoveryParams(params_nonconst.learning_rate_ + 0.02, params_nonconst.ridge_factor_ + 0.002,
                                        params_nonconst.planning_time_limit_ + 5, params_nonconst.max_iterations_ + 50);
    }

    // initialize a ChompOptimizer object to load up the optimizer with default parameters or with updated parameters in
    // case of a recovery behaviour
    optimizer.reset(new ChompOptimizer(&trajectory, planning_scene, req.group_name, &params_nonconst, start_state));
    if (!optimizer->isInitialized())
    {
      ROS_ERROR_STREAM_NAMED("chomp_planner", "Could not initialize optimizer");
      res.error_code.val = moveit_msgs::MoveItErrorCodes::PLANNING_FAILED;
      return false;
    }

    ROS_DEBUG_NAMED("chomp_planner", "Optimization took %f sec to create",
                    (ros::WallTime::now() - create_time).toSec());

    bool optimization_result = optimizer->optimize();

    // replan with updated parameters if no solution is found
    if (params_nonconst.enable_failure_recovery_)
    {
      ROS_INFO_NAMED("chomp_planner", "Planned with Chomp Parameters (learning_rate, ridge_factor, "
                                      "planning_time_limit, max_iterations), attempt: # %d ",
                     (replan_count + 1));
      ROS_INFO_NAMED("chomp_planner", "Learning rate: %f ridge factor: %f planning time limit: %f max_iterations %d ",
                     params_nonconst.learning_rate_, params_nonconst.ridge_factor_,
                     params_nonconst.planning_time_limit_, params_nonconst.max_iterations_);

      if (!optimization_result && replan_count < params_nonconst.max_recovery_attempts_)
      {
        replan_count++;
        replan_flag = true;
      }
      else
      {
        break;
      }
    }
    else
      break;
  }  // end of while loop

  // resetting the CHOMP Parameters to the original values after a successful plan
  params_nonconst.setRecoveryParams(org_learning_rate, org_ridge_factor, org_planning_time_limit, org_max_iterations);

  ROS_DEBUG_NAMED("chomp_planner", "Optimization actually took %f sec to run",
                  (ros::WallTime::now() - create_time).toSec());
  create_time = ros::WallTime::now();
  // assume that the trajectory is now optimized, fill in the output structure:

  ROS_DEBUG_NAMED("chomp_planner", "Output trajectory has %d joints", trajectory.getNumJoints());

  res.trajectory.resize(1);

  res.trajectory[0].joint_trajectory.joint_names = active_joint_names;

  res.trajectory[0].joint_trajectory.header = req.start_state.joint_state.header;  // @TODO this is probably a hack

  // fill in the entire trajectory
  res.trajectory[0].joint_trajectory.points.resize(trajectory.getNumPoints());
  for (int i = 0; i < trajectory.getNumPoints(); i++)
  {
    res.trajectory[0].joint_trajectory.points[i].positions.resize(trajectory.getNumJoints());
    for (size_t j = 0; j < res.trajectory[0].joint_trajectory.points[i].positions.size(); j++)
    {
      res.trajectory[0].joint_trajectory.points[i].positions[j] = trajectory.getTrajectoryPoint(i)(j);
    }
    // Setting invalid timestamps.
    // Further filtering is required to set valid timestamps accounting for velocity and acceleration constraints.
    res.trajectory[0].joint_trajectory.points[i].time_from_start = ros::Duration(0.0);
  }

  ROS_DEBUG_NAMED("chomp_planner", "Bottom took %f sec to create", (ros::WallTime::now() - create_time).toSec());
  ROS_DEBUG_NAMED("chomp_planner", "Serviced planning request in %f wall-seconds, trajectory duration is %f",
                  (ros::WallTime::now() - start_time).toSec(),
                  res.trajectory[0].joint_trajectory.points[goal_index].time_from_start.toSec());
  res.error_code.val = moveit_msgs::MoveItErrorCodes::SUCCESS;
  res.processing_time.push_back((ros::WallTime::now() - start_time).toSec());

  // report planning failure if path has collisions
  if (not optimizer->isCollisionFree())
  {
    ROS_ERROR_STREAM_NAMED("chomp_planner", "Motion plan is invalid.");
    res.error_code.val = moveit_msgs::MoveItErrorCodes::INVALID_MOTION_PLAN;
    return false;
  }

  // check that final state is within goal tolerances
  kinematic_constraints::JointConstraint jc(planning_scene->getRobotModel());
  robot_state::RobotState last_state(start_state);
  last_state.setVariablePositions(res.trajectory[0].joint_trajectory.joint_names,
                                  res.trajectory[0].joint_trajectory.points.back().positions);

  bool constraints_are_ok = true;
  for (const moveit_msgs::JointConstraint& constraint : req.goal_constraints[0].joint_constraints)
  {
    constraints_are_ok = constraints_are_ok and jc.configure(constraint);
    constraints_are_ok = constraints_are_ok and jc.decide(last_state).satisfied;
    if (not constraints_are_ok)
    {
      ROS_ERROR_STREAM_NAMED("chomp_planner", "Goal constraints are violated: " << constraint.joint_name);
      res.error_code.val = moveit_msgs::MoveItErrorCodes::GOAL_CONSTRAINTS_VIOLATED;
      return false;
    }
  }

  return true;
}
Пример #4
0
double mp2_gamma_ind_p(
  UnitCell& UCell,
  SuperCell& SCell,
  VMatrixXd& evals,
  std::string mointfile,
  bool read_in_from_file,
  moIntegralFactory& moint_class,
  int argc, char *argv[]
){
  //std::cout << "INITIALIZING MPI...." << std::endl;
  int numtasks, taskid, source;
  //MPI_Init( &argc, &argv );
  MPI_Comm_size( MPI_COMM_WORLD, &numtasks );
  std::cout << "MP2 : RUNNING " << numtasks << " TASKS WITH MPI." << std::endl;
  MPI_Comm_rank( MPI_COMM_WORLD, &taskid );
  std::cout << "MP2 : TASK " << taskid << " STARTED!" << std::endl;
  MPI_Status status;

  int tag1, tag2, tag3, tag4, tag5;
  tag1 = 1;
  tag2 = 2;
  tag3 = 3;
  tag4 = 4;
  tag5 = 5;
  double mp2en = 0.0;
  double total_mp2en = 0.0;
  int nocc, norb;
  std::vector< double > eigenvals;
  Eigen::MatrixXd kernel_matr;
  Eigen::MatrixXd evecsXd;
  std::vector< double > kernel_matr_data;
  std::vector< double > evecsXd_data;
  if( taskid == 0 ){
    nocc = (int)(SCell.nao/2);
    norb = SCell.nao;
    eigenvals.resize( norb );
    for( int i = 0; i < norb; ++i ) eigenvals[ i ] = evals.irrep( 0 )( i, 0 );
    kernel_matr = moint_class.get_kernel_matr();
    evecsXd = moint_class.get_evecsXd();

    /* Send these quantities to all tasks */
    for( int dest = 1; dest < numtasks; ++dest ){
      cout << "SENDING DATA TO TASK " << taskid << endl;
      MPI_Send( &nocc, 1, MPI_INT, dest, tag1, MPI_COMM_WORLD );
      MPI_Send( &norb, 1, MPI_INT, dest, tag2, MPI_COMM_WORLD );
      MPI_Send( &eigenvals[ 0 ], norb, MPI_DOUBLE, dest, tag3, MPI_COMM_WORLD );
      //for( int i = 0; i < norb; ++i ) cout << "TASK " << taskid << " EIGENVALUE " << i << " : " << eigenvals[ i ] << endl;
      MPI_Send( kernel_matr.data(), norb*norb, MPI_DOUBLE, dest, tag4, MPI_COMM_WORLD );
      MPI_Send( evecsXd.data(), norb*norb, MPI_DOUBLE, dest, tag5, MPI_COMM_WORLD );
      cout << "DONE : SENDING DATA TO TASK " << taskid << endl;
    }
  }
  if( taskid > 0 ){
    for( int dest = 1; dest < numtasks; ++dest ){
      if( taskid == dest ){
        cout << "TASK " << dest << " RECIEVING DATA" << endl;
        MPI_Recv( &nocc, 1, MPI_INT, 0, tag1, MPI_COMM_WORLD, &status );
        MPI_Recv( &norb, 1, MPI_INT, 0, tag2, MPI_COMM_WORLD, &status );
        eigenvals.resize( norb );
        kernel_matr_data.resize( norb * norb );
        evecsXd_data.resize( norb * norb );
        MPI_Recv( &eigenvals[ 0 ], norb, MPI_DOUBLE, 0, tag3, MPI_COMM_WORLD, &status );
        //for( int i = 0; i < norb; ++i ) cout << "TASK " << taskid << " EIGENVALUE " << i << " : " << eigenvals[ i ] << endl;
        MPI_Recv( &kernel_matr_data[ 0 ], norb*norb, MPI_DOUBLE, 0, tag4, MPI_COMM_WORLD, &status );
        //for( int i = 0; i < norb*norb; ++i ) cout << "TASK " << taskid << " KERNEL VAL " << i << " : " << kernel_matr_data[ i ] << endl;
        MPI_Recv( &evecsXd_data[ 0 ], norb*norb, MPI_DOUBLE, 0, tag5, MPI_COMM_WORLD, &status );
        //for( int i = 0; i < norb*norb; ++i ) cout << "TASK " << taskid << " EVEVCS_DATA " << i << " : " << evecsXd_data[ i ] << endl;
        kernel_matr.resize( norb, norb );
        evecsXd.resize( norb, norb );
        int counter = 0;
        for( int i = 0; i < norb; ++i )
        for( int j = 0; j < norb; ++j, counter++ ){
          kernel_matr( j, i ) = kernel_matr_data[ counter ];
          evecsXd( j, i ) = evecsXd_data[ counter ];
        }
        //cout << taskid << " KERNEL MATRIX : " << endl;
        //cout << kernel_matr.block( 0, 0, 3, 3 ) << endl;
        //cout << taskid << " EVECS MATRIX  : " << endl;
        //cout << evecsXd.block( 0, 0, 3, 3 ) << endl;
        cout << "DONE : TASK " << dest << " RECIEVING DATA" << endl;
      }
    }
  }

  MPI_Barrier( MPI_COMM_WORLD );

  int mo_index_in_arr;
  double denom, moint1, moint2;
  size_t moindex1, moindex2;
  std::vector< size_t >::iterator it;
  bool sort_mo = true;

  clock_t t, timer;
  size_t opt_size_arr, size_arr;
  std::vector< size_t > indx_arr;
  std::vector< double > dble_arr;
  bool optimize_size_arr;


  if( read_in_from_file ){
    opt_size_arr = 0;
    optimize_size_arr = true;
    std::cout << "READING FROM MULTIPLE MOINT FILES" << std::endl;
    for( int i = 0; i < SCell.nao; ++i ){
      read_gamma_mointb_ind_p( mointfile, i, dble_arr, indx_arr, size_arr, optimize_size_arr );
      if( size_arr > opt_size_arr ) opt_size_arr = size_arr;
    }
    std::cout << "OPTIMAL SIZE OF ARRAYS " << opt_size_arr << std::endl;
    optimize_size_arr = false;
    dble_arr.resize( opt_size_arr );
    indx_arr.resize( opt_size_arr );
  }else{
    dble_arr.resize( 5000000 );
    indx_arr.resize( 5000000 );
  }

  clock_t total_time;
  if( taskid == 0 )
  total_time = clock();

  std::vector< int > break_up_work;
  break_up_work.resize( numtasks + 1 );
  break_up_work[ 0 ] = nocc;
  break_up_work[ numtasks ] = norb;
  int break_up_load = (int)( ( norb - nocc ) / numtasks );

  for( int i = 1; i < break_up_work.size() - 1; ++i ) break_up_work[ i ] = break_up_work[ i - 1 ] + break_up_load;

  //for( int a = nocc; a < norb; ++a ){ 
  for( int i = 0; i < numtasks; ++i ){
  if( taskid == i ){

  int low = break_up_work[ i ];
  int high = break_up_work[ (i + 1) ];

  for( int a = low; a < high; ++a ){ 
    cout << "MP2 COMPLETED ( " << setw( 3 ) << ( a - nocc + 1 ) << " / " << setw( 3 ) << ( norb - nocc ) << " ) " << endl;
    if( read_in_from_file ){
      read_gamma_mointb_ind_p( mointfile, a, dble_arr, indx_arr, size_arr, optimize_size_arr );
    }else{
      t = clock();
      //moint_class.gamma_moint_to_vector( dble_arr, indx_arr, size_arr, 
      //                                   a, (a+1), 0, nocc, 
      //                                   nocc, (a+1), 0, nocc, -10 );
      gamma_moint_to_vector( dble_arr, indx_arr, kernel_matr, evecsXd, norb, size_arr,
                             a, (a+1), 0, nocc, 
                             nocc, (a+1), 0, nocc, -10 );
      t = clock() - t;
      if( taskid == 7 ){
        cout << " MOINT GENERATION FOR TASK " << taskid << endl;
        cout << " - time        = " << t / (double)CLOCKS_PER_SEC << " seconds." << endl;
        cout << " - # non-zero  = " << size_arr << endl;
        cout << " - 'a' virt val= " << a << endl;
      }
    }
    
    t = clock();
    if( sort_mo ){ sorter( dble_arr, indx_arr, size_arr ); }
    t = clock() - t;
    double time_sort = t / (double)CLOCKS_PER_SEC;
    //if( sort_mo )  cout << " o TIME FOR MOINT SORTING : " << time_sort << endl;
    
    t = clock();
    for( int i = 0; i < nocc; ++i ){
      for( int j = 0; j < nocc; ++j ){
        for( int b = nocc; b <= a; ++b ){ 
          moint1 = 0.0;
          moint2 = 0.0;
          denom = eigenvals[ i ] + eigenvals[ j ] - eigenvals[ a ] - eigenvals[ b ];
          moindex1 = get_ijkl( i, a, j, b ); 
          //
          //
          // NEEDS TO BE ORDERED FOR BINARY SEARCH
          //
          //
          if( sort_mo ){
            mo_index_in_arr = binary_search( indx_arr, moindex1, (size_t)0, size_arr-1 ); 
            if( mo_index_in_arr >= 0 ){
              moint1 = dble_arr[ mo_index_in_arr ];
            }else{
              //if( taskid == 7 ){ it = find( indx_arr.begin(), indx_arr.begin() + size_arr, moindex1 );
              //if( it != ( indx_arr.begin() + size_arr ) ){
              //  cout << "YA GOOFED" << endl;
              //  exit( EXIT_FAILURE );
              //}
              //}
            }
          }else{
            it = find( indx_arr.begin(), indx_arr.begin() + size_arr, moindex1 );
            if( it != ( indx_arr.begin() + size_arr ) ){ 
              mo_index_in_arr = it - indx_arr.begin(); 
              moint1 = dble_arr[ mo_index_in_arr ];
            }
          }
          moindex2 = get_ijkl( j, a, i, b ); 
          //
          //
          // NEEDS TO BE ORDERED FOR BINARY SEARCH
          //
          //
          if( sort_mo ){
            mo_index_in_arr = binary_search( indx_arr, moindex2, (size_t)0, size_arr-1 ); 
            if( mo_index_in_arr >= 0 ){
              moint2 = dble_arr[ mo_index_in_arr ];
            }
          }else{
            it = find( indx_arr.begin(), indx_arr.begin() + size_arr, moindex2 );
            if( it != ( indx_arr.begin() + size_arr ) ){ 
              mo_index_in_arr = it - indx_arr.begin(); 
              moint2 = dble_arr[ mo_index_in_arr ];
            }
          }
          if( a == b )
            mp2en += ( moint1 * ( 2. * moint1 - moint2 ) ) / denom;
          else
            mp2en += 2. * ( moint1 * ( 2. * moint1 - moint2 ) ) / denom;
          //if( taskid == 7 ) cout << "TASK 7 : " << moint1 << " " << moint2 << " " << denom << " " << eigenvals[ j ] << " " << \
                            eigenvals[ i ] << " " << eigenvals[ a ] << " " << eigenvals[ b ] << " " << mp2en << endl;
        }
      }
    }
    t = clock() - t;
    double time_other = t / (double)CLOCKS_PER_SEC;
    //cout << " o OTHER MP2 TIME : " << time_other << endl;
    //cout << " o TOTAL MP2 TIME : " << ( time_other + time_sort ) << endl;
  } // end loop over 'a', and so we have this contribution now to mp2 energy

  cout << "TASK " << taskid << " MP2 ENERGY : " << setw( 20 ) << setprecision( 16 ) << mp2en << endl;
  MPI_Reduce( &mp2en, &total_mp2en, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD );
  
  } // end if == taskid 
  } // end loop over tasks
  //MPI_Finalize();

  MPI_Barrier( MPI_COMM_WORLD );

  return total_mp2en;
}
Пример #5
0
void CloudAnalyzer2D::examineFreeSpaceEvidence() {
  freeSpaceEvidence.clear();
  Eigen::Vector3f cameraCenter = -1.0 * pointMin;

  voxel::DirectVoxel<char> freeSpace(numX, numY, numZ);

  for (int k = 0; k < numZ; ++k) {
    for (int j = 0; j < numY; ++j) {
      for (int i = 0; i < numX; ++i) {

        if (!pointInVoxel->at(i, j, k))
          continue;

        Eigen::Vector3d ray(i, j, k);
        ray[0] -= cameraCenter[0] * FLAGS_scale;
        ray[1] -= cameraCenter[1] * FLAGS_scale;
        ray[2] -= cameraCenter[2] * zScale;
        double length = ray.norm();
        Eigen::Vector3d unitRay = ray / length;

        Eigen::Vector3i voxelHit;
        for (int a = 0; a <= ceil(length); ++a) {
          voxelHit[0] = floor(cameraCenter[0] * FLAGS_scale + a * unitRay[0]);
          voxelHit[1] = floor(cameraCenter[1] * FLAGS_scale + a * unitRay[1]);
          voxelHit[2] = floor(cameraCenter[2] * zScale + a * unitRay[2]);

          if (voxelHit[0] < 0 || voxelHit[0] >= numX)
            continue;
          if (voxelHit[1] < 0 || voxelHit[1] >= numY)
            continue;
          if (voxelHit[2] < 0 || voxelHit[2] >= numZ)
            continue;

          freeSpace(voxelHit) = 1;
        }
      }
    }
  }

  for (int r = 0; r < R->size(); ++r) {
    Eigen::MatrixXd collapsedCount = Eigen::MatrixXd::Zero(newRows, newCols);

#pragma omp parallel for schedule(static)
    for (int i = 0; i < collapsedCount.cols(); ++i) {
      for (int j = 0; j < collapsedCount.rows(); ++j) {
        for (int k = 0; k < numZ; ++k) {
          const Eigen::Vector3d coord(i, j, k);
          const Eigen::Vector3i src =
              (R->at(r) * (coord - newZZ) + zeroZero)
                  .unaryExpr([](auto v) { return std::round(v); })
                  .cast<int>();

          if (src[0] < 0 || src[0] >= numX || src[1] < 0 || src[1] >= numY ||
              src[2] < 0 || src[2] >= numZ)
            continue;

          if (freeSpace(src))
            ++collapsedCount(j, i);
        }
      }
    }

    double average, sigma;
    const double *vPtr = collapsedCount.data();
    std::tie(average, sigma) = place::aveAndStdev(
        vPtr, vPtr + collapsedCount.size(), [](double v) { return v; },
        [](double v) -> bool { return v; });

    cv::Mat heatMap(newRows, newCols, CV_8UC1, cv::Scalar::all(255));
    for (int j = 0; j < heatMap.rows; ++j) {
      uchar *dst = heatMap.ptr<uchar>(j);
      for (int i = 0; i < heatMap.cols; ++i) {
        const double count = collapsedCount(j, i);
        if (count > 0) {
          const int gray = cv::saturate_cast<uchar>(
              255.0 * ((count - average) / sigma + 1.0));
          dst[i] = 255 - gray;
        }
      }
    }
    const double radius = 0.3;
    for (int j = -sqrt(radius) * FLAGS_scale; j < sqrt(radius) * FLAGS_scale;
         ++j) {
      uchar *dst = heatMap.ptr<uchar>(j + imageZeroZero[1]);
      for (int i = -sqrt(radius * FLAGS_scale * FLAGS_scale - j * j);
           i < sqrt(radius * FLAGS_scale * FLAGS_scale - j * j); ++i) {
        dst[i + imageZeroZero[0]] = 0;
      }
    }

    if (FLAGS_preview) {
      cvNamedWindow("Preview", CV_WINDOW_NORMAL);
      cv::imshow("Preview", heatMap);
      cv::waitKey(0);
    }

    freeSpaceEvidence.push_back(heatMap);
  }
}