mavlink / MAVSDK-Java

MAVSDK client for Java.
71 stars 41 forks source link

Offboard flight when drone is on mission? #27

Closed KrisRadowski closed 4 years ago

KrisRadowski commented 4 years ago

How to send offboard flight commands when UAV is on mission?

Now I'm pausing mission and I'm making PositionNEDYaw object like this:

drone.getMission().pauseMission(); //for pausing
drone.getOffboard().setPositionNed(new Offboard.PositionNEDYaw(0f,0f,10f,45f)); //for offboard flight
JonasVautherin commented 4 years ago

I'm not sure what you want to do. Something like obstacle avoidance?

Offboard is a mode, like mission. You cannot be in both at the same time. There is a way to alter a mission (which is used by obstacle avoidance), but those are different messages that would need to be contributed to MAVSDK.

KrisRadowski commented 4 years ago

I'm not sure what you want to do. Something like obstacle avoidance?

Something like this or changing flight modes by software, so I can move out drone by yourself...

JonasVautherin commented 4 years ago

Seems like you want to actually leave the mission mode and move to offboard, is that correct? You need to run start() in order to go in offboard mode: see here. The offboard examples here may help (they are in Python, but the interface is auto-generated from the same proto file, so all the features in Python do exist in Java).

Let me know how it goes :slightly_smiling_face:.

KrisRadowski commented 4 years ago

Now I did this code:

drone.getMission().pauseMission(); //for pausing
drone.getOffboard().setPositionNed(new Offboard.PositionNEDYaw(0f,0f,0f,0f)); //for offboard flight
drone.getOffboard().start()
drone.getOffboard().setPositionNed(new Offboard.PositionNEDYaw(0f,0f,10f,0f)); //for making new position

But drone still stays in HOLD mode (ignoring offboard commands) :/

JonasVautherin commented 4 years ago

I think that you should have a look at the Java example (e.g. here and here. I think that what you miss is that it is returning you RxJava objects (Completable, Single or Flowable). So you have to subscribe at some point.

drone.getAction().arm(); // Will output a Completable, but you don't do anything with it, so this line has no effect

drone.getAction().arm().subscribe(); // This one will arm

// Similarly, the following two lines will arm:
Completable armCompletable = drone.getAction().arm();
armCompletable.subscribe();

So in the offboard case, and following this simple example, I would do something like:

drone.getAction().arm()
     .andThen(drone.getOffboard().setPositionNed(new Offboard.PositionNEDYaw(0f,0f,0f,0f))
     .andThen(drone.getOffboard().start())
     .andThen(drone.getOffboard().setPositionNed(new Offboard.PositionNEDYaw(5.0f,0f,0f,0f))
     .subscribe();

I understand that it can be confusing to have to subscribe(), but that's also super powerful. It means that you can for instance create your own Flowable or Completable in a function, return it, and later subscribe() to it from somewhere else :blush:.

Did you manage to run the Java examples (just to make sure that your setup does work)?

KrisRadowski commented 4 years ago

I know how to do that in RxJava Objects, but I have problem with this code:

//drone at this time is armed and doing mission
Flowable.timer(1, TimeUnit.SECONDS).blockingSubscribe(n->System.out.print(""),
                err->System.err.println(err),
                ()->drone.getMission().pauseMission()
                        .doOnComplete(()->logger.debug("Mission aborted, drone moving out of the way")).subscribe()); //going to HOLD flight mode as expected

Flowable.timer(1, TimeUnit.SECONDS).blockingSubscribe(n->System.out.print(""),
                err->System.err.println(err),
                ()->drone.getOffboard().setPositionNed(new Offboard.PositionNEDYaw(0f,0f,0f,0f))
                        .andThen(drone.getOffboard().start())
                        .andThen(drone.getOffboard().setPositionNed(new Offboard.PositionNEDYaw(0f,0f,10f,0f))) 
                        .doOnComplete(()->logger.debug("Drone  moved out of the way")).subscribe());//this Flowable is not working
JonasVautherin commented 4 years ago

Can you try to run the following code, just to see if offboard works?

package io.mavsdk.example;

import io.mavsdk.System;
import io.mavsdk.action.Action;
import io.mavsdk.mission.Mission;
import io.mavsdk.offboard.Offboard;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class TakeoffAndLand {
  private static final Logger logger = LoggerFactory.getLogger(TakeoffAndLand.class);

  public static void main(String[] args) {
    logger.debug("Starting example: takeoff and land...");

    System drone = new System();
    CountDownLatch latch = new CountDownLatch(1);

    drone.getAction().arm()
          .doOnComplete(() -> logger.debug("Arming..."))
          .doOnError(throwable -> logger.error("Failed to arm: "
                  + ((Action.ActionException) throwable).getCode()))
          .andThen(drone.getAction().takeoff()
            .doOnComplete(() -> logger.debug("Taking off..."))
            .doOnError(throwable -> logger.error("Failed to take off: "
                    + ((Mission.MissionException) throwable).getCode())))
          .delay(5, TimeUnit.SECONDS)
          .andThen(drone.getOffboard().setPositionNed(new Offboard.PositionNEDYaw(0f,0f,0f,0f)))
          .andThen(drone.getOffboard().start())
          .andThen(drone.getOffboard().setPositionNed(new Offboard.PositionNEDYaw(5.0f,0f,-2.0f,0f)))
          .delay(15, TimeUnit.SECONDS)
          .andThen(drone.getAction().land()
            .doOnComplete(() -> logger.debug("Landing..."))
            .doOnError(throwable -> logger.error("Failed to land: "
                    + ((Mission.MissionException) throwable).getCode())))
          .subscribe(latch::countDown, throwable -> latch.countDown());

    try {
      latch.await();
    } catch (InterruptedException ignored) {
        // This is expected
    }
  }
}

I took the TakeoffAndLand example and copy-pasted the 3 lines I wrote above. It works for me, let's check if that's working for you as well!

KrisRadowski commented 4 years ago

I took the '''TakeoffAndLand''' example and copy-pasted the 3 lines I wrote above. It works for me, let's check if that's working for you as well!

works fine for me :)

JonasVautherin commented 4 years ago

Ok, that's good. So what happens in your code above, where you say "this Flowable is not working". Do you get any output at all?

And maybe you could try to actually compose them instead of waiting an arbitrary one second between two blockingSubscribe calls (I still added a delay, just for the example):

drone.getMission().pauseMission()
     .doOnComplete(() -> logger.debug("Mission aborted, drone moving out of the way"))
     .delay(2, TimeUnit.SECONDS)
     .andThen(drone.getOffboard().setPositionNed(new Offboard.PositionNEDYaw(0f,0f,0f,0f))
     .andThen(drone.getOffboard().start())
     .andThen(drone.getOffboard().setPositionNed(new Offboard.PositionNEDYaw(5.0f,0f,0f,0f))
     .subscribe();
KrisRadowski commented 4 years ago

Ok, that's good. So what happens in your code above, where you say "this Flowable is not working". Do you get any output at all?

It's just going to Hold mode and not doing nothing

edit: Welp I forgot I haven't configured MAVROS xd So I have another question: I have a vehicle with FCU @ port 14542 and MAVLINK @ port 14562. How to configure network to using MAVROS and MAVSDK server?

JonasVautherin commented 4 years ago

I quickly modified the mission example, and it seems like it is working for me:

package io.mavsdk.example;

import io.mavsdk.System;
import io.mavsdk.mission.Mission;
import io.mavsdk.offboard.Offboard;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class RunMission {
  private static final Logger logger = LoggerFactory.getLogger(RunMission.class);

  public static void main(String[] args) {
    logger.debug("Starting example: mission...");

    List<Mission.MissionItem> missionItems = new ArrayList<>();
    missionItems.add(generateMissionItem(47.398039859999997, 8.5455725400000002));
    missionItems.add(generateMissionItem(47.398036222362471, 8.5450146439425509));
    missionItems.add(generateMissionItem(47.397825620791885, 8.5450092830163271));
    missionItems.add(generateMissionItem(47.397832880000003, 8.5455939999999995));

    System drone = new System();

    // Upload, arm and start the mission
    drone.getMission()
        .setReturnToLaunchAfterMission(true)
        .andThen(drone.getMission().uploadMission(missionItems)
            .doOnComplete(() -> logger.debug("Upload succeeded")))
        .andThen(drone.getAction().arm())
        .andThen(drone.getMission().startMission()
            .doOnComplete(() -> logger.debug("Mission started")))
        .subscribe();

    // Pause after the first mission item is reached, run an offboard routine and resume mission
    drone.getMission()
        .getMissionProgress()
        .filter(progress -> progress.getCurrentItemIndex() == 1)
        .doOnNext(progress -> logger.debug("Reached first mission item, starting offboard routine..."))
        .take(1)
        .ignoreElements()
        .andThen(drone.getMission().pauseMission())
        .delay(2, TimeUnit.SECONDS)
        .andThen(drone.getOffboard().setPositionNed(new Offboard.PositionNEDYaw(0f,-5.0f,0.0f,0f)))
        .andThen(drone.getOffboard().start())
        .delay(1, TimeUnit.SECONDS)
        .andThen(drone.getOffboard().stop()
            .doOnComplete(() -> logger.debug("Resuming mission in 5 seconds...")))
        .delay(5, TimeUnit.SECONDS)
        .doOnComplete(() -> logger.debug("Resuming..."))
        .andThen(drone.getMission().startMission())
        .subscribe();

    // Monitor the mission progress and "release" the latch when it is finished
    CountDownLatch latch = new CountDownLatch(1);
    drone.getMission()
        .getMissionProgress()
        .filter(progress -> progress.getCurrentItemIndex() == progress.getMissionCount())
        .take(1)
        .subscribe(ignored -> latch.countDown());

    try {
      latch.await();
    } catch (InterruptedException ignored) {
      // This is expected
    }
  }

  public static Mission.MissionItem generateMissionItem(double latitudeDeg, double longitudeDeg) {
    return new Mission.MissionItem(
        latitudeDeg,
        longitudeDeg,
        10f,
        10f,
        true,
        Float.NaN,
        Float.NaN,
        Mission.MissionItem.CameraAction.NONE,
        Float.NaN,
        1.0);
  }
}

I monitor the mission progress, and when the first item is reached, I pause the mission, wait 2 seconds, start an offboard routine, interrupt it after 1 second and resume the mission.

Can you try this and let me know what happens on your side?

KrisRadowski commented 4 years ago

Thanks, your advice worked :)