main.cpp
Go to the documentation of this file.
1 // Copyright 2022-2024 Ekumen, Inc.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include <cmath>
16 #include <filesystem>
17 #include <fstream>
18 #include <iostream>
19 #include <random>
20 #include <string>
21 #include <vector>
22 
23 #include <range/v3/action.hpp>
24 #include <range/v3/algorithm/any_of.hpp>
25 #include <range/v3/algorithm/min_element.hpp>
26 #include <range/v3/numeric/accumulate.hpp>
27 #include <range/v3/range/conversion.hpp>
28 #include <range/v3/view/generate.hpp>
29 #include <range/v3/view/take_exactly.hpp>
30 
31 #include <yaml-cpp/yaml.h>
32 #include <beluga/beluga.hpp>
33 
34 namespace beluga::tutorial {
35 
36 struct Parameters {
38 
42  std::size_t map_size{100};
43 
45  std::size_t number_of_particles{300};
46 
48  std::size_t number_of_cycles{100};
49 
51  double initial_position{0.0};
52 
54  double initial_position_sigma{10.0};
55 
57  double dt{1.0};
58 
60  double velocity{1.0};
61 
63 
66  double motion_model_sigma{1.0};
67 
69  double sensor_range{3.0};
70 
72 
75  double sensor_model_sigma{1.0};
76 
78 
81  double min_particle_weight{0.08};
82 
84  std::vector<double> landmark_map{5, 12, 25, 37, 52, 55, 65, 74, 75, 87, 97};
85 
87 
90  std::filesystem::path record_path{"./record.yaml"};
91 };
92 
93 using Particle = std::tuple<double, beluga::Weight>;
94 
95 struct RobotRecord {
96  double ground_truth;
97  std::vector<Particle> current;
98  std::vector<Particle> prediction;
99  std::vector<Particle> update;
100  std::pair<double, double> estimation;
101 };
102 
103 } // namespace beluga::tutorial
104 
105 namespace YAML {
106 
107 template <>
108 struct convert<beluga::tutorial::Parameters> {
109  static bool decode(const Node& node, beluga::tutorial::Parameters& parameters) {
110  parameters.map_size = node["map_size"].as<std::size_t>();
111  parameters.number_of_particles = node["number_of_particles"].as<std::size_t>();
112  parameters.number_of_cycles = node["number_of_cycles"].as<std::size_t>();
113  parameters.initial_position = node["initial_position"].as<double>();
114  parameters.initial_position_sigma = node["initial_position_sigma"].as<double>();
115  parameters.dt = node["dt"].as<double>();
116  parameters.velocity = node["velocity"].as<double>();
117  parameters.motion_model_sigma = node["motion_model_sigma"].as<double>();
118  parameters.sensor_range = node["sensor_range"].as<double>();
119  parameters.sensor_model_sigma = node["sensor_model_sigma"].as<double>();
120  parameters.min_particle_weight = node["min_particle_weight"].as<double>();
121  parameters.record_path = node["record_path"].as<std::string>();
122  parameters.landmark_map = node["landmark_map"].as<std::vector<double>>();
123  return true;
124  }
125 };
126 
127 template <>
128 struct convert<std::vector<beluga::tutorial::Particle>> {
129  static Node encode(const std::vector<beluga::tutorial::Particle>& particles) {
130  Node node;
131  node["states"] = beluga::views::states(particles) | ranges::to<std::vector<double>>;
132  node["weights"] = beluga::views::weights(particles) | ranges::to<std::vector<double>>;
133  return node;
134  }
135 };
136 
137 template <>
138 struct convert<std::vector<beluga::tutorial::RobotRecord>> {
139  static Node encode(const std::vector<beluga::tutorial::RobotRecord>& records) {
140  Node node;
141  for (auto&& [cycle, record] : ranges::views::enumerate(records)) {
142  node[cycle]["ground_truth"] = record.ground_truth;
143 
144  auto particles = node[cycle]["particles"];
145  particles["current"] = record.current;
146  particles["prediction"] = record.prediction;
147  particles["update"] = record.update;
148 
149  auto estimation = node[cycle]["estimation"];
150  estimation["mean"] = std::get<0>(record.estimation);
151  estimation["sd"] = std::sqrt(std::get<1>(record.estimation));
152  }
153  return node;
154  }
155 };
156 
157 void DumpFile(const std::filesystem::path& path, const Node& node) {
158  std::ofstream fout(path, std::ios::trunc);
159  fout << node;
160 }
161 
162 } // namespace YAML
163 
164 namespace beluga::tutorial {
165 
166 int run(const std::filesystem::path& path) {
167  const auto parameters = YAML::LoadFile(path).as<beluga::tutorial::Parameters>();
168 
169  std::normal_distribution<double> initial_position_distribution(
170  parameters.initial_position, parameters.initial_position_sigma);
171  auto particles = beluga::views::sample(initial_position_distribution) | //
172  ranges::views::transform(beluga::make_from_state<Particle>) | //
173  ranges::views::take_exactly(parameters.number_of_particles) | //
174  ranges::to<std::vector>;
175 
176  std::vector<RobotRecord> records;
177  records.reserve(parameters.number_of_cycles);
178 
179  double current_position{parameters.initial_position};
180  for (std::size_t n = 0; n < parameters.number_of_cycles; ++n) {
181  RobotRecord record;
182 
183  current_position += parameters.velocity * parameters.dt;
184  record.ground_truth = current_position;
185 
186  if (current_position > static_cast<double>(parameters.map_size)) {
187  break;
188  }
189 
190  const auto motion_model = [&](double position, auto& random_engine) {
191  std::normal_distribution<double> motion_distribution(
192  parameters.velocity * parameters.dt, parameters.motion_model_sigma * parameters.dt);
193  return position + motion_distribution(random_engine);
194  };
195 
196  const auto range_measurements =
197  parameters.landmark_map | //
198  ranges::views::transform([&](double landmark_position) { return landmark_position - current_position; }) | //
199  ranges::views::remove_if([&](double range) { return std::abs(range) > parameters.sensor_range; }) | //
200  ranges::to<std::vector>;
201 
202  const auto sensor_model = [&](double position) {
203  auto range_map =
204  parameters.landmark_map | //
205  ranges::views::transform([&](double landmark_position) { return landmark_position - position; }) | //
206  ranges::to<std::vector>;
207 
208  return parameters.min_particle_weight +
209  std::transform_reduce(
210  range_measurements.begin(), range_measurements.end(), 1.0, std::multiplies<>{},
211  [&](double range_measurement) {
212  const auto distances = range_map | ranges::views::transform([&](double range) {
213  return std::abs(range - range_measurement);
214  });
215  const auto min_distance = ranges::min(distances);
216  return std::exp((-1 * std::pow(min_distance, 2)) / (2 * parameters.sensor_model_sigma));
217  });
218  };
219 
220  record.current = particles;
221 
222  particles |= beluga::actions::propagate(std::execution::seq, motion_model);
223  record.prediction = particles;
224 
225  particles |= beluga::actions::reweight(std::execution::seq, sensor_model) | //
228  ranges::views::take_exactly(parameters.number_of_particles) | //
230  record.update = particles;
231 
232  const auto estimation = beluga::estimate(beluga::views::states(particles), beluga::views::weights(particles));
233  record.estimation = estimation;
234 
235  records.push_back(std::move(record));
236  }
237 
238  YAML::Node output;
239  output["landmark_map"] = parameters.landmark_map;
240  output["simulation_records"] = std::move(records);
241  YAML::DumpFile(parameters.record_path, output);
242 
243  return 0;
244 }
245 
246 } // namespace beluga::tutorial
247 
248 int main(int argc, char* argv[]) {
249  if (argc < 2) {
250  std::cerr << "Usage: " << argv[0] << " <parameters_path>\n";
251  return 1;
252  }
253 
254  return beluga::tutorial::run(argv[1]);
255 }
beluga::tutorial::RobotRecord::current
std::vector< Particle > current
Definition: main.cpp:97
beluga::tutorial
Definition: main.cpp:34
beluga::tutorial::Parameters::motion_model_sigma
double motion_model_sigma
Motion model standard deviation, in meters per second (m/s).
Definition: main.cpp:66
beluga::tutorial::Parameters::initial_position
double initial_position
Initial robot position, in meters (m).
Definition: main.cpp:51
beluga::tutorial::Parameters::number_of_particles
std::size_t number_of_particles
Fixed number of particles used by the algorithm.
Definition: main.cpp:45
beluga::actions::assign
constexpr detail::assign_fn assign
beluga.hpp
YAML
Definition: main.cpp:105
beluga::actions::propagate
constexpr detail::propagate_fn propagate
beluga::actions::reweight
constexpr detail::reweight_fn reweight
YAML::convert< beluga::tutorial::Parameters >::decode
static bool decode(const Node &node, beluga::tutorial::Parameters &parameters)
Definition: main.cpp:109
beluga::tutorial::RobotRecord
Definition: main.cpp:95
beluga::tutorial::Parameters::velocity
double velocity
Constant robot velocity, in meters per second (m/s).
Definition: main.cpp:60
beluga::tutorial::run
int run(const std::filesystem::path &path)
Definition: main.cpp:166
beluga::tutorial::RobotRecord::update
std::vector< Particle > update
Definition: main.cpp:99
beluga::tutorial::RobotRecord::prediction
std::vector< Particle > prediction
Definition: main.cpp:98
beluga::tutorial::Particle
std::tuple< double, beluga::Weight > Particle
Definition: main.cpp:93
beluga::tutorial::Parameters::initial_position_sigma
double initial_position_sigma
Standard deviation of the robot's estimate of its initial position.
Definition: main.cpp:54
beluga::tutorial::RobotRecord::estimation
std::pair< double, double > estimation
Definition: main.cpp:100
beluga::tutorial::Parameters::record_path
std::filesystem::path record_path
Dataset path.
Definition: main.cpp:90
beluga::actions::normalize
constexpr ranges::actions::action_closure< detail::normalize_fn > normalize
YAML::convert< std::vector< beluga::tutorial::RobotRecord > >::encode
static Node encode(const std::vector< beluga::tutorial::RobotRecord > &records)
Definition: main.cpp:139
beluga::tutorial::Parameters::map_size
std::size_t map_size
Size of the 1D map in (m)
Definition: main.cpp:42
beluga::tutorial::Parameters::number_of_cycles
std::size_t number_of_cycles
Number of simulation cycles.
Definition: main.cpp:48
beluga::tutorial::Parameters::sensor_range
double sensor_range
Sensor field of view, in meters (m).
Definition: main.cpp:69
std
YAML::DumpFile
void DumpFile(const std::filesystem::path &path, const Node &node)
Definition: main.cpp:157
YAML::convert< std::vector< beluga::tutorial::Particle > >::encode
static Node encode(const std::vector< beluga::tutorial::Particle > &particles)
Definition: main.cpp:129
beluga::views::weights
constexpr auto weights
beluga::tutorial::Parameters::min_particle_weight
double min_particle_weight
Minimum particle weight.
Definition: main.cpp:81
beluga::tutorial::Parameters::dt
double dt
Delta time in seconds (s).
Definition: main.cpp:57
beluga::tutorial::RobotRecord::ground_truth
double ground_truth
Definition: main.cpp:96
beluga::tutorial::Parameters::sensor_model_sigma
double sensor_model_sigma
Sensor model standard deviation, in meters (m).
Definition: main.cpp:75
beluga::tutorial::Parameters::landmark_map
std::vector< double > landmark_map
Landmark positions in the simulated world.
Definition: main.cpp:84
beluga::tutorial::Parameters
Definition: main.cpp:36
beluga::estimate
std::pair< Sophus::SE2< Scalar >, Sophus::Matrix3< Scalar > > estimate(Poses &&poses)
main
int main(int argc, char *argv[])
Definition: main.cpp:248
beluga::views::states
constexpr auto states
beluga
beluga::views::sample
constexpr ranges::views::view_closure< detail::sample_fn > sample


beluga_tutorial
Author(s):
autogenerated on Tue Jul 16 2024 03:00:13