se2ez
cspace.cpp
Go to the documentation of this file.
1 
2 #include <se2ez/core/log.h>
3 #include <se2ez/core/math.h>
4 #include <se2ez/core/joint.h>
5 #include <se2ez/core/frame.h>
6 #include <se2ez/core/geometry.h>
7 #include <se2ez/core/state.h>
9 #include <se2ez/core/colormap.h>
10 
11 #include <se2ez/core/cspace.h>
12 
13 using namespace se2ez;
14 
15 ///
16 /// CSpaceGrid
17 ///
18 
20 
21 CSpaceGrid::CSpaceGrid(const RobotPtr &robot, unsigned int jointA, unsigned int jointB, unsigned int width,
22  unsigned int height, Mode mode)
23  : robot_(robot)
24  , indexA_(jointA)
25  , indexB_(jointB)
26  , limitA_({robot->getLowerLimits()[indexA_], robot->getUpperLimits()[indexA_]})
27  , limitB_({robot->getLowerLimits()[indexB_], robot->getUpperLimits()[indexB_]})
28  , contA_(robot->getContinuity()[indexA_])
29  , contB_(robot->getContinuity()[indexB_])
30  , width_(width)
31  , height_(height)
32  , mode_(mode)
33  , image_(std::make_shared<ompl::PPM>())
34  , state_(std::make_shared<State>(robot))
35  , temp_(std::make_shared<State>(robot))
36  , newState_(cores_, false)
37  , working_(cores_, false)
38  , threads_(cores_)
39 {
40  initialize();
41 }
42 
43 CSpaceGrid::CSpaceGrid(const RobotPtr &robot, const std::string &jointA, const std::string &jointB,
44  unsigned int width, unsigned int height, Mode mode)
45  : CSpaceGrid(robot, robot->getFrameDataConst(jointA)->index, robot->getFrameDataConst(jointB)->index, //
46  width, height, mode)
47 {
48 }
49 
51 {
52  // Setup PPM
53  image_->setWidth(width_);
54  image_->setHeight(height_);
55  image_->getPixels().resize(width_ * height_);
56 
57  for (unsigned int c = 0; c < cores_; ++c)
58  {
59  threads_[c] = new std::thread([&, c]() {
60  auto cm = std::make_shared<Box2DCollisionManager>(robot_);
61  auto state = std::make_shared<State>(robot_);
62  double &jA = state->data[indexA_];
63  double &jB = state->data[indexB_];
64 
65  while (true)
66  {
67  Chunk chunk;
68  GridCallback callback = {};
69 
70  // Pop a new chunk off
71  {
72  // Wait until there are chunks ready or it's time to exit.
74  work_.wait(slock, [&] { return !chunks_.empty() || done_; });
75 
76  // All done, time to exit.
77  if (done_)
78  break;
79 
80  // Copy state contents and mark done if needed.
81  {
83  if (newState_[c])
84  {
85  newState_[c] = false;
86  state->copy(state_);
87  }
88  }
89 
90  callback = callback_;
91 
92  // Copy chunk
93  chunk = chunks_.front();
94  chunks_.pop();
95 
96  // SE2EZ_DEBUG("Thread %5%: Popping image chunk %1%x%2% -> %3%x%4%", //
97  // chunk.x, chunk.y, chunk.x + chunk.w, chunk.y + chunk.h, c);
98  }
99 
100  working_[c] = true;
101 
102  // Iterate through pixels that need to be filled.
103  for (unsigned int i = chunk.x; i < chunk.x + chunk.w; ++i)
104  {
105  jA = math::remap(0, width_, i, limitA_[0], limitA_[1]);
106 
107  for (unsigned int j = chunk.y; j < chunk.y + chunk.h; ++j)
108  {
109  jB = math::remap(0, height_, j, limitB_[0], limitB_[1]);
110 
111  state->dirty = true;
112  auto &pixel = image_->getPixel(i, j);
113 
114  bool collision = false;
115  unsigned char rc = 0, gc = 0, bc = 0;
116  switch (mode_)
117  {
118  case DISTANCE:
119  case DISTANCE_COLOR:
120  {
121  double r, g, b;
122  auto sd = cm->distance(state);
123  double d = sd.distance;
124 
125  if (d < 0)
126  {
127  collision = true;
128  if (d < minDepth_)
129  d = minDepth_;
130 
131  if (mode_ == DISTANCE_COLOR)
132  {
133  grayscale(math::remap(0, minDepth_, d, 0.25, 1), r, g, b);
134  auto color = getFrameColor(sd.collider);
135 
136  r = color[0] * r;
137  g = color[1] * g;
138  b = color[2] * b;
139  }
140  else
141  grayscale(math::remap(0, minDepth_, d, 0, 1), r, g, b);
142  }
143  else
144  {
145  if (d > maxDepth_)
146  d = maxDepth_;
147  turbo(math::remap(0, maxDepth_, d, 0, 1), r, g, b);
148  }
149 
150  rc = std::round(255 * r);
151  gc = std::round(255 * g);
152  bc = std::round(255 * b);
153 
154  break;
155  }
156  case COLOR:
157  {
158  auto sd = cm->distance(state);
159  if (sd.distance >= 0)
160  rc = gc = bc = 255;
161  else
162  {
163  collision = true;
164  auto color = getFrameColor(sd.collider);
165 
166  rc = std::round(255 * color[0]);
167  gc = std::round(255 * color[1]);
168  bc = std::round(255 * color[2]);
169  }
170 
171  break;
172  }
173  case COLLISION:
174  collision = cm->collide(state);
175  rc = gc = bc = (collision) ? 0 : 255;
176  break;
177  }
178 
179  pixel = ompl::PPM::Color{rc, gc, bc};
180 
181  if (callback)
182  callback(pixel, state, collision);
183  }
184  }
185 
186  working_[c] = false;
187 
188  // Notify that we might be complete.
189  if (chunks_.empty())
191  }
192  });
193  }
194 }
195 
197 {
198  // Let threads know we are done.
199  done_ = true;
200  work_.notify_all();
201 
202  for (unsigned int c = 0; c < cores_; ++c)
203  {
204  threads_[c]->join();
205  delete threads_[c];
206  }
207 }
208 
210 {
211  cancel();
212 
214 
215  // Pump some chunks.
216  for (unsigned int x = 0; x < width_; x += xChunk_)
217  for (unsigned int y = 0; y < height_; y += yChunk_)
218  {
219  unsigned int remainX = std::min<unsigned int>({xChunk_, width_ - x});
220  unsigned int remainY = std::min<unsigned int>({yChunk_, height_ - y});
221  // SE2EZ_DEBUG("Emplacing image chunk %1%x%2% -> %3%x%4%", x, y, x + remainX, y + remainY);
222  chunks_.emplace(x, y, remainX, remainY);
223  }
224 
225  maxChunks_ = chunks_.size();
226 
227  slock.unlock();
228  work_.notify_all();
229 }
230 
231 void CSpaceGrid::compute(bool block, const StatePtr &base, const GridCallback &callback)
232 {
233  // Notify threads there is a new state to copy.
234  if (base)
235  {
237  state_->copy(base);
238 
239  for (unsigned int i = 0; i < cores_; ++i)
240  newState_[i] = true;
241  }
242 
243  callback_ = {};
244  if (callback)
245  callback_ = callback;
246 
247  // Reboot work queue.
248  emplaceChunks();
249 
250  // Possibly block until exit.
251  if (block)
252  waitToComplete();
253 }
254 
256 {
257  // SE2EZ_DEBUG("Waiting for CSpace Grid computation...");
258 
260 
261  // Wait until all threads are done working.
262  complete_.wait(slock, [&] { return not isComputing(); });
263 
264  // SE2EZ_DEBUG("Done waiting for CSpace Grid computation!");
265 }
266 
267 ompl::PPM::Color &CSpaceGrid::getPixel(const StatePtr &state)
268 {
269  return getPixel(state->data[indexA_], state->data[indexB_]);
270 }
271 
273 {
274  return getCoords(state->data[indexA_], state->data[indexB_]);
275 }
276 
277 ompl::PPM::Color &CSpaceGrid::getPixel(double i, double j)
278 {
279  const auto &coord = getCoords(i, j);
280  return image_->getPixel(coord.x, coord.y);
281 }
282 
284 {
285  double x = math::remap(limitA_[0], limitA_[1], i, 0, width_);
286  double y = math::remap(limitB_[0], limitB_[1], j, 0, height_);
287 
288  return {.x = (unsigned int)x, .y = (unsigned int)y};
289 }
290 
291 CSpaceGrid::RobotCoords CSpaceGrid::getCoords(unsigned int i, unsigned int j)
292 {
293  double x = math::remap(0, width_, i, limitA_[0], limitA_[1]);
294  double y = math::remap(0, height_, j, limitB_[0], limitB_[1]);
295 
296  return {.x = x, .y = y};
297 }
298 
300 {
301  std::vector<GridLine> lines;
302  lines.push_back({.a = getCoords(a), .b = getCoords(b)});
303 
304  auto scratch = std::make_shared<State>(robot_);
305  if (contA_)
306  {
307  std::vector<GridLine> newLines;
308  for (const auto &line : lines)
309  {
310  unsigned int ax = line.a.x, ay = line.a.y, bx = line.b.x, by = line.b.y;
311  if (ax > bx)
312  {
313  std::swap(ax, bx);
314  std::swap(ay, by);
315  }
316 
317  if ((bx - ax) <= width_ / 2)
318  {
319  newLines.emplace_back(line);
320  continue;
321  }
322 
323  double at = ax;
324  double bt = width_ - bx;
325  double tt = at + bt;
326 
327  unsigned int ma = ay + ((int)by - (int)ay) * at / tt;
328 
329  GridCoords an = {.x = 0, .y = ma};
330  GridCoords aa = {.x = ax, .y = ay};
331 
332  GridCoords bn = {.x = width_, .y = ma};
333  GridCoords bb = {.x = bx, .y = by};
334 
335  newLines.push_back({.a = an, .b = aa});
336  newLines.push_back({.a = bn, .b = bb});
337  }
338  lines = newLines;
339  }
340 
341  if (contB_)
342  {
343  std::vector<GridLine> newLines;
344  for (const auto &line : lines)
345  {
346  unsigned int ax = line.a.x, ay = line.a.y, bx = line.b.x, by = line.b.y;
347  if (ay > by)
348  {
349  std::swap(ax, bx);
350  std::swap(ay, by);
351  }
352 
353  if ((by - ay) <= height_ / 2)
354  {
355  newLines.emplace_back(line);
356  continue;
357  }
358 
359  double at = ay;
360  double bt = height_ - by;
361  double tt = at + bt;
362 
363  unsigned int ma = ax + ((int)bx - (int)ax) * at / tt;
364 
365  GridCoords an = {.x = ma, .y = 0};
366  GridCoords aa = {.x = ax, .y = ay};
367 
368  GridCoords bn = {.x = ma, .y = height_};
369  GridCoords bb = {.x = bx, .y = by};
370 
371  newLines.push_back({.a = an, .b = aa});
372  newLines.push_back({.a = bn, .b = bb});
373  }
374 
375  lines = newLines;
376  }
377 
378  return lines;
379 }
380 
382 {
383  return image_;
384 }
385 
387 {
388  bool working = false;
389  for (unsigned int i = 0; i < cores_; ++i)
390  working |= working_[i];
391  return working or not chunks_.empty();
392 }
393 
395 {
397 
398  // Clear current work queue.
399  while (!chunks_.empty())
400  chunks_.pop();
401 }
402 
404 {
405  temp_->copy(state);
406  temp_->data[indexA_] = 0;
407  temp_->data[indexB_] = 0;
408 
409  return temp_->data.norm();
410 }
411 
412 double CSpaceGrid::progress() const
413 {
414  if (not maxChunks_)
415  return 1.;
416 
417  return (double)(maxChunks_ - chunks_.size()) / maxChunks_;
418 }
419 
420 Eigen::Vector4d CSpaceGrid::getFrameColor(const std::string &frame) const
421 {
422  auto framep = robot_->getFrame(frame);
423  Eigen::Vector4d color{0, 0, 0, 0};
424  for (const auto &geometry : framep->getGeometry())
425  color += geometry->color;
426 
427  color /= (double)framep->getGeometry().size();
428  return color;
429 }
430 
431 unsigned int CSpaceGrid::getWidth() const
432 {
433  return width_;
434 }
435 
436 unsigned int CSpaceGrid::getHeight() const
437 {
438  return height_;
439 }
Container class for image chunk information to worker threads.
Definition: cspace.h:225
void initialize()
Initialize all internal constructs.
Definition: cspace.cpp:50
const unsigned int indexB_
Joint index for Y-axis.
Definition: cspace.h:212
T unlock(T... args)
A shared pointer wrapper for se2ez::State.
Coordinates for the robot.
Definition: cspace.h:55
const double maxDepth_
Maximum value for signed distance color map.
Definition: cspace.h:208
unsigned int getWidth() const
Return width of grid.
Definition: cspace.cpp:431
T swap(T... args)
StatePtr state_
State provided through compute().
Definition: cspace.h:248
bool isComputing() const
Checks if any computation is happening.
Definition: cspace.cpp:386
std::shared_ptr< ompl::PPM > image_
Image itself.
Definition: cspace.h:221
unsigned int x
x coordinate.
Definition: cspace.h:41
void cancel()
Cancels any remaining computation.
Definition: cspace.cpp:394
unsigned int maxChunks_
Maximum number of chunks in queue.
Definition: cspace.h:247
std::vector< bool > newState_
Has each thread updated to new base state for computation?
Definition: cspace.h:259
GridCoords getCoords(const StatePtr &state)
Get the pixel coordinate for a configuration.
Definition: cspace.cpp:272
Mode
Drawing mode for the grid.
Definition: cspace.h:63
T hardware_concurrency(T... args)
const unsigned int yChunk_
Size of Y chunk for threading.
Definition: cspace.h:206
const unsigned int xChunk_
Size of X chunk for threading.
Definition: cspace.h:205
std::mutex lock_
Mutex.
Definition: cspace.h:253
void compute(bool block=true, const StatePtr &base=nullptr, const GridCallback &callback={})
Compute a PPM image of the configuration space of two joints, jointA and jointB.
Definition: cspace.cpp:231
T push_back(T... args)
const unsigned int width_
Width of image.
Definition: cspace.h:218
double progress() const
Returns a value in [0, 1] of the progress of the current computation.
Definition: cspace.cpp:412
double distanceToSlice(const StatePtr &state)
Returns the distance along the dimensions of the robot not captured by the current C-Space slice...
Definition: cspace.cpp:403
std::vector< bool > working_
Which threads are currently working?
Definition: cspace.h:260
StatePtr temp_
Temporary state.
Definition: cspace.h:249
std::condition_variable work_
Condition variable that there is work to do.
Definition: cspace.h:255
std::mutex statelock_
Mutex for state.
Definition: cspace.h:254
void waitToComplete()
Wait for all grid computation to complete.
Definition: cspace.cpp:255
void grayscale(double s, double &r, double &g, double &b)
Maps a scalar s in [0, 1] to greyscale.
Definition: colormap.cpp:334
void turbo(double s, double &r, double &g, double &b)
Maps a scalar s in [0, 1] to the Turbo colormap.
Definition: colormap.cpp:329
Eigen::Vector4d getFrameColor(const std::string &frame) const
Gets the average color of a frame.
Definition: cspace.cpp:420
const double minDepth_
Minimum value for signed distance color map.
Definition: cspace.h:207
CSpaceGrid(const RobotPtr &robot, unsigned int jointA, unsigned int jointB, unsigned int width=500, unsigned int height=500, Mode mode=COLLISION)
Constructor.
Definition: cspace.cpp:21
const RobotPtr robot_
Robot to use.
Definition: cspace.h:204
std::condition_variable complete_
Condition variable to notify when complete.
Definition: cspace.h:256
static const unsigned int cores_
Number of cores on the machine.
Definition: cspace.h:210
const unsigned int height_
Height of image.
Definition: cspace.h:219
std::queue< Chunk > chunks_
Queue of chunks.
Definition: cspace.h:257
unsigned int y
Starting y coordinate of chunk.
Definition: cspace.h:242
Use viridis color palette for signed distance.
Definition: cspace.h:67
Draw collisions as black pixels.
Definition: cspace.h:65
A shared pointer wrapper for se2ez::Robot.
ompl::PPM::Color & getPixel(const StatePtr &state)
Get the pixel coordinate for a configuration.
Definition: cspace.cpp:267
const Eigen::Vector2d limitB_
Joint limits for second joint (1-DoF)
Definition: cspace.h:214
const Mode mode_
Render mode.
Definition: cspace.h:220
unsigned int w
Width of chunk.
Definition: cspace.h:243
Signed distance, but penetrations are colored by geometry.
Definition: cspace.h:68
Color obstacles by geometry color.
Definition: cspace.h:66
const bool contB_
Is joint B continuous?
Definition: cspace.h:216
Main namespace.
Definition: collision.h:11
Grid coordinates.
Definition: cspace.h:39
~CSpaceGrid()
Destructor.
Definition: cspace.cpp:196
std::vector< GridLine > getLines(const StatePtr &a, const StatePtr &b)
Get the line segments between two points on the grid. Can have two due to wrapping.
Definition: cspace.cpp:299
double remap(double a1, double a2, double av, double b1, double b2)
Remap a value av in the interval a1, a2 to the interval b1, b2.
Definition: math.cpp:160
std::shared_ptr< ompl::PPM > getImage() const
Get the current computed PPM image.
Definition: cspace.cpp:381
GridCallback callback_
Callback function to use for extra drawing.
Definition: cspace.h:250
const unsigned int indexA_
Joint index for X-axis.
Definition: cspace.h:211
std::vector< std::thread * > threads_
Threads.
Definition: cspace.h:261
bool done_
Notify threads object is to be destroyed.
Definition: cspace.h:252
unsigned int x
Starting x coordinate of chunk.
Definition: cspace.h:241
unsigned int getHeight() const
Return height of grid.
Definition: cspace.cpp:436
unsigned int h
Height of chunk.
Definition: cspace.h:244
const Eigen::Vector2d limitA_
Joint limits for first joint (1-DoF)
Definition: cspace.h:213
T round(T... args)
void emplaceChunks()
Emplaces all images chunks into work queue.
Definition: cspace.cpp:209
const bool contA_
Is joint A continuous?
Definition: cspace.h:215
Helper class to compute and draw C-Space images from arbitrary robots.
Definition: cspace.h:34
T emplace_back(T... args)