Initial version. Can fly simple mission defined in JSON file.

Please check README.md how to install dependencies and run the application.
This commit is contained in:
Tuomas Järvinen
2024-03-18 22:31:38 +01:00
commit f7acface7f
20 changed files with 1354 additions and 0 deletions
+9
View File
@@ -0,0 +1,9 @@
[submodule "3rd/px4"]
path = 3rd/px4
url = https://github.com/PX4/PX4-Autopilot.git
[submodule "3rd/mavsdk"]
path = 3rd/mavsdk
url = https://github.com/mavlink/MAVSDK.git
[submodule "3rd/rapidjson"]
path = 3rd/rapidjson
url = https://github.com/Tencent/rapidjson.git
Submodule
+1
Submodule 3rd/mavsdk added at e06de06d97
Submodule
+1
Submodule 3rd/px4 added at 32aa3263a6
Submodule
+1
Submodule 3rd/rapidjson added at 5ec44fb920
+47
View File
@@ -0,0 +1,47 @@
# Azaion Autopilot
A preliminary example of autonomous drone flight. This example is based on the MAVSDK framework and PX4 flight controller software. Running the example requires compiling MAVSDK and PX4 on a Linux image system using cmake. Example has been tested in Ubuntu 20.04 environment.
TODO!! Add support for Docker builds. PX4 installs many dependencies.
## Install necessary dependencies
sudo apt update
sudo apt install ccache git build-essential curl cmake
## Speed up the compilations
echo "export MAKEFLAGS=\"-j$(($(nproc)))\"" >> ~/.bashrc
echo "export PATH=/usr/lib/ccache:\$PATH" >> ~/.bashrc
## Clone source codes. You must add your SSH key before the cloning!
git clone --recursive git@github.com:azaion/autopilot.git
git submodule update --init --recursive
## Build and install PX4
cd 3rd/px4 && bash ./Tools/setup/ubuntu.sh && make px4_sitl jmavsim
## Install MAVSDK for Ubuntu 20.04
wget https://github.com/mavlink/MAVSDK/releases/download/v2.4.1/libmavsdk-dev_2.4.1_ubuntu20.04_amd64.deb
sudo dpkg -i ibmavsdk-dev_2.4.1_ubuntu20.04_amd64.deb
## Install MAVSDK for Ubuntu 22.04
wget https://github.com/mavlink/MAVSDK/releases/download/v2.4.1/libmavsdk-dev_2.4.1_ubuntu22.04_amd64.deb
sudo dpkg -i libmavsdk-dev_2.4.1_ubuntu22.04_amd64.deb
## Build example application
cd src && cmake . && make
## Launch similator
cd 3rd/px4
PX4_HOME_LAT=52.5314818 PX4_HOME_LON=13.3872884 ./build/px4_sitl_default/bin/px4
## Launch example application in the new terminal window
cd src
make && ./azaion mission.json
+105
View File
@@ -0,0 +1,105 @@
Language: Cpp
SeparateDefinitionBlocks: Always
EmptyLineBeforeAccessModifier: LogicalBlock
MaxEmptyLinesToKeep: 2
AccessModifierOffset: -4
AlignAfterOpenBracket: AlwaysBreak
AlignConsecutiveAssignments: true
AlignConsecutiveDeclarations: None
AlignEscapedNewlines: DontAlign
AlignOperands: true
AlignTrailingComments: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortBlocksOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: Inline
AllowShortIfStatementsOnASingleLine: true
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: Yes
BinPackArguments: false
BinPackParameters: false
BraceWrapping:
AfterClass: true
AfterControlStatement: false
AfterEnum: false
AfterFunction: true
AfterNamespace: false
AfterObjCDeclaration: false
AfterStruct: true
AfterUnion: false
BeforeCatch: false
BeforeElse: true
IndentBraces: false
SplitEmptyFunction: false
SplitEmptyRecord: false
SplitEmptyNamespace: false
BreakBeforeBinaryOperators: All
BreakBeforeBraces: Custom
BreakBeforeInheritanceComma: false
BreakBeforeTernaryOperators: true
BreakConstructorInitializersBeforeComma: false
BreakConstructorInitializers: BeforeComma
BreakAfterJavaFieldAnnotations: false
BreakStringLiterals: true
ColumnLimit: 120
CommentPragmas: '^ IWYU pragma:'
CompactNamespaces: false
ConstructorInitializerAllOnOneLineOrOnePerLine: false
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DerivePointerAlignment: false
DisableFormat: false
ExperimentalAutoDetectBinPacking: false
FixNamespaceComments: true
ForEachMacros:
- forever # avoids { wrapped to next line
- foreach
- Q_FOREACH
- BOOST_FOREACH
IncludeCategories:
- Regex: '^<Q.*'
Priority: 200
IncludeIsMainRegex: '(Test)?$'
IndentCaseLabels: false
IndentWidth: 4
IndentWrappedFunctionNames: false
InsertBraces: false
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: false
# Do not add QT_BEGIN_NAMESPACE/QT_END_NAMESPACE as this will indent lines in between.
MacroBlockBegin: ""
MacroBlockEnd: ""
NamespaceIndentation: None
ObjCBlockIndentWidth: 4
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: true
PenaltyBreakAssignment: 88
PenaltyBreakBeforeFirstCallParameter: 300
PenaltyBreakComment: 500
PenaltyBreakFirstLessLess: 400
PenaltyBreakString: 600
PenaltyExcessCharacter: 50
PenaltyReturnTypeOnItsOwnLine: 300
PointerAlignment: Right
ReflowComments: false
SortIncludes: CaseSensitive
SortUsingDeclarations: true
SpaceAfterCStyleCast: true
SpaceAfterTemplateKeyword: false
SpaceBeforeAssignmentOperators: true
SpaceBeforeParens: ControlStatements
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: Never
SpacesInContainerLiterals: false
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard: c++17
TabWidth: 4
UseTab: Never
+13
View File
@@ -0,0 +1,13 @@
.cmake/
.qtc/
.qtc_clangd/
CMakeCache.txt
CMakeCache.txt.prev
CMakeFiles/
CMakeLists.txt.user
Makefile
Testing/
azaion
cmake_install.cmake
qtcsettings.cmake
+28
View File
@@ -0,0 +1,28 @@
cmake_minimum_required(VERSION 3.10.2)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
project(azaion)
add_executable(azaion
main.cpp
az_mission.h az_mission.cpp
az_action_point.h az_action_point.cpp
az_coordinate.h az_coordinate.cpp
az_fly.h az_fly.cpp
)
find_package(MAVSDK REQUIRED)
target_link_libraries(azaion
MAVSDK::mavsdk
pthread
)
if(NOT MSVC)
add_compile_options(azaion PRIVATE -Wall -Wextra)
else()
add_compile_options(azaion PRIVATE -WX -W2)
endif()
+298
View File
@@ -0,0 +1,298 @@
# CMAKE generated file: DO NOT EDIT!
# Generated by "Unix Makefiles" Generator, CMake Version 3.16
# Default target executed when no arguments are given to make.
default_target: all
.PHONY : default_target
# Allow only one "make -f Makefile2" at a time, but pass parallelism.
.NOTPARALLEL:
#=============================================================================
# Special targets provided by cmake.
# Disable implicit rules so canonical targets will work.
.SUFFIXES:
# Remove some rules from gmake that .SUFFIXES does not remove.
SUFFIXES =
.SUFFIXES: .hpux_make_needs_suffix_list
# Suppress display of executed commands.
$(VERBOSE).SILENT:
# A target that is always out of date.
cmake_force:
.PHONY : cmake_force
#=============================================================================
# Set environment variables for the build.
# The shell in which to execute make rules.
SHELL = /bin/sh
# The CMake executable.
CMAKE_COMMAND = /usr/bin/cmake
# The command to remove a file.
RM = /usr/bin/cmake -E remove -f
# Escaping for special characters.
EQUALS = =
# The top-level source directory on which CMake was run.
CMAKE_SOURCE_DIR = /home/pama/azaion/autopilot/src
# The top-level build directory on which CMake was run.
CMAKE_BINARY_DIR = /home/pama/azaion/autopilot/src
#=============================================================================
# Targets provided globally by CMake.
# Special rule for the target rebuild_cache
rebuild_cache:
@$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Running CMake to regenerate build system..."
/usr/bin/cmake -S$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR)
.PHONY : rebuild_cache
# Special rule for the target rebuild_cache
rebuild_cache/fast: rebuild_cache
.PHONY : rebuild_cache/fast
# Special rule for the target edit_cache
edit_cache:
@$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "No interactive CMake dialog available..."
/usr/bin/cmake -E echo No\ interactive\ CMake\ dialog\ available.
.PHONY : edit_cache
# Special rule for the target edit_cache
edit_cache/fast: edit_cache
.PHONY : edit_cache/fast
# The main all target
all: cmake_check_build_system
$(CMAKE_COMMAND) -E cmake_progress_start /home/pama/azaion/autopilot/src/CMakeFiles /home/pama/azaion/autopilot/src/CMakeFiles/progress.marks
$(MAKE) -f CMakeFiles/Makefile2 all
$(CMAKE_COMMAND) -E cmake_progress_start /home/pama/azaion/autopilot/src/CMakeFiles 0
.PHONY : all
# The main clean target
clean:
$(MAKE) -f CMakeFiles/Makefile2 clean
.PHONY : clean
# The main clean target
clean/fast: clean
.PHONY : clean/fast
# Prepare targets for installation.
preinstall: all
$(MAKE) -f CMakeFiles/Makefile2 preinstall
.PHONY : preinstall
# Prepare targets for installation.
preinstall/fast:
$(MAKE) -f CMakeFiles/Makefile2 preinstall
.PHONY : preinstall/fast
# clear depends
depend:
$(CMAKE_COMMAND) -S$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) --check-build-system CMakeFiles/Makefile.cmake 1
.PHONY : depend
#=============================================================================
# Target rules for targets named azaion
# Build rule for target.
azaion: cmake_check_build_system
$(MAKE) -f CMakeFiles/Makefile2 azaion
.PHONY : azaion
# fast build rule for target.
azaion/fast:
$(MAKE) -f CMakeFiles/azaion.dir/build.make CMakeFiles/azaion.dir/build
.PHONY : azaion/fast
az_action_point.o: az_action_point.cpp.o
.PHONY : az_action_point.o
# target to build an object file
az_action_point.cpp.o:
$(MAKE) -f CMakeFiles/azaion.dir/build.make CMakeFiles/azaion.dir/az_action_point.cpp.o
.PHONY : az_action_point.cpp.o
az_action_point.i: az_action_point.cpp.i
.PHONY : az_action_point.i
# target to preprocess a source file
az_action_point.cpp.i:
$(MAKE) -f CMakeFiles/azaion.dir/build.make CMakeFiles/azaion.dir/az_action_point.cpp.i
.PHONY : az_action_point.cpp.i
az_action_point.s: az_action_point.cpp.s
.PHONY : az_action_point.s
# target to generate assembly for a file
az_action_point.cpp.s:
$(MAKE) -f CMakeFiles/azaion.dir/build.make CMakeFiles/azaion.dir/az_action_point.cpp.s
.PHONY : az_action_point.cpp.s
az_coordinate.o: az_coordinate.cpp.o
.PHONY : az_coordinate.o
# target to build an object file
az_coordinate.cpp.o:
$(MAKE) -f CMakeFiles/azaion.dir/build.make CMakeFiles/azaion.dir/az_coordinate.cpp.o
.PHONY : az_coordinate.cpp.o
az_coordinate.i: az_coordinate.cpp.i
.PHONY : az_coordinate.i
# target to preprocess a source file
az_coordinate.cpp.i:
$(MAKE) -f CMakeFiles/azaion.dir/build.make CMakeFiles/azaion.dir/az_coordinate.cpp.i
.PHONY : az_coordinate.cpp.i
az_coordinate.s: az_coordinate.cpp.s
.PHONY : az_coordinate.s
# target to generate assembly for a file
az_coordinate.cpp.s:
$(MAKE) -f CMakeFiles/azaion.dir/build.make CMakeFiles/azaion.dir/az_coordinate.cpp.s
.PHONY : az_coordinate.cpp.s
az_fly.o: az_fly.cpp.o
.PHONY : az_fly.o
# target to build an object file
az_fly.cpp.o:
$(MAKE) -f CMakeFiles/azaion.dir/build.make CMakeFiles/azaion.dir/az_fly.cpp.o
.PHONY : az_fly.cpp.o
az_fly.i: az_fly.cpp.i
.PHONY : az_fly.i
# target to preprocess a source file
az_fly.cpp.i:
$(MAKE) -f CMakeFiles/azaion.dir/build.make CMakeFiles/azaion.dir/az_fly.cpp.i
.PHONY : az_fly.cpp.i
az_fly.s: az_fly.cpp.s
.PHONY : az_fly.s
# target to generate assembly for a file
az_fly.cpp.s:
$(MAKE) -f CMakeFiles/azaion.dir/build.make CMakeFiles/azaion.dir/az_fly.cpp.s
.PHONY : az_fly.cpp.s
az_mission.o: az_mission.cpp.o
.PHONY : az_mission.o
# target to build an object file
az_mission.cpp.o:
$(MAKE) -f CMakeFiles/azaion.dir/build.make CMakeFiles/azaion.dir/az_mission.cpp.o
.PHONY : az_mission.cpp.o
az_mission.i: az_mission.cpp.i
.PHONY : az_mission.i
# target to preprocess a source file
az_mission.cpp.i:
$(MAKE) -f CMakeFiles/azaion.dir/build.make CMakeFiles/azaion.dir/az_mission.cpp.i
.PHONY : az_mission.cpp.i
az_mission.s: az_mission.cpp.s
.PHONY : az_mission.s
# target to generate assembly for a file
az_mission.cpp.s:
$(MAKE) -f CMakeFiles/azaion.dir/build.make CMakeFiles/azaion.dir/az_mission.cpp.s
.PHONY : az_mission.cpp.s
main.o: main.cpp.o
.PHONY : main.o
# target to build an object file
main.cpp.o:
$(MAKE) -f CMakeFiles/azaion.dir/build.make CMakeFiles/azaion.dir/main.cpp.o
.PHONY : main.cpp.o
main.i: main.cpp.i
.PHONY : main.i
# target to preprocess a source file
main.cpp.i:
$(MAKE) -f CMakeFiles/azaion.dir/build.make CMakeFiles/azaion.dir/main.cpp.i
.PHONY : main.cpp.i
main.s: main.cpp.s
.PHONY : main.s
# target to generate assembly for a file
main.cpp.s:
$(MAKE) -f CMakeFiles/azaion.dir/build.make CMakeFiles/azaion.dir/main.cpp.s
.PHONY : main.cpp.s
# Help Target
help:
@echo "The following are some of the valid targets for this Makefile:"
@echo "... all (the default if no target is provided)"
@echo "... clean"
@echo "... depend"
@echo "... rebuild_cache"
@echo "... edit_cache"
@echo "... azaion"
@echo "... az_action_point.o"
@echo "... az_action_point.i"
@echo "... az_action_point.s"
@echo "... az_coordinate.o"
@echo "... az_coordinate.i"
@echo "... az_coordinate.s"
@echo "... az_fly.o"
@echo "... az_fly.i"
@echo "... az_fly.s"
@echo "... az_mission.o"
@echo "... az_mission.i"
@echo "... az_mission.s"
@echo "... main.o"
@echo "... main.i"
@echo "... main.s"
.PHONY : help
#=============================================================================
# Special targets to cleanup operation of make.
# Special rule to run CMake to check the build system integrity.
# No rule that depends on this can have commands that come from listfiles
# because they might be regenerated.
cmake_check_build_system:
$(CMAKE_COMMAND) -S$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) --check-build-system CMakeFiles/Makefile.cmake 0
.PHONY : cmake_check_build_system
+68
View File
@@ -0,0 +1,68 @@
#include "az_action_point.h"
AzActionPoint::AzActionPoint(
const AzCoordinate &point, int height, AzActionPointType actionPointType, uint actionSpesific)
: mPoint(point)
, mHeight(height)
, mType(actionPointType)
, mActionSpesific(actionSpesific)
{}
AzCoordinate AzActionPoint::getPoint(void) const
{
return mPoint;
}
int AzActionPoint::getHeight(void) const
{
return mHeight;
}
AzActionPointType AzActionPoint::getType(void) const
{
return mType;
}
string AzActionPoint::getTypeStr(void) const
{
static const string typeStrs[] = {"None", "Waypoint", "Search", "Return"};
return typeStrs[mType];
}
string AzActionPoint::getActionSpecificStr(void) const
{
if (isTank() == true && isArtillery() == true) {
return "tank or artillery";
}
else if (isTank() == true) {
return "tank";
}
else if (isArtillery() == true) {
return "artillery";
}
else {
return "none";
}
}
bool AzActionPoint::isTank(void) const
{
return mActionSpesific & AZ_ACTION_SPECIFIC_TANK;
}
bool AzActionPoint::isArtillery(void) const
{
return mActionSpesific & AZ_ACTION_SPECIFIC_ARTILLERY;
}
ostream &operator<<(ostream &os, const AzActionPoint &obj)
{
os << "Point: " << obj.mPoint << endl;
os << "Height: " << obj.mHeight << endl;
os << "Type: " << obj.getTypeStr() << endl;
// TODO!! This is braindead.
os << "Action specific type: " + obj.getActionSpecificStr() << endl;
return os;
}
+44
View File
@@ -0,0 +1,44 @@
#pragma once
#include "az_coordinate.h"
enum AzActionPointType {
AZ_ACTION_POINT_TYPE_NONE = 0,
AZ_ACTION_POINT_TYPE_WAYPOINT = 1,
AZ_ACTION_POINT_TYPE_SEARCH = 2,
AZ_ACTION_POINT_TYPE_RETURN = 3
};
enum AzActionSpecific {
AZ_ACTION_SPECIFIC_NONE = 1 << 0,
AZ_ACTION_SPECIFIC_TANK = 1 << 1,
AZ_ACTION_SPECIFIC_ARTILLERY = 1 << 2,
};
class AzActionPoint
{
public:
AzActionPoint(const AzCoordinate &point, int height, AzActionPointType actionPointType, uint actionSpesific);
AzCoordinate getPoint(void) const;
int getHeight(void) const;
AzActionPointType getType(void) const;
string getTypeStr(void) const;
bool isTank(void) const;
bool isArtillery(void) const;
string getActionSpecificStr(void) const;
friend ostream &operator<<(ostream &os, const AzActionPoint &obj);
private:
AzCoordinate mPoint;
int mHeight;
AzActionPointType mType;
uint mActionSpesific;
};
+12
View File
@@ -0,0 +1,12 @@
#include "az_coordinate.h"
AzCoordinate::AzCoordinate(double lat, double lon)
: latitude(lat)
, longitude(lon)
{}
ostream &operator<<(ostream &os, const AzCoordinate &obj)
{
os << "(" << obj.latitude << ", " << obj.longitude << ")";
return os;
}
+19
View File
@@ -0,0 +1,19 @@
#pragma once
#include <ostream>
using namespace std;
const double INVALID_LATITUDE = -100000;
const double INVALID_LONGITUDE = -100000;
class AzCoordinate
{
public:
double latitude;
double longitude;
AzCoordinate(double lat, double lon);
friend ostream &operator<<(ostream &os, const AzCoordinate &obj);
};
+213
View File
@@ -0,0 +1,213 @@
#include <mavsdk/mavsdk.h>
#include <mavsdk/plugins/action/action.h>
#include <mavsdk/plugins/mission/mission.h>
#include <mavsdk/plugins/offboard/offboard.h>
#include <mavsdk/plugins/telemetry/telemetry.h>
#include <chrono>
#include <future>
#include <iostream>
#include <thread>
#include <vector>
#include "az_action_point.h"
#include "az_fly.h"
#include "az_mission.h"
using namespace std;
using chrono::seconds;
using this_thread::sleep_for;
AzFly::AzFly(AzMission &mission)
: mAzMission(mission)
, mCurrentMissionIndex(0)
{}
AzFly::~AzFly()
{
delete mTelemetry;
delete mMission;
delete mAction;
delete mOffboard;
}
vector<Mission::MissionItem> AzFly::getMissionItems(void)
{
vector<Mission::MissionItem> missionItems;
const vector<AzActionPoint> &actionPoints = mAzMission.getActionPoints();
for (int i = 0; i < actionPoints.size(); i++) {
Mission::MissionItem new_item{};
new_item.latitude_deg = actionPoints[i].getPoint().latitude;
new_item.longitude_deg = actionPoints[i].getPoint().longitude;
new_item.relative_altitude_m = 10;
new_item.speed_m_s = 15;
new_item.is_fly_through = actionPoints[i].getType() == AZ_ACTION_POINT_TYPE_WAYPOINT ? true : false;
new_item.gimbal_pitch_deg = -45;
new_item.gimbal_yaw_deg = -30;
new_item.camera_action = Mission::MissionItem::CameraAction::TakePhoto;
missionItems.push_back(new_item);
}
return missionItems;
}
bool AzFly::initialize(void)
{
ConnectionResult connection_result = mMavsdk.add_any_connection("udp://:14550");
if (connection_result != ConnectionResult::Success) {
cerr << "Connection failed: " << connection_result << '\n';
return false;
}
mSystem = mMavsdk.first_autopilot(3.0);
if (!mSystem) {
cerr << "Timed out waiting for system\n";
return false;
}
else {
cout << "Connection OK\n";
}
mAction = new Action{mSystem.value()};
mMission = new Mission{mSystem.value()};
mTelemetry = new Telemetry{mSystem.value()};
mOffboard = new Offboard{mSystem.value()};
while (!mTelemetry->health_all_ok()) {
cout << "Waiting for system to be ready\n";
sleep_for(seconds(1));
}
cout << "System ready. Creating and uploading mission." << endl;
return true;
}
void AzFly::findAndDetroyThreadFunc(void)
{
auto prom = std::make_shared<std::promise<Mission::Result>>();
auto future_result = prom->get_future();
mMission->pause_mission_async([prom](Mission::Result result) { prom->set_value(result); });
const Mission::Result result = future_result.get();
if (result != Mission::Result::Success) {
cout << "Failed to pause mission" << endl;
return;
}
else {
cout << "Starting Offboard mode. Flying small circle and try to find the target." << endl;
const Offboard::VelocityNedYaw stay{};
mOffboard->set_velocity_ned(stay);
Offboard::Result offboard_result = mOffboard->start();
if (offboard_result != Offboard::Result::Success) {
std::cerr << "Offboard::start() failed: " << offboard_result << '\n';
return;
}
const float radius = 10.0f; // Radius of the circle in meters
const float velocity = 2.0f; // Velocity of the drone in m/s
const float angular_velocity = velocity / radius; // Angular velocity in rad/s
for (float t = 0; t < 2 * M_PI; t += 0.1) {
Offboard::VelocityBodyYawspeed velocity_sp{velocity * cos(t), velocity * sin(t), 0.0f, angular_velocity};
mOffboard->set_velocity_body(velocity_sp);
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
// Stop Offboard mode after completing the circle.
Offboard::VelocityBodyYawspeed stop_velocity{0.0f, 0.0f, 0.0f, 0.0f};
mOffboard->set_velocity_body(stop_velocity);
std::this_thread::sleep_for(std::chrono::seconds(1));
offboard_result = mOffboard->stop();
if (offboard_result != Offboard::Result::Success) {
std::cerr << "Offboard::stop() failed: " << offboard_result << endl;
return;
}
cout << "Stopping Offboard succeeded. Continuing to the next mission item." << endl;
mMission->start_mission();
}
}
bool AzFly::start(void)
{
vector<Mission::MissionItem> mission_items = getMissionItems();
cout << "Uploading mission...\n";
Mission::MissionPlan mission_plan{};
mission_plan.mission_items = mission_items;
const Mission::Result upload_result = mMission->upload_mission(mission_plan);
if (upload_result != Mission::Result::Success) {
cerr << "Mission upload failed: " << upload_result << ", exiting." << endl;
return 1;
}
cout << "Arming...\n";
const Action::Result arm_result = mAction->arm();
if (arm_result != Action::Result::Success) {
cerr << "Arming failed: " << arm_result << '\n';
return 1;
}
cout << "Armed.\n";
// Before starting the mission, we want to be sure to subscribe to the mission progress.
mMission->subscribe_mission_progress([this](Mission::MissionProgress mission_progress) {
mCurrentMissionIndex = mission_progress.current;
if (mCurrentMissionIndex > 0) {
cout << "\nMission status update: " << mCurrentMissionIndex << "/" << mission_progress.total << "." << endl;
const vector<AzActionPoint> &actionPoints = mAzMission.getActionPoints();
string actionTypeStr = actionPoints[mCurrentMissionIndex - 1].getTypeStr();
string actionSpesificStr = actionPoints[mCurrentMissionIndex - 1].getActionSpecificStr();
AzActionPointType actionType = actionPoints[mCurrentMissionIndex - 1].getType();
cout << "Mission type: " << actionTypeStr << ". ";
if (actionType == AZ_ACTION_POINT_TYPE_WAYPOINT) {
cout << "Flying through waypoint." << endl;
}
else if (actionType == AZ_ACTION_POINT_TYPE_SEARCH) {
cout << "Pausing mission and searching " << actionSpesificStr << " target" << endl;
std::thread search_thread(&AzFly::findAndDetroyThreadFunc, this);
search_thread.detach();
}
else if (actionType == AZ_ACTION_POINT_TYPE_RETURN) {
cout << "Arrived to the return point. Landing next." << endl;
}
}
});
Mission::Result start_mission_result = mMission->start_mission();
if (start_mission_result != Mission::Result::Success) {
cerr << "Starting mission failed: " << start_mission_result << endl;
return 1;
}
while (!mMission->is_mission_finished().second) {
sleep_for(seconds(1));
}
const Action::Result landingResults = mAction->land();
if (landingResults != Action::Result::Success) {
cout << "Failed to land: " << landingResults << endl;
return 1;
}
// We need to wait a bit, otherwise the armed state might not be correct yet.
sleep_for(seconds(2));
// Wait until we're done.
while (mTelemetry->armed()) {
sleep_for(seconds(1));
}
cout << "Disarmed, exiting." << endl;
return true;
}
+33
View File
@@ -0,0 +1,33 @@
#pragma once
#include "az_mission.h"
#include <mavsdk/mavsdk.h>
#include <mavsdk/plugins/action/action.h>
#include <mavsdk/plugins/mission/mission.h>
#include <mavsdk/plugins/offboard/offboard.h>
#include <mavsdk/plugins/telemetry/telemetry.h>
using namespace mavsdk;
using namespace std;
class AzFly
{
public:
AzFly(AzMission &mission);
~AzFly();
bool initialize(void);
bool start(void);
private:
Mavsdk mMavsdk{Mavsdk::Configuration{Mavsdk::ComponentType::GroundStation}};
optional<shared_ptr<System>> mSystem;
Action *mAction;
Mission *mMission;
Telemetry *mTelemetry;
Offboard *mOffboard;
AzMission &mAzMission;
vector<Mission::MissionItem> getMissionItems(void);
void findAndDetroyThreadFunc(void);
int mCurrentMissionIndex;
};
+145
View File
@@ -0,0 +1,145 @@
#include "az_mission.h"
#include <filesystem>
#include <iostream>
#include <string>
#include "../3rd/rapidjson/include/rapidjson/document.h"
#include "../3rd/rapidjson/include/rapidjson/error/en.h"
#include "../3rd/rapidjson/include/rapidjson/filereadstream.h"
using namespace rapidjson;
using namespace std;
AzMission::AzMission(string filename)
: mFilename(filename)
, mReturnPoint(AzCoordinate(INVALID_LATITUDE, INVALID_LONGITUDE))
, mOperationalHeight(INVALID_HEIGHT)
{
if (filesystem::exists(mFilename) == false) {
cerr << "JSON file " << mFilename << " doesn't exists." << endl;
exit(1);
}
parse();
}
void AzMission::parse(void)
{
FILE *fp = fopen(mFilename.c_str(), "r");
char readBuffer[65536];
FileReadStream is(fp, readBuffer, sizeof(readBuffer));
Document document;
document.ParseStream(is);
fclose(fp);
if (document.HasParseError()) {
cerr << "JSON parse error: " << GetParseError_En(document.GetParseError()) << endl;
exit(1);
}
// Parse operational height
int operational_height = document["operational_height"].GetInt();
if (operational_height > 0) {
mOperationalHeight = operational_height;
}
// Parse geofences
const Value &geofences = document["geofences"];
const Value &polygons = geofences["polygons"];
for (SizeType i = 0; i < polygons.Size(); i++) {
AzGeofence geofence;
const Value &polygon = polygons[i];
const Value &points = polygon["points"];
for (SizeType j = 0; j < points.Size(); j++) {
AzCoordinate coord(points[j][0].GetDouble(), points[j][1].GetDouble());
geofence.coordinates.push_back(coord);
}
string fence_type = polygon["fence_type"].GetString();
if (fence_type == "INCLUSION") {
geofence.type = AZ_GEOFENCE_TYPE_INCLUSION;
}
else if (fence_type == "INCLUSION") {
geofence.type = AZ_GEOFENCE_TYPE_EXCLUSION;
}
mGeofences.push_back(geofence);
}
// Parse action points
const Value &action_points = document["action_points"];
for (SizeType i = 0; i < action_points.Size(); i++) {
double lat = action_points[i]["point"][0].GetDouble();
double lon = action_points[i]["point"][1].GetDouble();
AzCoordinate point(lat, lon);
int height = action_points[i]["height"].GetInt();
string action_enum = action_points[i]["action_enum"].GetString();
AzActionPointType action_point_type = AZ_ACTION_POINT_TYPE_NONE;
if (action_enum == "waypoint") {
action_point_type = AZ_ACTION_POINT_TYPE_WAYPOINT;
}
else if (action_enum == "search") {
action_point_type = AZ_ACTION_POINT_TYPE_SEARCH;
}
else if (action_enum == "return") {
action_point_type = AZ_ACTION_POINT_TYPE_RETURN;
}
uint actionSpefific = 0;
if (action_points[i].HasMember("action_specific")) {
const Value &targets = action_points[i]["action_specific"]["targets"];
for (SizeType j = 0; j < targets.Size(); j++) {
if (targets[j].GetString() == string("tank")) {
actionSpefific |= AZ_ACTION_SPECIFIC_TANK;
}
else if (targets[j].GetString() == string("artillery")) {
actionSpefific |= AZ_ACTION_SPECIFIC_ARTILLERY;
}
}
}
AzActionPoint actionPoint(point, height, action_point_type, actionSpefific);
mActionPoints.push_back(actionPoint);
}
// Parse return_point
mReturnPoint = AzCoordinate(document["return_point"][0].GetDouble(), document["return_point"][1].GetDouble());
}
const vector<AzGeofence> &AzMission::getGeofences(void) const
{
return mGeofences;
}
const vector<AzActionPoint> &AzMission::getActionPoints(void) const
{
return mActionPoints;
}
int AzMission::getOperationalHeight(void) const
{
return mOperationalHeight;
}
AzCoordinate AzMission::getReturnPoint(void) const
{
return mReturnPoint;
}
ostream &operator<<(ostream &os, const AzMission &obj)
{
os << "\nMission defination:" << endl;
os << "----------------------" << endl;
os << "Filename: " << obj.mFilename << endl;
os << "Operational height: " << obj.mOperationalHeight << endl;
os << "Return point: " << obj.mReturnPoint << endl;
for (int i = 0; i < obj.mActionPoints.size(); i++) {
os << "\nAction point[" << i << "]:" << endl;
os << obj.mActionPoints[i];
}
return os;
}
+46
View File
@@ -0,0 +1,46 @@
#pragma once
#include <iostream>
#include <string>
#include <vector>
#include "az_action_point.h"
#include "az_coordinate.h"
using namespace std;
const double INVALID_HEIGHT = -100000;
enum AzGeofenceType { AZ_GEOFENCE_TYPE_NONE = 0, AZ_GEOFENCE_TYPE_INCLUSION = 1, AZ_GEOFENCE_TYPE_EXCLUSION = 2 };
class AzGeofence
{
public:
vector<AzCoordinate> coordinates;
AzGeofenceType type;
};
class AzMission
{
public:
AzMission(string filename);
const vector<AzGeofence> &getGeofences(void) const;
const vector<AzActionPoint> &getActionPoints(void) const;
int getOperationalHeight(void) const;
AzCoordinate getReturnPoint(void) const;
friend std::ostream &operator<<(std::ostream &os, const AzMission &obj);
private:
void parse(void);
string mFilename;
vector<AzGeofence> mGeofences;
vector<AzActionPoint> mActionPoints;
int mOperationalHeight;
AzCoordinate mReturnPoint;
};
+230
View File
@@ -0,0 +1,230 @@
//
// Demonstrates how to add and fly Waypoint missions using the MAVSDK.
//
#include <mavsdk/mavsdk.h>
#include <mavsdk/plugins/action/action.h>
#include <mavsdk/plugins/mission/mission.h>
#include <mavsdk/plugins/telemetry/telemetry.h>
#include <chrono>
#include <functional>
#include <future>
#include <iostream>
#include <thread>
using namespace mavsdk;
using std::chrono::seconds;
using std::this_thread::sleep_for;
Mission::MissionItem make_mission_item(
double latitude_deg,
double longitude_deg,
float relative_altitude_m,
float speed_m_s,
bool is_fly_through,
float gimbal_pitch_deg,
float gimbal_yaw_deg,
Mission::MissionItem::CameraAction camera_action)
{
Mission::MissionItem new_item{};
new_item.latitude_deg = latitude_deg;
new_item.longitude_deg = longitude_deg;
new_item.relative_altitude_m = relative_altitude_m;
new_item.speed_m_s = speed_m_s;
new_item.is_fly_through = is_fly_through;
new_item.gimbal_pitch_deg = gimbal_pitch_deg;
new_item.gimbal_yaw_deg = gimbal_yaw_deg;
new_item.camera_action = camera_action;
return new_item;
}
void usage(const std::string &bin_name)
{
std::cerr << "Usage : " << bin_name << " <connection_url>\n"
<< "Connection URL format should be :\n"
<< " For TCP : tcp://[server_host][:server_port]\n"
<< " For UDP : udp://[bind_host][:bind_port]\n"
<< " For Serial : serial:///path/to/serial/dev[:baudrate]\n"
<< "For example, to connect to the simulator use URL: udp://:14540\n";
}
int main(int argc, char **argv)
{
if (argc != 2) {
usage(argv[0]);
return 1;
}
Mavsdk mavsdk{Mavsdk::Configuration{Mavsdk::ComponentType::GroundStation}};
ConnectionResult connection_result = mavsdk.add_any_connection(argv[1]);
if (connection_result != ConnectionResult::Success) {
std::cerr << "Connection failed: " << connection_result << '\n';
return 1;
}
auto system = mavsdk.first_autopilot(3.0);
if (!system) {
std::cerr << "Timed out waiting for system\n";
return 1;
}
else {
std::cout << "Connection OK\n";
}
auto action = Action{system.value()};
auto mission = Mission{system.value()};
auto telemetry = Telemetry{system.value()};
while (!telemetry.health_all_ok()) {
std::cout << "Waiting for system to be ready\n";
sleep_for(seconds(1));
}
std::cout << "System ready\n";
std::cout << "Creating and uploading mission\n";
std::vector<Mission::MissionItem> mission_items;
mission_items.push_back(make_mission_item(
47.398170327054473,
8.5456490218639658,
10.0f,
5.0f,
false,
20.0f,
60.0f,
Mission::MissionItem::CameraAction::None));
mission_items.push_back(make_mission_item(
47.398241338125118,
8.5455360114574432,
10.0f,
2.0f,
true,
0.0f,
-60.0f,
Mission::MissionItem::CameraAction::TakePhoto));
mission_items.push_back(make_mission_item(
47.398139363821485,
8.5453846156597137,
10.0f,
5.0f,
true,
-45.0f,
0.0f,
Mission::MissionItem::CameraAction::StartVideo));
mission_items.push_back(make_mission_item(
47.398058617228855,
8.5454618036746979,
10.0f,
2.0f,
false,
-90.0f,
30.0f,
Mission::MissionItem::CameraAction::StopVideo));
mission_items.push_back(make_mission_item(
47.398100366082858,
8.5456969141960144,
10.0f,
5.0f,
false,
-45.0f,
-30.0f,
Mission::MissionItem::CameraAction::StartPhotoInterval));
mission_items.push_back(make_mission_item(
47.398001890458097,
8.5455576181411743,
10.0f,
5.0f,
false,
0.0f,
0.0f,
Mission::MissionItem::CameraAction::StopPhotoInterval));
std::cout << "Uploading mission...\n";
Mission::MissionPlan mission_plan{};
mission_plan.mission_items = mission_items;
const Mission::Result upload_result = mission.upload_mission(mission_plan);
if (upload_result != Mission::Result::Success) {
std::cerr << "Mission upload failed: " << upload_result << ", exiting.\n";
return 1;
}
std::cout << "Arming...\n";
const Action::Result arm_result = action.arm();
if (arm_result != Action::Result::Success) {
std::cerr << "Arming failed: " << arm_result << '\n';
return 1;
}
std::cout << "Armed.\n";
std::atomic<bool> want_to_pause{false};
// Before starting the mission, we want to be sure to subscribe to the
// mission progress.
mission.subscribe_mission_progress([&want_to_pause](Mission::MissionProgress mission_progress) {
std::cout << "Mission status update: " << mission_progress.current << " / " << mission_progress.total << '\n';
if (mission_progress.current >= 2) {
// We can only set a flag here. If we do more request inside the
// callback, we risk blocking the system.
want_to_pause = true;
}
});
Mission::Result start_mission_result = mission.start_mission();
if (start_mission_result != Mission::Result::Success) {
std::cerr << "Starting mission failed: " << start_mission_result << '\n';
return 1;
}
while (!want_to_pause) {
sleep_for(seconds(1));
}
std::cout << "Pausing mission...\n";
const Mission::Result pause_mission_result = mission.pause_mission();
if (pause_mission_result != Mission::Result::Success) {
std::cerr << "Failed to pause mission:" << pause_mission_result << '\n';
}
std::cout << "Mission paused.\n";
// Pause for 5 seconds.
sleep_for(seconds(5));
// Then continue.
Mission::Result start_mission_again_result = mission.start_mission();
if (start_mission_again_result != Mission::Result::Success) {
std::cerr << "Starting mission again failed: " << start_mission_again_result << '\n';
return 1;
}
while (!mission.is_mission_finished().second) {
sleep_for(seconds(1));
}
// We are done, and can do RTL to go home.
std::cout << "Commanding RTL...\n";
const Action::Result rtl_result = action.return_to_launch();
if (rtl_result != Action::Result::Success) {
std::cout << "Failed to command RTL: " << rtl_result << '\n';
return 1;
}
std::cout << "Commanded RTL.\n";
// We need to wait a bit, otherwise the armed state might not be correct yet.
sleep_for(seconds(2));
while (telemetry.armed()) {
// Wait until we're done.
sleep_for(seconds(1));
}
std::cout << "Disarmed, exiting.\n";
}
+23
View File
@@ -0,0 +1,23 @@
#include <iostream>
#include "az_fly.h"
#include "az_mission.h"
using namespace std;
int main(int argc, char *argv[])
{
if (argc != 2) {
cout << "Plese give mission file as an argument\n";
return 1;
}
AzMission mission(argv[1]);
cout << mission;
AzFly fly(mission);
fly.initialize();
fly.start();
return 0;
}
+18
View File
@@ -0,0 +1,18 @@
{
"operational_height": 1100,
"geofences": {
"polygons": [
{"points": [[12543.4213, 23476.324], [123.312, 984356.345]], "fence_type": "INCLUSION"},
{"points": [[12543.4213, 23476.324], [123.312, 984356.345]], "fence_type": "EXCLUSION"}
]
},
"action_points": [
{"point": [52.53175307, 13.386865865], "height": 500, "action_enum": "waypoint"},
{"point": [52.53175307, 13.387710935], "height": 400, "action_enum": "search", "action_specific": {"targets": ["artillery"]}},
{"point": [52.53121053, 13.386865865], "height": 300, "action_enum": "search", "action_specific": {"targets": ["artillery", "tank"]}},
{"point": [52.53121053, 13.387710935], "height": 300, "action_enum": "search", "action_specific": {"targets": ["tank"]}},
{"point": [52.53148180, 13.387288400], "height": 500, "action_enum": "return"}
],
"return_point": [99.99, 11.11]
}