Skip to content

Commit 424c284

Browse files
authored
don't repeat work a little cleaner (#5762)
1 parent 36dc8e2 commit 424c284

File tree

2 files changed

+54
-46
lines changed

2 files changed

+54
-46
lines changed

motionplan/armplanning/linearized_frame_system.go

Lines changed: 40 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -9,31 +9,38 @@ import (
99
"go.viam.com/rdk/referenceframe"
1010
)
1111

12-
// return is floats from [0-1] given a percentage of their input range that should be searched
13-
// for example, if the frame system has 2 arms, and only is moving, the inputs for the non-moving arm will all be 0
14-
// the other arm will be scaled 0-1 based on the expected joint distance
15-
// there is a chacne it's not enough and will need be moved more.
16-
func inputChangeRatio(
12+
// searchHeadroom is the multiplier applied to raw joint sensitivity ratios when computing
13+
// search ranges. A value of 5 means we search 5x the estimated joint range needed to reach
14+
// the goal, providing headroom for the non-linearity of the distance function.
15+
const searchHeadroom = 5.0
16+
17+
// computeJointSensitivities returns a per-joint sensitivity ratio indicating what fraction of each
18+
// joint's total range is estimated to be needed to reach the goal. Non-moving joints are indicated
19+
// with a value of -1. Use clampSensitivities to convert raw ratios into usable search bounds.
20+
//
21+
// For each moving joint, a small perturbation (1% of range) is applied to estimate sensitivity.
22+
func computeJointSensitivities(
1723
mc *motionChains,
1824
startNotMine *referenceframe.LinearInputs,
1925
frameSystem *referenceframe.FrameSystem,
2026
distanceFunc motionplan.StateFSMetric,
21-
minJog float64,
2227
logger logging.Logger,
2328
) ([]float64, error) {
2429
inputsSchema, err := startNotMine.GetSchema(frameSystem)
2530
if err != nil {
2631
return nil, err
2732
}
2833

29-
ratios := []float64{}
34+
rawRatios := []float64{}
3035

3136
// Sorry for the hacky copy.
3237
start := startNotMine.ToFrameSystemInputs().ToLinearInputs()
3338
_, nonmoving := mc.framesFilteredByMovingAndNonmoving()
3439
startDistance := distanceFunc(&motionplan.StateFS{Configuration: startNotMine, FS: mc.fs})
3540
logger.Debugf("startDistance: %0.2f", startDistance)
3641

42+
const percentJog = 0.01
43+
3744
for _, frameName := range inputsSchema.FrameNamesInOrder() {
3845
frame := frameSystem.Frame(frameName)
3946
if len(frame.DoF()) == 0 {
@@ -44,22 +51,22 @@ func inputChangeRatio(
4451
if slices.Contains(nonmoving, frame.Name()) {
4552
// Frames that can move, but we are not moving them to solve this problem.
4653
for range frame.DoF() {
47-
ratios = append(ratios, 0)
54+
rawRatios = append(rawRatios, -1)
4855
}
4956
continue
5057
}
51-
const percentJog = 0.01
5258

5359
// For each degree of freedom, we want to determine how much impact a small change
5460
// makes. For cases where a small movement results in a big change in distance, we want to
5561
// walk in smaller steps. For cases where a small change has a small effect, we want to
5662
// allow the walking algorithm to take bigger steps.
5763
for idx := range frame.DoF() {
64+
linearIdx := len(rawRatios)
5865
orig := start.Get(frame.Name())[idx]
5966

6067
// Compute the new input for a specific joint that's one "jog" away. E.g: ~5 degrees for
6168
// a rotational joint.
62-
y := inputsSchema.Jog(len(ratios), orig, percentJog)
69+
y := inputsSchema.Jog(linearIdx, orig, percentJog)
6370

6471
// Update the copied joint set in place. This is undone at the end of the loop.
6572
start.Get(frame.Name())[idx] = y
@@ -69,28 +76,38 @@ func inputChangeRatio(
6976
// the ratio.
7077
//
7178
// Note that Go deals with the potential divide by 0. Representing `thisRatio` as
72-
// infinite. The following comparisons continue to work as expected. Resulting in an
73-
// adjusted jog ratio of 1.
79+
// infinite. The following comparisons continue to work as expected.
7480
thisRatio := startDistance / math.Abs(myDistance-startDistance)
7581
myJogRatio := percentJog * thisRatio
76-
// For movable frames/joints, 0.03 is the actual smallest value we'll use.
77-
adjustedJogRatio := min(1, max(minJog, (myJogRatio*5)))
78-
79-
if math.IsNaN(adjustedJogRatio) {
80-
adjustedJogRatio = 1
81-
}
8282

83-
logger.Debugf("idx: %d myDistance: %0.2f thisRatio: %0.3f myJogRatio: %0.3f adjustJogRatio: %0.3f",
84-
idx, myDistance, thisRatio, myJogRatio, adjustedJogRatio)
83+
logger.Debugf("idx: %d myDistance: %0.2f thisRatio: %0.3f myJogRatio: %0.3f",
84+
linearIdx, myDistance, thisRatio, myJogRatio)
8585

86-
ratios = append(ratios, adjustedJogRatio)
86+
rawRatios = append(rawRatios, myJogRatio)
8787

8888
// Undo the above modification. Returning `start` back to its original state.
8989
start.Get(frame.Name())[idx] = orig
9090
}
9191
}
9292

93-
logger.Debugf("inputChangeRatio result: %v", ratios)
93+
logger.Debugf("computeJointSensitivities result: %v", rawRatios)
94+
return rawRatios, nil
95+
}
9496

95-
return ratios, nil
97+
// clampSensitivities converts raw per-joint sensitivity ratios (from computeJointSensitivities)
98+
// into bounded search range ratios. Non-moving joints (indicated by -1) are set to 0.
99+
// Moving joints are scaled by searchHeadroom and clamped to [minJog, 1.0].
100+
func clampSensitivities(rawRatios []float64, minJog float64) []float64 {
101+
ratios := make([]float64, len(rawRatios))
102+
for i, raw := range rawRatios {
103+
if raw < 0 {
104+
// Non-moving joint sentinel.
105+
continue
106+
}
107+
ratios[i] = min(1, max(minJog, raw*searchHeadroom))
108+
if math.IsNaN(ratios[i]) {
109+
ratios[i] = 1
110+
}
111+
}
112+
return ratios
96113
}

motionplan/armplanning/node.go

Lines changed: 14 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -157,34 +157,23 @@ func newSolutionSolvingState(ctx context.Context, psc *planSegmentContext, logge
157157
sss.linearSeeds = [][]float64{psc.start.GetLinearizedInputs()}
158158
sss.seedLimits = [][]referenceframe.Limit{psc.pc.lis.GetLimits()}
159159

160-
ratios, minRatio, err := sss.computeGoodCost(psc.goal)
160+
rawRatios, minRatio, err := sss.computeGoodCost(psc.goal)
161161
if err != nil {
162162
return nil, err
163163
}
164164

165165
sss.linearSeeds = append(sss.linearSeeds, sss.linearSeeds[0])
166-
sss.seedLimits = append(sss.seedLimits, ik.ComputeAdjustLimitsArray(sss.linearSeeds[0], sss.seedLimits[0], ratios))
166+
sss.seedLimits = append(sss.seedLimits,
167+
ik.ComputeAdjustLimitsArray(sss.linearSeeds[0], sss.seedLimits[0], clampSensitivities(rawRatios, .03)))
167168

168-
{
169-
ratios, err := inputChangeRatio(sss.psc.motionChains, sss.psc.start, sss.psc.pc.fs,
170-
sss.psc.pc.planOpts.getGoalMetric(psc.goal), .25, sss.logger)
171-
if err != nil {
172-
return nil, err
173-
}
169+
sss.linearSeeds = append(sss.linearSeeds, sss.linearSeeds[0])
170+
sss.seedLimits = append(sss.seedLimits,
171+
ik.ComputeAdjustLimitsArray(sss.linearSeeds[0], sss.seedLimits[0], clampSensitivities(rawRatios, .25)))
174172

173+
if len(rawRatios) > 6 { // for multi-arms, add a seed that moves just the moving arms with complete freedom
175174
sss.linearSeeds = append(sss.linearSeeds, sss.linearSeeds[0])
176-
sss.seedLimits = append(sss.seedLimits, ik.ComputeAdjustLimitsArray(sss.linearSeeds[0], sss.seedLimits[0], ratios))
177-
178-
if len(ratios) > 6 { // for multi-arms, add a seed that moves just the moving arms with complete freedom
179-
ratios, err := inputChangeRatio(sss.psc.motionChains, sss.psc.start, sss.psc.pc.fs,
180-
sss.psc.pc.planOpts.getGoalMetric(psc.goal), 1, sss.logger)
181-
if err != nil {
182-
return nil, err
183-
}
184-
185-
sss.linearSeeds = append(sss.linearSeeds, sss.linearSeeds[0])
186-
sss.seedLimits = append(sss.seedLimits, ik.ComputeAdjustLimitsArray(sss.linearSeeds[0], sss.seedLimits[0], ratios))
187-
}
175+
sss.seedLimits = append(sss.
176+
seedLimits, ik.ComputeAdjustLimitsArray(sss.linearSeeds[0], sss.seedLimits[0], clampSensitivities(rawRatios, 1)))
188177
}
189178

190179
if sss.goodCost > 1 && minRatio > .05 {
@@ -223,12 +212,14 @@ func newSolutionSolvingState(ctx context.Context, psc *planSegmentContext, logge
223212
}
224213

225214
func (sss *solutionSolvingState) computeGoodCost(goal referenceframe.FrameSystemPoses) ([]float64, float64, error) {
226-
ratios, err := inputChangeRatio(sss.psc.motionChains, sss.psc.start, sss.psc.pc.fs,
227-
sss.psc.pc.planOpts.getGoalMetric(goal), .03, sss.logger)
215+
rawRatios, err := computeJointSensitivities(sss.psc.motionChains, sss.psc.start, sss.psc.pc.fs,
216+
sss.psc.pc.planOpts.getGoalMetric(goal), sss.logger)
228217
if err != nil {
229218
return nil, 1, err
230219
}
231220

221+
ratios := clampSensitivities(rawRatios, .03)
222+
232223
minRatio := 1.0
233224

234225
adjusted := []float64{}
@@ -250,7 +241,7 @@ func (sss *solutionSolvingState) computeGoodCost(goal referenceframe.FrameSystem
250241

251242
sss.goodCost = sss.psc.pc.configurationDistanceFunc(stepArc)
252243
sss.logger.Debugf("goodCost: %0.2f minRatio: %0.2f", sss.goodCost, minRatio)
253-
return ratios, minRatio, nil
244+
return rawRatios, minRatio, nil
254245
}
255246

256247
// return bool is if we should stop because we're done.

0 commit comments

Comments
 (0)