tue-robotics / wire

BSD 2-Clause "Simplified" License
21 stars 15 forks source link

How to delete (very) old objects? #8

Closed autonomobil closed 5 years ago

autonomobil commented 5 years ago

Hey,

is there a possibility to delete objects if they didn't get an update for a certain amount of time?

If not, where could I start to implement this?

autonomobil commented 5 years ago

I found the function removeObject in ObjectStorage.cpp, but it takes a SemanticObject as input. I only have the wire_msgs::ObjectState, is there a way to get from the object ID to the SemanticObject and then delete it?

jelfring commented 5 years ago

Hi, this is not supported by default (WIRE currently adopts the assumption that objects do not disappear, instead there position estimate gets very uncertain, e.g., a uniform distribution, to indicate that the object was once present but is now lost).

If for your application it is preferred to remove objects based on some criterion you could add an addition 'remove old objects'-step. For example somewhere in the function HypothesisTree::addEvidence, but can be on different locations as well.

This is how a solution could look like: the object storage contains all objects (member variable std::list<SemanticObject*> objects_). You can loop over the all semantic objects and remove the ones that are, e.g. not relevant, too old or too far away, using the removeObject function you mentioned. Once you do so, you should also make sure you remove the objects from the hypotheses containing this object, e.g. by looping over all hypotheses and then checking if the semantic object is in the hypothesis' object list (std::list<SemanticObject*> objects_). Keep in mind that one object can be present in multiple hypotheses, hence you will always need to do both steps to avoid a hypothesis tree that contains objects that are removed.

autonomobil commented 5 years ago

Thanks for the answer! I will try to come up with a solution..

Does every hypothesis have its own list of semantic objects or is there a global list of objects (object storage)? If I understand you correctly, it's both, am I right?

So the function I would implement would be similar to the functions void Hypothesis::clear() and void Hypothesis::clearInactive().

Should I remove the old objects before expandTree, pruneTree and applyAssignments or after?

jelfring commented 5 years ago

Does every hypothesis have its own list of semantic objects or is there a global list of objects (object storage)? If I understand you correctly, it's both, am I right?

Every hypothesis has its own list of pointers to semantic objects, the objects are stored in the object storage (hence different hypotheses may contain a pointer to the same object). As a result the function you could implement will look something like this:

  1. Get all objects, e.g. by calling const std::list<SemanticObject*>& getAllObjects() in the HypothesisTree object.
  2. Determine for each object whether or not you'd like to remove it
  3. Remove the objects that must be removed from the object storage using removeObject(SemanticObject& obj) in ObjectStorage.
  4. Loop over all hypotheses and remove within each hypothesis those objects that are removed in step 3 (you could for example use the unique IDs)

So the function I would implement would be similar to the functions void Hypothesis::clear() and void Hypothesis::clearInactive().

This function shows you how you can loop over hypotheses and can therefore indeed serve as an example.

Should I remove the old objects before expandTree, pruneTree and applyAssignments or after?

autonomobil commented 5 years ago

Thanks for all the help! The function const std::list<SemanticObject*>& getAllObjects() is only implemented in the HypothesesTree.h header file but not in HypothesesTree.cpp, is this correct?

I implemented something different: root_->removeAllOldObjects(); gets called before //** Propagate all objects, compute association probabilities ... in HypothesesTree.cpp(as you suggested).

Then in Hypothesis.cpp:

void Hypothesis::removeOldObjects() {

    for (list<SemanticObject*>::iterator it_obj = objects_.begin(); it_obj != objects_.end(); ++it_obj) {
        SemanticObject* obj = *it_obj;
        double time_diff = ros::Time::now().toSec() - obj->getLastUpdateTime();

        if(time_diff > 10){
            obj->removeFromHypothesis(this);
            if (obj->getNumParentHypotheses() == 0) {
                ObjectStorage::getInstance().removeObject(*obj);
                delete obj;
            }
        }
    }
}

void Hypothesis::removeAllOldObjects() {
    removeOldObjects();
    for (list<Hypothesis*>::const_iterator it = children_.begin(); it != children_.end(); ++it) {
        (*it)->removeAllOldObjects();
    }
}

But as soon as an object is "old enough" and it gets removed from the hypotheses and the object storage, the wire_server dies... any further suggestions?

jelfring commented 5 years ago

But as soon as an object is "old enough" and it gets removed from the hypotheses and the object storage, the wire_server dies... any further suggestions?

My first guess would be this one: image some object with ID 1 is too old and this object is present in two different hypotheses (A and B). When you call removeAllOldObjects() on hypothesis A, the object with ID 1 is removed from the object storage. Once you arrive at hypothesis B you try to call obj->getLastUpdateTime() on object with ID 1, however, this one already has been deleted.

As an alternative you can first check all objects and memorize (not remove) the ones that should be removed (e.g. in a set, at least avoid duplicate values). Only after checking all objects, you can then remove them by iterating the set and calling ObjectStorage::getInstance().removeObject(*obj).

Does this help?

autonomobil commented 5 years ago

I managed to get it working! I think the problem was that I didn't set the iterator properly after erasing an object. My code inside Hypothesis.cpp:

// remove old objects
void Hypothesis::removeOldObjects() {

    for (list<SemanticObject*>::iterator it_obj = objects_.begin(); it_obj != objects_.end(); ++it_obj) {

        SemanticObject* obj = *it_obj;
        double time_diff = ros::Time::now().toSec() - obj->getLastUpdateTime();

        if(time_diff > 5){
            obj->removeFromHypothesis(this);

            if (obj->getNumParentHypotheses() == 0) {

                ObjectStorage::getInstance().removeObject(*obj);
                delete obj;
            }
            objects_.erase(it_obj);
            it_obj--;
        }    
    }
}

void Hypothesis::removeAllOldObjects() {

    removeOldObjects();

    for (list<Hypothesis*>::const_iterator it = children_.begin(); it != children_.end(); ++it) {
        (*it)->removeAllOldObjects();
    }

}

and then in HypothesesTree.cpp:

...
root_->removeAllOldObjects();
//** Propagate all objects, compute association probabilities and add all possible measurement-track assignments
...
autonomobil commented 5 years ago

So now I'm working on a way to "reuse" old objectIDs, so if I let the tracking run for an hour, I don't get huge numbers, but instead use IDs/numbers of already deleted objects. Really huge numbers would be a problem because I use the IDs for further processing but there I only have the datatype uint16 available (max:65535).

I thought about modifying the long ObjectStorage::getUniqueID() function to look for "old" and currently not used IDs and then giving this number as output for new objects. Do you see any problem with this approach? Do the objects have to be some kind of sorted?

jelfring commented 5 years ago

From a theoretical perspective the risk with reusing object IDs is the following: say object with ID 12 gets deleted. If then a new object appears it could get ID 12, since it is available. As a result you or your application might incorrectly assume that this is the same object.

From a practical perspective however, I think it should work fine. The ID is increased every time a new object appears and the number of available IDs is large, hence the above mentioned risk should be very low.