-
Notifications
You must be signed in to change notification settings - Fork 110
/
kinematic.go
111 lines (95 loc) · 3.27 KB
/
kinematic.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
package motionplan
import (
"math"
"strings"
"github.com/pkg/errors"
pb "go.viam.com/api/component/arm/v1"
"gonum.org/v1/gonum/floats"
"gonum.org/v1/gonum/num/quat"
"go.viam.com/rdk/referenceframe"
"go.viam.com/rdk/spatialmath"
)
// ComputePosition takes a model and a protobuf JointPositions in degrees and returns the cartesian position of the
// end effector as a protobuf ArmPosition. This is performed statelessly without changing any data.
func ComputePosition(model referenceframe.Frame, joints *pb.JointPositions) (spatialmath.Pose, error) {
if len(joints.Values) != len(model.DoF()) {
return nil, errors.Errorf(
"incorrect number of joints passed to ComputePosition. Want: %d, got: %d",
len(model.DoF()),
len(joints.Values),
)
}
pose, err := model.Transform(model.InputFromProtobuf(joints))
if err != nil {
return nil, err
}
return pose, nil
}
// ComputeOOBPosition takes a model and a protobuf JointPositions in degrees and returns the cartesian
// position of the end effector as a protobuf ArmPosition even when the arm is in an out of bounds state.
// This is performed statelessly without changing any data.
func ComputeOOBPosition(model referenceframe.Frame, joints *pb.JointPositions) (spatialmath.Pose, error) {
if joints == nil {
return nil, referenceframe.ErrNilJointPositions
}
if model == nil {
return nil, referenceframe.ErrNilModelFrame
}
if len(joints.Values) != len(model.DoF()) {
return nil, errors.Errorf(
"incorrect number of joints passed to ComputePosition. Want: %d, got: %d",
len(model.DoF()),
len(joints.Values),
)
}
pose, err := model.Transform(model.InputFromProtobuf(joints))
if err != nil && !strings.Contains(err.Error(), referenceframe.OOBErrString) {
return nil, err
}
return pose, nil
}
// deriv will compute D(q), the derivative of q = e^w with respect to w
// Note that for prismatic joints, this will need to be expanded to dual quaternions.
func deriv(q quat.Number) []quat.Number {
w := quat.Log(q)
qNorm := math.Sqrt(w.Imag*w.Imag + w.Jmag*w.Jmag + w.Kmag*w.Kmag)
// qNorm hits a singularity every 2pi
// But if we flip the axis we get the same rotation but away from a singularity
var quatD []quat.Number
// qNorm is non-zero if our joint has a non-zero rotation
if qNorm > 0 {
b := math.Sin(qNorm) / qNorm
c := (math.Cos(qNorm) / (qNorm * qNorm)) - (math.Sin(qNorm) / (qNorm * qNorm * qNorm))
quatD = append(quatD, quat.Number{
Real: -1 * w.Imag * b,
Imag: b + w.Imag*w.Imag*c,
Jmag: w.Imag * w.Jmag * c,
Kmag: w.Imag * w.Kmag * c,
})
quatD = append(quatD, quat.Number{
Real: -1 * w.Jmag * b,
Imag: w.Jmag * w.Imag * c,
Jmag: b + w.Jmag*w.Jmag*c,
Kmag: w.Jmag * w.Kmag * c,
})
quatD = append(quatD, quat.Number{
Real: -1 * w.Kmag * b,
Imag: w.Kmag * w.Imag * c,
Jmag: w.Kmag * w.Jmag * c,
Kmag: b + w.Kmag*w.Kmag*c,
})
} else {
quatD = append(quatD, quat.Number{0, 1, 0, 0})
quatD = append(quatD, quat.Number{0, 0, 1, 0})
quatD = append(quatD, quat.Number{0, 0, 0, 1})
}
return quatD
}
// L2Distance returns the L2 normalized difference between two equal length arrays.
func L2Distance(q1, q2 []float64) float64 {
for i := 0; i < len(q1); i++ {
q1[i] -= q2[i]
}
// 2 is the L value returning a standard L2 Normalization
return floats.Norm(q1, 2)
}