blob: 9c124c8481d305746e3be9b16afe5625c85b84dd [file] [log] [blame]
# Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
""" Replay a finger recording with the Touchbot II
This script takes in a device spec (for the device the gesture
should be replayed on) and a path file (the list of readings that
were recorded originally) and reproduces that gesture with the
robot.
This only supports nice, clean logs. If there are stray touches,
palms, more than 2 fingers, etc, in the log is WILL CAUSE PROBLEMS.
Make sure your logs are perfect before trying to replay them or
it may DAMAGE THE ROBOT PERMANENTLY.
Usage:
python replay.py device_spec.p path_to_follow.py
ie: python replay.py lumpy.p spiral.p
"""
import math
import pickle
import sys
from touchbotII import Touchbot, Device
def distance(p1, p2):
x1, y1 = p1
x2, y2 = p2
return ((x1 - x2) ** 2 + (y1 - y2) ** 2) ** 0.5
try:
# Load the device spec
device = Device(sys.argv[1])
# Load the path to follow
path = pickle.load(open(sys.argv[2], 'r'))
except:
print 'Usage: python %s device_spec.p path_to_follow.p' % __file__
sys.exit(-1)
# Connect to the robot and configure the profile
bot = Touchbot()
prof = bot.GetCurrentProfile()
prof.speed = Touchbot.SPEED_MEDIUM
prof.inRange = Touchbot.BLEND_MOVEMENTS
bot.SetProfileData(prof)
last_p0 = None
last_p1 = None
last_angle = 0
bot.SetFingerStates([0, 0, 0, 0])
for i, positions in enumerate(path):
# TODO charliemooney: A better way to do this than just ignoring them
# There are a lot of reports and can make the movement jerky if you use
# all of them. This thins it out.
if i % 4 != 0:
continue
if len(positions) == 0:
bot.SetFingerStates([0, 0, 0, 0])
continue
elif len(positions) == 1:
p0 = last_p0 if last_p0 else (0.1, 0.1) # Dummy position
bot.SetFingerStates([0, 1, 0, 0])
elif len(positions) == 2:
p0 = positions[1]
bot.SetFingerStates([0, 1, 0, 1])
else:
print 'ERROR, invalid state!'
print positions
sys.exit(-1)
p1 = positions[0]
# Make sure the fingers don't try to swap order by checking the distances
if last_p0 and last_p1:
p0_p0_distance = distance(p0, last_p0)
p0_p1_distance = distance(p0, last_p1)
if p0_p0_distance > p0_p1_distance:
tmp = p0
p0 = p1
p1 = tmp
last_p0 = p0
last_p1 = p1
# To compute the position of the hand to put the fingers at the right
# places, center the hand over the mid-point between the fingers, then
# compute the distance and angle between them and rotate the wrist to
# that angle and open the fingers enough to place two of the fingers
# in the right spots
# +
# | p0 p_mid p1
# | + + +
# |
# | <-----------+----------->
# | dist
# |
# +-------------------------------+
# p1 & p1 are the relative coordinates of the two fingers
# p_mid is the relative coordinates of the point in between
p_mid = ((p1[0] + p0[0]) / 2.0, (p1[1] + p0[1]) / 2.0)
# Convert to the format we can actually send to the robot
abs_p = device.RelativePosToAbsolutePos(p_mid)
# Find the angle between the two finger positions (p0 and p1)
# This must be done using absolute coordinates, because depending on the
# geometry of the touchpad the angles may be slightly different for
# different devices.
abs_p0 = device.RelativePosToAbsolutePos(p0)
abs_p1 = device.RelativePosToAbsolutePos(p1)
abs_p.yaw = math.degrees(math.atan((abs_p0.y - abs_p1.y) /
(abs_p0.x - abs_p1.x)))
# The fingers are offset by 45 degrees
abs_p.yaw += 45
# Check that the angle hasn't aliased onto the opposite side
# and correct it if it has
ang_change = abs(abs_p.yaw - last_angle)
alias_ang_change = abs((abs_p.yaw - 180) - last_angle)
if ang_change > alias_ang_change:
abs_p.yaw = abs_p.yaw - 180
last_angle = abs_p.yaw
# Find the distance between p0 and p1 and spread the fingers
dist = distance((abs_p0.x, abs_p0.y), (abs_p1.x, abs_p1.y))
# Execute the move
bot.SetCartesian(abs_p, finger_distance=dist, blocking=False)
bot.SetFingerStates([0, 0, 0, 0])