Skip to content

Commit 9674667

Browse files
andreaantonelloAndreaAntonello
andauthored
Add rotation conventions to IK (#70)
Co-authored-by: AndreaAntonello <[email protected]>
1 parent 62575fa commit 9674667

File tree

4 files changed

+51
-9
lines changed

4 files changed

+51
-9
lines changed

Pipfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ requests = "*"
88
websockets = "*"
99
zeroconf = "==0.27.1"
1010
dataclasses = "*"
11+
pytransform3d = "==1.2.1"
1112

1213
[dev-packages]
1314
flake8 = "*"

evasdk/Eva.py

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ class Eva:
1414
def __init__(self, host_ip, token, request_timeout=5, renew_period=60 * 20):
1515
parsed_host_ip = strip_ip(host_ip)
1616

17-
self.__http_client = EvaHTTPClient(parsed_host_ip, token, request_timeout=request_timeout, renew_period=renew_period)
17+
self.__http_client = EvaHTTPClient(parsed_host_ip, token, request_timeout=request_timeout,
18+
renew_period=renew_period)
1819
self.__logger = logging.getLogger('evasdk.Eva:{}'.format(host_ip))
1920

2021
self.__eva_locker = EvaWithLocker(self)
@@ -41,11 +42,13 @@ def __exit__(self, type, value, traceback):
4142
# --------------------------------------------- HTTP HANDLERS ---------------------------------------------
4243
def api_call_with_auth(self, method, path, payload=None, headers={}, timeout=None, version='v1'):
4344
self.__logger.debug('Eva.api_call_with_auth {} {}'.format(method, path))
44-
return self.__http_client.api_call_with_auth(method, path, payload=payload, headers=headers, timeout=timeout, version=version)
45+
return self.__http_client.api_call_with_auth(method, path, payload=payload, headers=headers, timeout=timeout,
46+
version=version)
4547

4648
def api_call_no_auth(self, method, path, payload=None, headers={}, timeout=None, version='v1'):
4749
self.__logger.debug('Eva.api_call_no_auth {} {}'.format(method, path))
48-
return self.__http_client.api_call_no_auth(method, path, payload=payload, headers=headers, timeout=timeout, version=version)
50+
return self.__http_client.api_call_no_auth(method, path, payload=payload, headers=headers, timeout=timeout,
51+
version=version)
4952

5053

5154
# Global
@@ -197,9 +200,11 @@ def control_go_to(self, joints, wait_for_ready=True, max_speed=None, time_sec=No
197200
self.__logger.info('Eva.control_go_to called')
198201
if mode == 'teach':
199202
with self.__eva_locker.set_renew_period(Eva.__TEACH_RENEW_PERIOD):
200-
return self.__http_client.control_go_to(joints, wait_for_ready=wait_for_ready, max_speed=max_speed, time_sec=time_sec, mode=mode)
203+
return self.__http_client.control_go_to(joints, wait_for_ready=wait_for_ready, max_speed=max_speed,
204+
time_sec=time_sec, mode=mode)
201205
else:
202-
return self.__http_client.control_go_to(joints, wait_for_ready=wait_for_ready, max_speed=max_speed, time_sec=time_sec, mode=mode)
206+
return self.__http_client.control_go_to(joints, wait_for_ready=wait_for_ready, max_speed=max_speed,
207+
time_sec=time_sec, mode=mode)
203208

204209

205210
def control_pause(self, wait_for_paused=True):
@@ -233,9 +238,11 @@ def calc_forward_kinematics(self, joints, fk_type='both', tcp_config=None):
233238
return self.__http_client.calc_forward_kinematics(joints, fk_type=fk_type, tcp_config=tcp_config)
234239

235240

236-
def calc_inverse_kinematics(self, guess, target_position, target_orientation, tcp_config=None):
241+
def calc_inverse_kinematics(self, guess, target_position, target_orientation, tcp_config=None,
242+
orientation_type=None):
237243
self.__logger.debug('Eva.calc_inverse_kinematics called')
238-
return self.__http_client.calc_inverse_kinematics(guess, target_position, target_orientation, tcp_config=tcp_config)
244+
return self.__http_client.calc_inverse_kinematics(guess, target_position, target_orientation,
245+
tcp_config=tcp_config, orientation_type=orientation_type)
239246

240247

241248
def calc_nudge(self, joints, direction, offset, tcp_config=None):

evasdk/eva_http_client.py

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import time
33
import logging
44
import requests
5+
import pytransform3d.rotations as pyrot # type: ignore
56

67
from .robot_state import RobotState
78
from .eva_errors import eva_error, EvaError, EvaAutoRenewError
@@ -426,9 +427,41 @@ def calc_forward_kinematics(self, joints, fk_type='both', tcp_config=None):
426427
else:
427428
eva_error('calc_forward_kinematics invalid fk_type {}'.format(fk_type), r)
428429

430+
def calc_inverse_kinematics(self, guess, target_position, target_orientation, tcp_config=None,
431+
orientation_type=None):
432+
"""
433+
End-effector orientation (target_orientation) can be provided in several standard formats,
434+
by specifying the orinetation_type (default is None):
435+
- 'matrix': rotation matrix -> 3x3 array, in row major order
436+
- 'axis_angle': axis angle -> {'angle': float, 'x': float, 'y': float, 'z': float}
437+
- 'euler_zyx': {yaw, pitch, roll} Euler (Tait-Bryan) angles -> {'yaw': float, 'pitch': float, 'roll': float}
438+
- 'quat': quaternion -> {'w': float, 'x': float, 'y': float, 'z': float}
439+
- None: defaults to quaternion
440+
Conversion relies on pytransform3d library
441+
"""
442+
N_DIGITS = 8
443+
quat_not_normed = None
444+
445+
if orientation_type == 'matrix':
446+
quat_not_normed = pyrot.quaternion_from_matrix(pyrot.check_matrix(target_orientation))
447+
elif orientation_type == 'axis_angle':
448+
axis_angle = [target_orientation['x'], target_orientation['y'], target_orientation['z'],
449+
target_orientation['angle']]
450+
quat_not_normed = pyrot.quaternion_from_axis_angle(pyrot.check_axis_angle(axis_angle))
451+
elif orientation_type == 'euler_zyx':
452+
euler_zyx = [target_orientation['yaw'], target_orientation['pitch'], target_orientation['roll']]
453+
matrix = pyrot.matrix_from_euler_zyx(euler_zyx)
454+
quat_not_normed = pyrot.quaternion_from_matrix(pyrot.check_matrix(matrix))
455+
elif orientation_type == 'quat' or orientation_type is None:
456+
quat_not_normed = [target_orientation['w'], target_orientation['x'], target_orientation['y'],
457+
target_orientation['z']]
458+
else:
459+
eva_error(f'calc_inverse_kinematics invalid "{orientation_type}" orientation_type')
460+
461+
quat_normed = [round(num, N_DIGITS) for num in pyrot.check_quaternion(quat_not_normed)]
462+
quaternion = {'w': quat_normed[0], 'x': quat_normed[1], 'y': quat_normed[2], 'z': quat_normed[3]}
429463

430-
def calc_inverse_kinematics(self, guess, target_position, target_orientation, tcp_config=None):
431-
body = {'guess': guess, 'position': target_position, 'orientation': target_orientation}
464+
body = {'guess': guess, 'position': target_position, 'orientation': quaternion}
432465
if tcp_config is not None:
433466
body['tcp_config'] = tcp_config
434467

setup.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
'websockets',
2222
'zeroconf',
2323
'dataclasses',
24+
'pytransform3d',
2425
],
2526
classifiers=[
2627
"Programming Language :: Python :: 3",

0 commit comments

Comments
 (0)