cyberglove_calibrer.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 #
3 # Copyright 2011 Shadow Robot Company Ltd.
4 #
5 # This program is free software: you can redistribute it and/or modify it
6 # under the terms of the GNU General Public License as published by the Free
7 # Software Foundation version 2 of the License.
8 #
9 # This program is distributed in the hope that it will be useful, but WITHOUT
10 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 # more details.
13 #
14 # You should have received a copy of the GNU General Public License along
15 # with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
17 """
18 Calibration utility for the cyberglove.
19 
20 @author: Ugo Cupcic
21 @contact: ugo@shadowrobot.com, contact@shadowrobot.com
22 """
23 import roslib
24 # roslib.load_manifest('sr_control_gui')
25 
26 import os
27 from cyberglove_library import Cyberglove
28 from std_srvs.srv import Empty
29 
30 import yaml
31 
32 import rospy
33 
34 
35 class Joint:
36 
37  """
38  A class containing simple information regarding a joint: name, and calibrated value
39  """
40 
41  def __init__(self, name, min=0, max=90):
42  """
43  @param name: the name of the joint
44  @param min: the min value to calibrate
45  @param max: the max value to calibrate
46  """
47  self.name = name
48  self.min = min
49  self.max = max
50 
51 
53 
54  """
55  A class containing all the data for a given calibration step
56  """
57 
58  def __init__(self, step_name="", step_description=[""], joints=[]):
59  """
60  @param step_name: A short name for this calibration step
61  @param step_description: A table containing 2 descriptions: the first one describes the min pose, the second
62  the max
63  @param joints: A table containing the joints to be calibrated
64 
65  """
66  self.step_name = step_name
67  self.step_description = step_description
68  self.joints = joints
69 
70 
72 
73  """
74  A class Containing all the calibration data for a given joint.
75  """
76 
77  def __init__(self, raw_min=0.0, raw_max=0.0,
78  calibrated_min=0.0, calibrated_max=0.0,
79  is_calibrated=0):
80  self.raw_min = raw_min
81  self.raw_max = raw_max
82  self.calibrated_min = calibrated_min
83  self.calibrated_max = calibrated_max
84  self.is_calibrated = is_calibrated
85 
86 
87 def default_description(step_name, max=0):
88  """
89  The default description function for a step. Just prints a text for each step.
90 
91  @param step_name: the name of the step
92  @param max: if 0=>we're reading the min values, if 1=> max values
93  """
94  if max == 0:
95  print "calibrating min values for: " + str(step_name)
96  else:
97  print "calibrating max values for: " + str(step_name)
98 
99 
100 def do_nothing(step_name, max=0):
101  """
102  A function that does nothing. Used to have an empty description function.
103  """
104  nothing = True
105 
106 
108 
109  """
110  A utility to calibrate the cyberglove.
111  """
112 
113  def __init__(self, description_function=default_description, nb_sensors=22):
114  """
115  Initialize some class variables: a table containing the calibration steps, a connection to the cyberglove
116  library and a description function for the calibration steps
117 
118  @param description_function: specify a function you want to use to describe the calibration steps ( text /
119  pictures / animation / ... ). Must take a joint name as parameter.
120  """
122  if nb_sensors == 18:
123  self.nb_sensors = nb_sensors
124  else:
125  self.nb_sensors = 22
126  self.cyberglove = Cyberglove(nb_sensors=self.nb_sensors)
127 
128  # fill the table containing all the joints
129  self.joints = {}
130  for name in self.cyberglove.get_joints_names():
131  self.joints[name] = Calibration()
132 
133  self.get_calibration_steps()
134 
135  if description_function is None:
136  description_function = do_nothing
137  self.description_function = description_function
138 
140  """
141  Read the calibration steps from the xml file.
142 
143  @return: 0 when the values were read.
144  """
145 
146  # first step: calibrate 0s, 3s and TH4
147  if self.nb_sensors == 18:
148  joints1 = [Joint("G_IndexMPJ", 0, 90), Joint("G_IndexPIJ", 0, 90),
149  Joint("G_MiddleMPJ", 0, 90), Joint("G_MiddlePIJ", 0, 90),
150  Joint("G_RingMPJ", 0, 90), Joint("G_RingPIJ", 0, 90),
151  Joint("G_PinkieMPJ", 0, 90), Joint("G_PinkiePIJ", 0, 90),
152  Joint("G_ThumbAb", 50, 0)]
153  else:
154  joints1 = [Joint("G_IndexMPJ", 0, 90), Joint("G_IndexPIJ", 0, 90),
155  Joint("G_IndexDIJ", 0, 90), Joint("G_MiddleMPJ", 0, 90),
156  Joint("G_MiddlePIJ", 0, 90), Joint("G_MiddleDIJ", 0, 90),
157  Joint("G_RingMPJ", 0, 90), Joint("G_RingPIJ", 0, 90),
158  Joint("G_RingDIJ", 0, 90), Joint("G_PinkieMPJ", 0, 90),
159  Joint("G_PinkiePIJ", 0, 90), Joint("G_PinkieDIJ", 0, 90),
160  Joint("G_ThumbAb", 50, 0)]
161  self.calibration_steps.append(
163  step_name="Joints 0s, 3s and thumb abduction (THJ4)",
164  step_description=[
165  "Hand flat on a table, fingers joined, thumb opened",
166  "Hand forming a fist, thumb closed"],
167  joints=joints1))
168 
169  # second step: calibrate 4s, TH1, TH2, TH5 and WR2
170  joints2 = [Joint("G_ThumbIJ", 90, 0), Joint("G_ThumbMPJ", 30, -30),
171  Joint("G_MiddleIndexAb", 0, 50), Joint(
172  "G_RingMiddleAb", 0, 50), Joint("G_PinkieRingAb", 0, 50),
173  Joint("G_WristYaw", 10, -30)]
174  self.calibration_steps.append(
175  CalibrationStep(step_name="Joints 4s + TH1, 2, and WR2",
176  step_description=["Hand flat fingers joined, thumb completely curled "
177  "under the palm, wrist 2 bent to the left",
178  "Hand flat fingers apart, thumb completely opened,"
179  " wrist bent 2 to the right"],
180  joints=joints2))
181 
182  # third step: calibrate TH5
183  joints3 = [Joint("G_ThumbRotate", 60, -60)]
184  self.calibration_steps.append(CalibrationStep(step_name="Joint TH5",
185  step_description=["thumb completely under the palm",
186  "thumb completely opened, "
187  "curled over the palm"],
188  joints=joints3))
189 
190  # fourth step: calibrate LF5 and WR1
191  joints4 = [Joint("G_PalmArch", 0, 40), Joint("G_WristPitch", -30, 40)]
192  self.calibration_steps.append(CalibrationStep(step_name="LF5 and WR1",
193  step_description=["Hand flat, wrist 1 bent backward",
194  "Palm curled (LFJ5), wrist 1 bent forward"],
195  joints=joints4))
196 
197  return 0
198 
199  def do_step_min(self, index):
200  """
201  Run the given step of the calibration, gets the min values.
202 
203  @param index: the index of the step in the calibration file
204  @return: 0 when the values were read.
205  """
206  joints = self.calibration_steps[index].joints
207  # display the description
209  self.calibration_steps[index].step_description[0], 0)
210 
211  for joint in joints:
212  name = joint.name
213  # read the min values
214  self.joints[
215  name].raw_min = self.cyberglove.read_raw_average_value(name)
216  self.joints[name].calibrated_min = joint.min
217  # still needs to read the max before fully calibrated
218  self.joints[name].is_calibrated += 0.5
219  return 0
220 
221  def do_step_max(self, index):
222  """
223  Run the given step of the calibration, gets the max values.
224  As this method is called after the do_step_min() method, we don't display the description
225 
226  @param index: the index of the step in the calibration file
227  @return: 0 when the values were read.
228  """
229  joints = self.calibration_steps[index].joints
230  # display the description
232  self.calibration_steps[index].step_description[1], 0)
233 
234  for joint in joints:
235  name = joint.name
236  # read the max values
237  self.joints[
238  name].raw_max = self.cyberglove.read_raw_average_value(name)
239  self.joints[name].calibrated_max = joint.max
240  # still needs to read the max before fully calibrated
241  self.joints[name].is_calibrated += 0.5
242  return 0
243 
244  def is_step_done(self, joint_name):
245  """
246  Check if the joint is calibrated.
247 
248  @param joint_name: the name of the joint
249  @return: 1 if the joint has already been calibrated.
250  """
251 
252  return self.joints[joint_name].is_calibrated
253 
254  def all_steps_done(self):
255  """
256  Check if all the steps were processed.
257 
258  @return: True if all the steps were processed.
259  """
260  for calib in self.joints.values():
261  if calib.is_calibrated != 1:
262  return False
263 
264  return True
265 
267  """
268  Reorder the calibration: set the raw_min to the min raw_value
269  """
270  for name in self.joints.keys():
271  if self.joints[name].raw_min > self.joints[name].raw_max:
272  tmp_raw = self.joints[name].raw_min
273  tmp_cal = self.joints[name].calibrated_min
274  self.joints[name].raw_min = self.joints[name].raw_max
275  self.joints[name].calibrated_min = self.joints[
276  name].calibrated_max
277  self.joints[name].raw_max = tmp_raw
278  self.joints[name].calibrated_max = tmp_cal
279 
280  def check_ranges(self):
281  """
282  Check for zero range calibrations, also for min/max values (-> full sensor range not being used)
283  """
284  self.reorder_calibration()
285  errors = []
286  for name in self.joints.keys():
287  if self.joints[name].raw_min == self.joints[name].raw_max:
288  errors.append("%s zero range: %f. Min modified (-0.001)."
289  % (name, self.joints[name].raw_min))
290  self.joints[name].raw_min -= 0.001
291  if self.joints[name].raw_max == 1.0:
292  errors.append("%s max value is 1.0." % name)
293  if self.joints[name].raw_min == 0.0:
294  errors.append("%s min value is 0.0." % name)
295  for error in errors:
296  rospy.logwarn(error)
297 
298  return errors
299 
300  def write_calibration_file(self, filepath):
301  """
302  Checks if all the steps were processed by calling self.all_steps_done()
303  Reorder the calibration
304  Then writes the whole calibration to a given file.
305 
306  @param filepath: Where to write the calibration
307  @return: 0 if the file has been written,
308  -1 if the calibration is not finished yet,
309  -2 if other error
310  """
311 
312  # Where all the steps processed?
313  if not self.all_steps_done():
314  return -1
315 
316  # reorder the calibration
317  self.reorder_calibration()
318 
319  #
320  # Write to an xml file
321  #
322  # store the text in a table
323 
324  text = []
325  text.append("{'cyberglove_calibration': [")
326 
327  for name in self.joints:
328  cal = self.joints[name]
329  text.append("['%s', [[%f,%f],[%f,%f]]]," % (
330  name, cal.raw_min, cal.calibrated_min, cal.raw_max, cal.calibrated_max))
331 
332  text.append("]}")
333 
334  # write the text to a file
335  try:
336 
337  output = open(filepath, "w")
338 
339  for line in text:
340  output.write(line + "\n")
341  output.close()
342  except:
343  return -2
344 
345  return 0
346 
347  def load_calib(self, filename):
348  if filename == "":
349  return -1
350  try:
351  # TODO(@dg-shadow): remove the literal namespace (rh_calibration) and replace with something dynamic
352  rospy.wait_for_service('/rh_cyberglove/reload_calibration', timeout=5)
353  try:
354  calib = rospy.ServiceProxy(
355  '/rh_cyberglove/reload_calibration', Empty)
356  with open(filename, "r") as f:
357  calibration_dict = yaml.load(f.read())
358  calibration_string = calibration_dict["cyberglove_calibration"]
359  rospy.set_param("/rh_cyberglove/cyberglove_calibration", calibration_string)
360  calib()
361  except rospy.ServiceException, e:
362  print 'Failed to call service: %s' % str(e)
363  return -2
364  except rospy.ROSException, e:
365  print ('Service not found, is the driver running?')
366  return -3
367  return 0
368 
369 
370 #
371 # MAIN #
372 #
373 def main():
374  cyber_calib = CybergloveCalibrer()
375  for i in range(0, len(cyber_calib.calibration_steps)):
376  raw_input(cyber_calib.calibration_steps[i].step_description[0])
377  cyber_calib.do_step_min(i)
378  raw_input(cyber_calib.calibration_steps[i].step_description[1])
379 
380  cyber_calib.do_step_max(i)
381  error = cyber_calib.write_calibration_file("../../param/cyberglove.cal")
382  print error
383 
384  return 0
385 
386 # start the script
387 if __name__ == "__main__":
388  main()
def __init__(self, raw_min=0.0, raw_max=0.0, calibrated_min=0.0, calibrated_max=0.0, is_calibrated=0)
def __init__(self, step_name="", step_description=[""], joints=[])
def __init__(self, description_function=default_description, nb_sensors=22)


sr_gui_cyberglove_calibrator
Author(s): Ugo Cupcic
autogenerated on Wed Oct 14 2020 03:22:50