gyroscope.py
Go to the documentation of this file.
00001 #!/usr/bin/env python
00002 
00003 # Software License Agreement (BSD License)
00004 #
00005 # Copyright (c) 2011, Robot Control and Pattern Recognition Group, Warsaw University of Technology
00006 #
00007 # All rights reserved.
00008 # 
00009 # Redistribution and use in source and binary forms, with or without
00010 # modification, are permitted provided that the following conditions are met:
00011 #     * Redistributions of source code must retain the above copyright
00012 #       notice, this list of conditions and the following disclaimer.
00013 #     * Redistributions in binary form must reproduce the above copyright
00014 #       notice, this list of conditions and the following disclaimer in the
00015 #       documentation and/or other materials provided with the distribution.
00016 #     * Neither the name of the <organization> nor the
00017 #       names of its contributors may be used to endorse or promote products
00018 #       derived from this software without specific prior written permission.
00019 # 
00020 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
00021 # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
00022 # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
00023 # DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
00024 # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
00025 # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00026 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
00027 # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00028 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00029 # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00030 
00031 import roslib
00032 roslib.load_manifest('elektron_sensors')
00033 
00034 import rospy
00035 
00036 from sensor_msgs.msg import Imu
00037 from std_msgs.msg import Bool
00038 from std_srvs.srv import Empty, EmptyResponse
00039 
00040 from PyKDL import Rotation
00041 
00042 import serial
00043 import math
00044 
00045 
00046 class Gyro:
00047     """ 
00048     Class for interfacing with gyroscope on Elektron mobile robot.
00049     Responsible for retrieving data from device, calibration and
00050     calculating current orientation. 
00051     """
00052 
00053     def __init__(self):
00054         # open serial port
00055         self.device = rospy.get_param('~device', '/dev/ttyUSB0')
00056         self.baud = rospy.get_param('~baud', 38400)
00057         self.ser = serial.Serial(self.device, self.baud, timeout=1)
00058 
00059         # reset variables
00060         self.orientation = 0
00061         self.bias = 0
00062 
00063         self.calibrating = False
00064 
00065         self.frame_id = 'base_footprint'
00066         self.prev_time = rospy.Time.now()
00067 
00068         # publisher with imu data
00069         self.pub = rospy.Publisher("/imu/data", Imu)
00070         
00071         # rotation scale
00072         self.scale = rospy.get_param('~rot_scale', 1.0)
00073         
00074         # service for calibrating gyro bias
00075         rospy.Service("/imu/calibrate", Empty, self.calibrateCallback)
00076         
00077         # publisher with calibration state
00078         self.is_calibratedPublisher = rospy.Publisher('/imu/is_calibrated', Bool, latch=True)
00079         
00080         # We'll always just reuse this msg object:        
00081         self.is_CalibratedResponseMsg = Bool();
00082 
00083         # Initialize the latched is_calibrated state. 
00084         # At the beginning calibration is assumed to be done
00085 
00086         self.is_CalibratedResponseMsg.data = True;
00087         self.is_calibratedPublisher.publish(self.is_CalibratedResponseMsg)
00088 
00089     def calibrate(self):
00090         # calibration routine
00091         rospy.loginfo("Calibrating Gyro. Don't move the robot now")
00092         start_time = rospy.Time.now()
00093         cal_duration = rospy.Duration(5.0)
00094         offset = 0
00095         cnt = 0
00096         # flush input buffer to calibrate on newest data  
00097         self.ser.flushInput()
00098         while rospy.Time.now() < start_time + cal_duration:
00099 
00100             # get line from device
00101             str = self.ser.readline()
00102             nums = str.split()
00103 
00104             # check, if it was correct line
00105             if (len(nums) != 5):
00106                 continue
00107 
00108             cnt += 1
00109             gyro = int(nums[2])
00110             ref = int(nums[3])
00111             temp = int(nums[4])
00112 
00113             val = ref-gyro;
00114 
00115             offset += val;
00116 
00117         self.bias = 1.0 * offset / cnt
00118         rospy.loginfo("Gyro calibrated with offset %f"%self.bias)
00119         
00120         # Update the latched is_calibrated state:
00121         self.is_CalibratedResponseMsg.data = True
00122         self.is_calibratedPublisher.publish(self.is_CalibratedResponseMsg)
00123         
00124         return True
00125 
00126 
00127 
00128     def calibrateCallback(self, req):
00129         """The imu/calibrate service handler."""
00130           
00131         rospy.loginfo("Calibration request")
00132                 
00133         self.calibrating = True
00134         
00135         return EmptyResponse()
00136       
00137 
00138     def spin(self):
00139         self.prev_time = rospy.Time.now()
00140 
00141         while not rospy.is_shutdown():
00142             if self.calibrating:
00143                 self.calibrate()
00144                 self.calibrating = False
00145                 self.prev_time = rospy.Time.now()
00146             
00147             # prepare Imu frame
00148             imu = Imu()
00149             imu.header.frame_id = self.frame_id
00150 
00151             # get line from device
00152             str = self.ser.readline()
00153 
00154             # timestamp
00155             imu.header.stamp = rospy.Time.now()
00156 
00157             nums = str.split()
00158 
00159             # check, if it was correct line
00160             if (len(nums) != 5):
00161                 continue
00162 
00163             gyro = int(nums[2])
00164             ref = int(nums[3])
00165             temp = int(nums[4])
00166 
00167             val = (ref-gyro - self.bias) * 1000 / 3 / 1024 * self.scale
00168 
00169             imu.angular_velocity.x = 0
00170             imu.angular_velocity.y = 0
00171             imu.angular_velocity.z = val * math.pi / 180
00172             imu.angular_velocity_covariance = [0, 0, 0, 0, 0, 0, 0, 0, 1]
00173             imu.orientation_covariance = [0.001, 0, 0, 0, 0.001, 0, 0, 0, 0.1]
00174 
00175             self.orientation += imu.angular_velocity.z * (imu.header.stamp - self.prev_time).to_sec()
00176             self.prev_time = imu.header.stamp
00177             (imu.orientation.x, imu.orientation.y, imu.orientation.z, imu.orientation.w) = Rotation.RotZ(self.orientation).GetQuaternion()
00178             self.pub.publish(imu)
00179 
00180 
00181 
00182 if __name__ == '__main__':
00183     rospy.init_node('gyroscope')
00184     gyro = Gyro()
00185     gyro.calibrate()
00186 
00187     try:
00188         gyro.spin()
00189     except rospy.ROSInterruptException: pass
00190     except IOError: pass
00191     except KeyError: pass


elektron_sensors
Author(s): Maciej Stefanczyk
autogenerated on Mon Oct 6 2014 10:26:35