CrazyRadio.py 8.86 KB
Newer Older
bucyril's avatar
bucyril committed
1
2
3
#!/usr/bin/env python
# -*- coding: utf-8 -*-

4
import roslib; roslib.load_manifest('d_fall_pps')
bucyril's avatar
bucyril committed
5
import rospy
6
from d_fall_pps.msg import ControlCommand
7
from std_msgs.msg import Int32
bucyril's avatar
bucyril committed
8
9
10
11
12
13
14


# General import
import time, sys
import struct
import logging

15
import rosbag
16
from rospkg import RosPack
17
18
19
from std_msgs.msg import Float32
from std_msgs.msg import String

bucyril's avatar
bucyril committed
20
21
22
23
24
25
26
27
28
29
30
# Add library
#sys.path.append("lib")

# CrazyFlie client imports
import cflib

from cflib.crazyflie import Crazyflie
from cflib.crtp.crtpstack import CRTPPacket, CRTPPort

import cflib.drivers.crazyradio

31
# Logging import(*
bucyril's avatar
bucyril committed
32
33
34
35
36
from cflib.crazyflie.log import LogConfig

# Logging settings
logging.basicConfig(level=logging.ERROR)

37
38
39
CONTROLLER_MOTOR = 2
CONTROLLER_ANGLE = 1
CONTROLLER_RATE = 0
40
RAD_TO_DEG = 57.296
41

42
43
44
45
46
47
48
49
# CrazyRadio states:
CONNECTED = 0
CONNECTING = 1
DISCONNECTED = 2

# Commands coming
CMD_RECONNECT = 0

50
51
52
53
54
55
56
# Commands for PPSClient
CMD_USE_SAFE_CONTROLLER =   1
CMD_USE_CUSTOM_CONTROLLER = 2
CMD_CRAZYFLY_TAKE_OFF =     3
CMD_CRAZYFLY_LAND =         4
CMD_CRAZYFLY_MOTORS_OFF =   5

57
58
rp = RosPack()
record_file = rp.get_path('d_fall_pps') + '/LoggingOnboard.bag'
bucyril's avatar
bucyril committed
59
60
rospy.loginfo('afdsasdfasdfsadfasdfasdfasdfasdfasdfasdf')
rospy.loginfo(record_file)
61
bag = rosbag.Bag(record_file, 'w')
62

63
class PPSRadioClient:
bucyril's avatar
bucyril committed
64
    """
65
66
       CrazyRadio client that recieves the commands from the controller and
       sends them in a CRTP package to the crazyflie with the specified
67
       address.
bucyril's avatar
bucyril committed
68
69
70
71
72
73
74
75
76
77
78
    """
    def __init__(self, link_uri):

        # Setpoints to be sent to the CrazyFlie
        self.roll = 0.0
        self.pitch = 0.0
        self.yaw = 0.0
        self.motor1cmd = 0.0
        self.motor2cmd = 0.0
        self.motor3cmd = 0.0
        self.motor4cmd = 0.0
79
        self._status = DISCONNECTED
80
        self.link_uri = link_uri
bucyril's avatar
bucyril committed
81

roangel's avatar
roangel committed
82
        self.status_pub = rospy.Publisher(node_name + '/CrazyRadioStatus', Int32, queue_size=1)
83
        self.PPSClient_command_pub = rospy.Publisher('PPSClient/Command', Int32, queue_size=1)
84

bucyril's avatar
bucyril committed
85
        # Initialize the CrazyFlie and add callbacks
roangel's avatar
roangel committed
86
87
88
89
90
91
        self._init_cf()

        # Connect to the Crazyflie
        self.connect()

    def _init_cf(self):
bucyril's avatar
bucyril committed
92
93
        self._cf = Crazyflie()

94
        # Add callbacks that get executed depending on the connection _status.
bucyril's avatar
bucyril committed
95
96
97
98
99
        self._cf.connected.add_callback(self._connected)
        self._cf.disconnected.add_callback(self._disconnected)
        self._cf.connection_failed.add_callback(self._connection_failed)
        self._cf.connection_lost.add_callback(self._connection_lost)

100
101
102
        self.change_status_to(DISCONNECTED)

    def change_status_to(self, new_status):
103
        print "status changed to: %s" % new_status
104
105
        self._status = new_status
        self.status_pub.publish(new_status)
106

107
108
    def get_status(self):
        return self._status
109
110

    def connect(self):
roangel's avatar
roangel committed
111
        print "Connecting to %s" % self.link_uri
112
        self.change_status_to(CONNECTING)
113
114
        rospy.loginfo("connecting...")
        self._cf.open_link(self.link_uri)
roangel's avatar
roangel committed
115

116
    def _data_received_callback(self, timestamp, data, logconf):
117
        #print "log of stabilizer and battery: [%d][%s]: %s" % (timestamp, logconf.name, data)
118
        batteryVolt = Float32()
119
120
121
        stabilizerYaw = Float32()
        stabilizerPitch = Float32()
        stabilizerRoll = Float32()
122
        batteryVolt.data = data["pm.vbat"]
123
124
125
126
127
128
        stabilizerYaw.data = data["stabilizer.yaw"]
        stabilizerPitch.data = data["stabilizer.pitch"]
        bag.write('batteryVoltage', batteryVolt)
        bag.write('stabilizerYaw', stabilizerYaw)
        bag.write('stabilizerPitch', stabilizerPitch)
        bag.write('stabilizerRoll', stabilizerRoll)
129
130

        #publish battery voltage for GUI
131
        #cfbattery_pub.publish(std_msgs.Float32(batteryVolt.data))
132
        # print "batteryVolt: %s" % batteryVolt
133
134
        cfbattery_pub.publish(batteryVolt)

135
136


137
138
    def _logging_error(self, logconf, msg):
        print "Error when logging %s" % logconf.name
bucyril's avatar
bucyril committed
139

roangel's avatar
roangel committed
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
    # def _init_logging(self):

    def _start_logging(self):
        self.logconf = LogConfig("LoggingTest", 100) # second variable is freq in ms
        self.logconf.add_variable("stabilizer.roll", "float");
        self.logconf.add_variable("stabilizer.pitch", "float");
        self.logconf.add_variable("stabilizer.yaw", "float");
        self.logconf.add_variable("pm.vbat", "float");

        self._cf.log.add_config(self.logconf)
        if self.logconf.valid:
            self.logconf.data_received_cb.add_callback(self._data_received_callback)
            self.logconf.error_cb.add_callback(self._logging_error)
            print "logconf valid"
        else:
            print "logconf invalid"

        self.logconf.start()
        print "logconf start"

bucyril's avatar
bucyril committed
160
161
162
163
164
    def _connected(self, link_uri):
        """
            This callback is executed as soon as the connection to the
            quadrotor is established.
        """
165
        self.change_status_to(CONNECTED)
166
167
168
        # change state to motors OFF
        cf_client.PPSClient_command_pub.publish(CMD_CRAZYFLY_MOTORS_OFF)

bucyril's avatar
bucyril committed
169
        rospy.loginfo("Connection to %s successful: " % link_uri)
170
        # Config for Logging
roangel's avatar
roangel committed
171
        self._start_logging()
172

bucyril's avatar
bucyril committed
173

174
175


bucyril's avatar
bucyril committed
176
177
178
    def _connection_failed(self, link_uri, msg):
        """Callback when connection initial connection fails (i.e no Crazyflie
        at the specified address)"""
179
        self.change_status_to(DISCONNECTED)
bucyril's avatar
bucyril committed
180
181
182
183
184
        rospy.logerr("Connection to %s failed: %s" % (link_uri, msg))

    def _connection_lost(self, link_uri, msg):
        """Callback when disconnected after a connection has been made (i.e
        Crazyflie moves out of range)"""
185
        self.change_status_to(DISCONNECTED)
bucyril's avatar
bucyril committed
186
187
188
189
        rospy.logerr("Connection to %s lost: %s" % (link_uri, msg))

    def _disconnected(self, link_uri):
        """Callback when the Crazyflie is disconnected (called in all cases)"""
190
        self.change_status_to(DISCONNECTED)
bucyril's avatar
bucyril committed
191
        rospy.logwarn("Disconnected from %s" % link_uri)
roangel's avatar
roangel committed
192

193
194
195
        # change state to motors OFF
        self.PPSClient_command_pub.publish(CMD_CRAZYFLY_MOTORS_OFF)

roangel's avatar
roangel committed
196
197
198
199
        self.logconf.stop()
        rospy.loginfo("logconf stopped")
        self.logconf.delete()
        rospy.loginfo("logconf deleted")
200

bucyril's avatar
bucyril committed
201

202
    def _send_to_commander(self,roll, pitch, yaw, thrust, cmd1, cmd2, cmd3, cmd4, mode):
bucyril's avatar
bucyril committed
203
204
        pk = CRTPPacket()
        pk.port = CRTPPort.COMMANDER
205
        pk.data = struct.pack('<fffHHHHHH', roll * RAD_TO_DEG, pitch * RAD_TO_DEG, yaw * RAD_TO_DEG, thrust, cmd1, cmd2, cmd3, cmd4, mode)
bucyril's avatar
bucyril committed
206
207
        self._cf.send_packet(pk)

208
    def crazyRadioCommandCallback(self, msg):
209
        """Callback to tell CrazyRadio to reconnect"""
210
        print "crazyRadio command received %s" % msg.data
211
        if msg.data == CMD_RECONNECT:            # reconnect, check _status first and then do whatever needs to be done
212
            print "entered reconnect"
213
214
            print "_status: %s" % self._status
            if self.get_status() == DISCONNECTED:
215
216
                print "entered disconnected"
                self.connect()
217
218
            if self.get_status() == CONNECTED:
                self.status_pub.publish(CONNECTED)
219

220
221
def controlCommandCallback(data):
    """Callback for controller actions"""
222
    #rospy.loginfo("controller callback : %s, %s, %s", data.roll, data.pitch, data.yaw)
bucyril's avatar
bucyril committed
223

224
    #cmd1..4 must not be 0, as crazyflie onboard controller resets!
225
    #pitch and yaw are inverted on crazyflie controller
226
    cf_client._send_to_commander(data.roll, -data.pitch, -data.yaw, 0, data.motorCmd1, data.motorCmd2, data.motorCmd3, data.motorCmd4, data.onboardControllerType)
227
228
229
230




bucyril's avatar
bucyril committed
231
232

if __name__ == '__main__':
roangel's avatar
roangel committed
233
234
235
    global node_name
    node_name = "CrazyRadio"
    rospy.init_node(node_name, anonymous=True)
236
    # Initialize the low-level drivers (don't list the debug drivers)
bucyril's avatar
bucyril committed
237
238
    cflib.crtp.init_drivers(enable_debug_driver=False)

239
    #wait until address parameter is set by PPSClient
240
    while not rospy.has_param("~crazyFlieAddress"):
bucyril's avatar
bucyril committed
241
242
        time.sleep(0.05)

243
244
    radio_address = "radio://" + rospy.get_param("~crazyFlieAddress")
    rospy.loginfo("Crazyradio connecting to %s" % radio_address)
245

246
    #use this following two lines to connect without data from CentralManager
roangel's avatar
roangel committed
247
248
249
    # radio_address = "radio://0/72/2M"
    # rospy.loginfo("manual address loaded")

250
    global cfbattery_pub
251
    cfbattery_pub = rospy.Publisher(node_name + '/CFBattery', Float32, queue_size=10)
252

253
    global cf_client
254

255
    cf_client = PPSRadioClient(radio_address)
256
    rospy.Subscriber("PPSClient/crazyRadioCommand", Int32, cf_client.crazyRadioCommandCallback) # allows commands from scripts
257

258
    time.sleep(1.0)
259

260
    rospy.Subscriber("PPSClient/ControlCommand", ControlCommand, controlCommandCallback)
261

262
263
    rospy.spin()
    rospy.loginfo("Turning off crazyflie")
264

265
    cf_client._send_to_commander(0, 0, 0, 0, 0, 0, 0, 0, CONTROLLER_MOTOR)
266
267
    # change state to motors OFF
    cf_client.PPSClient_command_pub.publish(CMD_CRAZYFLY_MOTORS_OFF)
268
269
    #wait for client to send its commands
    time.sleep(1.0)
270

271
272


roangel's avatar
roangel committed
273
274
275
    bag.close()
    rospy.loginfo("bag closed")

276
277
    cf_client._cf.close_link()
    rospy.loginfo("Link closed")