Sascha
Published © MIT

Autonomous driving and obstacle avoiding Rover

Rover that drives autonomous over GNSS and digital compass. Based on Windows IoT Enterprise/UWP-App, OpenStreetMap, Gamepad and iPad Mini.

AdvancedShowcase (no instructions)Over 6 days678
Autonomous driving and obstacle avoiding Rover

Things used in this project

Hardware components

Aaeon Up²
×1
Arexx Wild Thumper 4WD (34:1 gear ratio)
×1
Logitech Brio Webcam
×1
Microsoft Xbox One Controller
×1
Microsoft Xbox Wireless Receiver
×1
Apple iPad Mini 5
×1
Controller mount for Xbox One S & Apple iPad mini
×1
ASUS AC54 B1 WiFi
×1
NAVILOCK NL-8012U Multi GNSS
×1
Robot Electronics CMPS12 - digital compass
×1
Terabee TeraRanger Evo 60m Rangefinder
Used with TeraRanger I2C/UART backboard.
×2
Adafruit ADS1115 - Analog To Digital
×1
Sabrent HB-MCRM USB 2.0 Hub
×1
AZDelivery CP2102 USB to Serial TTL
×1
Piezo Summer Buzzer - 3.3 volt
×1
Pololu High Power Simple Motor Controller G2 18v25
×2
Matek PDB-XT60 Power Distribution Board
×1
Pololu 5V, 15A Step-Down Voltage Regulator
×1
Pololu 3.3V, 3.6A Step-Down Voltage Regulator
×1
SLS XTRON 7000MAH 2S1P 7.4V 34/70C LiPo
×1

Software apps and online services

Microsoft Windows IoT Enterprise
Microsoft Visual Studio 2019
To build the UWP-App for the Rover.
OpenStreetMap - Offline Maps
Generating map-tiles with Maperitive and OpenStreetMap-OSM file from https://www.geofabrik.de and host in IIS. In browser Leaflet ist used to display map.
u-blox u-center
Used to configure the GNSS-receiver.

Story

Read more

Schematics

Rover wiring

Shows how the components are connected to each other. Doesn't show how individual components have to be soldered.

Code

Autonomous driving (Code snippet)

C#
The rover checks if the bearing corresponds with the target navigation point. If not the Rover drives a small curve to the target if bearing delta is small. In case of big bearing delta the Rover stops and turn directly to the target direction.
using rover.sl.Geography;
using rover.sl.Motors;
using rover.sl.Sensors.Compass;
using rover.sl.Sensors.Encoder;
using rover.sl.Sensors.Gnss;
using rover.sl.Sensors.Rangefinder;
using System.Threading;
using System.Threading.Tasks;

namespace rover.sl.Drive
{
    public class AutonomousDriveController : DriveBase
    {
        /// <summary>
        /// Entfernung in Metern ab wann ein Marker als erreicht gilt.
        /// </summary>
        private const double MARKER_REACHED_DISTANCE_METER = 3;

        /// <summary>
        /// Gibt an ob nach dem autonomen Fahren der Rover gestoppt wurde.
        /// </summary>
        private bool _driveStopped = true;

        /// <summary>
        /// Die Standard Richtung in die der Rover länger ausweichen soll. True = links.False = rechts.
        /// </summary>
        private bool _obstacleAvoidDefaultLeftOrRight = false;

        // Abhänigkeiten.
        private ObstacleDriveController _obstacleDriveController;

        public AutonomousDriveController(MotorController motorController, EncoderController encoderController, RangefinderController rangefinderController)
            : base(motorController, encoderController)
        {
            _obstacleDriveController = new ObstacleDriveController(motorController, encoderController, rangefinderController);

            var thread = new Thread(() =>
            {
                DriveAsync().Wait();
            });
            thread.Priority = ThreadPriority.Normal;
            thread.Start();
        }

        private async Task DriveAsync()
        {
            while (true)
            {
                // Wenn autonom gefahren werden soll.
                if (await CancelRequiredAsync(null, false) == false)
                {
                    // Hole den ersten Marker zu dem navigiert werden soll.
                    var marker = GeographyMarkerChanges.GetFirstMarker();

                    // Wenn ein Marker bereitsteht und das Gnss Signal ausreichend gut ist.
                    if (marker != null)
                    {
                        // Der autonome Rover fährt.
                        _driveStopped = false;

                        // Rover Ausrichtung.
                        var roverBearing = CompassController.Heading;

                        // Rover Geschwindigkeit.
                        var roverSpeed = GetRoverSpeed();

                        // Hole Marker Position.
                        var markerPoint = new GeographyPoint(marker.Point.Latitude, marker.Point.Longitude);

                        // Hole aktuelle Position des Rover.
                        var roverPoint = new GeographyPoint(Gnss.Latitude, Gnss.Longitude);

                        // Distanz in Meter wie weit der Rover vom Marker entfernt ist.
                        var pointsDistanceMeters = GeographyCalculator.GetDistanceInMeter(roverPoint, markerPoint);

                        // Der Rover hat den Marker erreicht.
                        if (pointsDistanceMeters <= MARKER_REACHED_DISTANCE_METER)
                        {
                            // Erreichten Marker entfernen und mit nächstem Fortfahren.
                            GeographyMarkerChanges.RemoveMarker(marker.Id);
                            continue;
                        }

                        // Berechne Ausrichtung von Rover zu Marker.
                        var roverToMarkerBearing = GeographyCalculator.GetBearing(roverPoint, markerPoint);

                        // Differenz zwischen Rover Ausrichtung und Ausrichtung zu Marker.
                        var bearingDifference = GeographyCalculator.GetBearingDifference(roverBearing, roverToMarkerBearing);

                        // Die Ausrichtung des Rovers stimmt nicht mit der Ausrichtung zum Marker überein.
                        if (IsBearingCorrect(bearingDifference) == false)
                        {
                            // Die Ausrichtung des Rover weicht nicht stark von der Ausrichtung zum Marker ab: Leichte Kurven fahren.
                            if (IsBearingSmall(bearingDifference))
                            {
                                // Berechne um wieviel Grad der Rover die Ausrichtung ändern muss.
                                var bearingChange = GetBearingChange(bearingDifference, roverSpeed, pointsDistanceMeters);

                                // Abbruch.
                                if (await CancelRequiredAsync(marker.Id, false))
                                {
                                    continue;
                                }

                                // Falls die Hinderniserkennung aktiv ist, Hindernissen ausweichen.
                                if (MotorController.UseObstacleDetection)
                                {
                                    // Hinderniserkennung.
                                    if (await AvoidObstaclesAsync(roverSpeed, marker.Id))
                                    {
                                        // Falls ein Hindernis erkannt wurde Berechnung von vorne beginnen (für aktuelle Navigationsdaten).
                                        continue;
                                    }
                                }

                                // Übergebe den Motor Befehl entsprechend der neuen Ausrichtung und Geschwindigkeit.
                                await MoveRoverAsync(bearingChange, roverSpeed);
                            }
                            // Die Ausrichtung des Rover weicht stark von der Ausrichtung zum Marker ab: Auf der Stelle direkt zum Marker wenden.
                            else
                            {
                                // Der Rover muss angehalten werden für eine direkte Drehung. Rover anhalten.
                                if (_encoderController.IsStopped == false)
                                {
                                    // Übergebe Motor Stop Befehl.
                                    await StopRoverAsync();
                                }
                                // Wenn der Rover angehalten hat, den Rover zum Marker direkt Ausrichten.
                                else
                                {
                                    // Wende Rover zu Marker aus.
                                    if (await TurnRoverToBearingAsync(roverToMarkerBearing, marker.Id, false))
                                    {
                                        // Abbruch.
                                        continue;
                                    }
                                }
                            }
                        }
                        // Die Ausrichtung des Rovers stimmt mit der Ausrichtung zum Marker überein: Gerade aus fahren.
                        else
                        {
                            // Abbruch.
                            if (await CancelRequiredAsync(marker.Id, false))
                            {
                                continue;
                            }

                            // Falls die Hinderniserkennung aktiv ist, Hindernissen ausweichen.
                            if (MotorController.UseObstacleDetection)
                            {
                                // Hinderniserkennung.
                                if (await AvoidObstaclesAsync(roverSpeed, marker.Id))
                                {
                                    // Falls ein Hindernis erkannt wurde Berechnung von vorne beginnen (für aktuelle Navigationsdaten).
                                    continue;
                                }
                            }

                            // Übergebe Motor gerade aus fahren Befehl.
                            await MoveRoverAsync(roverSpeed);
                        }
                    }
                    else
                    {
                        // Abbruch.
                        if (await CancelRequiredAsync(marker?.Id, false))
                        {
                            continue;
                        }

                        // Es gibt keine weiteren Marker. Rover stoppen.
                        await StopRoverAsync();
                    }
                }
                else
                {
                    // Der autonome Rover wurde noch nicht gestoppet.
                    if (_driveStopped == false)
                    {
                        // Autonomes Fahren ist nicht aktiv. Rover stoppen.
                        await StopRoverAsync();

                        // Der autonome wurde gestoppt.
                        _driveStopped = true;
                    }
                }

                // Wartezeit bis die Berechnung der autonomen Fahrt erneut ausgeführt wird.
                await Task.Delay(DRIVE_LOOP_TIMEOUT_MS);
            }
        }

        /// <summary>
        /// Erkenne und umfahre Hindernisse.
        /// </summary>
        /// <param name="roverSpeed">Rover Geschwindigkeit.</param>
        /// <param name="markerId">Aktuelle Marker Id.</param>
        /// <returns></returns>
        private async Task<bool> AvoidObstaclesAsync(double roverSpeed, long markerId)
        {
            // Ergebnis der Hindernisumfahrung.
            var obstacleAvoidResult = await _obstacleDriveController.AvoidObstaclesAsync(roverSpeed, markerId, _obstacleAvoidDefaultLeftOrRight);

            // Es wurde abgebrochen oder ein Hindernis erkannt.
            if (obstacleAvoidResult.Cancel)
            {
                return true;
            }

            // Setze neue Standardrichtung in die länger ausgeweicht werden soll.
            _obstacleAvoidDefaultLeftOrRight = obstacleAvoidResult.DefaultLeftOrRight;

            // Abbruch.
            if (await CancelRequiredAsync(markerId, false))
            {
                return true;
            }

            return false;
        }
    }
}

Obstacle avoid driving (Code snippet)

C#
If the Rover detects a far obstacle he try to avoid it with a small curve to left. If he has no success he try to avoid the obstacle at the right. If the obstacle is near the Rover stops and turn left. If there is an obstacle the Rover turn right. If Rover has no success to avoid obstacle he drives back.
using rover.sl.Geography;
using rover.sl.Motors;
using rover.sl.Sensors.Compass;
using rover.sl.Sensors.Encoder;
using rover.sl.Sensors.Rangefinder;
using System;
using System.Threading.Tasks;

namespace rover.sl.Drive
{
    public class ObstacleDriveController : DriveBase
    {
        /// <summary>
        /// Der Rover steht vor einem fernen Hindernis in Zentimetern.
        /// </summary>
        private const int ROVER_FAR_OBSTACLE_CENTIMETERS = 500; // Feintuning: Minimum: 160 | Standard: 500

        /// <summary>
        /// Der Rover steht vor einem nahmen Hindernis in Zentimetern.
        /// </summary>
        private const int ROVER_NEAR_OBSTACLE_CENTIMETERS = 150; // Feintuning: Minimum: 120 | Standard: 150

        /// <summary>
        /// Der Rover steht vor einem nahem Hindernis. Ausweichen/Lenken in Grad.
        /// </summary>
        private const int ROVER_NEAR_OBSTACLE_TURN_DEGREES = 90;

        /// <summary>
        /// Der Rover steht vor einem fernen Hindernis. Ausweichen/Lenken nach rechts in Grad.
        /// </summary>
        private const double ROVER_FAR_OBSTACLE_TURN_RIGHT_DEGREES = 3;

        /// <summary>
        /// Der Rover steht vor einem fernen Hindernis. Ausweichen/Lenken nach links in Grad.
        /// </summary>
        private const double ROVER_FAR_OBSTACLE_TURN_LEFT_DEGREES = ROVER_FAR_OBSTACLE_TURN_RIGHT_DEGREES * -1;

        /// <summary>
        /// Anteil der Strecke die in eine Richtung leicht ausgewichen werden soll bei einem fernen Hindernis bevor in die andere Richtung leicht ausgewichen werden soll.
        /// </summary>
        private const int THIRDS_TIME_TO_OBSTACLE_DIVIDER = 6; // Feintuning: Minimum: 5d | Standard: 8d

        /// <summary>
        /// Start der Fahrzeit nachdem ein Weg ohne Hindernisse gefunden wurde und zuvor ein nahes oder fernes Hindernis erkannt wurde.
        /// </summary>
        private DateTime? _driveObstacleFree = null;

        /// <summary>
        /// Die Zeit die der Rover sich nach rechts oder links freifahren soll, wenn er einem fernen Hindernis ausweicht.
        /// </summary>
        private const int DRIVE_OBSTACLE_FREE_TIME_STARIGHT_AWAY = 700; // Feintuning: Minimum: 500 | Standard: 700

        /// <summary>
        /// Die Zeit die der Rover sich nach rechts oder links freifahren soll, wenn er einem nahen Hindernis ausweicht.
        /// </summary>
        private const int DRIVE_OBSTACLE_FREE_TIME_NOT_STARIGHT_AWAY = 1500; // Feintuning: Minimum: 2000 | Standard: 1500

        /// <summary>
        /// Die Zeit die der Rover sich nach rechts oder links freifahren soll und bereits in eine Richtung gefahren ist.
        /// </summary>
        private const int DRIVE_OBSTACLE_FREE_TIME_NOT_STARIGHT_AWAY_DOUBLE_TIME_FACTOR = 2600; // Feintuning: Minimum: 3000 | Standard: 2600

        /// <summary>
        /// Faktor wenn der Rover bereits ausgerichtet wurde und sich vollständig in die endgegengesetzte Richtung wenden muss.
        /// </summary>
        private const int AVOID_OBSTACLE_BEARING_DEGREES_REVERT = 2;

        // Abhänigkeiten.
        private RangefinderController _rangefinderController;

        public ObstacleDriveController(MotorController motorController, EncoderController encoderController, RangefinderController rangefinderController)
            : base(motorController, encoderController)
        {
            _rangefinderController = rangefinderController;
        }

        /// <summary>
        /// Erkennt und umfährt Hindernisse.
        /// </summary>
        /// <param name="roverSpeed">Geschwindigkeit in km/h.</param>
        /// <param name="markerId">Marker Id.</param>
        /// <param name="defaultLeftOrRight">Die Standard Richtung in die der Rover länger ausweichen soll. True = links. False = rechts.</param>
        /// <returns></returns>
        public async Task<ObstacleDriveResult> AvoidObstaclesAsync(double roverSpeed, long markerId, bool defaultLeftOrRight)
        {
            // Hindernis umfahren.
            var obstacleDetectedAndCancel = false;

            // Entfernung zu nächstem davorliegenden Hindernis.
            var obstacleDistanceCentimeter = _rangefinderController.FrontDistanceCentimeters;

            // Ein nahes Hindernis wurde erkannt.
            var nearObstacle = obstacleDistanceCentimeter <= ROVER_NEAR_OBSTACLE_CENTIMETERS;

            // Ein fernes Hindernis wurde erkannt.
            var farObstacle = obstacleDistanceCentimeter <= ROVER_FAR_OBSTACLE_CENTIMETERS;

            // Ein Hindernis wurde erkannt.
            var anyObstacleDetected = nearObstacle || farObstacle;

            // Die aktuelle Ausrichtung des Rovers wenn er versucht Hindernissen auszuweichen.
            var obstacleAvoidDirection = new ObstacleAvoidDirection();
            obstacleAvoidDirection.LeftOrRight = defaultLeftOrRight;

            // Die neue Standard Richtung in die der Rover länger ausweichen soll. True = links.False = rechts.
            var newDefaultLeftOrRight = defaultLeftOrRight;

            // Gibt an ob gefahren werden muss um dem Hindernis auszuweichen.
            var driveObstacleAvoid = false;

            // Gibt an wie lange der Rover fahren muss um das Hindernis zu erreichen.
            var timeToObstacle = GetTimeToDistance(roverSpeed, obstacleDistanceCentimeter);

            // Gibt an wie lange der Rover zum drittel des Ziels fahren muss.
            var thirdsTimeToObstacle = timeToObstacle / THIRDS_TIME_TO_OBSTACLE_DIVIDER;

            // Ein Hindernis wurde erkannt oder es wurde kein Hindernis mehr erkannt und für eine angegebene Zeit in diese Richtung fahren.
            while (anyObstacleDetected || driveObstacleAvoid)
            {
                // Abbruch.
                if (await CancelRequiredAsync(markerId, true))
                {
                    // Abbruch.
                    obstacleDetectedAndCancel = true;
                    break;
                }

                // Hindernis wurde erkannt.
                obstacleDetectedAndCancel = true;

                // Wenn kein Hindernis mehr erkannt wurde.
                if (anyObstacleDetected == false)
                {
                    // Starte Fahrzeit wenn kein Hindernis mehr erkannt wurde und zuvor ein nahes oder fernes Hindernis erkannt wurde.
                    if (_driveObstacleFree.HasValue == false)
                    {
                        _driveObstacleFree = DateTime.Now;
                    }

                    // Der Rover steht gerade, da er einem fernen Hindernis ausweicht.
                    if (obstacleAvoidDirection.IsStraightAway)
                    {
                        // Rover muss mit einer leichten Kurve freigefahren werden. Übergebe Motor Befehl.
                        await MoveRoverAsync(obstacleAvoidDirection.DriveFarObstacleFreeTurnDegrees, roverSpeed);
                    }
                    // Der Rover steht seitwärts, da er einem nahen Hindernis ausweicht.
                    else
                    {
                        // Rover muss gerade aus freigefahren werden. Übergebe Motor Befehl.
                        await MoveRoverAsync(roverSpeed);
                    }
                }
                // Wenn ein Hindernis erkannt wurde.
                else
                {
                    // Wenn der Rover vor einem nahen Hindernis steht und sich wenden möchte muss er zuerst gestoppt werden.
                    if (nearObstacle && _encoderController.IsStopped == false)
                    {
                        // Abbruch.
                        if (await CancelRequiredAsync(markerId, true))
                        {
                            // Abbruch.
                            obstacleDetectedAndCancel = true;
                            break;
                        }

                        // Übergebe Motor Stop Befehl.
                        await StopRoverAsync();
                    }
                    // Der Rover steht vor einem nahmen Hindernis.
                    else if (nearObstacle)
                    {
                        // Setze Start der Fahrzeit zurück wenn ein Hindernis erkannt wurde.
                        _driveObstacleFree = null;

                        // Wenn bereits versucht wurde nach rechts und links auszuweichen, fahre nach hinten.
                        if (obstacleAvoidDirection.IsStraightAway == false && obstacleAvoidDirection.LeftOrRight == defaultLeftOrRight)
                        {
                            // Richte den Rover gerade aus.
                            if (await TurnRoverStraightAwayAsync(obstacleAvoidDirection, markerId))
                            {
                                // Abbruch.
                                obstacleDetectedAndCancel = true;
                                break;
                            }

                            // Fahre entsprechend der angegebenen Zeit und wenn es kein Hindernis gibt zurück.
                            if (await DriveBackAsync(roverSpeed, DRIVE_OBSTACLE_FREE_TIME_NOT_STARIGHT_AWAY, markerId))
                            {
                                // Abbruch.
                                obstacleDetectedAndCancel = true;
                                break;
                            }

                            // Rover ist gerade ausgerichtet.
                            obstacleAvoidDirection.IsStraightAway = true;

                            // Neue Standardausweich Richtung setzen.
                            newDefaultLeftOrRight = obstacleAvoidDirection.LeftOrRight;

                            // Abbruch. Der Rover konnte sich nicht freifahren. Führt zur Aktualisierung der Navigationsdaten. Ggf. ist der Rover dann nicht mehr blockiert.
                            obstacleDetectedAndCancel = true;
                            break;
                        }
                        // Wenn der Rover noch nicht versucht hat nach links und rechts auszuweichen.
                        else
                        {
                            // Die neue Standard Richtung in die der Rover länger ausweichen soll. True = links.False = rechts.
                            newDefaultLeftOrRight = obstacleAvoidDirection.LeftOrRight;

                            // Setze nächste Richtung in die der Rover ausweichen soll.
                            obstacleAvoidDirection.LeftOrRight = obstacleAvoidDirection.LeftOrRight == false;

                            // Der Rover versucht dem Hindernis links auszuweichen.
                            var avoidObstacleBearingDegrees = -ROVER_NEAR_OBSTACLE_TURN_DEGREES;

                            // Der Rover versucht dem Hindernis rechts auszuweichen.
                            if (obstacleAvoidDirection.LeftOrRight == false)
                            {
                                avoidObstacleBearingDegrees = ROVER_NEAR_OBSTACLE_TURN_DEGREES;
                            }

                            // Der Rover wurde bereits ausgerichtet und muss sich vollständig in die endgegengesetzte Richtung wenden.
                            if (obstacleAvoidDirection.IsStraightAway == false)
                            {
                                avoidObstacleBearingDegrees *= AVOID_OBSTACLE_BEARING_DEGREES_REVERT;
                            }

                            // Winkel in dem der Rover dem Hindernis ausweicht.
                            var avoidObstacleBearing = GeographyCalculator.CalculateBearing(CompassController.Heading, avoidObstacleBearingDegrees);

                            // Wende in andere Richtung als dem Hindernis.
                            if (await TurnRoverToBearingAsync(avoidObstacleBearing, markerId, true))
                            {
                                // Abbruch.
                                obstacleDetectedAndCancel = true;
                                break;
                            }

                            // Rover fährt gerade aus.
                            await MoveRoverAsync(roverSpeed);

                            // Rover muss freigefahren werden.
                            obstacleAvoidDirection.DriveObstacleAvoid = true;

                            // Rover ist nicht gerade ausgerichtet.
                            obstacleAvoidDirection.IsStraightAway = false;
                        }
                    }
                    // Der Rover steht vor einem fernen Hindernis und ist gerade ausgerichtet.
                    else if (obstacleAvoidDirection.IsStraightAway)
                    {
                        // Abbruch.
                        if (await CancelRequiredAsync(markerId, true))
                        {
                            obstacleDetectedAndCancel = true;
                            break;
                        }

                        // Setze Start der Fahrzeit zurück wenn ein Hindernis erkannt wurde.
                        _driveObstacleFree = null;

                        // Wenn noch nicht versucht wurde einem fernen Hindernis eine bestimmte Zeit auszuweichen.
                        if (obstacleAvoidDirection.FarObstacleDirectionTime.HasValue == false)
                        {
                            // Setze nächste Richtung in die der Rover ausweichen soll.
                            obstacleAvoidDirection.LeftOrRight = obstacleAvoidDirection.LeftOrRight == false;

                            // Starte die Zeit in der der Rover versucht einem Hindernis auszuweichen.
                            obstacleAvoidDirection.FarObstacleDirectionTime = DateTime.Now;
                        }
                        // Wenn der Rover eine bestimmte Zeit versucht hat einem Hindernis in eine Richtung auszuweichen in die andere Richtung wechseln.
                        else if (DateTime.Now >= obstacleAvoidDirection.FarObstacleDirectionTime.Value.AddMilliseconds(thirdsTimeToObstacle))
                        {
                            // Wenn bereits versucht wurde nach rechts und links auszuweichen. Abbruch.
                            if (obstacleAvoidDirection.LeftOrRight == defaultLeftOrRight)
                            {
                                // Die neue Standard Richtung in die der Rover länger ausweichen soll. True = links.False = rechts.
                                newDefaultLeftOrRight = obstacleAvoidDirection.LeftOrRight;

                                // Abbruch. Der Rover konnte sich nicht freifahren. Führt zur Aktualisierung der Navigationsdaten. Ggf. ist der Rover dann nicht mehr blockiert.
                                obstacleDetectedAndCancel = true;
                                break;
                            }

                            // Setze nächste Richtung in die der Rover ausweichen soll.
                            obstacleAvoidDirection.LeftOrRight = obstacleAvoidDirection.LeftOrRight == false;

                            // Starte die Zeit in der der Rover versucht einem Hindernis auszuweichen.
                            obstacleAvoidDirection.FarObstacleDirectionTime = DateTime.Now;
                        }

                        // Die neue Standard Richtung in die der Rover länger ausweichen soll. True = links.False = rechts.
                        newDefaultLeftOrRight = obstacleAvoidDirection.LeftOrRight;

                        // Der Rover versucht dem fernen Hindernis links auszuweichen.
                        if (obstacleAvoidDirection.LeftOrRight == true)
                        {
                            // Übergebe den Motor Befehl nach links ausweichen.
                            await MoveRoverAsync(ROVER_FAR_OBSTACLE_TURN_LEFT_DEGREES, roverSpeed);

                            // Gibt an mit wieviel Grad der Rover nach links sich freifahren soll.
                            obstacleAvoidDirection.DriveFarObstacleFreeTurnDegrees = ROVER_FAR_OBSTACLE_TURN_LEFT_DEGREES;
                        }
                        // Der Rover versucht dem fernen Hindernis rechts auszuweichen.
                        else
                        {
                            // Übergebe den Motor Befehl nach rechts ausweichen.
                            await MoveRoverAsync(ROVER_FAR_OBSTACLE_TURN_RIGHT_DEGREES, roverSpeed);

                            // Gibt an mit wieviel Grad der Rover nach links sich freifahren soll.
                            obstacleAvoidDirection.DriveFarObstacleFreeTurnDegrees = ROVER_FAR_OBSTACLE_TURN_RIGHT_DEGREES;
                        }

                        // Rover muss freigefahren werden.
                        obstacleAvoidDirection.DriveObstacleAvoid = true;
                    }
                }

                // Wartezeit bis die Berechnung der autonomen Fahrt erneut ausgeführt wird.
                await Task.Delay(DRIVE_LOOP_TIMEOUT_MS);

                // Entfernung zu nächstem davorliegenden Hindernis.
                obstacleDistanceCentimeter = _rangefinderController.FrontDistanceCentimeters;

                // Prüfen ob weiterhin ein nahes Hindernis erkennt wird.
                nearObstacle = obstacleDistanceCentimeter <= ROVER_NEAR_OBSTACLE_CENTIMETERS;

                // Prüfen ob weiterhin ein fernes Hindernis erkennt wird.
                farObstacle = obstacleDistanceCentimeter <= ROVER_FAR_OBSTACLE_CENTIMETERS && obstacleAvoidDirection.IsStraightAway;

                // Ein Hinderniss wurde erkannt.
                anyObstacleDetected = nearObstacle || farObstacle;

                // Gibt an ob der Rover sich seitwärts wegen einem nahmen Hindernis freifahren muss, oder sich wegen einem fernen Hindernis freifahren muss
                // um diesem nicht zu knapp auszuweichen. Der Rover muss sich freifahren wenn er nicht blockiert war und seitwärts fährt, oder mit leichter
                // Kurve einem fernen Hindernis ausweicht.
                driveObstacleAvoid = obstacleAvoidDirection.DriveObstacleAvoid;

                // Der Rover muss sich freifahren.
                if (driveObstacleAvoid)
                {
                    // Die Zeit die der Rover sich seitwärts freifahren soll, nach einem nahen Hindernis.
                    var driveObstacleFreeTime = DRIVE_OBSTACLE_FREE_TIME_NOT_STARIGHT_AWAY;

                    // Der Rover steht gerade, da er einem fernen Hindernis ausweicht.
                    if (obstacleAvoidDirection.IsStraightAway)
                    {
                        // Die Zeit die der Rover sich freifahren soll, nach einem fernen Hindernis.
                        driveObstacleFreeTime = DRIVE_OBSTACLE_FREE_TIME_STARIGHT_AWAY;
                    }
                    // Der Rover steht seitwärts, da er einem nahen Hindernis ausweicht.
                    else
                    {
                        // Wenn der Rover bereits in die eine Richtung ausgewichen ist und nun in die andere Richtung ausweicht muss er doppel solange fahren,
                        // da er erst die andere Ausweichstrecke zurückfahren muss.
                        if (obstacleAvoidDirection.LeftOrRight == defaultLeftOrRight)
                        {
                            driveObstacleFreeTime = DRIVE_OBSTACLE_FREE_TIME_NOT_STARIGHT_AWAY_DOUBLE_TIME_FACTOR;
                        }
                    }

                    // Gibt an ob gefahren werden muss um einem nahen Hindernis seitwärts auszuweichen oder einem fernen Hindernis mit einer leichten Kurve.
                    driveObstacleAvoid = _driveObstacleFree.HasValue == false || DateTime.Now <= _driveObstacleFree.Value.AddMilliseconds(driveObstacleFreeTime);
                }
            }

            // Wenn Rover nicht gerade ausgerichtet ist.
            if (obstacleAvoidDirection.IsStraightAway == false)
            {
                // Der Rover muss angehalten werden für eine direkte Drehung. 
                if (await DriveStopAsync(markerId))
                {
                    // Abbruch.
                    obstacleDetectedAndCancel = true;
                }
                else
                {
                    // Richte den Rover gerade aus.
                    if (await TurnRoverStraightAwayAsync(obstacleAvoidDirection, markerId))
                    {
                        // Abbruch.
                        obstacleDetectedAndCancel = true;
                    }
                }
            }

            // Ergebnis der Hindernissumfahrung.
            var obstacleAvoidResult = new ObstacleDriveResult
            {
                Cancel = obstacleDetectedAndCancel,
                DefaultLeftOrRight = newDefaultLeftOrRight
            };

            return obstacleAvoidResult;
        }

        /// <summary>
        /// Halte den Rover an.
        /// </summary>
        /// <param name="markerId">Marker Id.</param>
        /// <returns></returns>
        private async Task<bool> DriveStopAsync(long markerId)
        {
            // Rover anhalten.
            while (_encoderController.IsStopped == false)
            {
                // Abbruch.
                if (await CancelRequiredAsync(markerId, true))
                {
                    return true;
                }

                // Übergebe Motor Stop Befehl.
                await StopRoverAsync();

                // Wartezeit bis geprüft wird ob der Rover gestoppet ist.
                await Task.Delay(DRIVE_STOP_CHECK_MS);
            }

            return false;
        }

        private async Task<bool> DriveBackAsync(double roverSpeed, int driveBackTime, long markerId)
        {
            // Entfernung zu nächstem dahinterliegenden Hindernis.
            var backObstacleDistanceCentimeter = _rangefinderController.BackDistanceCentimeters;

            // Ein nahes dahinterliegendes Hindernis wurde erkannt.
            var backNearObstacle = backObstacleDistanceCentimeter <= ROVER_NEAR_OBSTACLE_CENTIMETERS;

            // Rover fährt eine angegebene Zeit Rückwärts, oder solange bis Hindernisse erkannt werden.
            var driveBackStart = DateTime.Now;
            while (backNearObstacle == false && DateTime.Now <= driveBackStart.AddMilliseconds(driveBackTime))
            {
                // Abbruch.
                if (await CancelRequiredAsync(markerId, true))
                {
                    return true;
                }

                // Rover fährt rückwärts.
                await MoveRoverAsync(roverSpeed, false);

                // Wartezeit bis geprüft wird ob der Rover weiter Rückwärts fahren muss.
                await Task.Delay(DRIVE_BACK_CHECK_MS);

                // Entfernung zu nächstem dahinterliegenden Hindernis.
                backObstacleDistanceCentimeter = _rangefinderController.BackDistanceCentimeters;

                // Ein nahes dahinterliegendes Hindernis wurde erkannt.
                backNearObstacle = backObstacleDistanceCentimeter <= ROVER_NEAR_OBSTACLE_CENTIMETERS;
            }

            // Abbruch.
            if (await CancelRequiredAsync(markerId, true))
            {
                return true;
            }

            // Übergebe Motor Stop Befehl.
            await StopRoverAsync();

            return false;
        }

        /// <summary>
        /// Rover gerade ausrichten.
        /// </summary>
        /// <param name="obstacleAvoidDirection">Information über die Ausrichtung des Rovers.</param>
        /// <param name="markerId">Marker Id.</param>
        /// <returns></returns>
        private async Task<bool> TurnRoverStraightAwayAsync(ObstacleAvoidDirection obstacleAvoidDirection, long markerId)
        {
            // Wenn der Rover nicht gerade ausgerichtet ist.
            if (obstacleAvoidDirection.IsStraightAway == false)
            {
                // Der Rover muss gerade ausgerichtet.
                var avoidObstacleBearingDegrees = obstacleAvoidDirection.LeftOrRight ? ROVER_NEAR_OBSTACLE_TURN_DEGREES : -ROVER_NEAR_OBSTACLE_TURN_DEGREES;

                // Winkel in dem der Rover sich gerade ausrichtet.
                var avoidObstacleBearing = GeographyCalculator.CalculateBearing(CompassController.Heading, avoidObstacleBearingDegrees);

                // Richte Rover gerade aus.
                if (await TurnRoverToBearingAsync(avoidObstacleBearing, markerId, true))
                {
                    // Abbruch.
                    return true;
                }
            }

            return false;
        }
    }
}

Credits

Sascha
5 projects • 9 followers
.NET Developer
Contact

Comments

Please log in or sign up to comment.