NASA Astrobee Robot Software  0.19.1
Flight software for the Astrobee robots operating inside the International Space Station.
timestamped_node_adder.h
Go to the documentation of this file.
1 /* Copyright (c) 2017, United States Government, as represented by the
2  * Administrator of the National Aeronautics and Space Administration.
3  *
4  * All rights reserved.
5  *
6  * The Astrobee platform is licensed under the Apache License, Version 2.0
7  * (the "License"); you may not use this file except in compliance with the
8  * License. You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
14  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
15  * License for the specific language governing permissions and limitations
16  * under the License.
17  */
18 
19 #ifndef NODE_ADDERS_TIMESTAMPED_NODE_ADDER_H_
20 #define NODE_ADDERS_TIMESTAMPED_NODE_ADDER_H_
21 
27 
28 #include <algorithm>
29 #include <vector>
30 
31 namespace node_adders {
32 
33 // Sliding window node adder using timestamp-indexed nodes.
34 // Generates functions that adds nodes, relative factors, splits old factors, and so on.
35 // Uses the provided node adder model to accomplish these.
36 template <typename NodeType, typename TimestampedNodesType, typename NodeAdderModelType>
39 
40  public:
41  // Construct using nodes. Creates timestamped nodes interally.
43  const typename NodeAdderModelType::Params& node_adder_model_params,
44  std::shared_ptr<nodes::Values> values);
45 
46  // Construct using already constructed timestamped nodes.
48  const typename NodeAdderModelType::Params& node_adder_model_params,
49  std::shared_ptr<TimestampedNodesType> timestamped_nodes);
50 
51  // For serialization only
52  TimestampedNodeAdder() = default;
53 
54  virtual ~TimestampedNodeAdder() = default;
55 
56  void AddInitialNodesAndPriors(gtsam::NonlinearFactorGraph& factors) final;
57 
58  // Adds initial nodes and priors using provided noise values and timestamp.
59  void AddInitialNodesAndPriors(const NodeType& initial_node, const std::vector<gtsam::SharedNoiseModel>& initial_noise,
60  const localization_common::Time timestamp, gtsam::NonlinearFactorGraph& factors);
61 
62  bool AddNode(const localization_common::Time timestamp, gtsam::NonlinearFactorGraph& factors) final;
63 
64  // Slides the window, removes nodes older than oldest allowed time.
65  // Adds priors to the oldest remaining nodes using their marginalized covariances
66  // and removes old priors containing any key in old keys if param use_priors is true.
67  // Note: Old factor removal (other than starting priors) is handled in the graph optimizer.
68  // The oldest allowed timestamp is also determing by the graph optimizer based on
69  // the ideal oldest allowed timestamps of each node adder used in the graph.
70  bool SlideWindow(const localization_common::Time oldest_allowed_timestamp,
71  const boost::optional<const gtsam::Marginals&>& marginals, const gtsam::KeyVector& old_keys,
72  const double huber_k, gtsam::NonlinearFactorGraph& factors) override;
73 
74  // Returns the oldest node time that should remain after SlideWindow is called.
75  // Calculated using params to ensure the set min/max node limits are enforced
76  // and the ideal duration is enforced otherwise.
77  // Returns boost::none if no nodes exist or too few nodes exist.
78  // If the min/max limits are enforced and the total duration of nodes is still less than
79  // the ideal duration, the new oldest time is set to 0.
80  // Used by the graph optimizer to compute the new oldest time for all node adders
81  // in the graph.
82  boost::optional<localization_common::Time> SlideWindowNewStartTime() const final;
83 
84  gtsam::KeyVector OldKeys(const localization_common::Time oldest_allowed_time,
85  const gtsam::NonlinearFactorGraph& graph) const final;
86 
87  boost::optional<localization_common::Time> StartTime() const final;
88 
89  boost::optional<localization_common::Time> EndTime() const final;
90 
91  bool CanAddNode(const localization_common::Time timestamp) const final;
92 
93  gtsam::KeyVector Keys(const localization_common::Time timestamp) const final;
94 
95  const TimestampedNodesType& nodes() const { return *nodes_; }
96 
97  std::shared_ptr<const TimestampedNodesType> nodes_ptr() { return nodes_; }
98 
99  NodeAdderModelType& node_adder_model() { return node_adder_model_; }
100 
101  private:
102  void RemovePriors(const gtsam::KeyVector& old_keys, gtsam::NonlinearFactorGraph& factors);
103  bool AddNewNodesAndRelativeFactors(const localization_common::Time timestamp, gtsam::NonlinearFactorGraph& factors);
104  bool SplitOldRelativeFactor(const localization_common::Time timestamp, gtsam::NonlinearFactorGraph& factors);
105 
106  // Serialization function
107  friend class boost::serialization::access;
108  template <class Archive>
109  void serialize(Archive& ar, const unsigned int file_version) {
110  ar& BOOST_SERIALIZATION_BASE_OBJECT_NVP(Base);
111  ar& BOOST_SERIALIZATION_NVP(nodes_);
112  ar& BOOST_SERIALIZATION_NVP(node_adder_model_);
113  ar& BOOST_SERIALIZATION_NVP(params_);
114  }
115 
117  NodeAdderModelType node_adder_model_;
118  std::shared_ptr<TimestampedNodesType> nodes_;
119 };
120 
121 // Implementation
122 template <typename NodeType, typename TimestampedNodesType, typename NodeAdderModelType>
125  const typename NodeAdderModelType::Params& node_adder_model_params, std::shared_ptr<nodes::Values> values)
126  : params_(params),
127  nodes_(std::make_shared<TimestampedNodesType>(values)),
128  node_adder_model_(node_adder_model_params) {}
129 
130 template <typename NodeType, typename TimestampedNodesType, typename NodeAdderModelType>
133  const typename NodeAdderModelType::Params& node_adder_model_params,
134  std::shared_ptr<TimestampedNodesType> timestamped_nodes)
135  : params_(params), nodes_(timestamped_nodes), node_adder_model_(node_adder_model_params) {}
136 
137 template <typename NodeType, typename TimestampedNodesType, typename NodeAdderModelType>
139  gtsam::NonlinearFactorGraph& factors) {
140  AddInitialNodesAndPriors(params_.start_node, params_.start_noise_models, params_.starting_time, factors);
141 }
142 
143 template <typename NodeType, typename TimestampedNodesType, typename NodeAdderModelType>
145  const NodeType& initial_node, const std::vector<gtsam::SharedNoiseModel>& initial_noise,
146  const localization_common::Time timestamp, gtsam::NonlinearFactorGraph& factors) {
147  nodes_->Add(timestamp, initial_node);
148  node_adder_model_.AddPriors(initial_node, initial_noise, timestamp, *nodes_, factors);
149  // Store initial node as measurement so subsequent measurements can be computed relative to this
150 }
151 
152 template <typename NodeType, typename TimestampedNodesType, typename NodeAdderModelType>
154  const localization_common::Time oldest_allowed_timestamp, const boost::optional<const gtsam::Marginals&>& marginals,
155  const gtsam::KeyVector& old_keys, const double huber_k, gtsam::NonlinearFactorGraph& factors) {
156  if (oldest_allowed_timestamp <= *(StartTime())) {
157  LogDebug("SlideWindow: Oldest allowed timestamp older than current start time, nothing to do.");
158  return true;
159  }
160  nodes_->RemoveOldNodes(oldest_allowed_timestamp);
161  if (params_.add_priors) {
162  // Add prior to oldest pose using covariances from last round of
163  // optimization
164  const auto oldest_node = nodes_->OldestNode();
165  const auto start_time = StartTime();
166  if (!oldest_node || !start_time) {
167  LogError("SlideWindow: Failed to get oldest node and timestamp.");
168  return false;
169  }
170 
171  // Make sure priors are removed before adding new ones
172  RemovePriors(old_keys, factors);
173  if (marginals) {
174  const auto keys = nodes_->Keys(*start_time);
175  if (keys.empty()) {
176  LogError("SlideWindow: Failed to get oldest keys.");
177  return false;
178  }
179 
180  std::vector<gtsam::SharedNoiseModel> prior_noise_models;
181  for (const auto& key : keys) {
182  // If covariance doesn't exist yet (can happen if the new start node hasn't been optimized yet)
183  // revert to the initial start noise for the prior.
184  try {
185  const auto prior_noise = localization_common::Robust(
186  gtsam::noiseModel::Gaussian::Covariance(marginals->marginalCovariance(key)), huber_k);
187  prior_noise_models.emplace_back(prior_noise);
188  } catch (...) {
189  prior_noise_models = params_.start_noise_models;
190  break;
191  }
192  }
193  node_adder_model_.AddPriors(*oldest_node, prior_noise_models, *start_time, *nodes_, factors);
194  } else {
195  // TODO(rsoussan): Add seperate marginal fallback sigmas instead of relying on starting prior noise
196  node_adder_model_.AddPriors(*oldest_node, params_.start_noise_models, *start_time, *nodes_, factors);
197  }
198  }
199 
200  return true;
201 }
202 
203 template <typename NodeType, typename TimestampedNodesType, typename NodeAdderModelType>
204 boost::optional<localization_common::Time>
206  if (nodes_->empty()) {
207  LogDebug("SlideWindowNewStartTime: No states in map.");
208  return boost::none;
209  }
210 
211  const int size = nodes_->size();
212  if (size <= params_.min_num_states) {
213  LogDebug("SlideWindowNewStartTime: Not enough states to remove.");
214  return boost::none;
215  }
216 
217  const double total_duration = nodes_->Duration();
218  LogDebug("SlideWindowNewStartTime: Starting total num states: " << nodes_->size());
219  LogDebug("SlideWindowNewStartTime: Starting total duration is " << total_duration);
220  const localization_common::Time ideal_oldest_allowed_state = std::max(0.0, *(EndTime()) - params_.ideal_duration);
221 
222  // Find oldest timestamp that first prioritizes that the graph has at least min_num_states.
223  // Second priority, find the optimal oldest timestamp that additionally ensures the graph does not have more than
224  // max_num_states. Last priortity, find the optimial oldest timestamp that is also closest to the ideal oldest
225  // timestamp (and thus ensures the graph is <= the ideal duration).
226  int num_states_to_be_removed = 0;
227  for (const auto& timestamp : nodes_->Timestamps()) {
228  const int new_num_states = size - num_states_to_be_removed++;
229  // First priority
230  if (new_num_states <= params_.min_num_states) return timestamp;
231  // Second priority
232  if (new_num_states > params_.max_num_states) continue;
233  // Last priority
234  if (timestamp >= ideal_oldest_allowed_state) return timestamp;
235  }
236 
237  // If the constraints aren't able to be satisfied, an error was made
238  // when setting the graph duration or state limit params
239  LogError("SlideWindowNewStartTime: Invalid sliding window params set, max num states: "
240  << params_.max_num_states << ", min num states: " << params_.min_num_states
241  << ", ideal duration: " << params_.ideal_duration);
242  return boost::none;
243 }
244 
245 template <typename NodeType, typename TimestampedNodesType, typename NodeAdderModelType>
247  const localization_common::Time oldest_allowed_time, const gtsam::NonlinearFactorGraph& graph) const {
248  return nodes_->OldKeys(oldest_allowed_time);
249 }
250 
251 template <typename NodeType, typename TimestampedNodesType, typename NodeAdderModelType>
252 boost::optional<localization_common::Time>
254  return nodes_->OldestTimestamp();
255 }
256 
257 template <typename NodeType, typename TimestampedNodesType, typename NodeAdderModelType>
258 boost::optional<localization_common::Time>
260  return nodes_->LatestTimestamp();
261 }
262 
263 template <typename NodeType, typename TimestampedNodesType, typename NodeAdderModelType>
265  const localization_common::Time timestamp) const {
266  return node_adder_model_.CanAddNode(timestamp);
267 }
268 
269 template <typename NodeType, typename TimestampedNodesType, typename NodeAdderModelType>
271  const localization_common::Time timestamp) const {
272  return nodes_->Keys(timestamp);
273 }
274 
275 template <typename NodeType, typename TimestampedNodesType, typename NodeAdderModelType>
277  const gtsam::KeyVector& old_keys, gtsam::NonlinearFactorGraph& factors) {
278  int removed_factors = 0;
279  for (auto factor_it = factors.begin(); factor_it != factors.end();) {
280  bool erase_factor = false;
281  const auto prior_factor = dynamic_cast<gtsam::PriorFactor<NodeType>*>(factor_it->get());
282  if (prior_factor) {
283  // Erase factor if it contains an old key
284  for (const auto& old_key : old_keys) {
285  if (prior_factor->key() == old_key) {
286  erase_factor = true;
287  factor_it = factors.erase(factor_it);
288  ++removed_factors;
289  }
290  }
291  if (!erase_factor) {
292  ++factor_it;
293  }
294  } else {
295  ++factor_it;
296  }
297  }
298  LogDebug("RemovePriors: Erase " << removed_factors << " factors.");
299 }
300 
301 template <typename NodeType, typename TimestampedNodesType, typename NodeAdderModelType>
303  const localization_common::Time timestamp, gtsam::NonlinearFactorGraph& factors) {
304  if (nodes_->Contains(timestamp)) {
305  LogDebug(
306  "Adder: Node exists at "
307  "timestamp, nothing to do.");
308  return true;
309  }
310 
311  const auto end_time = EndTime();
312  if (!end_time) {
313  LogError("Adder: Failed to get end timestamp.");
314  return false;
315  }
316  if (timestamp > *end_time) {
317  LogDebug("Adder: Adding new nodes and relative factors.");
318  return AddNewNodesAndRelativeFactors(timestamp, factors);
319  } else {
320  LogDebug("Adder: Splitting old relative factor.");
321  return SplitOldRelativeFactor(timestamp, factors);
322  }
323 }
324 
325 template <typename NodeType, typename TimestampedNodesType, typename NodeAdderModelType>
327  const localization_common::Time timestamp, gtsam::NonlinearFactorGraph& factors) {
328  const auto timestamp_a = EndTime();
329  if (!timestamp_a) {
330  LogError("AddNewNodesAndRelativeFactor: Failed to get end timestamp.");
331  return false;
332  }
333  return node_adder_model_.AddNodesAndRelativeFactors(*timestamp_a, timestamp, *nodes_, factors);
334 }
335 
336 template <typename NodeType, typename TimestampedNodesType, typename NodeAdderModelType>
337 bool TimestampedNodeAdder<NodeType, TimestampedNodesType, NodeAdderModelType>::SplitOldRelativeFactor(
338  const localization_common::Time timestamp, gtsam::NonlinearFactorGraph& factors) {
339  const auto timestamp_bounds = nodes_->LowerAndUpperBoundTimestamps(timestamp);
340  // TODO(rsoussan): print upper and lower bound! print timestamp!
341  if (!timestamp_bounds.first || !timestamp_bounds.second) {
342  LogError("SplitOldRelativeFactor: Failed to get upper and lower bound timestamp.");
343  return false;
344  }
345 
346  const localization_common::Time lower_bound_time = *(timestamp_bounds.first);
347  const localization_common::Time upper_bound_time = *(timestamp_bounds.second);
348 
349  if (timestamp < lower_bound_time || timestamp > upper_bound_time) {
350  LogError("SplitOldRelativeFactor: Timestamp is not within bounds of existing timestamps.");
351  return false;
352  }
353 
354  const bool removed_old_factors =
355  node_adder_model_.RemoveRelativeFactors(lower_bound_time, upper_bound_time, *nodes_, factors);
356  if (!removed_old_factors) {
357  LogError(
358  "SplitOldRelativeFactor: Failed to remove "
359  "old factors.");
360  return false;
361  }
362  if (!node_adder_model_.AddNodesAndRelativeFactors(lower_bound_time, timestamp, *nodes_, factors)) {
363  LogError("SplitOldRelativeFactor: Failed to add first relative node and factor.");
364  return false;
365  }
366  if (!node_adder_model_.AddRelativeFactors(timestamp, upper_bound_time, *nodes_, factors)) {
367  LogError("SplitOldRelativeFactor: Failed to add second relative factor.");
368  return false;
369  }
370  return true;
371 }
372 } // namespace node_adders
373 
374 #endif // NODE_ADDERS_TIMESTAMPED_NODE_ADDER_H_
node_adders::TimestampedNodeAdder::EndTime
boost::optional< localization_common::Time > EndTime() const final
Definition: timestamped_node_adder.h:259
node_adders::Covariance
gtsam::Matrix Covariance(const gtsam::SharedNoiseModel &robust_gaussian_noise)
Definition: utilities.cc:22
node_adders::TimestampedNodeAdder::OldKeys
gtsam::KeyVector OldKeys(const localization_common::Time oldest_allowed_time, const gtsam::NonlinearFactorGraph &graph) const final
Definition: timestamped_node_adder.h:246
node_adders::TimestampedNodeAdder::StartTime
boost::optional< localization_common::Time > StartTime() const final
Definition: timestamped_node_adder.h:253
node_adders::TimestampedNodeAdder::AddNode
bool AddNode(const localization_common::Time timestamp, gtsam::NonlinearFactorGraph &factors) final
Definition: timestamped_node_adder.h:302
LogError
#define LogError(msg)
Definition: logger.h:55
utilities.h
node_adders::SlidingWindowNodeAdder
Definition: sliding_window_node_adder.h:30
node_adders
Definition: between_factor_node_adder_model.h:35
node_adders::TimestampedNodeAdder::SlideWindowNewStartTime
boost::optional< localization_common::Time > SlideWindowNewStartTime() const final
Definition: timestamped_node_adder.h:205
vive_localization::Params
Params
Definition: vive.h:58
timestamped_nodes.h
node_adders::TimestampedNodeAdder::TimestampedNodeAdder
TimestampedNodeAdder()=default
node_adders::TimestampedNodeAdder::~TimestampedNodeAdder
virtual ~TimestampedNodeAdder()=default
node_adders::TimestampedNodeAdder::SlideWindow
bool SlideWindow(const localization_common::Time oldest_allowed_timestamp, const boost::optional< const gtsam::Marginals & > &marginals, const gtsam::KeyVector &old_keys, const double huber_k, gtsam::NonlinearFactorGraph &factors) override
Definition: timestamped_node_adder.h:153
node_adders::TimestampedNodeAdder::AddInitialNodesAndPriors
void AddInitialNodesAndPriors(gtsam::NonlinearFactorGraph &factors) final
Definition: timestamped_node_adder.h:138
sliding_window_node_adder.h
node_adders::TimestampedNodeAdder::CanAddNode
bool CanAddNode(const localization_common::Time timestamp) const final
Definition: timestamped_node_adder.h:264
std
Definition: tensor.h:39
LogDebug
#define LogDebug(msg)
Definition: logger.h:69
node_adders::TimestampedNodeAdder::node_adder_model
NodeAdderModelType & node_adder_model()
Definition: timestamped_node_adder.h:99
localization_common::Robust
gtsam::noiseModel::Robust::shared_ptr Robust(const gtsam::SharedNoiseModel &noise, const double huber_k)
Definition: utilities.cc:334
node_adders::TimestampedNodeAdderParams
Definition: timestamped_node_adder_params.h:32
localization_common::Time
double Time
Definition: time.h:23
timestamped_node_adder_model.h
node_adders::TimestampedNodeAdder::nodes_ptr
std::shared_ptr< const TimestampedNodesType > nodes_ptr()
Definition: timestamped_node_adder.h:97
node_adders::TimestampedNodeAdder::Keys
gtsam::KeyVector Keys(const localization_common::Time timestamp) const final
Definition: timestamped_node_adder.h:270
timestamped_node_adder_params.h
node_adders::TimestampedNodeAdder::nodes
const TimestampedNodesType & nodes() const
Definition: timestamped_node_adder.h:95
node_adders::TimestampedNodeAdder
Definition: timestamped_node_adder.h:37