Advertisement
Guest User

input.py with relativeThrottle

a guest
Aug 19th, 2013
319
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 12.25 KB | None | 0 0
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. #
  4. # || ____ _ __
  5. # +------+ / __ )(_) /_______________ _____ ___
  6. # | 0xBC | / __ / / __/ ___/ ___/ __ `/_ / / _ \
  7. # +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/
  8. # || || /_____/_/\__/\___/_/ \__,_/ /___/\___/
  9. #
  10. # Copyright (C) 2011-2013 Bitcraze AB
  11. #
  12. # Crazyflie Nano Quadcopter Client
  13. #
  14. # This program is free software; you can redistribute it and/or
  15. # modify it under the terms of the GNU General Public License
  16. # as published by the Free Software Foundation; either version 2
  17. # of the License, or (at your option) any later version.
  18. #
  19. # This program is distributed in the hope that it will be useful,
  20. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  21. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  22. # GNU General Public License for more details.
  23.  
  24. # You should have received a copy of the GNU General Public License
  25. # along with this program; if not, write to the Free Software
  26. # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  27.  
  28. """
  29. The input module that will read joysticks/input devices and send control set-points to
  30. the Crazyflie. It will also accept settings from the UI.
  31.  
  32. This module can use different drivers for reading the input device data. Currently it can
  33. just use the PyGame driver but in the future there will be a Linux and Windows driver that can
  34. bypass PyGame.
  35.  
  36. When reading values from inputdevice a config is used to map axis and buttons to control functions
  37. for the Crazyflie.
  38. """
  39.  
  40. __author__ = 'Bitcraze AB'
  41. __all__ = ['JoystickReader']
  42.  
  43. import sys
  44. import time
  45. import os
  46. import glob
  47. import traceback
  48. import logging
  49. import shutil
  50. import copy
  51.  
  52. logger = logging.getLogger(__name__)
  53.  
  54. from pygamereader import PyGameReader
  55. from cfclient.utils.config import Config
  56. from cfclient.utils.config_manager import ConfigManager
  57.  
  58. from PyQt4 import Qt, QtCore, QtGui, uic
  59. from PyQt4.QtCore import *
  60. from PyQt4.QtGui import *
  61. from PyQt4.Qt import *
  62.  
  63. class JoystickReader(QThread):
  64. """Thread that will read input from devices/joysticks and send control-setponts to
  65. the Crazyflie"""
  66. PITCH_AXIS_ID = 0
  67. ROLL_AXIS_ID = 1
  68. YAW_AXIS_ID = 2
  69. THRUST_AXIS_ID = 3
  70.  
  71. # Incomming signal to start input capture
  72. startInputSignal = pyqtSignal(str, str)
  73. # Incomming signal to stop input capture
  74. stopInputSignal = pyqtSignal()
  75. # Incomming signal to set min/max thrust
  76. updateMinMaxThrustSignal = pyqtSignal(int, int)
  77. # Incomming signal to set roll/pitch calibration
  78. update_trim_roll_signal = pyqtSignal(float)
  79. update_trim_pitch_signal = pyqtSignal(float)
  80. # Incomming signal to set max roll/pitch angle
  81. updateMaxRPAngleSignal = pyqtSignal(int)
  82. # Incomming signal to set max yaw rate
  83. updateMaxYawRateSignal = pyqtSignal(int)
  84. # Incomming signal to set thrust lowering slew rate limiting
  85. updateThrustLoweringSlewrateSignal = pyqtSignal(int, int)
  86.  
  87. # Configure the aixs: Axis id, joystick axis id and inverse or not
  88. updateAxisConfigSignal = pyqtSignal(int, int, float)
  89. # Start detection of variation for joystick axis
  90. detectAxisVarSignal = pyqtSignal()
  91.  
  92. # Outgoing signal for device found
  93. inputUpdateSignal = pyqtSignal(float, float, float, float)
  94. # Outgoing signal for when pitch/roll calibration has been updated
  95. calUpdateSignal = pyqtSignal(float, float)
  96. # Outgoing signal for emergency stop
  97. emergencyStopSignal = pyqtSignal(bool)
  98.  
  99. inputDeviceErrorSignal = pyqtSignal('QString')
  100.  
  101. sendControlSetpointSignal = pyqtSignal(float, float, float, int)
  102.  
  103. discovery_signal = pyqtSignal(object)
  104.  
  105. inputConfig = []
  106.  
  107. def __init__(self):
  108. QThread.__init__(self)
  109. #self.moveToThread(self)
  110.  
  111. # TODO: Should be OS dependant
  112. self.inputdevice = PyGameReader()
  113.  
  114. self.startInputSignal.connect(self.startInput)
  115. self.stopInputSignal.connect(self.stopInput)
  116. self.updateMinMaxThrustSignal.connect(self.updateMinMaxThrust)
  117. self.update_trim_roll_signal.connect(self._update_trim_roll)
  118. self.update_trim_pitch_signal.connect(self._update_trim_pitch)
  119. self.updateMaxRPAngleSignal.connect(self.updateMaxRPAngle)
  120. self.updateThrustLoweringSlewrateSignal.connect(self.updateThrustLoweringSlewrate)
  121. self.updateMaxYawRateSignal.connect(self.updateMaxYawRate)
  122.  
  123. self.maxRPAngle = 0
  124. self.thrustDownSlew = 0
  125. self.thrustSlewEnabled = False
  126. self.slewEnableLimit = 0
  127. self.maxYawRate = 0
  128. self.detectAxis = False
  129. self.emergencyStop = False
  130.  
  131. self.relativeThrust = True
  132.  
  133. self.thrust = 0.0
  134. self.oldThrust = 0
  135.  
  136. self._trim_roll = Config().get("trim_roll")
  137. self._trim_pitch = Config().get("trim_pitch")
  138.  
  139. # TODO: The polling interval should be set from config file
  140. self.readTimer = QTimer()
  141. self.readTimer.setInterval(10);
  142. self.connect(self.readTimer, SIGNAL("timeout()"), self.readInput)
  143.  
  144. self._discovery_timer = QTimer()
  145. self._discovery_timer.setInterval(1000);
  146. self.connect(self._discovery_timer, SIGNAL("timeout()"), self._do_device_discovery)
  147. self._discovery_timer.start()
  148.  
  149. self._available_devices = {}
  150.  
  151. # Check if user config exists, otherwise copy files
  152. if not os.path.isdir(ConfigManager().configs_dir):
  153. logger.info("No user config found, copying dist files")
  154. os.makedirs(ConfigManager().configs_dir)
  155. for f in glob.glob(sys.path[0] + "/cfclient/configs/input/[A-Za-z]*.json"):
  156. shutil.copy2(f, ConfigManager().configs_dir)
  157.  
  158. def _do_device_discovery(self):
  159. devs = self.getAvailableDevices()
  160.  
  161. if len(devs):
  162. self.discovery_signal.emit(devs)
  163. self._discovery_timer.stop()
  164.  
  165. def getAvailableDevices(self):
  166. """List all available input devices."""
  167. devs = self.inputdevice.getAvailableDevices()
  168.  
  169. for d in devs:
  170. self._available_devices[d["name"]] = d["id"]
  171.  
  172. return devs
  173.  
  174. def enableRawReading(self, deviceId):
  175. """Enable raw reading of the input device with id deviceId. This is used to
  176. get raw values for setting up of input devices. Values are read without using a mapping."""
  177. self.inputdevice.enableRawReading(deviceId)
  178.  
  179. def disableRawReading(self):
  180. """Disable raw reading of input device."""
  181. self.inputdevice.disableRawReading()
  182.  
  183. def readRawValues(self):
  184. """ Read raw values from the input device."""
  185. return self.inputdevice.readRawValues()
  186.  
  187. # Fix for Ubuntu... doing self.moveToThread will not work without this
  188. # since it seems that the default implementation of run will not call exec_ to process
  189. # events.
  190. def run(self):
  191. self.exec_()
  192.  
  193. @pyqtSlot(str, str)
  194. def startInput(self, device_name, config_name):
  195. """Start reading input from the device with name device_name using config config_name"""
  196. try:
  197. device_id = self._available_devices[device_name]
  198. self.inputdevice.startInput(device_id, ConfigManager().get_config(config_name))
  199. self.readTimer.start()
  200. except Exception:
  201. self.inputDeviceErrorSignal.emit("Error while opening/initializing input device\n\n%s" % (traceback.format_exc()))
  202.  
  203. @pyqtSlot()
  204. def stopInput(self):
  205. """Stop reading from the input device."""
  206. self.readTimer.stop()
  207.  
  208. @pyqtSlot(int)
  209. def updateMaxYawRate(self, maxRate):
  210. """Set a new max yaw rate value."""
  211. self.maxYawRate = maxRate
  212.  
  213. @pyqtSlot(int)
  214. def updateMaxRPAngle(self, maxAngle):
  215. """Set a new max roll/pitch value."""
  216. self.maxRPAngle = maxAngle
  217.  
  218. @pyqtSlot(int, int)
  219. def updateThrustLoweringSlewrate(self, thrustDownSlew, slewLimit):
  220. """Set new values for limit where the slewrate control kicks in and
  221. for the slewrate."""
  222. self.thrustDownSlew = thrustDownSlew
  223. self.slewEnableLimit = slewLimit
  224. if (thrustDownSlew > 0):
  225. self.thrustSlewEnabled = True
  226. else:
  227. self.thrustSlewEnabled = False
  228.  
  229. def setCrazyflie(self, cf):
  230. """Set the referance for the Crazyflie"""
  231. self.cf = cf
  232.  
  233. @pyqtSlot(int, int)
  234. def updateMinMaxThrust(self, minThrust, maxThrust):
  235. """Set a new min/max thrust limit."""
  236. self.minThrust = minThrust
  237. self.maxThrust = maxThrust
  238.  
  239. @pyqtSlot(float)
  240. def _update_trim_roll(self, trim_roll):
  241. """Set a new value for the roll trim."""
  242. self._trim_roll = trim_roll
  243.  
  244. @pyqtSlot(float)
  245. def _update_trim_pitch(self, trim_pitch):
  246. """Set a new value for the trim trim."""
  247. self._trim_pitch = trim_pitch
  248.  
  249. @pyqtSlot()
  250. def readInput(self):
  251. """Read input data from the selected device"""
  252. try:
  253. data = self.inputdevice.readInput()
  254. roll = data["roll"] * self.maxRPAngle
  255. pitch = data["pitch"] * self.maxRPAngle
  256. yaw = data["yaw"]
  257. thrust = data["thrust"]
  258. raw_thrust = data["thrust"]
  259. emergency_stop = data["estop"]
  260. trim_roll = data["rollcal"]
  261. trim_pitch = data["pitchcal"]
  262.  
  263. if self.emergencyStop != emergency_stop:
  264. self.emergencyStop = emergency_stop
  265. self.emergencyStopSignal.emit(self.emergencyStop)
  266.  
  267.  
  268. # Thust limiting (slew, minimum and emergency stop)
  269. if self.relativeThrust:
  270. self.thrust += raw_thrust * 0.015;
  271.  
  272. if self.thrust < 0: self.thrust = 0.0
  273. if self.thrust > 1: self.thrust = 1.0
  274.  
  275. thrust = self.thrust * 65535;
  276. if thrust > self.maxThrust: thrust = self.maxThrust
  277. #if thrust < self.minThrust: thrust = self.minThrust
  278.  
  279. else:
  280. # Thust limiting (slew, minimum and emergency stop)
  281. if raw_thrust<0.05 or emergency_stop:
  282. thrust=0
  283. else:
  284. thrust = self.minThrust + thrust * (self.maxThrust - self.minThrust)
  285.  
  286. if self.thrustSlewEnabled == True and self.slewEnableLimit > thrust and not emergency_stop:
  287. if self.oldThrust > self.slewEnableLimit:
  288. self.oldThrust = self.slewEnableLimit
  289. if thrust < (self.oldThrust - (self.thrustDownSlew/100)):
  290. thrust = self.oldThrust - self.thrustDownSlew/100
  291. if raw_thrust < 0 or thrust < self.minThrust:
  292. thrust = 0
  293. if raw_thrust < 0.05 or emergency_stop: thrust=0
  294.  
  295. if emergency_stop: self.thrust = 0.
  296. self.oldThrust = thrust
  297.  
  298. #logger.info("raw_thurst: %f, thrust: %f, self.thrust: %f", raw_thrust, thrust, self.thrust);
  299.  
  300.  
  301. # Yaw deadband
  302. # TODO: Add to input device config?
  303. if yaw < -0.2 or yaw > 0.2:
  304. if yaw < 0:
  305. yaw = (yaw + 0.2) * self.maxYawRate * 1.25
  306. else:
  307. yaw = (yaw - 0.2) * self.maxYawRate * 1.25
  308. else:
  309. self.yaw = 0
  310.  
  311. if trim_roll != 0 or trim_pitch != 0:
  312. self._trim_roll += trim_roll
  313. self._trim_pitch += trim_pitch
  314. self.calUpdateSignal.emit(self._trim_roll, self._trim_pitch)
  315.  
  316. self.inputUpdateSignal.emit(roll, pitch, yaw, thrust)
  317. trimmed_rol = roll + self._trim_roll
  318. trimmed_pitch = pitch + self._trim_pitch
  319. self.sendControlSetpointSignal.emit(trimmed_rol, trimmed_pitch,
  320. yaw, thrust)
  321. except Exception:
  322. logger.warning("Exception while reading inputdevice: %s", traceback.format_exc())
  323. self.inputDeviceErrorSignal.emit("Error reading from input device\n\n%s"%traceback.format_exc())
  324. self.readTimer.stop()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement