|
37 | 37 | from swift import Swift
|
38 | 38 | from spatialgeometry import Shape, CollisionShape, Cylinder, SceneNode
|
39 | 39 |
|
40 |
| -from spatialmath import SE3 |
| 40 | +from spatialmath import ( |
| 41 | + SE3, |
| 42 | + SpatialAcceleration, |
| 43 | + SpatialVelocity, |
| 44 | + SpatialInertia, |
| 45 | + SpatialForce, |
| 46 | +) |
41 | 47 | import spatialmath.base as smb
|
42 | 48 | from spatialmath.base.argcheck import (
|
43 | 49 | isvector,
|
@@ -4404,3 +4410,138 @@ def indiv_calculation(link: Link, link_col: CollisionShape, q: NDArray):
|
4404 | 4410 | bin = np.concatenate((bin, l_bin))
|
4405 | 4411 |
|
4406 | 4412 | return Ain, bin
|
| 4413 | + |
| 4414 | + # --------------------------------------------------------------------- # |
| 4415 | + # --------- Dynamics Methods ------------------------------------------ # |
| 4416 | + # --------------------------------------------------------------------- # |
| 4417 | + |
| 4418 | + def rne( |
| 4419 | + self, |
| 4420 | + q: NDArray, |
| 4421 | + qd: NDArray, |
| 4422 | + qdd: NDArray, |
| 4423 | + symbolic: bool = False, |
| 4424 | + gravity: Union[ArrayLike, None] = None, |
| 4425 | + ): |
| 4426 | + """ |
| 4427 | + Compute inverse dynamics via recursive Newton-Euler formulation |
| 4428 | +
|
| 4429 | + ``rne_dh(q, qd, qdd)`` where the arguments have shape (n,) where n is |
| 4430 | + the number of robot joints. The result has shape (n,). |
| 4431 | +
|
| 4432 | + ``rne_dh(q, qd, qdd)`` where the arguments have shape (m,n) where n |
| 4433 | + is the number of robot joints and where m is the number of steps in |
| 4434 | + the joint trajectory. The result has shape (m,n). |
| 4435 | +
|
| 4436 | + ``rne_dh(p)`` where the input is a 1D array ``p`` = [q, qd, qdd] with |
| 4437 | + shape (3n,), and the result has shape (n,). |
| 4438 | +
|
| 4439 | + ``rne_dh(p)`` where the input is a 2D array ``p`` = [q, qd, qdd] with |
| 4440 | + shape (m,3n) and the result has shape (m,n). |
| 4441 | +
|
| 4442 | + Parameters |
| 4443 | + ---------- |
| 4444 | + q |
| 4445 | + Joint coordinates |
| 4446 | + qd |
| 4447 | + Joint velocity |
| 4448 | + qdd |
| 4449 | + Joint acceleration |
| 4450 | + symbolic |
| 4451 | + If True, supports symbolic expressions |
| 4452 | + gravity |
| 4453 | + Gravitational acceleration, defaults to attribute |
| 4454 | + of self |
| 4455 | +
|
| 4456 | + Returns |
| 4457 | + ------- |
| 4458 | + tau |
| 4459 | + Joint force/torques |
| 4460 | +
|
| 4461 | + Notes |
| 4462 | + ----- |
| 4463 | + - This version supports symbolic model parameters |
| 4464 | + - Verified against MATLAB code |
| 4465 | +
|
| 4466 | + """ |
| 4467 | + |
| 4468 | + n = self.n |
| 4469 | + |
| 4470 | + # allocate intermediate variables |
| 4471 | + Xup = SE3.Alloc(n) |
| 4472 | + Xtree = SE3.Alloc(n) |
| 4473 | + |
| 4474 | + v = SpatialVelocity.Alloc(n) |
| 4475 | + a = SpatialAcceleration.Alloc(n) |
| 4476 | + f = SpatialForce.Alloc(n) |
| 4477 | + I = SpatialInertia.Alloc(n) # noqa |
| 4478 | + s = [] # joint motion subspace |
| 4479 | + |
| 4480 | + if symbolic: |
| 4481 | + Q = np.empty((n,), dtype="O") # joint torque/force |
| 4482 | + else: |
| 4483 | + Q = np.empty((n,)) # joint torque/force |
| 4484 | + |
| 4485 | + # TODO Should the dynamic parameters of static links preceding joint be |
| 4486 | + # somehow merged with the joint? |
| 4487 | + |
| 4488 | + # A temp variable to handle static joints |
| 4489 | + Ts = SE3() |
| 4490 | + |
| 4491 | + # A counter through joints |
| 4492 | + j = 0 |
| 4493 | + |
| 4494 | + # initialize intermediate variables |
| 4495 | + for link in self.links: |
| 4496 | + if link.isjoint: |
| 4497 | + I[j] = SpatialInertia(m=link.m, r=link.r) |
| 4498 | + if symbolic and link.Ts is None: |
| 4499 | + Xtree[j] = SE3(np.eye(4, dtype="O"), check=False) |
| 4500 | + else: |
| 4501 | + Xtree[j] = Ts * SE3(link.Ts, check=False) |
| 4502 | + |
| 4503 | + if link.v is not None: |
| 4504 | + s.append(link.v.s) |
| 4505 | + |
| 4506 | + # Increment the joint counter |
| 4507 | + j += 1 |
| 4508 | + |
| 4509 | + # Reset the Ts tracker |
| 4510 | + Ts = SE3() |
| 4511 | + else: |
| 4512 | + # TODO Keep track of inertia and transform??? |
| 4513 | + Ts *= SE3(link.Ts, check=False) |
| 4514 | + |
| 4515 | + if gravity is None: |
| 4516 | + a_grav = -SpatialAcceleration(self.gravity) |
| 4517 | + else: |
| 4518 | + a_grav = -SpatialAcceleration(gravity) |
| 4519 | + |
| 4520 | + # forward recursion |
| 4521 | + for j in range(0, n): |
| 4522 | + vJ = SpatialVelocity(s[j] * qd[j]) |
| 4523 | + |
| 4524 | + # transform from parent(j) to j |
| 4525 | + Xup[j] = SE3(self.links[j].A(q[j])).inv() |
| 4526 | + |
| 4527 | + if self.links[j].parent is None: |
| 4528 | + v[j] = vJ |
| 4529 | + a[j] = Xup[j] * a_grav + SpatialAcceleration(s[j] * qdd[j]) |
| 4530 | + else: |
| 4531 | + jp = self.links[j].parent.jindex # type: ignore |
| 4532 | + v[j] = Xup[j] * v[jp] + vJ |
| 4533 | + a[j] = Xup[j] * a[jp] + SpatialAcceleration(s[j] * qdd[j]) + v[j] @ vJ |
| 4534 | + |
| 4535 | + f[j] = I[j] * a[j] + v[j] @ (I[j] * v[j]) |
| 4536 | + |
| 4537 | + # backward recursion |
| 4538 | + for j in reversed(range(0, n)): |
| 4539 | + |
| 4540 | + # next line could be dot(), but fails for symbolic arguments |
| 4541 | + Q[j] = sum(f[j].A * s[j]) |
| 4542 | + |
| 4543 | + if self.links[j].parent is not None: |
| 4544 | + jp = self.links[j].parent.jindex # type: ignore |
| 4545 | + f[jp] = f[jp] + Xup[j] * f[j] |
| 4546 | + |
| 4547 | + return Q |
0 commit comments