-
Notifications
You must be signed in to change notification settings - Fork 84
Expand file tree
/
Copy pathLinkage.js
More file actions
290 lines (249 loc) · 9.09 KB
/
Linkage.js
File metadata and controls
290 lines (249 loc) · 9.09 KB
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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
/* * * * *
..................
LINKAGE
..................
p0 *----* p1
\ * p0 = origin / bodyContactPoint
* p2 * p1 = coxiaPoint
| * p2 = femurPoint
* p3 * p3 = tibiaPoint / footTipPoint
* coxiaVector = vector from p0 to p1
localZ * femurVector = vector from p1 to p2
| localY * tibiaVector = vector from p2 to p3
| /
|/
|------ localX * LegPointId = {legId}-{pointId}
2 1 * legId - legName - localXaxisAngle
\ head / * 0 - rightMiddle - 0
*---*---* * 1 - rightFront - 45
/ | \ * 2 - leftFront - 135
/ | \ * 3 - leftMiddle - 180
3 -*-----cog-----*- 0 * 4 - leftBack - 225
\ | / * 5 - rightBack - 315
\ | /
*---*---* ^ hexapodY
/ \ |
4 5 *---> hexapodX
/
* hexapodZ
* localXaxisAngle = angle made by hexapodXaxis and localXaxis
* alpha = angle made by coxia Vector and localXaxis
p2 * beta = angle made by coxiaVector and femurVector
* = angle made by points p2, p1 and pxPrime
/ \
*---*---\---> pxPrime
p0 p1 * p3
p0 p1 * gamma = angle made by vector perpendicular to
*---* coxiaVector and tibiaVector
| \ = angle made by points pzPrime, p1, p3
| \
V * p3
pzPrime
..................
LINKAGE PROPERTIES
..................
{} this.dimensions: { coxia, femur, tibia }
{} this.pose: { alpha, beta, gamma }
"" this.position: "rightMiddle" from POSITION_NAMES_LIST or "linkage-position-not-defined"
[] this.allPointsList: A list pointing to each of the four points in the map
which the first element being the bodyContactPoint, the last element being the footTipPoint
[
{x, y, z, id: "5-0", name: "rightBack-bodyContactPoint"},
{x, y, z, id: "5-1", name: "rightBack-coxiaPoint"},
{x, y, z, id: "5-2", name: "rightBack-femurPoint"},
{x, y, z, id: "5-3", name: "rightBack-footTipPoint"},
]
each id is prefixed with 5 because the leg point id corresponding to "rightBack"
position is 5.
....................
(linkage derived properties)
....................
{} this.maybeGroundContactPoint: The point which probably is the one in contact
with the ground, but not necessarily the case (no guarantees)
"" this.name: "{position}Leg" e.g. "rightMiddleLeg"
"" this.id : a number from 0 to 5 corresponding to a particular position
* * * * */
import { tRotYmatrix, tRotZmatrix, multiply4x4 } from "./geometry"
import {
LEG_POINT_TYPES_LIST,
POSITION_NAME_TO_ID_MAP,
POSITION_NAME_TO_AXIS_ANGLE_MAP,
} from "./constants"
import Vector from "./Vector"
class Linkage {
constructor(
dimensions,
position,
originPoint = { x: 0, y: 0, z: 0 },
pose = { alpha: 0, beta: 0, gamma: 0 },
flags = { hasNoPoints: false }
) {
Object.assign(this, { dimensions, pose, position })
if (flags.hasNoPoints) {
return
}
this.allPointsList = this._computePoints(pose, originPoint)
}
get bodyContactPoint() {
return this.allPointsList[0]
}
get coxiaPoint() {
return this.allPointsList[1]
}
get femurPoint() {
return this.allPointsList[2]
}
get footTipPoint() {
return this.allPointsList[3]
}
get id() {
return POSITION_NAME_TO_ID_MAP[this.position]
}
get name() {
return `${this.position}Leg`
}
get maybeGroundContactPoint() {
const reversedList = this.allPointsList.slice().reverse()
const testPoint = reversedList[0]
const maybeGroundContactPoint = reversedList.reduce(
(testPoint, point) => (point.z < testPoint.z ? point : testPoint),
testPoint
)
return maybeGroundContactPoint
}
/* *
* .............
* clone (translate) rotate shift cloneTrotShift
* .............
*
* params type:
* matrix: 4x4 matrix
* tx, ty, tz: numbers
*
* Return a copy of the leg with the same properties
* except all the points are rotated and shifted
* given the transformation matrix (4x4 matrix) and tx, ty, tz
* Note: The transformation matrix can translate the leg
* if the last column of of the matrix have non-zero elements
* and again be translated by tx, ty, tz
* */
cloneTrotShift(transformMatrix, tx, ty, tz) {
return this._doTransform("cloneTrotShift", transformMatrix, tx, ty, tz)
}
cloneTrot(transformMatrix) {
return this._doTransform("cloneTrot", transformMatrix)
}
cloneShift(tx, ty, tz) {
return this._doTransform("cloneShift", tx, ty, tz)
}
_doTransform(transformFunction, ...args) {
const newPointsList = this.allPointsList.map(oldPoint =>
oldPoint[transformFunction](...args)
)
return this._buildClone(newPointsList)
}
_buildClone(allPointsList) {
let clone = new Linkage(
this.dimensions,
this.position,
this.bodyContactPoint,
this.pose,
{ hasNoPoints: true }
)
// override allPointsList of clone
clone.allPointsList = allPointsList
return clone
}
/* *
* .............
* structure of pointNameIds
* .............
*
* pointNameIds = [
* { name: "{legPosition}-bodyContactPoint", id: "{legId}-0" },
* { name: "{legPosition}-coxiaPoint", id: "{legId}-1" },
* { name: "{legPosition}-femurPoint", id: "{legId}-2" },
* { name: "{legPosition}-footTipPoint", id: "{legId}-3" },
* ]
*
* */
_buildNameId = (pointName, id) => ({
name: `${this.position}-${pointName}`,
id: `${this.id}-${id}`,
})
_buildPointNameIds = () =>
LEG_POINT_TYPES_LIST.map((pointType, index) =>
this._buildNameId(pointType, index)
)
/* *
* ................
* STEP 1 of computing points:
* find points wrt body contact point
* ................
* NOTE:
* matrix_ab is the matrix which defines the
* pose of that coordinate system defined by
* matrix_b wrt the coordinate system defined by matrix_a
* matrix_ab is the pose of matrix_b wrt matrix_a
* where pa is the origin of matrix_a
* and pb is the origin of matrix_b wrt pa
*
* */
_computePointsWrtBodyContact(beta, gamma) {
const matrix01 = tRotYmatrix(-beta, this.dimensions.coxia, 0, 0)
const matrix12 = tRotYmatrix(90 - gamma, this.dimensions.femur, 0, 0)
const matrix23 = tRotYmatrix(0, this.dimensions.tibia, 0, 0)
const matrix02 = multiply4x4(matrix01, matrix12)
const matrix03 = multiply4x4(matrix02, matrix23)
const originPoint = new Vector(0, 0, 0)
const localPoints = [
originPoint, // bodyContactPoint
originPoint.cloneTrot(matrix01), // coxiaPoint
originPoint.cloneTrot(matrix02), // femurPoint
originPoint.cloneTrot(matrix03), // footTipPoint
]
return localPoints
}
/* *
* ................
* STEP 2 of computing points:
* find local points wrt hexapod's center of gravity (0, 0, 0)
* ................
* */
_computePointsWrtHexapodCog(alpha, originPoint, localPoints, pointNameIds) {
const zAngle = POSITION_NAME_TO_AXIS_ANGLE_MAP[this.position] + alpha
const twistMatrix = tRotZmatrix(
zAngle,
originPoint.x,
originPoint.y,
originPoint.z
)
const allPointsList = localPoints.map((localPoint, index) => {
const name = pointNameIds[index].name
const id = pointNameIds[index].id
const point = localPoint.newTrot(twistMatrix, name, id)
return point
})
return allPointsList
}
/* *
* Example of allPointsList = [
* {x, y, z, id: "5-0", name: "rightBack-bodyContactPoint"},
* {x, y, z, id: "5-1", name: "rightBack-coxiaPoint"},
* {x, y, z, id: "5-2", name: "rightBack-femurPoint"},
* {x, y, z, id: "5-3", name: "rightBack-footTipPoint"},
* ]
* x, y, z are numbers
* */
_computePoints(pose, originPoint) {
const { alpha, beta, gamma } = pose
const pointNameIds = this._buildPointNameIds()
const localPoints = this._computePointsWrtBodyContact(beta, gamma)
// prettier-ignore
const allPointsList = this._computePointsWrtHexapodCog(
alpha, originPoint, localPoints, pointNameIds
)
return allPointsList
}
}
export default Linkage