Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Not arming after landing #1297

Closed
SeppeVdB opened this issue Jun 14, 2023 · 8 comments
Closed

Not arming after landing #1297

SeppeVdB opened this issue Jun 14, 2023 · 8 comments
Milestone

Comments

@SeppeVdB
Copy link

Since the last firmware update (2023.02 works fine) the drone disarms after landing and doesn't arm again afterwards.

During landing, I send a send_stop_setpoint command a few centimeters above the ground. The crazyflie lands and after about 1 second, the "is armed" state goes to zero. Then I wait for about 5 seconds, after which it is still zero. After sending a reboot command the drone is armed again and it will take off, until the next landing.

During testing I was reading out the supervisor.info parameter and got the following:

In flight: 00011110
One second after landing: 00000100
(still the same five seconds after landing)
After rebooting: 00001110

Sending an arming command myself also doesn't work (have tested this) as the first but of supervisor.info is zero.

From my tests, the hardness of the landing has no effect on this behaviour.

@krichardsson
Copy link
Contributor

This indeed looks like it might be a bug, but I think some more information would be helpful.

  1. I assume you are controlling the Crazyflie from a python script, correct?
  2. Would it be possible to share the python code? Or even better a minimal example that shows the problem. It would be interesting to know if you use the high level commander for instance, or what type of setpoints/commands you send from take-off to landing.
  3. Have you checked the console log? It might give some clue. If you are running from a script, console log is typically lost, but you can check out the exmple of how to print it from a script. Just copy/past relevant parts into your code.

Thanks!

@krichardsson
Copy link
Contributor

krichardsson commented Jun 14, 2023

Another good test would be to enable more debug prints from the supervisor state machine by un-commenting the // #define DEBUG_ME line in supervisor_state_machine.c and build and flash. This will output more information in the console log.

@SeppeVdB
Copy link
Author

I am indeed running the Crazyflie from a python script and am only using the commander, not the high level commander. I am also not switching between the two.

I'll create a minimal working example and see if I can print the console log.

@SeppeVdB
Copy link
Author

Here is a minimal working example.

import time

import cflib.crtp
from cflib.crazyflie import Crazyflie
from cflib.crazyflie.log import LogConfig
from cflib.crazyflie.syncCrazyflie import SyncCrazyflie
from cflib.utils import uri_helper

# URI to the Crazyflie to connect to
uri = uri_helper.uri_from_env(default='radio://0/20/2M/247E000002')


pos_z = 0



def position_callback(timestamp, data, logconf):
    global pos_z
    pos_z = data['stateEstimate.z']


def start_position_logging(scf):
    log_conf = LogConfig(name='Position', period_in_ms=20)
    log_conf.add_variable('stateEstimate.z', 'float')

    scf.cf.log.add_config(log_conf)
    log_conf.data_received_cb.add_callback(position_callback)
    log_conf.start()

def console_callback(text: str):
    print(text, end='')


def take_off(scf):
    global pos_z

    cf = scf.cf

    t = time.time()
    while pos_z < 0.5:
        cf.commander.send_velocity_world_setpoint(0, 0, 0.5, 0)
        time.sleep(0.1)

        if time.time() - t > 5:
            print("take off timeout")
            break
    cf.commander.send_velocity_world_setpoint(0, 0, 0, 0)


def land(scf):
    global pos_z

    cf = scf.cf

    while pos_z > 0.07:
        cf.commander.send_velocity_world_setpoint(0, 0, -0.3, 0)
        time.sleep(0.1)
    cf.commander.send_stop_setpoint()


if __name__ == '__main__':
    cflib.crtp.init_drivers()

    cf=Crazyflie(rw_cache='./cache')

    cf.console.receivedChar.add_callback(console_callback)

    with SyncCrazyflie(uri, cf=cf) as scf:
        start_position_logging(scf)
        
        for i in range(4):
            print("taking off")
            take_off(scf)
            time.sleep(2)
            print("landing")
            land(scf)
            time.sleep(5)

I did not yet enable more debug prints but this is the console log:

SYS: ----------------------------
SYS: Crazyflie 2.1 is up and running!
SYS: Build 13:2cf04d7099f9 (2023.06 +13) MODIFIED
SYS: I am 0x2037343548435012003C003C and I have 1024KB of flash!
CFGBLK: v1, verification [OK]
DECK_INFO: Warning! No driver found for deck.
DECK_CORE: 2 deck(s) found
DECK_CORE: Calling INIT on driver bcLighthouse4 for deck 0
IMU: BMI088: Using I2C interface.
IMU: BMI088 Gyro connection [OK].
IMU: BMI088 Accel connection [OK]
IMU: BMP388 I2C connection [OK]
ESTIMATOR: Using Kalman (2) estimator
CONTROLLER: Using PID (1) controller
MTR-DRV: Using brushed motor driver
SYS: About to run tests in system.c.
SYS: NRF51 version: 2023.02
EEPROM: I2C connection [OK].
STORAGE: Storage check [OK].
IMU: BMI088 gyro self-test [OK]
SYS: Self test passed!
STAB: Wait for sensor calibration...
SYS: Free heap: 19184 bytes
LHFL: Lighthouse bootloader version: 2
STAB: Starting stabilizer loop
ESTKALMAN: State out of bounds, resetting
SUP: Can not fly
SUP: Ready to fly
LHFL: Bitstream CRC32: 112BC794 [PASS]
LHFL: Firmware version 6 verified, booting deck!
taking off
SUP: Can not fly
landing
SUP: Can not fly
SUP: Locked, reboot required
taking off
take off timeout
landing

@krichardsson
Copy link
Contributor

I have looked at this and it turns out that it actually is two problems, one in your code and one that is related to this functionality but not really used by you at the moment.

The first (and main) problem is related to a safety feature in the Crazyflie. When setpoints are received in the Crazyflie, there is a watch dog that checks that they are received continuously. If they stop for more than 0.5 seconds, the Crazyflie will assume the connection is broken and it will level out. After 2 seconds it will turn off the motors. The idea is that if you are flying remotely (with gamepad or script) and the connection is lost, the Crazyflie should stop.
This is what is happening in your script, after take off and landing, you simply sleep for a while which will trigger the watch dog and freeze the Crazyfie.

The solution is to continue to send set points, also when hovering and landed, something like

# ...

def hover(scf, hover_time):
    end_time = time.time() + hover_time
    while time.time() < end_time:
        scf.cf.commander.send_velocity_world_setpoint(0, 0, 0, 0)
        time.sleep(0.1)

def wait_on_the_ground(scf, wait_time):
    end_time = time.time() + wait_time
    while time.time() < end_time:
        scf.cf.commander.send_stop_setpoint()
        time.sleep(0.1)

# ...

        for i in range(4):
            print("taking off")
            take_off(scf)
            print('Hover')
            hover(scf, 2)
            print("landing")
            land(scf)
            print('Landed')
            wait_on_the_ground(scf, 5)

The other problem I found on the way is related to switching back and forth between the high level commander and low level set points, it is described in #1298

Please close this issue if you are satisfied with the solution.

@SeppeVdB
Copy link
Author

Thank you for the response.

I see how this could be useful while the drone is in flight, but is there a way to disable this functionality when a send_stop_setpoint command is send? I don't want to constantly send stop commands to the drone after it is landed such that the bandwidth doesn't get crowded.

Also if I understand the radiodriver correctly, when a safe link is established, the radio keeps sending commands to check if the drone is still connected. Wouldn't this already be enough to detect connection loss?

Thanks

@krichardsson
Copy link
Contributor

Also if I understand the radiodriver correctly, when a safe link is established, the radio keeps sending commands to check if the drone is still connected. Wouldn't this already be enough to detect connection loss?

At some point (a long time ago) it was decided to use set points for this, maybe it will change in the future?

I see how this could be useful while the drone is in flight, but is there a way to disable this functionality when a send_stop_setpoint command is send? I don't want to constantly send stop commands to the drone after it is landed such that the bandwidth doesn't get crowded.

The idea is that someone must always push in set points, and what might not be obvious is that the high level commander is doing this most of the time. If you turn the Crazyflie on, the high level commander will start to send null set points to the commander which will keep the watch dog happy. When you send in low level set points from your script, there is a priority functionality that will kick in and use your set points instead of the high level commander ones. The high level commander will be stopped which means that any running trajectory is terminated, and it will start to send null set points. Due to the lower priority these set points will be discarded at this point. If you want to lower the priority again and use the high level commander you can send a packet to tell the commander to reset the priority by calling the cf.commander.send_notify_setpoint_stop() method.

Your land() function could look like something like this

def land(scf):
    global pos_z

    cf = scf.cf

    while pos_z > 0.07:
        cf.commander.send_velocity_world_setpoint(0, 0, -0.3, 0)
        time.sleep(0.1)
    cf.commander.send_stop_setpoint()
    # Hand control over to the high level commander again
    cf.commander.send_notify_setpoint_stop()

This is what was fixed in #1298 yesterday.

the bandwidth doesn't get crowded.

Unless you really need to send in your own set points, the high level commander is usually a better option to fly trajectories as it does not require packets to be sent all the time. Note that it is possible to re-plan trajectories that have been started as well.
But that is a completely other topic and is better to discuss in a discussion on https://github.com/orgs/bitcraze/discussions

@SeppeVdB
Copy link
Author

Oh ok, I fully understand now. This solution indeed fixes my problem.

Thanks a lot!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants