Files
teleoperation/script/calibrator.py
2019-02-28 15:00:40 +01:00

178 lines
4.8 KiB
Python
Executable File

#! /usr/bin/env python
"""The script for calibrating the arm lengths and 'joystick' extents.
You will normally not call this script directly but rather with
`roslaunch teleoperation calibration.launch`
"""
import os
import json
import rospy
import tf
import actionlib
import numpy as np
from naoqi import ALProxy
from teleoperation.msg import RequestSpeechAction, RequestSpeechGoal
def calib_field(sentences, src_frames, targ_frames):
"""Retrieve a relative transform of some marker.
Parameters
----------
sentences : list of str
What to say.
src_frames : list of str
The names of tf source frames (from where to transform)
targ_frames : list of str
The names of tf target frames (to where to transform)
Returns
-------
list of transforms
"""
ok = False
t_and_q = []
while not ok:
for s in sentences:
tts.say(s)
tts.say('Then say start')
client.send_goal(RequestSpeechGoal(['start']))
client.wait_for_result()
for src, targ in zip(src_frames, targ_frames):
try:
t_and_q.append(ll.lookupTransform(src, targ, rospy.Time(0)))
ok = True
except:
ok = False
tts.say('Step failed. Try again')
break
return t_and_q
def calib_center():
"""Retrieve the coordinates of the 'joystick' zero state."""
trans, q = calib_field([
'Stand in front of camera',
'Far enough to see your arms'
], ['odom'], ['Aruco_0_frame'])[0]
rot = tf.transformations.euler_from_quaternion(q)
return trans, rot
def calib_arms():
"""Retrieve the hand positions relative to the chest."""
ts_and_qs = calib_field(
['Now stretch your arms', 'Make sure the markers are detected'],
['odom'] * 3,
['Aruco_{}_frame'.format(i) for i in range(3)]
)
a0 = ts_and_qs[0][0]
a1 = ts_and_qs[1][0]
a2 = ts_and_qs[2][0]
return (
tuple(c1 - c0 for c0, c1 in zip(a0, a1)),
tuple(c2 - c0 for c0, c2 in zip(a0, a2))
)
def calib_shoulders():
"""Retrieve the shoulder positions relative to the chest."""
ts_and_qs = calib_field(
['Now place the markers on the corresponding shoulders',
'Make sure the markers are detected'],
['odom'] * 3,
['Aruco_{}_frame'.format(i) for i in range(3)]
)
a0 = ts_and_qs[0][0]
a1 = ts_and_qs[1][0]
a2 = ts_and_qs[2][0]
return (
tuple(c1 - c0 for c0, c1 in zip(a0, a1)),
tuple(c2 - c0 for c0, c2 in zip(a0, a2))
)
def calib_rotation():
"""Retrieve the limits of the z-axis of the 'joystick'."""
rots = []
for side in ('left', 'right'):
_, q = calib_field(
['Now rotate to your {}'.format(side),
'So that marker on the chest is still detected'],
['odom'], ['Aruco_0_frame']
)[0]
rots.append(tf.transformations.euler_from_quaternion(q))
return tuple(rots)
def calib_extremes():
"""Retrieve the limits of the x and y axes of the 'joystick'."""
transs = []
for direction in ('forward', 'backward', 'to the left', 'to the right'):
trans, _ = calib_field(
['Now return to the initial position',
'Take a big step {}'.format(direction)],
['odom'],
['Aruco_0_frame']
)[0]
transs.append(trans)
return tuple(transs)
def calibration():
"""Run full calibration and dump the results to the config file."""
center = calib_center()
larm, rarm = calib_arms()
lsh, rsh = calib_shoulders()
larm, rarm, lsh, rsh = larm[1:], rarm[1:], lsh[1:], rsh[1:]
lr, rr = calib_rotation()
fw, bk, lt, rt = calib_extremes()
tts.say('Well done')
calib = {
'cr': [center[0][0], center[0][1], center[1][2]],
'arm': (np.linalg.norm(np.array(larm) - np.array(lsh)) +
np.linalg.norm(np.array(rarm) - np.array(rsh))) / 2,
'lsh': [0.0] + list(lsh),
'rsh': [0.0] + list(rsh),
'rr': rr[2],
'lr': lr[2],
'fw': fw[0],
'bk': bk[0],
'lt': lt[1],
'rt': rt[1],
}
here = os.path.dirname(os.path.realpath(__file__))
with open('{}/../config/default.yaml'.format(here), 'w') as f:
json.dump(calib, f, indent=4)
if __name__ == '__main__':
rospy.init_node('calibrator', disable_signals=True)
try:
tts = ALProxy('ALTextToSpeech', os.environ['NAO_IP'], 9559)
client = actionlib.SimpleActionClient('speech_server',
RequestSpeechAction)
client.wait_for_server()
ll = tf.TransformListener()
rospy.on_shutdown(lambda: client.cancel_goal())
calibration()
except KeyboardInterrupt:
rospy.signal_shutdown('Interrupted')