Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RSDK-8683 - Checking if start is within planDevMM of goal should be handled by motionplan/ik package #4426

Open
wants to merge 6 commits into
base: main
Choose a base branch
from

Conversation

nfranczak
Copy link
Member

@nfranczak nfranczak commented Oct 7, 2024

This is an improvement for this ticket RSDK-8481

The planner still runs, but on the first iteration of RRT we check if we are within PlanDevMM of the startPosition.
If we are, our returned plan is:

[0, 0, 0, 0],
[0, 0, 0, 0]

In this PR, we remove atGoalCheck from the moveRequest struct.
Since it is removed we redefine how we check for success once a plan has been completed.

@viambot viambot added the safe to test This pull request is marked safe to test from a trusted zone label Oct 7, 2024
@@ -186,6 +186,9 @@ type plannerOptions struct {
// profile is the string representing the motion profile
profile string

// planDeviationMM is a float representing how close we must be to the goal in order for planning to finish
planDeviationMM float64
Copy link
Member

Choose a reason for hiding this comment

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

How is this distinct from GoalThreshold?

Copy link
Member Author

Choose a reason for hiding this comment

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

let delta = abs(startPose - goalPose).
suppose we are on the first iteration of planning.
if delta <= planDevMM (should it exist) then we are done planning.

The difference is that GoalThreshold exists for all invocations of the planner, but planDevMM only exists for TPSpace RRT and is only used for the first iteration of planning.

Copy link
Member

Choose a reason for hiding this comment

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

Why would we not want the check if delta <= GoalThreshold for all planners? In this case there is no work to be done you are already there

Copy link
Member

Choose a reason for hiding this comment

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

I agree we don't want two separate values here. We should have the motion service pass the planDeviationMM as the goal threshold if we want to do the check.

Unsure at the moment if we want to pass the smaller or larger of the two numbers, thoughts?

Copy link
Member Author

Choose a reason for hiding this comment

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

I think that is makes more sense to use the larger of the two numbers.

At plan time, for a given moveBase call, we may already be within PlanDevMM of a goal but not within the goal threshold.

Copy link
Member Author

@nfranczak nfranczak Oct 14, 2024

Choose a reason for hiding this comment

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

I'm now wondering if perhaps we do after all want planDeviationMm to be fully handled in the motion service.

I am also thinking about this. As you mentioned on the merged PR related to this (#4327 (comment)) this should be handled within the motionplan package. If not, then there is no work to be done other than perhaps adding some comments around existing code.

IMO, the only way to avoid the scenario where the base ends up outside the shed is to have plannerOptions include a field for planDevMM, and a supplementary field for planDevMM's success function only pertinent to the first iteration of our planner.

This itself however, highlights an improper abstraction we've made which makes me wonder how else to approach the problem/issue. I say this is an improper abstraction since planDevMM and its success function are un-used for plans not related to bases.

Copy link
Member

Choose a reason for hiding this comment

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

Okay, I'm looking more at the comment thread on that old PR, thinking about the behavior we've got, and here's what I've come up with.

The idea of planDevMM relies on the existence of a plan, and the fact that we may have deviated from it. It doesn't actually have anything to do with the generation of a plan.

Thinking along those lines, I think we should instead revisit the assumption that we should do nothing if we request a plan while being within planDevMM of the goal. Another interpretation would be simply that we would try to plan and move regardless, and this would simply disable replanning.

Copy link
Member

Choose a reason for hiding this comment

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

Bumping this comment thread since I think it got missed and deciding the behavior here is core to what this PR should do @raybjork @nfranczak

Copy link
Member Author

Choose a reason for hiding this comment

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

oop, I did miss this comment. Thank you for bumping the thread

Copy link
Member Author

Choose a reason for hiding this comment

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

The idea of planDevMM relies on the existence of a plan, and the fact that we may have deviated from it. It doesn't actually have anything to do with the generation of a plan.

This is a good point.

I think we should instead revisit the assumption that we should do nothing if we request a plan while being within planDevMM of the goal.

I think this is a safe assumption to be made. Suppose we are within planDevMM of the goal at start and we've generated some plan whose inputs are non-zero. Then executing that generated plan is pointless.

Another interpretation would be simply that we would try to plan and move regardless, and this would simply disable replanning.

I do not think this is safe. If the kinematic base driver is faulty for some reason we will end up executing the plan and might wind up somewhere that is outside of the defined planDevMM. This would then cause for a replan to be issued and we might end up stuck in a cycle of perpetual replanning.

Overall, I think the best way to handle this is to return a plan whose inputs are: [0, 0, 0, 0], [0, 0, 0, 0].

@raybjork what are your thoughts on this?


// Check to see if the startPose is already within some predefined delta of the goalPose
if iter == 0 {
atGoal := func(startPose, goalPose spatialmath.Pose) bool {
Copy link
Member

Choose a reason for hiding this comment

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

This is already handled (more accurately) by mp.planOpts.DistanceFunc. Use that instead of this.

Copy link
Member Author

Choose a reason for hiding this comment

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

will do.
question: we define DistanceFunc as L2InputMetric

InputsL2Distance returns the square of the two-norm between the from and to vectors.

does this account for when we are not operating in PositionOnly mode?

Copy link
Member

Choose a reason for hiding this comment

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

Ah, you're right, it wouldn't.

However, within the algorithm itself is not an appropriate place to be checking business logic such as Position Only Mode.

I think we need a third way.

Copy link
Member Author

Choose a reason for hiding this comment

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

However, within the algorithm itself is not an appropriate place to be checking business logic such as Position Only Mode.

It is not immediately obvious to me why you say that this shouldn't live at the algorithm level. Is it because it introduces special handling code/tech-debt? What are your thoughts?

If it does not live on the algorithm level, "it" being the early return from the planner, then "it" must live either before we enter the motionplan package or once we return from it. The main branch currently handles the latter -- the check existing once we exit the planner.

}
if atGoal(startPose, goalPose) {
path := extractTPspacePath(rrt.maps.startMap, rrt.maps.goalMap,
&nodePair{
Copy link
Member

Choose a reason for hiding this comment

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

Let's use the actual nodes in the map, rather than create new ones.

@@ -186,6 +186,9 @@ type plannerOptions struct {
// profile is the string representing the motion profile
profile string

// planDeviationMM is a float representing how close we must be to the goal in order for planning to finish
planDeviationMM float64
Copy link
Member

Choose a reason for hiding this comment

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

I agree we don't want two separate values here. We should have the motion service pass the planDeviationMM as the goal threshold if we want to do the check.

Unsure at the moment if we want to pass the smaller or larger of the two numbers, thoughts?

@@ -306,6 +306,36 @@ func (mp *tpSpaceRRTMotionPlanner) rrtBackgroundRunner(
rrt.solutionChan <- &rrtSolution{err: fmt.Errorf("TP Space RRT timeout %w", ctx.Err()), maps: rrt.maps}
return
}

// Check to see if the startPose is already within some predefined delta of the goalPose
if iter == 0 {
Copy link
Member

Choose a reason for hiding this comment

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

We should probably actually move this up to where we iterate the goal map on line 224 and check for every goal node.

@@ -108,6 +108,16 @@ func (req *PlanRequest) validatePlanRequest() error {
}
}

_, ok := req.Options["planDeviationMM"].(float64)
Copy link
Member

Choose a reason for hiding this comment

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

This should be able to go away when this is merged with GoalThreshold as discussed below.

@viambot viambot added safe to test This pull request is marked safe to test from a trusted zone and removed safe to test This pull request is marked safe to test from a trusted zone labels Oct 15, 2024
@@ -905,6 +905,17 @@ func (pm *planManager) planRelativeWaypoint(ctx context.Context, request *PlanRe
return nil, err
}

if opt.profile == PositionOnlyMotionProfile {
Copy link
Member Author

Choose a reason for hiding this comment

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

@biotinker let me know your thoughts on this.
Business logic of caring for position only mode is not in the algorithm, however I did need to introduce a new field to plannerOpts.

@viambot viambot added safe to test This pull request is marked safe to test from a trusted zone and removed safe to test This pull request is marked safe to test from a trusted zone labels Oct 15, 2024
@viambot viambot added safe to test This pull request is marked safe to test from a trusted zone and removed safe to test This pull request is marked safe to test from a trusted zone labels Oct 15, 2024
Copy link
Contributor

Availability

Scene # viamrobotics:main nfranczak:RSDK-8683 Percent Improvement Health
1 100% 100% 0%
2 100% 100% 0%
3 100% 100% 0%
4 100% 100% 0%
5 90% 90% 0%
6 70% 80% 14%
7 20% 20% 0%
8 100% 100% 0%
9 90% 90% 0%
10 100% 100% 0%
11 100% 100% 0%
12 100% 100% 0%
13 100% 100% 0%
14 100% 100% 0%
15 100% 100% 0%
16 90% 90% 0%
17 100% 100% 0%
18 70% 70% 0%

Quality

Scene # viamrobotics:main nfranczak:RSDK-8683 Percent Improvement Probability of Improvement Health
1 1.31±0.00 1.31±0.00 -0% 50%
2 0.90±0.00 0.90±0.00 -0% 50%
3 2.48±0.13 2.48±0.13 -0% 50%
4 4.04±1.26 3.86±1.12 4% 54%
5 14.40±5.95 14.08±4.98 2% 52%
6 17.56±4.22 18.28±3.52 -4% 45%
7 5.28±1.61 5.28±1.61 -0% 50%
8 4.99±1.08 4.99±1.08 -0% 50%
9 4.52±0.14 4.52±0.14 0% 50%
10 4.13±0.34 4.12±0.33 0% 51%
11 3.13±0.00 3.13±0.00 -0% 50%
12 3.91±0.85 4.00±0.98 -2% 47%
13 904.77±13.78 904.77±13.78 -0% 50%
14 2035.67±601.28 2035.67±601.28 -0% 50%
15 50790.10±9415.58 50790.10±9415.58 -0% 50%
16 59103.42±7272.96 59103.42±7272.96 -0% 50%
17 15936.40±3302.53 15936.40±3302.53 -0% 50%
18 120076.85±14640.44 119821.27±14955.31 0% 50%

Performance

Scene # viamrobotics:main nfranczak:RSDK-8683 Percent Improvement Probability of Improvement Health
1 0.09±0.01 0.09±0.00 4% 66%
2 0.10±0.01 0.11±0.01 -1% 46%
3 1.01±0.62 1.01±0.62 0% 50%
4 2.01±0.12 2.00±0.13 0% 52%
5 2.69±0.93 2.80±0.91 -4% 47%
6 3.06±0.89 3.15±1.04 -3% 48%
7 2.89±0.58 2.92±0.58 -1% 48%
8 0.39±0.15 0.39±0.15 0% 50%
9 4.96±0.21 4.98±0.21 -0% 48%
10 0.14±0.02 0.14±0.02 2% 53%
11 0.10±0.00 0.10±0.00 -1% 43%
12 0.12±0.01 0.13±0.01 -1% 44%
13 0.07±0.00 0.07±0.01 -4% 38%
14 0.78±0.27 0.79±0.27 -2% 48%
15 1.10±0.17 1.11±0.17 -1% 48%
16 2.48±0.81 2.52±0.81 -2% 49%
17 1.28±0.14 1.29±0.15 -0% 49%
18 4.65±0.79 4.65±0.79 -0% 50%

The above data was generated by running scenes defined in the motion-testing repository
The SHA1 for viamrobotics:main is: 900738751a11aa6be03dd90a3932f38e1c35f187
The SHA1 for nfranczak:RSDK-8683 is: 900738751a11aa6be03dd90a3932f38e1c35f187

  • 10 samples were taken for each scene
  • A timeout of 5.0 seconds was imposed for each trial

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
safe to test This pull request is marked safe to test from a trusted zone
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants