Loading...
Searching...
No Matches
LightningDB.cpp
1/*********************************************************************
2 * Software License Agreement (BSD License)
3 *
4 * Copyright (c) 2014, JSK, The University of Tokyo.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * * Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * * Redistributions in binary form must reproduce the above
14 * copyright notice, this list of conditions and the following
15 * disclaimer in the documentation and/or other materials provided
16 * with the distribution.
17 * * Neither the name of the JSK, The University of Tokyo nor the names of its
18 * contributors may be used to endorse or promote products derived
19 * from this software without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 * POSSIBILITY OF SUCH DAMAGE.
33 *********************************************************************/
34
35/* Author: Dave Coleman */
36
37// OMPL
38#include "ompl/tools/lightning/LightningDB.h"
39#include "ompl/base/ScopedState.h"
40#include "ompl/util/Time.h"
41#include "ompl/tools/config/SelfConfig.h"
42#include "ompl/base/PlannerDataStorage.h"
43
44// Boost
45#include <filesystem>
46
47ompl::tools::LightningDB::LightningDB(const base::StateSpacePtr &space)
48{
49 si_ = std::make_shared<base::SpaceInformation>(space);
50
51 // Set nearest neighbor type
52 nn_ = std::make_shared<ompl::NearestNeighborsSqrtApprox<ompl::base::PlannerDataPtr>>();
53
54 // Use our custom distance function for nearest neighbor tree
55 nn_->setDistanceFunction([this](const ompl::base::PlannerDataPtr &a, const ompl::base::PlannerDataPtr &b)
56 { return distanceFunction(a, b); });
57
58 // Load the PlannerData instance to be used for searching
59 nnSearchKey_ = std::make_shared<ompl::base::PlannerData>(si_);
60}
61
63{
64 if (numUnsavedPaths_)
65 OMPL_WARN("The database is being unloaded with unsaved experiences");
66}
67
68bool ompl::tools::LightningDB::load(const std::string &fileName)
69{
70 // Error checking
71 if (fileName.empty())
72 {
73 OMPL_ERROR("Empty filename passed to save function");
74 return false;
75 }
76 if (!std::filesystem::exists(fileName))
77 {
78 OMPL_WARN("Database file does not exist: %s", fileName.c_str());
79 return false;
80 }
81
82 // Load database from file, track loading time
83 time::point start = time::now();
84
85 OMPL_INFORM("Loading database from file: %s", fileName.c_str());
86
87 // Open a binary input stream
88 std::ifstream iStream(fileName.c_str(), std::ios::binary);
89
90 // Get the total number of paths saved
91 double numPaths = 0;
92 iStream >> numPaths;
93
94 // Check that the number of paths makes sense
95 if (numPaths < 0 || numPaths > std::numeric_limits<double>::max())
96 {
97 OMPL_WARN("Number of paths to load %d is a bad value", numPaths);
98 return false;
99 }
100
101 // Start loading all the PlannerDatas
102 for (std::size_t i = 0; i < numPaths; ++i)
103 {
104 // Create a new planner data instance
105 auto plannerData(std::make_shared<ompl::base::PlannerData>(si_));
106
107 // Note: the StateStorage class checks if the states match for us
108 plannerDataStorage_.load(iStream, *plannerData.get());
109
110 // Add to nearest neighbor tree
111 nn_->add(plannerData);
112 }
113
114 // Close file
115 iStream.close();
116
117 double loadTime = time::seconds(time::now() - start);
118 OMPL_INFORM("Loaded database from file in %f sec with %d paths", loadTime, nn_->size());
119 return true;
120}
121
123{
124 // Benchmark runtime
125 time::point startTime = time::now();
126 {
127 addPathHelper(solutionPath);
128 }
129 insertionTime = time::seconds(time::now() - startTime);
130}
131
132void ompl::tools::LightningDB::addPathHelper(ompl::geometric::PathGeometric &solutionPath)
133{
134 // Create a new planner data instance
135 auto plannerData(std::make_shared<ompl::base::PlannerData>(si_));
136
137 // Add the states to one nodes files
138 for (auto &i : solutionPath.getStates())
139 {
140 ompl::base::PlannerDataVertex vert(i); // TODO tag?
141
142 plannerData->addVertex(vert);
143 }
144
145 // Deep copy the states in the vertices so that when the planner goes out of scope, all data remains intact
146 plannerData->decoupleFromPlanner();
147
148 // Add to nearest neighbor tree
149 nn_->add(plannerData);
150
151 numUnsavedPaths_++;
152}
153
154bool ompl::tools::LightningDB::saveIfChanged(const std::string &fileName)
155{
156 if (numUnsavedPaths_)
157 return save(fileName);
158 else
159 OMPL_INFORM("Not saving because database has not changed");
160 return true;
161}
162
163bool ompl::tools::LightningDB::save(const std::string &fileName)
164{
165 // Error checking
166 if (fileName.empty())
167 {
168 OMPL_ERROR("Empty filename passed to save function");
169 return false;
170 }
171
172 // Save database from file, track saving time
173 time::point start = time::now();
174
175 OMPL_INFORM("Saving database to file: %s", fileName.c_str());
176
177 // Open a binary output stream
178 std::ofstream outStream(fileName.c_str(), std::ios::binary);
179
180 // Convert the NN tree to a vector
181 std::vector<ompl::base::PlannerDataPtr> plannerDatas;
182 nn_->list(plannerDatas);
183
184 // Write the number of paths we will be saving
185 double numPaths = plannerDatas.size();
186 outStream << numPaths;
187
188 // Start saving each planner data object
189 for (std::size_t i = 0; i < numPaths; ++i)
190 {
191 ompl::base::PlannerData &pd = *plannerDatas[i].get();
192
193 // Save a single planner data
194 plannerDataStorage_.store(pd, outStream);
195 }
196
197 // Close file
198 outStream.close();
199
200 // Benchmark
201 double loadTime = time::seconds(time::now() - start);
202 OMPL_INFORM("Saved database to file in %f sec with %d paths", loadTime, plannerDatas.size());
203
204 numUnsavedPaths_ = 0;
205
206 return true;
207}
208
209void ompl::tools::LightningDB::getAllPlannerDatas(std::vector<ompl::base::PlannerDataPtr> &plannerDatas) const
210{
211 OMPL_DEBUG("LightningDB: getAllPlannerDatas");
212
213 // Convert the NN tree to a vector
214 nn_->list(plannerDatas);
215
216 OMPL_DEBUG("Number of paths found: %d", plannerDatas.size());
217}
218
219std::vector<ompl::base::PlannerDataPtr> ompl::tools::LightningDB::findNearestStartGoal(int nearestK,
220 const base::State *start,
221 const base::State *goal)
222{
223 // Fill in our pre-made PlannerData instance with the new start and goal states to be searched for
224 if (nnSearchKey_->numVertices() == 2)
225 {
226 nnSearchKey_->getVertex(0) = ompl::base::PlannerDataVertex(start);
227 nnSearchKey_->getVertex(1) = ompl::base::PlannerDataVertex(goal);
228 }
229 else
230 {
231 nnSearchKey_->addVertex(ompl::base::PlannerDataVertex(start));
232 nnSearchKey_->addVertex(ompl::base::PlannerDataVertex(goal));
233 }
234 assert(nnSearchKey_->numVertices() == 2);
235
236 std::vector<ompl::base::PlannerDataPtr> nearest;
237 nn_->nearestK(nnSearchKey_, nearestK, nearest);
238
239 return nearest;
240}
241
242double ompl::tools::LightningDB::distanceFunction(const ompl::base::PlannerDataPtr &a,
243 const ompl::base::PlannerDataPtr &b) const
244{
245 // Bi-directional implementation - check path b from [start, goal] and [goal, start]
246 return std::min(
247 // [ a.start, b.start] + [a.goal + b.goal]
248 si_->distance(a->getVertex(0).getState(), b->getVertex(0).getState()) +
249 si_->distance(a->getVertex(a->numVertices() - 1).getState(), b->getVertex(b->numVertices() - 1).getState()),
250 // [ a.start, b.goal] + [a.goal + b.start]
251 si_->distance(a->getVertex(0).getState(), b->getVertex(b->numVertices() - 1).getState()) +
252 si_->distance(a->getVertex(a->numVertices() - 1).getState(), b->getVertex(0).getState()));
253}
254
256{
257 return nn_->size();
258}
259
261{
262 // Loop through every PlannerData and sum the number of states
263 std::size_t statesCount = 0;
264
265 // Convert the NN tree to a vector
266 std::vector<ompl::base::PlannerDataPtr> plannerDatas;
267 nn_->list(plannerDatas);
268
269 // Start saving each planner data object
270 for (auto &plannerData : plannerDatas)
271 {
272 statesCount += plannerData->numVertices();
273 }
274
275 return statesCount;
276}
Base class for a vertex in the PlannerData structure. All derived classes must implement the clone an...
Definition PlannerData.h:59
Object containing planner generated vertex and edge data. It is assumed that all vertices are unique,...
Definition of an abstract state.
Definition State.h:50
Definition of a geometric path.
std::vector< base::State * > & getStates()
Get the states that make up the path (as a reference, so it can be modified, hence the function is no...
std::size_t getStatesCount() const
Get the total number of states stored in the database, across all paths.
void getAllPlannerDatas(std::vector< ompl::base::PlannerDataPtr > &plannerDatas) const
Get a vector of all the paths in the nearest neighbor tree.
virtual ~LightningDB()
Deconstructor.
LightningDB(const base::StateSpacePtr &space)
Constructor needs the state space used for planning.
base::SpaceInformationPtr si_
The created space information.
ompl::base::PlannerDataStorage plannerDataStorage_
Helper class for storing each plannerData instance.
void addPath(geometric::PathGeometric &solutionPath, double &insertionTime)
Add a new solution path to our database. Des not actually save to file so experience will be lost if ...
bool save(const std::string &fileName)
Save loaded database to file.
bool saveIfChanged(const std::string &fileName)
Save loaded database to file, except skips saving if no paths have been added.
std::size_t getExperiencesCount() const
Get the total number of paths stored in the database.
bool load(const std::string &fileName)
Load database from file.
std::vector< ompl::base::PlannerDataPtr > findNearestStartGoal(int nearestK, const base::State *start, const base::State *goal)
Find the k nearest paths to our queries one.
#define OMPL_INFORM(fmt,...)
Log a formatted information string.
Definition Console.h:68
#define OMPL_ERROR(fmt,...)
Log a formatted error string.
Definition Console.h:64
#define OMPL_DEBUG(fmt,...)
Log a formatted debugging string.
Definition Console.h:70
#define OMPL_WARN(fmt,...)
Log a formatted warning string.
Definition Console.h:66
std::chrono::system_clock::time_point point
Representation of a point in time.
Definition Time.h:52
point now()
Get the current time point.
Definition Time.h:58
duration seconds(double sec)
Return the time duration representing a given number of seconds.
Definition Time.h:64