Component.cpp
Go to the documentation of this file.
1/**
2 * This file is part of ArmarX.
3 *
4 * ArmarX is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 *
8 * ArmarX is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * @package armarx::navigation::components::costmap_merger
17 * @author Tobias Gröger ( tobias dot groeger at student dot kit dot edu )
18 * @date 2025
19 * @copyright http://www.gnu.org/licenses/gpl-2.0.txt
20 * GNU General Public License
21 */
22
23
24#include "Component.h"
25
26#include <algorithm>
27#include <cstddef>
28#include <limits>
29#include <mutex>
30#include <string>
31#include <utility>
32#include <vector>
33
34#include <SimoxUtility/algorithm/string/string_tools.h>
35
44
46
51
52#include <range/v3/range/conversion.hpp>
53#include <range/v3/view/filter.hpp>
54
56{
58
60 {
61 addPlugin(costmapReaderPlugin);
62 addPlugin(costmapWriterPlugin);
63 }
64
65 const std::string Component::defaultName = "costmap_merger";
66
69 {
72
73 def->required(
74 properties.costmapsToMerge,
75 "p.costmapsToMerge",
76 "The costmaps to merge, they are prioritized in the order they are specified, i.e. the "
77 "first costmap will take priority over the second one etc.");
78
79 def->optional(
80 properties.outputCostmapName, "p.outputCostmapName", "The name of the merged costmap.");
81
82 return def;
83 }
84
85 void
87 {
88 usingProxy("navigation_memory");
89
90 // This should not be necessary but seems to be. ToDo: Look into this.
92
96 this,
98 }
99
100 void
102 {
103 ARMARX_VERBOSE << "Creating initial merge of costmaps";
105 }
106
107 void
109 const std::vector<armem::MemoryID>& snapshotIDs)
110 {
111 ARMARX_VERBOSE << "A costmap instance has changed!";
112
113 const std::vector<armem::MemoryID> relevantCostmaps =
114 snapshotIDs |
115 ranges::views::filter(
116 [&](const armem::MemoryID& id)
117 {
118 std::string name = id.providerSegmentName + "/" + id.entityName;
119 return std::find(properties.costmapsToMerge.cbegin(),
120 properties.costmapsToMerge.cend(),
121 name) != properties.costmapsToMerge.cend();
122 }) |
123 ranges::to_vector;
124
125 if (relevantCostmaps.empty())
126 {
127 return;
128 }
129
130 ARMARX_INFO << "Relevant costmaps changed: " << relevantCostmaps;
131
132 // Re-merge the costmaps and store them
134 }
135
136 void
138 {
139 std::lock_guard g{mergeCostmapMtx};
140
141 ARMARX_VERBOSE << "Reading costmaps";
142
144
145 std::vector<Costmap> costmaps;
146
147 for (const auto& costmap : properties.costmapsToMerge)
148 {
149 ARMARX_VERBOSE << "Reading costmap " << QUOTED(costmap) << ".";
150 const std::vector<std::string> splits = simox::alg::split(costmap, "/");
151 ARMARX_CHECK_EQUAL(splits.size(), 2) << "Invalid costmap name: " << costmap;
152
154 .providerName = splits.front(), .name = splits.back(), .timestamp = timestamp};
155 auto result = costmapReaderPlugin->get().query(query);
156
157 if (not result)
158 {
159 ARMARX_WARNING << "Attempting to merge costmap `" << costmap
160 << "` which doesn't exist (yet) (" << result.errorMessage << ")";
161 }
162 else
163 {
164 ARMARX_CHECK_NOT_NULL(result.costmap);
165 costmaps.emplace_back(std::move(*result.costmap));
166 }
167 }
168
169 if (costmaps.empty())
170 {
171 ARMARX_WARNING << "Nothing to merge";
172 return;
173 }
174
176 ARMARX_VERBOSE << "Merging costmaps";
177
178 auto mergedCostmap = [&]()
179 {
180 // calculate the union of the two costmaps
181 // and the cell size of the most detailed costmap
182 Eigen::AlignedBox2f aabb;
183 float minCellSize = std::numeric_limits<float>::max();
184 for (const auto& c : costmaps)
185 {
186 // origin transformation might mix up where min/max lies, therefore not min/max anymore
187 Eigen::Vector2f globalCornerA = c.origin() * c.getLocalSceneBounds().min;
188 Eigen::Vector2f globalCornerB = c.origin() * c.getLocalSceneBounds().max;
189
190 aabb.extend(globalCornerA);
191 aabb.extend(globalCornerB);
192
193 minCellSize = std::min(minCellSize, c.params().cellSize);
194 }
196 ARMARX_VERBOSE << "Scene bounds intersections is " << aabb.min() << " and "
197 << aabb.max() << " with minCellSize as " << minCellSize;
198
199
200 // create costmap with given cell size and scene bounds
201
202 //+1 for explicit rounding up
203 Eigen::Vector2f boundingSizes = aabb.sizes();
204 const auto cX = static_cast<std::size_t>((boundingSizes.x() / minCellSize) + 1);
205 const auto cY = static_cast<std::size_t>((boundingSizes.y() / minCellSize) + 1);
206
207 Eigen::MatrixXf grid(cX, cY);
208 grid.setZero();
209
210 Costmap costmap(grid,
211 Costmap::Parameters{.cellSize = minCellSize, .sceneBoundsMargin = 0.F},
212 algorithms::SceneBounds{.min = aabb.min(), .max = aabb.max()});
213
214 // to copy shape from grid to mask
215 costmap.getMutableMask() = costmap.getGrid().array() == 0;
216 costmap.getMutableMask()->setOnes();
217
218 return costmap;
219 }();
220
221 ARMARX_CHECK(mergedCostmap.getMask().has_value());
222
223 // check entry of first valid costmap in order and return whether any costmap was valid
224 const auto& checkInOrder = [&](int x, int y, const Eigen::Vector2f& position)
225 {
226 for (const auto& other : costmaps)
227 {
228 Eigen::AlignedBox2f bounds(other.getLocalSceneBounds().min,
229 other.getLocalSceneBounds().max);
230 const auto otherVertex = other.toVertex(position);
231 // only use this costmap if the current position lies inside it's bounds
232 if (bounds.contains(position) && other.isValid(otherVertex.index))
233 {
234 mergedCostmap.getMutableGrid()(x, y) = *other.value(otherVertex.index);
235 return true;
236 }
237 }
238 return false;
239 };
240
241 for (int x = 0; x < mergedCostmap.getGrid().rows(); x++)
242 {
243 for (int y = 0; y < mergedCostmap.getGrid().cols(); y++)
244 {
246 const Costmap::Position position = mergedCostmap.toPositionGlobal({x, y});
247 mergedCostmap.getMutableMask().value()(x, y) = checkInOrder(x, y, position);
248 }
249 }
250
251 costmapWriterPlugin->get().store(
252 mergedCostmap, properties.outputCostmapName, getName(), timestamp);
253 }
254
255 void
259
260 void
264
265 std::string
267 {
268 return Component::defaultName;
269 }
270
271 std::string
273 {
274 return Component::defaultName;
275 }
276
278
279} // namespace armarx::navigation::components::costmap_merger
std::string timestamp()
#define ARMARX_REGISTER_COMPONENT_EXECUTABLE(ComponentT, applicationName)
Definition Decoupled.h:29
#define QUOTED(x)
constexpr T c
static DateTime Now()
Current time on the virtual clock.
Definition Clock.cpp:93
Default component property definition container.
Definition Component.h:70
std::string getConfigIdentifier()
Retrieve config identifier for this component as set in constructor.
Definition Component.cpp:90
bool usingProxy(const std::string &name, const std::string &endpoints="")
Registers a proxy for retrieval after initialization and adds it to the dependency list.
PluginT * addPlugin(const std::string prefix="", ParamsT &&... params)
std::string getName() const
Retrieve name of object.
void setComponent(ManagedIceObject *component)
SubscriptionHandle subscribe(const MemoryID &subscriptionID, Callback Callback)
Represents a point in time.
Definition DateTime.h:25
std::optional< Costmap::Mask > & getMutableMask() noexcept
Definition Costmap.cpp:535
armarx::PropertyDefinitionsPtr createPropertyDefinitions() override
Definition Component.cpp:68
void processObjectInstance(const armem::MemoryID &id, const std::vector< armem::MemoryID > &snapshotIDs)
static std::string GetDefaultName()
Get the component's default name.
#define ARMARX_CHECK(expression)
Shortcut for ARMARX_CHECK_EXPRESSION.
#define ARMARX_CHECK_NOT_NULL(ptr)
This macro evaluates whether ptr is not null and if it turns out to be false it will throw an Express...
#define ARMARX_CHECK_EQUAL(lhs, rhs)
This macro evaluates whether lhs is equal (==) rhs and if it turns out to be false it will throw an E...
#define ARMARX_INFO
The normal logging level.
Definition Logging.h:181
#define ARMARX_WARNING
The logging level for unexpected behaviour, but not a serious problem.
Definition Logging.h:193
#define ARMARX_VERBOSE
The logging level for verbose information.
Definition Logging.h:187
constexpr const char * CostmapCoreSegmentName
Definition constants.h:36
constexpr const char * NavigationMemoryName
Definition constants.h:29
This file offers overloads of toIce() and fromIce() functions for STL container types.
IceUtil::Handle< class PropertyDefinitionContainer > PropertyDefinitionsPtr
PropertyDefinitions smart pointer type.
#define ARMARX_TRACE
Definition trace.h:77