example-robot-data-loaders 4.4.0__0-py313-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1295 @@
1
+ import typing
2
+
3
+ import numpy as np
4
+ import pinocchio as pin
5
+
6
+ from .utils import RobotLoader
7
+
8
+
9
+ class HumanLoader(RobotLoader):
10
+ path = "human_description"
11
+ urdf_filename = "human.urdf"
12
+ free_flyer = True
13
+ ref_posture = "anatomical"
14
+ # Enforced, unchangeable free-flyer orientation (90° about X, and Y↔Z swap)
15
+ freeflyer_ori: np.ndarray = np.array(
16
+ [
17
+ [1, 0, 0],
18
+ [0, 0, -1],
19
+ [0, 1, 0],
20
+ ]
21
+ )
22
+
23
+ def __init__(
24
+ self,
25
+ height: typing.Optional[float] = None,
26
+ weight: typing.Optional[float] = None,
27
+ gender: str = "m",
28
+ verbose: bool = False,
29
+ ):
30
+ """
31
+ Initialize the robot loader with optional scaling and configuration parameters. The scaling of the model is based on Dumas 2007, except for the
32
+ abdomen and thorax segments, which are scaled based de Leva 1996.
33
+ Parameters:
34
+ height (float, optional): The height to scale the robot model to. If None, uses the default height in the urdf of 1.80m.
35
+ weight (float, optional): The weight to scale the robot model to. If None, uses the default weight in the urdf 75kg.
36
+ gender (str, optional): The gender specification for scaling, default is 'm'.
37
+ verbose (bool, optional): If True, enables verbose output. Default is False.
38
+ Behavior:
39
+ - Calls the base loader's initializer.
40
+ - If height or weight is provided, scales the robot model accordingly.
41
+ - The free-flyer is present, it enforces its orientation and reapplies joint limits.
42
+ """
43
+
44
+ # call base loader
45
+ super().__init__(verbose=verbose)
46
+
47
+ if height is not None or weight is not None:
48
+ self._scale_pin_model(
49
+ self.robot.model, self.robot.visual_model, height, weight, gender
50
+ )
51
+
52
+ # automatically apply the enforced free-flyer orientation
53
+ if self.free_flyer:
54
+ # get joint index
55
+ j_id = self.robot.model.getJointId("root_joint")
56
+ # assign enforced rotation
57
+ self.robot.model.jointPlacements[j_id].rotation = self.freeflyer_ori
58
+ # re-apply limits
59
+ self.addFreeFlyerJointLimits()
60
+
61
+ @staticmethod
62
+ def get_dict_inertial_param(
63
+ height: float, weight: float, gender: str = "m"
64
+ ) -> dict:
65
+ inertial_segment_names = [
66
+ "middle_pelvis",
67
+ "left_upperleg",
68
+ "left_lowerleg",
69
+ "left_foot",
70
+ "middle_abdomen",
71
+ "middle_thorax",
72
+ "middle_head",
73
+ "left_upperarm",
74
+ "left_lowerarm",
75
+ "left_hand",
76
+ "right_upperarm",
77
+ "right_lowerarm",
78
+ "right_hand",
79
+ "right_upperleg",
80
+ "right_lowerleg",
81
+ "right_foot",
82
+ ]
83
+
84
+ sgt_lengths = {}
85
+
86
+ dicts = []
87
+
88
+ # Ratios from Dumas 2007 and De Leva 1996 for abdomen and thorax
89
+ for name in inertial_segment_names:
90
+ dict_sgmt = {}
91
+ if name == "middle_pelvis":
92
+ length = (0.0634 if gender == "f" else 0.0505) * height
93
+ sgt_lengths["middle_pelvis_0"] = length
94
+ sgt_lengths["middle_pelvis_0_width"] = (
95
+ 0.1478 if gender == "f" else 0.1265
96
+ ) * height
97
+ dict_sgmt["mass"] = np.round(
98
+ (0.146 if gender == "f" else 0.142) * weight, 2
99
+ )
100
+ dict_sgmt["com"] = np.round(
101
+ (
102
+ np.array([-0.009, -0.232, 0.002])
103
+ if gender == "f"
104
+ else np.array([0.028, -0.28, -0.006])
105
+ )
106
+ * length,
107
+ 3,
108
+ )
109
+ dict_sgmt["inertia"] = np.round(
110
+ (
111
+ np.array(
112
+ [
113
+ (length * 0.91) ** 2,
114
+ -((length * 0.34) ** 2),
115
+ -((length * 0.01) ** 2),
116
+ (length * 1) ** 2,
117
+ -((length * 0.01) ** 2),
118
+ (length * 0.79) ** 2,
119
+ ]
120
+ )
121
+ if gender == "f"
122
+ else np.array(
123
+ [
124
+ (length * 1.01) ** 2,
125
+ -((length * 0.25) ** 2),
126
+ -((length * 0.12) ** 2),
127
+ (length * 1.06) ** 2,
128
+ -((length * 0.08) ** 2),
129
+ (length * 0.95) ** 2,
130
+ ]
131
+ )
132
+ )
133
+ * dict_sgmt["mass"],
134
+ 5,
135
+ )
136
+ dicts.append(dict_sgmt)
137
+ elif name == "left_upperleg":
138
+ length = (0.2354 if gender == "f" else 0.2441) * height
139
+ sgt_lengths["left_upperleg_0"] = length
140
+ dict_sgmt["mass"] = np.round(
141
+ (0.146 if gender == "f" else 0.123) * weight, 2
142
+ )
143
+ dict_sgmt["com"] = np.round(
144
+ (
145
+ np.array([-0.077, -0.377, 0.009])
146
+ if gender == "f"
147
+ else np.array([-0.041, -0.429, 0.033])
148
+ )
149
+ * length,
150
+ 3,
151
+ )
152
+ dict_sgmt["inertia"] = np.round(
153
+ (
154
+ np.array(
155
+ [
156
+ (length * 0.31) ** 2,
157
+ (length * 0.07) ** 2,
158
+ -((length * 0.02) ** 2),
159
+ (length * 0.19) ** 2,
160
+ -((length * 0.07) ** 2),
161
+ (length * 0.32) ** 2,
162
+ ]
163
+ )
164
+ if gender == "f"
165
+ else np.array(
166
+ [
167
+ (length * 0.29) ** 2,
168
+ (length * 0.07) ** 2,
169
+ -((length * 0.02) ** 2),
170
+ (length * 0.15) ** 2,
171
+ -((length * 0.07) ** 2),
172
+ (length * 0.3) ** 2,
173
+ ]
174
+ )
175
+ )
176
+ * dict_sgmt["mass"],
177
+ 5,
178
+ )
179
+ dicts.append(dict_sgmt)
180
+ elif name == "left_lowerleg":
181
+ length = (0.2410 if gender == "f" else 0.2446) * height
182
+ sgt_lengths["left_lowerleg_0"] = length
183
+ dict_sgmt["mass"] = np.round(
184
+ (0.045 if gender == "f" else 0.048) * weight, 2
185
+ )
186
+ dict_sgmt["com"] = np.round(
187
+ (
188
+ np.array([-0.049, -0.404, 0.031])
189
+ if gender == "f"
190
+ else np.array([-0.048, -0.41, 0.007])
191
+ )
192
+ * length,
193
+ 3,
194
+ )
195
+ dict_sgmt["inertia"] = np.round(
196
+ (
197
+ np.array(
198
+ [
199
+ (length * 0.28) ** 2,
200
+ (length * 0.02) ** 2,
201
+ (length * 0.01) ** 2,
202
+ (length * 0.1) ** 2,
203
+ (length * 0.06) ** 2,
204
+ (length * 0.28) ** 2,
205
+ ]
206
+ )
207
+ if gender == "f"
208
+ else np.array(
209
+ [
210
+ (length * 0.28) ** 2,
211
+ (length * 0.04) ** 2,
212
+ (length * 0.02) ** 2,
213
+ (length * 0.1) ** 2,
214
+ (length * 0.05) ** 2,
215
+ (length * 0.28) ** 2,
216
+ ]
217
+ )
218
+ )
219
+ * dict_sgmt["mass"],
220
+ 5,
221
+ )
222
+ dicts.append(dict_sgmt)
223
+ elif name == "left_foot":
224
+ length = (0.1447 if gender == "f" else 0.1497) * height
225
+ sgt_lengths["left_foot_0"] = length
226
+ dict_sgmt["mass"] = np.round(
227
+ (0.01 if gender == "f" else 0.012) * weight, 2
228
+ )
229
+ dict_sgmt["com"] = np.round(
230
+ (
231
+ np.array([0.27, -0.218, 0.039])
232
+ if gender == "f"
233
+ else np.array([0.382, -0.151, 0.026])
234
+ )
235
+ * length,
236
+ 3,
237
+ )
238
+ dict_sgmt["inertia"] = np.round(
239
+ (
240
+ np.array(
241
+ [
242
+ (length * 0.17) ** 2,
243
+ -((length * 0.10) ** 2),
244
+ (length * 0.06) ** 2,
245
+ (length * 0.36) ** 2,
246
+ -((length * 0.04) ** 2),
247
+ (length * 0.35) ** 2,
248
+ ]
249
+ )
250
+ if gender == "f"
251
+ else np.array(
252
+ [
253
+ (length * 0.17) ** 2,
254
+ (length * 0.13) ** 2,
255
+ -((length * 0.08) ** 2),
256
+ (length * 0.37) ** 2,
257
+ (length * 0) ** 2,
258
+ (length * 0.36) ** 2,
259
+ ]
260
+ )
261
+ )
262
+ * dict_sgmt["mass"],
263
+ 5,
264
+ )
265
+ dicts.append(dict_sgmt)
266
+ elif name == "middle_abdomen":
267
+ length = (0.1183 if gender == "f" else 0.1237) * height
268
+ sgt_lengths["middle_abdomen_0"] = length
269
+ dict_sgmt["mass"] = np.round(
270
+ (0.1465 if gender == "f" else 0.1633) * weight, 2
271
+ )
272
+ dict_sgmt["com"] = np.round(
273
+ (
274
+ np.array([-0.016, -0.4512, -0.006])
275
+ if gender == "f"
276
+ else np.array([-0.036, 0.4502, -0.002])
277
+ )
278
+ * length,
279
+ 3,
280
+ )
281
+ dict_sgmt["inertia"] = np.round(
282
+ (
283
+ np.array(
284
+ [
285
+ (length * 0.29) ** 2,
286
+ (length * 0.22) ** 2,
287
+ (length * 0.05) ** 2,
288
+ (length * 0.27) ** 2,
289
+ -((length * 0.05) ** 2),
290
+ (length * 0.29) ** 2,
291
+ ]
292
+ )
293
+ if gender == "f"
294
+ else np.array(
295
+ [
296
+ (length * 0.27) ** 2,
297
+ (length * 0.18) ** 2,
298
+ -((length * 0.02) ** 2),
299
+ (length * 0.25) ** 2,
300
+ -((length * 0.04) ** 2),
301
+ (length * 0.28) ** 2,
302
+ ]
303
+ )
304
+ )
305
+ * dict_sgmt["mass"],
306
+ 5,
307
+ )
308
+ dicts.append(dict_sgmt)
309
+ elif name == "middle_thorax":
310
+ length = (0.1314 if gender == "f" else 0.1390) * height
311
+ sgt_lengths["middle_thorax_0"] = length
312
+ dict_sgmt["mass"] = np.round(
313
+ (0.1545 if gender == "f" else 0.1596) * weight, 2
314
+ )
315
+ dict_sgmt["com"] = np.round(
316
+ (
317
+ np.array([-0.016, 0.5050, -0.006])
318
+ if gender == "f"
319
+ else np.array([-0.036, 0.5066, -0.002])
320
+ )
321
+ * length,
322
+ 3,
323
+ )
324
+ dict_sgmt["inertia"] = np.round(
325
+ (
326
+ np.array(
327
+ [
328
+ (length * 0.29) ** 2,
329
+ (length * 0.22) ** 2,
330
+ (length * 0.05) ** 2,
331
+ (length * 0.27) ** 2,
332
+ -((length * 0.05) ** 2),
333
+ (length * 0.29) ** 2,
334
+ ]
335
+ )
336
+ if gender == "f"
337
+ else np.array(
338
+ [
339
+ (length * 0.27) ** 2,
340
+ (length * 0.18) ** 2,
341
+ -((length * 0.02) ** 2),
342
+ (length * 0.25) ** 2,
343
+ -((length * 0.04) ** 2),
344
+ (length * 0.28) ** 2,
345
+ ]
346
+ )
347
+ )
348
+ * dict_sgmt["mass"],
349
+ 5,
350
+ )
351
+ dicts.append(dict_sgmt)
352
+ elif name == "middle_head":
353
+ length = (0.1308 if gender == "f" else 0.1310) * height
354
+ sgt_lengths["middle_head_0"] = length
355
+ dict_sgmt["mass"] = np.round(
356
+ 0.067 * weight, 2
357
+ ) # same for male and female
358
+ dict_sgmt["com"] = np.round(
359
+ (
360
+ np.array([-0.07, 0.597, 0])
361
+ if gender == "f"
362
+ else np.array([-0.062, 0.555, 0.001])
363
+ )
364
+ * length,
365
+ 3,
366
+ )
367
+ dict_sgmt["inertia"] = np.round(
368
+ (
369
+ np.array(
370
+ [
371
+ (length * 0.32) ** 2,
372
+ -((length * 0.06) ** 2),
373
+ (length * 0.01) ** 2,
374
+ (length * 0.27) ** 2,
375
+ -((length * 0.01) ** 2),
376
+ (length * 0.34) ** 2,
377
+ ]
378
+ )
379
+ if gender == "f"
380
+ else np.array(
381
+ [
382
+ (length * 0.31) ** 2,
383
+ -((length * 0.09) ** 2),
384
+ -((length * 0.02) ** 2),
385
+ (length * 0.25) ** 2,
386
+ (length * 0.03) ** 2,
387
+ (length * 0.33) ** 2,
388
+ ]
389
+ )
390
+ )
391
+ * dict_sgmt["mass"],
392
+ 5,
393
+ )
394
+ dicts.append(dict_sgmt)
395
+ elif name == "left_upperarm":
396
+ length = (0.1510 if gender == "f" else 0.1531) * height
397
+ sgt_lengths["left_upperarm_0"] = length
398
+ dict_sgmt["mass"] = np.round(
399
+ (0.022 if gender == "f" else 0.024) * weight, 2
400
+ )
401
+ dict_sgmt["com"] = np.round(
402
+ (
403
+ np.array([-0.073, -0.454, -0.028])
404
+ if gender == "f"
405
+ else np.array([0.017, -0.452, -0.026])
406
+ )
407
+ * length,
408
+ 3,
409
+ )
410
+ dict_sgmt["inertia"] = np.round(
411
+ (
412
+ np.array(
413
+ [
414
+ (length * 0.33) ** 2,
415
+ (length * 0.03) ** 2,
416
+ -((length * 0.05) ** 2),
417
+ (length * 0.17) ** 2,
418
+ -((length * 0.14) ** 2),
419
+ (length * 0.33) ** 2,
420
+ ]
421
+ )
422
+ if gender == "f"
423
+ else np.array(
424
+ [
425
+ (length * 0.31) ** 2,
426
+ (length * 0.06) ** 2,
427
+ (length * 0.05) ** 2,
428
+ (length * 0.14) ** 2,
429
+ (length * 0.02) ** 2,
430
+ (length * 0.32) ** 2,
431
+ ]
432
+ )
433
+ )
434
+ * dict_sgmt["mass"],
435
+ 5,
436
+ )
437
+ dicts.append(dict_sgmt)
438
+ elif name == "left_lowerarm":
439
+ length = (0.1534 if gender == "f" else 0.1593) * height
440
+ sgt_lengths["left_lowerarm_0"] = length
441
+ dict_sgmt["mass"] = np.round(
442
+ (0.013 if gender == "f" else 0.017) * weight, 2
443
+ )
444
+ dict_sgmt["com"] = np.round(
445
+ (
446
+ np.array([0.021, -0.411, 0.019])
447
+ if gender == "f"
448
+ else np.array([0.01, -0.417, 0.014])
449
+ )
450
+ * length,
451
+ 3,
452
+ )
453
+ dict_sgmt["inertia"] = np.round(
454
+ (
455
+ np.array(
456
+ [
457
+ (length * 0.26) ** 2,
458
+ (length * 0.1) ** 2,
459
+ (length * 0.04) ** 2,
460
+ (length * 0.14) ** 2,
461
+ -((length * 0.13) ** 2),
462
+ (length * 0.25) ** 2,
463
+ ]
464
+ )
465
+ if gender == "f"
466
+ else np.array(
467
+ [
468
+ (length * 0.28) ** 2,
469
+ (length * 0.03) ** 2,
470
+ (length * 0.02) ** 2,
471
+ (length * 0.11) ** 2,
472
+ -((length * 0.08) ** 2),
473
+ (length * 0.27) ** 2,
474
+ ]
475
+ )
476
+ )
477
+ * dict_sgmt["mass"],
478
+ 5,
479
+ )
480
+ dicts.append(dict_sgmt)
481
+ elif name == "left_hand":
482
+ length = (0.0989 if gender == "f" else 0.1014) * height
483
+ sgt_lengths["left_hand_0"] = length
484
+ dict_sgmt["mass"] = np.round(
485
+ (0.005 if gender == "f" else 0.006) * weight, 2
486
+ )
487
+ dict_sgmt["com"] = np.round(
488
+ (
489
+ np.array([0.077, -0.768, 0.048])
490
+ if gender == "f"
491
+ else np.array([0.082, -0.839, 0.074])
492
+ )
493
+ * length,
494
+ 3,
495
+ )
496
+ dict_sgmt["inertia"] = np.round(
497
+ (
498
+ np.array(
499
+ [
500
+ (length * 0.63) ** 2,
501
+ (length * 0.29) ** 2,
502
+ (length * 0.23) ** 2,
503
+ (length * 0.43) ** 2,
504
+ -((length * 0.28) ** 2),
505
+ (length * 0.58) ** 2,
506
+ ]
507
+ )
508
+ if gender == "f"
509
+ else np.array(
510
+ [
511
+ (length * 0.61) ** 2,
512
+ (length * 0.22) ** 2,
513
+ (length * 0.15) ** 2,
514
+ (length * 0.38) ** 2,
515
+ -((length * 0.2) ** 2),
516
+ (length * 0.56) ** 2,
517
+ ]
518
+ )
519
+ )
520
+ * dict_sgmt["mass"],
521
+ 5,
522
+ )
523
+ dicts.append(dict_sgmt)
524
+ elif name == "right_upperarm":
525
+ length = (0.1510 if gender == "f" else 0.1531) * height
526
+ sgt_lengths["right_upperarm_0"] = length
527
+ dict_sgmt["mass"] = np.round(
528
+ (0.022 if gender == "f" else 0.024) * weight, 2
529
+ )
530
+ dict_sgmt["com"] = np.round(
531
+ (
532
+ np.array([-0.073, -0.454, -0.028])
533
+ if gender == "f"
534
+ else np.array([0.017, -0.452, -0.026])
535
+ )
536
+ * length,
537
+ 3,
538
+ )
539
+ dict_sgmt["inertia"] = np.round(
540
+ (
541
+ np.array(
542
+ [
543
+ (length * 0.33) ** 2,
544
+ (length * 0.03) ** 2,
545
+ -((length * 0.05) ** 2),
546
+ (length * 0.17) ** 2,
547
+ -((length * 0.14) ** 2),
548
+ (length * 0.33) ** 2,
549
+ ]
550
+ )
551
+ if gender == "f"
552
+ else np.array(
553
+ [
554
+ (length * 0.31) ** 2,
555
+ (length * 0.06) ** 2,
556
+ (length * 0.05) ** 2,
557
+ (length * 0.14) ** 2,
558
+ (length * 0.02) ** 2,
559
+ (length * 0.32) ** 2,
560
+ ]
561
+ )
562
+ )
563
+ * dict_sgmt["mass"],
564
+ 5,
565
+ )
566
+ dicts.append(dict_sgmt)
567
+ elif name == "right_lowerarm":
568
+ length = (0.1534 if gender == "f" else 0.1593) * height
569
+ sgt_lengths["right_lowerarm_0"] = length
570
+ dict_sgmt["mass"] = np.round(
571
+ (0.013 if gender == "f" else 0.017) * weight, 2
572
+ )
573
+ dict_sgmt["com"] = np.round(
574
+ (
575
+ np.array([0.021, -0.411, 0.019])
576
+ if gender == "f"
577
+ else np.array([0.01, -0.417, 0.014])
578
+ )
579
+ * length,
580
+ 3,
581
+ )
582
+ dict_sgmt["inertia"] = np.round(
583
+ (
584
+ np.array(
585
+ [
586
+ (length * 0.26) ** 2,
587
+ (length * 0.1) ** 2,
588
+ (length * 0.04) ** 2,
589
+ (length * 0.14) ** 2,
590
+ -((length * 0.13) ** 2),
591
+ (length * 0.25) ** 2,
592
+ ]
593
+ )
594
+ if gender == "f"
595
+ else np.array(
596
+ [
597
+ (length * 0.28) ** 2,
598
+ (length * 0.03) ** 2,
599
+ (length * 0.02) ** 2,
600
+ (length * 0.11) ** 2,
601
+ -((length * 0.08) ** 2),
602
+ (length * 0.27) ** 2,
603
+ ]
604
+ )
605
+ )
606
+ * dict_sgmt["mass"],
607
+ 5,
608
+ )
609
+ dicts.append(dict_sgmt)
610
+ elif name == "right_hand":
611
+ length = (0.0989 if gender == "f" else 0.1014) * height
612
+ sgt_lengths["right_hand_0"] = length
613
+ dict_sgmt["mass"] = np.round(
614
+ (0.005 if gender == "f" else 0.006) * weight, 2
615
+ )
616
+ dict_sgmt["com"] = np.round(
617
+ (
618
+ np.array([0.077, -0.768, 0.048])
619
+ if gender == "f"
620
+ else np.array([0.082, -0.839, 0.074])
621
+ )
622
+ * length,
623
+ 3,
624
+ )
625
+ dict_sgmt["inertia"] = np.round(
626
+ (
627
+ np.array(
628
+ [
629
+ (length * 0.63) ** 2,
630
+ (length * 0.29) ** 2,
631
+ (length * 0.23) ** 2,
632
+ (length * 0.43) ** 2,
633
+ -((length * 0.28) ** 2),
634
+ (length * 0.58) ** 2,
635
+ ]
636
+ )
637
+ if gender == "f"
638
+ else np.array(
639
+ [
640
+ (length * 0.61) ** 2,
641
+ (length * 0.22) ** 2,
642
+ (length * 0.15) ** 2,
643
+ (length * 0.38) ** 2,
644
+ -((length * 0.2) ** 2),
645
+ (length * 0.56) ** 2,
646
+ ]
647
+ )
648
+ )
649
+ * dict_sgmt["mass"],
650
+ 5,
651
+ )
652
+ dicts.append(dict_sgmt)
653
+ elif name == "right_upperleg":
654
+ length = (0.2354 if gender == "f" else 0.2441) * height
655
+ sgt_lengths["right_upperleg_0"] = length
656
+ dict_sgmt["mass"] = np.round(
657
+ (0.146 if gender == "f" else 0.123) * weight, 2
658
+ )
659
+ dict_sgmt["com"] = np.round(
660
+ (
661
+ np.array([-0.077, -0.377, 0.009])
662
+ if gender == "f"
663
+ else np.array([-0.041, -0.429, 0.033])
664
+ )
665
+ * length,
666
+ 3,
667
+ )
668
+ dict_sgmt["inertia"] = np.round(
669
+ (
670
+ np.array(
671
+ [
672
+ (length * 0.31) ** 2,
673
+ -((length * 0.07) ** 2),
674
+ -((length * 0.02) ** 2),
675
+ (length * 0.19) ** 2,
676
+ -((length * 0.07) ** 2),
677
+ (length * 0.32) ** 2,
678
+ ]
679
+ )
680
+ if gender == "f"
681
+ else np.array(
682
+ [
683
+ (length * 0.29) ** 2,
684
+ -((length * 0.07) ** 2),
685
+ -((length * 0.02) ** 2),
686
+ (length * 0.15) ** 2,
687
+ -((length * 0.07) ** 2),
688
+ (length * 0.3) ** 2,
689
+ ]
690
+ )
691
+ )
692
+ * dict_sgmt["mass"],
693
+ 5,
694
+ )
695
+ dicts.append(dict_sgmt)
696
+ elif name == "right_lowerleg":
697
+ length = (0.2410 if gender == "f" else 0.2446) * height
698
+ sgt_lengths["right_lowerleg_0"] = length
699
+ dict_sgmt["mass"] = np.round(
700
+ (0.045 if gender == "f" else 0.048) * weight, 2
701
+ )
702
+ dict_sgmt["com"] = np.round(
703
+ (
704
+ np.array([-0.049, -0.404, 0.031])
705
+ if gender == "f"
706
+ else np.array([-0.048, -0.41, 0.007])
707
+ )
708
+ * length,
709
+ 3,
710
+ )
711
+ dict_sgmt["inertia"] = np.round(
712
+ (
713
+ np.array(
714
+ [
715
+ (length * 0.28) ** 2,
716
+ (length * 0.02) ** 2,
717
+ (length * 0.01) ** 2,
718
+ (length * 0.1) ** 2,
719
+ (length * 0.06) ** 2,
720
+ (length * 0.28) ** 2,
721
+ ]
722
+ )
723
+ if gender == "f"
724
+ else np.array(
725
+ [
726
+ (length * 0.28) ** 2,
727
+ (length * 0.04) ** 2,
728
+ (length * 0.02) ** 2,
729
+ (length * 0.1) ** 2,
730
+ (length * 0.05) ** 2,
731
+ (length * 0.28) ** 2,
732
+ ]
733
+ )
734
+ )
735
+ * dict_sgmt["mass"],
736
+ 5,
737
+ )
738
+ dicts.append(dict_sgmt)
739
+ elif name == "right_foot":
740
+ length = (0.1447 if gender == "f" else 0.1497) * height
741
+ sgt_lengths["right_foot_0"] = length
742
+ dict_sgmt["mass"] = np.round(
743
+ (0.01 if gender == "f" else 0.012) * weight, 2
744
+ )
745
+ dict_sgmt["com"] = np.round(
746
+ (
747
+ np.array([0.27, -0.218, 0.039])
748
+ if gender == "f"
749
+ else np.array([0.382, -0.151, 0.026])
750
+ )
751
+ * length,
752
+ 3,
753
+ )
754
+ dict_sgmt["inertia"] = np.round(
755
+ (
756
+ np.array(
757
+ [
758
+ (length * 0.17) ** 2,
759
+ -((length * 0.10) ** 2),
760
+ (length * 0.06) ** 2,
761
+ (length * 0.36) ** 2,
762
+ -((length * 0.04) ** 2),
763
+ (length * 0.35) ** 2,
764
+ ]
765
+ )
766
+ if gender == "f"
767
+ else np.array(
768
+ [
769
+ (length * 0.17) ** 2,
770
+ (length * 0.13) ** 2,
771
+ -((length * 0.08) ** 2),
772
+ (length * 0.37) ** 2,
773
+ (length * 0) ** 2,
774
+ (length * 0.36) ** 2,
775
+ ]
776
+ )
777
+ )
778
+ * dict_sgmt["mass"],
779
+ 5,
780
+ )
781
+ dicts.append(dict_sgmt)
782
+
783
+ return dict(zip(inertial_segment_names, dicts)), sgt_lengths
784
+
785
+ @staticmethod
786
+ def get_dict_joint_placements(height: float, gender: str = "m") -> dict:
787
+ joint_names = [
788
+ "middle_thoracic_Z",
789
+ "middle_cervical_Z",
790
+ "left_clavicle_joint_X",
791
+ "right_clavicle_joint_X",
792
+ "left_shoulder_Z",
793
+ "left_elbow_Z",
794
+ "left_wrist_Z",
795
+ "right_shoulder_Z",
796
+ "right_elbow_Z",
797
+ "right_wrist_Z",
798
+ "left_hip_Z",
799
+ "left_knee_Z",
800
+ "left_ankle_Z",
801
+ "right_hip_Z",
802
+ "right_knee_Z",
803
+ "right_ankle_Z",
804
+ ]
805
+
806
+ lengths_names = [
807
+ "L_abdomen",
808
+ "L_thorax_cerv",
809
+ "L_thorax_supr",
810
+ "L_upperarm",
811
+ "L_lowerarm",
812
+ "L_upperleg",
813
+ "L_lowerleg",
814
+ ]
815
+ ratios = np.array(
816
+ [
817
+ 0.1183
818
+ if gender == "f"
819
+ else 0.1237, # L_abdomen MPT from XYP to OMPH (De Leva 1996)
820
+ 0.1314
821
+ if gender == "f"
822
+ else 0.1390, # L_thorax UPT from CERV to XYPH (De Leva 1996)
823
+ 0.0821 if gender == "f" else 0.0980, # from SUPR to XYPH (De Leva 1996)
824
+ 0.1510 if gender == "f" else 0.1531, # L_upperarm (Dumas 2007)
825
+ 0.1534 if gender == "f" else 0.1593, # L_lowerarm (Dumas 2007)
826
+ 0.2354 if gender == "f" else 0.2441, # L_upperleg (Dumas 2007)
827
+ 0.2410 if gender == "f" else 0.2446, # L_lowerleg (Dumas 2007)
828
+ ]
829
+ )
830
+ # Ratios from De Leva : HJC from Seidel
831
+ # SJC Schnorenberg-style AC/H = 0.132 (F) or 0.0129 (M)
832
+ SJ_thorax_X_r = 0.0043 if gender == "f" else 0.0046
833
+ SJ_thorax_Y_r = -0.0449 if gender == "f" else -0.0416
834
+ SJ_thorax_Z_r = 0.1108 if gender == "f" else 0.1164
835
+ HJ_pelvis_X_r = 0.0138 if gender == "f" else 0.0126
836
+ HJ_pelvis_Y_r = -0.0570 if gender == "f" else -0.0558
837
+ HJ_pelvis_Z_r = 0.0548 if gender == "f" else 0.0457
838
+
839
+ lengths = np.round(ratios * height, 3) # mm accuracy
840
+ dict_lengths = dict(zip(lengths_names, lengths))
841
+
842
+ joint_placements = []
843
+ for j in joint_names:
844
+ if j == "middle_thoracic_Z":
845
+ joint_placements.append(np.array([0, dict_lengths["L_abdomen"], 0]))
846
+ elif j == "middle_cervical_Z":
847
+ joint_placements.append(np.array([0, dict_lengths["L_thorax_cerv"], 0]))
848
+ elif j == "left_clavicle_joint_X":
849
+ joint_placements.append(np.array([0, dict_lengths["L_thorax_supr"], 0]))
850
+ elif j == "right_clavicle_joint_X":
851
+ joint_placements.append(np.array([0, dict_lengths["L_thorax_supr"], 0]))
852
+ elif j == "left_shoulder_Z":
853
+ joint_placements.append(
854
+ np.round(
855
+ height
856
+ * np.array([SJ_thorax_X_r, SJ_thorax_Y_r, -SJ_thorax_Z_r]),
857
+ 3,
858
+ )
859
+ )
860
+ elif j == "left_elbow_Z":
861
+ joint_placements.append(np.array([0, -dict_lengths["L_upperarm"], 0]))
862
+ elif j == "left_wrist_Z":
863
+ joint_placements.append(np.array([0, -dict_lengths["L_lowerarm"], 0]))
864
+ elif j == "right_shoulder_Z":
865
+ joint_placements.append(
866
+ np.round(
867
+ height
868
+ * np.array([SJ_thorax_X_r, SJ_thorax_Y_r, SJ_thorax_Z_r]),
869
+ 3,
870
+ )
871
+ )
872
+ elif j == "right_elbow_Z":
873
+ joint_placements.append(np.array([0, -dict_lengths["L_upperarm"], 0]))
874
+ elif j == "right_wrist_Z":
875
+ joint_placements.append(np.array([0, -dict_lengths["L_lowerarm"], 0]))
876
+ elif j == "left_hip_Z":
877
+ joint_placements.append(
878
+ np.round(
879
+ height
880
+ * np.array([HJ_pelvis_X_r, HJ_pelvis_Y_r, -HJ_pelvis_Z_r]),
881
+ 3,
882
+ )
883
+ )
884
+ elif j == "left_knee_Z":
885
+ joint_placements.append(np.array([0, -dict_lengths["L_upperleg"], 0]))
886
+ elif j == "left_ankle_Z":
887
+ joint_placements.append(np.array([0, -dict_lengths["L_lowerleg"], 0]))
888
+ elif j == "right_hip_Z":
889
+ joint_placements.append(
890
+ np.round(
891
+ height
892
+ * np.array([HJ_pelvis_X_r, HJ_pelvis_Y_r, HJ_pelvis_Z_r]),
893
+ 3,
894
+ )
895
+ )
896
+ elif j == "right_knee_Z":
897
+ joint_placements.append(np.array([0, -dict_lengths["L_upperleg"], 0]))
898
+ elif j == "right_ankle_Z":
899
+ joint_placements.append(np.array([0, -dict_lengths["L_lowerleg"], 0]))
900
+
901
+ return dict(zip(joint_names, joint_placements))
902
+
903
+ @staticmethod
904
+ def get_dict_meshes_scale(
905
+ height: float, sgt_lengths: dict, gender: str = "m"
906
+ ) -> dict:
907
+ meshes_names = [
908
+ "middle_pelvis_0",
909
+ "left_upperleg_0",
910
+ "left_lowerleg_0",
911
+ "left_lowerleg_1",
912
+ "left_foot_0",
913
+ "middle_abdomen_0",
914
+ "middle_thorax_0",
915
+ "middle_head_0",
916
+ "middle_head_1",
917
+ "left_upperarm_0",
918
+ "left_upperarm_1",
919
+ "left_lowerarm_0",
920
+ "left_lowerarm_1",
921
+ "left_hand_0",
922
+ "right_upperarm_0",
923
+ "right_upperarm_1",
924
+ "right_lowerarm_0",
925
+ "right_lowerarm_1",
926
+ "right_hand_0",
927
+ "right_upperleg_0",
928
+ "right_lowerleg_0",
929
+ "right_lowerleg_1",
930
+ "right_foot_0",
931
+ ]
932
+
933
+ scales = []
934
+
935
+ for name in meshes_names:
936
+ if (
937
+ name == "middle_pelvis_0"
938
+ ): # pelvis mesh size is : X = 37.050m, Y = 32.551m, Z = 39.252m
939
+ scale = np.round(
940
+ np.array(
941
+ [
942
+ 1.09 * sgt_lengths["middle_pelvis_0_width"] / 39.252,
943
+ sgt_lengths["middle_pelvis_0_width"] / 39.252,
944
+ sgt_lengths["middle_pelvis_0_width"] / 39.252,
945
+ ]
946
+ ),
947
+ 4,
948
+ )
949
+ scales.append(scale)
950
+ elif (
951
+ name == "left_upperleg_0"
952
+ ): # upperleg mesh size is : X = 20.977m, Y = 68.524m, Z = 20.977m
953
+ scale = np.round(
954
+ np.array(
955
+ [
956
+ 0.96 * sgt_lengths["left_upperleg_0"] / 68.524,
957
+ sgt_lengths["left_upperleg_0"] / 68.524,
958
+ 1.08 * sgt_lengths["left_upperleg_0"] / 68.524,
959
+ ]
960
+ ),
961
+ 4,
962
+ )
963
+ scales.append(scale)
964
+ elif (
965
+ name == "left_lowerleg_0"
966
+ ): # lowerleg mesh size is : X = 17.986m, Y = 67m, Z = 17.977m
967
+ scale = np.round(
968
+ np.array(
969
+ [
970
+ 0.96 * sgt_lengths["left_lowerleg_0"] / 67,
971
+ sgt_lengths["left_lowerleg_0"] / 67,
972
+ 1.07 * sgt_lengths["left_lowerleg_0"] / 67,
973
+ ]
974
+ ),
975
+ 4,
976
+ )
977
+ scales.append(scale)
978
+ elif name == "left_lowerleg_1": # knee mesh is a sphere of 12.5m
979
+ scale = np.round(
980
+ np.array(
981
+ [
982
+ 0.7
983
+ * (0.053 * height if gender == "f" else 0.056 * height)
984
+ / 12.5,
985
+ 0.7
986
+ * (0.053 * height if gender == "f" else 0.056 * height)
987
+ / 12.5,
988
+ 0.7
989
+ * (0.053 * height if gender == "f" else 0.056 * height)
990
+ / 12.5,
991
+ ]
992
+ ),
993
+ 4,
994
+ )
995
+ scales.append(scale)
996
+ elif (
997
+ name == "left_foot_0"
998
+ ): # foot mesh size is : X = 40.5m, Y = 16.5m, Z = 17.999m
999
+ scale = np.round(
1000
+ np.array(
1001
+ [
1002
+ sgt_lengths["left_foot_0"] / 40.5,
1003
+ sgt_lengths["left_foot_0"] / 40.5,
1004
+ sgt_lengths["left_foot_0"] / 40.5,
1005
+ ]
1006
+ ),
1007
+ 4,
1008
+ )
1009
+ scales.append(scale)
1010
+ elif name == "middle_abdomen_0": # abdomen meshis a sphere of radius 30 m
1011
+ scale = np.round(
1012
+ np.array(
1013
+ [
1014
+ sgt_lengths["middle_abdomen_0"] / 30,
1015
+ sgt_lengths["middle_abdomen_0"] / 30,
1016
+ sgt_lengths["middle_abdomen_0"] / 30,
1017
+ ]
1018
+ ),
1019
+ 4,
1020
+ )
1021
+ scales.append(scale)
1022
+ elif (
1023
+ name == "middle_thorax_0"
1024
+ ): # torso mesh size is : X = 36.991m, Y = 35m, Z = 40.029m
1025
+ scale = np.round(
1026
+ np.array(
1027
+ [
1028
+ sgt_lengths["middle_thorax_0"] / 35,
1029
+ sgt_lengths["middle_thorax_0"] / 35,
1030
+ sgt_lengths["middle_thorax_0"] / 35,
1031
+ ]
1032
+ ),
1033
+ 4,
1034
+ )
1035
+ scales.append(scale)
1036
+ elif (
1037
+ name == "middle_head_0"
1038
+ ): # head mesh size is : X = 32.049m, Y = 42m, Z = 32.049m
1039
+ scale = np.round(
1040
+ np.array(
1041
+ [
1042
+ sgt_lengths["middle_head_0"] / 42,
1043
+ sgt_lengths["middle_head_0"] / 42,
1044
+ sgt_lengths["middle_head_0"] / 42,
1045
+ ]
1046
+ ),
1047
+ 4,
1048
+ )
1049
+ scales.append(scale)
1050
+ elif name == "middle_head_1": # neck mesh
1051
+ scale = np.round(
1052
+ np.array(
1053
+ [
1054
+ (0.078 * height if gender == "f" else 0.086 * height)
1055
+ / 31.0,
1056
+ (0.078 * height if gender == "f" else 0.086 * height)
1057
+ / 31.0,
1058
+ (0.078 * height if gender == "f" else 0.086 * height)
1059
+ / 31.0,
1060
+ ]
1061
+ ),
1062
+ 4,
1063
+ )
1064
+ scales.append(scale)
1065
+ elif name == "left_upperarm_0": # shoulder mesh is a shpere of radius 17.5
1066
+ scale = np.round(
1067
+ np.array(
1068
+ [
1069
+ (0.037 * height if gender == "f" else 0.038 * height)
1070
+ / 17.5,
1071
+ (0.037 * height if gender == "f" else 0.038 * height)
1072
+ / 17.5,
1073
+ (0.037 * height if gender == "f" else 0.038 * height)
1074
+ / 17.5,
1075
+ ]
1076
+ ),
1077
+ 4,
1078
+ )
1079
+ scales.append(scale)
1080
+ elif (
1081
+ name == "left_upperarm_1"
1082
+ ): # upperarm mesh is of size : X = 17.158m, Y = 46m, Z = 17.183m
1083
+ scale = np.round(
1084
+ np.array(
1085
+ [
1086
+ 1.05 * sgt_lengths["left_upperarm_0"] / 46,
1087
+ sgt_lengths["left_upperarm_0"] / 46,
1088
+ 1.16 * sgt_lengths["left_upperarm_0"] / 46,
1089
+ ]
1090
+ ),
1091
+ 4,
1092
+ )
1093
+ scales.append(scale)
1094
+ elif (
1095
+ name == "left_lowerarm_0"
1096
+ ): # lowerarm mesh is of size : X = 15.547m, Y = 45m, Z = 15.541m
1097
+ scale = np.round(
1098
+ np.array(
1099
+ [
1100
+ sgt_lengths["left_lowerarm_0"] / 45,
1101
+ sgt_lengths["left_lowerarm_0"] / 45,
1102
+ sgt_lengths["left_lowerarm_0"] / 45,
1103
+ ]
1104
+ ),
1105
+ 4,
1106
+ )
1107
+ scales.append(scale)
1108
+ elif name == "left_lowerarm_1": # elbow mesh is a sphere of radius 12.5
1109
+ scale = np.round(
1110
+ np.array(
1111
+ [
1112
+ (0.037 * height if gender == "f" else 0.038 * height)
1113
+ / 12.5,
1114
+ (0.037 * height if gender == "f" else 0.038 * height)
1115
+ / 12.5,
1116
+ (0.037 * height if gender == "f" else 0.038 * height)
1117
+ / 12.5,
1118
+ ]
1119
+ ),
1120
+ 4,
1121
+ )
1122
+ scales.append(scale)
1123
+ elif (
1124
+ name == "left_hand_0"
1125
+ ): # hand mesh is of size : X = 12.055m, Y = 30m, Z = 16.646m
1126
+ scale = np.round(
1127
+ np.array(
1128
+ [
1129
+ sgt_lengths["left_hand_0"] / 30,
1130
+ sgt_lengths["left_hand_0"] / 30,
1131
+ sgt_lengths["left_hand_0"] / 30,
1132
+ ]
1133
+ ),
1134
+ 4,
1135
+ )
1136
+ scales.append(scale)
1137
+ elif name == "right_upperarm_0": # shoulder mesh
1138
+ scale = np.round(
1139
+ np.array(
1140
+ [
1141
+ (0.037 * height if gender == "f" else 0.038 * height)
1142
+ / 17.5,
1143
+ (0.037 * height if gender == "f" else 0.038 * height)
1144
+ / 17.5,
1145
+ (0.037 * height if gender == "f" else 0.038 * height)
1146
+ / 17.5,
1147
+ ]
1148
+ ),
1149
+ 4,
1150
+ )
1151
+ scales.append(scale)
1152
+ elif name == "right_upperarm_1": # upperarm mesh
1153
+ scale = np.round(
1154
+ np.array(
1155
+ [
1156
+ 1.05 * sgt_lengths["right_upperarm_0"] / 46,
1157
+ sgt_lengths["right_upperarm_0"] / 46,
1158
+ 1.16 * sgt_lengths["right_upperarm_0"] / 46,
1159
+ ]
1160
+ ),
1161
+ 4,
1162
+ )
1163
+ scales.append(scale)
1164
+ elif name == "right_lowerarm_0": # lowerarm mesh
1165
+ scale = np.round(
1166
+ np.array(
1167
+ [
1168
+ sgt_lengths["right_lowerarm_0"] / 45,
1169
+ sgt_lengths["right_lowerarm_0"] / 45,
1170
+ sgt_lengths["right_lowerarm_0"] / 45,
1171
+ ]
1172
+ ),
1173
+ 4,
1174
+ )
1175
+ scales.append(scale)
1176
+ elif name == "right_lowerarm_1": # elbow mesh
1177
+ scale = np.round(
1178
+ np.array(
1179
+ [
1180
+ (0.037 * height if gender == "f" else 0.038 * height)
1181
+ / 12.5,
1182
+ (0.037 * height if gender == "f" else 0.038 * height)
1183
+ / 12.5,
1184
+ (0.037 * height if gender == "f" else 0.038 * height)
1185
+ / 12.5,
1186
+ ]
1187
+ ),
1188
+ 4,
1189
+ )
1190
+ scales.append(scale)
1191
+ elif name == "right_hand_0": # hand mesh
1192
+ scale = np.round(
1193
+ np.array(
1194
+ [
1195
+ sgt_lengths["right_hand_0"] / 30,
1196
+ sgt_lengths["right_hand_0"] / 30,
1197
+ sgt_lengths["right_hand_0"] / 30,
1198
+ ]
1199
+ ),
1200
+ 4,
1201
+ )
1202
+ scales.append(scale)
1203
+ elif name == "right_upperleg_0": # upperleg mesh
1204
+ scale = np.round(
1205
+ np.array(
1206
+ [
1207
+ 0.96 * sgt_lengths["right_upperleg_0"] / 68.524,
1208
+ sgt_lengths["right_upperleg_0"] / 68.524,
1209
+ 1.08 * sgt_lengths["right_upperleg_0"] / 68.524,
1210
+ ]
1211
+ ),
1212
+ 4,
1213
+ )
1214
+ scales.append(scale)
1215
+ elif name == "right_lowerleg_0": # lowerleg mesh
1216
+ scale = np.round(
1217
+ np.array(
1218
+ [
1219
+ 0.96 * sgt_lengths["right_lowerleg_0"] / 67,
1220
+ sgt_lengths["right_lowerleg_0"] / 67,
1221
+ 1.07 * sgt_lengths["right_lowerleg_0"] / 67,
1222
+ ]
1223
+ ),
1224
+ 4,
1225
+ )
1226
+ scales.append(scale)
1227
+ elif name == "right_lowerleg_1": # knee mesh
1228
+ scale = np.round(
1229
+ np.array(
1230
+ [
1231
+ 0.7
1232
+ * (0.053 * height if gender == "f" else 0.056 * height)
1233
+ / 12.5,
1234
+ 0.7
1235
+ * (0.053 * height if gender == "f" else 0.056 * height)
1236
+ / 12.5,
1237
+ 0.7
1238
+ * (0.053 * height if gender == "f" else 0.056 * height)
1239
+ / 12.5,
1240
+ ]
1241
+ ),
1242
+ 4,
1243
+ )
1244
+ scales.append(scale)
1245
+ elif name == "right_foot_0": # foot mesh
1246
+ scale = np.round(
1247
+ np.array(
1248
+ [
1249
+ sgt_lengths["right_foot_0"] / 40.5,
1250
+ sgt_lengths["right_foot_0"] / 40.5,
1251
+ sgt_lengths["right_foot_0"] / 40.5,
1252
+ ]
1253
+ ),
1254
+ 4,
1255
+ )
1256
+ scales.append(scale)
1257
+
1258
+ return dict(zip(meshes_names, scales))
1259
+
1260
+ def _scale_pin_model(
1261
+ self,
1262
+ model: pin.Model,
1263
+ visual_model: pin.GeometryModel,
1264
+ height: float,
1265
+ weight: float,
1266
+ gender: str = "m",
1267
+ ) -> None:
1268
+ """
1269
+ Overwrite model.inertias, model.jointPlacements, and model.geometryObjects.meshScale
1270
+ based on anthropometry.
1271
+ """
1272
+ # compute parameters
1273
+ joints = self.get_dict_joint_placements(height, gender)
1274
+ inert, sgt_lengths = self.get_dict_inertial_param(height, weight, gender)
1275
+ scales = self.get_dict_meshes_scale(height, sgt_lengths, gender)
1276
+
1277
+ # apply inertias
1278
+ for seg, P in inert.items():
1279
+ b_id = model.frames[model.getFrameId(seg)].parentJoint
1280
+
1281
+ Ixx, Ixy, Ixz, Iyy, Iyz, Izz = P["inertia"]
1282
+ I_mat = np.array([[Ixx, Ixy, Ixz], [Ixy, Iyy, Iyz], [Ixz, Iyz, Izz]])
1283
+ model.inertias[b_id] = pin.Inertia(P["mass"], P["com"], I_mat)
1284
+
1285
+ # apply joint placements
1286
+ for j, pos in joints.items():
1287
+ j_id = model.getJointId(j)
1288
+ M = model.jointPlacements[j_id]
1289
+ M.translation = pos
1290
+ model.jointPlacements[j_id] = M
1291
+
1292
+ # scale visuals
1293
+ assert len(visual_model.geometryObjects.tolist()) == len(scales)
1294
+ for geom_obj in visual_model.geometryObjects:
1295
+ geom_obj.meshScale = scales[geom_obj.name]