05 Juli 2022

Drohnen fliegen lassen mit Cadence – Einführung

Lassen Sie uns eine neue und komplexere Cadence-Anwendung für Drohnen-Lieferungen erstellen! Dies ist der erste Teil einer mehrteiligen Reihe über Cadence-Drohnen mit einer Einführung in das Problem der Drohnenlieferung, mit dem Cadence-Hauptworkflow für Drohnen und mit einem Anhang zu Drohnenbewegungen.

1. Der Himmel ist voller Drohnen (und Krähen)

(Quelle: Shutterstock)

Meine jüngste Silvesterlektüre war „Termination Shock“ von Neal Stephenson, ein Roman, der in der nahen Zukunft spielt, in der Drohnen allgegenwärtig sind, wobei es kurioserweise auch um dressierte Raubvögel geht, die Drohnen zerstören. Fakten sind häufig seltsamer als Fiktion, denn dies erinnerte mich an den Test eines Drohnen-Lieferdienstes in der Nähe von Canberra, wo ich lebe, der es jüngst wegen eines Krähen-Angriffs in die Nachrichten schaffte. Als ich den Lieferdienst selbst testen wollte, stellte sich leider heraus, dass er in meiner Gegend noch nicht angeboten wird, obwohl es viele perfekte Drohnen-Landeplätze in der Nähe gibt. Nun gut, vorerst musste also die Fiktion genügen.

Dafür beschloss ich aber, für mein nächstes Cadence-Experiment einen Drohnen-Lieferdienst aufzubauen (simuliert natürlich, auch wenn meine Garage mit mehreren Generationen ausrangierter Drohnen vollgestopft ist). Im Experiment soll der örtliche Versuch als Vorbild dienen, bei dem die Kunden bei ausgewählten teilnehmenden Geschäften kleine Artikel (z. B. Medikamente, Kaffee, Lebensmittel, Kleinteile) bestellen können. Es gibt einen zentralen Drohnenstützpunkt (ähnlich, aber viel langweiliger als das Versteck eines klassischen Film-Schurken oder Tracy Island aus der Science-Fiction-Serie „Thunderbirds Are Go!“ oder die Drohnensammelplätze der Bienenvölker), wo sich alle Drohnen befinden, wenn sie nicht gerade eine Bestellung ausliefern.

Drohnensammelplätze von Bienen (Quelle: Shutterstock)

Der Stützpunkt ist der Ort, an dem die Batterien der Drohnen aufgeladen werden. Wenn eine Bestellung versandfertig ist, fliegen die Drohnen vom Stützpunkt zum jeweiligen Geschäft, holen die Bestellung ab und fliegen zum Standort des Kunden, um die Bestellung auszuliefern. Danach fliegen sie zurück zum Stützpunkt, und der Prozess beginnt von vorn. Da die Drohnen autonom sind, kann es viele von ihnen geben. Es gibt allerdings Grenzen hinsichtlich Gewicht der Artikel, Einsatzzeiten, Bereiche, in denen geflogen werden darf oder nicht (z. B. Flugverbotszonen), dazu viele potenzielle Fehlerarten (z. B. Nichterreichen des Zielstandorts, Nichtzustellung der Bestellung, Absturz usw.) und die maximale Reichweite der Drohnenbatterie mit ausreichendem Sicherheitspuffer, um zum Stützpunkt zurückzukehren (insbesondere, wenn sie angreifenden Krähen ausweichen müssen oder vom Kurs abgebracht werden usw.). Die einzigen Faktoren, die ich zunächst berücksichtigen wollte, waren Entfernung und Flugzeit.

2. Anwendung Drohnenlieferung: Cadence Workflow-Design

Der allgemeine Drohnen-Workflow (Quelle: Shutterstock – Zusammenstellung)

Ich wollte bei meiner Demo für Drohnenlieferungen die Dinge einfach, aber trotzdem interessant halten. Daher beschloss ich, alles, was stateful ist und Aktionen oder Aufgaben haben kann, als Cadence Workflow zu implementieren. Es gibt zwei Workflow-Typen, die miteinander interagieren.

Jede Drohne ist als Workflow modelliert, wobei verschiedene Zustände durchlaufen werden: Bereit am Stützpunkt, Warten auf eine Bestellung, Fliegen zur Abholung der Bestellung, Abholen der Bestellung, Fliegen zum Zielstandort, Zustellen der Bestellung und dann Zurückfliegen zum Stützpunkt und Aufladen. Nach jedem Lieferzyklus startet der Drohnen-Workflow eine neue Instanz (mit derselben Workflow-ID, aber einer anderen Ausführungs- oder Run-ID) und ist bereit für die nächste Lieferung.

Außerdem habe ich entschieden, Bestellungen als Cadence Workflow zu modellieren. Der Grund dafür ist, dass auch Bestellungen einen Zustand und Zustandsübergänge von einem Zustand zum nächsten haben (z. B. Annahme der Bestellung, Bereit zur Abholung, Von Drohne abgeholt, Unterwegs, Geliefert, Bestellung abgeschlossen). Außerdem gibt es Aktionen. Der Bestellungs-Workflow ist beispielsweise dafür verantwortlich, zufällige Abhol- und Lieferorte für Bestellungen zu generieren, die sich innerhalb der Drohnenreichweite des Stützpunkts befinden, und anzugeben, wann die Bestellung zur Abholung bereit ist. Wir werden diesen Bestellungs-Workflow im nächsten Blog genauer unter die Lupe nehmen.

Im Folgenden sind die Hauptschritte des Cadence Workflow für Drohnen aufgeführt. Zuerst wird für jede Drohne genau eine Drohnen-Workflow-Instanz erstellt. Der Workflow modelliert den kompletten Liefervorgang einer Bestellung, einschließlich Aufladen der Drohne, und startet dann eine neue Instanz mit einer voll aufgeladenen Drohne, die für die nächste Lieferung bereit ist. Beachten Sie, dass dieser Code nur die @WorkflowMethod (entry point)-Methode für die Drohnen-Workflow-Implementierung darstellt und daher nicht vollständig ist. Der Code verwendet bei einigen Schritten von mir bereitgestellte Hilfsfunktionen, und er benötigt zur Ausführung die Workflow-Schnittstelle und Workflow-Activities. Der gesamte Code ist hier verfügbar.

// missing code

public static class DroneWorkflowImpl implements DroneWorkflow {

// missing code

@Override

    public String startWorkflow(String name) {

    

     droneName = name;

    

     System.out.println("Started Drone workflow " + name + ", ID=" + Workflow.getWorkflowInfo().getWorkflowId());

    

     // STEP 0 - ready

     // Drones always start ready, at the base location

     newStateAndLocation("ready", "base");

        

     // STEP 1 - wait for an Order

     // this step calls a real activity which blocks until an order arrives

     // returns an OrderWorkflow which we used to signal Order WF, also sets OrderID which is just a String

     orderWorkflow = step1_GetOrder();

     newStateAndLocation("gotOrder", "base");




    

        // STEP 2 - generate "flight plan" using the order and delivery locations from the Order

     // The Order WF is responsible for generating random order and delivery locations that are within Drone range

        step2_GenerateFlightPlan();

     newStateAndLocation("flightPlanGenerated", "base");




        // STEP 3 - another real activity - flying to get the order 

        System.out.println("Drone + " + name + " flying to pickup Order");

     newStateAndLocation("flyingToOrder", "betweenBaseAndOrder");

     // Let the Order WF know that the drone is on the way

        orderWorkflow.signalOrder("droneOnWayForPickup");

        // nextLeg is where the Drone movement is calculated, causing the drone to "fly" from planStart to planOrder locations

        // false and null arguments ensure that the Order location isn't updated yet, but charge is reduced

        activities.nextLeg(planStart, planOrder, false, null);

        

        // STEP 4 - arrived at order location, collect order - this takes time and uses charge to

        System.out.println("Drone + " + name + " picking up Order");

        newStateAndLocation("pickingUpOrder", "orderLocation");

        step4_pickUpOrder();

        

        // STEP 5 - flying to deliver the order   

        System.out.println("Drone + " + name + " delivering Order...");

        newStateAndLocation("startedDelivery", "betweenOrderAndDelivery");

        // next GPS location drone flies to

        nextGPSLocation = planDelivery;

        // let Order WF know the delivery has started

        orderWorkflow.signalOrder("outForDelivery");

        orderWorkflow.updateLocation("onWay");

        // drone flies to delivery location, updating drone and order locations and drone charge as it goes

        activities.nextLeg(planOrder, planDelivery, true, orderID);

        

        // STEP 6 - drop order

        System.out.println("Drone + " + name + " dropping Order!");

        newStateAndLocation("droppingOrder", "deliveryLocation");

        step6_dropOrder();

         

        // Step 7 - return to base

        System.out.println("Drone + " + name + " returning to Base");

        newStateAndLocation("returningToBase", "betweenDeliveryAndBase");

        nextGPSLocation = planEnd;

        // fly back to base, update drone location and charge, but not Order location as it's already been delivered.

        activities.nextLeg(planDelivery, planEnd, false, null);

        

        // STEP 8 - back at base

        System.out.println("Drone + " + name + " returned to Base!");

        newStateAndLocation("backAtBase", "base");

 

        // STEP 9 - check order - if successful then Order WF completes

        newStateAndLocation("checkOrder", "base");

        step9_checkOrder();  

        

        // Step 10 - delivery complete

        newStateAndLocation("droneDeliveryCompleted", "base");

              

        // Step 11 - charge

        newStateAndLocation("charging", "base");

        step11_recharge();

        

        // Step 12 - fully recharged

        newStateAndLocation("charged", "base");

        

        System.out.println("Starting new Drone delivery WF with coninueAsnew with same WF ID!");

        

        Workflow.continueAsNew(name);

        

     return "Drone Delivery Workflow " + name + " completed!";

    }

}

3. Anwendungsfall für Cadence: Drohnenlieferung von A nach B

Drohnenlieferung von A nach B (Quelle: Shutterstock)

Mit dem obigen Code und dieser Abbildung scheinen Drohnenlieferungen von einem Standort zum anderen eine einfache Sache zu sein. In der Praxis ist die Sache jedoch etwas komplexer, was mich motiviert hat, bei einigen Workflow-Schritten Cadence-Activity zu verwenden.

Der Standort ist bei Drohnenlieferungen der wichtigste Faktor. Ich habe entschieden, Koordinaten mit Längen- und Breitengrad (als Dezimalwerte) mit einer Auflösung von 1 m zu verwenden. Jede Drohne hat ein „GPS“, mit dem die Position der Drohne verfolgt wird, und die Workflow-Instanz der Drohne hat einen Standort-Zustand. Drohnen starten am Stützpunkt-Standort und kehren (hoffentlich) wieder dorthin zurück. Bestellungen und deren zugehörige Workflow-Instanzen haben außerdem einen Standort-Zustand. Dieser umfasst den Standort der Abholung, den Standort der Zustellung und den eigentlichen Standort (aktuelle Position) der Bestellung, der während des Transports der Bestellung durch die Drohne aktualisiert wird. Dies ist wichtig, da der Drohnen-Lieferdienst und das Geschäft jederzeit wissen möchten, wo sich die Bestellung befindet. Weiterhin können so auch die Kunden verfolgen, wann ihre Lieferung eintrifft, und diese dann von der Drohne in Empfang nehmen (und vor verärgerten Krähen retten).

Hier ist die Karte mit einem Beispiel für eine komplette Drohnenlieferung:

Bevor sich die Piraten auf Schatzsuche begeben, lassen sie sich aus dem Dorf noch eine Mahlzeit liefern! (Quelle: Shutterstock)

Aber wie kommt eine Drohne von einem Standort zum anderen? Ich habe ein einfaches Drohnenmathematik-Package DroneMaths geschrieben, das bei den Berechnungen hilft. Wenn Sie an Details interessiert sind, wie wir die Drohnenbewegungen von einem Standort zum anderen berechnen, werfen Sie einen Blick in den Anhang unten. Die Funktion DroneMaths.nextPosition(start, end, speed, time) berechnet die nächste Position der Drohne unter Verwendung des Startstandorts (start), des Zielstandorts (end) und von Geschwindigkeit (speed) und Flugzeit (time), was dann in der nextLeg()-Activity verwendet wird.

4. Cadence Workflow Activity

Die nextLeg()-Activity ist für die Simulation der Drohnenbewegung in jedem Teilabschnitt des Lieferflugs und für das Stoppen bei Ankunft am Ziel verantwortlich. Die Activity aktualisiert die Drohnenposition und den Ladestand der Drohnenbatterie und optional den Bestellstandort während des Flugs. Die Activity kehrt zurück, wenn die Drohne angekommen ist. Wir verwenden dafür eine Cadence Activity, da diese potenziell über längere Zeit ausgeführt wird, durch Verwendung von DroneMaths rechenintensiv ist und daher in einem vom Drohnen-Workflow separaten Thread ausgeführt werden sollte. Und sie kann potenziell fehlschlagen (wie bei einer echten Drohne, d. h. einer nicht simulierten, wirklich fliegenden Drohne, die mit der realen Welt interagieren muss, was sie zu einem guten Beispiel für eine nicht deterministische Activity macht). In diesem Fall soll sichergestellt sein, dass die Activity neu gestartet und die Drohnenbewegung von dem Punkt aus fortgesetzt wird, wo die Unterbrechung stattfand. Wir werden diesen Aspekt aber erst im nächsten Blog berücksichtigen, wenn wir uns eingehender mit der Behandlung von Ausnahmen befassen.

public static class DroneActivitiesImpl implements DroneActivities

{




// “Fly” from start to end location, return when drone arrives 

// Update Drone location and charge, and Order Location only if required.

public void nextLeg(LatLon start, LatLon end, boolean updateOrderLocation, String orderID) {         

         WorkflowExecution execution = Activity.getWorkflowExecution();

         String id = execution.getWorkflowId();  

DroneWorkflow droneWF = workflowClient.newWorkflowStub(DroneWorkflow.class, id);

 

OrderWorkflow orderWF = workflowClient.newWorkflowStub(OrderWorkflow.class, orderID);  

LatLon here = start;

       

         while (true)

      {

         try {

Thread.sleep((int)(moveTime * 1000 * timeScale));

} catch (InterruptedException e) { e.printStackTrace();

return;

}

         

      LatLon next = DroneMaths.nextPosition(here, end, droneSpeed, moveTime);

      here = next;

      System.out.println("Drone flew to new location = " + here.toString());

      double distance = DroneMaths.distance(here, end);

      System.out.println("Distance to destination = " + distance + " km");

      // update drone location and charge         droneWF.updateGPSLocation(here);

      droneWF.updateCharge(moveTime);

      // only update Order location if drone is transporting it

      if (updateOrderLocation)

      orderWF.updateGPSLocation(here);

     

      // check if we have arrived within 1m

      if (end.sameLocation(here))

    {

System.out.println("Drone arrived at destination.");

      return;

      }

      }

         }

}

Sie werden bemerken, dass ich im Activity-Code den allgemeinen Thread.sleep()-Aufruf verwendet habe. Das ist in Ordnung, da eine Cadence Activity (im Gegensatz zu einem Cadence Workflow) beliebigen Code verwenden kann. Allerdings habe ich bei den Activities eine Einschränkung entdeckt. Es hat sich herausgestellt, dass die Argumente und Rückgabewerte einer Activity-Methode mit dem bereitgestellten DataConverter in ein Byte-Array serialisiert werden müssen (die Standardimplementierung verwendet einen JSON-Serialisierer). Daher musste ich an die Methode einen „String OrderID“-Wert übergeben und damit eine neue OrderWorkflow-Instanz erstellen, anstatt nur einen OrderWorkflow zu übergeben (der nicht serialisierbar ist).

Abschließend gibt es noch einen DroneWorkflow-Schritt step1_GetOrder(), der entscheidend für den Fortschritt des Drohnen-Workflows ist. Dieser Schritt wartet, bis eine Bestellung zur Lieferung bereit ist, und legt dann die Bestell-ID und eine OrderWorkflow-Instanz im Hauptworkflow fest. Diese Hilfsmethode ist eigentlich ein Wrapper für eine weitere Activity, die blockiert, bis eine Bestellung zur Abholung durch eine Drohne bereit ist, und die garantiert, dass jede abholbereite Bestellung von genau einer Drohne abgeholt wird. Wie funktioniert das? Im Grunde ist dies ein weiteres Beispiel für ein Cadence+Kafka-Integrationsmuster, das wir im nächsten Blog zusammen mit einigen weiteren Kafka+Cadence-Mustern untersuchen werden (einschließlich Starten eines Cadence Workflow mit Kafka, das auch beim Bestellungs-Workflow verwendet wird). Im nächsten Blog werden wir außerdem die gesamte Lösung analysieren, einschließlich des Bestellungs-Workflows, sowie weitere Cadence-Funktionen wie Abfragen, Neuversuche, Heartbeats, Als neu fortfahren und Nebeneffekte.

P.S. Hat der Drohnen-Lieferdienst zufällig von meinem Projekt gehört? Finde gerade eine Broschüre in meinem Briefkasten (ja, ziemlich „old school“, aber vielleicht per Drohne zugestellt?), die den Start des Lieferdienstes in meiner Gegend ankündigt – immer her mit den Drohen-gelieferten Sachen! (Ich habe auch festgestellt, dass deren Drohnen viel schneller sind als meine – ich werden die Durchschnittsgeschwindigkeit auf 100 km/h erhöhen müssen, um mithalten zu können).

5. Anhang – Drohnenbewegung: Standort, Entfernung, Richtung, Geschwindigkeit und Ladung!

Wie bewegt sich eine Drohne von einem Standort zum anderen? Ich habe ein einfaches Drohnenmathematik-Package DroneMaths geschrieben, das bei den Berechnungen hilft (die meisten Formeln sind hier nachzulesen). Zuerst benötigen wir eine Funktion zur Berechnung der Entfernung (in km) zwischen zwei Standorten anhand der dezimalen Längen-/Breitenkoordinaten (dabei wird auch die Erdkrümmung berücksichtigt, wobei dies für die kurzen Entfernungen, mit denen wir es hier zu tun haben, vernachlässigt werden kann – außer natürlich, die Drohnen fliegen sehr hoch). So wird sichergestellt, dass sich der gesamte Liefervorgang (einschließlich Rückkehr) im Bereich der maximalen Reichweite der Drohne abspielt.

Aber wie erfolgt die Navigation von einem Standort zum anderen? Wie sich herausstellt, handelt es sich hier um eine ganz traditionelle Navigation, wie sie für Schiffe verwendet wird. Dafür muss die Richtung (Kurs) von einem Standort zum anderen in Grad bekannt sein (0 Grad nach Norden, 90 Grad nach Osten, 180 Grad nach Süden und 270 Grad nach Westen).

Um Schiffe rund um den Erdball zu navigieren, braucht es nur Karte, Kompass und Zirkel. (Quelle: Shutterstock)

Um schließlich den nächsten „Wegpunkt“ zu ermitteln und der Drohne zu ermöglichen, auf kürzestem Weg von einem Standort zum anderen zu gelangen (also per Luftlinie, was voraussetzt, dass es auf dem Weg keine Hindernisse oder Flugverbotszonen gibt – und auch keine Krähen), verwenden wir die Funktion nextPosition() zur Berechnung der nächsten Drohnenposition. Damit wird die Richtung vom aktuellen Standort zum vorgesehenen Zielstandort ermittelt (Bestellstandort, Lieferstandort oder Stützpunkt-Standort, je nach aktuellem Zustand der Drohne), und bei gegebener Geschwindigkeit (wir nehmen an, dass sich die Drohne über die gesamte Strecke mit einer Durchschnittsgeschwindigkeit von 20 km/h bewegt) wird die im nächsten Zeitintervall (welches konfigurierbar ist und für eine höhere Geschwindigkeit als in Echtzeit-Simulationen skaliert werden kann) zurückzulegende Entfernung berechnet. Danach wird eine weitere Funktion aufgerufen, um den Standort anhand des aktuellen Standorts, der Reichweite und der Richtung zu berechnen.

Wenn wir das Ziel bereits erreicht haben, kann der Flug gestoppt und die nächste Aktion ausgeführt werden (z. B. Bestellung aufnehmen, Bestellung ablegen, zum Stützpunkt absenken). Momentan gehen wir davon aus, dass jede Flugroute erfolgreich ist, aber aus Spaß können auch verschiedene Ausnahmen wie Wind, Krähen, Zusammenstöße usw. mit der entsprechende Ausnahmebehandlung eingeführt werden (wieder je nach Drohnenzustand – wenn z. B. die Drohne aus irgendeinem Grund eine Lieferung nicht aufnehmen kann, muss die Bestellung neu geplant werden, vielleicht unter Verwendung einer Prioritätswarteschlange, sodass die nächste verfügbare Drohne erst der nicht gelieferten Bestellung zugeteilt wird, bevor weitere Bestellungen akzeptiert werden).

Die Activity nextLeg() (siehe oben) ist für die Berechnung der verschiedenen Drohnenbewegungen in jedem Abschnitt der Lieferung verantwortlich, aber sendet vor allem die aktualisierten Positionsdaten der Drohne und der zugehörigen Bestellung unter Verwendung von Cadence-Signalen, deren Funktionsweise im vorherigen Blog erklärt wurde. Auch der Drohnen-Workflow selbst signalisiert dem Bestellungs-Workflow Zustandsänderungen, z. B. mit orderWorkflow.signalOrder("droneHasOrder").

Der nächste Drohnenstandort wird anhand von aktuellem Standort, Richtung, Geschwindigkeit und Entfernung berechnet (Quelle: Shutterstock)

Drohnen werden elektrisch betrieben, sodass beim Fliegen, Schweben usw. Strom verbraucht wird. Für jede inkrementelle Entfernung, die die Drohne fliegt, reduzieren wir die Batterieladung mithilfe der Hilfsfunktion updateCharge(time), die den Ladungsverbrauch auf Basis der bereitgestellten Flugzeit berechnet.

Und natürlich muss die Batterieladung ausreichen, bis die Drohne zum Stützpunkt zurückgekehrt ist. (Quelle: Shutterstock)

Wir unterstützen Sie gerne

Ob Cadence, Debian oder PostgreSQL: mit über 22+ Jahren an Entwicklungs- und Dienstleistungserfahrung im Open Source Bereich, können credativ und Instaclustr Sie mit einem beispiellosen und individuell konfigurierbaren Support professionell begleiten und Sie in allen Fragen bei Ihrer Open Source Infrastruktur voll und ganz unterstützen.

Sie möchten mehr über Cadence lernen und über die Vorteile die es Ihrer Organisation bietet. Dann laden Sie sich unser englischsprachiges Whitepaper runter.

Sollten Sie Fragen zu unserem Artikel haben oder würden sich wünschen, dass unsere Spezialisten sich Ihr System angucken und Ihre Infrastruktur optimieren, dann schauen Sie doch vorbei und melden sich über unser Kontaktformular oder schreiben uns eine E-mail an info@credativ.de.

Über unsere Mutterfirma Instaclustr bieten wir auch eine komplett verwaltete Plattform für Cadence an.

Original englischsprachige Artikel auf Instaclustr.com

Folgen Sie der Reihe auf credativ.de: Drohnen fliegen lassen mit Cadence

Kategorien: HowTos
Tags: apachekafka® cadence

über den Autor

Paul Brebner

zur Person

Paul is the Technology Evangelist at Instaclustr/Spot by NetApp. For the past five years, he has been learning new scalable Big Data technologies, solving realistic problems, building applications, and blogging and talking about Apache Cassandra, Apache Spark, Apache Kafka, Redis, Elasticsearch, PostgreSQL, Cadence, and many more open source technologies. Since learning to program on a VAX 11/780, Paul has extensive R&D, teaching, and consulting experience in distributed systems, technology innovation, software architecture and engineering, software performance and scalability, grid and cloud computing, and data analytics and machine learning. Paul has also worked at Waikato University (NZ), UNSW, CSIRO, UCL (UK), NICTA/ANU, and several tech start-ups. Paul has an MSc in Machine Learning and a BSc (Computer Science and Philosophy).

Beiträge ansehen


Beitrag teilen: