ArDrone can not takeoff if Publisher is call within a Class

Hello everyone,

I got an error in Unit 5: Actions

I tried to create a Class that covers all Ardrone behavior like Takeoff, Land, TakePicture. The problem is that if I call the takeoff() function of the class, the ARDrone does nothings. It only takeoff when the publish command is executed directly in the main function.

Here is my code for reference:

#!/usr/bin/env python

import rospy as ros
import actionlib as alib 
from geometry_msgs.msg import Twist 
from ardrone_as.msg import (
    ArdroneAction, 
    ArdroneResult, 
    ArdroneGoal,
    ArdroneFeedback)

from std_msgs.msg import Empty


import time  

class ArdroneController():

    def __init__(self):
        self.velController = ros.Publisher('/cmd_vel', Twist, queue_size = 1)
        self.velCommand = Twist()   
        self.rate = ros.Rate(1)
        self.is_airbone = False # 0: Landing 1:Airbone
        self.camera = alib.SimpleActionClient('/ardrone_action_server', ArdroneAction)
        self.camera.wait_for_server()
        ros.on_shutdown(self._shutdownhook)
        self.camera_timer = ArdroneGoal()
        
    def _shutdownhook(self):
        ros.loginfo('Terminate Drone operation')
        if self.is_airbone:
            self.land()

    def investigate(self):
        if not self.is_airbone:
            self.takeoff()
        self.camera_timer.nseconds = 10.
        self.camera.send_goal(self.camera_timer, feedback_cb = self._camera_callback) 

    def _camera_callback(self, feedback):
        pass
        # velMess.linear.x = 2.0 
        # velMess.angular.x = 0.1 
        # velMess.angular.z = 0.2

        # velPubl.publish(velMess)

    def takeoff(self):
        message = Empty()
        publisher = ros.Publisher('/drone/takeoff', Empty, queue_size = 1)
        ros.loginfo("Taking Off ...")
        i = 0
        while not i == 3:
            publisher.publish(message)
            time.sleep(1.)
            i +=1
        self.is_airbone = True
            
        # del publisher, message
        ros.loginfo('Taken-Off !') 
                
    def land(self):
        message = Empty()
        publisher = ros.Publisher('/drone/land', Empty, queue_size = 1)
        while self.is_airbone:
            ros.loginfo('Landing ...')
            connections = publisher.get_num_connections()
            if connections > 0:
                publisher.publish(message)
                self.is_airbone = False
                break
        # del publisher, message
        ros.loginfo('Landed !') 

if __name__ == "__main__":
    ros.init_node('ardrone_client_node')
    controller = ArdroneController()
    try:
        controller.takeoff()
    except ros.ROSInterruptException:
        pass

    time.sleep(5)
    # controller.land()

    # message = Empty()
    # publisher = ros.Publisher('/drone/takeoff', Empty, queue_size = 1)
        
    # ros.loginfo("Taking Off ...")
    # for i in range(3):
        
    #     publisher.publish(message)
    #     time.sleep(1.)
        
    # # del publisher, message
    # ros.loginfo('Taken-Off !')

Hi @qhuy.phm,
welcome to the community :slight_smile: The code you posted is just the action CLIENT, is your action SERVER running in a seperate terminal? If yes, and it doesnt work, please post your server code

Hi @simon.steinmann91,

I’m sorry to confuse you. In my code, there are only Publisher and Subscriber implemented, I haven’t touch the Action part yet.
In order to take-off, I publish an Empty message to the topic /drone/takeoff. It works nicely when executed in the main program (below if name == “main”), but hads no effect when call within class ( ArdroneController.takeoff() )

Do you get this in your terminal?

ros.loginfo("Taking Off ...")

meaning, is the function called or not? Perhaps you wanna add a print() to you exception below, or not use Try at all.

I even got the “Taken-Off” notification. But the drone did not move an inch :exploding_head:

You could try a publish loop like this for the takeoff message as well. It ensures that it is really sent. Also check with rostopic echo /drone/takeoff whether the message gets sent

Hi Simon,

I did that, but the Ardrone is not taking off, even it is not moving at all and the /cmd_vel, /takeoff and /landtopics are active. But using the command line it works.

Any ideas please?

thanks for your time.

When using a class you need to remember that once you create a object of the class as you do in the line controller = ArdroneController() and call a method from the class it must refer back to the object that is why you have the self keyword this refers back to the instance/object of the class. So your methods (just a term used to refer to a function that is part of a class) need to have keyword self so for the drone to takeoff try:

           def takeoff(self): 
                 self.message = Empty()
                 self.publisher = ros.Publisher('/drone/takeoff', Empty, queue_size = 1)
                 ros.loginfo("Taking Off ...")
                 i = 0
                 while not i == 3:
                       self.publisher.publish(self.message)
                       time.sleep(1.)
                       i +=1

The use of self is required in other parts of your code too. Use this example to fix the rest of your code if you have trouble post again.

2 Likes