Skip to content

Add G-RRT* (Greedy RRT*) planner#95

Open
mlsdpk wants to merge 6 commits into
KavrakiLab:mainfrom
mlsdpk:main
Open

Add G-RRT* (Greedy RRT*) planner#95
mlsdpk wants to merge 6 commits into
KavrakiLab:mainfrom
mlsdpk:main

Conversation

@mlsdpk
Copy link
Copy Markdown
Contributor

@mlsdpk mlsdpk commented Mar 10, 2026

Hi everyone! This follows up on #24, where I mentioned adding a bidirectional asymptotically optimal planner as an alternative to the existing AORRTC (G-RRT* is rewiring-based, as it builds on RRT*). It took a bit longer than expected, but here it is! The related publication can be found here.

I haven't updated the README yet - happy to do so once you're on board with including this planner in VAMP.

I tested this on the existing sphere cage problem. You can find some results below for both deterministic and randomized versions. I also included this test script, which might be useful for others to try out and use as a reference for adding new anytime planners.

convergence_deterministic convergence_multi_seed

Copy link
Copy Markdown
Collaborator

@zkingston zkingston left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the contribution! I've left some comments on the main GRRT* planner, mostly nits. I'll look at the rest later.

Comment thread src/impl/vamp/planning/grrtstar.hh Outdated
{ return buffer.get() + index * Configuration::num_scalars_rounded; };

std::vector<std::size_t> parents(settings.max_samples);
std::vector<float> costs(settings.max_samples, std::numeric_limits<float>::infinity());
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

May be careful with infinity due to numeric issues with compiler optimizations.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Switched to std::numeric_limits::max() to be safe!

Comment thread src/impl/vamp/planning/grrtstar.hh Outdated
Comment thread src/impl/vamp/planning/grrtstar.hh Outdated
Comment thread src/impl/vamp/planning/grrtstar.hh Outdated
Comment thread src/impl/vamp/planning/grrtstar.hh Outdated
Comment thread src/impl/vamp/planning/grrtstar.hh Outdated
Comment thread src/impl/vamp/planning/grrtstar.hh
Comment thread src/impl/vamp/planning/grrtstar.hh Outdated
}
start_chain.push_back(current);

for (int i = static_cast<int>(start_chain.size()) - 1; i >= 0; --i)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably not going to overflow, but is int the right width?

Copy link
Copy Markdown
Contributor Author

@mlsdpk mlsdpk Apr 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right! I just replaced it with reverse iterators.

Comment thread src/impl/vamp/planning/grrtstar.hh Outdated
Comment thread src/impl/vamp/planning/grrtstar.hh Outdated
float cost_delta = costs[nbr_node.index] - nbr_new_cost;

auto &old_parent_children = children[parents[nbr_node.index]];
old_parent_children.erase(
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Curious how efficient this is compared to just marking the old as invalid and extending. Also, potentially reserving space for the neighbors in advance. Not sure how expensive all these operations are.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, you're right. The erase-remove was directly copied from the OMPL implementation, but it's O(n) for both find and remove.

I've replaced that with the overwrite-pop pattern - same O(n) find but O(1) removal.

I also looked at the mark-as-invalid pattern you mentioned and micro-benchmarked it against different implementations. Overwrite-pop wins overall (the hash-based mark patterns have expensive insert/lookup costs, and a simple linear scan + O(1) pop wins over hash or vector bookkeeping).

Also added neighbors.reserve(settings.max_samples) following the pattern in aorrtc.hh and fcit.hh.

Copy link
Copy Markdown
Member

@claytonwramsey claytonwramsey left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR looks mostly good to me.

There's currently no visualization script (at least that I can find) to verify that the planner actually produces correct solutions, unless @zkingston has one that I do not know about. The typical scripts I use for satisficing planners (evaluate_mbm and sphere_cage_example) seem to time out.

I can't sensibly accept a PR for a planner until I can verify its correctness with a viz, so it is currently in request-changes until I figure out how to.

Maybe we should add a demo script for optimal planning visualization, to show that the plan quality improves over search time? I will consider experimenting with this later.

Comment thread src/impl/vamp/planning/grrtstar.hh Outdated
result.size.emplace_back(1);
return result;
}
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is it necessarily correct to check straight-line solutions first?
Also, this would be incorrect -- selecting the first valid straight line solution necessarily means skipping a (possibly faster) non-straight solution to a distinct goal.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yup you're right! I totally forgot about the multiple goals. removed that block in new changes

std::shared_ptr<ProlateHyperspheroidRNG<Robot>> phs_rng_ptr;
if (settings.use_phs and goals.size() == 1)
{
phs_ptr = std::make_unique<ProlateHyperspheroid<Robot>>(start, goals[0]);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PHS sampling should support multi-goal; but I am willing to say it's not blocking for this PR.

TODO: make AORRTC and friends also do PHS sampling for multi-goal.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's doable! Ping me again once you start working on multi-goal support for PHS sampling. I can help you with the changes in grrtstar

Comment thread src/impl/vamp/planning/grrtstar.hh Outdated
};

// Trees for balanced bidirectional growth
bool tree_a_is_start = not true;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

...false?

Copy link
Copy Markdown
Contributor

@ShrutheeshIR ShrutheeshIR Apr 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just jumping in Clayton, on a slightly tangential line, in rrtc we have

bool tree_a_is_start = not settings.start_tree_first;
So I believe this could be following a similar pattern

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah i was following the rrtc pattern, just changed back to false

@@ -0,0 +1,690 @@
#pragma once
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

perhaps include comment with citation either at top of file or on the class? @zkingston do you have opinions?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm good with any options, up to you folks!

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it'd be good practice to include the citation in the file akin to OMPL, please go ahead and add.

@mlsdpk
Copy link
Copy Markdown
Contributor Author

mlsdpk commented May 1, 2026

@claytonwramsey @zkingston I added two more parameters to make it more robust and easier to use:

  • optimize flag to enable/disable anytime planning (similar to AORRTC, default True)
  • cap for KNN to bound rewiring at large trees, otherwise it will be too slow (default 512 is reasonable, tested with Panda across different MBM problems and the sphere cage example)

I think we need to explicitly set the iteration budget in the sphere cage example for anytime planners, otherwise it will run for very long (up to the default iteration budget, I think). Some results below:

Sphere cage benchmark (Panda, single goal, halton, default range=2.0, simplification applied to all paths)

Satisficing mode - first solution from each planner. After simplification, all three converge to the same cost:

Planner iters time (ms) raw cost simp cost
rrtc 275 1.0 8.70 6.35
aorrtc(optimize=False) 275 1.2 6.35 6.35
grrtstar(optimize=False) 275 2.9 8.70 6.35

Anytime convergence - both planners with optimize=True at increasing iteration budgets:

Iter budget AORRTC time (ms) AORRTC raw AORRTC simp G-RRT* time (ms) G-RRT* raw G-RRT* simp
1k 3.5 5.08 5.08 5.9 7.73 5.27
2k 7.7 5.08 5.08 13.9 5.75 4.91
5k 49.2 5.08 5.08 46.7 5.75 4.91
10k 204.9 5.08 5.08 128.6 5.62 4.37
20k 744.7 4.44 4.44 532.6 4.83 4.11
50k 2656.9 4.44 4.44 969.1 4.43 4.16

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants