195 lines
5.2 KiB
Python
Executable File
195 lines
5.2 KiB
Python
Executable File
#! /usr/bin/env python
|
|
"""Library for executing control for a given effector"""
|
|
|
|
from __future__ import division
|
|
import os
|
|
import json
|
|
from math import asin, atan, radians
|
|
|
|
import numpy as np
|
|
|
|
from masterloop import mp
|
|
|
|
|
|
FRAME_TORSO = 0
|
|
AXIS_XYZ = 7
|
|
K = 0.1
|
|
|
|
_here = os.path.dirname(os.path.realpath(__file__))
|
|
with open('{}/../config/default.yaml'.format(_here)) as f:
|
|
_xxx = json.load(f)
|
|
|
|
ARM = _xxx['arm']
|
|
MY_SHOULDERS = {
|
|
'L': _xxx['lsh'],
|
|
'R': _xxx['rsh']
|
|
}
|
|
JOINTS = ('ShoulderPitch', 'ShoulderRoll', 'ElbowYaw', 'ElbowRoll')
|
|
NAO_ARM = 0.22
|
|
|
|
|
|
def xyz(joint):
|
|
"""Get 3D coordinate of a joint in torso frame."""
|
|
return np.array(mp.getPosition(joint, FRAME_TORSO, False),
|
|
dtype=np.float64)[:3]
|
|
|
|
|
|
crt_xyz = {
|
|
'L': xyz('LArm'),
|
|
'R': xyz('RArm')
|
|
}
|
|
|
|
|
|
crt_joint = {
|
|
'L': np.array(mp.getAngles(['L' + j for j in JOINTS], False),
|
|
dtype=np.float64),
|
|
'R': np.array(mp.getAngles(['R' + j for j in JOINTS], False),
|
|
dtype=np.float64)
|
|
}
|
|
|
|
|
|
best_guess = {
|
|
'L': np.random.randn(3, 4),
|
|
'R': np.random.randn(3, 4)
|
|
}
|
|
|
|
|
|
def get_transform(joint):
|
|
"""Retrieve a transform matrix of a joint in the torso frame."""
|
|
return np.array(
|
|
mp.getTransform(joint, FRAME_TORSO, False), dtype=np.float64
|
|
).reshape((4, 4))
|
|
|
|
|
|
def diff_jacobian(side):
|
|
"""Update and return the differential Jacobian. NOT GOOD."""
|
|
new_end_xyz = xyz('{}Arm'.format(side))
|
|
delta_r = new_end_xyz - crt_xyz[side]
|
|
crt_xyz[side] = new_end_xyz.copy()
|
|
|
|
new_joint = np.array(mp.getAngles([side + j for j in JOINTS], False))
|
|
delta_j = new_joint - crt_joint[side]
|
|
crt_joint[side] = new_joint.copy()
|
|
|
|
J = delta_r[:,None] / delta_j
|
|
okval = np.isfinite(J)
|
|
J[~okval] = best_guess[side][~okval]
|
|
best_guess[side][okval] = J[okval]
|
|
return J
|
|
|
|
|
|
def jacobian(side):
|
|
"""Calculate the analytical Jacobian for side 'L' or 'R'."""
|
|
end_xyz = xyz('{}Arm'.format(side))
|
|
xyzs = np.array([xyz(side + j) for j in JOINTS])
|
|
|
|
x_axis = np.array([[1, 0, 0, 1]]).T
|
|
y_axis = np.array([[0, 1, 0, 1]]).T
|
|
z_axis = np.array([[0, 0, 1, 1]]).T
|
|
|
|
ax_order = (y_axis, z_axis, x_axis, z_axis)
|
|
|
|
# Calculate the Jacobian after the formula from the report
|
|
axes = np.concatenate(
|
|
[get_transform(side + j).dot(ax) for j, ax in zip(JOINTS, ax_order)],
|
|
axis=1
|
|
)[:3].T
|
|
|
|
J = np.cross(axes, end_xyz - xyzs).T
|
|
return J
|
|
|
|
|
|
def to_nao(my_xyz, side):
|
|
"""Transform object coordinates from operator chest frame to NAO torso."""
|
|
sh_offs = np.array(MY_SHOULDERS[side])
|
|
my_sh_xyz = my_xyz - sh_offs
|
|
nao_sh_xyz = my_sh_xyz / ARM * NAO_ARM
|
|
nao_xyz = nao_sh_xyz + xyz(side + 'Shoulder')
|
|
return nao_xyz
|
|
|
|
|
|
def inv_jacobian(j):
|
|
"""Singluarity robust inverse Jacobian."""
|
|
u, s, vt = np.linalg.svd(j)
|
|
s_inv = np.zeros((vt.shape[0], u.shape[1]))
|
|
np.fill_diagonal(s_inv, 1 / s)
|
|
s_inv[np.abs(s_inv) > 30] = 0
|
|
return vt.T.dot(s_inv).dot(u.T)
|
|
|
|
|
|
def _some_cartesian(my_xyz, side, jfunc):
|
|
"""A generic cartesian controller.
|
|
|
|
Parameters
|
|
----------
|
|
my_xyz : numpy.array
|
|
Coordinates of the object/target in the operator chest frame.
|
|
side : str, 'L' or 'R'
|
|
jfunc : func
|
|
A function that will return a jacobian matrix for the given side.
|
|
|
|
Returns
|
|
-------
|
|
numpy.array
|
|
The control to execute in the joint space.
|
|
|
|
"""
|
|
nao_xyz = to_nao(my_xyz, side)
|
|
delta_r = 0.1 * (nao_xyz - xyz('{}Arm'.format(side)))
|
|
crt_q = mp.getAngles([side + j for j in JOINTS], False)
|
|
delta_q = inv_jacobian(jfunc(side)).dot(delta_r).flatten()
|
|
return crt_q + delta_q
|
|
|
|
|
|
def our_cartesian(my_xyz, side):
|
|
"""Our cartesian controller."""
|
|
return _some_cartesian(my_xyz, side, jacobian)
|
|
|
|
|
|
def our_diff_cartesian(my_xyz, side):
|
|
"""Our differential cartesian controller."""
|
|
return _some_cartesian(my_xyz, side, diff_jacobian)
|
|
|
|
|
|
def _elbow(arm_):
|
|
"Dumb way to calculate the elbow roll."""
|
|
quot = min(1.0, arm_ / 0.70)
|
|
return radians(180 * (1 - quot))
|
|
|
|
|
|
def dumb(my_xyz, side):
|
|
"""Our dumb controller that directly maps 3D coords into joint space."""
|
|
sign = 1 if side == 'L' else -1
|
|
roll = asin(my_xyz[1] / np.linalg.norm(my_xyz))
|
|
roll -= sign * radians(25)
|
|
pitch = atan(-my_xyz[2] / np.abs(my_xyz[0]))
|
|
|
|
eroll = -sign * _elbow(np.linalg.norm(my_xyz))
|
|
eyaw = -sign * radians(40)
|
|
return np.array([pitch, roll, eyaw, eroll])
|
|
|
|
|
|
def movement(my_xyz, side, control):
|
|
"""Execute the movement.
|
|
|
|
my_xyz : numpy.array
|
|
Coordinates of the object/target in the operator chest frame.
|
|
side : str, 'L' or 'R'
|
|
control : func
|
|
A function to calculate the conrol for the side, depending on the
|
|
target coordinates. It must return returning the `numpy.array`
|
|
joint vector of format
|
|
[ShoulderPitch, ShoulderRoll, ElbowYaw, ElbowRoll].
|
|
|
|
"""
|
|
ref = control(np.array(my_xyz), side).tolist()
|
|
mp.setAngles([side + j for j in JOINTS], ref, 0.3)
|
|
|
|
|
|
def nao_cart_movement(my_arm_xyz, side, *_):
|
|
"""Execute the movement using the NAO cartesian controller."""
|
|
nao_xyz = to_nao(my_arm_xyz, side)
|
|
mp.setPositions('{}Arm'.format(side), FRAME_TORSO,
|
|
tuple(nao_xyz.tolist()) + (0, 0, 0),
|
|
1.0, AXIS_XYZ)
|