Skip to content

Commit 32c9ebe

Browse files
authored
RSDK-4051 tp space should be able to continuously sample ptgs (#2792)
1 parent 4bff6b8 commit 32c9ebe

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+2102
-910
lines changed

components/base/kinematicbase/differentialDrive.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import (
1313
utils "go.viam.com/utils"
1414

1515
"go.viam.com/rdk/components/base"
16-
"go.viam.com/rdk/motionplan"
16+
"go.viam.com/rdk/motionplan/ik"
1717
"go.viam.com/rdk/referenceframe"
1818
"go.viam.com/rdk/services/motion"
1919
"go.viam.com/rdk/spatialmath"
@@ -186,7 +186,7 @@ func (ddk *differentialDriveKinematics) GoToInputs(ctx context.Context, desired
186186
if prevInputs == nil {
187187
prevInputs = currentInputs
188188
}
189-
positionChange := motionplan.L2InputMetric(&motionplan.Segment{
189+
positionChange := ik.L2InputMetric(&ik.Segment{
190190
StartConfiguration: prevInputs,
191191
EndConfiguration: currentInputs,
192192
})

components/base/kinematicbase/ptgKinematics.go

+10-9
Original file line numberDiff line numberDiff line change
@@ -74,11 +74,12 @@ func wrapWithPTGKinematics(
7474
return nil, err
7575
}
7676

77-
frame, err := referenceframe.NewPTGFrameFromTurningRadius(
77+
frame, err := tpspace.NewPTGFrameFromTurningRadius(
7878
b.Name().ShortName(),
79+
logger,
7980
baseMillimetersPerSecond,
8081
baseTurningRadius,
81-
0, // pass 0 to use the default
82+
0, // pass 0 to use the default refDist
8283
geometries,
8384
)
8485
if err != nil {
@@ -122,15 +123,15 @@ func (ptgk *ptgBaseKinematics) GoToInputs(ctx context.Context, inputs []referenc
122123
ptgk.logger.Debugf("GoToInputs going to %v", inputs)
123124

124125
selectedPTG := ptgk.ptgs[int(math.Round(inputs[ptgIndex].Value))]
125-
selectedTraj := selectedPTG.Trajectory(uint(math.Round(inputs[trajectoryIndexWithinPTG].Value)))
126+
selectedTraj, err := selectedPTG.Trajectory(inputs[trajectoryIndexWithinPTG].Value, inputs[distanceAlongTrajectoryIndex].Value)
127+
if err != nil {
128+
return multierr.Combine(err, ptgk.Base.Stop(ctx, nil))
129+
}
126130

127131
lastTime := 0.
128-
for i, trajNode := range selectedTraj {
129-
if trajNode.Dist > inputs[distanceAlongTrajectoryIndex].Value {
130-
ptgk.logger.Debugf("finished executing trajectory after %d steps", i)
131-
// We have reached the desired distance along the given trajectory
132-
break
133-
}
132+
for _, trajNode := range selectedTraj {
133+
// TODO: Most trajectories update their velocities infrequently, or sometimes never.
134+
// This function could be improved by looking ahead through the trajectory and minimizing the amount of SetVelocity calls.
134135
timestep := time.Duration((trajNode.Time-lastTime)*1000*1000) * time.Microsecond
135136
lastTime = trajNode.Time
136137
linVel := r3.Vector{0, trajNode.LinVelMMPS, 0}

components/base/kinematicbase/ptgKinematics_test.go

+50
Original file line numberDiff line numberDiff line change
@@ -51,3 +51,53 @@ func TestPTGKinematics(t *testing.T) {
5151
test.That(t, err, test.ShouldBeNil)
5252
test.That(t, plan, test.ShouldNotBeNil)
5353
}
54+
55+
func TestPTGKinematicsWithGeom(t *testing.T) {
56+
logger := golog.NewTestLogger(t)
57+
58+
name, err := resource.NewFromString("is:a:fakebase")
59+
test.That(t, err, test.ShouldBeNil)
60+
61+
baseGeom, err := spatialmath.NewBox(spatialmath.NewZeroPose(), r3.Vector{1, 1, 1}, "")
62+
test.That(t, err, test.ShouldBeNil)
63+
64+
b := &fake.Base{
65+
Named: name.AsNamed(),
66+
Geometry: []spatialmath.Geometry{baseGeom},
67+
WidthMeters: 0.2,
68+
TurningRadius: 0.3,
69+
}
70+
71+
ctx := context.Background()
72+
73+
kbOpt := NewKinematicBaseOptions()
74+
kbOpt.AngularVelocityDegsPerSec = 0
75+
kb, err := WrapWithKinematics(ctx, b, logger, nil, nil, kbOpt)
76+
test.That(t, err, test.ShouldBeNil)
77+
test.That(t, kb, test.ShouldNotBeNil)
78+
ptgBase, ok := kb.(*ptgBaseKinematics)
79+
test.That(t, ok, test.ShouldBeTrue)
80+
test.That(t, ptgBase, test.ShouldNotBeNil)
81+
82+
dstPIF := referenceframe.NewPoseInFrame(referenceframe.World, spatialmath.NewPoseFromPoint(r3.Vector{X: 2000, Y: 0, Z: 0}))
83+
84+
fs := referenceframe.NewEmptyFrameSystem("test")
85+
f := kb.Kinematics()
86+
test.That(t, err, test.ShouldBeNil)
87+
fs.AddFrame(f, fs.World())
88+
inputMap := referenceframe.StartPositions(fs)
89+
90+
obstacle, err := spatialmath.NewBox(spatialmath.NewPoseFromPoint(r3.Vector{1000, 0, 0}), r3.Vector{1, 1, 1}, "")
91+
test.That(t, err, test.ShouldBeNil)
92+
93+
geoms := []spatialmath.Geometry{obstacle}
94+
worldState, err := referenceframe.NewWorldState(
95+
[]*referenceframe.GeometriesInFrame{referenceframe.NewGeometriesInFrame(referenceframe.World, geoms)},
96+
nil,
97+
)
98+
test.That(t, err, test.ShouldBeNil)
99+
100+
plan, err := motionplan.PlanMotion(ctx, logger, dstPIF, f, inputMap, fs, worldState, nil, nil)
101+
test.That(t, err, test.ShouldBeNil)
102+
test.That(t, plan, test.ShouldNotBeNil)
103+
}

motionplan/cBiRRT.go

+24-23
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"github.com/edaniels/golog"
1414
"go.viam.com/utils"
1515

16+
"go.viam.com/rdk/motionplan/ik"
1617
"go.viam.com/rdk/referenceframe"
1718
"go.viam.com/rdk/spatialmath"
1819
)
@@ -55,7 +56,7 @@ func newCbirrtOptions(planOpts *plannerOptions) (*cbirrtOptions, error) {
5556
// https://ieeexplore.ieee.org/document/5152399/
5657
type cBiRRTMotionPlanner struct {
5758
*planner
58-
fastGradDescent *NloptIK
59+
fastGradDescent *ik.NloptIK
5960
algOpts *cbirrtOptions
6061
}
6162

@@ -74,7 +75,7 @@ func newCBiRRTMotionPlanner(
7475
return nil, err
7576
}
7677
// nlopt should try only once
77-
nlopt, err := CreateNloptIKSolver(frame, logger, 1, opt.GoalThreshold)
78+
nlopt, err := ik.CreateNloptIKSolver(frame, logger, 1, true)
7879
if err != nil {
7980
return nil, err
8081
}
@@ -132,7 +133,7 @@ func (mp *cBiRRTMotionPlanner) rrtBackgroundRunner(
132133
rrt.maps = planSeed.maps
133134
}
134135
mp.logger.Infof("goal node: %v\n", rrt.maps.optNode.Q())
135-
target := referenceframe.InterpolateInputs(seed, rrt.maps.optNode.Q(), 0.5)
136+
target := newConfigurationNode(referenceframe.InterpolateInputs(seed, rrt.maps.optNode.Q(), 0.5))
136137

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

@@ -163,13 +164,13 @@ func (mp *cBiRRTMotionPlanner) rrtBackgroundRunner(
163164
default:
164165
}
165166

166-
tryExtend := func(target []referenceframe.Input) (node, node, error) {
167+
tryExtend := func(target node) (node, node, error) {
167168
// attempt to extend maps 1 and 2 towards the target
168169
utils.PanicCapturingGo(func() {
169-
m1chan <- nm1.nearestNeighbor(nmContext, mp.planOpts, newConfigurationNode(target), map1)
170+
m1chan <- nm1.nearestNeighbor(nmContext, mp.planOpts, target, map1)
170171
})
171172
utils.PanicCapturingGo(func() {
172-
m2chan <- nm2.nearestNeighbor(nmContext, mp.planOpts, newConfigurationNode(target), map2)
173+
m2chan <- nm2.nearestNeighbor(nmContext, mp.planOpts, target, map2)
173174
})
174175
nearest1 := <-m1chan
175176
nearest2 := <-m2chan
@@ -186,10 +187,10 @@ func (mp *cBiRRTMotionPlanner) rrtBackgroundRunner(
186187
rseed2 := rand.New(rand.NewSource(int64(mp.randseed.Int())))
187188

188189
utils.PanicCapturingGo(func() {
189-
mp.constrainedExtend(ctx, rseed1, map1, nearest1, newConfigurationNode(target), m1chan)
190+
mp.constrainedExtend(ctx, rseed1, map1, nearest1, target, m1chan)
190191
})
191192
utils.PanicCapturingGo(func() {
192-
mp.constrainedExtend(ctx, rseed2, map2, nearest2, newConfigurationNode(target), m2chan)
193+
mp.constrainedExtend(ctx, rseed2, map2, nearest2, target, m2chan)
193194
})
194195
map1reached := <-m1chan
195196
map2reached := <-m2chan
@@ -206,24 +207,24 @@ func (mp *cBiRRTMotionPlanner) rrtBackgroundRunner(
206207
return
207208
}
208209

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

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

222223
// Solved!
223224
if reachedDelta <= mp.planOpts.JointSolveDist {
224225
mp.logger.Debugf("CBiRRT found solution after %d iterations", i)
225226
cancel()
226-
path := extractPath(rrt.maps.startMap, rrt.maps.goalMap, &nodePair{map1reached, map2reached})
227+
path := extractPath(rrt.maps.startMap, rrt.maps.goalMap, &nodePair{map1reached, map2reached}, true)
227228
rrt.solutionChan <- &rrtPlanReturn{steps: path, maps: rrt.maps}
228229
return
229230
}
@@ -268,8 +269,8 @@ func (mp *cBiRRTMotionPlanner) constrainedExtend(
268269
default:
269270
}
270271

271-
dist := mp.planOpts.DistanceFunc(&Segment{StartConfiguration: near.Q(), EndConfiguration: target.Q()})
272-
oldDist := mp.planOpts.DistanceFunc(&Segment{StartConfiguration: oldNear.Q(), EndConfiguration: target.Q()})
272+
dist := mp.planOpts.DistanceFunc(&ik.Segment{StartConfiguration: near.Q(), EndConfiguration: target.Q()})
273+
oldDist := mp.planOpts.DistanceFunc(&ik.Segment{StartConfiguration: oldNear.Q(), EndConfiguration: target.Q()})
273274
switch {
274275
case dist < mp.planOpts.JointSolveDist:
275276
mchan <- near
@@ -286,7 +287,7 @@ func (mp *cBiRRTMotionPlanner) constrainedExtend(
286287
newNear = mp.constrainNear(ctx, randseed, oldNear.Q(), newNear)
287288

288289
if newNear != nil {
289-
nearDist := mp.planOpts.DistanceFunc(&Segment{StartConfiguration: oldNear.Q(), EndConfiguration: newNear})
290+
nearDist := mp.planOpts.DistanceFunc(&ik.Segment{StartConfiguration: oldNear.Q(), EndConfiguration: newNear})
290291
if nearDist < math.Pow(mp.planOpts.JointSolveDist, 3) {
291292
if !doubled {
292293
doubled = true
@@ -343,7 +344,7 @@ func (mp *cBiRRTMotionPlanner) constrainNear(
343344
return nil
344345
}
345346

346-
newArc := &Segment{
347+
newArc := &ik.Segment{
347348
StartPosition: seedPos,
348349
EndPosition: goalPos,
349350
StartConfiguration: seedInputs,
@@ -356,29 +357,29 @@ func (mp *cBiRRTMotionPlanner) constrainNear(
356357
if ok {
357358
return target
358359
}
359-
solutionGen := make(chan []referenceframe.Input, 1)
360+
solutionGen := make(chan *ik.Solution, 1)
360361
// Spawn the IK solver to generate solutions until done
361362
err = mp.fastGradDescent.Solve(ctx, solutionGen, target, mp.planOpts.pathMetric, randseed.Int())
362363
// We should have zero or one solutions
363-
var solved []referenceframe.Input
364+
var solved *ik.Solution
364365
select {
365366
case solved = <-solutionGen:
366367
default:
367368
}
368369
close(solutionGen)
369-
if err != nil {
370+
if err != nil || solved == nil {
370371
return nil
371372
}
372373

373374
ok, failpos := mp.planOpts.CheckSegmentAndStateValidity(
374-
&Segment{StartConfiguration: seedInputs, EndConfiguration: solved, Frame: mp.frame},
375+
&ik.Segment{StartConfiguration: seedInputs, EndConfiguration: solved.Configuration, Frame: mp.frame},
375376
mp.planOpts.Resolution,
376377
)
377378
if ok {
378-
return solved
379+
return solved.Configuration
379380
}
380381
if failpos != nil {
381-
dist := mp.planOpts.DistanceFunc(&Segment{StartConfiguration: target, EndConfiguration: failpos.EndConfiguration})
382+
dist := mp.planOpts.DistanceFunc(&ik.Segment{StartConfiguration: target, EndConfiguration: failpos.EndConfiguration})
382383
if dist > mp.planOpts.JointSolveDist {
383384
// If we have a first failing position, and that target is updating (no infinite loop), then recurse
384385
seedInputs = failpos.StartConfiguration
@@ -439,7 +440,7 @@ func (mp *cBiRRTMotionPlanner) smoothPath(
439440
// Note this could technically replace paths with "longer" paths i.e. with more waypoints.
440441
// However, smoothed paths are invariably more intuitive and smooth, and lend themselves to future shortening,
441442
// so we allow elongation here.
442-
dist := mp.planOpts.DistanceFunc(&Segment{StartConfiguration: inputSteps[i].Q(), EndConfiguration: reached.Q()})
443+
dist := mp.planOpts.DistanceFunc(&ik.Segment{StartConfiguration: inputSteps[i].Q(), EndConfiguration: reached.Q()})
443444
if dist < mp.planOpts.JointSolveDist {
444445
for _, hitCorner := range hitCorners {
445446
hitCorner.SetCorner(false)

motionplan/cBiRRT_test.go

+3-2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"go.viam.com/test"
1111
"go.viam.com/utils"
1212

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

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

8687
seedReached.SetCorner(true)

0 commit comments

Comments
 (0)