c4dynamics 2.0.3__py3-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.
Files changed (50) hide show
  1. c4dynamics/__init__.py +240 -0
  2. c4dynamics/datasets/__init__.py +95 -0
  3. c4dynamics/datasets/_manager.py +596 -0
  4. c4dynamics/datasets/_registry.py +80 -0
  5. c4dynamics/detectors/__init__.py +37 -0
  6. c4dynamics/detectors/yolo3_opencv.py +686 -0
  7. c4dynamics/detectors/yolo3_tf.py +124 -0
  8. c4dynamics/eqm/__init__.py +324 -0
  9. c4dynamics/eqm/derivs.py +212 -0
  10. c4dynamics/eqm/integrate.py +359 -0
  11. c4dynamics/filters/__init__.py +1373 -0
  12. c4dynamics/filters/a.py +48 -0
  13. c4dynamics/filters/ekf.py +320 -0
  14. c4dynamics/filters/kalman.py +725 -0
  15. c4dynamics/filters/kalman_v0.py +1071 -0
  16. c4dynamics/filters/kalman_v1.py +821 -0
  17. c4dynamics/filters/lowpass.py +123 -0
  18. c4dynamics/filters/luenberger.py +97 -0
  19. c4dynamics/rotmat/__init__.py +141 -0
  20. c4dynamics/rotmat/animate.py +465 -0
  21. c4dynamics/rotmat/rotmat.py +351 -0
  22. c4dynamics/sensors/__init__.py +72 -0
  23. c4dynamics/sensors/lineofsight.py +78 -0
  24. c4dynamics/sensors/radar.py +740 -0
  25. c4dynamics/sensors/seeker.py +1030 -0
  26. c4dynamics/states/__init__.py +327 -0
  27. c4dynamics/states/lib/__init__.py +320 -0
  28. c4dynamics/states/lib/datapoint.py +660 -0
  29. c4dynamics/states/lib/pixelpoint.py +776 -0
  30. c4dynamics/states/lib/rigidbody.py +677 -0
  31. c4dynamics/states/state.py +1486 -0
  32. c4dynamics/utils/__init__.py +44 -0
  33. c4dynamics/utils/_struct.py +6 -0
  34. c4dynamics/utils/const.py +130 -0
  35. c4dynamics/utils/cprint.py +80 -0
  36. c4dynamics/utils/gen_gif.py +142 -0
  37. c4dynamics/utils/idx2keys.py +4 -0
  38. c4dynamics/utils/images_loader.py +63 -0
  39. c4dynamics/utils/math.py +136 -0
  40. c4dynamics/utils/plottools.py +140 -0
  41. c4dynamics/utils/plottracks.py +304 -0
  42. c4dynamics/utils/printpts.py +36 -0
  43. c4dynamics/utils/slides_gen.py +64 -0
  44. c4dynamics/utils/tictoc.py +167 -0
  45. c4dynamics/utils/video_gen.py +300 -0
  46. c4dynamics/utils/vidgen.py +182 -0
  47. c4dynamics-2.0.3.dist-info/METADATA +242 -0
  48. c4dynamics-2.0.3.dist-info/RECORD +50 -0
  49. c4dynamics-2.0.3.dist-info/WHEEL +5 -0
  50. c4dynamics-2.0.3.dist-info/top_level.txt +1 -0
@@ -0,0 +1,677 @@
1
+ import numpy as np
2
+ import sys
3
+ sys.path.append('.')
4
+ import c4dynamics as c4d
5
+
6
+ from c4dynamics.states.lib.datapoint import datapoint
7
+
8
+ class rigidbody(datapoint): #
9
+ '''
10
+ A rigid-body object
11
+
12
+
13
+ The :class:`rigidbody` extends the :class:`datapoint <c4dynamics.states.lib.datapoint.datapoint>`
14
+ class to form an elementary rigidbody object in space, i.e.
15
+ an object with length and attitude.
16
+
17
+ It introduces attributes related to rotational dynamics,
18
+ such as angular position, angular velocity, and moment of inertia.
19
+ As such its state vector consists of the following variables:
20
+
21
+
22
+ .. math::
23
+
24
+ X = [x, y, z, v_x, v_y, v_z, \\varphi, \\theta, \\psi, p, q, r)]^T
25
+
26
+ - Position coordinates, velocity coordinates.
27
+ - Angles, angular rates.
28
+
29
+
30
+
31
+ **Arguments**
32
+
33
+ x : float or int, optional
34
+ The x-position of the datapoint. Default value :math:`x = 0`.
35
+ y : float or int, optional
36
+ The y-position of the datapoint. Default value :math:`y = 0`.
37
+ z : float or int, optional
38
+ The z-position of the datapoint. Default value :math:`z = 0`.
39
+ vx : float or int, optional
40
+ Component of velocity along the x-axis. Default value :math:`v_x = 0`.
41
+ vy : float or int, optional
42
+ Component of velocity along the y-axis. Default value :math:`v_y = 0`.
43
+ vz : float or int, optional
44
+ Component of velocity along the z-axis. Default value :math:`v_z = 0`.
45
+ phi : float or int, optional
46
+ Euler angle representing rotation around the x-axis (rad). Default value :math:`\\varphi = 0`.
47
+ theta : float or int, optional
48
+ Euler angle representing rotation around the y-axis (rad). Default value :math:`\\theta = 0`.
49
+ psi : float or int, optional
50
+ Euler angle representing rotation around the z-axis (rad). Default value :math:`\\psi = 0`.
51
+ p : float or int, optional
52
+ Angular rate around the x-axis (roll). (rad/sec). Default value :math:`p = 0`.
53
+ q : float or int, optional
54
+ Angular rate around the y-axis (pitch). (rad/sec). Default value :math:`q = 0`.
55
+ r : float or int, optional
56
+ Angular rate around the z-axis (yaw). (rad/sec). Default value :math:`r = 0`.
57
+
58
+
59
+ The input arguments determine the initial values of the instance.
60
+ The vector of initial conditions can be retrieved by calling
61
+ :attr:`rigidbody.X0 <c4dynamics.states.state.state.X0>`:
62
+
63
+ .. code::
64
+
65
+ >>> from c4dynamics import rigidbody
66
+
67
+ .. code::
68
+
69
+ >>> rb = rigidbody(z = 1000, theta = 10 * c4d.d2r, q = 0.5 * c4d.d2r)
70
+ >>> rb.X0 # doctest: +NUMPY_FORMAT
71
+ [0 0 1000 0 0 0 0 0.174 0 0 0.0087 0]
72
+
73
+
74
+ When the initial values are not known at the stage of constructing
75
+ the state object, it's possible to pass zeros and override them later
76
+ by direct assignment of the state variable with a `0` suffix.
77
+ See more at :attr:`state.X0 <c4dynamics.states.state.state.X0>`.
78
+
79
+
80
+ Parameters
81
+ ==========
82
+
83
+ mass : float
84
+ The mass of the datapoint
85
+ I : [float, float, float]
86
+ An array of moments of inertia
87
+
88
+ See Also
89
+ ========
90
+ .lib
91
+ .rotmat
92
+ .state
93
+ .eqm
94
+
95
+
96
+ Example
97
+ =======
98
+
99
+ A simplified model of an aircraft autopilot is given by:
100
+
101
+ .. math::
102
+
103
+ \\dot{z}(t) = 5 \\cdot \\theta(t)
104
+
105
+ \\dot{\\theta}(t) = -0.5 \\cdot \\theta(t) - 0.1 \\cdot z(t)
106
+
107
+
108
+ Where:
109
+
110
+ - :math:`z` is the deviation of the aircraft from the required altitude
111
+ - :math:`\\theta` is the pitch angle
112
+
113
+ The aircraft is represented by a `rigidbody` object.
114
+ `scipy's odeint` is employed to solve the
115
+ dynamics equations and update the state vector `X`.
116
+
117
+
118
+ import required packages:
119
+
120
+ .. code::
121
+
122
+ >>> import c4dynamics as c4d
123
+ >>> from matplotlib import pyplot as plt
124
+ >>> from scipy.integrate import odeint
125
+ >>> import numpy as np
126
+
127
+
128
+ Settings and initial conditions:
129
+
130
+ .. code::
131
+
132
+ >>> dt, tf = 0.01, 15
133
+ >>> tspan = np.arange(0, tf, dt)
134
+ >>> A = np.zeros((12, 12))
135
+ >>> A[2, 7] = 5
136
+ >>> A[7, 2] = -0.1
137
+ >>> A[7, 7] = -0.5
138
+ >>> f16 = c4d.rigidbody(z = 1, theta = 0)
139
+ >>> for t in tspan:
140
+ ... f16.X = odeint(lambda y, t: A @ y, f16.X, [t, t + dt])[-1]
141
+ ... f16.store(t)
142
+
143
+
144
+ .. code::
145
+
146
+ >>> _, ax = plt.subplots(2, 1)
147
+ >>> f16.plot('z', ax = ax[0]) # doctest: +IGNORE_OUTPUT
148
+ >>> ax[0].set(xlabel = '') # doctest: +IGNORE_OUTPUT
149
+ >>> f16.plot('theta', ax = ax[1], scale = c4d.r2d)
150
+ >>> plt.show()
151
+
152
+ .. figure:: /_examples/rigidbody/intro_f16_autopilot.png
153
+
154
+ The :meth:`animate` method allows the user to play the attitude
155
+ histories given a 3D model (requires installation of `open3D`).
156
+
157
+ The model in the example can be fetched using the c4dynamics datasets module
158
+ (see :mod:`c4dynamics.datasets`):
159
+
160
+ .. code::
161
+
162
+ >>> modelpath = c4d.datasets.d3_model('f16')
163
+ Fetched successfully
164
+
165
+ .. code::
166
+
167
+ >>> f16colors = np.vstack(([255, 215, 0], [255, 215, 0], [184, 134, 11], [0, 32, 38], [218, 165, 32], [218, 165, 32], [54, 69, 79], [205, 149, 12], [205, 149, 12])) / 255
168
+ >>> f16.animate(modelpath, angle0 = [90 * c4d.d2r, 0, 180 * c4d.d2r], modelcolor = f16colors)
169
+
170
+ .. figure:: /_examples/rigidbody/rb_intro_ap.gif
171
+
172
+ '''
173
+ phi: float
174
+ theta: float
175
+ psi: float
176
+ p: float
177
+ q: float
178
+ r: float
179
+
180
+
181
+ _ixx = 0
182
+ _iyy = 0
183
+ _izz = 0
184
+
185
+ def __init__(self, x = 0, y = 0, z = 0, vx = 0, vy = 0, vz = 0
186
+ , phi = 0, theta = 0, psi = 0, p = 0, q = 0, r = 0
187
+ ):
188
+
189
+ rbargs = {}
190
+
191
+ rbargs.setdefault('x', x)
192
+ rbargs.setdefault('y', y)
193
+ rbargs.setdefault('z', z)
194
+ rbargs.setdefault('vx', vx)
195
+ rbargs.setdefault('vy', vy)
196
+ rbargs.setdefault('vz', vz)
197
+
198
+ rbargs.setdefault('phi', phi)
199
+ rbargs.setdefault('theta', theta)
200
+ rbargs.setdefault('psi', psi)
201
+ rbargs.setdefault('p', p)
202
+ rbargs.setdefault('q', q)
203
+ rbargs.setdefault('r', r)
204
+
205
+
206
+ c4d.state.__init__(self, **rbargs)
207
+
208
+
209
+ @property
210
+ def I(self):
211
+ '''
212
+ Gets and sets the array of moments of inertia.
213
+
214
+ .. math::
215
+
216
+ I = [I_{xx}, I_{yy}, I_{zz}]
217
+
218
+
219
+ Default: :math:`I = [0, 0, 0]`
220
+
221
+
222
+ Parameters
223
+ ----------
224
+ I : numpy.array or list
225
+ An array of three moments of inertia about each
226
+ one of the axes :math:`([I_{xx}, I_{yy}, I_{zz}])`.
227
+
228
+
229
+ Returns
230
+ -------
231
+ out : numpy.array
232
+ An array of the three moments of inertia :math:`[I_{xx}, I_{yy}, I_{zz}]`.
233
+
234
+
235
+ Example
236
+ -------
237
+
238
+ The moment of inertia
239
+ determines how much torque is required for a
240
+ desired angular acceleration about a rotational axis.
241
+
242
+
243
+ In this example, two physical pendulums with the same
244
+ initial conditions
245
+ show the effect of different moments of inertia
246
+ on the time period of an oscillation:
247
+
248
+ .. math::
249
+
250
+ T = 2 \\cdot \\pi \\cdot \\sqrt{{I \\over m \\cdot g \\cdot l}}
251
+
252
+ where here :math:`m` is the mass :math:`m = 1`,
253
+ :math:`l` is the length from the center of mass :math:`l = 1`,
254
+ :math:`g` is the gravity acceleration,
255
+ and :math:`I` is the moment of inertia about :math:`y`, :math:`I_{yy1} = 0.5, I_{yy2} = 0.05`
256
+
257
+
258
+ Import required packages:
259
+
260
+ .. code::
261
+
262
+ >>> import c4dynamics as c4d
263
+ >>> from matplotlib import pyplot as plt
264
+ >>> from scipy.integrate import odeint
265
+ >>> import numpy as np
266
+
267
+
268
+ Settings and initial condtions:
269
+
270
+ .. code::
271
+
272
+ >>> b = 0.5
273
+ >>> dt = 0.01
274
+ >>> g = c4d.g_ms2
275
+ >>> theta0 = 80 * c4d.d2r
276
+ >>> rb05 = c4d.rigidbody(theta = theta0)
277
+ >>> rb05.I = [0, .5, 0]
278
+ >>> rb005 = c4d.rigidbody(theta = theta0)
279
+ >>> rb005.I = [0, .05, 0]
280
+
281
+
282
+
283
+ Physical pendulum dynamics:
284
+
285
+ .. code::
286
+
287
+ >>> def pendulum(yin, t, Iyy):
288
+ ... theta, q = yin[7], yin[10]
289
+ ... yout = np.zeros(12)
290
+ ... yout[7] = q
291
+ ... yout[10] = -g * c4d.sin(theta) / Iyy - b * q
292
+ ... return yout
293
+
294
+
295
+
296
+ Main loop
297
+
298
+ .. code::
299
+
300
+ >>> for ti in np.arange(0, 5, dt):
301
+ ... # Iyy = 0.5
302
+ ... rb05.X = odeint(pendulum, rb05.X, [ti, ti + dt], (rb05.I[1],))[1]
303
+ ... rb05.store(ti)
304
+ ... # Iyy = 0.05
305
+ ... rb005.X = odeint(pendulum, rb005.X, [ti, ti + dt], (rb005.I[1],))[1]
306
+ ... rb005.store(ti)
307
+
308
+
309
+ Plot results:
310
+
311
+ .. code::
312
+
313
+ >>> rb05.plot('theta')
314
+ >>> rb005.plot('theta', ax = plt.gca(), color = 'c')
315
+ >>> plt.show()
316
+
317
+ .. figure:: /_examples/rigidbody/Iyy_pendulum.png
318
+
319
+
320
+ '''
321
+ return np.array([self._ixx, self._iyy, self._izz])
322
+
323
+ @I.setter
324
+ def I(self, I):
325
+ self._ixx = I[0]
326
+ self._iyy = I[1]
327
+ self._izz = I[2]
328
+
329
+
330
+ @property
331
+ def angles(self):
332
+ '''
333
+ Returns an array of Euler angles.
334
+
335
+
336
+ .. math::
337
+
338
+ angles = [\\varphi, \\theta, \\psi]
339
+
340
+
341
+
342
+ Returns
343
+ -------
344
+ out : numpy.array
345
+ An array of three Euler angles, about each one of the axes
346
+ :math:`([\\varphi, \\theta, \\psi])`
347
+
348
+
349
+ Examples
350
+ --------
351
+
352
+ .. code::
353
+
354
+ >>> rb = c4d.rigidbody(phi = 135)
355
+ >>> rb.angles # doctest: +NUMPY_FORMAT
356
+ [135 0 0]
357
+
358
+ '''
359
+
360
+ return np.array([self.phi, self.theta, self.psi])
361
+
362
+
363
+ @property
364
+ def ang_rates(self):
365
+ '''
366
+ Returns an array of angular rates.
367
+
368
+ .. math::
369
+
370
+ angular rates = [p, q, r]
371
+
372
+
373
+
374
+ Returns
375
+ -------
376
+ out : numpy.array
377
+ An array of three angular rates of the body axes
378
+ :math:`([p, q, r])`
379
+
380
+
381
+ Examples
382
+ --------
383
+
384
+ .. code::
385
+
386
+ >>> q0 = 30
387
+ >>> rb = c4d.rigidbody(q = q0)
388
+ >>> rb.ang_rates # doctest: +NUMPY_FORMAT
389
+ [0 30 0]
390
+
391
+ '''
392
+ return np.array([self.p, self.q, self.r])
393
+
394
+
395
+ @property
396
+ def BR(self):
397
+ '''
398
+ Returns a Body-from-Reference Direction Cosine Matrix (DCM).
399
+
400
+
401
+ Based on the current Euler angles, `BR` returns the DCM in a 3-2-1 order,
402
+ i.e. first rotation about the z axis (yaw, :math:`\\psi`), then a rotation about the
403
+ y axis (pitch, :math:`\\theta`), and finally a rotation about the x axis (roll, :math:`\\varphi`).
404
+
405
+ The `DCM321` matrix is calculated by the
406
+ :mod:`rotmat <c4dynamics.rotmat>` module and is given by:
407
+
408
+
409
+ .. math::
410
+
411
+ R = \\begin{bmatrix}
412
+ c\\theta \\cdot c\\psi
413
+ & c\\theta \\cdot s\\psi
414
+ & -s\\theta \\\\
415
+ s\\varphi \\cdot s\\theta \\cdot c\\psi - c\\varphi \\cdot s\\psi
416
+ & s\\varphi \\cdot s\\theta \\cdot s\\psi + c\\varphi \\cdot c\\psi
417
+ & s\\varphi \\cdot c\\theta \\\\
418
+ c\\varphi \\cdot s\\theta \\cdot c\\psi + s\\varphi \\cdot s\\psi
419
+ & c\\varphi \\cdot s\\theta \\cdot s\\psi - s\\varphi \\cdot c\\psi
420
+ & c\\varphi \\cdot c\\theta
421
+ \\end{bmatrix}
422
+
423
+ where
424
+
425
+ - :math:`c\\varphi \\equiv cos(\\varphi)`
426
+ - :math:`s\\varphi \\equiv sin(\\varphi)`
427
+ - :math:`c\\theta \\equiv cos(\\theta)`
428
+ - :math:`s\\theta \\equiv sin(\\theta)`
429
+ - :math:`c\\psi \\equiv cos(\\psi)`
430
+ - :math:`s\\psi \\equiv sin(\\psi)`
431
+
432
+
433
+
434
+ For the background material regarding the rotational matrix operations,
435
+ see :mod:`rotmat <c4dynamics.rotmat>`.
436
+
437
+ Returns
438
+ -------
439
+
440
+ out : numpy.ndarray
441
+ A 3x3 DCM matrix uses to rotate a vector
442
+ to the body frame
443
+ from a reference frame of coordinates.
444
+
445
+
446
+ Example
447
+ -------
448
+
449
+ .. code::
450
+
451
+ >>> v_inertial = [1, 0, 0]
452
+ >>> rb = c4d.rigidbody(psi = 45 * c4d.d2r)
453
+ >>> v_body = rb.BR @ v_inertial
454
+ >>> v_body # doctest: +NUMPY_FORMAT
455
+ [0.707 -0.707 0.0]
456
+
457
+ '''
458
+
459
+ return c4d.rotmat.dcm321(phi = self.phi, theta = self.theta, psi = self.psi)
460
+
461
+
462
+ @property
463
+ def RB(self):
464
+ '''
465
+ Returns a Reference-from-Body Direction Cosine Matrix (DCM).
466
+
467
+
468
+ Based on the current Euler angles, `RB` returns the
469
+ transpose matrix of :attr:`BR <c4dynamics.states.lib.rigidbody.rigidbody.BR>`,
470
+ where :attr:`BR <c4dynamics.states.lib.rigidbody.rigidbody.BR>`
471
+ is the Body from Reference
472
+ DCM in 3-2-1 order.
473
+
474
+ The transpose matrix of the DCM generated by
475
+ three Euler angles :math:`\\varphi` (rotation about `x`),
476
+ :math:`\\theta` (about `y`), and :math:`\\psi` (about `z`) in 3-2-1 order,
477
+ is given by:
478
+
479
+ .. math::
480
+
481
+ R = \\begin{bmatrix}
482
+ c\\theta \\cdot c\\psi
483
+ & s\\varphi \\cdot s\\theta \\cdot c\\psi - c\\varphi \\cdot s\\psi
484
+ & c\\varphi \\cdot s\\theta \\cdot c\\psi + s\\varphi \\cdot s\\psi \\\\
485
+ c\\theta \\cdot s\\psi
486
+ & s\\varphi \\cdot s\\theta \\cdot s\\psi + c\\varphi \\cdot c\\psi
487
+ & c\\varphi \\cdot s\\theta \\cdot s\\psi - s\\varphi \\cdot c\\psi \\\\
488
+ -s\\theta
489
+ & s\\varphi \\cdot c\\theta
490
+ & c\\varphi \\cdot c\\theta
491
+ \\end{bmatrix}
492
+
493
+ where
494
+
495
+ - :math:`c\\varphi \\equiv cos(\\varphi)`
496
+ - :math:`s\\varphi \\equiv sin(\\varphi)`
497
+ - :math:`c\\theta \\equiv cos(\\theta)`
498
+ - :math:`s\\theta \\equiv sin(\\theta)`
499
+ - :math:`c\\psi \\equiv cos(\\psi)`
500
+ - :math:`s\\psi \\equiv sin(\\psi)`
501
+
502
+
503
+ For the background material regarding the rotational matrix operations,
504
+ see :mod:`rotmat <c4dynamics.rotmat>`.
505
+
506
+
507
+
508
+ Returns
509
+ -------
510
+
511
+ out : numpy.ndarray
512
+ A 3x3 DCM matrix uses to rotate a vector from a body frame
513
+ to a reference frame of coordinates.
514
+
515
+
516
+ Example
517
+ -------
518
+
519
+ .. code::
520
+
521
+ >>> v_body = [np.sqrt(3), 0, 1]
522
+ >>> rb = c4d.rigidbody(theta = 30 * c4d.d2r)
523
+ >>> v_inertial = rb.RB @ v_body
524
+ >>> v_inertial # doctest: +NUMPY_FORMAT
525
+ [2.0 0.0 0.0]
526
+
527
+ '''
528
+ return np.transpose(self.BR)
529
+
530
+
531
+ def inteqm(self, forces, moments, dt): # type: ignore
532
+ '''
533
+ Advances the state vector, :attr:`rigidbody.X <c4dynamics.states.state.state.X>`,
534
+ with respect to the input
535
+ forces and moments on a single step of time, `dt`.
536
+
537
+ Integrates equations of six degrees motion using the Runge-Kutta method.
538
+
539
+ This method numerically integrates the equations of motion for a dynamic system
540
+ using the fourth-order Runge-Kutta method as given by
541
+ :func:`eqm.int6 <c4dynamics.eqm.integrate.int6>`.
542
+
543
+ The derivatives of the equations are of six dimensional motion as
544
+ given by
545
+ :py:func:`eqm.eqm6 <c4dynamics.eqm.derivs.eqm6>`.
546
+
547
+
548
+ Parameters
549
+ ----------
550
+ forces : numpy.array or list
551
+ An external forces vector acting on the body, `forces = [Fx, Fy, Fz]`
552
+ moments : numpy.array or list
553
+ An external moments vector acting on the body, `moments = [Mx, My, Mz]`
554
+ dt : float or int
555
+ Interval time step for integration.
556
+
557
+
558
+ Returns
559
+ -------
560
+ out : numpy.float64
561
+ An acceleration array at the final time step.
562
+
563
+
564
+ Warning
565
+ -------
566
+ This method is not recommanded when the vectors
567
+ of forces or moments depend on the state variables.
568
+ Since the vectors of forces and moments are provided once at the
569
+ entrance to the integration, they remain constant
570
+ for the entire steps.
571
+ Therefore, when the forces or moments depend on the state variables
572
+ the results of this method are not accurate and may lead to instability.
573
+
574
+
575
+ Examples
576
+ --------
577
+ A torque is applied by spacecraft thrusters to stabilize
578
+ the roll in a constant rate.
579
+
580
+ Import required packages:
581
+
582
+ .. code::
583
+
584
+ >>> import c4dynamics as c4d
585
+ >>> from matplotlib import pyplot as plt
586
+ >>> import numpy as np
587
+
588
+
589
+ Settings and initial conditions:
590
+
591
+ .. code::
592
+
593
+ >>> dt = 0.001
594
+ >>> torque = [0.1, 0, 0]
595
+ >>> rb = c4d.rigidbody()
596
+ >>> rb.I = [0.5, 0, 0] # Moment of inertia about x
597
+
598
+
599
+ Main loop:
600
+
601
+ .. code::
602
+
603
+ >>> for ti in np.arange(0, 5, dt):
604
+ ... rb.inteqm(np.zeros(3), torque, dt) # doctest: +IGNORE_OUTPUT
605
+ ... if rb.p >= 10 * c4d.d2r:
606
+ ... torque = [0, 0, 0]
607
+ ... rb.store(ti)
608
+
609
+
610
+ Plot results:
611
+
612
+ .. code::
613
+
614
+ >>> rb.plot('p')
615
+ >>> plt.show()
616
+
617
+ .. figure:: /_examples/rigidbody/inteqm_rollstable.png
618
+
619
+
620
+ '''
621
+ # from c4dynamics.eqm import eqm6
622
+ self.X, acc = c4d.eqm.int6(self, forces, moments, dt, derivs_out = True)
623
+ return acc
624
+
625
+
626
+ def animate(self, modelpath, angle0 = [0, 0, 0]
627
+ , modelcolor = None, dt = 1e-3
628
+ , savedir = None, cbackground = [1, 1, 1]):
629
+
630
+ c4d.rotmat.animate(self, modelpath, angle0, modelcolor, dt, savedir, cbackground)
631
+
632
+
633
+
634
+ rigidbody.animate.__doc__ = c4d.rotmat.animate.__doc__
635
+
636
+
637
+
638
+ if __name__ == "__main__":
639
+
640
+ import doctest, contextlib, os
641
+ from c4dynamics import IgnoreOutputChecker, cprint
642
+
643
+ # Register the custom OutputChecker
644
+ doctest.OutputChecker = IgnoreOutputChecker
645
+
646
+ tofile = False
647
+ optionflags = doctest.FAIL_FAST
648
+
649
+
650
+ # Filter out tests for the animate method by overriding the testmod.
651
+ def custom_testmod(module=None, **kwargs):
652
+ tests = doctest.DocTestFinder().find(module)
653
+
654
+ tests = [test for test in tests if test.name != "__main__.rigidbody.animate"] # Exclude animate
655
+
656
+ runner = doctest.DocTestRunner(**kwargs)
657
+ for test in tests:
658
+ runner.run(test)
659
+ return runner.summarize()
660
+
661
+
662
+
663
+ if tofile:
664
+ with open(os.path.join('tests', '_out', 'output.txt'), 'w') as f:
665
+ with contextlib.redirect_stdout(f), contextlib.redirect_stderr(f):
666
+ result = custom_testmod(sys.modules[__name__], optionflags=optionflags)
667
+ else:
668
+ result = custom_testmod(sys.modules[__name__], optionflags=optionflags)
669
+
670
+ if result.failed == 0:
671
+ cprint(os.path.basename(__file__) + ": all tests passed!", 'g')
672
+ else:
673
+ print(f"{result.failed}")
674
+
675
+
676
+
677
+