|
6 | 6 | from biorbd_casadi import ( |
7 | 7 | GeneralizedCoordinates, |
8 | 8 | ) |
9 | | -from casadi import MX, DM, vertcat, horzcat, Function, solve, rootfinder, inv, nlpsol |
| 9 | +from casadi import MX, DM, vertcat, horzcat, Function, solve, rootfinder, inv, nlpsol, jacobian |
10 | 10 |
|
11 | 11 | from .biorbd_model import BiorbdModel |
12 | 12 | from ...models.protocols.holonomic_constraints import HolonomicConstraintsList |
@@ -491,7 +491,79 @@ def biais_vector(self, q: MX, qdot: MX) -> MX: |
491 | 491 | partitioned_constraints_jacobian_v = partitioned_constraints_jacobian[:, self.nb_independent_joints :] |
492 | 492 | partitioned_constraints_jacobian_v_inv = inv(partitioned_constraints_jacobian_v) |
493 | 493 |
|
494 | | - return -partitioned_constraints_jacobian_v_inv @ self.holonomic_constraints_jacobian(qdot) @ qdot |
| 494 | + # return -partitioned_constraints_jacobian_v_inv @ self.holonomic_constraints_jacobian(qdot) @ qdot |
| 495 | + # return -partitioned_constraints_jacobian_v_inv @ self._holonomic_constraints_jacobian_time_derivative_finite_difference(q, qdot) @ qdot |
| 496 | + # return -partitioned_constraints_jacobian_v_inv @ self._holonomic_constraints_jacobian_time_derivative(q, qdot) @ qdot |
| 497 | + return -partitioned_constraints_jacobian_v_inv @ self.holonomic_constraints_double_derivative(q, qdot, MX()) |
| 498 | + |
| 499 | + |
| 500 | + def _holonomic_constraints_jacobian_time_derivative_finite_difference( |
| 501 | + self, q: MX, qdot: MX, epsilon: float = 1e-8 |
| 502 | + ) -> MX: |
| 503 | + """ |
| 504 | + Compute Jdot = dJ/dt = (∂J/∂q) * qdot using finite differences. |
| 505 | +
|
| 506 | + Args: |
| 507 | + q: Configuration vector (n,) |
| 508 | + qdot: Velocity vector (n,) |
| 509 | + epsilon: Finite difference step size |
| 510 | +
|
| 511 | + Returns: |
| 512 | + Jdot: Time derivative of Jacobian (m, n) where m = number of constraints |
| 513 | + """ |
| 514 | + n = q.shape[0] |
| 515 | + J0 = self.holonomic_constraints_jacobian(q) |
| 516 | + m = J0.shape[0] |
| 517 | + |
| 518 | + Jdot = MX.zeros(m, n) |
| 519 | + |
| 520 | + for i in range(n): |
| 521 | + # Perturb only q_i |
| 522 | + q_plus = MX(q) |
| 523 | + q_plus[i] += epsilon |
| 524 | + J_plus = self.holonomic_constraints_jacobian(q_plus) |
| 525 | + |
| 526 | + q_minus = MX(q) |
| 527 | + q_minus[i] -= epsilon |
| 528 | + J_minus = self.holonomic_constraints_jacobian(q_minus) |
| 529 | + |
| 530 | + # approximated by finite difference |
| 531 | + dJ_dqi = (J_plus - J_minus) / (2 * epsilon) |
| 532 | + |
| 533 | + # Contract: Jdot += (∂J/∂q_i) * qdot_i |
| 534 | + Jdot += dJ_dqi * qdot[i] |
| 535 | + |
| 536 | + return Jdot |
| 537 | + |
| 538 | + def _holonomic_constraints_jacobian_time_derivative( |
| 539 | + self, q: MX, qdot: MX, |
| 540 | + ) -> MX: |
| 541 | + """ |
| 542 | + Compute Jdot = dJ/dt = (∂J/∂q) * qdot using finite differences. |
| 543 | +
|
| 544 | + Args: |
| 545 | + q: Configuration vector (n,) |
| 546 | + qdot: Velocity vector (n,) |
| 547 | +
|
| 548 | + Returns: |
| 549 | + Jdot: Time derivative of Jacobian (m, n) where m = number of constraints |
| 550 | +
|
| 551 | + Note: |
| 552 | + $$\dot{J} = \frac{dJ}{dt} = \frac{\partial J}{\partial q} \dot{q} = \sum_{k=1}^{n} \frac{\partial J}{\partial q_k} \dot{q}_k$$ |
| 553 | + """ |
| 554 | + n = q.shape[0] |
| 555 | + J0 = self.holonomic_constraints_jacobian(q) |
| 556 | + m = J0.shape[0] |
| 557 | + |
| 558 | + Jdot = MX.zeros(m, n) |
| 559 | + for k in range(n): |
| 560 | + # Compute the partial derivative of J with respect to q_k |
| 561 | + dJ_dqk = (J0, q[k]) # Shape (m, n) |
| 562 | + |
| 563 | + # Contract: Jdot += (∂J/∂q_k) * qdot_k |
| 564 | + Jdot += dJ_dqk @ qdot[k] |
| 565 | + |
| 566 | + return Jdot |
495 | 567 |
|
496 | 568 | def state_from_partition(self, state_u: MX, state_v: MX) -> MX: |
497 | 569 | """ |
|
0 commit comments