|
19 | 19 | min,
|
20 | 20 | max,
|
21 | 21 | where,
|
| 22 | + cross, |
| 23 | + flip, |
| 24 | + concatenate, |
22 | 25 | )
|
23 | 26 | from numpy.random import uniform
|
24 | 27 | from numpy.linalg import inv, det, cond, pinv, matrix_rank, svd, eig
|
@@ -1503,6 +1506,171 @@ def asada(robot, J, q, axes, **kwargs):
|
1503 | 1506 | # else:
|
1504 | 1507 | return w
|
1505 | 1508 |
|
| 1509 | + def partial_fkine0(self, q: ArrayLike, n: int) -> ndarray: |
| 1510 | + r""" |
| 1511 | + Manipulator Forward Kinematics nth Partial Derivative |
| 1512 | +
|
| 1513 | + The manipulator Hessian tensor maps joint acceleration to end-effector |
| 1514 | + spatial acceleration, expressed in the ee frame. This |
| 1515 | + function calulcates this based on the ETS of the robot. One of Je or q |
| 1516 | + is required. Supply Je if already calculated to save computation time |
| 1517 | +
|
| 1518 | + :param q: The joint angles/configuration of the robot (Optional, |
| 1519 | + if not supplied will use the stored q values). |
| 1520 | + :type q: ArrayLike |
| 1521 | + :param end: the final link/Gripper which the Hessian represents |
| 1522 | + :param start: the first link which the Hessian represents |
| 1523 | + :param tool: a static tool transformation matrix to apply to the |
| 1524 | + end of end, defaults to None |
| 1525 | +
|
| 1526 | + :return: The nth Partial Derivative of the forward kinematics |
| 1527 | +
|
| 1528 | + :references: |
| 1529 | + - Kinematic Derivatives using the Elementary Transform |
| 1530 | + Sequence, J. Haviland and P. Corke |
| 1531 | + """ |
| 1532 | + |
| 1533 | + # Calculate the Jacobian and Hessian |
| 1534 | + J = self.jacob0(q) |
| 1535 | + H = self.hessian0(q) |
| 1536 | + |
| 1537 | + # A list of derivatives, starting with the jacobian and hessian |
| 1538 | + dT = [J, H] |
| 1539 | + |
| 1540 | + # The tensor dimensions of the latest derivative |
| 1541 | + # Set to the current size of the Hessian |
| 1542 | + size = [self.n, 6, self.n] |
| 1543 | + |
| 1544 | + # An array which keeps track of the index of the partial derivative |
| 1545 | + # we are calculating |
| 1546 | + # It stores the indices in the order: "j, k, l. m, n, o, ..." |
| 1547 | + # where count is extended to match oder of the partial derivative |
| 1548 | + count = array([0, 0]) |
| 1549 | + |
| 1550 | + # The order of derivative for which we are calculating |
| 1551 | + # The Hessian is the 2nd-order so we start with c = 2 |
| 1552 | + c = 2 |
| 1553 | + |
| 1554 | + def add_indices(indices, c): |
| 1555 | + total = len(indices * 2) |
| 1556 | + new_indices = [] |
| 1557 | + |
| 1558 | + for i in range(total): |
| 1559 | + j = i // 2 |
| 1560 | + new_indices.append([]) |
| 1561 | + new_indices[i].append(indices[j][0].copy()) |
| 1562 | + new_indices[i].append(indices[j][1].copy()) |
| 1563 | + |
| 1564 | + if i % 2 == 0: |
| 1565 | + # if even number |
| 1566 | + new_indices[i][0].append(c) |
| 1567 | + else: |
| 1568 | + # if odd number |
| 1569 | + new_indices[i][1].append(c) |
| 1570 | + |
| 1571 | + return new_indices |
| 1572 | + |
| 1573 | + def add_pdi(pdi): |
| 1574 | + total = len(pdi * 2) |
| 1575 | + new_pdi = [] |
| 1576 | + |
| 1577 | + for i in range(total): |
| 1578 | + j = i // 2 |
| 1579 | + new_pdi.append([]) |
| 1580 | + new_pdi[i].append(pdi[j][0]) |
| 1581 | + new_pdi[i].append(pdi[j][1]) |
| 1582 | + |
| 1583 | + # if even number |
| 1584 | + if i % 2 == 0: |
| 1585 | + new_pdi[i][0] += 1 |
| 1586 | + # if odd number |
| 1587 | + else: |
| 1588 | + new_pdi[i][1] += 1 |
| 1589 | + |
| 1590 | + return new_pdi |
| 1591 | + |
| 1592 | + # these are the indices used for the hessian |
| 1593 | + indices = [[[1], [0]]] |
| 1594 | + |
| 1595 | + # The partial derivative indices (pdi) |
| 1596 | + # the are the pd indices used in the cross products |
| 1597 | + pdi = [[0, 0]] |
| 1598 | + |
| 1599 | + # The length of dT correspods to the number of derivatives we have calculated |
| 1600 | + while len(dT) != n: |
| 1601 | + |
| 1602 | + # Add to the start of the tensor size list |
| 1603 | + size.insert(0, self.n) |
| 1604 | + |
| 1605 | + # Add an axis to the count array |
| 1606 | + count = concatenate(([0], count)) |
| 1607 | + |
| 1608 | + # This variables corresponds to indices within the previous partial derivatives |
| 1609 | + # to be cross prodded |
| 1610 | + # The order is: "[j, k, l, m, n, o, ...]" |
| 1611 | + # Although, our partial derivatives have the order: pd[..., o, n, m, l, k, cartesian DoF, j] |
| 1612 | + # For example, consider the Hessian Tensor H[n, 6, n], the index H[k, :, j]. This corrsponds |
| 1613 | + # to the second partial derivative of the kinematics of joint j with respect to joint k. |
| 1614 | + indices = add_indices(indices, c) |
| 1615 | + |
| 1616 | + # This variable corresponds to the indices in Td which corresponds to the |
| 1617 | + # partial derivatives we need to use |
| 1618 | + pdi = add_pdi(pdi) |
| 1619 | + |
| 1620 | + c += 1 |
| 1621 | + |
| 1622 | + # Allocate our new partial derivative tensor |
| 1623 | + pd = zeros(size) |
| 1624 | + |
| 1625 | + # We need to loop n^c times |
| 1626 | + # There are n^c columns to calculate |
| 1627 | + for _ in range(self.n**c): |
| 1628 | + |
| 1629 | + # Allocate the rotation and translation components |
| 1630 | + rot = zeros(3) |
| 1631 | + trn = zeros(3) |
| 1632 | + |
| 1633 | + # This loop calculates a single column ([trn, rot]) of the tensor for dT(x) |
| 1634 | + for j in range(len(indices)): |
| 1635 | + pdr0 = dT[pdi[j][0]] |
| 1636 | + pdr1 = dT[pdi[j][1]] |
| 1637 | + |
| 1638 | + idx0 = count[indices[j][0]] |
| 1639 | + idx1 = count[indices[j][1]] |
| 1640 | + |
| 1641 | + # This is a list of indices selecting the slices of the previous tensor |
| 1642 | + idx0_slices = flip(idx0[1:]) |
| 1643 | + idx1_slices = flip(idx1[1:]) |
| 1644 | + |
| 1645 | + # This index selecting the column within the 2d slice of the previous tensor |
| 1646 | + idx0_n = idx0[0] |
| 1647 | + idx1_n = idx1[0] |
| 1648 | + |
| 1649 | + # Use our indices to select the rotational column from pdr0 and pdr1 |
| 1650 | + col0_rot = pdr0[(*idx0_slices, slice(3, 6), idx0_n)] |
| 1651 | + col1_rot = pdr1[(*idx1_slices, slice(3, 6), idx1_n)] |
| 1652 | + |
| 1653 | + # Use our indices to select the translational column from pdr1 |
| 1654 | + col1_trn = pdr1[(*idx1_slices, slice(0, 3), idx1_n)] |
| 1655 | + |
| 1656 | + # Perform the cross product as described in the maths above |
| 1657 | + rot += cross(col0_rot, col1_rot) |
| 1658 | + trn += cross(col0_rot, col1_trn) |
| 1659 | + |
| 1660 | + pd[(*flip(count[1:]), slice(0, 3), count[0])] = trn |
| 1661 | + pd[(*flip
8E94
(count[1:]), slice(3, 6), count[0])] = rot |
| 1662 | + |
| 1663 | + count[0] += 1 |
| 1664 | + for j in range(len(count)): |
| 1665 | + if count[j] == self.n: |
| 1666 | + count[j] = 0 |
| 1667 | + if j != len(count) - 1: |
| 1668 | + count[j + 1] += 1 |
| 1669 | + |
| 1670 | + dT.append(pd) |
| 1671 | + |
| 1672 | + return dT[-1] |
| 1673 | + |
1506 | 1674 | def ik_lm_chan(
|
1507 | 1675 | self,
|
1508 | 1676 | Tep: Union[ndarray, SE3],
|
|
0 commit comments