00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035 """The wiimote_node broadcasts four topics, and listens to messages that control
00036 the Wiimote stick's rumble (vibration) and LEDs. Transmitted topics (@100Hz):
00037
00038 o wiijoy Messages contain the accelerometer and gyro axis data, and all button states.
00039 o imu/data Messages contain the accelerometer and gyro axis data, and covariance matrices
00040 o wiimote/state the wiijoy and /imu messages, the button states, the LED states,
00041 rumble (i.e. vibrator) state, IR light sensor readings, time since last zeroed,
00042 and battery state. See State.msg
00043 o imu/is_calibrated Latched message
00044 o nunchuk Joy messages using the nunchuk as a joystick
00045 o classic Joy messages using the nunchuck as a joystic
00046
00047 The node listens to the following messages:
00048
00049 o joy/set_feedback
00050 Topic that listens to sensor_msgs/JoyFeedbackArray messages. This controls the LEDs and the Rumble. There are 4 LEDs with ids of 0 through 3. The "Player 1" LED is id 0; the "Player 4" LED is id3. The Wiimote only has one rumble, so it is id 0.
00051 o imu/calibrate
00052 Request to calibrate the device.
00053
00054 No command line parameters.
00055 """
00056
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066
00067
00068
00069
00070
00071 import sys
00072 import threading
00073 import traceback
00074 import time
00075
00076
00077
00078 import rospy
00079 from geometry_msgs.msg import Vector3
00080 from sensor_msgs.msg import Imu
00081 from std_srvs.srv import Empty
00082 from std_srvs.srv import EmptyResponse
00083 from std_msgs.msg import Bool
00084 from sensor_msgs.msg import Joy
00085 from sensor_msgs.msg import JoyFeedback
00086 from sensor_msgs.msg import JoyFeedbackArray
00087 from wiimote.msg import IrSourceInfo
00088 from wiimote.msg import State
00089
00090
00091 from wiimote.wiimoteExceptions import *
00092 from wiimote.wiimoteConstants import *
00093 import wiimote.WIIMote
00094 import wiimote.wiiutils
00095
00096 GATHER_CALIBRATION_STATS = True
00097
00098 class WiimoteNode():
00099
00100
00101 def runWiimoteNode(self):
00102 """Initialize the wiimote_node, establishing its name for communication with the Master"""
00103
00104
00105
00106
00107 rospy.init_node('wiimote', anonymous=True, log_level=rospy.ERROR)
00108 wiimoteDevice = wiimote.WIIMote.WIIMote()
00109 wiimoteDevice.zeroDevice()
00110
00111 try:
00112 IMUSender(wiimoteDevice, freq=100).start()
00113 JoySender(wiimoteDevice, freq=100).start()
00114 WiiSender(wiimoteDevice, freq=100).start()
00115 NunSender(wiimoteDevice, freq=100).start()
00116 ClasSender(wiimoteDevice, freq=100).start()
00117 WiimoteListeners(wiimoteDevice).start()
00118
00119 rospy.spin()
00120
00121 except:
00122 rospy.loginfo("Error in startup")
00123 rospy.loginfo(sys.exc_info()[0])
00124 finally:
00125 try:
00126 wiimoteDevice.setRumble(False)
00127 wiimoteDevice.setLEDs([False, False, False, False])
00128 wiimoteDevice.shutdown()
00129 except:
00130 pass
00131
00132 def shutdown(self):
00133 try:
00134 IMUSender.stop
00135 JoySender.stop
00136 WiiSender.stop
00137 NunSender.stop
00138 WiimoteListener.stop
00139 except:
00140 pass
00141
00142 class WiimoteDataSender(threading.Thread):
00143
00144 def __init__(self, wiiMote, freq=100):
00145
00146 threading.Thread.__init__(self)
00147 self.wiiMote = wiiMote
00148 self.freq = freq
00149 self.sleepDuration = 1.0 / freq
00150
00151 varianceAccelerator = self.wiiMote.getVarianceAccelerator();
00152 self.linear_acceleration_covariance = [varianceAccelerator[X], 0., 0.,
00153 0., varianceAccelerator[Y], 0.,
00154 0., 0., varianceAccelerator[Z]]
00155
00156 varianceGyro = self.wiiMote.getVarianceGyro();
00157 self.angular_velocity_covariance = [varianceGyro[X], 0., 0.,
00158 0., varianceGyro[Y], 0.,
00159 0., 0., varianceGyro[Z]]
00160
00161
00162
00163
00164 self.gyroAbsence_covariance = [-1., 0., 0.,
00165 0., 0., 0.,
00166 0., 0., 0.]
00167
00168 def obtainWiimoteData(self):
00169 """Retrieve one set of Wiimote measurements from the Wiimote instance. Return scaled accelerator and gyro readings.
00170
00171 We canonicalize both accelerator and gyro data through
00172 scaling them by constants that turn them into m/sec^2, and
00173 radians/sec, respectively.
00174
00175 Return: list of canonicalized accelerator and gyro readings.
00176 """
00177
00178 while not rospy.is_shutdown():
00179 self.wiistate = self.wiiMote.getWiimoteState()
00180 if self.wiistate is not None and self.wiistate.acc is not None:
00181 break
00182 else:
00183 rospy.sleep(0.1)
00184
00185 return self.canonicalizeWiistate()
00186
00187 def canonicalizeWiistate(self):
00188 """Scale accelerator, nunchuk accelerator, and gyro readings to be m/sec^2, m/sec^2 and radians/sec, respectively."""
00189
00190 try:
00191
00192 canonicalAccel = self.wiistate.acc.scale(EARTH_GRAVITY)
00193
00194
00195
00196 if self.wiistate.nunchukPresent:
00197 canonicalNunchukAccel = self.wiistate.nunchukAcc.scale(EARTH_GRAVITY)
00198 else:
00199 canonicalNunchukAccel = None
00200
00201
00202
00203
00204 if self.wiistate.motionPlusPresent:
00205 canonicalAngleRate = self.wiistate.angleRate.scale(GYRO_SCALE_FACTOR)
00206 else:
00207 canonicalAngleRate = None
00208
00209 return [canonicalAccel, canonicalNunchukAccel, canonicalAngleRate]
00210 except AttributeError:
00211
00212
00213 rospy.loginfo(self.threadName + " shutting down.")
00214 exit(0)
00215
00216
00217 class IMUSender(WiimoteDataSender):
00218 """Broadcasting Wiimote accelerator and gyro readings as IMU messages to Topic sensor_data/Imu"""
00219
00220 def __init__(self, wiiMote, freq=100):
00221 """Initializes the Wiimote IMU publisher.
00222
00223 Parameters:
00224 wiiMote: a bluetooth-connected, calibrated WIIMote instance
00225 freq: the message sending frequency in messages/sec. Max is 100, because
00226 the Wiimote only samples the sensors at 100Hz.
00227 """
00228
00229 WiimoteDataSender.__init__(self, wiiMote, freq)
00230
00231 self.pub = rospy.Publisher('imu/data', Imu)
00232
00233 def run(self):
00234 """Loop that obtains the latest wiimote state, publishes the IMU data, and sleeps.
00235
00236 The IMU message, if fully filled in, contains information on orientation,
00237 acceleration (in m/s^2), and angular rate (in radians/sec). For each of
00238 these quantities, the IMU message format also wants the corresponding
00239 covariance matrix.
00240
00241 Wiimote only gives us acceleration and angular rate. So we ensure that the orientation
00242 data entry is marked invalid. We do this by setting the first
00243 entry of its associated covariance matrix to -1. The covariance
00244 matrices are the 3x3 matrix with the axes' variance in the
00245 diagonal. We obtain the variance from the Wiimote instance.
00246 """
00247
00248 rospy.loginfo("Wiimote IMU publisher starting (topic /imu/data).")
00249 self.threadName = "IMU topic Publisher"
00250 try:
00251 while not rospy.is_shutdown():
00252 (canonicalAccel, canonicalNunchukAccel, canonicalAngleRate) = self.obtainWiimoteData()
00253
00254 msg = Imu(header=None,
00255 orientation=None,
00256 orientation_covariance=[-1.,0.,0.,0.,0.,0.,0.,0.,0.],
00257 angular_velocity=None,
00258 angular_velocity_covariance=self.angular_velocity_covariance,
00259 linear_acceleration=None,
00260 linear_acceleration_covariance=self.linear_acceleration_covariance)
00261
00262
00263
00264
00265
00266
00267 if self.wiistate.motionPlusPresent:
00268 msg.angular_velocity.x = canonicalAngleRate[PHI]
00269 msg.angular_velocity.y = canonicalAngleRate[THETA]
00270 msg.angular_velocity.z = canonicalAngleRate[PSI]
00271 else:
00272 msg.angular_velocity_covariance = self.gyroAbsence_covariance
00273
00274 msg.linear_acceleration.x = canonicalAccel[X]
00275 msg.linear_acceleration.y = canonicalAccel[Y]
00276 msg.linear_acceleration.z = canonicalAccel[Z]
00277
00278 measureTime = self.wiistate.time
00279 timeSecs = int(measureTime)
00280 timeNSecs = int(abs(timeSecs - measureTime) * 10**9)
00281 msg.header.stamp.secs = timeSecs
00282 msg.header.stamp.nsecs = timeNSecs
00283
00284 try:
00285 self.pub.publish(msg)
00286 except rospy.ROSException:
00287 rospy.loginfo("Topic imu/data closed. Shutting down Imu sender.")
00288 exit(0)
00289
00290
00291
00292 rospy.sleep(self.sleepDuration)
00293 except rospy.ROSInterruptException:
00294 rospy.loginfo("Shutdown request. Shutting down Imu sender.")
00295 exit(0)
00296
00297
00298 class JoySender(WiimoteDataSender):
00299
00300 """Broadcasting Wiimote accelerator and gyro readings as Joy(stick) messages to Topic joy"""
00301
00302 def __init__(self, wiiMote, freq=100):
00303 """Initializes the Wiimote Joy(stick) publisher.
00304
00305 Parameters:
00306 wiiMote: a bluetooth-connected, calibrated WIIMote instance
00307 freq: the message sending frequency in messages/sec. Max is 100, because
00308 the Wiimote only samples the sensors at 100Hz.
00309 """
00310
00311 WiimoteDataSender.__init__(self, wiiMote, freq)
00312
00313
00314 self.pub = rospy.Publisher('joy', Joy)
00315
00316 def run(self):
00317 """Loop that obtains the latest wiimote state, publishes the joystick data, and sleeps.
00318
00319 The Joy.msg message types calls for just two fields: float32[] axes, and int32[] buttons.
00320 """
00321
00322 rospy.loginfo("Wiimote joystick publisher starting (topic wiijoy).")
00323 self.threadName = "Joy topic Publisher"
00324 try:
00325 while not rospy.is_shutdown():
00326 (canonicalAccel, canonicalNunchukAccel, canonicalAngleRate) = self.obtainWiimoteData()
00327
00328 msg = Joy(header=None,
00329 axes=[canonicalAccel[X], canonicalAccel[Y], canonicalAccel[Z]],
00330 buttons=None)
00331
00332
00333
00334 if self.wiistate.motionPlusPresent:
00335 msg.axes.extend([canonicalAngleRate[PHI], canonicalAngleRate[THETA], canonicalAngleRate[PSI]])
00336
00337
00338
00339
00340 theButtons = [False,False,False,False,False,False,False,False,False,False,False]
00341 theButtons[State.MSG_BTN_1] = self.wiistate.buttons[BTN_1]
00342 theButtons[State.MSG_BTN_2] = self.wiistate.buttons[BTN_2]
00343 theButtons[State.MSG_BTN_A] = self.wiistate.buttons[BTN_A]
00344 theButtons[State.MSG_BTN_B] = self.wiistate.buttons[BTN_B]
00345 theButtons[State.MSG_BTN_PLUS] = self.wiistate.buttons[BTN_PLUS]
00346 theButtons[State.MSG_BTN_MINUS] = self.wiistate.buttons[BTN_MINUS]
00347 theButtons[State.MSG_BTN_LEFT] = self.wiistate.buttons[BTN_LEFT]
00348 theButtons[State.MSG_BTN_RIGHT] = self.wiistate.buttons[BTN_RIGHT]
00349 theButtons[State.MSG_BTN_UP] = self.wiistate.buttons[BTN_UP]
00350 theButtons[State.MSG_BTN_DOWN] = self.wiistate.buttons[BTN_DOWN]
00351 theButtons[State.MSG_BTN_HOME] = self.wiistate.buttons[BTN_HOME]
00352
00353 msg.buttons = theButtons
00354
00355 measureTime = self.wiistate.time
00356 timeSecs = int(measureTime)
00357 timeNSecs = int(abs(timeSecs - measureTime) * 10**9)
00358
00359 msg.header.stamp.secs = timeSecs
00360 msg.header.stamp.nsecs = timeNSecs
00361
00362 try:
00363 self.pub.publish(msg)
00364 except rospy.ROSException:
00365 rospy.loginfo("Topic wiijoy closed. Shutting down Joy sender.")
00366 exit(0)
00367
00368
00369
00370 rospy.sleep(self.sleepDuration)
00371 except rospy.ROSInterruptException:
00372 rospy.loginfo("Shutdown request. Shutting down Joy sender.")
00373 exit(0)
00374
00375 class NunSender(WiimoteDataSender):
00376
00377 """Broadcasting nunchuk accelerator and joystick readings as Joy(stick) messages to Topic joy"""
00378
00379 def __init__(self, wiiMote, freq=100):
00380 """Initializes the nunchuk Joy(stick) publisher.
00381
00382 Parameters:
00383 wiiMote: a bluetooth-connected, calibrated WIIMote instance
00384 freq: the message sending frequency in messages/sec. Max is 100, because
00385 the Wiimote only samples the sensors at 100Hz.
00386 """
00387
00388 WiimoteDataSender.__init__(self, wiiMote, freq)
00389
00390
00391
00392
00393
00394 self.pub = None
00395
00396 def run(self):
00397 """Loop that obtains the latest nunchuk state, publishes the joystick data, and sleeps.
00398
00399 The Joy.msg message types calls for just two fields: float32[] axes, and int32[] buttons.
00400 """
00401
00402 self.threadName = "nunchuk Joy topic Publisher"
00403 try:
00404 while not rospy.is_shutdown():
00405 rospy.sleep(self.sleepDuration)
00406 (canonicalAccel, scaledAcc, canonicalAngleRate) = self.obtainWiimoteData()
00407 if not self.wiistate.nunchukPresent:
00408 continue
00409 if self.pub is None:
00410 self.pub = rospy.Publisher('/wiimote/nunchuk', Joy)
00411 rospy.loginfo("Wiimote Nunchuk joystick publisher starting (topic nunchuk).")
00412
00413 (joyx, joyy) = self.wiistate.nunchukStick
00414
00415 msg = Joy(header=None,
00416 axes=[joyx, joyy,
00417 scaledAcc[X], scaledAcc[Y], scaledAcc[Z]],
00418 buttons=None)
00419
00420 theButtons = [False,False]
00421 theButtons[State.MSG_BTN_Z] = self.wiistate.nunchukButtons[BTN_Z]
00422 theButtons[State.MSG_BTN_C] = self.wiistate.nunchukButtons[BTN_C]
00423
00424 msg.buttons = theButtons
00425
00426 measureTime = self.wiistate.time
00427 timeSecs = int(measureTime)
00428 timeNSecs = int(abs(timeSecs - measureTime) * 10**9)
00429 msg.header.stamp.secs = timeSecs
00430 msg.header.stamp.nsecs = timeNSecs
00431
00432 try:
00433 self.pub.publish(msg)
00434 except rospy.ROSException:
00435 rospy.loginfo("Topic /wiimote/nunchuk closed. Shutting down Nun sender.")
00436 exit(0)
00437
00438
00439
00440
00441 except rospy.ROSInterruptException:
00442 rospy.loginfo("Shutdown request. Shutting down Nun sender.")
00443 exit(0)
00444
00445 class ClasSender(WiimoteDataSender):
00446
00447 """Broadcasting Classic Controller joystick readings as Joy(stick) messages to Topic joy"""
00448
00449 def __init__(self, wiiMote, freq=100):
00450 """Initializes the Classic Controller Joy(stick) publisher.
00451
00452 Parameters:
00453 wiiMote: a bluetooth-connected, calibrated WIIMote instance
00454 freq: the message sending frequency in messages/sec. Max is 100, because
00455 the Wiimote only samples the sensors at 100Hz.
00456 """
00457
00458 WiimoteDataSender.__init__(self, wiiMote, freq)
00459
00460
00461
00462 self.pub = None
00463
00464 def run(self):
00465 """Loop that obtains the latest classic controller state, publishes the joystick data, and sleeps.
00466
00467 The Joy.msg message types calls for just two fields: float32[] axes, and int32[] buttons.
00468 """
00469
00470 self.threadName = "Classic Controller Joy topic Publisher"
00471 try:
00472 while not rospy.is_shutdown():
00473 rospy.sleep(self.sleepDuration)
00474 self.obtainWiimoteData()
00475
00476 if not self.wiistate.classicPresent:
00477 continue
00478 if self.pub is None:
00479 self.pub = rospy.Publisher('/wiimote/classic', Joy)
00480 rospy.loginfo("Wiimote Classic Controller joystick publisher starting (topic /wiimote/classic).")
00481
00482 (l_joyx, l_joyy) = self.wiistate.classicStickLeft
00483 (r_joyx, r_joyy) = self.wiistate.classicStickRight
00484
00485 l_joyx = -(l_joyx-33)/27.
00486 l_joyy = (l_joyy-33)/27.
00487 r_joyx = -(r_joyx-15)/13.
00488 r_joyy = (r_joyy-15)/13.
00489
00490 if abs(l_joyx) < .05:
00491 l_joyx = 0
00492 if abs(l_joyy) < .05:
00493 l_joyy = 0
00494 if abs(r_joyx) < .05:
00495 r_joyx = 0
00496 if abs(r_joyy) < .05:
00497 r_joyy = 0
00498
00499 msg = Joy(header=None,
00500 axes=[l_joyx, l_joyy,r_joyx, r_joyy],
00501 buttons=None)
00502
00503 theButtons = [False,False,False,False,False,False,False,False,False,False,False,False,False,False,False]
00504 theButtons[State.MSG_CLASSIC_BTN_X] = self.wiistate.classicButtons[CLASSIC_BTN_X]
00505 theButtons[State.MSG_CLASSIC_BTN_Y] = self.wiistate.classicButtons[CLASSIC_BTN_Y]
00506 theButtons[State.MSG_CLASSIC_BTN_A] = self.wiistate.classicButtons[CLASSIC_BTN_A]
00507 theButtons[State.MSG_CLASSIC_BTN_B] = self.wiistate.classicButtons[CLASSIC_BTN_B]
00508 theButtons[State.MSG_CLASSIC_BTN_PLUS] = self.wiistate.classicButtons[CLASSIC_BTN_PLUS]
00509 theButtons[State.MSG_CLASSIC_BTN_MINUS] = self.wiistate.classicButtons[CLASSIC_BTN_MINUS]
00510 theButtons[State.MSG_CLASSIC_BTN_LEFT] = self.wiistate.classicButtons[CLASSIC_BTN_LEFT]
00511 theButtons[State.MSG_CLASSIC_BTN_RIGHT] = self.wiistate.classicButtons[CLASSIC_BTN_RIGHT]
00512 theButtons[State.MSG_CLASSIC_BTN_UP] = self.wiistate.classicButtons[CLASSIC_BTN_UP]
00513 theButtons[State.MSG_CLASSIC_BTN_DOWN] = self.wiistate.classicButtons[CLASSIC_BTN_DOWN]
00514 theButtons[State.MSG_CLASSIC_BTN_HOME] = self.wiistate.classicButtons[CLASSIC_BTN_HOME]
00515 theButtons[State.MSG_CLASSIC_BTN_L] = self.wiistate.classicButtons[CLASSIC_BTN_L]
00516 theButtons[State.MSG_CLASSIC_BTN_R] = self.wiistate.classicButtons[CLASSIC_BTN_R]
00517 theButtons[State.MSG_CLASSIC_BTN_ZL] = self.wiistate.classicButtons[CLASSIC_BTN_ZL]
00518 theButtons[State.MSG_CLASSIC_BTN_ZR] = self.wiistate.classicButtons[CLASSIC_BTN_ZR]
00519
00520 msg.buttons = theButtons
00521
00522 measureTime = self.wiistate.time
00523 timeSecs = int(measureTime)
00524 timeNSecs = int(abs(timeSecs - measureTime) * 10**9)
00525 msg.header.stamp.secs = timeSecs
00526 msg.header.stamp.nsecs = timeNSecs
00527
00528 try:
00529 self.pub.publish(msg)
00530 except rospy.ROSException:
00531 rospy.loginfo("Topic /wiimote/classic closed. Shutting down Clas sender.")
00532 exit(0)
00533
00534
00535
00536
00537 except rospy.ROSInterruptException:
00538 rospy.loginfo("Shutdown request. Shutting down Clas sender.")
00539 exit(0)
00540
00541
00542 class WiiSender(WiimoteDataSender):
00543 """Broadcasting complete Wiimote messages to Topic wiimote"""
00544
00545 def __init__(self, wiiMote, freq=100):
00546 """Initializes the full-Wiimote publisher.
00547
00548 Parameters:
00549 wiiMote: a bluetooth-connected, calibrated WIIMote instance
00550 freq: the message sending frequency in messages/sec. Max is 100, because
00551 the Wiimote only samples the sensors at 100Hz.
00552 """
00553
00554 WiimoteDataSender.__init__(self, wiiMote, freq)
00555
00556 self.pub = rospy.Publisher('/wiimote/state', State)
00557
00558 def run(self):
00559 """Loop that obtains the latest wiimote state, publishes the data, and sleeps.
00560
00561 The wiimote message, if fully filled in, contains information in common with Imu.msg:
00562 acceleration (in m/s^2), and angular rate (in radians/sec). For each of
00563 these quantities, the IMU message format also wants the corresponding
00564 covariance matrix.
00565
00566 The covariance matrices are the 3x3 matrix with the axes' variance in the
00567 diagonal. We obtain the variance from the Wiimote instance.
00568 """
00569
00570 rospy.loginfo("Wiimote state publisher starting (topic /wiimote/state).")
00571 self.threadName = "Wiimote topic Publisher"
00572 try:
00573 while not rospy.is_shutdown():
00574 rospy.sleep(self.sleepDuration)
00575 (canonicalAccel, canonicalNunchukAccel, canonicalAngleRate) = self.obtainWiimoteData()
00576
00577 zeroingTimeSecs = int(self.wiiMote.lastZeroingTime)
00578 zeroingTimeNSecs = int((self.wiiMote.lastZeroingTime - zeroingTimeSecs) * 10**9)
00579 msg = State(header=None,
00580 angular_velocity_zeroed=None,
00581 angular_velocity_raw=None,
00582 angular_velocity_covariance=self.angular_velocity_covariance,
00583 linear_acceleration_zeroed=None,
00584 linear_acceleration_raw=None,
00585 linear_acceleration_covariance=self.linear_acceleration_covariance,
00586 nunchuk_acceleration_zeroed=None,
00587 nunchuk_acceleration_raw=None,
00588 nunchuk_joystick_zeroed=None,
00589 nunchuk_joystick_raw=None,
00590 buttons=[False,False,False,False,False,False,False,False,False,False],
00591 nunchuk_buttons=[False,False],
00592 rumble=False,
00593 LEDs=None,
00594 ir_tracking = None,
00595 raw_battery=None,
00596 percent_battery=None,
00597 zeroing_time=rospy.Time(zeroingTimeSecs, zeroingTimeNSecs),
00598 errors=0)
00599
00600
00601
00602
00603
00604 if self.wiistate.motionPlusPresent:
00605 msg.angular_velocity_zeroed.x = canonicalAngleRate[PHI]
00606 msg.angular_velocity_zeroed.y = canonicalAngleRate[THETA]
00607 msg.angular_velocity_zeroed.z = canonicalAngleRate[PSI]
00608
00609 msg.angular_velocity_raw.x = self.wiistate.angleRateRaw[PHI]
00610 msg.angular_velocity_raw.y = self.wiistate.angleRateRaw[THETA]
00611 msg.angular_velocity_raw.z = self.wiistate.angleRateRaw[PSI]
00612
00613 else:
00614 msg.angular_velocity_covariance = self.gyroAbsence_covariance
00615
00616 msg.linear_acceleration_zeroed.x = canonicalAccel[X]
00617 msg.linear_acceleration_zeroed.y = canonicalAccel[Y]
00618 msg.linear_acceleration_zeroed.z = canonicalAccel[Z]
00619
00620 msg.linear_acceleration_raw.x = self.wiistate.accRaw[X]
00621 msg.linear_acceleration_raw.y = self.wiistate.accRaw[Y]
00622 msg.linear_acceleration_raw.z = self.wiistate.accRaw[Z]
00623
00624 if self.wiistate.nunchukPresent:
00625 msg.nunchuk_acceleration_zeroed.x = canonicalNunchukAccel[X]
00626 msg.nunchuk_acceleration_zeroed.y = canonicalNunchukAccel[Y]
00627 msg.nunchuk_acceleration_zeroed.z = canonicalNunchukAccel[Z]
00628
00629 msg.nunchuk_acceleration_raw.x = self.wiistate.nunchukAccRaw[X]
00630 msg.nunchuk_acceleration_raw.y = self.wiistate.nunchukAccRaw[Y]
00631 msg.nunchuk_acceleration_raw.z = self.wiistate.nunchukAccRaw[Z]
00632
00633 msg.nunchuk_joystick_zeroed = self.wiistate.nunchukStick
00634 msg.nunchuk_joystick_raw = self.wiistate.nunchukStickRaw
00635 moreButtons = []
00636 moreButtons.append(self.wiistate.nunchukButtons[BTN_Z])
00637 moreButtons.append(self.wiistate.nunchukButtons[BTN_C])
00638 msg.nunchuk_buttons = moreButtons
00639
00640 theButtons = []
00641 theButtons.append(self.wiistate.buttons[BTN_1])
00642 theButtons.append(self.wiistate.buttons[BTN_2])
00643 theButtons.append(self.wiistate.buttons[BTN_PLUS])
00644 theButtons.append(self.wiistate.buttons[BTN_MINUS])
00645 theButtons.append(self.wiistate.buttons[BTN_A])
00646 theButtons.append(self.wiistate.buttons[BTN_B])
00647 theButtons.append(self.wiistate.buttons[BTN_UP])
00648 theButtons.append(self.wiistate.buttons[BTN_DOWN])
00649 theButtons.append(self.wiistate.buttons[BTN_LEFT])
00650 theButtons.append(self.wiistate.buttons[BTN_RIGHT])
00651 theButtons.append(self.wiistate.buttons[BTN_HOME])
00652
00653 ledStates = self.wiiMote.getLEDs()
00654 for indx in range(len(msg.LEDs)):
00655 if ledStates[indx]:
00656 msg.LEDs[indx] = True
00657 else:
00658 msg.LEDs[indx] = False
00659
00660 msg.buttons = theButtons
00661
00662 msg.raw_battery = self.wiiMote.getBattery()
00663 msg.percent_battery = msg.raw_battery * 100./self.wiiMote.BATTERY_MAX
00664
00665 irSources = self.wiistate.IRSources
00666
00667 for irSensorIndx in range(NUM_IR_SENSORS):
00668 if irSources[irSensorIndx] is not None:
00669
00670 try:
00671 pos = irSources[irSensorIndx]['pos']
00672 except KeyError:
00673
00674 msg.ir_tracking.append(IrSourceInfo(State.INVALID_FLOAT, State.INVALID_FLOAT, State.INVALID))
00675
00676 else:
00677
00678
00679 try:
00680 size = irSources[irSensorIndx]['size']
00681 except KeyError:
00682
00683 size = State.INVALID
00684 lightInfo = IrSourceInfo(pos[0], pos[1], size)
00685 msg.ir_tracking.append(lightInfo)
00686 else:
00687 msg.ir_tracking.append(IrSourceInfo(State.INVALID_FLOAT, State.INVALID_FLOAT, State.INVALID))
00688
00689 measureTime = self.wiistate.time
00690 timeSecs = int(measureTime)
00691 timeNSecs = int(abs(timeSecs - measureTime) * 10**9)
00692 msg.header.stamp.secs = timeSecs
00693 msg.header.stamp.nsecs = timeNSecs
00694
00695 try:
00696 self.pub.publish(msg)
00697 except rospy.ROSException:
00698 rospy.loginfo("Topic /wiimote/state closed. Shutting down Wiimote sender.")
00699 exit(0)
00700
00701
00702
00703
00704
00705
00706
00707 except rospy.ROSInterruptException:
00708 rospy.loginfo("Shutdown request. Shutting down Wiimote sender.")
00709 exit(0)
00710
00711 class WiimoteListeners(threading.Thread):
00712 """Listen for request to rumble and LED blinking.
00713 """
00714
00715 def __init__(self, wiiMote):
00716
00717 threading.Thread.__init__(self)
00718 self.wiiMote = wiiMote
00719
00720 self.ledCommands = [False, False, False, False]
00721 self.rumbleCommand = False
00722
00723
00724
00725
00726
00727 self.is_calibratedPublisher = rospy.Publisher('/imu/is_calibrated', Bool, latch=True)
00728
00729 self.is_CalibratedResponseMsg = Bool();
00730
00731
00732
00733
00734
00735
00736 self.is_CalibratedResponseMsg.data = self.wiiMote.latestCalibrationSuccessful;
00737 self.is_calibratedPublisher.publish(self.is_CalibratedResponseMsg)
00738
00739 def run(self):
00740
00741 def feedbackCallback(msg):
00742 """The callback for handle the feedback array messages and sending that to the Wiimote"""
00743 for fb in msg.array:
00744 if fb.type == JoyFeedback.TYPE_LED:
00745 try:
00746 if fb.intensity >= 0.5:
00747 self.ledCommands[fb.id] = True
00748 else:
00749 self.ledCommands[fb.id] = False
00750 except:
00751 rospy.logwarn("LED ID out of bounds, ignoring!")
00752 elif fb.type == JoyFeedback.TYPE_RUMBLE:
00753 if fb.id == 0:
00754 if fb.intensity >= 0.5:
00755 self.rumbleCommand = True
00756 else:
00757 self.rumbleCommand = False
00758 else:
00759 rospy.logwarn("RUMBLE ID out of bounds, ignoring!")
00760
00761 self.wiiMote.setLEDs(self.ledCommands)
00762 self.wiiMote.setRumble(self.rumbleCommand)
00763
00764
00765 return
00766
00767
00768 def calibrateCallback(req):
00769 """The imu/calibrate service handler."""
00770
00771 rospy.loginfo("Calibration request")
00772
00773 calibrationSuccess = self.wiiMote.zeroDevice()
00774
00775
00776
00777 self.is_CalibratedResponseMsg.data = calibrationSuccess
00778 self.is_calibratedPublisher.publish(self.is_CalibratedResponseMsg)
00779
00780 return EmptyResponse()
00781
00782
00783
00784
00785
00786 rospy.loginfo("Wiimote feedback listener starting (topic /joy/set_feedback).")
00787 rospy.Subscriber("joy/set_feedback", JoyFeedbackArray, feedbackCallback)
00788 rospy.loginfo("Wiimote calibration service starting (topic /imu/calibrate).")
00789 rospy.Service("imu/calibrate", Empty, calibrateCallback)
00790 rospy.loginfo("Wiimote latched is_calibrated publisher starting (topic /imu/is_calibrated).")
00791
00792 try:
00793 rospy.spin()
00794 except rospy.ROSInterruptException:
00795 rospy.loginfo("Shutdown request. Shutting down Wiimote listeners.")
00796 exit(0)
00797
00798
00799 if __name__ == '__main__':
00800 wiimoteNode = WiimoteNode()
00801 try:
00802 wiimoteNode.runWiimoteNode()
00803
00804 except rospy.ROSInterruptException:
00805 rospy.loginfo("ROS Shutdown Request.")
00806 except KeyboardInterrupt, e:
00807 rospy.loginfo("Received keyboard interrupt.")
00808 except WiimoteNotFoundError, e:
00809 rospy.logfatal(str(e))
00810 except WiimoteEnableError, e:
00811 rospy.logfatal(str(e))
00812 except CallbackStackMultInstError, e:
00813 rospy.logfatal(str(e))
00814 except CallbackStackEmptyError, e:
00815 rospy.logfatal(str(e))
00816 except ResumeNonPausedError, e:
00817 rospy.logfatal(str(e))
00818 except CallbackStackEmptyError, e:
00819 rospy.logfatal(str(e))
00820
00821 except:
00822 excType, excValue, excTraceback = sys.exc_info()[:3]
00823 traceback.print_exception(excType, excValue, excTraceback)
00824 finally:
00825 if (wiimoteNode is not None):
00826 wiimoteNode.shutdown()
00827 rospy.loginfo("Exiting Wiimote node.")
00828 sys.exit(0)