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-4051 tp space should be able to continuously sample ptgs #2792

Merged
Show file tree
Hide file tree
Changes from 36 commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
bd4468b
Implement the first couple Transform methods
biotinker Jul 25, 2023
98bc9ba
Update CS and CCS ptgs
biotinker Aug 2, 2023
2fa3644
Merge branch 'main' into 20230717_RSDK-4051-tp-space-should-be-able-t…
biotinker Aug 2, 2023
1d13d50
remove duplicate const
biotinker Aug 2, 2023
a7644de
Merge branch 'main' into 20230717_RSDK-4051-tp-space-should-be-able-t…
biotinker Aug 4, 2023
952b675
add IK ptg file (placeholder)
biotinker Aug 4, 2023
0f8a799
Some intermediate work
biotinker Aug 8, 2023
0c79ef3
add file
biotinker Aug 8, 2023
b4f05b5
Update ctxs and errors
biotinker Aug 8, 2023
da7752b
barely-working tp-space IK
biotinker Aug 9, 2023
6672323
Add files
biotinker Aug 9, 2023
e17df26
Kinda working, no obstacle avoidance yet
biotinker Aug 10, 2023
827f265
Decently-working IK solving, unidirectional
biotinker Aug 10, 2023
0270fc7
Bidirectional solving working, but tree reconstruction afterwards not…
biotinker Aug 11, 2023
4445a9c
deprecate x y phi from ptgs
biotinker Aug 11, 2023
62a5ba6
Bidirectional tp-space IK solving working reasonably quickly and well…
biotinker Aug 12, 2023
248f7b4
Fiddling with parallelization etc
biotinker Aug 15, 2023
ab8deaa
Fixed smoothing, but for real this time
biotinker Aug 15, 2023
9aa1af8
Reintroduce filtering on node nearness to avoid 15-point turns
biotinker Aug 15, 2023
59381c6
Linting and working
biotinker Aug 16, 2023
c797032
Remove some comments and old file
biotinker Aug 16, 2023
0722a57
Merge branch 'main' into 20230717_RSDK-4051-tp-space-should-be-able-t…
biotinker Aug 16, 2023
a9a1150
Fix merge conflict
biotinker Aug 16, 2023
1939e78
lint
biotinker Aug 16, 2023
92b1b29
Assorted cleanup
biotinker Aug 16, 2023
8583ec6
RDK is on older Go than my dev machine
biotinker Aug 16, 2023
4fdc37d
Fix err join
biotinker Aug 16, 2023
95b565b
Fix solving syncgroups
biotinker Aug 17, 2023
5f242a0
Move IK to its own package
biotinker Aug 22, 2023
0e1748b
Address all PR comments, including a big reshuffle of IK
biotinker Aug 22, 2023
c534fa0
Delete files
biotinker Aug 22, 2023
8e343c2
Remove extraneous tpspace rrt parameter
biotinker Aug 22, 2023
898919a
Merge branch 'main' into 20230717_RSDK-4051-tp-space-should-be-able-t…
biotinker Aug 22, 2023
e283a10
Fix flaky test
biotinker Aug 23, 2023
e3b110d
Fix race
biotinker Aug 23, 2023
7eb28dc
Allow use of rectifyTPspacePath without a tpspace planner
biotinker Aug 23, 2023
72c79d3
Fix test
biotinker Aug 23, 2023
9c92456
Put moved code back where it was
biotinker Aug 23, 2023
716404d
Fix function ordering
biotinker Aug 23, 2023
dc361dc
Correct errChan capacity
biotinker Aug 23, 2023
8ae6899
Remove log statements
biotinker Aug 23, 2023
0c6734f
PR feedback
biotinker Aug 23, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions components/base/kinematicbase/differentialDrive.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import (
utils "go.viam.com/utils"

"go.viam.com/rdk/components/base"
"go.viam.com/rdk/motionplan"
"go.viam.com/rdk/motionplan/ik"
"go.viam.com/rdk/referenceframe"
"go.viam.com/rdk/services/motion"
"go.viam.com/rdk/spatialmath"
Expand Down Expand Up @@ -186,7 +186,7 @@ func (ddk *differentialDriveKinematics) GoToInputs(ctx context.Context, desired
if prevInputs == nil {
prevInputs = currentInputs
}
positionChange := motionplan.L2InputMetric(&motionplan.Segment{
positionChange := ik.L2InputMetric(&ik.Segment{
StartConfiguration: prevInputs,
EndConfiguration: currentInputs,
})
Expand Down
19 changes: 10 additions & 9 deletions components/base/kinematicbase/ptgKinematics.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,11 +74,12 @@ func wrapWithPTGKinematics(
return nil, err
}

frame, err := referenceframe.NewPTGFrameFromTurningRadius(
frame, err := tpspace.NewPTGFrameFromTurningRadius(
b.Name().ShortName(),
logger,
baseMillimetersPerSecond,
baseTurningRadius,
0, // pass 0 to use the default
0, // pass 0 to use the default refDist
geometries,
)
if err != nil {
Expand Down Expand Up @@ -122,15 +123,15 @@ func (ptgk *ptgBaseKinematics) GoToInputs(ctx context.Context, inputs []referenc
ptgk.logger.Debugf("GoToInputs going to %v", inputs)

selectedPTG := ptgk.ptgs[int(math.Round(inputs[ptgIndex].Value))]
selectedTraj := selectedPTG.Trajectory(uint(math.Round(inputs[trajectoryIndexWithinPTG].Value)))
selectedTraj, err := selectedPTG.Trajectory(inputs[trajectoryIndexWithinPTG].Value, inputs[distanceAlongTrajectoryIndex].Value)
if err != nil {
return multierr.Combine(err, ptgk.Base.Stop(ctx, nil))
}

lastTime := 0.
for i, trajNode := range selectedTraj {
if trajNode.Dist > inputs[distanceAlongTrajectoryIndex].Value {
ptgk.logger.Debugf("finished executing trajectory after %d steps", i)
// We have reached the desired distance along the given trajectory
break
}
for _, trajNode := range selectedTraj {
// TODO: Most trajectories update their velocities infrequently, or sometimes never.
// This function could be improved by looking ahead through the trajectory and minimizing the amount of SetVelocity calls.
timestep := time.Duration((trajNode.Time-lastTime)*1000*1000) * time.Microsecond
lastTime = trajNode.Time
linVel := r3.Vector{0, trajNode.LinVelMMPS, 0}
Expand Down
50 changes: 50 additions & 0 deletions components/base/kinematicbase/ptgKinematics_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,53 @@ func TestPTGKinematics(t *testing.T) {
test.That(t, err, test.ShouldBeNil)
test.That(t, plan, test.ShouldNotBeNil)
}

func TestPTGKinematicsWithGeom(t *testing.T) {
logger := golog.NewTestLogger(t)

name, err := resource.NewFromString("is:a:fakebase")
test.That(t, err, test.ShouldBeNil)

baseGeom, err := spatialmath.NewBox(spatialmath.NewZeroPose(), r3.Vector{1, 1, 1}, "")
test.That(t, err, test.ShouldBeNil)

b := &fake.Base{
Named: name.AsNamed(),
Geometry: []spatialmath.Geometry{baseGeom},
WidthMeters: 0.2,
TurningRadius: 0.3,
}

ctx := context.Background()

kbOpt := NewKinematicBaseOptions()
kbOpt.AngularVelocityDegsPerSec = 0
kb, err := WrapWithKinematics(ctx, b, logger, nil, nil, kbOpt)
test.That(t, err, test.ShouldBeNil)
test.That(t, kb, test.ShouldNotBeNil)
ptgBase, ok := kb.(*ptgBaseKinematics)
test.That(t, ok, test.ShouldBeTrue)
test.That(t, ptgBase, test.ShouldNotBeNil)

dstPIF := referenceframe.NewPoseInFrame(referenceframe.World, spatialmath.NewPoseFromPoint(r3.Vector{X: 2000, Y: 0, Z: 0}))

fs := referenceframe.NewEmptyFrameSystem("test")
f := kb.Kinematics()
test.That(t, err, test.ShouldBeNil)
fs.AddFrame(f, fs.World())
inputMap := referenceframe.StartPositions(fs)

obstacle, err := spatialmath.NewBox(spatialmath.NewPoseFromPoint(r3.Vector{1000, 0, 0}), r3.Vector{1, 1, 1}, "")
test.That(t, err, test.ShouldBeNil)

geoms := []spatialmath.Geometry{obstacle}
worldState, err := referenceframe.NewWorldState(
[]*referenceframe.GeometriesInFrame{referenceframe.NewGeometriesInFrame(referenceframe.World, geoms)},
nil,
)
test.That(t, err, test.ShouldBeNil)

plan, err := motionplan.PlanMotion(ctx, logger, dstPIF, f, inputMap, fs, worldState, nil, nil)
test.That(t, err, test.ShouldBeNil)
test.That(t, plan, test.ShouldNotBeNil)
}
49 changes: 26 additions & 23 deletions motionplan/cBiRRT.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"github.com/edaniels/golog"
"go.viam.com/utils"

"go.viam.com/rdk/motionplan/ik"
"go.viam.com/rdk/referenceframe"
"go.viam.com/rdk/spatialmath"
)
Expand Down Expand Up @@ -55,7 +56,7 @@ func newCbirrtOptions(planOpts *plannerOptions) (*cbirrtOptions, error) {
// https://ieeexplore.ieee.org/document/5152399/
type cBiRRTMotionPlanner struct {
*planner
fastGradDescent *NloptIK
fastGradDescent *ik.NloptIK
algOpts *cbirrtOptions
}

Expand All @@ -74,7 +75,7 @@ func newCBiRRTMotionPlanner(
return nil, err
}
// nlopt should try only once
nlopt, err := CreateNloptIKSolver(frame, logger, 1, opt.GoalThreshold)
nlopt, err := ik.CreateNloptIKSolver(frame, logger, 1, true)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -124,15 +125,17 @@ func (mp *cBiRRTMotionPlanner) rrtBackgroundRunner(
mp.start = time.Now()

if rrt.maps == nil || len(rrt.maps.goalMap) == 0 {
mp.logger.Info("initing solutions")
planSeed := initRRTSolutions(ctx, mp, seed)
mp.logger.Info("init solutions done")
if planSeed.planerr != nil || planSeed.steps != nil {
rrt.solutionChan <- planSeed
return
}
rrt.maps = planSeed.maps
}
mp.logger.Infof("goal node: %v\n", rrt.maps.optNode.Q())
target := referenceframe.InterpolateInputs(seed, rrt.maps.optNode.Q(), 0.5)
target := newConfigurationNode(referenceframe.InterpolateInputs(seed, rrt.maps.optNode.Q(), 0.5))

map1, map2 := rrt.maps.startMap, rrt.maps.goalMap

Expand Down Expand Up @@ -163,13 +166,13 @@ func (mp *cBiRRTMotionPlanner) rrtBackgroundRunner(
default:
}

tryExtend := func(target []referenceframe.Input) (node, node, error) {
tryExtend := func(target node) (node, node, error) {
// attempt to extend maps 1 and 2 towards the target
utils.PanicCapturingGo(func() {
m1chan <- nm1.nearestNeighbor(nmContext, mp.planOpts, newConfigurationNode(target), map1)
m1chan <- nm1.nearestNeighbor(nmContext, mp.planOpts, target, map1)
})
utils.PanicCapturingGo(func() {
m2chan <- nm2.nearestNeighbor(nmContext, mp.planOpts, newConfigurationNode(target), map2)
m2chan <- nm2.nearestNeighbor(nmContext, mp.planOpts, target, map2)
})
nearest1 := <-m1chan
nearest2 := <-m2chan
Expand All @@ -186,10 +189,10 @@ func (mp *cBiRRTMotionPlanner) rrtBackgroundRunner(
rseed2 := rand.New(rand.NewSource(int64(mp.randseed.Int())))

utils.PanicCapturingGo(func() {
mp.constrainedExtend(ctx, rseed1, map1, nearest1, newConfigurationNode(target), m1chan)
mp.constrainedExtend(ctx, rseed1, map1, nearest1, target, m1chan)
})
utils.PanicCapturingGo(func() {
mp.constrainedExtend(ctx, rseed2, map2, nearest2, newConfigurationNode(target), m2chan)
mp.constrainedExtend(ctx, rseed2, map2, nearest2, target, m2chan)
})
map1reached := <-m1chan
map2reached := <-m2chan
Expand All @@ -206,24 +209,24 @@ func (mp *cBiRRTMotionPlanner) rrtBackgroundRunner(
return
}

reachedDelta := mp.planOpts.DistanceFunc(&Segment{StartConfiguration: map1reached.Q(), EndConfiguration: map2reached.Q()})
reachedDelta := mp.planOpts.DistanceFunc(&ik.Segment{StartConfiguration: map1reached.Q(), EndConfiguration: map2reached.Q()})

// Second iteration; extend maps 1 and 2 towards the halfway point between where they reached
if reachedDelta > mp.planOpts.JointSolveDist {
target = referenceframe.InterpolateInputs(map1reached.Q(), map2reached.Q(), 0.5)
target = newConfigurationNode(referenceframe.InterpolateInputs(map1reached.Q(), map2reached.Q(), 0.5))
map1reached, map2reached, err = tryExtend(target)
if err != nil {
rrt.solutionChan <- &rrtPlanReturn{planerr: err, maps: rrt.maps}
return
}
reachedDelta = mp.planOpts.DistanceFunc(&Segment{StartConfiguration: map1reached.Q(), EndConfiguration: map2reached.Q()})
reachedDelta = mp.planOpts.DistanceFunc(&ik.Segment{StartConfiguration: map1reached.Q(), EndConfiguration: map2reached.Q()})
}

// Solved!
if reachedDelta <= mp.planOpts.JointSolveDist {
mp.logger.Debugf("CBiRRT found solution after %d iterations", i)
cancel()
path := extractPath(rrt.maps.startMap, rrt.maps.goalMap, &nodePair{map1reached, map2reached})
path := extractPath(rrt.maps.startMap, rrt.maps.goalMap, &nodePair{map1reached, map2reached}, true)
rrt.solutionChan <- &rrtPlanReturn{steps: path, maps: rrt.maps}
return
}
Expand Down Expand Up @@ -268,8 +271,8 @@ func (mp *cBiRRTMotionPlanner) constrainedExtend(
default:
}

dist := mp.planOpts.DistanceFunc(&Segment{StartConfiguration: near.Q(), EndConfiguration: target.Q()})
oldDist := mp.planOpts.DistanceFunc(&Segment{StartConfiguration: oldNear.Q(), EndConfiguration: target.Q()})
dist := mp.planOpts.DistanceFunc(&ik.Segment{StartConfiguration: near.Q(), EndConfiguration: target.Q()})
oldDist := mp.planOpts.DistanceFunc(&ik.Segment{StartConfiguration: oldNear.Q(), EndConfiguration: target.Q()})
switch {
case dist < mp.planOpts.JointSolveDist:
mchan <- near
Expand All @@ -286,7 +289,7 @@ func (mp *cBiRRTMotionPlanner) constrainedExtend(
newNear = mp.constrainNear(ctx, randseed, oldNear.Q(), newNear)

if newNear != nil {
nearDist := mp.planOpts.DistanceFunc(&Segment{StartConfiguration: oldNear.Q(), EndConfiguration: newNear})
nearDist := mp.planOpts.DistanceFunc(&ik.Segment{StartConfiguration: oldNear.Q(), EndConfiguration: newNear})
if nearDist < math.Pow(mp.planOpts.JointSolveDist, 3) {
if !doubled {
doubled = true
Expand Down Expand Up @@ -343,7 +346,7 @@ func (mp *cBiRRTMotionPlanner) constrainNear(
return nil
}

newArc := &Segment{
newArc := &ik.Segment{
StartPosition: seedPos,
EndPosition: goalPos,
StartConfiguration: seedInputs,
Expand All @@ -356,29 +359,29 @@ func (mp *cBiRRTMotionPlanner) constrainNear(
if ok {
return target
}
solutionGen := make(chan []referenceframe.Input, 1)
solutionGen := make(chan *ik.Solution, 1)
// Spawn the IK solver to generate solutions until done
err = mp.fastGradDescent.Solve(ctx, solutionGen, target, mp.planOpts.pathMetric, randseed.Int())
// We should have zero or one solutions
var solved []referenceframe.Input
var solved *ik.Solution
select {
case solved = <-solutionGen:
default:
}
close(solutionGen)
if err != nil {
if err != nil || solved == nil {
return nil
}

ok, failpos := mp.planOpts.CheckSegmentAndStateValidity(
&Segment{StartConfiguration: seedInputs, EndConfiguration: solved, Frame: mp.frame},
&ik.Segment{StartConfiguration: seedInputs, EndConfiguration: solved.Configuration, Frame: mp.frame},
mp.planOpts.Resolution,
)
if ok {
return solved
return solved.Configuration
}
if failpos != nil {
dist := mp.planOpts.DistanceFunc(&Segment{StartConfiguration: target, EndConfiguration: failpos.EndConfiguration})
dist := mp.planOpts.DistanceFunc(&ik.Segment{StartConfiguration: target, EndConfiguration: failpos.EndConfiguration})
if dist > mp.planOpts.JointSolveDist {
// If we have a first failing position, and that target is updating (no infinite loop), then recurse
seedInputs = failpos.StartConfiguration
Expand Down Expand Up @@ -439,7 +442,7 @@ func (mp *cBiRRTMotionPlanner) smoothPath(
// Note this could technically replace paths with "longer" paths i.e. with more waypoints.
// However, smoothed paths are invariably more intuitive and smooth, and lend themselves to future shortening,
// so we allow elongation here.
dist := mp.planOpts.DistanceFunc(&Segment{StartConfiguration: inputSteps[i].Q(), EndConfiguration: reached.Q()})
dist := mp.planOpts.DistanceFunc(&ik.Segment{StartConfiguration: inputSteps[i].Q(), EndConfiguration: reached.Q()})
if dist < mp.planOpts.JointSolveDist {
for _, hitCorner := range hitCorners {
hitCorner.SetCorner(false)
Expand Down
5 changes: 3 additions & 2 deletions motionplan/cBiRRT_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"go.viam.com/test"
"go.viam.com/utils"

"go.viam.com/rdk/motionplan/ik"
"go.viam.com/rdk/referenceframe"
"go.viam.com/rdk/spatialmath"
rutils "go.viam.com/rdk/utils"
Expand Down Expand Up @@ -38,7 +39,7 @@ func TestSimpleLinearMotion(t *testing.T) {
goalPos := spatialmath.NewPose(r3.Vector{X: 206, Y: 100, Z: 120.5}, &spatialmath.OrientationVectorDegrees{OY: -1})

opt := newBasicPlannerOptions(m)
opt.SetGoalMetric(NewSquaredNormMetric(goalPos))
opt.SetGoalMetric(ik.NewSquaredNormMetric(goalPos))
mp, err := newCBiRRTMotionPlanner(m, rand.New(rand.NewSource(42)), logger, opt)
test.That(t, err, test.ShouldBeNil)
cbirrt, _ := mp.(*cBiRRTMotionPlanner)
Expand Down Expand Up @@ -80,7 +81,7 @@ func TestSimpleLinearMotion(t *testing.T) {
cbirrt.constrainedExtend(ctx, cbirrt.randseed, goalMap, near2, seedReached, m1chan)
})
goalReached := <-m1chan
dist := opt.DistanceFunc(&Segment{StartConfiguration: seedReached.Q(), EndConfiguration: goalReached.Q()})
dist := opt.DistanceFunc(&ik.Segment{StartConfiguration: seedReached.Q(), EndConfiguration: goalReached.Q()})
test.That(t, dist < cbirrt.planOpts.JointSolveDist, test.ShouldBeTrue)

seedReached.SetCorner(true)
Expand Down
Loading