The Rover drives autonomously and automatically avoid obstacles. Users have an easy way to enter the Rover's navigation points on an iPad Mini and can see how it's moving with the camera live stream. The Rover can be manually controlled by a Gamepad, too.
The Rover knows its current position from a GNSS receiver (GPS and Glonass satellites). To turn the rover to the target navigation point, it receives its bearing from a digital compass. To avoid obstacles rangefinders in front and back of the Rover are used.
ImpressionsThe Rover uses an Aaeon Up² running Windows IoT Enterprise. It uses a .NET UWP-App (Universal Windows Platform) written in C#. The App host a HTTP- and Websocket-Server. The HTTP-Server serves to provide the HTML-page and styles on the iPad. The Websocker-Server provides the camera live stream and synchronize the map navigation points. The UWP-App processes position, bearing and obstacle data and control motors to enable autonomous navigation and obstacle avoidance. The App also process the Gamepad readings to manually drive the Rover. The map-tiles (square cut-outs of map) was generated with an OpenStreetMap-OSM file and Maperitive. The map-tiles hosted at the IIS (Webserver) on the Rover. In the client (browser) Leaflet is used to display the map. The Rover hosts its own WiFi Hotspot and works without any connection to the internet.
The Rover uses a GNSS-receiver with a u-blox 8 chip. For reasons of space the GNSS-receiver is mounted vertically at the back. This is not the ideally place, but with an active antenna it's no problem. Many USB 3.0 devices interference with satellite signals. In my case I had to replace a USB 3.0 Hub with an USB 2.0 Hub. After that I have good signals with the GNSS-receiver.
The Rover knows his bearing through a digital compass (CMPS 12). The digital compass has a magnetometer. The magnetometer measures the earth magnetic field to get the magnetic north pole. To stabilize bearing the digital compass use an accelerometer and gyroscope. The digital compass needs to see the earth magnetic field only one time after power up and delivers with help to the other sensors stable bearings. The digital compass is mounted at the top right corner. The chassis and the electronic components interferences the readings of the magnetometer in the digital compass. This is the reasons why the Rover needed to be upside down and fully rotated after power up to can "see" the earth magnetic field. As an alternative the digital compass can mounted at the end of a rod.
The GNSS-receiver and the digital compass enables the autonomous driving (see code snippet below). To see and avoid obstacles rangefinders in back and front are used. The TeraRanger Evo 60m delivers the distance to the next obstacle and enable the obstacle avoidance (see code snippet below).
Autonomous driving (Code snippet)
C#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#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;
}
}
}
Comments
Please log in or sign up to comment.