-
Notifications
You must be signed in to change notification settings - Fork 107
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-5391 - Create distinct planning and execution frames for PTG KinematicWrapper #3876
base: main
Are you sure you want to change the base?
Conversation
… off the planningFrame within execution.go
…ts and not transform off a pose if we are relative
…ntroduced absolute frame system which houses the execution frame + planning frame
@@ -148,21 +149,22 @@ func newSolverFrame(fs frame.FrameSystem, solveFrameName, goalFrameName string, | |||
} | |||
|
|||
var ptgs []tpspace.PTGSolver | |||
anyPTG := false // Whether PTG frames have been observed | |||
anyNonzero := false // Whether non-PTG frames | |||
anyPTG := false // Whether PTG frames have been observed |
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.
I needed to comment this out since we have introduced an executionFrame
into the mix for solving...
Not immediately sure how to circumvent this.
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.
bump looking for advice on this
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.
We may want to move
// re-root the frame system on the relative frame
relativeOnlyFS, err := pm.frame.fss.FrameSystemSubset(request.Frame)
upstream of this so that this is a non-issue.
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.
hmmmmm, this is quite tricky...
I will attempt to describe why I think this is tricky best I can. First we describe our nomenclature.
Let AbsFS := localizationFrame - planningFrame - camera
and,
Let RelFS := planningFrame - camera
solveFrame.go
wants to be fed in RelFS
. The "why" it wants RelFS
is the result of anyNonzero
and anyPTG
being true should we choose to pass in AbsFS
.
Once we are done with constructing our solverFrame, we then proceed to PlanSingleWaypoint
and then into planRelativeWaypoint
. From there, we enter plannerSetupFromMoveRequest
.
We note that: One of the goals of the body of work is the allow placing of robot geometries exclusively through a FrameSystem and its corresponding seedMap.
This is where things get tricky. We have simplified the code to become
// create robot collision entities
movingGeometriesInFrame, err := pm.frame.Geometries(frameInputs)
Where pm.Frame
is actually the solverFrame
entity we have created upstream.
If we step into the solverFrame.Geometries
method we iterate over sf.movingFS
which was constructed using the RelFS
. This will end up incorrectly placing the robot collision entities.
Hence, solverFrame code wants RelFS
while plannerSetupFromMoveRequest code wants AbsFS
.
For this reason, I say that
We may want to move
// re-root the frame system on the relative frame relativeOnlyFS, err := pm.frame.fss.FrameSystemSubset(request.Frame)
upstream of this so that this is a non-issue.
is not the solution, given what we want to accomplish.
I am certain that there is a clean solution to the issue I have described above, I just need to think on it a little longer.
Would you agree with my problem description?
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.
I take this comment back, I think what I wrote is wrong
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.
fleshing out the details now but I think the solution here is that we will want to combine the
LocalizationFrame (poseFrame) with the PlanningFrame(ptgGroupFrame) into a singular frame called WrappedFrame. This will circumvent this issue and we will allows to un-comment this code.
Yesterday, I went down the wrong train of thought as I thought this is an issue from plan construction. Instead, this is an issue for interacting with CheckPlan.
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.
spoke with ray, he recommended I use a referenceframe.Model
instead of building a one off WrapperFrame
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.
spoke with Peter, moving the any-non zero check into planRelativeWayPoint circumvents this issue and we can pass in AbsFS := localizationFrame - planningFrame - camera
to solverFrame construction
@@ -58,7 +58,6 @@ type moveRequest struct { | |||
requestType requestType | |||
// geoPoseOrigin is only set if requestType == requestTypeMoveOnGlobe | |||
geoPoseOrigin *spatialmath.GeoPose | |||
poseOrigin spatialmath.Pose |
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.
no need to have this anymore since we use the absoluteFS
to directly place observed obstacles into the world frame.
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.
This is really cool, just one request for a rename. Looks clean now thanks for following through on all our comments
@@ -67,6 +66,7 @@ type moveRequest struct { | |||
obstacleDetectors map[vision.Service][]resource.Name | |||
replanCostFactor float64 | |||
fsService framesystem.Service | |||
absoluteFS referenceframe.FrameSystem |
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.
can we just name this fs
or frameSystem
?
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.
I would like to have a descriptive name for the field name, what are your thoughts on checkPlanFS
.
I want to communicate through this name that we have the following elements in the FS:
checkPlanFS := localizationFrame -- planningFrame -- other_children
opposed to
regular_FS_topology_we've_used_until_now: := planningFrame -- other_children
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.
How about localizingFS then? I think the word absolute can be a little confusing
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.
sounds good to me!
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.
Overall looking really nice. I think there's a substantial simplification left to be made to CheckPlan, but otherwise only small comments
// we intentionally set our OX, OY, OZ values to be greater than 1 since | ||
// a possible orientation which a planner produces may have the following | ||
// values: OZ: 1.0000000002, Theta: t. | ||
{Min: -1.5, Max: 1.5}, |
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.
I wonder if all of these should be +/- infinity, and then normalized to a unit vector and +/- pi. That makes mildly more sense to me than having a buffer of 0.5.
referenceframe/frame.go
Outdated
} | ||
|
||
// NewPoseFrame created a frame with 7 degrees of freedom, x, y, z, OX, OY, OZ, and theta in radians. | ||
func NewPoseFrame(name string, limits []Limit, geometry spatial.Geometry) (Frame, error) { |
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.
Shouldn't the limits be always the same? Why pass them in?
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.
good point, I will update this.
motionplan/check.go
Outdated
@@ -168,7 +173,7 @@ func checkPlanRelative( | |||
expectedCurrentPose := spatialmath.PoseBetweenInverse(remainingArcPose, expectedArcEndInWorld.Pose()) | |||
errorState := spatialmath.PoseBetween(expectedCurrentPose, currentPoseInWorld.Pose()) | |||
|
|||
planStartPiF, ok := plan.Path()[0][checkFrame.Name()] | |||
planStartPiF, ok := plan.Path()[wayPointIdx][checkFrame.Name()] |
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.
Why is this changing?
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.
Good question.
planStartPiF
is turned into planStartPoseWorld
which is then passed into plannerSetupFromMoveRequest
.
On main
and within plannerSetupFromMoveRequest
we would then transform the solverFrame
off our seedMap
of type map[string][]referenceframe.Input
. But, since this seedMap
contains relative inputs, the transform's result would incorrectly place us in a relative position. We were then able to position ourselves absolutely since we were composing the relative position with planStartPoseWorld
.
Now suppose that our wayPointIdx
is 2
. Then, the planStartPiF
should reflect that.
However, this itself is not entirely correct either.
Since solverFrame.Transform(seedMap)
will now place us in our absolute position from the get-go, we do not need to compose its output with anything else. Hence, I believe the correct course of action here is to actually remove planStartPiF
, and planStartPoseWorld
and instead pass in currentPoseInWorld
into plannerSetupFromMoveRequest
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.
The intended use of planStartPoseWorld
is to place all frames where they were when the plan was created, as this will accurately reflect which things may or may not be in collision, something the current inputs may not do.
As the first element of the plan will generally be all 0 inputs for a relative plan, that should not have placed the frame incorrectly.
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.
That said, relying on the first set of inputs to be 0 is bad, and thus this PR will be an improvement. But we will still want to use index 0 of the plan.
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.
as this will accurately reflect which things may or may not be in collision, something the current inputs may not do.
This was my missing mental link. Thank you for explaining :) I am pretty sure I get it now.
I want to ensure that I understand the "why"
we may not rely on current inputs to correctly inform us about allowed/un-allowed collisions.
Suppose we have just started executing a plan and we are still close to our start pose.
Suppose that we have some transient obstacle named X
which is positioned on the path, e.g. 2 meters ahead.
If for some reason at this iteration of CheckPlan
we do not correctly identify collision with X
then we risk having it being included in our zeroCg
?
motionplan/check.go
Outdated
return err | ||
} | ||
currentWayPointTraj := plan.Trajectory()[wayPointIdx] | ||
currentWayPointTraj[localizationFrame.Name()] = referenceframe.FloatsToInputs([]float64{ |
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.
IMO it would make more sense for this to live in the motion service. I think if we do that, it removes the need to pass in the localization frame specially.
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.
hmmmmm, this is an interesting suggestion.
The first route I took to attempt at accomplishing this suggestion was to augment the existing plan object we pass into CheckPlan
to house inputs information relating to the LocalizationFrame
.
However, I do not believe that it is possible to accomplish this (this way) due to:
nextInputs[localizationFrame.Name()] = startInputs[localizationFrame.Name()]
Above, is a part of the for loop we construct to create the necessary segments to iterate over.
Since we want our nextInputs
to equal startInputs
for the LocalizationFrame, there is no way AFAIK to augment the existing plan to accomplish this.
referenceframe/frame.go
Outdated
geometry spatial.Geometry | ||
} | ||
|
||
// NewPoseFrame created a frame with 7 degrees of freedom, x, y, z, OX, OY, OZ, and theta in radians. |
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.
Let's specifically call out that this is an Orientation Vector
@@ -305,6 +301,21 @@ func (mr *moveRequest) obstaclesIntersectPlan( | |||
return state.ExecuteResponse{}, nil | |||
} | |||
|
|||
func (mr *moveRequest) createInputMap(baseExecutionState motionplan.ExecutionState) map[string][]referenceframe.Input { | |||
inputMap := referenceframe.StartPositions(mr.localizaingFS) |
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.
This should call CurrentInputs() on everything InputEnabled, not assume their inputs are zero.
Otherwise we're laying landmines for ourselves to hit when we start allowing arms on bases
Availability
Quality
Performance
The above data was generated by running scenes defined in the
|
Want:
getTransientDetections
usesframesystem
, (FS
), object to place obstacles into the world frameik.State
which is checked withCheckStateConstraints
withinCheckPlan
currentInputs
to tell us where we actually are, not where we think we areToDos:
localizationFrame
as an argument toCheckPlan