amymcgovern / pyparrot

Python interface for Parrot Drones
MIT License
276 stars 129 forks source link

Problem after adding new function to Bebop file #201

Open Adrials opened 4 years ago

Adrials commented 4 years ago

Hi @amymcgovern ,

Im a young french student, and I have, with a friend, a school programming project with the bebop2 drone.

We need to move the drone to several points (with GPS coordinates). It seems there is no function for this in your pyparrot code, although bebop2 has the ability to move to a GPS coordinate point, judging by the xml file of the drone. That's why I've created one (here below), based on your work and reusing your functions. But, after testing it, a problem arise, that my friend and I can't solve (although we've gone through your pyparrot files...).

We have implemented this 'move_to' function in the Bebop.py file (just after your 'flat_trim' function). Then, we tried a simple script to see if the function worked, by calling the 'move_to' function. And an error was raised by python, with a large traceback. Or, more accurately, I think two errors raised, one that it is intercepted and then displayed, and one that is not intercepted. Both are AttributeError: 'tuple' object has no attribute 'update' and 'tuple' object has no attribute 'set_max_altitude'. The tuple object in question is the sensors object in the Bebop.py file. So the program obviously crashed. But even more surprisingly, the scripts that worked before, don't work anymore, even after the original Bebop.py file has been restored...!

So, I have 2 questions :

-First one : is the function correct, at least theoretically? Is there an error, something to replace or add?

-Second one : I don't know how this problem can occur, nor how to solve it. Therefore, Im wondering if you could help us to solve this problem (cause we can't use the bebop2 right now)? Hoping that you can understand why this error appeared, and that you will agree to help us,

Thank you so much in advance!

CODE created by ourselves with move_to function :

`# -*- coding: utf-8 -*-
"""
Created on Sat Apr 25 22:04:08 2020.

@author: adaml

Code à insérer dans la bibliothèque pyparrot, dans le fichier Bebop, dans le constructeur du même nom.
Définition des méthodes move_to et cancel_move_to, permettant au drone d'aller à un point de coordonnées GPS données, et d'annuler la manoeuvre.

À noter : implémenter également cette ligne dans le fichier Bebop (de la bibliothèque pyparrot toujours), et dans le constructeur BebopSensors : self.MoveToEnded = False

Tout a planté lors de son utilisation.
erreur : la variable self.sensors du constructeur Bebop est tuple...!?
"""

def move_to(self, latitude, longitude, altitude, orientation_mode, heading=None):
    """
    Adam and Alexi's function. Move the drone to a specified location.

    :param latitude: location's latitude to reach(degrees)
    :param longitude: location's longitude to reach (degrees)
    :param altitude: location's altitude to reach (meters)
    :param orientation_mode: Orientation mode of the move to
    :param heading: Heading (relative to the North in degrees).This value is only used if the orientation mode is HEADING_START or HEADING_DURING
    :return: nothing but move to location.
    """
    fixed_orientation = orientation_mode.upper()
    if (fixed_orientation not in ("NONE", "TO_TARGET", "HEADING_START", "HEADING_DURING")):
        print("Error: %s is not a valid direction.  Must be one of %s" % orientation_mode, "NONE, TO_TARGET, HEADING_START, or HEADING_DURING")
        print("Ignoring command and returning")
        return

    (command_tuple, enum_tuple) = self.command_parser.get_command_tuple_with_enum("ardrone3", "Piloting", "moveTo", fixed_orientation)

    if heading is not None:
        if fixed_orientation not in ("HEADING_START", "HEADING_DURING"):
            print("Error: heading parameter cannot be used. Can only be used if orientation_mode is 'HEADING_START' or 'HEADING_DURING'")
            print("Ignoring command and returning")
            return
        param_tuple = [latitude, longitude, altitude, enum_tuple, heading]  # Enable
        param_type_tuple = ['double', 'double', 'double', 'enum', 'float']
    else:
        param_tuple = [latitude, longitude, altitude, enum_tuple]  # Enable
        param_type_tuple = ['double', 'double', 'double', 'enum']

    command_tuple = self.command_parser.get_command_tuple("ardrone3", "Piloting", "moveTo")
    # reset the bit that tells when the move ends
    self.sensors.MoveToEnded = False

    # send the command
    self.drone_connection.send_param_command_packet(command_tuple, param_tuple, param_type_tuple)

    # sleep until it ends
    while (not self.sensors.MoveToEnded):  # we added this MoveToEnded variable to the BebopSensors class, so it exists
        self.smart_sleep(0.01)

def cancel_move_to(self):
    """
    Adam and Alexi's function. Cancel the move_to command. If there is no current moveTo, this command has no effect.

    :return: Nothing
    """
    command_tuple = self.command_parser.get_command_tuple("ardrone3", "Piloting", "CancelMoveTo")
    self.drone_connection.send_noparam_command_packet_ack(command_tuple)
`
amymcgovern commented 4 years ago

everything I did was indoors so I never implemented the GPS functions. In theory, they should work just fine. I am guessing you broke something fundamental with the edits (though it isn't clear what it is!) so I would reinstall and start there (but keep your old code around). Python is odd sometimes that way! Others on the discussion board have discussed GPS movements and I would also look at those.

Adrials commented 4 years ago

I'm guessing too, but yes it's not clear, cause we only added function... Thanks for your advice. So we have uninstalled pyparrot's codes and installed again. We can't test the bebop2 right now, so we will look at the discussion board, and test all in few weeks. I'll let you know the result. Thank you so much for your prompt response.

faweigend commented 1 year ago

Hi, apologies for reviving this old issue. I was wondering if you were able to solve this @Adrials? I'm stuck here too. I send the move_to command and receive the acknowledgement, but the bebop2 doesn't move.

Adrials commented 1 year ago

Hi @faweigend, I'm sorry. The COVID crisis forced me to stop the project, so I've never had a chance to look at the problem again (insofar as the drone wasn't mine...).

Here are the only two things I can tell you:

Hope you find the solution (by the way, let me know if you do, I'm still interested)!

amymcgovern commented 1 year ago

Sorry, I haven't had working bebops for awhile and haven't got a reason to buy a new one (no longer part of my research program). If there is a solution and I need to accept a change, I will though!

faweigend commented 1 year ago

Thanks for the replies @Adrials @amymcgovern,

I found a solution and successfully tested it on bebop2. It is not fully integrated though, because I had to work around the convenience enum function. I hope to get the time to integrate it properly soon. For anyone who wants to use my current workaround:

  def move_to(self, lat, lon, altitude):
      command_tuple = [1, 0, 10]

      # Calculate packet size:
      # base packet <BBBIBBH is 11 bytes
      packet_size = 11 + 8 + 8 + 8 + 4 + 4

      seq_str = 'SEND_WITH_ACK'
      ack_string = 'DATA_WITH_ACK'
      self.bebop.drone_connection.sequence_counter[seq_str] = (self.bebop.drone_connection.sequence_counter[
                                                                   seq_str] + 1) % 256
      seq_id = self.bebop.drone_connection.sequence_counter[seq_str]
      packet = struct.pack(
          "<BBBIBBHdddif",
          self.bebop.drone_connection.data_types_by_name[ack_string],  # data with ack
          self.bebop.drone_connection.buffer_ids[seq_str],  # send with ack
          seq_id,  # sequence counter
          packet_size,
          command_tuple[0],
          command_tuple[1],
          command_tuple[2],
          lat,
          lon,
          altitude,
          0,  # mode: NONE
          0.0  # heading is ignored
      )
      res = self.bebop.drone_connection.send_command_packet_ack(packet, seq_id)
      self.bebop.smart_sleep(1)
      return res