-
Notifications
You must be signed in to change notification settings - Fork 75
[reopen #274] Send Octomap diff instead of whole tree #302
Changes from all commits
cfa9218
cb5725b
854e136
8f46a4f
f3eb655
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -49,6 +49,8 @@ | |
namespace planning_scene | ||
{ | ||
const std::string PlanningScene::OCTOMAP_NS = "<octomap>"; | ||
const std::string PlanningScene::OCTOMAP_MSG_TYPE = "OcTree"; | ||
const std::string PlanningScene::OCTOMAP_DIFF_MSG_TYPE = "diff(OcTree)"; | ||
const std::string PlanningScene::DEFAULT_SCENE_NAME = "(noname)"; | ||
|
||
class SceneTransforms : public robot_state::Transforms | ||
|
@@ -853,14 +855,76 @@ void planning_scene::PlanningScene::getPlanningSceneMsgOctomap(moveit_msgs::Plan | |
if (map->shapes_.size() == 1) | ||
{ | ||
const shapes::OcTree *o = static_cast<const shapes::OcTree*>(map->shapes_[0].get()); | ||
octomap_msgs::fullMapToMsg(*o->octree, scene_msg.world.octomap.octomap); | ||
boost::shared_ptr<const octomap::OcTree> octree = o->octree; | ||
if (scene_msg.is_diff == true && octree->isChangeDetectionEnabled()) | ||
{ | ||
int num_changes = octree->numChangesDetected(); | ||
int expected_size_diff = sizeof(int)+num_changes*((3*sizeof(unsigned short int))+sizeof(float)); | ||
int expected_size_tree = octree->size()*(sizeof(float)+sizeof(char)); | ||
if (expected_size_diff > expected_size_tree) | ||
{ | ||
logInform("Cheaper to send tree instead of diff by %i bytes with %i changes", expected_size_diff-expected_size_tree, num_changes); | ||
octomap_msgs::fullMapToMsg(*octree, scene_msg.world.octomap.octomap); | ||
if(scene_msg.world.octomap.octomap.id != OCTOMAP_MSG_TYPE) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. curly brackets on new line |
||
logWarn("fullMapToMsg produced unexpected octomap type: %s", | ||
scene_msg.world.octomap.octomap.id.c_str()); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. see the comment below |
||
} | ||
else | ||
{ | ||
getPlanningSceneMsgOctomapDiff(octree, scene_msg.world.octomap.octomap); | ||
} | ||
} | ||
else | ||
{ | ||
octomap_msgs::fullMapToMsg(*octree, scene_msg.world.octomap.octomap); | ||
if(scene_msg.world.octomap.octomap.id != OCTOMAP_MSG_TYPE) { | ||
logWarn("fullMapToMsg produced unexpected octomap type: %s", | ||
scene_msg.world.octomap.octomap.id.c_str()); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This looks like something that was needed during development, but bloats the final result. |
||
} | ||
tf::poseEigenToMsg(map->shape_poses_[0], scene_msg.world.octomap.origin); | ||
} | ||
else | ||
logError("Unexpected number of shapes in octomap collision object. Not including '%s' object", OCTOMAP_NS.c_str()); | ||
} | ||
} | ||
|
||
bool planning_scene::PlanningScene::getPlanningSceneMsgOctomapDiff(boost::shared_ptr<const octomap::OcTree> octree, octomap_msgs::Octomap &msg) const | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. line too long There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we have a standard concerning maximum line length?
Personally I prefer to have even long function signatures in a single line, to support `grep` users.
But I see the disadvantages of that too..
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The ROS standard is 120 per style guidelines |
||
{ | ||
msg.id = OCTOMAP_DIFF_MSG_TYPE; | ||
msg.resolution = octree->getResolution(); | ||
msg.binary = false; | ||
std::stringstream datastream; | ||
|
||
int num_changes = octree->numChangesDetected(); | ||
datastream.write((const char*) &num_changes, sizeof(int)); | ||
//logInform("Octomap diff has %i changes", num_changes); | ||
|
||
// this is safe as long as we only use the const_iterators for changedKeys | ||
octomap::OcTree* octreeNonConst = const_cast<octomap::OcTree*>(octree.get()); | ||
for (octomap::KeyBoolMap::const_iterator it = octreeNonConst->changedKeysBegin(); | ||
it != octreeNonConst->changedKeysEnd() && !(!datastream); ++it) | ||
{ | ||
octomap::OcTreeKey key = it->first; | ||
for (int j=0; j<3; j++) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. spaces between symbols |
||
{ | ||
datastream.write((const char*) &key[j], sizeof(unsigned short int)); | ||
} | ||
float value = octree->search(key)->getLogOdds(); | ||
datastream.write((const char*) &value, sizeof(float)); | ||
} | ||
|
||
if (!datastream) | ||
{ | ||
logError("Error while writing Octomap diff message"); | ||
return false; | ||
} | ||
std::string datastring = datastream.str(); | ||
msg.data = std::vector<int8_t>(datastring.begin(), datastring.end()); | ||
return true; | ||
} | ||
|
||
void planning_scene::PlanningScene::getPlanningSceneMsg(moveit_msgs::PlanningScene &scene_msg) const | ||
{ | ||
scene_msg.name = name_; | ||
|
@@ -1245,60 +1309,101 @@ bool planning_scene::PlanningScene::usePlanningSceneMsg(const moveit_msgs::Plann | |
return setPlanningSceneMsg(scene_msg); | ||
} | ||
|
||
void planning_scene::PlanningScene::processOctomapMsg(const octomap_msgs::Octomap &map) | ||
void planning_scene::PlanningScene::removeAllCollisionObjects() | ||
{ | ||
// each octomap replaces any previous one | ||
world_->removeObject(OCTOMAP_NS); | ||
const std::vector<std::string> &object_ids = world_->getObjectIds(); | ||
for (std::size_t i = 0; i < object_ids.size(); ++i) | ||
if (object_ids[i] != OCTOMAP_NS) | ||
world_->removeObject(object_ids[i]); | ||
} | ||
|
||
if (map.data.empty()) | ||
return; | ||
void planning_scene::PlanningScene::processOctomapMsg(const octomap_msgs::OctomapWithPose &msg) | ||
{ | ||
const Eigen::Affine3d &t = getTransforms().getTransform(msg.header.frame_id); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This does not check if |
||
Eigen::Affine3d p; | ||
tf::poseMsgToEigen(msg.origin, p); | ||
p = t * p; | ||
processOctomapMsg(msg.octomap, p); | ||
} | ||
|
||
if (map.id != "OcTree") | ||
void planning_scene::PlanningScene::processOctomapMsg(const octomap_msgs::Octomap &msg) | ||
{ | ||
if (!msg.header.frame_id.empty()) | ||
{ | ||
logError("Received octomap is of type '%s' but type 'OcTree' is expected.", map.id.c_str()); | ||
return; | ||
const Eigen::Affine3d &t = getTransforms().getTransform(msg.header.frame_id); | ||
processOctomapMsg(msg, t); | ||
} | ||
else | ||
{ | ||
processOctomapMsg(msg, Eigen::Affine3d::Identity()); | ||
} | ||
} | ||
|
||
boost::shared_ptr<octomap::OcTree> om(static_cast<octomap::OcTree*>(octomap_msgs::msgToMap(map))); | ||
if (!map.header.frame_id.empty()) | ||
void planning_scene::PlanningScene::processOctomapMsg(const octomap_msgs::Octomap &msg, const Eigen::Affine3d &t) | ||
{ | ||
if (msg.id.empty()) | ||
{ | ||
const Eigen::Affine3d &t = getTransforms().getTransform(map.header.frame_id); | ||
world_->removeObject(OCTOMAP_NS); | ||
} | ||
else if (msg.id == OCTOMAP_DIFF_MSG_TYPE) | ||
{ | ||
collision_detection::CollisionWorld::ObjectConstPtr map = world_->getObject(OCTOMAP_NS); | ||
if (map && map->shapes_.size() == 1) | ||
{ | ||
if (msg.data.empty()) | ||
return; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please move this check to the beginning of the surrounding block, so that the first thing that happens for empty diffs is check-and-return. There's no reason not to do that iif |
||
const shapes::OcTree *o = static_cast<const shapes::OcTree*>(map->shapes_[0].get()); | ||
boost::shared_ptr<const octomap::OcTree> octree = o->octree; | ||
boost::shared_ptr<octomap::OcTree> nc_octree = boost::const_pointer_cast<octomap::OcTree>(octree); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👎 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should be safe, as it is happening in the same function that - without the diff function - would just replace the complete OctoMap. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I don't see why this makes it safe to unconst a pointer out of a list of const elements from a class instance that has been extracted from a static_cast'ed const pointer from a map of objects that is from outside the world only const-visible. Also I believe this does not involve much management to have a separate shared_ptr for the latest octree. If you want to have this in indigo, then I would propose to have the copy in indigo and jade and add the pointer-version in kinetic. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see what you mean. The clean way would be to remove the object from the scene, do the operations and re-insert the changed one. Currently the order is perform operations (with const-cast), remove, add. However, I don't see a way in the moveit API to extract something from a shape, while retaining it exclusively. Having a second OcTree sounds hard as the actual OcTree lives in the CollisionWorld. There might be changes from different components in there (e.g. world updates and filtering by other objects) that would need to be managed. The only somewhat simple and safe way that I see right now is to clone the OcTree and apply changes to that. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. On Mon, Aug 01, 2016 at 07:01:07AM -0700, Christian Dornhege wrote:
Sadly, this is not possible without const-cast with the current API...
Crap, I didn't think about that, you're right.
+1 please do that for now. If you could also address the other changes I'll +1 this for indigo. If you want to have an optimized version in MoveIt! (that would be really nice), |
||
processOctomapMsgDiff(msg, nc_octree); | ||
processOctomapPtr(octree, t); | ||
} | ||
} | ||
else if (msg.id == OCTOMAP_MSG_TYPE) | ||
{ | ||
world_->removeObject(OCTOMAP_NS); // Octomap replaces any previous one | ||
if (msg.data.empty()) | ||
return; | ||
boost::shared_ptr<octomap::OcTree> om(static_cast<octomap::OcTree*>(octomap_msgs::msgToMap(msg))); | ||
world_->addToObject(OCTOMAP_NS, shapes::ShapeConstPtr(new shapes::OcTree(om)), t); | ||
} | ||
else | ||
{ | ||
world_->addToObject(OCTOMAP_NS, shapes::ShapeConstPtr(new shapes::OcTree(om)), Eigen::Affine3d::Identity()); | ||
logError("Received Octomap is of unknown type '%s'", msg.id.c_str()); | ||
} | ||
} | ||
|
||
void planning_scene::PlanningScene::removeAllCollisionObjects() | ||
void planning_scene::PlanningScene::processOctomapMsgDiff(const octomap_msgs::Octomap &msg, boost::shared_ptr<octomap::OcTree> octree) | ||
{ | ||
const std::vector<std::string> &object_ids = world_->getObjectIds(); | ||
for (std::size_t i = 0; i < object_ids.size(); ++i) | ||
if (object_ids[i] != OCTOMAP_NS) | ||
world_->removeObject(object_ids[i]); | ||
} | ||
|
||
void planning_scene::PlanningScene::processOctomapMsg(const octomap_msgs::OctomapWithPose &map) | ||
{ | ||
// each octomap replaces any previous one | ||
world_->removeObject(OCTOMAP_NS); | ||
std::stringstream datastream; | ||
datastream.write((const char*) &msg.data[0], msg.data.size()); | ||
|
||
if (map.octomap.data.empty()) | ||
return; | ||
|
||
if (map.octomap.id != "OcTree") | ||
int num_changes; | ||
datastream.read((char*) &num_changes, sizeof(int)); | ||
//logInform("Octomap diff has %i changes", num_changes); | ||
int expected_size = sizeof(int)+num_changes*((3*sizeof(unsigned short int))+sizeof(float)); | ||
if (expected_size > msg.data.size()) | ||
{ | ||
logError("Received octomap is of type '%s' but type 'OcTree' is expected.", map.octomap.id.c_str()); | ||
logError("Did not receive enough data for specified diff size: %i bytes expected, %i received", expected_size, msg.data.size()); | ||
return; | ||
} | ||
if(expected_size < msg.data.size()) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. bracket break |
||
logWarn("Got more data than expected (%zu > %d)", msg.data.size(), expected_size); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please simplify this into There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @v4hn consider tweaking your rosconsole output to hide logging you are not interested in. i'm a big fan of good warnings like this so that when something goes wrong, we understand. there are too many instances and reports from users of moveit saying "failed to plan" with no other feedback. with a complex system like this we need as good tools to debug issues. see my rosconsole article for tips There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for the link! It's in my TODO queue. I fully agree that MoveIt needs informative warnings and debug messages. On the other hand one always has to look at the amount of code bloat added because of log output In this particular case I believe it is perfectly fine to have one warning There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Actually that seems reasonable. If the data size is not what is expected (either less or more) something is probably wrong. I would not go for anything that aborts without the option to only enable this globally. This could destroy a working system because of "something". There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Yes its hard to read, but seeing that many sanity checks has not been my experience with the code base |
||
|
||
boost::shared_ptr<octomap::OcTree> om(static_cast<octomap::OcTree*>(octomap_msgs::msgToMap(map.octomap))); | ||
const Eigen::Affine3d &t = getTransforms().getTransform(map.header.frame_id); | ||
Eigen::Affine3d p; | ||
tf::poseMsgToEigen(map.origin, p); | ||
p = t * p; | ||
world_->addToObject(OCTOMAP_NS, shapes::ShapeConstPtr(new shapes::OcTree(om)), p); | ||
for (int i=0; i<num_changes && !(!datastream); ++i) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. spaces |
||
{ | ||
octomap::OcTreeKey key; | ||
for (int j=0; j<3; j++) | ||
{ | ||
datastream.read((char*) &key[j], sizeof(unsigned short int)); | ||
} | ||
float value; | ||
datastream.read((char*) &value, sizeof(float)); | ||
octree->setNodeValue(key, value); | ||
} | ||
if (!datastream) | ||
logError("Error while reading Octomap diff message"); | ||
} | ||
|
||
void planning_scene::PlanningScene::processOctomapPtr(const boost::shared_ptr<const octomap::OcTree> &octree, const Eigen::Affine3d &t) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should this really be an inform? I would prefer debug.