|
| 1 | +import numpy as np |
| 2 | +from spatialmath import base |
| 3 | + |
| 4 | +def numjac(f, x, dx=1e-8, tN=0, rN=0): |
| 5 | + r""" |
| 6 | + Numerically compute Jacobian of function |
| 7 | +
|
| 8 | + :param f: the function, returns an m-vector |
| 9 | + :type f: callable |
| 10 | + :param x: function argument |
| 11 | + :type x: ndarray(n) |
| 12 | + :param dx: the numerical perturbation, defaults to 1e-8 |
| 13 | + :type dx: float, optional |
| 14 | + :param N: function returns SE(N) matrix, defaults to 0 |
| 15 | + :type N: int, optional |
| 16 | + :return: Jacobian matrix |
| 17 | + :rtype: ndarray(m,n) |
| 18 | +
|
| 19 | + Computes a numerical approximation to the Jacobian for ``f(x)`` where |
| 20 | + :math:`f: \mathbb{R}^n \mapsto \mathbb{R}^m`. |
| 21 | +
|
| 22 | + Uses first-order difference :math:`J[:,i] = (f(x + dx) - f(x)) / dx`. |
| 23 | +
|
| 24 | + If ``N`` is 2 or 3, then it is assumed that the function returns |
| 25 | + an SE(N) matrix which is converted into a Jacobian column comprising the |
| 26 | + translational Jacobian followed by the rotational Jacobian. |
| 27 | + """ |
| 28 | + x = np.array(x) |
| 29 | + Jcol = [] |
| 30 | + J0 = f(x) |
| 31 | + I = np.eye(len(x)) |
| 32 | + f0 = np.array(f(x)) |
| 33 | + for i in range(len(x)): |
| 34 | + fi = np.array(f(x + I[:,i] * dx)) |
| 35 | + Ji = (fi - f0) / dx |
| 36 | + |
| 37 | + if tN > 0: |
| 38 | + t = Ji[:tN,tN] |
| 39 | + r = base.vex(Ji[:tN,:tN] @ J0[:tN,:tN].T) |
| 40 | + Jcol.append(np.r_[t, r]) |
| 41 | + elif rN > 0: |
| 42 | + R = Ji[:rN,:rN] |
| 43 | + r = base.vex(R @ J0[:rN,:rN].T) |
| 44 | + Jcol.append(r) |
| 45 | + else: |
| 46 | + Jcol.append(Ji) |
| 47 | + # print(Ji) |
| 48 | + |
| 49 | + return np.c_[Jcol].T |
| 50 | + |
0 commit comments