mirror of
https://gitee.com/mirrors_PX4/PX4-Autopilot.git
synced 2026-06-12 23:20:05 +08:00
Compare commits
235 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 50bd148f53 | |||
| ea545f2813 | |||
| 6213b2266b | |||
| f79c3bb5ea | |||
| 25141ce184 | |||
| 1930cc2fbe | |||
| 90e7ce1b96 | |||
| 1cfb441527 | |||
| 59f56f4a5b | |||
| 01e1bac365 | |||
| 0cd24874f3 | |||
| 8c647f11d0 | |||
| 37e3234e49 | |||
| 1f63d85869 | |||
| f86d4b18f8 | |||
| 31ab496f31 | |||
| dd5524da3d | |||
| c0c0666d5c | |||
| 275f462136 | |||
| bf84cf0dcf | |||
| be8adbfdf3 | |||
| 2186f7b1b1 | |||
| 70de169f15 | |||
| 02c4ec9b2a | |||
| 62c2fbb443 | |||
| 2dcd617a8f | |||
| 7178f8416d | |||
| f2cd5e3e9f | |||
| e5b784736f | |||
| 678e2c415d | |||
| 3fcffe1f3b | |||
| 51437a89e1 | |||
| 14cc9e9919 | |||
| 3ffc1fd25b | |||
| 545f8c4452 | |||
| f3bd241dbe | |||
| 430cdada60 | |||
| 08443c0bfc | |||
| ec65ff7c5e | |||
| 627788c93c | |||
| 2bb4644180 | |||
| 45441d62b1 | |||
| 7af3cb9df8 | |||
| f87402b16c | |||
| 49180de27c | |||
| 641129ad4e | |||
| c8590e0fb1 | |||
| c7b5a6f463 | |||
| 757d905089 | |||
| 1cd0ca9c6c | |||
| 96d04af6e8 | |||
| 84d7eb2900 | |||
| 573fbeda04 | |||
| 14e8ee75e7 | |||
| 5af4704aac | |||
| 1f4bad0624 | |||
| d67cbfba3a | |||
| 5db534849a | |||
| 4d08f56fae | |||
| 92540fc6d8 | |||
| f4362c5ae5 | |||
| b8e6fc2730 | |||
| 6bdc18df6d | |||
| 734a6c8a42 | |||
| cd60fb6102 | |||
| e9960b5532 | |||
| 202c29154a | |||
| 66f614435f | |||
| d3a220f807 | |||
| 75e4a856a5 | |||
| 3f67ddbdba | |||
| 63deb40a76 | |||
| f46db40b10 | |||
| ab5a268ca5 | |||
| f9e7c66718 | |||
| 5ce381dfc7 | |||
| fefed35dfe | |||
| 9d61febd39 | |||
| 49bed47924 | |||
| 91cedcaba3 | |||
| 9f2bb6c7f9 | |||
| ad532d0510 | |||
| 386c34a563 | |||
| 33266ef2c8 | |||
| 1468d4ed39 | |||
| 3b71c70583 | |||
| e7fe8f7268 | |||
| 3041438132 | |||
| e2b2f97d0d | |||
| 9a7f99f3cd | |||
| 0bfd2925bf | |||
| d1d367011e | |||
| d26e037df4 | |||
| 3bfa194933 | |||
| 18715ebd80 | |||
| c0efaa4ca9 | |||
| 6fbfde9ec3 | |||
| d22398f733 | |||
| cd0fbb3cd2 | |||
| 03c5e9172d | |||
| bb3746e710 | |||
| c3f630ca14 | |||
| 18d13498de | |||
| ca472ebfaf | |||
| 7277d72db5 | |||
| 5d186f374b | |||
| f7b4f13e81 | |||
| 2ba7b41f5c | |||
| 5072c0b5ae | |||
| 32aa8d4f51 | |||
| 40702b36ee | |||
| 1aebc69fed | |||
| 715b571dac | |||
| dc6e47f777 | |||
| a649bbebb7 | |||
| cf55901ac9 | |||
| 2167457e2e | |||
| 90b4afebb5 | |||
| ddf0ecfc38 | |||
| 074636a8ae | |||
| c06251f3be | |||
| 1f21256f6a | |||
| 0013f641aa | |||
| ac113d71af | |||
| 7d44567fab | |||
| 2c148236ae | |||
| 0ef245aee1 | |||
| 168e070f94 | |||
| 51111fc6e3 | |||
| d7aaab07fc | |||
| 6ad9e59a7a | |||
| b8b9f15a34 | |||
| d61e0651ab | |||
| 4c041f12ea | |||
| f550c8735a | |||
| c65db00914 | |||
| db5e932f48 | |||
| 17e58dc08b | |||
| c6760cc6fb | |||
| d91b2347dd | |||
| badcddc29a | |||
| 98ca693298 | |||
| 85e879a574 | |||
| 1b4a224223 | |||
| cc2cf40e6e | |||
| 8b591aa13a | |||
| 0f8f319411 | |||
| 63d24a9e1e | |||
| 417351390f | |||
| be930d4372 | |||
| dacc45c3d1 | |||
| ca6f6b27a5 | |||
| ff6928fb63 | |||
| 65f9005bc6 | |||
| 32a450f5dd | |||
| 344cf83549 | |||
| 4980b93830 | |||
| 9c378a7ca1 | |||
| 3ead5c2afd | |||
| 301be5ed8a | |||
| 859b19db9a | |||
| 34ea229a78 | |||
| ab30532f52 | |||
| 3cc356a703 | |||
| ab2f85d4ff | |||
| 5d4086309f | |||
| e3f5f8e475 | |||
| cbc8b50aa1 | |||
| 4445ffc70e | |||
| 6623fd0212 | |||
| d57ed6d17f | |||
| 6a701adf3c | |||
| 2eb3392c39 | |||
| bb516be61e | |||
| 0ae1737e85 | |||
| 644db1b03f | |||
| c31e31bf5e | |||
| f69a6af989 | |||
| fa8222e188 | |||
| 370da89573 | |||
| 1cab556ddb | |||
| 23d15c1365 | |||
| 72823e6eb4 | |||
| fc7c8b4b89 | |||
| 17e17d79dd | |||
| d0fba8bf8b | |||
| c0be801b5c | |||
| 5a6cde41d5 | |||
| ca804a2308 | |||
| 294fbc46a9 | |||
| 55be098e3b | |||
| 5d6edcc15d | |||
| 1ea5de43cf | |||
| 043ad3c33e | |||
| 176738c688 | |||
| ec57832a8f | |||
| 7dab5d4380 | |||
| b7189012dc | |||
| 2f50a07afb | |||
| 925c65b4d5 | |||
| 58d1cdc733 | |||
| a8c26265b5 | |||
| 964cb486a9 | |||
| 375ae991bc | |||
| cf7ad67678 | |||
| 4e175d13c4 | |||
| 74868f8c2b | |||
| 6172315cf7 | |||
| a4be7ae7d0 | |||
| 7a9f7eb424 | |||
| 4bd7d62b5c | |||
| aea8985c8d | |||
| 3da8031e8e | |||
| eeff52cda7 | |||
| ec50193d6c | |||
| 5195210893 | |||
| 57f92250b3 | |||
| 9bff0c8c04 | |||
| e2d6c0a8f9 | |||
| d5bb948cbb | |||
| a4b127960b | |||
| e8624f8afe | |||
| 28d1ec8afe | |||
| 642aeccd1e | |||
| 75b93b0728 | |||
| a15ca72288 | |||
| 4beeb7f560 | |||
| baff0832bc | |||
| c398c95fd5 | |||
| b1315a71ec | |||
| 670111875e | |||
| e4180f6a72 | |||
| 868ff42f47 | |||
| cebe7add8b | |||
| d7aa5df3cd |
@@ -0,0 +1,33 @@
|
||||
# How to install:
|
||||
# gem install github_changelog_generator
|
||||
# How to run:
|
||||
# github_changelog_generator -u PX4 -p Firmware
|
||||
# Description:
|
||||
# The following params are sensible defaults for the PX4 project,
|
||||
# if you want to do a changelog before a release you need to update since-tag and future-releases,
|
||||
|
||||
# Params:
|
||||
# github_changelog_generator --help for all options
|
||||
|
||||
# max-issues
|
||||
# max threshold for github api queries
|
||||
# make sure you set your CHANGELOG_GITHUB_TOKEN before
|
||||
# running
|
||||
max-issues=1500
|
||||
|
||||
# exclude-tags-regex
|
||||
# excludes release candidates
|
||||
exclude-tags-regex=rc[0-9]{1,}|beta[0-9]{1,}
|
||||
|
||||
# since-tag
|
||||
# version of last stable release
|
||||
# you need to change this depending on what you need
|
||||
# if you want a changelog between versions this is the lowest version
|
||||
since-tag=1.6.5
|
||||
|
||||
# future-release
|
||||
# version you are about to release
|
||||
# if you want a changelog between a version and all unreleased changes grouped as a release
|
||||
# eg: v1.6.5 to v1.7.0
|
||||
future-release=v1.7.0
|
||||
|
||||
+29
-5
@@ -110,7 +110,7 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PX4_BINARY_DIR})
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG ${PX4_BINARY_DIR})
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE ${PX4_BINARY_DIR})
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${PX4_SOURCE_DIR}/cmake")
|
||||
list(APPEND CMAKE_MODULE_PATH ${PX4_SOURCE_DIR}/cmake)
|
||||
|
||||
#=============================================================================
|
||||
# git
|
||||
@@ -155,6 +155,9 @@ if (NOT EXTERNAL_MODULES_LOCATION STREQUAL "")
|
||||
get_filename_component(EXTERNAL_MODULES_LOCATION "${EXTERNAL_MODULES_LOCATION}" ABSOLUTE)
|
||||
endif()
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH ${PX4_SOURCE_DIR}/platforms/${OS}/cmake)
|
||||
|
||||
include(platforms/${OS}/cmake/px4_impl_os.cmake)
|
||||
set(config_module "configs/${CONFIG}")
|
||||
include(${config_module})
|
||||
|
||||
@@ -243,11 +246,11 @@ set(BUILD_SHARED_LIBS OFF)
|
||||
#=============================================================================
|
||||
# ccache
|
||||
#
|
||||
option(CCACHE "Use ccache if available" OFF)
|
||||
option(CCACHE "Use ccache if available" ON)
|
||||
find_program(CCACHE_PROGRAM ccache)
|
||||
if (CCACHE AND CCACHE_PROGRAM)
|
||||
message(STATUS "Enabled ccache: ${CCACHE_PROGRAM}")
|
||||
if (CCACHE AND CCACHE_PROGRAM AND NOT DEFINED ENV{CCACHE_DISABLE})
|
||||
set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE "${CCACHE_PROGRAM}")
|
||||
else()
|
||||
endif()
|
||||
|
||||
#=============================================================================
|
||||
@@ -398,7 +401,7 @@ foreach(module ${config_module_list})
|
||||
add_subdirectory(src/${module})
|
||||
endforeach()
|
||||
|
||||
add_subdirectory(src/firmware/${OS})
|
||||
add_subdirectory(platforms/${OS})
|
||||
|
||||
#=============================================================================
|
||||
# generate custom target to print for all executable and module cmake targets
|
||||
@@ -413,6 +416,27 @@ if (all_posix_cmake_targets)
|
||||
)
|
||||
endif()
|
||||
|
||||
|
||||
#=============================================================================
|
||||
# uORB graph generation: add a custom target 'uorb_graph'
|
||||
#
|
||||
set(uorb_graph_config ${BOARD})
|
||||
|
||||
set(graph_module_list "")
|
||||
foreach(module ${config_module_list})
|
||||
set(graph_module_list "${graph_module_list}" "--src-path" "src/${module}")
|
||||
endforeach()
|
||||
|
||||
add_custom_command(OUTPUT ${uorb_graph_config}
|
||||
COMMAND ${PYTHON_EXECUTABLE} ${PX4_SOURCE_DIR}/Tools/uorb_graph/create.py
|
||||
${module_list}
|
||||
--exclude-path src/examples
|
||||
--file ${PX4_SOURCE_DIR}/Tools/uorb_graph/graph_${uorb_graph_config}
|
||||
WORKING_DIRECTORY ${PX4_SOURCE_DIR}
|
||||
COMMENT "Generating uORB graph"
|
||||
)
|
||||
add_custom_target(uorb_graph DEPENDS ${uorb_graph_config})
|
||||
|
||||
#=============================================================================
|
||||
# packaging
|
||||
#
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 17 KiB |
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 92 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 79 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 74 KiB |
@@ -1,98 +0,0 @@
|
||||
[TOC]
|
||||
|
||||
# Introduction
|
||||
|
||||
The HIL architecture allows you to test the flight stack replacing the real physical vehicle and sensors with a simulator of vehicle dynamics and sensor outputs. The flight stack "is not aware" that it is not on a real vehicle. This is a powerful tool for develping and testing code rapidly in a benchtop environment.
|
||||
|
||||
The flight stack can be run anywhere that supports a network connection to the simulator (with sufficient bandwidth and latency to transport the sensor and actuator messages). This can be on a standard linux workstation, an on-target linux image, or the on-target DSP image. These modes can be selected based on the goals of the testing. Workstation is useful for rapid testing in a tool-rich environment. DSP image testing is the closest to the final implementation, so is useful for testing actual HW operation, other than the physical sensing and actuation.
|
||||
|
||||
## Px4 High-level HIL Architecture
|
||||
|
||||
A diagram of the setup described is shown here. Note that UDP port numbers are only displayed on the socket server and are left blank on the socket client.
|
||||
|
||||
(???NOTES: This diagram needs to be updated to use control inputs over UDP, either from QGC or from other)
|
||||
|
||||

|
||||
|
||||
## Requirements
|
||||
The simulator that is currently supported is jMAVSim. The setup described here requires PX4 and jMAVSim installed and running. qGroundControl (QGC) is also required because it is the supported method of providing manual control commands.
|
||||
|
||||
## Assumptions
|
||||
|
||||
# Compiling Code
|
||||
|
||||
## JMAVSim
|
||||
|
||||
### Platform Requirements
|
||||
Linux with java-1.7.x or greater
|
||||
|
||||
### Build Instructions
|
||||
In a clean directory
|
||||
```
|
||||
> git clone https://github.com/PX4/jMAVSim.git
|
||||
> cd jMAVSim
|
||||
> git submodule init
|
||||
> git submodule update
|
||||
> ant
|
||||
```
|
||||
|
||||
## qGroundControl
|
||||
|
||||
### Platform Requirements
|
||||
Windows 7
|
||||
Logitech Gamepad F310 joystick controller
|
||||
|
||||
### Download/Install Instructions
|
||||
Download QGC from http://qgroundcontrol.org/downloads and install using the windows executable.
|
||||
|
||||
|
||||
## PX4
|
||||
|
||||
### Platform Requirements
|
||||
Linux or Eagle with a working IP interface (?? does this need further instructions?)
|
||||
|
||||
### Build Host Requirements
|
||||
(???Notes: Windows?)
|
||||
|
||||
### Download & Build Instructions
|
||||
|
||||
### Installing binaries on the Qualcomm Target
|
||||
|
||||
# Running PX4 in HIL Mode
|
||||
|
||||
## Starting PX4 on Qualcomm Eagle
|
||||
|
||||
```
|
||||
> adb shell
|
||||
# bash
|
||||
root@linaro-developer:/# cd ???
|
||||
root@linaro-developer:/# ./px4
|
||||
App name: px4
|
||||
Enter a command and its args:
|
||||
uorb start
|
||||
muorb start
|
||||
mavlink start -u 14556
|
||||
simulator start -p
|
||||
```
|
||||
|
||||
## Starting jMAVSim
|
||||
In the directory where jMAVSim is installed
|
||||
```
|
||||
java -cp lib/*:out/production/jmavsim.jar me.drton.jmavsim.Simulator -udp <IPADDR>:14560 -n 100
|
||||
```
|
||||
replacing <IPADDR> with the IP address of the machine running PX4 (Eagle). This can be found by running "ifconfig" on that machine.
|
||||
|
||||
## Starting qGroundControl
|
||||
Launch the qGroundControl application
|
||||
1. Set up the communication to the flight stack. In the menu File:Settings:CommLinks, select Add. Enter a Link Name of your choice. Select Link Type: UDP. Set the listening port to an unused port (example: 14561). Select Add. Enter the IP address and port of the PX4 Mavlink app, which is <IPADDR>:14556 with <IPADDR> being the IP address of the Eagle board. Select OK.
|
||||
1. Set up the joystick. Plug in the joystick to your Windows machine. In the menu File:Settings:CommLinks, check Enable Controllers. Select "Gamepad F310". Select "Manual". Set the axes/channel mapping to 0:Yaw, 1:Throttle, 2:unset, 3:Pitch, 4:Roll. Seletct "Inverted" for the throttle axis. Click "Calibrate range". Move the right joystick through its full range of motion. Move the left joystick full left then full right. Move the left joystick full forward (but not full backward). Click "end calibration."
|
||||
1. Connect to the flight stack. Click Analyze. Click the "Connect" button in the upper right, and select the connection that you created in the first step.
|
||||
|
||||
You should now be connected to the flight stack. You can see incoming Mavlink packets using the MAVLink Instpector (from Advanced:Tool Widgets)
|
||||
|
||||
|
||||
## Controlling PX4 flight in HIL Mode
|
||||
The joystick can now be used to fly the simulated vehicle. The jMAVSim world visualization gives a FPV view, and QGC can be used to display instruments such as artificial horizon and maps (if GPS simulation is enabled).
|
||||
|
||||
|
||||
# Debugging/FAQ
|
||||
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
@@ -1,2 +0,0 @@
|
||||
#!/bin/sh
|
||||
git log --pretty=format:"Last change: commit %h - %aN, %ar : %s" -1 $1 || echo no git
|
||||
Vendored
+369
-37
@@ -1,17 +1,6 @@
|
||||
pipeline {
|
||||
agent none
|
||||
stages {
|
||||
stage('Quality Checks') {
|
||||
agent {
|
||||
docker {
|
||||
image 'px4io/px4-dev-base:2017-10-23'
|
||||
args '-e CI=true'
|
||||
}
|
||||
}
|
||||
steps {
|
||||
sh 'make check_format'
|
||||
}
|
||||
}
|
||||
|
||||
stage('Build') {
|
||||
steps {
|
||||
@@ -55,7 +44,7 @@ pipeline {
|
||||
sh "make clean"
|
||||
sh "ccache -z"
|
||||
sh "git fetch --tags"
|
||||
sh "make nuttx_px4io-v2_default"
|
||||
sh "make px4io-v2_default"
|
||||
sh "make nuttx_px4fmu-v2_default"
|
||||
sh "make nuttx_px4fmu-v2_lpe"
|
||||
sh "make nuttx_px4fmu-v3_default"
|
||||
@@ -118,6 +107,28 @@ pipeline {
|
||||
}
|
||||
|
||||
|
||||
// posix_sitl
|
||||
for (def option in ["sitl_default", "sitl_rtps"]) {
|
||||
def node_name = "${option}"
|
||||
|
||||
builds["${node_name}"] = {
|
||||
node {
|
||||
stage("Build Test ${node_name}") {
|
||||
docker.image('px4io/px4-dev-base:2017-12-30').inside('-e CI=true -e CCACHE_BASEDIR=$WORKSPACE -e CCACHE_DIR=/tmp/ccache -v /tmp/ccache:/tmp/ccache:rw') {
|
||||
stage("${node_name}") {
|
||||
checkout scm
|
||||
sh "make clean"
|
||||
sh "ccache -z"
|
||||
sh "make posix_${node_name}"
|
||||
sh "ccache -s"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// raspberry pi and bebop (armhf)
|
||||
for (def option in ["rpi_cross", "bebop_default"]) {
|
||||
def node_name = "${option}"
|
||||
@@ -125,7 +136,7 @@ pipeline {
|
||||
builds["${node_name}"] = {
|
||||
node {
|
||||
stage("Build Test ${node_name}") {
|
||||
docker.image('px4io/px4-dev-raspi:2017-10-23').inside('-e CI=true -e CCACHE_BASEDIR=$WORKSPACE -e CCACHE_DIR=/tmp/ccache -v /tmp/ccache:/tmp/ccache:rw') {
|
||||
docker.image('px4io/px4-dev-raspi:2017-12-30').inside('-e CI=true -e CCACHE_BASEDIR=$WORKSPACE -e CCACHE_DIR=/tmp/ccache -v /tmp/ccache:/tmp/ccache:rw') {
|
||||
stage("${node_name}") {
|
||||
checkout scm
|
||||
sh "make clean"
|
||||
@@ -147,7 +158,7 @@ pipeline {
|
||||
builds["${node_name}"] = {
|
||||
node {
|
||||
stage("Build Test ${node_name}") {
|
||||
docker.image('px4io/px4-dev-armhf:2017-10-23').inside('-e CI=true -e CCACHE_BASEDIR=$WORKSPACE -e CCACHE_DIR=/tmp/ccache -v /tmp/ccache:/tmp/ccache:rw') {
|
||||
docker.image('px4io/px4-dev-armhf:2017-12-30').inside('-e CI=true -e CCACHE_BASEDIR=$WORKSPACE -e CCACHE_DIR=/tmp/ccache -v /tmp/ccache:/tmp/ccache:rw') {
|
||||
stage("${node_name}") {
|
||||
checkout scm
|
||||
sh "make clean"
|
||||
@@ -170,7 +181,7 @@ pipeline {
|
||||
node {
|
||||
stage("Build Test ${node_name}") {
|
||||
docker.withRegistry('https://registry.hub.docker.com', 'docker_hub_dagar') {
|
||||
docker.image("lorenzmeier/px4-dev-snapdragon:2017-10-23").inside {
|
||||
docker.image("lorenzmeier/px4-dev-snapdragon:2017-12-29").inside('-e CI=true -e CCACHE_BASEDIR=$WORKSPACE -e CCACHE_DIR=/tmp/ccache -v /tmp/ccache:/tmp/ccache:rw') {
|
||||
stage("${node_name}") {
|
||||
checkout scm
|
||||
sh "make clean"
|
||||
@@ -186,8 +197,29 @@ pipeline {
|
||||
}
|
||||
|
||||
|
||||
// GCC7 tests
|
||||
for (def option in ["posix_sitl_default", "nuttx_px4fmu-v5_default"]) {
|
||||
// GCC7 posix
|
||||
for (def option in ["sitl_default"]) {
|
||||
def node_name = "${option}"
|
||||
|
||||
builds["${node_name} (GCC7)"] = {
|
||||
node {
|
||||
stage("Build Test ${node_name} (GCC7)") {
|
||||
docker.image('px4io/px4-dev-base-archlinux:2017-12-30').inside('-e CI=true -e CCACHE_BASEDIR=$WORKSPACE -e CCACHE_DIR=/tmp/ccache -v /tmp/ccache:/tmp/ccache:rw') {
|
||||
stage("${node_name}") {
|
||||
checkout scm
|
||||
sh "make clean"
|
||||
sh "ccache -z"
|
||||
sh "make posix_${node_name}"
|
||||
sh "ccache -s"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GCC7 nuttx
|
||||
for (def option in ["px4fmu-v5_default"]) {
|
||||
def node_name = "${option}"
|
||||
|
||||
builds["${node_name} (GCC7)"] = {
|
||||
@@ -198,7 +230,7 @@ pipeline {
|
||||
checkout scm
|
||||
sh "make clean"
|
||||
sh "ccache -z"
|
||||
sh "make ${node_name}"
|
||||
sh "make nuttx_${node_name}"
|
||||
sh "ccache -s"
|
||||
}
|
||||
}
|
||||
@@ -215,24 +247,95 @@ pipeline {
|
||||
stage('Test') {
|
||||
parallel {
|
||||
|
||||
// temporarily disabled until build resources are available
|
||||
//stage('clang-tidy') {
|
||||
// agent {
|
||||
// docker {
|
||||
// image 'px4io/px4-dev-clang:2017-10-23'
|
||||
// args '-e CI=true -e CCACHE_BASEDIR=$WORKSPACE -e CCACHE_DIR=/tmp/ccache -v /tmp/ccache:/tmp/ccache:rw'
|
||||
// }
|
||||
// }
|
||||
// steps {
|
||||
// sh 'make clean'
|
||||
// sh 'make clang-tidy-quiet'
|
||||
// }
|
||||
//}
|
||||
stage('check style') {
|
||||
agent {
|
||||
docker {
|
||||
image 'px4io/px4-dev-base:2017-12-30'
|
||||
args '-e CI=true'
|
||||
}
|
||||
}
|
||||
steps {
|
||||
sh 'make check_format'
|
||||
}
|
||||
}
|
||||
|
||||
stage('clang analyzer') {
|
||||
agent {
|
||||
docker {
|
||||
image 'px4io/px4-dev-clang:2017-12-30'
|
||||
args '-e CI=true -e CCACHE_BASEDIR=$WORKSPACE -e CCACHE_DIR=/tmp/ccache -v /tmp/ccache:/tmp/ccache:rw'
|
||||
}
|
||||
}
|
||||
steps {
|
||||
sh 'make clean'
|
||||
sh 'make scan-build'
|
||||
// publish html
|
||||
publishHTML target: [
|
||||
reportTitles: 'clang static analyzer',
|
||||
allowMissing: false,
|
||||
alwaysLinkToLastBuild: true,
|
||||
keepAll: true,
|
||||
reportDir: 'build/scan-build/report_latest',
|
||||
reportFiles: '*',
|
||||
reportName: 'Clang Static Analyzer'
|
||||
]
|
||||
}
|
||||
when {
|
||||
anyOf {
|
||||
branch 'master'
|
||||
branch 'beta'
|
||||
branch 'stable'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stage('clang tidy') {
|
||||
agent {
|
||||
docker {
|
||||
image 'px4io/px4-dev-clang:2017-12-30'
|
||||
args '-e CI=true -e CCACHE_BASEDIR=$WORKSPACE -e CCACHE_DIR=/tmp/ccache -v /tmp/ccache:/tmp/ccache:rw'
|
||||
}
|
||||
}
|
||||
steps {
|
||||
sh 'make clean'
|
||||
sh 'make clang-tidy-quiet'
|
||||
}
|
||||
}
|
||||
|
||||
stage('cppcheck') {
|
||||
agent {
|
||||
docker {
|
||||
image 'px4io/px4-dev-base:ubuntu17.10'
|
||||
args '-e CI=true -e CCACHE_BASEDIR=$WORKSPACE -e CCACHE_DIR=/tmp/ccache -v /tmp/ccache:/tmp/ccache:rw'
|
||||
}
|
||||
}
|
||||
steps {
|
||||
sh 'make clean'
|
||||
sh 'make cppcheck'
|
||||
// publish html
|
||||
publishHTML target: [
|
||||
reportTitles: 'Cppcheck',
|
||||
allowMissing: false,
|
||||
alwaysLinkToLastBuild: true,
|
||||
keepAll: true,
|
||||
reportDir: 'build/cppcheck/',
|
||||
reportFiles: '*',
|
||||
reportName: 'Cppcheck'
|
||||
]
|
||||
}
|
||||
when {
|
||||
anyOf {
|
||||
branch 'master'
|
||||
branch 'beta'
|
||||
branch 'stable'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stage('tests') {
|
||||
agent {
|
||||
docker {
|
||||
image 'px4io/px4-dev-base:2017-10-23'
|
||||
image 'px4io/px4-dev-base:2017-12-30'
|
||||
args '-e CI=true -e CCACHE_BASEDIR=$WORKSPACE -e CCACHE_DIR=/tmp/ccache -v /tmp/ccache:/tmp/ccache:rw'
|
||||
}
|
||||
}
|
||||
@@ -243,11 +346,227 @@ pipeline {
|
||||
}
|
||||
}
|
||||
|
||||
stage('ROS vtol mission new 1') {
|
||||
agent {
|
||||
docker {
|
||||
image 'px4io/px4-dev-ros:2017-12-31'
|
||||
args '-e CI=true -e CCACHE_BASEDIR=$WORKSPACE -e CCACHE_DIR=/tmp/ccache -v /tmp/ccache:/tmp/ccache:rw -e HOME=$WORKSPACE'
|
||||
}
|
||||
}
|
||||
steps {
|
||||
sh 'make clean; rm -rf .ros; rm -rf .gazebo'
|
||||
sh 'git fetch --tags'
|
||||
sh 'make posix_sitl_default'
|
||||
sh 'make posix_sitl_default sitl_gazebo'
|
||||
sh './test/rostest_px4_run.sh mavros_posix_test_mission.test mission:=vtol_new_1.txt vehicle:=vtol_standard'
|
||||
}
|
||||
post {
|
||||
success {
|
||||
sh './Tools/upload_log.py -q --description "ROS mission test vtol_new_1.txt: ${CHANGE_ID}" --feedback "${CHANGE_TITLE} - ${CHANGE_URL}" --source CI .ros/rootfs/fs/microsd/log/*/*.ulg'
|
||||
}
|
||||
failure {
|
||||
sh './Tools/upload_log.py -q --description "ROS mission test vtol_new_1.txt: ${CHANGE_ID}" --feedback "${CHANGE_TITLE} - ${CHANGE_URL}" --source CI --email "${CHANGE_AUTHOR_EMAIL}" .ros/rootfs/fs/microsd/log/*/*.ulg'
|
||||
archiveArtifacts '**/*.ulg'
|
||||
archiveArtifacts '.ros/*/px4/**.xml'
|
||||
archiveArtifacts '.ros/log/**.log'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stage('ROS vtol mission new 2') {
|
||||
agent {
|
||||
docker {
|
||||
image 'px4io/px4-dev-ros:2017-12-31'
|
||||
args '-e CI=true -e CCACHE_BASEDIR=$WORKSPACE -e CCACHE_DIR=/tmp/ccache -v /tmp/ccache:/tmp/ccache:rw -e HOME=$WORKSPACE'
|
||||
}
|
||||
}
|
||||
steps {
|
||||
sh 'make clean; rm -rf .ros; rm -rf .gazebo'
|
||||
sh 'git fetch --tags'
|
||||
sh 'make posix_sitl_default'
|
||||
sh 'make posix_sitl_default sitl_gazebo'
|
||||
sh './test/rostest_px4_run.sh mavros_posix_test_mission.test mission:=vtol_new_2.txt vehicle:=vtol_standard'
|
||||
}
|
||||
post {
|
||||
success {
|
||||
sh './Tools/upload_log.py -q --description "ROS mission test vtol_new_2.txt: ${CHANGE_ID}" --feedback "${CHANGE_TITLE} - ${CHANGE_URL}" --source CI .ros/rootfs/fs/microsd/log/*/*.ulg'
|
||||
}
|
||||
failure {
|
||||
sh './Tools/upload_log.py -q --description "ROS mission test vtol_new_2.txt: ${CHANGE_ID}" --feedback "${CHANGE_TITLE} - ${CHANGE_URL}" --source CI --email "${CHANGE_AUTHOR_EMAIL}" .ros/rootfs/fs/microsd/log/*/*.ulg'
|
||||
archiveArtifacts '**/*.ulg'
|
||||
archiveArtifacts '.ros/*/px4/**.xml'
|
||||
archiveArtifacts '.ros/log/**.log'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stage('ROS vtol mission old 1') {
|
||||
agent {
|
||||
docker {
|
||||
image 'px4io/px4-dev-ros:2017-12-31'
|
||||
args '-e CI=true -e CCACHE_BASEDIR=$WORKSPACE -e CCACHE_DIR=/tmp/ccache -v /tmp/ccache:/tmp/ccache:rw -e HOME=$WORKSPACE'
|
||||
}
|
||||
}
|
||||
steps {
|
||||
sh 'make clean; rm -rf .ros; rm -rf .gazebo'
|
||||
sh 'git fetch --tags'
|
||||
sh 'make posix_sitl_default'
|
||||
sh 'make posix_sitl_default sitl_gazebo'
|
||||
sh './test/rostest_px4_run.sh mavros_posix_test_mission.test mission:=vtol_old_1.txt vehicle:=vtol_standard'
|
||||
}
|
||||
post {
|
||||
success {
|
||||
sh './Tools/upload_log.py -q --description "ROS mission test vtol_old_1.txt: ${CHANGE_ID}" --feedback "${CHANGE_TITLE} - ${CHANGE_URL}" --source CI .ros/rootfs/fs/microsd/log/*/*.ulg'
|
||||
}
|
||||
failure {
|
||||
sh './Tools/upload_log.py -q --description "ROS mission test vtol_old_1.txt: ${CHANGE_ID}" --feedback "${CHANGE_TITLE} - ${CHANGE_URL}" --source CI --email "${CHANGE_AUTHOR_EMAIL}" .ros/rootfs/fs/microsd/log/*/*.ulg'
|
||||
archiveArtifacts '**/*.ulg'
|
||||
archiveArtifacts '.ros/*/px4/**.xml'
|
||||
archiveArtifacts '.ros/log/**.log'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stage('ROS vtol mission old 2') {
|
||||
agent {
|
||||
docker {
|
||||
image 'px4io/px4-dev-ros:2017-12-31'
|
||||
args '-e CI=true -e CCACHE_BASEDIR=$WORKSPACE -e CCACHE_DIR=/tmp/ccache -v /tmp/ccache:/tmp/ccache:rw -e HOME=$WORKSPACE'
|
||||
}
|
||||
}
|
||||
steps {
|
||||
sh 'make clean; rm -rf .ros; rm -rf .gazebo'
|
||||
sh 'git fetch --tags'
|
||||
sh 'make posix_sitl_default'
|
||||
sh 'make posix_sitl_default sitl_gazebo'
|
||||
sh './test/rostest_px4_run.sh mavros_posix_test_mission.test mission:=vtol_old_2.txt vehicle:=vtol_standard'
|
||||
}
|
||||
post {
|
||||
success {
|
||||
sh './Tools/upload_log.py -q --description "ROS mission test vtol_old_2.txt: ${CHANGE_ID}" --feedback "${CHANGE_TITLE} - ${CHANGE_URL}" --source CI .ros/rootfs/fs/microsd/log/*/*.ulg'
|
||||
}
|
||||
failure {
|
||||
sh './Tools/upload_log.py -q --description "ROS mission test vtol_old_2.txt: ${CHANGE_ID}" --feedback "${CHANGE_TITLE} - ${CHANGE_URL}" --source CI --email "${CHANGE_AUTHOR_EMAIL}" .ros/rootfs/fs/microsd/log/*/*.ulg'
|
||||
archiveArtifacts '**/*.ulg'
|
||||
archiveArtifacts '.ros/*/px4/**.xml'
|
||||
archiveArtifacts '.ros/log/**.log'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stage('ROS vtol mission old 3') {
|
||||
agent {
|
||||
docker {
|
||||
image 'px4io/px4-dev-ros:2017-12-31'
|
||||
args '-e CI=true -e CCACHE_BASEDIR=$WORKSPACE -e CCACHE_DIR=/tmp/ccache -v /tmp/ccache:/tmp/ccache:rw -e HOME=$WORKSPACE'
|
||||
}
|
||||
}
|
||||
steps {
|
||||
sh 'make clean; rm -rf .ros; rm -rf .gazebo'
|
||||
sh 'git fetch --tags'
|
||||
sh 'make posix_sitl_default'
|
||||
sh 'make posix_sitl_default sitl_gazebo'
|
||||
sh './test/rostest_px4_run.sh mavros_posix_test_mission.test mission:=vtol_old_3.txt vehicle:=vtol_standard'
|
||||
}
|
||||
post {
|
||||
success {
|
||||
sh './Tools/upload_log.py -q --description "ROS mission test vtol_old_3.txt: ${CHANGE_ID}" --feedback "${CHANGE_TITLE} - ${CHANGE_URL}" --source CI .ros/rootfs/fs/microsd/log/*/*.ulg'
|
||||
}
|
||||
failure {
|
||||
sh './Tools/upload_log.py -q --description "ROS mission test vtol_old_3.txt: ${CHANGE_ID}" --feedback "${CHANGE_TITLE} - ${CHANGE_URL}" --source CI --email "${CHANGE_AUTHOR_EMAIL}" .ros/rootfs/fs/microsd/log/*/*.ulg'
|
||||
archiveArtifacts '**/*.ulg'
|
||||
archiveArtifacts '.ros/*/px4/**.xml'
|
||||
archiveArtifacts '.ros/log/**.log'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stage('ROS MC mission box') {
|
||||
agent {
|
||||
docker {
|
||||
image 'px4io/px4-dev-ros:2017-12-31'
|
||||
args '-e CI=true -e CCACHE_BASEDIR=$WORKSPACE -e CCACHE_DIR=/tmp/ccache -v /tmp/ccache:/tmp/ccache:rw -e HOME=$WORKSPACE'
|
||||
}
|
||||
}
|
||||
steps {
|
||||
sh 'make clean; rm -rf .ros; rm -rf .gazebo'
|
||||
sh 'git fetch --tags'
|
||||
sh 'make posix_sitl_default'
|
||||
sh 'make posix_sitl_default sitl_gazebo'
|
||||
sh './test/rostest_px4_run.sh mavros_posix_test_mission.test mission:=multirotor_box.mission vehicle:=iris'
|
||||
}
|
||||
post {
|
||||
success {
|
||||
sh './Tools/upload_log.py -q --description "ROS mission test multirotor_box.mission: ${CHANGE_ID}" --feedback "${CHANGE_TITLE} - ${CHANGE_URL}" --source CI .ros/rootfs/fs/microsd/log/*/*.ulg'
|
||||
}
|
||||
failure {
|
||||
sh './Tools/upload_log.py -q --description "ROS mission test multirotor_box.mission: ${CHANGE_ID}" --feedback "${CHANGE_TITLE} - ${CHANGE_URL}" --source CI --email "${CHANGE_AUTHOR_EMAIL}" .ros/rootfs/fs/microsd/log/*/*.ulg'
|
||||
archiveArtifacts '**/*.ulg'
|
||||
archiveArtifacts '.ros/*/px4/**.xml'
|
||||
archiveArtifacts '.ros/log/**.log'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stage('ROS offboard att') {
|
||||
agent {
|
||||
docker {
|
||||
image 'px4io/px4-dev-ros:2017-12-31'
|
||||
args '-e CI=true -e CCACHE_BASEDIR=$WORKSPACE -e CCACHE_DIR=/tmp/ccache -v /tmp/ccache:/tmp/ccache:rw -e HOME=$WORKSPACE'
|
||||
}
|
||||
}
|
||||
steps {
|
||||
sh 'make clean; rm -rf .ros; rm -rf .gazebo'
|
||||
sh 'git fetch --tags'
|
||||
sh 'make posix_sitl_default'
|
||||
sh 'make posix_sitl_default sitl_gazebo'
|
||||
sh './test/rostest_px4_run.sh mavros_posix_tests_offboard_attctl.test'
|
||||
}
|
||||
post {
|
||||
success {
|
||||
sh './Tools/upload_log.py -q --description "ROS offboard attitude test: ${CHANGE_ID}" --feedback "${CHANGE_TITLE} - ${CHANGE_URL}" --source CI .ros/rootfs/fs/microsd/log/*/*.ulg'
|
||||
}
|
||||
failure {
|
||||
sh './Tools/upload_log.py -q --description "ROS offboard attitude test: ${CHANGE_ID}" --feedback "${CHANGE_TITLE} - ${CHANGE_URL}" --source CI --email "${CHANGE_AUTHOR_EMAIL}" .ros/rootfs/fs/microsd/log/*/*.ulg'
|
||||
archiveArtifacts '**/*.ulg'
|
||||
archiveArtifacts '.ros/*/px4/**.xml'
|
||||
archiveArtifacts '.ros/log/**.log'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stage('ROS offboard pos') {
|
||||
agent {
|
||||
docker {
|
||||
image 'px4io/px4-dev-ros:2017-12-31'
|
||||
args '-e CI=true -e CCACHE_BASEDIR=$WORKSPACE -e CCACHE_DIR=/tmp/ccache -v /tmp/ccache:/tmp/ccache:rw -e HOME=$WORKSPACE'
|
||||
}
|
||||
}
|
||||
steps {
|
||||
sh 'make clean; rm -rf .ros; rm -rf .gazebo'
|
||||
sh 'git fetch --tags'
|
||||
sh 'make posix_sitl_default'
|
||||
sh 'make posix_sitl_default sitl_gazebo'
|
||||
sh './test/rostest_px4_run.sh mavros_posix_tests_offboard_posctl.test'
|
||||
}
|
||||
post {
|
||||
success {
|
||||
sh './Tools/upload_log.py -q --description "ROS offboard position test: ${CHANGE_ID}" --feedback "${CHANGE_TITLE} - ${CHANGE_URL}" --source CI .ros/rootfs/fs/microsd/log/*/*.ulg'
|
||||
}
|
||||
failure {
|
||||
sh './Tools/upload_log.py -q --description "ROS offboard position test: ${CHANGE_ID}" --feedback "${CHANGE_TITLE} - ${CHANGE_URL}" --source CI --email "${CHANGE_AUTHOR_EMAIL}" .ros/rootfs/fs/microsd/log/*/*.ulg'
|
||||
archiveArtifacts '**/*.ulg'
|
||||
archiveArtifacts '.ros/*/px4/**.xml'
|
||||
archiveArtifacts '.ros/log/**.log'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// temporarily disabled until stable
|
||||
//stage('tests coverage') {
|
||||
// agent {
|
||||
// docker {
|
||||
// image 'px4io/px4-dev-base:2017-10-23'
|
||||
// image 'px4io/px4-dev-base:2017-12-30'
|
||||
// args '-e CI=true -e CCACHE_BASEDIR=$WORKSPACE -e CCACHE_DIR=/tmp/ccache -v /tmp/ccache:/tmp/ccache:rw'
|
||||
// }
|
||||
// }
|
||||
@@ -275,7 +594,7 @@ pipeline {
|
||||
|
||||
stage('airframe') {
|
||||
agent {
|
||||
docker { image 'px4io/px4-dev-base:2017-10-23' }
|
||||
docker { image 'px4io/px4-dev-base:2017-12-30' }
|
||||
}
|
||||
steps {
|
||||
sh 'make airframe_metadata'
|
||||
@@ -285,7 +604,7 @@ pipeline {
|
||||
|
||||
stage('parameter') {
|
||||
agent {
|
||||
docker { image 'px4io/px4-dev-base:2017-10-23' }
|
||||
docker { image 'px4io/px4-dev-base:2017-12-30' }
|
||||
}
|
||||
steps {
|
||||
sh 'make parameters_metadata'
|
||||
@@ -295,19 +614,32 @@ pipeline {
|
||||
|
||||
stage('module') {
|
||||
agent {
|
||||
docker { image 'px4io/px4-dev-base:2017-10-23' }
|
||||
docker { image 'px4io/px4-dev-base:2017-12-30' }
|
||||
}
|
||||
steps {
|
||||
sh 'make module_documentation'
|
||||
archiveArtifacts(artifacts: 'modules/*.md', fingerprint: true)
|
||||
}
|
||||
}
|
||||
|
||||
stage('uorb graphs') {
|
||||
agent {
|
||||
docker {
|
||||
image 'px4io/px4-dev-nuttx:2017-12-30'
|
||||
args '-e CI=true -e CCACHE_BASEDIR=$WORKSPACE -e CCACHE_DIR=/tmp/ccache -v /tmp/ccache:/tmp/ccache:rw -e HOME=$WORKSPACE'
|
||||
}
|
||||
}
|
||||
steps {
|
||||
sh 'make uorb_graphs'
|
||||
archiveArtifacts(artifacts: 'Tools/uorb_graph/graph_sitl.json')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stage('S3 Upload') {
|
||||
agent {
|
||||
docker { image 'px4io/px4-dev-base:2017-10-23' }
|
||||
docker { image 'px4io/px4-dev-base:2017-12-30' }
|
||||
}
|
||||
|
||||
when {
|
||||
|
||||
@@ -218,7 +218,7 @@ check_rtps: \
|
||||
check_posix_sitl_rtps \
|
||||
sizes
|
||||
|
||||
.PHONY: sizes check quick_check check_rtps
|
||||
.PHONY: sizes check quick_check check_rtps uorb_graphs
|
||||
|
||||
sizes:
|
||||
@-find build -name *.elf -type f | xargs size 2> /dev/null || :
|
||||
@@ -235,6 +235,14 @@ check_%:
|
||||
@$(MAKE) --no-print-directory $(subst check_,,$@)
|
||||
@echo
|
||||
|
||||
uorb_graphs:
|
||||
@./Tools/uorb_graph/create_from_startupscript.sh
|
||||
@./Tools/uorb_graph/create.py --src-path src --exclude-path src/examples --file Tools/uorb_graph/graph_full
|
||||
@$(MAKE) --no-print-directory px4fmu-v2_default uorb_graph
|
||||
@$(MAKE) --no-print-directory px4fmu-v4_default uorb_graph
|
||||
@$(MAKE) --no-print-directory posix_sitl_default uorb_graph
|
||||
|
||||
|
||||
.PHONY: coverity_scan
|
||||
|
||||
coverity_scan: posix_sitl_default
|
||||
@@ -256,37 +264,6 @@ module_documentation:
|
||||
|
||||
px4_metadata: parameters_metadata airframe_metadata module_documentation
|
||||
|
||||
# S3 upload helpers
|
||||
# --------------------------------------------------------------------
|
||||
# s3cmd uses these ENV variables
|
||||
# AWS_ACCESS_KEY_ID
|
||||
# AWS_SECRET_ACCESS_KEY
|
||||
# AWS_S3_BUCKET
|
||||
.PHONY: s3put_firmware s3put_qgc_firmware s3put_px4fmu_firmware s3put_misc_qgc_extra_firmware s3put_metadata s3put_scan-build s3put_cppcheck s3put_coverage
|
||||
|
||||
s3put_qgc_firmware: s3put_px4fmu_firmware s3put_misc_qgc_extra_firmware
|
||||
|
||||
s3put_px4fmu_firmware: px4fmu_firmware
|
||||
@find $(SRC_DIR)/build -name "*.px4" -exec $(SRC_DIR)/Tools/s3put.sh "{}" \;
|
||||
|
||||
s3put_misc_qgc_extra_firmware: misc_qgc_extra_firmware
|
||||
@find $(SRC_DIR)/build -name "*.px4" -exec $(SRC_DIR)/Tools/s3put.sh "{}" \;
|
||||
|
||||
s3put_metadata: px4_metadata
|
||||
@$(SRC_DIR)/Tools/s3put.sh airframes.md
|
||||
@$(SRC_DIR)/Tools/s3put.sh airframes.xml
|
||||
@$(SRC_DIR)/Tools/s3put.sh parameters.xml
|
||||
@$(SRC_DIR)/Tools/s3put.sh parameters.md
|
||||
|
||||
s3put_scan-build: scan-build
|
||||
@$(SRC_DIR)/Tools/s3put.sh `find $(SRC_DIR)/build/scan-build -mindepth 1 -maxdepth 1 -type d`/
|
||||
|
||||
s3put_cppcheck: cppcheck
|
||||
@$(SRC_DIR)/Tools/s3put.sh $(SRC_DIR)/build/cppcheck/
|
||||
|
||||
s3put_coverage: tests_coverage
|
||||
@$(SRC_DIR)/Tools/s3put.sh $(SRC_DIR)/build/posix_sitl_default/coverage-html/
|
||||
|
||||
# Astyle
|
||||
# --------------------------------------------------------------------
|
||||
.PHONY: check_format format
|
||||
@@ -302,7 +279,7 @@ format:
|
||||
|
||||
# Testing
|
||||
# --------------------------------------------------------------------
|
||||
.PHONY: tests tests_coverage
|
||||
.PHONY: tests tests_coverage tests_mission tests_offboard rostest
|
||||
|
||||
tests:
|
||||
@$(MAKE) --no-print-directory posix_sitl_default test_results \
|
||||
@@ -310,9 +287,25 @@ tests:
|
||||
UBSAN_OPTIONS="color=always"
|
||||
|
||||
tests_coverage:
|
||||
@$(MAKE) clean
|
||||
@$(MAKE) --no-print-directory posix_sitl_default PX4_CMAKE_BUILD_TYPE=Coverage
|
||||
@$(MAKE) --no-print-directory posix_sitl_default sitl_gazebo PX4_CMAKE_BUILD_TYPE=Coverage
|
||||
@$(SRC_DIR)/test/rostest_px4_run.sh mavros_posix_tests_missions.test
|
||||
@$(SRC_DIR)/test/rostest_px4_run.sh mavros_posix_tests_offboard_attctl.test
|
||||
@$(SRC_DIR)/test/rostest_px4_run.sh mavros_posix_tests_offboard_posctl.test
|
||||
@$(MAKE) --no-print-directory posix_sitl_default test_coverage_genhtml PX4_CMAKE_BUILD_TYPE=Coverage
|
||||
@echo "Open $(SRC_DIR)/build/posix_sitl_default/coverage-html/index.html to see coverage"
|
||||
|
||||
rostest: posix_sitl_default
|
||||
@$(MAKE) --no-print-directory posix_sitl_default sitl_gazebo
|
||||
|
||||
tests_mission: rostest
|
||||
@$(SRC_DIR)/test/rostest_px4_run.sh mavros_posix_tests_missions.test
|
||||
|
||||
tests_offboard: rostest
|
||||
@$(SRC_DIR)/test/rostest_px4_run.sh mavros_posix_tests_offboard_attctl.test
|
||||
@$(SRC_DIR)/test/rostest_px4_run.sh mavros_posix_tests_offboard_posctl.test
|
||||
|
||||
# static analyzers (scan-build, clang-tidy, cppcheck)
|
||||
# --------------------------------------------------------------------
|
||||
.PHONY: scan-build posix_sitl_default-clang clang-tidy clang-tidy-fix clang-tidy-quiet cppcheck check_stack
|
||||
@@ -320,9 +313,12 @@ tests_coverage:
|
||||
scan-build:
|
||||
@export CCC_CC=clang
|
||||
@export CCC_CXX=clang++
|
||||
@rm -rf $(SRC_DIR)/build/posix_sitl_default-scan-build
|
||||
@rm -rf $(SRC_DIR)/build/scan-build/report_latest
|
||||
@mkdir -p $(SRC_DIR)/build/posix_sitl_default-scan-build
|
||||
@cd $(SRC_DIR)/build/posix_sitl_default-scan-build && scan-build cmake $(SRC_DIR) -GNinja -DCONFIG=posix_sitl_default
|
||||
@scan-build -o $(SRC_DIR)/build/scan-build cmake --build $(SRC_DIR)/build/posix_sitl_default-scan-build
|
||||
@find $(SRC_DIR)/build/scan-build -maxdepth 1 -mindepth 1 -type d -exec cp -r "{}" $(SRC_DIR)/build/scan-build/report_latest \;
|
||||
|
||||
posix_sitl_default-clang:
|
||||
@mkdir -p $(SRC_DIR)/build/posix_sitl_default-clang
|
||||
|
||||
@@ -1,34 +1,42 @@
|
||||
## PX4 Pro Drone Autopilot ##
|
||||
# PX4 Pro Drone Autopilot
|
||||
|
||||
[](https://github.com/PX4/Firmware/releases) [](https://zenodo.org/badge/latestdoi/22634/PX4/Firmware) [](https://travis-ci.org/PX4/Firmware) [](https://scan.coverity.com/projects/3966?tab=overview)
|
||||
[](https://github.com/PX4/Firmware/releases) [](https://zenodo.org/badge/latestdoi/22634/PX4/Firmware)
|
||||
|
||||
[](http://ci.px4.io:8080/blue/organizations/jenkins/Firmware/activity) [](https://scan.coverity.com/projects/3966?tab=overview)
|
||||
|
||||
[](http://slack.px4.io)
|
||||
|
||||
This repository holds the [PX4 Pro](http://px4.io) flight control solution for drones, with the main applications located in the [src/modules](https://github.com/PX4/Firmware/tree/master/src/modules) directory. It also contains the PX4 Drone Middleware Platform, which provides drivers and middleware to run drones.
|
||||
|
||||
* Official Website: http://px4.io (License: BSD 3-clause, [LICENSE.md](https://github.com/PX4/Firmware/blob/master/LICENSE.md))
|
||||
* Supported airframes:
|
||||
* [Multicopters](http://px4.io/portfolio_category/multicopter/)
|
||||
* [Fixed wing](http://px4.io/portfolio_category/plane/)
|
||||
* [VTOL](http://px4.io/portfolio_category/vtol/)
|
||||
* many more experimental types (Rovers, Blimps, Boats, Submarines, etc)
|
||||
* Releases: [Downloads](https://github.com/PX4/Firmware/releases)
|
||||
* Official Website: http://px4.io (License: BSD 3-clause, [LICENSE](https://github.com/PX4/Firmware/blob/master/LICENSE))
|
||||
* [Supported airframes](https://docs.px4.io/en/airframes/airframe_reference.html) ([portfolio](http://px4.io/#airframes)):
|
||||
* [Multicopters](https://docs.px4.io/en/airframes/airframe_reference.html#copter)
|
||||
* [Fixed wing](https://docs.px4.io/en/airframes/airframe_reference.html#plane)
|
||||
* [VTOL](https://docs.px4.io/en/airframes/airframe_reference.html#vtol)
|
||||
* many more experimental types (Rovers, Blimps, Boats, Submarines, etc)
|
||||
* Releases: [Downloads](https://github.com/PX4/Firmware/releases)
|
||||
|
||||
|
||||
## PX4 Users
|
||||
|
||||
The [PX4 User Guide](https://docs.px4.io/en/) explains how to assemble [supported vehicles](https://docs.px4.io/en/airframes/airframe_reference.html) and fly drones with PX4.
|
||||
See the [forum and chat](https://docs.px4.io/en/#support) if you need help!
|
||||
|
||||
|
||||
## PX4 Developers
|
||||
|
||||
This [Developer Guide](https://dev.px4.io/) is for software developers who want to modify the flight stack and middleware (e.g. to add new flight modes), hardware integrators who want to support new flight controller boards and peripherals, and anyone who wants to get PX4 working on a new (unsupported) airframe/vehicle.
|
||||
|
||||
Developers should read the [Guide for Contributions](https://dev.px4.io/en/contribute/).
|
||||
See the [forum and chat](https://dev.px4.io/en/#support) if you need help!
|
||||
|
||||
Please refer to the [user documentation](https://docs.px4.io/en/) and [user forum](http://discuss.px4.io) for flying drones with the PX4 flight stack.
|
||||
|
||||
### Weekly Dev Call
|
||||
|
||||
The PX4 Dev Team syncs up on a [weekly dev call](https://dev.px4.io/en/contribute/).
|
||||
The PX4 Dev Team syncs up on a [weekly dev call](https://dev.px4.io/en/contribute/#dev_call).
|
||||
|
||||
* [Wednesday 17:00 Central European Time, 11:00 Eastern Time, 08:00 Pacific Standard Time](https://www.google.com/calendar/embed?src=bGludXhmb3VuZGF0aW9uLm9yZ19nMjF0dmFtMjRtN3BtN2poZXYwMWJ2bHFoOEBncm91cC5jYWxlbmRhci5nb29nbGUuY29t)
|
||||
* [Uber conference (dial-in or web client)](https://www.uberconference.com/lf-dronecode)
|
||||
* The agenda is announced in advance on the [PX4 Discuss](http://discuss.px4.io/c/weekly-dev-call)
|
||||
* Issues and PRs may be labelled [devcall](https://github.com/PX4/Firmware/issues?q=is%3Aopen+is%3Aissue+label%3Adevcall) to flag them for discussion
|
||||
> **Note** The dev call is open to all interested developers (not just the core dev team). This is a great opportunity to meet the team and contribute to the ongoing development of the platform.
|
||||
|
||||
### Developers ###
|
||||
* [Developer Guide](https://dev.px4.io/)
|
||||
* [Build instructions](https://dev.px4.io/en/setup/building_px4.html)
|
||||
* [Guide for Contributions](https://dev.px4.io/en/contribute/)
|
||||
|
||||
## Maintenance Team
|
||||
|
||||
@@ -71,28 +79,32 @@ The PX4 Dev Team syncs up on a [weekly dev call](https://dev.px4.io/en/contribut
|
||||
* [Airmind MindPX / MindRacer](https://github.com/PX4/Firmware/labels/mindpx) - [Henry Zhang](https://github.com/iZhangHui)
|
||||
* RTPS/ROS2 Interface - [Vicente Monge](https://github.com/vicenteeprosima)
|
||||
|
||||
See also [About Us](http://px4.io/about-us/#development_team) (px4.io) and the [contributors list](https://github.com/PX4/Firmware/graphs/contributors) (Github).
|
||||
|
||||
## Supported Hardware
|
||||
|
||||
This repository contains code supporting these boards:
|
||||
* [Snapdragon Flight](https://dev.px4.io/en/flight_controller/snapdragon_flight.html)
|
||||
* [Intel Aero](https://dev.px4.io/en/flight_controller/intel_aero.html)
|
||||
* [Raspberry PI with Navio 2](https://dev.px4.io/en/flight_controller/raspberry_pi.html)
|
||||
* [Snapdragon Flight](https://docs.px4.io/en/flight_controller/snapdragon_flight.html)
|
||||
* [Intel Aero](https://docs.px4.io/en/flight_controller/intel_aero.html)
|
||||
* [Raspberry PI with Navio 2](https://docs.px4.io/en/flight_controller/raspberry_pi_navio2.html)
|
||||
* [Parrot Bebop 2](https://dev.px4.io/en/advanced/parrot_bebop.html)
|
||||
* FMUv2.x
|
||||
* [Pixhawk](https://dev.px4.io/en/flight_controller/pixhawk.html)
|
||||
* Pixhawk Mini
|
||||
* [Pixfalcon](https://dev.px4.io/en/flight_controller/pixfalcon.html)
|
||||
* [Pixhawk](https://docs.px4.io/en/flight_controller/pixhawk.html)
|
||||
* [Pixhawk Mini](https://docs.px4.io/en/flight_controller/pixhawk_mini.html)
|
||||
* [Pixfalcon](https://docs.px4.io/en/flight_controller/pixfalcon.html)
|
||||
* FMUv3.x [Pixhawk 2](https://pixhawk.org/modules/pixhawk2)
|
||||
* FMUv4.x
|
||||
* [Pixracer](https://dev.px4.io/en/flight_controller/pixracer.html)
|
||||
* Pixhawk 3 Pro
|
||||
* [Pixracer](https://docs.px4.io/en/flight_controller/pixracer.html)
|
||||
* [Pixhawk 3 Pro](https://docs.px4.io/en/flight_controller/pixhawk3_pro.html)
|
||||
* FMUv5.x (ARM Cortex M7, future Pixhawk)
|
||||
* STM32F4Discovery (basic support) [Tutorial](https://pixhawk.org/modules/stm32f4discovery)
|
||||
* Gumstix AeroCore (v1 and v2)
|
||||
* [STM32F4Discovery](http://www.st.com/en/evaluation-tools/stm32f4discovery.html) (basic support) [Tutorial](https://pixhawk.org/modules/stm32f4discovery)
|
||||
* [Gumstix AeroCore](https://www.gumstix.com/aerocore-2/) (only v2)
|
||||
* [Airmind MindPX V2.8](http://www.mindpx.net/assets/accessories/UserGuide_MindPX.pdf)
|
||||
* [Airmind MindRacer V1.2](http://mindpx.net/assets/accessories/mindracer_user_guide_v1.2.pdf)
|
||||
* [Bitcraze Crazyflie 2.0](https://dev.px4.io/en/flight_controller/crazyflie2.html)
|
||||
* [Bitcraze Crazyflie 2.0](https://docs.px4.io/en/flight_controller/crazyflie2.html)
|
||||
|
||||
## Project Milestones
|
||||
Additional information about supported hardware can be found in [PX4 user Guide > Autopilot Hardware](https://docs.px4.io/en/flight_controller/).
|
||||
|
||||
The PX4 software and Pixhawk hardware (which has been designed for it) has been created in 2011 by [Lorenz Meier](https://github.com/LorenzMeier).
|
||||
## Project Roadmap
|
||||
|
||||
A high level project roadmap is available [here](https://www.dronecode.org/roadmap/).
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
# @maintainer Lorenz Meier <lorenz@px4.io>
|
||||
#
|
||||
|
||||
sh /etc/init.d/rc.mc_defaults
|
||||
sh /etc/init.d/4001_quad_x
|
||||
|
||||
set MIXER quad_x
|
||||
param set SYS_HITL 1
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
# @output AUX4 Rudder
|
||||
# @output AUX5 Throttle
|
||||
#
|
||||
# @board px4fmu-v1 exclude
|
||||
|
||||
sh /etc/init.d/rc.vtol_defaults
|
||||
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
#
|
||||
# @maintainer Roman Bapst <roman@px4.io>
|
||||
#
|
||||
# @board px4fmu-v2 exclude
|
||||
#
|
||||
|
||||
sh /etc/init.d/rc.vtol_defaults
|
||||
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
#
|
||||
# @maintainer Samay Siga <samay_s@icloud.com>
|
||||
#
|
||||
# @board px4fmu-v2 exclude
|
||||
#
|
||||
|
||||
sh /etc/init.d/rc.vtol_defaults
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
# @type Quadrotor x
|
||||
# @class Copter
|
||||
#
|
||||
# @maintainer Lucas de Marchi
|
||||
# @maintainer Beat Kueng <beat@px4.io>
|
||||
#
|
||||
|
||||
sh /etc/init.d/rc.mc_defaults
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
#
|
||||
# @maintainer
|
||||
#
|
||||
# @board px4fmu-v2 exclude
|
||||
#
|
||||
|
||||
sh /etc/init.d/rc.ugv_defaults
|
||||
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
# @output MAIN7 pass-through of control group 0, channel 6
|
||||
# @output MAIN8 pass-through of control group 0, channel 7
|
||||
#
|
||||
# @board px4fmu-v2 exclude
|
||||
#
|
||||
|
||||
sh /etc/init.d/rc.axialracing_ax10_defaults
|
||||
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
#
|
||||
# @maintainer Marco Zorzi
|
||||
#
|
||||
# @board px4fmu-v2 exclude
|
||||
#
|
||||
|
||||
sh /etc/init.d/rc.ugv_defaults
|
||||
|
||||
|
||||
@@ -69,6 +69,9 @@ fi
|
||||
|
||||
if ver hwcmp PX4FMU_V2
|
||||
then
|
||||
# V2 build hwtypecmp is always false
|
||||
set BOARD_FMUV3 0
|
||||
|
||||
# External I2C bus
|
||||
hmc5883 -C -T -X start
|
||||
lis3mdl -X start
|
||||
@@ -79,8 +82,6 @@ then
|
||||
# Internal SPI bus ICM-20608-G
|
||||
mpu6000 -T 20608 start
|
||||
|
||||
# V2 build hwtypecmp is always false
|
||||
set BOARD_FMUV3 0
|
||||
# V3 build hwtypecmp supports V2|V2M|V30
|
||||
if ver hwtypecmp V30
|
||||
then
|
||||
@@ -99,6 +100,26 @@ then
|
||||
fi
|
||||
fi
|
||||
|
||||
# Check if a Pixhack (which reports as V2M) is present
|
||||
if ver hwtypecmp V2M
|
||||
then
|
||||
# Pixhawk Mini doesn't have these sensors,
|
||||
# so if they are found we know its a Pixhack
|
||||
|
||||
# external MPU6K is rotated 180 degrees yaw
|
||||
if mpu6000 -S -R 4 start
|
||||
then
|
||||
set BOARD_FMUV3 20
|
||||
else
|
||||
# Check for Pixhack 3.1
|
||||
# external MPU9250 is rotated 180 degrees yaw
|
||||
if mpu9250 -S -R 4 start
|
||||
then
|
||||
set BOARD_FMUV3 21
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ $BOARD_FMUV3 != 0 ]
|
||||
then
|
||||
# sensor heating is available, but we disable it for now
|
||||
@@ -372,6 +393,12 @@ then
|
||||
teraranger start
|
||||
fi
|
||||
|
||||
# Benewake TFMini
|
||||
if param greater SENS_EN_TFMINI 0
|
||||
then
|
||||
tfmini start
|
||||
fi
|
||||
|
||||
# Wait 20 ms for sensors (because we need to wait for the HRT and work queue callbacks to fire)
|
||||
usleep 20000
|
||||
sensors start
|
||||
|
||||
@@ -4,20 +4,17 @@ set VEHICLE_TYPE vtol
|
||||
|
||||
if [ $AUTOCNF == yes ]
|
||||
then
|
||||
param set MC_ROLL_P 6.0
|
||||
param set MC_PITCH_P 6.0
|
||||
param set MC_YAW_P 4
|
||||
|
||||
#
|
||||
# Default parameters for mission and position handling
|
||||
#
|
||||
param set NAV_ACC_RAD 3
|
||||
param set MPC_TKO_SPEED 1.0
|
||||
param set MPC_LAND_SPEED 0.7
|
||||
param set MPC_Z_VEL_MAX_DN 1.5
|
||||
param set MPC_XY_VEL_MAX 4.0
|
||||
param set MIS_TAKEOFF_ALT 20
|
||||
param set MIS_YAW_TMT 10
|
||||
|
||||
param set MPC_ACC_HOR_MAX 2.0
|
||||
param set MPC_LAND_SPEED 0.7
|
||||
param set MPC_TKO_SPEED 1.0
|
||||
param set MPC_XY_VEL_MAX 4.0
|
||||
param set MPC_Z_VEL_MAX_DN 1.5
|
||||
|
||||
param set NAV_ACC_RAD 3
|
||||
|
||||
param set RTL_LAND_DELAY 0
|
||||
fi
|
||||
|
||||
|
||||
@@ -60,7 +60,6 @@ set LOG_FILE /fs/microsd/bootlog.txt
|
||||
# REBOOTWORK this needs to start after the flight control loop
|
||||
if mount -t vfat /dev/mmcsd0 /fs/microsd
|
||||
then
|
||||
echo "[i] microSD mounted: /fs/microsd"
|
||||
if hardfault_log check
|
||||
then
|
||||
tone_alarm error
|
||||
@@ -79,9 +78,9 @@ else
|
||||
then
|
||||
if mount -t vfat /dev/mmcsd0 /fs/microsd
|
||||
then
|
||||
echo "INFO [init] MicroSD card formatted"
|
||||
echo "INFO [init] card formatted"
|
||||
else
|
||||
echo "ERROR [init] Format failed"
|
||||
echo "ERROR [init] format failed"
|
||||
tone_alarm MNBG
|
||||
set LOG_FILE /dev/null
|
||||
fi
|
||||
@@ -97,7 +96,6 @@ fi
|
||||
set FRC /fs/microsd/etc/rc.txt
|
||||
if [ -f $FRC ]
|
||||
then
|
||||
echo "INFO [init] Executing script: ${FRC}"
|
||||
sh $FRC
|
||||
set MODE custom
|
||||
fi
|
||||
@@ -194,7 +192,7 @@ then
|
||||
set MAV_TYPE none
|
||||
set FAILSAFE none
|
||||
set USE_IO no
|
||||
set LOGGER_BUF 16
|
||||
set LOGGER_BUF 14
|
||||
|
||||
if ver hwcmp PX4FMU_V4
|
||||
then
|
||||
@@ -316,8 +314,6 @@ then
|
||||
|
||||
if px4io checkcrc ${IO_FILE}
|
||||
then
|
||||
echo "[init] PX4IO CRC OK" >> $LOG_FILE
|
||||
|
||||
set IO_PRESENT yes
|
||||
else
|
||||
tone_alarm MLL32CP8MB
|
||||
@@ -464,7 +460,6 @@ then
|
||||
then
|
||||
if param compare UAVCAN_ENABLE 0
|
||||
then
|
||||
echo "OVERRIDING UAVCAN_ENABLE = 3" >> $LOG_FILE
|
||||
param set UAVCAN_ENABLE 3
|
||||
fi
|
||||
fi
|
||||
@@ -505,7 +500,6 @@ then
|
||||
if mkblctrl $MKBLCTRL_ARG
|
||||
then
|
||||
else
|
||||
echo "MK start failed" >> $LOG_FILE
|
||||
tone_alarm $TUNE_ERR
|
||||
fi
|
||||
unset MKBLCTRL_ARG
|
||||
@@ -823,7 +817,6 @@ then
|
||||
# Still no MAV_TYPE found
|
||||
if [ $MAV_TYPE == none ]
|
||||
then
|
||||
echo "Unknown MAV_TYPE"
|
||||
param set MAV_TYPE 2
|
||||
else
|
||||
param set MAV_TYPE ${MAV_TYPE}
|
||||
|
||||
@@ -3,8 +3,17 @@
|
||||
function check_git_submodule {
|
||||
|
||||
# The .git exists in a submodule if init and update have been done.
|
||||
if [ "$CI" != "true" ] && [[ -f $1"/.git" || -d $1"/.git" ]];
|
||||
if [[ -f $1"/.git" || -d $1"/.git" ]];
|
||||
then
|
||||
|
||||
if [ "$CI" == "true" ];
|
||||
then
|
||||
git submodule sync --recursive -- $1
|
||||
git submodule update --init --recursive --force -- $1 || true
|
||||
git submodule update --init --recursive --force -- $1
|
||||
exit 0
|
||||
fi
|
||||
|
||||
SUBMODULE_STATUS=$(git submodule summary "$1")
|
||||
STATUSRETVAL=$(echo $SUBMODULE_STATUS | grep -A20 -i "$1")
|
||||
if ! [[ -z "$STATUSRETVAL" ]];
|
||||
@@ -35,8 +44,8 @@ then
|
||||
elif [ "$user_cmd" == "u" ]
|
||||
then
|
||||
git submodule sync --recursive -- $1
|
||||
git submodule update --init --recursive --force --quiet -- $1 || true
|
||||
git submodule update --init --recursive -- $1
|
||||
git submodule update --init --recursive -- $1 || true
|
||||
git submodule update --init --recursive --force -- $1
|
||||
echo "Submodule fixed, continuing build.."
|
||||
else
|
||||
echo "Build aborted."
|
||||
@@ -44,9 +53,9 @@ then
|
||||
fi
|
||||
fi
|
||||
else
|
||||
git submodule sync --recursive -- $1
|
||||
git submodule update --init --recursive --force -- $1 || true
|
||||
git submodule update --init --recursive --force -- $1
|
||||
git submodule sync --recursive --quiet -- $1
|
||||
git submodule update --init --recursive -- $1 || true
|
||||
git submodule update --init --recursive -- $1
|
||||
fi
|
||||
|
||||
}
|
||||
|
||||
+5
-5
@@ -7,22 +7,22 @@ if [ -z ${PX4_DOCKER_REPO+x} ]; then
|
||||
PX4_DOCKER_REPO="px4io/px4-dev-nuttx:2017-10-23"
|
||||
elif [[ $@ =~ .*rpi.* ]] || [[ $@ =~ .*bebop.* ]]; then
|
||||
# posix_rpi_cross, posix_bebop_default
|
||||
PX4_DOCKER_REPO="px4io/px4-dev-raspi:2017-10-23"
|
||||
PX4_DOCKER_REPO="px4io/px4-dev-raspi:2017-12-30"
|
||||
elif [[ $@ =~ .*eagle.* ]] || [[ $@ =~ .*excelsior.* ]]; then
|
||||
# eagle, excelsior
|
||||
PX4_DOCKER_REPO="lorenzmeier/px4-dev-snapdragon:2017-10-23"
|
||||
PX4_DOCKER_REPO="lorenzmeier/px4-dev-snapdragon:2017-12-29"
|
||||
elif [[ $@ =~ .*ocpoc.* ]]; then
|
||||
# posix_ocpoc_ubuntu
|
||||
PX4_DOCKER_REPO="px4io/px4-dev-armhf:2017-10-23"
|
||||
PX4_DOCKER_REPO="px4io/px4-dev-armhf:2017-12-30"
|
||||
elif [[ $@ =~ .*clang.* ]] || [[ $@ =~ .*scan-build.* ]]; then
|
||||
# clang tools
|
||||
PX4_DOCKER_REPO="px4io/px4-dev-clang:2017-10-23"
|
||||
PX4_DOCKER_REPO="px4io/px4-dev-clang:2017-12-30"
|
||||
elif [[ $@ =~ .*cppcheck.* ]]; then
|
||||
# TODO: remove this once px4io/px4-dev-base updates
|
||||
PX4_DOCKER_REPO="px4io/px4-dev-base:ubuntu17.10"
|
||||
elif [[ $@ =~ .*tests* ]]; then
|
||||
# run all tests with simulation
|
||||
PX4_DOCKER_REPO="px4io/px4-dev-simulation:2017-10-23"
|
||||
PX4_DOCKER_REPO="px4io/px4-dev-simulation:2017-12-30"
|
||||
fi
|
||||
else
|
||||
echo "PX4_DOCKER_REPO is set to '$PX4_DOCKER_REPO'";
|
||||
|
||||
+1
-1
Submodule Tools/jMAVSim updated: d24a1e1a68...21a0ad7790
@@ -488,9 +488,18 @@ class uploader(object):
|
||||
print("FORCED WRITE, FLASHING ANYWAY!")
|
||||
else:
|
||||
raise IOError(msg)
|
||||
|
||||
# Prevent uploads where the image would overflow the flash
|
||||
if self.fw_maxsize < fw.property('image_size'):
|
||||
raise RuntimeError("Firmware image is too large for this board")
|
||||
|
||||
# Prevent uploads where the maximum image size of the board config is smaller than the flash
|
||||
# of the board. This is a hint the user chose the wrong config and will lack features
|
||||
# for this particular board.
|
||||
if self.fw_maxsize > fw.property('image_maxsize'):
|
||||
raise RuntimeError("Board can accept larger flash images (%u bytes) than board config (%u bytes). Please use the correct board configuration to avoid lacking critical functionality."
|
||||
% (self.fw_maxsize, fw.property('image_maxsize')))
|
||||
|
||||
# OTP added in v4:
|
||||
if self.bl_rev > 3:
|
||||
for byte in range(0, 32*6, 4):
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
filename=${1}
|
||||
|
||||
# Requires these ENV variables
|
||||
# AWS_ACCESS_KEY_ID
|
||||
# AWS_SECRET_ACCESS_KEY
|
||||
# AWS_S3_BUCKET
|
||||
|
||||
[ -z "$AWS_ACCESS_KEY_ID" ] && { echo "ERROR: Need to set AWS_ACCESS_KEY_ID"; exit 1; }
|
||||
[ -z "$AWS_SECRET_ACCESS_KEY" ] && { echo "ERROR: Need to set AWS_SECRET_ACCESS_KEY"; exit 1; }
|
||||
[ -z "$AWS_S3_BUCKET" ] && { echo "ERROR: Need to set AWS_S3_BUCKET"; exit 1; }
|
||||
|
||||
if [ -f ${filename} ]; then
|
||||
base_file_name=`basename $filename`
|
||||
s3cmd --access_key=${AWS_ACCESS_KEY_ID} --secret_key=${AWS_SECRET_ACCESS_KEY} put ${filename} s3://${AWS_S3_BUCKET}/${base_file_name}
|
||||
elif [ -d ${filename} ]; then
|
||||
dir_name=$filename
|
||||
s3cmd --access_key=${AWS_ACCESS_KEY_ID} --secret_key=${AWS_SECRET_ACCESS_KEY} put -r ${dir_name} s3://${AWS_S3_BUCKET}/
|
||||
else
|
||||
echo "ERROR: ${file} doesn't exist"
|
||||
exit 1
|
||||
fi
|
||||
+1
-1
Submodule Tools/sitl_gazebo updated: b052c97f7c...b59e6e78e4
@@ -0,0 +1,2 @@
|
||||
*.json
|
||||
|
||||
Executable
+641
@@ -0,0 +1,641 @@
|
||||
#! /usr/bin/env python
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import codecs
|
||||
import re
|
||||
import colorsys
|
||||
import json
|
||||
|
||||
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Generate uORB pub/sub dependency graph from source code')
|
||||
|
||||
parser.add_argument('-s', '--src-path', action='append',
|
||||
help='Source path(s) (default=src, can be specified multiple times)',
|
||||
default=[])
|
||||
parser.add_argument('-e', '--exclude-path', action='append',
|
||||
help='Excluded path(s), can be specified multiple times',
|
||||
default=[])
|
||||
parser.add_argument('-f', '--file', metavar='file', action='store',
|
||||
help='output file name prefix',
|
||||
default='graph')
|
||||
parser.add_argument('-o', '--output', metavar='output', action='store',
|
||||
help='output format (json or graphviz)',
|
||||
default='json')
|
||||
parser.add_argument('--use-topic-union', action='store_true',
|
||||
help='''
|
||||
Use the union of all publication and subscription topics (useful for complete
|
||||
graphs or only few/single module(s)). The default is to use the intersection
|
||||
(remove topics that have no subscriber or no publisher)''')
|
||||
parser.add_argument('-m', '--modules', action='store',
|
||||
help='Comma-separated whitelist of modules (the module\'s '+
|
||||
'MAIN, e.g. from a startup script)',
|
||||
default='')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
|
||||
g_debug = False
|
||||
def dbg_print(string):
|
||||
if g_debug:
|
||||
print(string)
|
||||
|
||||
|
||||
def get_N_colors(N, s=0.8, v=0.9):
|
||||
""" get N distinct colors as a list of hex strings """
|
||||
HSV_tuples = [(x*1.0/N, s, v) for x in range(N)]
|
||||
hex_out = []
|
||||
for rgb in HSV_tuples:
|
||||
rgb = map(lambda x: int(x*255), colorsys.hsv_to_rgb(*rgb))
|
||||
hex_out.append("#"+"".join(map(lambda x: format(x, '02x'), rgb)))
|
||||
return hex_out
|
||||
|
||||
|
||||
class PubSub:
|
||||
""" Collects either publication or subscription information for nodes
|
||||
(modules and topics) & edges """
|
||||
|
||||
def __init__(self, is_publication, topic_blacklist, orb_pub_sub_regexes, special_cases):
|
||||
"""
|
||||
:param is_publication: if True, publications, False for
|
||||
subscriptions
|
||||
:param topic_blacklist: list of topics to blacklist
|
||||
:param orb_pub_sub_regexes: list of regexes to extract orb calls
|
||||
(e.g. orb_subscribe). They need to have 2 captures, the second
|
||||
one is the one capturing ORB_ID(<topic>
|
||||
"""
|
||||
self._module_pubsubs = {} # key = module name, value = set of topic names
|
||||
self._special_cases = special_cases
|
||||
self._special_cases_matched = None
|
||||
self._topic_blacklist = topic_blacklist
|
||||
self._orb_pub_sub_regexes = orb_pub_sub_regexes
|
||||
if is_publication:
|
||||
self._method = 'Publication'
|
||||
else:
|
||||
self._method = 'Subscription'
|
||||
|
||||
def reset(self):
|
||||
self._special_cases_matched = [False]*len(self._special_cases)
|
||||
|
||||
def filter_modules(self, module_whitelist):
|
||||
remove = [k for k in self._module_pubsubs if k not in module_whitelist]
|
||||
for k in remove: del self._module_pubsubs[k]
|
||||
|
||||
|
||||
def check_if_match_found(self, modules):
|
||||
""" check if all special cases got a match (if not, it means the source
|
||||
code got changed)
|
||||
"""
|
||||
for i, (module_match, file_match_re, src_match_re, _) in enumerate(self._special_cases):
|
||||
if module_match in modules and src_match_re is not None:
|
||||
if not self._special_cases_matched[i]:
|
||||
raise Exception('Module '+module_match+
|
||||
': no match for '+self._method+' special case'+
|
||||
src_match_re.pattern+'. The case needs to be updated')
|
||||
|
||||
|
||||
def extract(self, file_name, src_str, module, orb_id_vehicle_attitude_controls_topic):
|
||||
""" Extract subscribed/published topics from a source string
|
||||
:param src_str: string of C/C++ code with comments and whitespace removed
|
||||
"""
|
||||
|
||||
orb_pubsub_matches = []
|
||||
for regex in self._orb_pub_sub_regexes:
|
||||
orb_pubsub_matches += re.findall(regex, src_str)
|
||||
|
||||
orb_id = 'ORB_ID('
|
||||
|
||||
for _, match in orb_pubsub_matches:
|
||||
if match == 'ORB_ID_VEHICLE_ATTITUDE_CONTROLS': # special case
|
||||
match = orb_id+orb_id_vehicle_attitude_controls_topic
|
||||
|
||||
# match has the form: '[ORB_ID(]<topic_name>'
|
||||
if match.startswith(orb_id):
|
||||
topic_name = match[len(orb_id):]
|
||||
self._add_topic(topic_name, file_name, module)
|
||||
else:
|
||||
ignore_found = False
|
||||
for module_match, file_match_re, _, ignore_re in self._special_cases:
|
||||
if module == module_match:
|
||||
if file_match_re.search(file_name):
|
||||
if ignore_re.search(match):
|
||||
ignore_found = True
|
||||
if not ignore_found:
|
||||
# If we land here, we need to add another special case
|
||||
raise Exception(self._method+' w/o ORB_ID(): '+match+' in '
|
||||
+file_name+' ('+module+'). You need to add another special case.')
|
||||
|
||||
# handle special cases
|
||||
for i, (module_match, file_match_re, src_match_re, _) in enumerate(self._special_cases):
|
||||
if src_match_re is None:
|
||||
continue
|
||||
if module == module_match:
|
||||
if file_match_re.search(file_name):
|
||||
matches = src_match_re.findall(src_str)
|
||||
for match in matches:
|
||||
# match has the form: '[ORB_ID(]<topic_name>'
|
||||
if match.startswith(orb_id):
|
||||
topic_name = match[len(orb_id):]
|
||||
dbg_print('Found '+self._method+' for special case in '
|
||||
+module+': '+topic_name)
|
||||
self._add_topic(topic_name, file_name, module)
|
||||
self._special_cases_matched[i] = True
|
||||
else:
|
||||
# this is not fatal, as it could be a method delaration/definition
|
||||
dbg_print('Special case '+self._method+' w/o ORB_ID(): '
|
||||
+match+' in '+file_name+' ('+module+')')
|
||||
|
||||
def _add_topic(self, topic_name, file_name, module):
|
||||
""" add a subscription/publication for a module
|
||||
"""
|
||||
if topic_name in self._topic_blacklist:
|
||||
dbg_print('ignoring blacklisted topic '+topic_name)
|
||||
return
|
||||
|
||||
if module is None:
|
||||
if not file_name.endswith('hott/messages.cpp'): # hott has a special module structure. just ignore it
|
||||
print('Warning: found '+self._method+' without associated module: '
|
||||
+topic_name+' in '+file_name)
|
||||
return
|
||||
|
||||
if not module in self._module_pubsubs:
|
||||
self._module_pubsubs[module] = set()
|
||||
self._module_pubsubs[module].add(topic_name)
|
||||
|
||||
|
||||
def get_topics(self, modules):
|
||||
""" get the set of topics
|
||||
:param modules: list of modules to take into account
|
||||
"""
|
||||
topics = set()
|
||||
for module in modules:
|
||||
if module in self._module_pubsubs:
|
||||
topics |= self._module_pubsubs[module]
|
||||
return topics
|
||||
|
||||
@property
|
||||
def pubsubs(self):
|
||||
""" get dict of all publication/subscriptions (key=modules, value=set of
|
||||
topic names"""
|
||||
return self._module_pubsubs
|
||||
|
||||
|
||||
class Graph:
|
||||
""" Collects Node and Edge information by parsing the source tree """
|
||||
def __init__(self, module_whitelist=[], topic_blacklist=[]):
|
||||
self._current_module = [] # stack with current module (they can be nested)
|
||||
self._all_modules = set() # set of all found modules
|
||||
|
||||
self._comment_remove_pattern = re.compile(
|
||||
r'//.*?$|/\*.*?\*/|\'(?:\\.|[^\\\'])*\'|"(?:\\.|[^\\"])*"',
|
||||
re.DOTALL | re.MULTILINE)
|
||||
self._whitespace_pattern = re.compile(r'\s+')
|
||||
self._module_whitelist = module_whitelist
|
||||
self._excluded_paths = []
|
||||
|
||||
self._orb_id_vehicle_attitude_controls_topic = 'actuator_controls_0'
|
||||
self._orb_id_vehicle_attitude_controls_re = \
|
||||
re.compile(r'\#define\s+ORB_ID_VEHICLE_ATTITUDE_CONTROLS\s+([^,)]+)')
|
||||
|
||||
self._module_subscriptions = {} # key = module name, value = set of topic names
|
||||
self._module_publications = {} # key = module name, value = set of topic names
|
||||
|
||||
self._modules = set() # all modules
|
||||
self._topics = set() # all topics
|
||||
self._topic_colors = {} # key = topic, value = color (html string)
|
||||
|
||||
# handle special cases
|
||||
# format: list of tuples with 4 entries:
|
||||
# - module name to match (module MAIN)
|
||||
# - regex for file name(s) to match within the module (matched against the full path)
|
||||
# - regex to extract the topic name: the match must be ORB_ID(<topic_name>
|
||||
# Note: whitespace is removed from source code, so it does not need to be
|
||||
# accounted for in the regex.
|
||||
# If this is None, it will just be ignored
|
||||
# - regex to ignore matches in the form orb_[subscribe|advertise](<match>
|
||||
# (the expectation is that the previous matching ORB_ID() will be passed
|
||||
# to this, so that we can ignore it)
|
||||
special_cases_sub = [
|
||||
('sensors', r'voted_sensors_update\.cpp$', r'\binit_sensor_class\b\(([^,)]+)', r'^meta$'),
|
||||
('mavlink', r'.*', r'\badd_orb_subscription\b\(([^,)]+)', r'^_topic$'),
|
||||
('sdlog2', r'.*', None, r'^topic$'),
|
||||
('logger', r'.*', None, r'^(topic|sub\.metadata|_polling_topic_meta)$'),
|
||||
|
||||
('uavcan', r'uavcan_main\.cpp$', r'\b_control_topics\[[0-9]\]=([^,)]+)', r'^_control_topics\[i\]$'),
|
||||
('tap_esc', r'.*', r'\b_control_topics\[[0-9]\]=([^,)]+)', r'^_control_topics\[i\]$'),
|
||||
('pwm_out_sim', r'.*', r'\b_control_topics\[[0-9]\]=([^,)]+)', r'^_control_topics\[i\]$'),
|
||||
('snapdragon_pwm_out', r'.*', r'\b_controls_topics\[[0-9]\]=([^,)]+)', r'^_controls_topics\[i\]$'),
|
||||
('fmu', r'.*', r'\b_control_topics\[[0-9]\]=([^,)]+)', r'^_control_topics\[i\]$'),
|
||||
('linux_pwm_out', r'.*', r'\b_controls_topics\[[0-9]\]=([^,)]+)', r'^_controls_topics\[i\]$'),
|
||||
]
|
||||
special_cases_sub = [(a, re.compile(b), re.compile(c) if c is not None else None, re.compile(d))
|
||||
for a,b,c,d in special_cases_sub]
|
||||
|
||||
self._subscriptions = PubSub(False, topic_blacklist,
|
||||
[r"\borb_subscribe(_multi|)\b\(([^,)]+)"],
|
||||
special_cases_sub)
|
||||
|
||||
|
||||
special_cases_pub = [
|
||||
('replay', r'replay_main\.cpp$', None, r'^sub\.orb_meta$'),
|
||||
('fw_pos_control_l1', r'FixedwingPositionControl\.cpp$', r'\b_attitude_setpoint_id=([^,)]+)', r'^_attitude_setpoint_id$'),
|
||||
|
||||
('mc_pos_control', r'mc_pos_control_main\.cpp$', r'\b_attitude_setpoint_id=([^,)]+)', r'^_attitude_setpoint_id$'),
|
||||
|
||||
('mc_att_control', r'mc_att_control_main\.cpp$', r'\b_rates_sp_id=([^,)]+)', r'^_rates_sp_id$'),
|
||||
('mc_att_control', r'mc_att_control_main\.cpp$', r'\b_actuators_id=([^,)]+)', r'^_actuators_id$'),
|
||||
|
||||
('fw_att_control', r'fw_att_control_main\.cpp$', r'\b_rates_sp_id=([^,)]+)', r'^_rates_sp_id$'),
|
||||
('fw_att_control', r'fw_att_control_main\.cpp$', r'\b_actuators_id=([^,)]+)', r'^_actuators_id$'),
|
||||
('fw_att_control', r'fw_att_control_main\.cpp$', r'\b_attitude_setpoint_id=([^,)]+)', r'^_attitude_setpoint_id$'),
|
||||
|
||||
('uavcan', r'sensors/.*\.cpp$', r'\bUavcanCDevSensorBridgeBase\([^{]*DEVICE_PATH,([^,)]+)', r'^_orb_topic$'),
|
||||
('batt_smbus', r'batt_smbus\.cpp$', r'\b_batt_orb_id=([^,)]+)', r'^_batt_orb_id$'),
|
||||
]
|
||||
special_cases_pub = [(a, re.compile(b), re.compile(c) if c is not None else None, re.compile(d))
|
||||
for a,b,c,d in special_cases_pub]
|
||||
self._publications = PubSub(True, topic_blacklist,
|
||||
[r"\borb_advertise(_multi|_queue|_multi_queue|)\b\(([^,)]+)",
|
||||
r"\borb_publish_auto()\b\(([^,)]+)"],
|
||||
special_cases_pub)
|
||||
|
||||
|
||||
def _get_current_module(self):
|
||||
if len(self._current_module) == 0:
|
||||
return None
|
||||
return self._current_module[-1]
|
||||
|
||||
def build(self, src_path_list, excluded_paths=[], use_topic_pubsub_union=True):
|
||||
""" parse the source tree & extract pub/sub information.
|
||||
:param use_topic_pubsub_union: if true, use all topics that have a
|
||||
publisher or subscriber. If false, use only topics with at least one
|
||||
publisher and subscriber.
|
||||
|
||||
fill in self._module_subsciptions & self._module_publications
|
||||
"""
|
||||
|
||||
self._subscriptions.reset()
|
||||
self._publications.reset()
|
||||
|
||||
self._excluded_paths = [os.path.normpath(p) for p in excluded_paths]
|
||||
for path in src_path_list:
|
||||
self._build_recursive(path)
|
||||
|
||||
# filter by whitelist
|
||||
if len(self._module_whitelist) > 0:
|
||||
self._subscriptions.filter_modules(self._module_whitelist)
|
||||
self._publications.filter_modules(self._module_whitelist)
|
||||
|
||||
# modules & topics sets
|
||||
self._modules = set(self._publications.pubsubs.keys() +
|
||||
self._subscriptions.pubsubs.keys())
|
||||
print('number of modules: '+str(len(self._modules)))
|
||||
self._topics = self._get_topics(use_topic_pubsub_union=use_topic_pubsub_union)
|
||||
print('number of topics: '+str(len(self._topics)))
|
||||
|
||||
# initialize colors
|
||||
color_list = get_N_colors(len(self._topics), 0.7, 0.85)
|
||||
self._topic_colors = {}
|
||||
for i, topic in enumerate(self._topics):
|
||||
self._topic_colors[topic] = color_list[i]
|
||||
|
||||
|
||||
# validate that all special rules got used
|
||||
self._subscriptions.check_if_match_found(self._all_modules)
|
||||
self._publications.check_if_match_found(self._all_modules)
|
||||
|
||||
|
||||
def _get_topics(self, use_topic_pubsub_union=True):
|
||||
""" get the set of topics
|
||||
"""
|
||||
subscribed_topics = self._subscriptions.get_topics(self._modules)
|
||||
published_topics = self._publications.get_topics(self._modules)
|
||||
if use_topic_pubsub_union:
|
||||
return subscribed_topics | published_topics
|
||||
return subscribed_topics & published_topics
|
||||
|
||||
def _build_recursive(self, path):
|
||||
|
||||
if os.path.normpath(path) in self._excluded_paths:
|
||||
dbg_print('ignoring excluded path '+path)
|
||||
return
|
||||
|
||||
entries = os.listdir(path)
|
||||
|
||||
# check if entering a new module
|
||||
cmake_file = 'CMakeLists.txt'
|
||||
new_module = False
|
||||
if cmake_file in entries:
|
||||
new_module = self._extract_module_name(os.path.join(path, cmake_file))
|
||||
|
||||
# iterate directories recursively
|
||||
for entry in entries:
|
||||
file_name = os.path.join(path, entry)
|
||||
if os.path.isdir(file_name):
|
||||
self._build_recursive(file_name)
|
||||
|
||||
|
||||
# iterate source files
|
||||
# Note: we could skip the entries if we're not in a module, but we don't
|
||||
# so that we get appropriate error messages to know where we miss subs
|
||||
# or pubs
|
||||
for entry in entries:
|
||||
file_name = os.path.join(path, entry)
|
||||
if os.path.isfile(file_name):
|
||||
_, ext = os.path.splitext(file_name)
|
||||
if ext in ['.cpp', '.c', '.h', '.hpp']:
|
||||
self._process_source_file(file_name)
|
||||
|
||||
|
||||
if new_module:
|
||||
self._current_module.pop()
|
||||
|
||||
|
||||
def _extract_module_name(self, file_name):
|
||||
""" extract the module name from a CMakeLists.txt file and store
|
||||
in self._current_module if there is any """
|
||||
datafile = file(file_name)
|
||||
found_module_def = False
|
||||
for line in datafile:
|
||||
if 'px4_add_module' in line: # must contain 'px4_add_module'
|
||||
found_module_def = True
|
||||
|
||||
words = line.split()
|
||||
# get the definition of MAIN
|
||||
if found_module_def and 'MAIN' in words and len(words) >= 2:
|
||||
self._current_module.append(words[1])
|
||||
self._all_modules.add(words[1])
|
||||
dbg_print('Found module name: '+words[1])
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def _process_source_file(self, file_name):
|
||||
""" extract information from a single source file """
|
||||
|
||||
with codecs.open(file_name, 'r', 'utf-8') as f:
|
||||
try:
|
||||
content = f.read()
|
||||
except:
|
||||
print('Failed reading file: %s, skipping content.' % path)
|
||||
return
|
||||
|
||||
|
||||
current_module = self._get_current_module()
|
||||
if current_module == 'uorb_tests': # skip this
|
||||
return
|
||||
if current_module == 'uorb':
|
||||
|
||||
# search and validate the ORB_ID_VEHICLE_ATTITUDE_CONTROLS define
|
||||
matches = self._orb_id_vehicle_attitude_controls_re.findall(content)
|
||||
for match in matches:
|
||||
if match != 'ORB_ID('+self._orb_id_vehicle_attitude_controls_topic:
|
||||
# if we land here, you need to change _orb_id_vehicle_attitude_controls_topic
|
||||
raise Exception(
|
||||
'The extracted define for ORB_ID_VEHICLE_ATTITUDE_CONTROLS '
|
||||
'is '+match+' but expected ORB_ID('+
|
||||
self._orb_id_vehicle_attitude_controls_topic)
|
||||
|
||||
return # skip uorb module for the rest
|
||||
|
||||
|
||||
|
||||
if content.lower().find('orb_') != -1: # approximative filter to quickly
|
||||
# discard files we're not interested in
|
||||
# (speedup the parsing)
|
||||
src = self._comment_remover(content)
|
||||
src = re.sub(self._whitespace_pattern, '', src) # remove all whitespace
|
||||
|
||||
|
||||
# subscriptions
|
||||
self._subscriptions.extract(file_name, src, current_module,
|
||||
self._orb_id_vehicle_attitude_controls_topic)
|
||||
|
||||
# publications
|
||||
self._publications.extract(file_name, src, current_module,
|
||||
self._orb_id_vehicle_attitude_controls_topic)
|
||||
|
||||
# TODO: handle Publication & Subscription template classes
|
||||
|
||||
|
||||
|
||||
def _comment_remover(self, text):
|
||||
""" remove C++ & C style comments.
|
||||
Source: https://stackoverflow.com/a/241506 """
|
||||
def replacer(match):
|
||||
s = match.group(0)
|
||||
if s.startswith('/'):
|
||||
return " " # note: a space and not an empty string
|
||||
else:
|
||||
return s
|
||||
return re.sub(self._comment_remove_pattern, replacer, text)
|
||||
|
||||
|
||||
@property
|
||||
def modules(self):
|
||||
""" get the set of all modules """
|
||||
return self._modules
|
||||
|
||||
@property
|
||||
def topics(self):
|
||||
""" get set set of all topics """
|
||||
return self._topics
|
||||
|
||||
@property
|
||||
def topic_colors(self):
|
||||
""" get a dict of all topic colors with key=topic, value=color """
|
||||
return self._topic_colors
|
||||
|
||||
@property
|
||||
def module_subscriptions(self):
|
||||
""" get a dict of all subscriptions with key=module name, value=set(topic names) """
|
||||
return self._subscriptions.pubsubs
|
||||
|
||||
@property
|
||||
def module_publications(self):
|
||||
""" get a dict of all publications with key=module name, value=set(topic names) """
|
||||
return self._publications.pubsubs
|
||||
|
||||
|
||||
|
||||
class OutputGraphviz:
|
||||
""" write graph using Graphviz """
|
||||
|
||||
def __init__(self, graph):
|
||||
self._graph = graph
|
||||
|
||||
def write(self, file_name, engine='fdp',
|
||||
show_publications=True, show_subscriptions=True):
|
||||
""" write the graph to a file
|
||||
:param engine: graphviz engine
|
||||
- fdp works for large graphs
|
||||
- neato works better for smaller graphs
|
||||
- circo works for single modules
|
||||
CLI: fdp graph.fv -Tpdf -o test.pdf
|
||||
"""
|
||||
|
||||
print('Writing to '+file_name)
|
||||
|
||||
ratio = 1 # aspect ratio
|
||||
|
||||
modules = self._graph.modules
|
||||
topics = self._graph.topics
|
||||
topic_colors = self._graph.topic_colors
|
||||
module_publications = self._graph.module_publications
|
||||
module_subscriptions = self._graph.module_subscriptions
|
||||
|
||||
graph_attr={'splines': 'true', 'ratio': str(ratio), 'overlap': 'false'}
|
||||
graph_attr['sep'] = '"+15,15"' # increase spacing between nodes
|
||||
graph = Digraph(comment='autogenerated graph with graphviz using uorb_graph.py',
|
||||
engine=engine, graph_attr=graph_attr)
|
||||
|
||||
|
||||
# nodes
|
||||
for module in modules:
|
||||
graph.node('m_'+module, module, shape='box', fontcolor='#ffffff',
|
||||
style='filled', color='#666666', fontsize='16')
|
||||
|
||||
for topic in topics:
|
||||
graph.node('t_'+topic, topic, shape='ellipse', fontcolor='#ffffff',
|
||||
style='filled', color=topic_colors[topic])
|
||||
|
||||
|
||||
# edges
|
||||
if show_publications:
|
||||
for module in modules:
|
||||
if module in module_publications:
|
||||
for topic in module_publications[module]:
|
||||
if topic in topics:
|
||||
graph.edge('m_'+module, 't_'+topic,
|
||||
color=topic_colors[topic], style='dashed')
|
||||
|
||||
if show_subscriptions:
|
||||
for module in modules:
|
||||
if module in module_subscriptions:
|
||||
for topic in module_subscriptions[module]:
|
||||
if topic in topics:
|
||||
graph.edge('t_'+topic, 'm_'+module,
|
||||
color=topic_colors[topic])
|
||||
|
||||
graph.render(file_name, view=False)
|
||||
|
||||
|
||||
class OutputJSON:
|
||||
""" write graph to a JSON file (that can be used with D3.js) """
|
||||
|
||||
def __init__(self, graph):
|
||||
self._graph = graph
|
||||
|
||||
def write(self, file_name):
|
||||
|
||||
print('Writing to '+file_name)
|
||||
|
||||
modules = self._graph.modules
|
||||
topics = self._graph.topics
|
||||
topic_colors = self._graph.topic_colors
|
||||
module_publications = self._graph.module_publications
|
||||
module_subscriptions = self._graph.module_subscriptions
|
||||
|
||||
data = {}
|
||||
nodes = []
|
||||
|
||||
# nodes
|
||||
# (sort by length, such that short names are last. The rendering order
|
||||
# will be the same, so that in case of an overlap, the shorter label
|
||||
# will be on top)
|
||||
for module in sorted(modules, key=len, reverse=True):
|
||||
node = {}
|
||||
node['id'] = 'm_'+module
|
||||
node['name'] = module
|
||||
node['type'] = 'module'
|
||||
node['color'] = '#666666'
|
||||
# TODO: add url to open module documentation?
|
||||
nodes.append(node)
|
||||
|
||||
for topic in sorted(topics, key=len, reverse=True):
|
||||
node = {}
|
||||
node['id'] = 't_'+topic
|
||||
node['name'] = topic
|
||||
node['type'] = 'topic'
|
||||
node['color'] = topic_colors[topic]
|
||||
# url is opened when double-clicking on the node
|
||||
# TODO: does not work for multi-topics
|
||||
node['url'] = 'https://github.com/PX4/Firmware/blob/master/msg/'+topic+'.msg'
|
||||
nodes.append(node)
|
||||
|
||||
data['nodes'] = nodes
|
||||
|
||||
edges = []
|
||||
|
||||
# edges
|
||||
for module in modules:
|
||||
if module in module_publications:
|
||||
for topic in module_publications[module]:
|
||||
if topic in topics:
|
||||
edge = {}
|
||||
edge['source'] = 'm_'+module
|
||||
edge['target'] = 't_'+topic
|
||||
edge['color'] = topic_colors[topic]
|
||||
edge['style'] = 'dashed'
|
||||
edges.append(edge)
|
||||
|
||||
for module in modules:
|
||||
if module in module_subscriptions:
|
||||
for topic in module_subscriptions[module]:
|
||||
if topic in topics:
|
||||
edge = {}
|
||||
edge['source'] = 't_'+topic
|
||||
edge['target'] = 'm_'+module
|
||||
edge['color'] = topic_colors[topic]
|
||||
edge['style'] = 'normal'
|
||||
edges.append(edge)
|
||||
|
||||
data['links'] = edges
|
||||
|
||||
with open(file_name, 'w') as outfile:
|
||||
json.dump(data, outfile) # add indent=2 for readable formatting
|
||||
|
||||
|
||||
|
||||
# ignore topics that are subscribed/published by many topics, but are not really
|
||||
# useful to show in the graph
|
||||
topic_blacklist = [ 'parameter_update', 'mavlink_log', 'log_message' ]
|
||||
print('Excluded topics: '+str(topic_blacklist))
|
||||
|
||||
if len(args.modules) == 0:
|
||||
module_whitelist = []
|
||||
else:
|
||||
module_whitelist = [ m.strip() for m in args.modules.split(',')]
|
||||
|
||||
graph = Graph(module_whitelist=module_whitelist, topic_blacklist=topic_blacklist)
|
||||
if len(args.src_path) == 0:
|
||||
args.src_path = ['src']
|
||||
graph.build(args.src_path, args.exclude_path, use_topic_pubsub_union=args.use_topic_union)
|
||||
|
||||
|
||||
if args.output == 'json':
|
||||
output_json = OutputJSON(graph)
|
||||
output_json.write(args.file+'.json')
|
||||
|
||||
elif args.output == 'graphviz':
|
||||
try:
|
||||
from graphviz import Digraph
|
||||
except:
|
||||
print("Failed to import graphviz.")
|
||||
print("You may need to install it with 'pip install graphviz'")
|
||||
print("")
|
||||
raise
|
||||
output_graphviz = OutputGraphviz(graph)
|
||||
engine='fdp' # use neato or fdp
|
||||
output_graphviz.write(args.file+'.fv', engine=engine)
|
||||
output_graphviz.write(args.file+'_subs.fv', show_publications=False, engine=engine)
|
||||
output_graphviz.write(args.file+'_pubs.fv', show_subscriptions=False, engine=engine)
|
||||
else:
|
||||
print('Error: unknown output format '+args.output)
|
||||
|
||||
|
||||
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
#! /bin/bash
|
||||
# create the graph from a posix (e.g. SITL) startup script
|
||||
|
||||
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
|
||||
startup_file="$SCRIPT_DIR"/../../posix-configs/SITL/init/ekf2/typhoon_h480
|
||||
[ -n "$1" ] && startup_file=$1
|
||||
# get the modules as comma-separated list
|
||||
modules=$(cat "$startup_file"|cut -f1 -d' '|sort|uniq|tr '\n' ,)
|
||||
|
||||
cd "$SCRIPT_DIR/../.."
|
||||
"$SCRIPT_DIR"/create.py --src-path src -m "$modules" -f "$SCRIPT_DIR/graph_runtime_sitl"
|
||||
|
||||
Vendored
-131
@@ -1,131 +0,0 @@
|
||||
# -*- mode: ruby -*-
|
||||
# vi: set ft=ruby :
|
||||
|
||||
# All Vagrant configuration is done below. The "2" in Vagrant.configure
|
||||
# configures the configuration version (we support older styles for
|
||||
# backwards compatibility). Please don't change it unless you know what
|
||||
# you're doing.
|
||||
Vagrant.configure(2) do |config|
|
||||
|
||||
# Every Vagrant development environment requires a box. You can search for
|
||||
# boxes at https://atlas.hashicorp.com/search.
|
||||
config.vm.box = "ubuntu/trusty64"
|
||||
|
||||
# Disable automatic box update checking. If you disable this, then
|
||||
# boxes will only be checked for updates when the user runs
|
||||
# `vagrant box outdated`. This is not recommended.
|
||||
# config.vm.box_check_update = false
|
||||
|
||||
# Create a forwarded port mapping which allows access to a specific port
|
||||
# within the machine from a port on the host machine. In the example below,
|
||||
# accessing "localhost:8080" will access port 80 on the guest machine.
|
||||
# MAVLink telemetry via UDP in SITL mode
|
||||
config.vm.network "forwarded_port", guest: 14556, host: 14556, protocol: "udp"
|
||||
# SITL simulation data
|
||||
config.vm.network "forwarded_port", guest: 14560, host: 14560, protocol: "udp"
|
||||
|
||||
# Create a private network, which allows host-only access to the machine
|
||||
# using a specific IP.
|
||||
config.vm.network "private_network", ip: "192.168.33.10"
|
||||
|
||||
# Create a public network, which generally matched to bridged network.
|
||||
# Bridged networks make the machine appear as another physical device on
|
||||
# your network.
|
||||
# config.vm.network "public_network"
|
||||
|
||||
# Share an additional folder to the guest VM. The first argument is
|
||||
# the path on the host to the actual folder. The second argument is
|
||||
# the path on the guest to mount the folder. And the optional third
|
||||
# argument is a set of non-required options.
|
||||
# NFS should be faster: https://stefanwrobel.com/how-to-make-vagrant-performance-not-suck
|
||||
config.vm.synced_folder ".", "/Firmware", type: "nfs"
|
||||
|
||||
# Provider-specific configuration so you can fine-tune various
|
||||
# backing providers for Vagrant. These expose provider-specific options.
|
||||
# Example for VirtualBox:
|
||||
#
|
||||
|
||||
# This is to configure the machine to be as fast as possible
|
||||
# Alternative: https://github.com/rdsubhas/vagrant-faster
|
||||
config.vm.provider "virtualbox" do |vb|
|
||||
# Display the VirtualBox GUI when booting the machine
|
||||
vb.gui = false
|
||||
vb.customize ["modifyvm", :id, "--ioapic", "on"]
|
||||
#vb.customize ["modifyvm", :id, "--cpus", "2"]
|
||||
|
||||
config.vm.provider "virtualbox" do |v|
|
||||
host = RbConfig::CONFIG['host_os']
|
||||
|
||||
# Give VM 1/4 system memory & access to all cpu cores on the host
|
||||
if host =~ /darwin/
|
||||
cpus = `sysctl -n hw.ncpu`.to_i
|
||||
# sysctl returns Bytes and we need to convert to MB
|
||||
mem = `sysctl -n hw.memsize`.to_i / 1024 / 1024 / 4
|
||||
elsif host =~ /linux/
|
||||
cpus = `nproc`.to_i
|
||||
# meminfo shows KB and we need to convert to MB
|
||||
mem = `grep 'MemTotal' /proc/meminfo | sed -e 's/MemTotal://' -e 's/ kB//'`.to_i / 1024 / 4
|
||||
else # sorry Windows folks, I can't help you
|
||||
cpus = 2
|
||||
mem = 1024
|
||||
end
|
||||
|
||||
v.customize ["modifyvm", :id, "--memory", mem]
|
||||
v.customize ["modifyvm", :id, "--cpus", cpus]
|
||||
end
|
||||
|
||||
# Since make and other tools freak out if they see timestamps
|
||||
# from the future and we share directories, tightly lock the host and guest clocks together (clock sync if more than 2 seconds off)
|
||||
vb.customize ["guestproperty", "set", :id, "/VirtualBox/GuestAdd/VBoxService/--timesync-set-threshold", 2000]
|
||||
# Do this on start and restore
|
||||
vb.customize ["guestproperty", "set", :id, "/VirtualBox/GuestAdd/VBoxService/--timesync-set-start"]
|
||||
vb.customize ["guestproperty", "set", :id, "/VirtualBox/GuestAdd/VBoxService/--timesync-set-on-restore", "1"]
|
||||
|
||||
# Customize the amount of memory on the VM:
|
||||
#vb.memory = "2048"
|
||||
end
|
||||
#
|
||||
# View the documentation for the provider you are using for more
|
||||
# information on available options.
|
||||
|
||||
# Define a Vagrant Push strategy for pushing to Atlas. Other push strategies
|
||||
# such as FTP and Heroku are also available. See the documentation at
|
||||
# https://docs.vagrantup.com/v2/push/atlas.html for more information.
|
||||
# config.push.define "atlas" do |push|
|
||||
# push.app = "YOUR_ATLAS_USERNAME/YOUR_APPLICATION_NAME"
|
||||
# end
|
||||
|
||||
# Enable provisioning with a shell script. Additional provisioners such as
|
||||
# Puppet, Chef, Ansible, Salt, and Docker are also available. Please see the
|
||||
# documentation for more information about their specific syntax and use.
|
||||
config.vm.provision "shell", privileged: false, inline: <<-SHELL
|
||||
# Ensure we start in the Firmware folder
|
||||
echo "cd /Firmware" >> ~/.bashrc
|
||||
# Install software
|
||||
sudo add-apt-repository ppa:terry.guo/gcc-arm-embedded -y
|
||||
sudo add-apt-repository ppa:george-edison55/cmake-3.x -y
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y build-essential ccache cmake clang-3.5 lldb-3.5 g++-4.8 gcc-4.8 genromfs libc6-i386 libncurses5-dev python-argparse python-empy python-serial s3cmd texinfo zlib1g-dev git-core zip gdb gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf
|
||||
pushd .
|
||||
cd ~
|
||||
wget -q https://launchpadlibrarian.net/186124160/gcc-arm-none-eabi-4_8-2014q3-20140805-linux.tar.bz2
|
||||
tar -jxf gcc-arm-none-eabi-4_8-2014q3-20140805-linux.tar.bz2
|
||||
exportline="export PATH=$HOME/gcc-arm-none-eabi-4_8-2014q3/bin:\$PATH"
|
||||
if grep -Fxq "$exportline" ~/.profile; then echo nothing to do ; else echo $exportline >> ~/.profile; fi
|
||||
exportline2="export HEXAGON_TOOLS_ROOT=$HOME/Qualcomm/HEXAGON_Tools/7.2.10/Tools"
|
||||
if grep -Fxq "$exportline2" ~/.profile; then echo nothing to do ; else echo $exportline2 >> ~/.profile; fi
|
||||
. ~/.profile
|
||||
popd
|
||||
# setup ccache
|
||||
mkdir -p ~/bin
|
||||
ln -s /usr/bin/ccache ~/bin/arm-none-eabi-g++
|
||||
ln -s /usr/bin/ccache ~/bin/arm-none-eabi-gcc
|
||||
ln -s /usr/bin/ccache ~/bin/g++-4.8
|
||||
ln -s /usr/bin/ccache ~/bin/gcc-4.8
|
||||
export PATH=~/bin:$PATH
|
||||
|
||||
# Configure hardware related bits
|
||||
sudo apt-get -y remove modemmanager
|
||||
sudo usermod -a -G dialout $USER
|
||||
SHELL
|
||||
end
|
||||
@@ -1,4 +1,3 @@
|
||||
include(nuttx/px4_impl_nuttx)
|
||||
|
||||
px4_nuttx_configure(HWCLASS m4 CONFIG nsh ROMFS y ROMFSROOT px4fmu_common)
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
include(nuttx/px4_impl_nuttx)
|
||||
|
||||
px4_nuttx_configure(HWCLASS m4 CONFIG nsh ROMFS y ROMFSROOT px4fmu_common)
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
include(nuttx/px4_impl_nuttx)
|
||||
|
||||
px4_nuttx_configure(HWCLASS m4 CONFIG nsh ROMFS y ROMFSROOT px4fmu_common IO px4io-v2)
|
||||
|
||||
@@ -49,6 +48,7 @@ set(config_module_list
|
||||
drivers/bst
|
||||
drivers/snapdragon_rc_pwm
|
||||
drivers/lis3mdl
|
||||
drivers/tfmini
|
||||
|
||||
#
|
||||
# System commands
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
include(nuttx/px4_impl_nuttx)
|
||||
|
||||
px4_nuttx_configure(HWCLASS m4 CONFIG nsh ROMFS y ROMFSROOT px4fmu_common)
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
include(nuttx/px4_impl_nuttx)
|
||||
|
||||
add_definitions(
|
||||
-DFLASH_BASED_PARAMS
|
||||
@@ -28,7 +27,7 @@ add_definitions(
|
||||
)
|
||||
|
||||
px4_nuttx_make_uavcan_bootloadable(BOARD ${BOARD}
|
||||
BIN ${CMAKE_CURRENT_BINARY_DIR}/src/firmware/nuttx/esc35-v1.bin
|
||||
BIN ${PX4_BINARY_DIR}/platforms/nuttx/esc35-v1.bin
|
||||
HWNAME ${uavcanblid_name}
|
||||
HW_MAJOR ${uavcanblid_hw_version_major}
|
||||
HW_MINOR ${uavcanblid_hw_version_minor}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
include(nuttx/px4_impl_nuttx)
|
||||
|
||||
px4_nuttx_configure(HWCLASS m4 CONFIG nsh ROMFS y ROMFSROOT px4fmu_common)
|
||||
|
||||
@@ -52,6 +51,7 @@ set(config_module_list
|
||||
drivers/camera_trigger
|
||||
drivers/bst
|
||||
drivers/snapdragon_rc_pwm
|
||||
drivers/tfmini
|
||||
|
||||
#
|
||||
# System commands
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
include(nuttx/px4_impl_nuttx)
|
||||
|
||||
px4_nuttx_configure(HWCLASS m4 CONFIG nsh ROMFS y ROMFSROOT px4fmu_common)
|
||||
|
||||
@@ -59,6 +58,7 @@ set(config_module_list
|
||||
drivers/teraranger
|
||||
drivers/vmount
|
||||
modules/sensors
|
||||
drivers/tfmini
|
||||
|
||||
#
|
||||
# System commands
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
include(nuttx/px4_impl_nuttx)
|
||||
|
||||
px4_nuttx_configure(HWCLASS m7 CONFIG nsh ROMFS y ROMFSROOT px4fmu_common)
|
||||
|
||||
@@ -48,6 +47,8 @@ set(config_module_list
|
||||
drivers/bst
|
||||
drivers/snapdragon_rc_pwm
|
||||
drivers/lis3mdl
|
||||
drivers/tfmini
|
||||
|
||||
|
||||
#
|
||||
# System commands
|
||||
@@ -167,4 +168,4 @@ set(config_module_list
|
||||
|
||||
# Hardware test
|
||||
#examples/hwtest
|
||||
)
|
||||
)
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
include(nuttx/px4_impl_nuttx)
|
||||
|
||||
px4_nuttx_configure(HWCLASS m4 CONFIG nsh ROMFS y ROMFSROOT px4fmu_common)
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
include(nuttx/px4_impl_nuttx)
|
||||
|
||||
add_definitions(
|
||||
-DPARAM_NO_ORB
|
||||
@@ -26,7 +25,7 @@ add_definitions(
|
||||
)
|
||||
|
||||
px4_nuttx_make_uavcan_bootloadable(BOARD ${BOARD}
|
||||
BIN ${PX4_BINARY_DIR}/src/firmware/nuttx/px4cannode-v1.bin
|
||||
BIN ${PX4_BINARY_DIR}/platforms/nuttx/px4cannode-v1.bin
|
||||
HWNAME ${uavcanblid_name}
|
||||
HW_MAJOR ${uavcanblid_hw_version_major}
|
||||
HW_MINOR ${uavcanblid_hw_version_minor}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
include(nuttx/px4_impl_nuttx)
|
||||
|
||||
add_definitions(
|
||||
-DFLASH_BASED_PARAMS
|
||||
@@ -28,7 +27,7 @@ add_definitions(
|
||||
)
|
||||
|
||||
px4_nuttx_make_uavcan_bootloadable(BOARD ${BOARD}
|
||||
BIN ${PX4_BINARY_DIR}/src/firmware/nuttx/px4esc-v1.bin
|
||||
BIN ${PX4_BINARY_DIR}/platforms/nuttx/px4esc-v1.bin
|
||||
HWNAME ${uavcanblid_name}
|
||||
HW_MAJOR ${uavcanblid_hw_version_major}
|
||||
HW_MINOR ${uavcanblid_hw_version_minor}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
include(nuttx/px4_impl_nuttx)
|
||||
px4_nuttx_configure(HWCLASS m4 CONFIG nsh ROMFS y ROMFSROOT px4fmu_common IO px4io-v2)
|
||||
|
||||
#set(config_uavcan_num_ifaces 2)
|
||||
@@ -59,6 +58,7 @@ set(config_module_list
|
||||
#drivers/ulanding
|
||||
drivers/vmount
|
||||
modules/sensors
|
||||
#drivers/tfmini
|
||||
|
||||
#
|
||||
# System commands
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
include(nuttx/px4_impl_nuttx)
|
||||
|
||||
px4_nuttx_configure(HWCLASS m4 CONFIG nsh ROMFS y ROMFSROOT px4fmu_test)
|
||||
|
||||
@@ -48,6 +47,7 @@ set(config_module_list
|
||||
#drivers/bst
|
||||
#drivers/snapdragon_rc_pwm
|
||||
#drivers/lis3mdl
|
||||
drivers/tfmini
|
||||
|
||||
#
|
||||
# System commands
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
# FMUv3 is FMUv2 with access to the full 2MB flash
|
||||
set(BOARD px4fmu-v2 CACHE string "" FORCE)
|
||||
set(FW_NAME nuttx_px4fmu-v3_default.elf CACHE string "" FORCE)
|
||||
set(FW_PROTOTYPE px4fmu-v3 CACHE string "" FORCE)
|
||||
set(LD_SCRIPT ld_full.script CACHE string "" FORCE)
|
||||
|
||||
include(nuttx/px4_impl_nuttx)
|
||||
px4_nuttx_configure(HWCLASS m4 CONFIG nsh ROMFS y ROMFSROOT px4fmu_common IO px4io-v2)
|
||||
|
||||
set(config_uavcan_num_ifaces 2)
|
||||
@@ -64,6 +64,7 @@ set(config_module_list
|
||||
drivers/ulanding
|
||||
drivers/vmount
|
||||
modules/sensors
|
||||
drivers/tfmini
|
||||
|
||||
#
|
||||
# System commands
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
include(nuttx/px4_impl_nuttx)
|
||||
|
||||
px4_nuttx_configure(HWCLASS m4 CONFIG nsh ROMFS y ROMFSROOT px4fmu_common)
|
||||
|
||||
@@ -56,6 +55,7 @@ set(config_module_list
|
||||
drivers/teraranger
|
||||
drivers/vmount
|
||||
modules/sensors
|
||||
drivers/tfmini
|
||||
|
||||
#
|
||||
# System commands
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
include(nuttx/px4_impl_nuttx)
|
||||
|
||||
px4_nuttx_configure(HWCLASS m4 CONFIG nsh ROMFS y ROMFSROOT px4fmu_common IO px4io-v2)
|
||||
|
||||
@@ -56,6 +55,7 @@ set(config_module_list
|
||||
drivers/teraranger
|
||||
drivers/vmount
|
||||
modules/sensors
|
||||
drivers/tfmini
|
||||
|
||||
#
|
||||
# System commands
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
include(nuttx/px4_impl_nuttx)
|
||||
|
||||
px4_nuttx_configure(HWCLASS m7 CONFIG nsh ROMFS y ROMFSROOT px4fmu_common)
|
||||
|
||||
@@ -56,6 +55,7 @@ set(config_module_list
|
||||
drivers/teraranger
|
||||
drivers/vmount
|
||||
modules/sensors
|
||||
drivers/tfmini
|
||||
|
||||
#
|
||||
# System commands
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
include(nuttx/px4_impl_nuttx)
|
||||
|
||||
px4_nuttx_configure(HWCLASS m3 CONFIG nsh)
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
include(nuttx/px4_impl_nuttx)
|
||||
|
||||
px4_nuttx_configure(HWCLASS m7 CONFIG nsh ROMFS y ROMFSROOT px4fmu_common)
|
||||
|
||||
@@ -50,6 +49,7 @@ set(config_module_list
|
||||
drivers/tap_esc
|
||||
drivers/teraranger
|
||||
modules/sensors
|
||||
drivers/tfmini
|
||||
|
||||
#
|
||||
# System commands
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
include(nuttx/px4_impl_nuttx)
|
||||
|
||||
add_definitions(
|
||||
-DPARAM_NO_ORB
|
||||
@@ -26,7 +25,7 @@ include(configs/uavcan_board_ident/s2740vc-v1)
|
||||
|
||||
# N.B. this would be uncommented when there is an APP
|
||||
#px4_nuttx_make_uavcan_bootloadable(BOARD ${BOARD}
|
||||
# BIN ${CMAKE_CURRENT_BINARY_DIR}/src/firmware/nuttx/s2740vc-v1.bin
|
||||
# BIN ${PX4_BINARY_DIR}/platforms/nuttx/s2740vc-v1.bin
|
||||
# HWNAME ${uavcanblid_name}
|
||||
# HW_MAJOR ${uavcanblid_hw_version_major}
|
||||
# HW_MINOR ${uavcanblid_hw_version_minor}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
include(nuttx/px4_impl_nuttx)
|
||||
|
||||
px4_nuttx_configure(HWCLASS m4 CONFIG nsh ROMFS y ROMFSROOT tap_common)
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
include(posix/px4_impl_posix)
|
||||
|
||||
set(CMAKE_TOOLCHAIN_FILE ${PX4_SOURCE_DIR}/cmake/toolchains/Toolchain-arm-linux-gnueabihf.cmake)
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
include(posix/px4_impl_posix)
|
||||
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${PX4_SOURCE_DIR}/cmake/cmake_hexagon")
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
include(posix/px4_impl_posix)
|
||||
|
||||
set(CMAKE_TOOLCHAIN_FILE ${PX4_SOURCE_DIR}/cmake/cmake_hexagon/toolchain/Toolchain-arm-linux-gnueabihf.cmake)
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@ set(CMAKE_PROGRAM_PATH
|
||||
${CMAKE_PROGRAM_PATH}
|
||||
)
|
||||
|
||||
include(posix/px4_impl_posix)
|
||||
|
||||
add_definitions(
|
||||
-D__PX4_POSIX_OCPOC
|
||||
|
||||
@@ -5,7 +5,6 @@ set(CMAKE_PROGRAM_PATH
|
||||
${CMAKE_PROGRAM_PATH}
|
||||
)
|
||||
|
||||
include(posix/px4_impl_posix)
|
||||
|
||||
add_definitions(
|
||||
-D__PX4_POSIX_OCPOC
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
# This file is shared between posix_rpi_native.cmake
|
||||
# and posix_rpi_cross.cmake.
|
||||
|
||||
include(posix/px4_impl_posix)
|
||||
|
||||
# This definition allows to differentiate if this just the usual POSIX build
|
||||
# or if it is for the RPi.
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
include(common/px4_git)
|
||||
px4_add_git_submodule(TARGET git_cmake_hexagon PATH "cmake/cmake_hexagon")
|
||||
|
||||
include(posix/px4_impl_posix)
|
||||
|
||||
# Get $QC_SOC_TARGET from environment if existing.
|
||||
if (DEFINED ENV{QC_SOC_TARGET})
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
include(common/px4_git)
|
||||
px4_add_git_submodule(TARGET git_cmake_hexagon PATH "cmake/cmake_hexagon")
|
||||
|
||||
include(posix/px4_impl_posix)
|
||||
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${PX4_SOURCE_DIR}/cmake/cmake_hexagon")
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
include(posix/px4_impl_posix)
|
||||
|
||||
set(CMAKE_TOOLCHAIN_FILE ${PX4_SOURCE_DIR}/cmake/toolchains/Toolchain-native.cmake)
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
include(posix/px4_impl_posix)
|
||||
|
||||
set(CMAKE_TOOLCHAIN_FILE ${PX4_SOURCE_DIR}/cmake/toolchains/Toolchain-native.cmake)
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
include(qurt/px4_impl_qurt)
|
||||
|
||||
if ("$ENV{HEXAGON_SDK_ROOT}" STREQUAL "")
|
||||
message(FATAL_ERROR "Enviroment variable HEXAGON_SDK_ROOT must be set")
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
include(qurt/px4_impl_qurt)
|
||||
|
||||
if ("$ENV{HEXAGON_SDK_ROOT}" STREQUAL "")
|
||||
message(FATAL_ERROR "Enviroment variable HEXAGON_SDK_ROOT must be set")
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
include(qurt/px4_impl_qurt)
|
||||
|
||||
if ("$ENV{HEXAGON_SDK_ROOT}" STREQUAL "")
|
||||
message(FATAL_ERROR "Enviroment variable HEXAGON_SDK_ROOT must be set")
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
include(qurt/px4_impl_qurt)
|
||||
|
||||
if ("$ENV{HEXAGON_SDK_ROOT}" STREQUAL "")
|
||||
message(FATAL_ERROR "Enviroment variable HEXAGON_SDK_ROOT must be set")
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
include(qurt/px4_impl_qurt)
|
||||
|
||||
set(CONFIG_SHMEM "1")
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
include(common/px4_git)
|
||||
px4_add_git_submodule(TARGET git_cmake_hexagon PATH "cmake/cmake_hexagon")
|
||||
|
||||
include(qurt/px4_impl_qurt)
|
||||
|
||||
if ("$ENV{HEXAGON_SDK_ROOT}" STREQUAL "")
|
||||
message(FATAL_ERROR "Enviroment variable HEXAGON_SDK_ROOT must be set")
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
include(common/px4_git)
|
||||
px4_add_git_submodule(TARGET git_cmake_hexagon PATH "cmake/cmake_hexagon")
|
||||
|
||||
include(qurt/px4_impl_qurt)
|
||||
|
||||
if ("$ENV{HEXAGON_SDK_ROOT}" STREQUAL "")
|
||||
message(FATAL_ERROR "Enviroment variable HEXAGON_SDK_ROOT must be set")
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
############################################################################
|
||||
#
|
||||
# Copyright (c) 2015 PX4 Development Team. All rights reserved.
|
||||
# Copyright (c) 2017 PX4 Development Team. All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
@@ -32,16 +32,19 @@
|
||||
############################################################################
|
||||
|
||||
#=============================================================================
|
||||
# FILE: posix/px4_target_impl.cmake
|
||||
#
|
||||
# Each PX4 target OS must implement the cmake/${OS}/px4_target_impl.cmake
|
||||
# rules for their target that implement the following macros:
|
||||
# Converts a cygwin path (/cygdrive/c/...) to a mixed windows path (C:/...)
|
||||
#
|
||||
# px4_target_set_flags
|
||||
# px4_target_validate_config
|
||||
# px4_target_firmware
|
||||
# px4_target_rules
|
||||
# px4_target_testing
|
||||
#
|
||||
# The macros are called from the top level CMakeLists.txt
|
||||
# It is called on every platform but only adjusts the output when
|
||||
# called inside the cygwin environment.
|
||||
#
|
||||
|
||||
macro (CYGPATH _path _cygpath)
|
||||
if (CMAKE_HOST_SYSTEM_NAME STREQUAL CYGWIN)
|
||||
EXECUTE_PROCESS(COMMAND cygpath.exe -m ${${_path}}
|
||||
OUTPUT_VARIABLE ${_cygpath})
|
||||
string (STRIP ${${_cygpath}} ${_cygpath})
|
||||
else()
|
||||
set(${_cygpath} ${${_path}})
|
||||
endif ()
|
||||
endmacro (CYGPATH)
|
||||
@@ -1,37 +0,0 @@
|
||||
diff --git a/Eigen/src/Core/util/Macros.h b/Eigen/src/Core/util/Macros.h
|
||||
index 1ca1d66..9bc928b 100644
|
||||
--- a/Eigen/src/Core/util/Macros.h
|
||||
+++ b/Eigen/src/Core/util/Macros.h
|
||||
@@ -194,6 +194,12 @@
|
||||
#define EIGEN_ARCH_PPC 0
|
||||
#endif
|
||||
|
||||
+/// \internal EIGEN_ARCH_HEXAGON set to 1 if the architecture is Hexagon
|
||||
+#ifdef __HEXAGON_ARCH__
|
||||
+ #define EIGEN_ARCH_HEXAGON 1
|
||||
+#else
|
||||
+ #define EIGEN_ARCH_HEXAGON 0
|
||||
+#endif
|
||||
|
||||
|
||||
// Operating system identification, EIGEN_OS_*
|
||||
@@ -334,15 +340,16 @@
|
||||
#endif
|
||||
|
||||
// Do we support r-value references?
|
||||
-#if (__has_feature(cxx_rvalue_references) || \
|
||||
+#if ((__has_feature(cxx_rvalue_references) || \
|
||||
(defined(__cplusplus) && __cplusplus >= 201103L) || \
|
||||
defined(__GXX_EXPERIMENTAL_CXX0X__) || \
|
||||
- (EIGEN_COMP_MSVC >= 1600))
|
||||
+ (EIGEN_COMP_MSVC >= 1600)) && (!defined(EIGEN_ARCH_HEXAGON)))
|
||||
#define EIGEN_HAVE_RVALUE_REFERENCES
|
||||
#endif
|
||||
|
||||
// Does the compiler support result_of?
|
||||
-#if (__has_feature(cxx_lambdas) || (defined(__cplusplus) && __cplusplus >= 201103L))
|
||||
+#if ((__has_feature(cxx_lambdas) || (defined(__cplusplus) && __cplusplus >= 201103L)) && \
|
||||
+ (!defined(EIGEN_ARCH_HEXAGON)))
|
||||
#define EIGEN_HAS_STD_RESULT_OF 1
|
||||
#endif
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
"""
|
||||
The module facilitates testing in cmake.
|
||||
It takes a command and a regex for failure ok passing.
|
||||
It passes if:
|
||||
* No stderr output.
|
||||
* Stdout doesn't match failure regex.
|
||||
* Stdout matches ok regex if given.
|
||||
"""
|
||||
from __future__ import print_function
|
||||
import argparse
|
||||
import subprocess
|
||||
import re
|
||||
import sys
|
||||
|
||||
#pylint: disable=invalid-name
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
|
||||
parser.add_argument('cmd')
|
||||
parser.add_argument('--re-fail')
|
||||
parser.add_argument('--re-ok')
|
||||
parser.add_argument('--verbose', '-v', dest='verbose', action='store_true')
|
||||
|
||||
parser.set_defaults(verbose=False)
|
||||
args = parser.parse_args()
|
||||
|
||||
proc = subprocess.Popen(args.cmd.split(), stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
stdout, stderr = proc.communicate()
|
||||
|
||||
if stderr != "":
|
||||
print(stderr)
|
||||
sys.exit(1)
|
||||
|
||||
if args.re_fail is not None:
|
||||
fail_match = re.search(args.re_fail, stdout)
|
||||
if fail_match is not None:
|
||||
print(stdout)
|
||||
sys.exit(1)
|
||||
|
||||
if args.re_ok is not None:
|
||||
ok_match = re.search(args.re_ok, stdout)
|
||||
if re.match(args.re_ok, stdout) is None:
|
||||
print(stdout)
|
||||
sys.exit(1)
|
||||
|
||||
if args.verbose:
|
||||
print(stdout)
|
||||
|
||||
sys.exit(0)
|
||||
|
||||
# vim: set et ft=python fenc=utf-8 ff=unix sts=4 sw=4 ts=4 :
|
||||
@@ -1,27 +0,0 @@
|
||||
INFO Shell id is 47996278451456
|
||||
WARN 1 starting task wkr_high (file /home/jgoppert/git/px4/cmake-Firmware/src/platforms/posix/px4_layer/px4_posix_tasks.cpp line 146)
|
||||
WARN 54 starting task wkr_low (file /home/jgoppert/git/px4/cmake-Firmware/src/platforms/posix/px4_layer/px4_posix_tasks.cpp line 146)
|
||||
WARN 100 starting task wkr_hrt (file /home/jgoppert/git/px4/cmake-Firmware/src/platforms/posix/px4_layer/px4_posix_tasks.cpp line 146)
|
||||
App name: px4
|
||||
Enter a command and its args:
|
||||
----------------------------------
|
||||
Running: uorb
|
||||
Returning: uorb
|
||||
Enter a command and its args:
|
||||
----------------------------------
|
||||
Running: accelsim
|
||||
Returning: accelsim
|
||||
Enter a command and its args:
|
||||
----------------------------------
|
||||
Running: px4_simple_app
|
||||
Hello Sky!
|
||||
[px4_simple_app] Got no data within a second
|
||||
[px4_simple_app] Got no data within a second
|
||||
[px4_simple_app] Got no data within a second
|
||||
[px4_simple_app] Got no data within a second
|
||||
[px4_simple_app] Got no data within a second
|
||||
Returning: px4_simple_app
|
||||
Enter a command and its args:
|
||||
----------------------------------
|
||||
Running: shutdown
|
||||
Shutting down
|
||||
@@ -1,4 +0,0 @@
|
||||
uorb start
|
||||
accelsim start
|
||||
px4_simple_app
|
||||
shutdown
|
||||
@@ -37,21 +37,19 @@
|
||||
#
|
||||
# The shebang of this file is currently Python2 because some
|
||||
# dependencies such as pymavlink don't play well with Python3 yet.
|
||||
from __future__ import division
|
||||
|
||||
PKG = 'px4'
|
||||
|
||||
import unittest
|
||||
import rospy
|
||||
import rosbag
|
||||
import time
|
||||
|
||||
from std_msgs.msg import Header
|
||||
from std_msgs.msg import Float64
|
||||
from geometry_msgs.msg import PoseStamped, Quaternion
|
||||
from threading import Thread
|
||||
from tf.transformations import quaternion_from_euler
|
||||
from mavros_msgs.srv import CommandLong
|
||||
from sensor_msgs.msg import NavSatFix
|
||||
#from px4_test_helper import PX4TestHelper
|
||||
from geometry_msgs.msg import PoseStamped, Quaternion, Vector3
|
||||
from mavros_msgs.msg import AttitudeTarget, HomePosition, State
|
||||
from mavros_msgs.srv import CommandBool, SetMode
|
||||
from std_msgs.msg import Header
|
||||
|
||||
|
||||
class MavrosOffboardAttctlTest(unittest.TestCase):
|
||||
"""
|
||||
@@ -62,90 +60,212 @@ class MavrosOffboardAttctlTest(unittest.TestCase):
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
rospy.init_node('test_node', anonymous=True)
|
||||
rospy.wait_for_service('mavros/cmd/arming', 30)
|
||||
#self.helper = PX4TestHelper("mavros_offboard_attctl_test")
|
||||
#self.helper.setUp()
|
||||
|
||||
rospy.Subscriber("mavros/local_position/pose", PoseStamped, self.position_callback)
|
||||
rospy.Subscriber("mavros/global_position/global", NavSatFix, self.global_position_callback)
|
||||
self.pub_att = rospy.Publisher('mavros/setpoint_attitude/attitude', PoseStamped, queue_size=10)
|
||||
self.pub_thr = rospy.Publisher('mavros/setpoint_attitude/att_throttle', Float64, queue_size=10)
|
||||
rospy.wait_for_service('mavros/cmd/command', 30)
|
||||
self._srv_cmd_long = rospy.ServiceProxy('mavros/cmd/command', CommandLong, persistent=True)
|
||||
self.rate = rospy.Rate(10) # 10hz
|
||||
self.has_global_pos = False
|
||||
self.local_position = PoseStamped()
|
||||
self.state = State()
|
||||
self.att = AttitudeTarget()
|
||||
self.sub_topics_ready = {
|
||||
key: False
|
||||
for key in ['local_pos', 'home_pos', 'state']
|
||||
}
|
||||
|
||||
# setup ROS topics and services
|
||||
try:
|
||||
rospy.wait_for_service('mavros/cmd/arming', 30)
|
||||
rospy.wait_for_service('mavros/set_mode', 30)
|
||||
except rospy.ROSException:
|
||||
self.fail("failed to connect to mavros services")
|
||||
|
||||
self.set_arming_srv = rospy.ServiceProxy('mavros/cmd/arming',
|
||||
CommandBool)
|
||||
self.set_mode_srv = rospy.ServiceProxy('mavros/set_mode', SetMode)
|
||||
self.local_pos_sub = rospy.Subscriber('mavros/local_position/pose',
|
||||
PoseStamped,
|
||||
self.local_position_callback)
|
||||
self.home_pos_sub = rospy.Subscriber('mavros/home_position/home',
|
||||
HomePosition,
|
||||
self.home_position_callback)
|
||||
self.state_sub = rospy.Subscriber('mavros/state', State,
|
||||
self.state_callback)
|
||||
self.att_setpoint_pub = rospy.Publisher(
|
||||
'mavros/setpoint_raw/attitude', AttitudeTarget, queue_size=10)
|
||||
|
||||
# send setpoints in seperate thread to better prevent failsafe
|
||||
self.att_thread = Thread(target=self.send_att, args=())
|
||||
self.att_thread.daemon = True
|
||||
self.att_thread.start()
|
||||
|
||||
def tearDown(self):
|
||||
#self.helper.tearDown()
|
||||
pass
|
||||
|
||||
#
|
||||
# General callback functions used in tests
|
||||
# Callback functions
|
||||
#
|
||||
def position_callback(self, data):
|
||||
def local_position_callback(self, data):
|
||||
self.local_position = data
|
||||
|
||||
def global_position_callback(self, data):
|
||||
self.has_global_pos = True
|
||||
if not self.sub_topics_ready['local_pos']:
|
||||
self.sub_topics_ready['local_pos'] = True
|
||||
|
||||
def home_position_callback(self, data):
|
||||
# this topic publishing seems to be a better indicator that the sim
|
||||
# is ready, it's not actually needed
|
||||
self.home_pos_sub.unregister()
|
||||
|
||||
if not self.sub_topics_ready['home_pos']:
|
||||
self.sub_topics_ready['home_pos'] = True
|
||||
|
||||
def state_callback(self, data):
|
||||
self.state = data
|
||||
|
||||
if not self.sub_topics_ready['state']:
|
||||
self.sub_topics_ready['state'] = True
|
||||
|
||||
#
|
||||
# Helper methods
|
||||
#
|
||||
def send_att(self):
|
||||
rate = rospy.Rate(10) # Hz
|
||||
self.att.body_rate = Vector3()
|
||||
self.att.header = Header()
|
||||
self.att.header.frame_id = "base_footprint"
|
||||
self.att.orientation = Quaternion(*quaternion_from_euler(0.25, 0.25,
|
||||
0))
|
||||
self.att.thrust = 0.7
|
||||
self.att.type_mask = 7 # ignore body rate
|
||||
|
||||
while not rospy.is_shutdown():
|
||||
self.att.header.stamp = rospy.Time.now()
|
||||
self.att_setpoint_pub.publish(self.att)
|
||||
try: # prevent garbage in console output when thread is killed
|
||||
rate.sleep()
|
||||
except rospy.ROSInterruptException:
|
||||
pass
|
||||
|
||||
def set_mode(self, mode, timeout):
|
||||
"""mode: PX4 mode string, timeout(int): seconds"""
|
||||
old_mode = self.state.mode
|
||||
loop_freq = 1 # Hz
|
||||
rate = rospy.Rate(loop_freq)
|
||||
mode_set = False
|
||||
for i in xrange(timeout * loop_freq):
|
||||
if self.state.mode == mode:
|
||||
mode_set = True
|
||||
rospy.loginfo(
|
||||
"set mode success | new mode: {0}, old mode: {1} | seconds: {2} of {3}".
|
||||
format(mode, self.state.mode, i / loop_freq, timeout))
|
||||
break
|
||||
else:
|
||||
try:
|
||||
res = self.set_mode_srv(0, mode) # 0 is custom mode
|
||||
if not res.mode_sent:
|
||||
rospy.logerr("failed to send mode command")
|
||||
except rospy.ServiceException as e:
|
||||
rospy.logerr(e)
|
||||
|
||||
rate.sleep()
|
||||
|
||||
self.assertTrue(mode_set, (
|
||||
"failed to set mode | new mode: {0}, old mode: {1} | timeout(seconds): {2}".
|
||||
format(mode, old_mode, timeout)))
|
||||
|
||||
def set_arm(self, arm, timeout):
|
||||
"""arm: True to arm or False to disarm, timeout(int): seconds"""
|
||||
old_arm = self.state.armed
|
||||
loop_freq = 1 # Hz
|
||||
rate = rospy.Rate(loop_freq)
|
||||
arm_set = False
|
||||
for i in xrange(timeout * loop_freq):
|
||||
if self.state.armed == arm:
|
||||
arm_set = True
|
||||
rospy.loginfo(
|
||||
"set arm success | new arm: {0}, old arm: {1} | seconds: {2} of {3}".
|
||||
format(arm, old_arm, i / loop_freq, timeout))
|
||||
break
|
||||
else:
|
||||
try:
|
||||
res = self.set_arming_srv(arm)
|
||||
if not res.success:
|
||||
rospy.logerr("failed to send arm command")
|
||||
except rospy.ServiceException as e:
|
||||
rospy.logerr(e)
|
||||
|
||||
rate.sleep()
|
||||
|
||||
self.assertTrue(arm_set, (
|
||||
"failed to set arm | new arm: {0}, old arm: {1} | timeout(seconds): {2}".
|
||||
format(arm, self.state.armed, timeout)))
|
||||
|
||||
def wait_for_topics(self, timeout):
|
||||
"""wait for simulation to be ready, make sure we're getting topic info
|
||||
from all topics by checking dictionary of flag values set in callbacks,
|
||||
timeout(int): seconds"""
|
||||
rospy.loginfo("waiting for simulation topics to be ready")
|
||||
loop_freq = 1 # Hz
|
||||
rate = rospy.Rate(loop_freq)
|
||||
simulation_ready = False
|
||||
for i in xrange(timeout * loop_freq):
|
||||
if all(value for value in self.sub_topics_ready.values()):
|
||||
simulation_ready = True
|
||||
rospy.loginfo("simulation topics ready | seconds: {0} of {1}".
|
||||
format(i / loop_freq, timeout))
|
||||
break
|
||||
|
||||
rate.sleep()
|
||||
|
||||
self.assertTrue(simulation_ready, (
|
||||
"failed to hear from all subscribed simulation topics | topic ready flags: {0} | timeout(seconds): {1}".
|
||||
format(self.sub_topics_ready, timeout)))
|
||||
|
||||
#
|
||||
# Test method
|
||||
#
|
||||
def test_attctl(self):
|
||||
"""Test offboard attitude control"""
|
||||
# boundary to cross
|
||||
boundary_x = 5
|
||||
boundary_y = 5
|
||||
boundary_z = -5
|
||||
|
||||
# FIXME: hack to wait for simulation to be ready
|
||||
while not self.has_global_pos:
|
||||
self.rate.sleep()
|
||||
# delay starting the mission
|
||||
self.wait_for_topics(30)
|
||||
|
||||
# set some attitude and thrust
|
||||
att = PoseStamped()
|
||||
att.header = Header()
|
||||
att.header.frame_id = "base_footprint"
|
||||
att.header.stamp = rospy.Time.now()
|
||||
quaternion = quaternion_from_euler(0.25, 0.25, 0)
|
||||
att.pose.orientation = Quaternion(*quaternion)
|
||||
rospy.loginfo("seting mission mode")
|
||||
self.set_mode("OFFBOARD", 5)
|
||||
rospy.loginfo("arming")
|
||||
self.set_arm(True, 5)
|
||||
|
||||
throttle = Float64()
|
||||
throttle.data = 0.7
|
||||
armed = False
|
||||
|
||||
# does it cross expected boundaries in X seconds?
|
||||
count = 0
|
||||
timeout = 120
|
||||
while count < timeout:
|
||||
# update timestamp for each published SP
|
||||
att.header.stamp = rospy.Time.now()
|
||||
|
||||
self.pub_att.publish(att)
|
||||
#self.helper.bag_write('mavros/setpoint_attitude/attitude', att)
|
||||
self.pub_thr.publish(throttle)
|
||||
#self.helper.bag_write('mavros/setpoint_attitude/att_throttle', throttle)
|
||||
self.rate.sleep()
|
||||
|
||||
# FIXME: arm and switch to offboard
|
||||
# (need to wait the first few rounds until PX4 has the offboard stream)
|
||||
if not armed and count > 5:
|
||||
self._srv_cmd_long(False, 176, False,
|
||||
1, 6, 0, 0, 0, 0, 0)
|
||||
# make sure the first command doesn't get lost
|
||||
time.sleep(1)
|
||||
|
||||
self._srv_cmd_long(False, 400, False,
|
||||
# arm
|
||||
1, 0, 0, 0, 0, 0, 0)
|
||||
|
||||
armed = True
|
||||
|
||||
if (self.local_position.pose.position.x > 5
|
||||
and self.local_position.pose.position.z > 5
|
||||
and self.local_position.pose.position.y < -5):
|
||||
rospy.loginfo("run mission")
|
||||
rospy.loginfo("attempting to cross boundary | x: {0}, y: {1}, z: {2}".
|
||||
format(boundary_x, boundary_y, boundary_z))
|
||||
# does it cross expected boundaries in 'timeout' seconds?
|
||||
timeout = 12 # (int) seconds
|
||||
loop_freq = 10 # Hz
|
||||
rate = rospy.Rate(loop_freq)
|
||||
crossed = False
|
||||
for i in xrange(timeout * loop_freq):
|
||||
if (self.local_position.pose.position.x > boundary_x and
|
||||
self.local_position.pose.position.z > boundary_y and
|
||||
self.local_position.pose.position.y < boundary_z):
|
||||
rospy.loginfo("boundary crossed | seconds: {0} of {1}".format(
|
||||
i / loop_freq, timeout))
|
||||
crossed = True
|
||||
break
|
||||
count = count + 1
|
||||
|
||||
self.assertTrue(count < timeout, "took too long to cross boundaries")
|
||||
rate.sleep()
|
||||
|
||||
self.assertTrue(crossed, (
|
||||
"took too long to cross boundaries | current position x: {0}, y: {1}, z: {2} | timeout(seconds): {3}".
|
||||
format(self.local_position.pose.position.x,
|
||||
self.local_position.pose.position.y,
|
||||
self.local_position.pose.position.z, timeout)))
|
||||
|
||||
rospy.loginfo("disarming")
|
||||
self.set_arm(False, 5)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
import rostest
|
||||
rostest.rosrun(PKG, 'mavros_offboard_attctl_test', MavrosOffboardAttctlTest)
|
||||
#unittest.main()
|
||||
rospy.init_node('test_node', anonymous=True)
|
||||
|
||||
rostest.rosrun(PKG, 'mavros_offboard_attctl_test',
|
||||
MavrosOffboardAttctlTest)
|
||||
|
||||
@@ -37,24 +37,21 @@
|
||||
#
|
||||
# The shebang of this file is currently Python2 because some
|
||||
# dependencies such as pymavlink don't play well with Python3 yet.
|
||||
from __future__ import division
|
||||
|
||||
PKG = 'px4'
|
||||
|
||||
import unittest
|
||||
import rospy
|
||||
import math
|
||||
import rosbag
|
||||
import time
|
||||
|
||||
from numpy import linalg
|
||||
import numpy as np
|
||||
|
||||
from std_msgs.msg import Header
|
||||
from geometry_msgs.msg import PoseStamped, Quaternion
|
||||
from threading import Thread
|
||||
from tf.transformations import quaternion_from_euler
|
||||
from mavros_msgs.srv import CommandLong
|
||||
from sensor_msgs.msg import NavSatFix
|
||||
#from px4_test_helper import PX4TestHelper
|
||||
from geometry_msgs.msg import PoseStamped, Quaternion
|
||||
from mavros_msgs.msg import HomePosition, State
|
||||
from mavros_msgs.srv import CommandBool, SetMode
|
||||
from std_msgs.msg import Header
|
||||
|
||||
|
||||
class MavrosOffboardPosctlTest(unittest.TestCase):
|
||||
"""
|
||||
@@ -62,115 +59,236 @@ class MavrosOffboardPosctlTest(unittest.TestCase):
|
||||
via MAVROS.
|
||||
|
||||
For the test to be successful it needs to reach all setpoints in a certain time.
|
||||
|
||||
FIXME: add flight path assertion (needs transformation from ROS frame to NED)
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
rospy.init_node('test_node', anonymous=True)
|
||||
#self.helper = PX4TestHelper("mavros_offboard_posctl_test")
|
||||
#self.helper.setUp()
|
||||
|
||||
rospy.Subscriber("mavros/local_position/pose", PoseStamped, self.position_callback)
|
||||
rospy.Subscriber("mavros/global_position/global", NavSatFix, self.global_position_callback)
|
||||
self.pub_spt = rospy.Publisher('mavros/setpoint_position/local', PoseStamped, queue_size=10)
|
||||
rospy.wait_for_service('mavros/cmd/command', 30)
|
||||
self._srv_cmd_long = rospy.ServiceProxy('mavros/cmd/command', CommandLong, persistent=True)
|
||||
self.rate = rospy.Rate(10) # 10hz
|
||||
self.has_global_pos = False
|
||||
self.local_position = PoseStamped()
|
||||
self.armed = False
|
||||
self.state = State()
|
||||
self.pos = PoseStamped()
|
||||
self.sub_topics_ready = {
|
||||
key: False
|
||||
for key in ['local_pos', 'home_pos', 'state']
|
||||
}
|
||||
|
||||
# setup ROS topics and services
|
||||
try:
|
||||
rospy.wait_for_service('mavros/cmd/arming', 30)
|
||||
rospy.wait_for_service('mavros/set_mode', 30)
|
||||
except rospy.ROSException:
|
||||
self.fail("failed to connect to mavros services")
|
||||
|
||||
self.set_arming_srv = rospy.ServiceProxy('/mavros/cmd/arming',
|
||||
CommandBool)
|
||||
self.set_mode_srv = rospy.ServiceProxy('/mavros/set_mode', SetMode)
|
||||
self.local_pos_sub = rospy.Subscriber('mavros/local_position/pose',
|
||||
PoseStamped,
|
||||
self.local_position_callback)
|
||||
self.home_pos_sub = rospy.Subscriber('mavros/home_position/home',
|
||||
HomePosition,
|
||||
self.home_position_callback)
|
||||
self.state_sub = rospy.Subscriber('mavros/state', State,
|
||||
self.state_callback)
|
||||
self.pos_setpoint_pub = rospy.Publisher(
|
||||
'mavros/setpoint_position/local', PoseStamped, queue_size=10)
|
||||
|
||||
# send setpoints in seperate thread to better prevent failsafe
|
||||
self.pos_thread = Thread(target=self.send_pos, args=())
|
||||
self.pos_thread.daemon = True
|
||||
self.pos_thread.start()
|
||||
|
||||
def tearDown(self):
|
||||
#self.helper.tearDown()
|
||||
pass
|
||||
|
||||
#
|
||||
# General callback functions used in tests
|
||||
# Callback functions
|
||||
#
|
||||
def position_callback(self, data):
|
||||
def local_position_callback(self, data):
|
||||
self.local_position = data
|
||||
|
||||
def global_position_callback(self, data):
|
||||
self.has_global_pos = True
|
||||
if not self.sub_topics_ready['local_pos']:
|
||||
self.sub_topics_ready['local_pos'] = True
|
||||
|
||||
def home_position_callback(self, data):
|
||||
# this topic publishing seems to be a better indicator that the sim
|
||||
# is ready, it's not actually needed
|
||||
self.home_pos_sub.unregister()
|
||||
|
||||
if not self.sub_topics_ready['home_pos']:
|
||||
self.sub_topics_ready['home_pos'] = True
|
||||
|
||||
def state_callback(self, data):
|
||||
self.state = data
|
||||
|
||||
if not self.sub_topics_ready['state']:
|
||||
self.sub_topics_ready['state'] = True
|
||||
|
||||
#
|
||||
# Helper methods
|
||||
#
|
||||
def send_pos(self):
|
||||
rate = rospy.Rate(10) # Hz
|
||||
self.pos.header = Header()
|
||||
self.pos.header.frame_id = "base_footprint"
|
||||
|
||||
while not rospy.is_shutdown():
|
||||
self.pos.header.stamp = rospy.Time.now()
|
||||
self.pos_setpoint_pub.publish(self.pos)
|
||||
try: # prevent garbage in console output when thread is killed
|
||||
rate.sleep()
|
||||
except rospy.ROSInterruptException:
|
||||
pass
|
||||
|
||||
def set_mode(self, mode, timeout):
|
||||
"""mode: PX4 mode string, timeout(int): seconds"""
|
||||
old_mode = self.state.mode
|
||||
loop_freq = 1 # Hz
|
||||
rate = rospy.Rate(loop_freq)
|
||||
mode_set = False
|
||||
for i in xrange(timeout * loop_freq):
|
||||
if self.state.mode == mode:
|
||||
mode_set = True
|
||||
rospy.loginfo(
|
||||
"set mode success | new mode: {0}, old mode: {1} | seconds: {2} of {3}".
|
||||
format(mode, old_mode, i / loop_freq, timeout))
|
||||
break
|
||||
else:
|
||||
try:
|
||||
res = self.set_mode_srv(0, mode) # 0 is custom mode
|
||||
if not res.mode_sent:
|
||||
rospy.logerr("failed to send mode command")
|
||||
except rospy.ServiceException as e:
|
||||
rospy.logerr(e)
|
||||
|
||||
rate.sleep()
|
||||
|
||||
self.assertTrue(mode_set, (
|
||||
"failed to set mode | new mode: {0}, old mode: {1} | timeout(seconds): {2}".
|
||||
format(mode, self.state.mode, timeout)))
|
||||
|
||||
def set_arm(self, arm, timeout):
|
||||
"""arm: True to arm or False to disarm, timeout(int): seconds"""
|
||||
old_arm = self.state.armed
|
||||
loop_freq = 1 # Hz
|
||||
rate = rospy.Rate(loop_freq)
|
||||
arm_set = False
|
||||
for i in xrange(timeout * loop_freq):
|
||||
if self.state.armed == arm:
|
||||
arm_set = True
|
||||
rospy.loginfo(
|
||||
"set arm success | new arm: {0}, old arm: {1} | seconds: {2} of {3}".
|
||||
format(arm, old_arm, i / loop_freq, timeout))
|
||||
break
|
||||
else:
|
||||
try:
|
||||
res = self.set_arming_srv(arm)
|
||||
if not res.success:
|
||||
rospy.logerr("failed to send arm command")
|
||||
except rospy.ServiceException as e:
|
||||
rospy.logerr(e)
|
||||
|
||||
rate.sleep()
|
||||
|
||||
self.assertTrue(arm_set, (
|
||||
"failed to set arm | new arm: {0}, old arm: {1} | timeout(seconds): {2}".
|
||||
format(arm, self.state.armed, timeout)))
|
||||
|
||||
def wait_for_topics(self, timeout):
|
||||
"""wait for simulation to be ready, make sure we're getting topic info
|
||||
from all topics by checking dictionary of flag values set in callbacks,
|
||||
timeout(int): seconds"""
|
||||
rospy.loginfo("waiting for simulation topics to be ready")
|
||||
loop_freq = 1 # Hz
|
||||
rate = rospy.Rate(loop_freq)
|
||||
simulation_ready = False
|
||||
for i in xrange(timeout * loop_freq):
|
||||
if all(value for value in self.sub_topics_ready.values()):
|
||||
simulation_ready = True
|
||||
rospy.loginfo("simulation topics ready | seconds: {0} of {1}".
|
||||
format(i / loop_freq, timeout))
|
||||
break
|
||||
|
||||
rate.sleep()
|
||||
|
||||
self.assertTrue(simulation_ready, (
|
||||
"failed to hear from all subscribed simulation topics | topic ready flags: {0} | timeout(seconds): {1}".
|
||||
format(self.sub_topics_ready, timeout)))
|
||||
|
||||
def is_at_position(self, x, y, z, offset):
|
||||
rospy.logdebug("current position %f, %f, %f" %
|
||||
(self.local_position.pose.position.x,
|
||||
self.local_position.pose.position.y,
|
||||
self.local_position.pose.position.z))
|
||||
rospy.logdebug("current position | x:{0}, y:{1}, z:{2}".format(
|
||||
self.local_position.pose.position.x, self.local_position.pose.
|
||||
position.y, self.local_position.pose.position.z))
|
||||
|
||||
desired = np.array((x, y, z))
|
||||
pos = np.array((self.local_position.pose.position.x,
|
||||
self.local_position.pose.position.y,
|
||||
self.local_position.pose.position.z))
|
||||
return linalg.norm(desired - pos) < offset
|
||||
return np.linalg.norm(desired - pos) < offset
|
||||
|
||||
def reach_position(self, x, y, z, timeout):
|
||||
# set a position setpoint
|
||||
pos = PoseStamped()
|
||||
pos.header = Header()
|
||||
pos.header.frame_id = "base_footprint"
|
||||
pos.pose.position.x = x
|
||||
pos.pose.position.y = y
|
||||
pos.pose.position.z = z
|
||||
self.pos.pose.position.x = x
|
||||
self.pos.pose.position.y = y
|
||||
self.pos.pose.position.z = z
|
||||
rospy.loginfo("attempting to reach position | x: {0}, y: {1}, z: {2}".
|
||||
format(x, y, z))
|
||||
|
||||
# For demo purposes we will lock yaw/heading to north.
|
||||
yaw_degrees = 0 # North
|
||||
yaw = math.radians(yaw_degrees)
|
||||
quaternion = quaternion_from_euler(0, 0, yaw)
|
||||
pos.pose.orientation = Quaternion(*quaternion)
|
||||
self.pos.pose.orientation = Quaternion(*quaternion)
|
||||
|
||||
# does it reach the position in X seconds?
|
||||
count = 0
|
||||
while count < timeout:
|
||||
# update timestamp for each published SP
|
||||
pos.header.stamp = rospy.Time.now()
|
||||
self.pub_spt.publish(pos)
|
||||
#self.helper.bag_write('mavros/setpoint_position/local', pos)
|
||||
|
||||
# FIXME: arm and switch to offboard
|
||||
# (need to wait the first few rounds until PX4 has the offboard stream)
|
||||
if not self.armed and count > 5:
|
||||
self._srv_cmd_long(False, 176, False,
|
||||
1, 6, 0, 0, 0, 0, 0)
|
||||
# make sure the first command doesn't get lost
|
||||
time.sleep(1)
|
||||
|
||||
self._srv_cmd_long(False, 400, False,
|
||||
# arm
|
||||
1, 0, 0, 0, 0, 0, 0)
|
||||
|
||||
self.armed = True
|
||||
|
||||
if self.is_at_position(pos.pose.position.x, pos.pose.position.y, pos.pose.position.z, 1):
|
||||
# does it reach the position in 'timeout' seconds?
|
||||
loop_freq = 10 # Hz
|
||||
rate = rospy.Rate(loop_freq)
|
||||
reached = False
|
||||
for i in xrange(timeout * loop_freq):
|
||||
if self.is_at_position(self.pos.pose.position.x,
|
||||
self.pos.pose.position.y,
|
||||
self.pos.pose.position.z, 1):
|
||||
rospy.loginfo("position reached | seconds: {0} of {1}".format(
|
||||
i / loop_freq, timeout))
|
||||
reached = True
|
||||
break
|
||||
count = count + 1
|
||||
self.rate.sleep()
|
||||
|
||||
self.assertTrue(count < timeout, "took too long to get to position")
|
||||
rate.sleep()
|
||||
|
||||
self.assertTrue(reached, (
|
||||
"took too long to get to position | current position x: {0}, y: {1}, z: {2} | timeout(seconds): {3}".
|
||||
format(self.local_position.pose.position.x,
|
||||
self.local_position.pose.position.y,
|
||||
self.local_position.pose.position.z, timeout)))
|
||||
|
||||
#
|
||||
# Test method
|
||||
#
|
||||
def test_posctl(self):
|
||||
"""Test offboard position control"""
|
||||
|
||||
# FIXME: hack to wait for simulation to be ready
|
||||
while not self.has_global_pos:
|
||||
self.rate.sleep()
|
||||
# delay starting the mission
|
||||
self.wait_for_topics(30)
|
||||
|
||||
positions = (
|
||||
(0, 0, 0),
|
||||
(2, 2, 2),
|
||||
(2, -2, 2),
|
||||
(-2, -2, 2),
|
||||
(2, 2, 2))
|
||||
rospy.loginfo("seting mission mode")
|
||||
self.set_mode("OFFBOARD", 5)
|
||||
rospy.loginfo("arming")
|
||||
self.set_arm(True, 5)
|
||||
|
||||
for i in range(0, len(positions)):
|
||||
self.reach_position(positions[i][0], positions[i][1], positions[i][2], 180)
|
||||
rospy.loginfo("run mission")
|
||||
positions = ((0, 0, 0), (2, 2, 2), (2, -2, 2), (-2, -2, 2), (2, 2, 2))
|
||||
|
||||
for i in xrange(len(positions)):
|
||||
self.reach_position(positions[i][0], positions[i][1],
|
||||
positions[i][2], 18)
|
||||
|
||||
rospy.loginfo("disarming")
|
||||
self.set_arm(False, 5)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
import rostest
|
||||
rostest.rosrun(PKG, 'mavros_offboard_posctl_test', MavrosOffboardPosctlTest)
|
||||
#unittest.main()
|
||||
rospy.init_node('test_node', anonymous=True)
|
||||
|
||||
rostest.rosrun(PKG, 'mavros_offboard_posctl_test',
|
||||
MavrosOffboardPosctlTest)
|
||||
|
||||
@@ -38,269 +38,399 @@
|
||||
|
||||
# The shebang of this file is currently Python2 because some
|
||||
# dependencies such as pymavlink don't play well with Python3 yet.
|
||||
from __future__ import division
|
||||
|
||||
PKG = 'px4'
|
||||
|
||||
import unittest
|
||||
import rospy
|
||||
import math
|
||||
import rosbag
|
||||
import sys
|
||||
import os
|
||||
import time
|
||||
import glob
|
||||
import json
|
||||
|
||||
import mavros
|
||||
from pymavlink import mavutil
|
||||
from mavros import mavlink
|
||||
|
||||
import math
|
||||
import os
|
||||
import px4tools
|
||||
|
||||
from geometry_msgs.msg import PoseStamped
|
||||
from mavros_msgs.srv import CommandLong, WaypointPush
|
||||
from mavros_msgs.msg import Mavlink, Waypoint, ExtendedState
|
||||
from sensor_msgs.msg import NavSatFix
|
||||
import sys
|
||||
from mavros import mavlink
|
||||
from mavros.mission import QGroundControlWP
|
||||
#from px4_test_helper import PX4TestHelper
|
||||
from pymavlink import mavutil
|
||||
from threading import Thread
|
||||
from mavros_msgs.msg import Altitude, ExtendedState, HomePosition, Mavlink, \
|
||||
State, Waypoint
|
||||
from mavros_msgs.srv import CommandBool, SetMode, WaypointPush
|
||||
from sensor_msgs.msg import NavSatFix
|
||||
|
||||
|
||||
def get_last_log():
|
||||
try:
|
||||
log_path = os.environ['PX4_LOG_DIR']
|
||||
except KeyError:
|
||||
log_path = os.path.join(os.environ['HOME'], 'ros/rootfs/fs/microsd/log')
|
||||
last_log_dir = sorted(
|
||||
glob.glob(os.path.join(log_path, '*')))[-1]
|
||||
log_path = os.path.join(os.environ['HOME'],
|
||||
'.ros/rootfs/fs/microsd/log')
|
||||
last_log_dir = sorted(glob.glob(os.path.join(log_path, '*')))[-1]
|
||||
last_log = sorted(glob.glob(os.path.join(last_log_dir, '*.ulg')))[-1]
|
||||
return last_log
|
||||
|
||||
|
||||
def read_new_mission(f):
|
||||
d = json.load(f)
|
||||
current = True
|
||||
for wp in d['items']:
|
||||
yield Waypoint(
|
||||
is_current = current,
|
||||
frame = int(wp['frame']),
|
||||
command = int(wp['command']),
|
||||
param1 = float(wp['param1']),
|
||||
param2 = float(wp['param2']),
|
||||
param3 = float(wp['param3']),
|
||||
param4 = float(wp['param4']),
|
||||
x_lat = float(wp['coordinate'][0]),
|
||||
y_long = float(wp['coordinate'][1]),
|
||||
z_alt = float(wp['coordinate'][2]),
|
||||
autocontinue = bool(wp['autoContinue']))
|
||||
is_current=current,
|
||||
frame=int(wp['frame']),
|
||||
command=int(wp['command']),
|
||||
param1=float(wp['param1']),
|
||||
param2=float(wp['param2']),
|
||||
param3=float(wp['param3']),
|
||||
param4=float(wp['param4']),
|
||||
x_lat=float(wp['coordinate'][0]),
|
||||
y_long=float(wp['coordinate'][1]),
|
||||
z_alt=float(wp['coordinate'][2]),
|
||||
autocontinue=bool(wp['autoContinue']))
|
||||
if current:
|
||||
current = False
|
||||
|
||||
|
||||
class MavrosMissionTest(unittest.TestCase):
|
||||
"""
|
||||
Run a mission
|
||||
"""
|
||||
# dictionaries correspond to mavros ExtendedState msg
|
||||
LAND_STATES = {
|
||||
0: 'UNDEFINED',
|
||||
1: 'ON_GROUND',
|
||||
2: 'IN_AIR',
|
||||
3: 'TAKEOFF',
|
||||
4: 'LANDING'
|
||||
}
|
||||
VTOL_STATES = {
|
||||
0: 'VTOL UNDEFINED',
|
||||
1: 'VTOL MC->FW',
|
||||
2: 'VTOL FW->MC',
|
||||
3: 'VTOL MC',
|
||||
4: 'VTOL FW'
|
||||
}
|
||||
|
||||
def setUp(self):
|
||||
rospy.init_node('test_node', anonymous=True)
|
||||
|
||||
self.rate = rospy.Rate(10) # 10hz
|
||||
self.rate = rospy.Rate(10) # 10hz
|
||||
self.has_global_pos = False
|
||||
self.local_position = PoseStamped()
|
||||
self.global_position = NavSatFix()
|
||||
self.extended_state = ExtendedState()
|
||||
self.home_alt = 0
|
||||
self.altitude = Altitude()
|
||||
self.state = State()
|
||||
self.mc_rad = 5
|
||||
self.fw_rad = 60
|
||||
self.fw_alt_rad = 10
|
||||
self.last_alt_d = 9999
|
||||
self.last_pos_d = 9999
|
||||
self.last_alt_d = None
|
||||
self.last_pos_d = None
|
||||
self.mission_name = ""
|
||||
self.sub_topics_ready = {
|
||||
key: False
|
||||
for key in ['global_pos', 'home_pos', 'ext_state', 'alt', 'state']
|
||||
}
|
||||
|
||||
# need to simulate heartbeat for datalink loss detection
|
||||
rospy.Timer(rospy.Duration(0.5), self.send_heartbeat)
|
||||
# setup ROS topics and services
|
||||
try:
|
||||
rospy.wait_for_service('mavros/mission/push', 30)
|
||||
rospy.wait_for_service('mavros/cmd/arming', 30)
|
||||
rospy.wait_for_service('mavros/set_mode', 30)
|
||||
except rospy.ROSException:
|
||||
self.fail("failed to connect to mavros services")
|
||||
|
||||
rospy.wait_for_service('mavros/cmd/command', 30)
|
||||
self.pub_mavlink = rospy.Publisher('mavlink/to', Mavlink, queue_size=1)
|
||||
self._srv_cmd_long = rospy.ServiceProxy('mavros/cmd/command', CommandLong, persistent=True)
|
||||
self._srv_wp_push = rospy.ServiceProxy('mavros/mission/push', WaypointPush, persistent=True)
|
||||
self.wp_push_srv = rospy.ServiceProxy('mavros/mission/push',
|
||||
WaypointPush)
|
||||
self.set_arming_srv = rospy.ServiceProxy('/mavros/cmd/arming',
|
||||
CommandBool)
|
||||
self.set_mode_srv = rospy.ServiceProxy('/mavros/set_mode', SetMode)
|
||||
self.global_pos_sub = rospy.Subscriber('mavros/global_position/global',
|
||||
NavSatFix,
|
||||
self.global_position_callback)
|
||||
self.home_pos_sub = rospy.Subscriber('mavros/home_position/home',
|
||||
HomePosition,
|
||||
self.home_position_callback)
|
||||
self.ext_state_sub = rospy.Subscriber('mavros/extended_state',
|
||||
ExtendedState,
|
||||
self.extended_state_callback)
|
||||
self.alt_sub = rospy.Subscriber('mavros/altitude', Altitude,
|
||||
self.altitude_callback)
|
||||
self.state_sub = rospy.Subscriber('mavros/state', State,
|
||||
self.state_callback)
|
||||
self.mavlink_pub = rospy.Publisher('mavlink/to', Mavlink, queue_size=1)
|
||||
|
||||
rospy.Subscriber("mavros/local_position/pose", PoseStamped, self.position_callback)
|
||||
rospy.Subscriber("mavros/global_position/global", NavSatFix, self.global_position_callback)
|
||||
rospy.Subscriber("mavros/extended_state", ExtendedState, self.extended_state_callback)
|
||||
# need to simulate heartbeat to prevent datalink loss detection
|
||||
self.hb_mav_msg = mavutil.mavlink.MAVLink_heartbeat_message(
|
||||
mavutil.mavlink.MAV_TYPE_GCS, 0, 0, 0, 0, 0)
|
||||
self.hb_mav_msg.pack(mavutil.mavlink.MAVLink('', 2, 1))
|
||||
self.hb_ros_msg = mavlink.convert_to_rosmsg(self.hb_mav_msg)
|
||||
self.hb_thread = Thread(target=self.send_heartbeat, args=())
|
||||
self.hb_thread.daemon = True
|
||||
self.hb_thread.start()
|
||||
|
||||
def tearDown(self):
|
||||
#self.helper.tearDown()
|
||||
pass
|
||||
|
||||
#
|
||||
# General callback functions used in tests
|
||||
# Callback functions
|
||||
#
|
||||
def position_callback(self, data):
|
||||
self.local_position = data
|
||||
|
||||
def global_position_callback(self, data):
|
||||
self.global_position = data
|
||||
|
||||
if not self.has_global_pos:
|
||||
if data.altitude != 0:
|
||||
self.home_alt = data.altitude
|
||||
self.has_global_pos = True
|
||||
if not self.sub_topics_ready['global_pos']:
|
||||
self.sub_topics_ready['global_pos'] = True
|
||||
|
||||
def home_position_callback(self, data):
|
||||
# this topic publishing seems to be a better indicator that the sim
|
||||
# is ready, it's not actually needed
|
||||
self.home_pos_sub.unregister()
|
||||
|
||||
if not self.sub_topics_ready['home_pos']:
|
||||
self.sub_topics_ready['home_pos'] = True
|
||||
|
||||
def extended_state_callback(self, data):
|
||||
if self.extended_state.vtol_state != data.vtol_state:
|
||||
rospy.loginfo("VTOL state changed from {0} to {1}".format(
|
||||
self.VTOL_STATES.get(self.extended_state.vtol_state),
|
||||
self.VTOL_STATES.get(data.vtol_state)))
|
||||
|
||||
prev_state = self.extended_state.vtol_state;
|
||||
if self.extended_state.landed_state != data.landed_state:
|
||||
rospy.loginfo("landed state changed from {0} to {1}".format(
|
||||
self.LAND_STATES.get(self.extended_state.landed_state),
|
||||
self.LAND_STATES.get(data.landed_state)))
|
||||
|
||||
self.extended_state = data
|
||||
if (prev_state != self.extended_state.vtol_state):
|
||||
print("VTOL state change: %d" % self.extended_state.vtol_state);
|
||||
|
||||
if not self.sub_topics_ready['ext_state']:
|
||||
self.sub_topics_ready['ext_state'] = True
|
||||
|
||||
def state_callback(self, data):
|
||||
if self.state.armed != data.armed:
|
||||
rospy.loginfo("armed state changed from {0} to {1}".format(
|
||||
self.state.armed, data.armed))
|
||||
|
||||
if self.state.mode != data.mode:
|
||||
rospy.loginfo("mode changed from {0} to {1}".format(
|
||||
self.state.mode, data.mode))
|
||||
|
||||
self.state = data
|
||||
|
||||
# mavros publishes a disconnected state message on init
|
||||
if not self.sub_topics_ready['state'] and data.connected:
|
||||
self.sub_topics_ready['state'] = True
|
||||
|
||||
def altitude_callback(self, data):
|
||||
self.altitude = data
|
||||
|
||||
# amsl has been observed to be nan while other fields are valid
|
||||
if not self.sub_topics_ready['alt'] and not math.isnan(data.amsl):
|
||||
self.sub_topics_ready['alt'] = True
|
||||
|
||||
#
|
||||
# Helper methods
|
||||
#
|
||||
def send_heartbeat(self):
|
||||
rate = rospy.Rate(2) # Hz
|
||||
while not rospy.is_shutdown():
|
||||
self.mavlink_pub.publish(self.hb_ros_msg)
|
||||
try: # prevent garbage in console output when thread is killed
|
||||
rate.sleep()
|
||||
except rospy.ROSInterruptException:
|
||||
pass
|
||||
|
||||
def set_mode(self, mode, timeout):
|
||||
"""mode: PX4 mode string, timeout(int): seconds"""
|
||||
old_mode = self.state.mode
|
||||
loop_freq = 1 # Hz
|
||||
rate = rospy.Rate(loop_freq)
|
||||
mode_set = False
|
||||
for i in xrange(timeout * loop_freq):
|
||||
if self.state.mode == mode:
|
||||
mode_set = True
|
||||
rospy.loginfo(
|
||||
"set mode success | new mode: {0}, old mode: {1} | seconds: {2} of {3}".
|
||||
format(mode, old_mode, i / loop_freq, timeout))
|
||||
break
|
||||
else:
|
||||
try:
|
||||
res = self.set_mode_srv(0, mode) # 0 is custom mode
|
||||
if not res.mode_sent:
|
||||
rospy.logerr("failed to send mode command")
|
||||
except rospy.ServiceException as e:
|
||||
rospy.logerr(e)
|
||||
|
||||
rate.sleep()
|
||||
|
||||
self.assertTrue(mode_set, (
|
||||
"failed to set mode | new mode: {0}, old mode: {1} | timeout(seconds): {2}".
|
||||
format(mode, old_mode, timeout)))
|
||||
|
||||
def set_arm(self, arm, timeout):
|
||||
"""arm: True to arm or False to disarm, timeout(int): seconds"""
|
||||
old_arm = self.state.armed
|
||||
loop_freq = 1 # Hz
|
||||
rate = rospy.Rate(loop_freq)
|
||||
arm_set = False
|
||||
for i in xrange(timeout * loop_freq):
|
||||
if self.state.armed == arm:
|
||||
arm_set = True
|
||||
rospy.loginfo(
|
||||
"set arm success | new arm: {0}, old arm: {1} | seconds: {2} of {3}".
|
||||
format(arm, old_arm, i / loop_freq, timeout))
|
||||
break
|
||||
else:
|
||||
try:
|
||||
res = self.set_arming_srv(arm)
|
||||
if not res.success:
|
||||
rospy.logerr("failed to send arm command")
|
||||
except rospy.ServiceException as e:
|
||||
rospy.logerr(e)
|
||||
|
||||
rate.sleep()
|
||||
|
||||
self.assertTrue(arm_set, (
|
||||
"failed to set arm | new arm: {0}, old arm: {1} | timeout(seconds): {2}".
|
||||
format(arm, old_arm, timeout)))
|
||||
|
||||
def is_at_position(self, lat, lon, alt, xy_offset, z_offset):
|
||||
R = 6371000 # metres
|
||||
"""alt(amsl), xy_offset, z_offset: meters"""
|
||||
R = 6371000 # metres
|
||||
rlat1 = math.radians(lat)
|
||||
rlat2 = math.radians(self.global_position.latitude)
|
||||
|
||||
rlat_d = math.radians(self.global_position.latitude - lat)
|
||||
rlon_d = math.radians(self.global_position.longitude - lon)
|
||||
|
||||
a = (math.sin(rlat_d / 2) * math.sin(rlat_d / 2) +
|
||||
math.cos(rlat1) * math.cos(rlat2) *
|
||||
math.sin(rlon_d / 2) * math.sin(rlon_d / 2))
|
||||
c = 2 * math.atan2(math.sqrt(a), math.sqrt(1-a))
|
||||
a = (math.sin(rlat_d / 2) * math.sin(rlat_d / 2) + math.cos(rlat1) *
|
||||
math.cos(rlat2) * math.sin(rlon_d / 2) * math.sin(rlon_d / 2))
|
||||
c = 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a))
|
||||
|
||||
d = R * c
|
||||
alt_d = abs(alt - self.global_position.altitude)
|
||||
|
||||
#rospy.loginfo("d: %f, alt_d: %f", d, alt_d)
|
||||
alt_d = abs(alt - self.altitude.amsl)
|
||||
|
||||
# remember best distances
|
||||
if self.last_pos_d > d:
|
||||
if not self.last_pos_d or self.last_pos_d > d:
|
||||
self.last_pos_d = d
|
||||
if self.last_alt_d > alt_d:
|
||||
if not self.last_alt_d or self.last_alt_d > alt_d:
|
||||
self.last_alt_d = alt_d
|
||||
|
||||
rospy.logdebug("d: {0}, alt_d: {1}".format(d, alt_d))
|
||||
return d < xy_offset and alt_d < z_offset
|
||||
|
||||
def reach_position(self, lat, lon, alt, timeout, index):
|
||||
"""alt(amsl): meters, timeout(int): seconds"""
|
||||
# reset best distances
|
||||
self.last_alt_d = 9999
|
||||
self.last_pos_d = 9999
|
||||
self.last_alt_d = None
|
||||
self.last_pos_d = None
|
||||
|
||||
rospy.loginfo("trying to reach waypoint " +
|
||||
"lat: %13.9f, lon: %13.9f, alt: %6.2f, timeout: %d, index: %d" %
|
||||
(lat, lon, alt, timeout, index))
|
||||
rospy.loginfo(
|
||||
"trying to reach waypoint | lat: {0:13.9f}, lon: {1:13.9f}, alt: {2:6.2f}, index: {3}".
|
||||
format(lat, lon, alt, index))
|
||||
|
||||
# does it reach the position in X seconds?
|
||||
count = 0
|
||||
while count < timeout:
|
||||
# does it reach the position in 'timeout' seconds?
|
||||
loop_freq = 10 # Hz
|
||||
rate = rospy.Rate(loop_freq)
|
||||
reached = False
|
||||
for i in xrange(timeout * loop_freq):
|
||||
# use MC radius by default
|
||||
# FIXME: also check MAV_TYPE from system status, otherwise pure fixed-wing won't work
|
||||
xy_radius = self.mc_rad
|
||||
z_radius = self.mc_rad
|
||||
|
||||
# use FW radius if in FW or in transition
|
||||
if (self.extended_state.vtol_state == ExtendedState.VTOL_STATE_FW or
|
||||
self.extended_state.vtol_state == ExtendedState.VTOL_STATE_TRANSITION_TO_MC or
|
||||
self.extended_state.vtol_state == ExtendedState.VTOL_STATE_TRANSITION_TO_FW):
|
||||
if (self.extended_state.vtol_state == ExtendedState.VTOL_STATE_FW
|
||||
or self.extended_state.vtol_state ==
|
||||
ExtendedState.VTOL_STATE_TRANSITION_TO_MC or
|
||||
self.extended_state.vtol_state ==
|
||||
ExtendedState.VTOL_STATE_TRANSITION_TO_FW):
|
||||
xy_radius = self.fw_rad
|
||||
z_radius = self.fw_alt_rad
|
||||
|
||||
if self.is_at_position(lat, lon, alt, xy_radius, z_radius):
|
||||
rospy.loginfo("position reached, index: %d, count: %d, pos_d: %f, alt_d: %f" %
|
||||
(index, count, self.last_pos_d, self.last_alt_d))
|
||||
reached = True
|
||||
rospy.loginfo(
|
||||
"position reached | pos_d: {0:.2f}, alt_d: {1:.2f}, index: {2} | seconds: {3} of {4}".
|
||||
format(self.last_pos_d, self.last_alt_d, index, i /
|
||||
loop_freq, timeout))
|
||||
break
|
||||
|
||||
count = count + 1
|
||||
self.rate.sleep()
|
||||
rate.sleep()
|
||||
|
||||
vtol_state_string = "VTOL undefined"
|
||||
self.assertTrue(reached, (
|
||||
"({0}) took too long to get to position | lat: {1:13.9f}, lon: {2:13.9f}, alt: {3:6.2f}, xy off: {4}, z off: {5}, pos_d: {6:.2f}, alt_d: {7:.2f}, VTOL state: {8}, index: {9} | timeout(seconds): {10}".
|
||||
format(self.mission_name, lat, lon, alt, xy_radius, z_radius,
|
||||
self.last_pos_d, self.last_alt_d,
|
||||
self.VTOL_STATES.get(self.extended_state.vtol_state), index,
|
||||
timeout)))
|
||||
|
||||
if (self.extended_state.vtol_state == ExtendedState.VTOL_STATE_MC):
|
||||
vtol_state_string = "VTOL MC"
|
||||
if (self.extended_state.vtol_state == ExtendedState.VTOL_STATE_FW):
|
||||
vtol_state_string = "VTOL FW"
|
||||
if (self.extended_state.vtol_state == ExtendedState.VTOL_STATE_TRANSITION_TO_MC):
|
||||
vtol_state_string = "VTOL FW->MC"
|
||||
if (self.extended_state.vtol_state == ExtendedState.VTOL_STATE_TRANSITION_TO_FW):
|
||||
vtol_state_string = "VTOL MC->FW"
|
||||
|
||||
self.assertTrue(count < timeout, (("(%s) took too long to get to position " +
|
||||
"lat: %13.9f, lon: %13.9f, alt: %6.2f, xy off: %f, z off: %f, timeout: %d, index: %d, pos_d: %f, alt_d: %f, VTOL state: %s") %
|
||||
(self.mission_name, lat, lon, alt, xy_radius, z_radius, timeout, index, self.last_pos_d, self.last_alt_d, vtol_state_string)))
|
||||
|
||||
def run_mission(self):
|
||||
# Hack to wait until vehicle is ready
|
||||
# TODO better integration with pre-flight status reporting
|
||||
time.sleep(5)
|
||||
"""switch mode: auto and arm"""
|
||||
self._srv_cmd_long(False, 176, False,
|
||||
# custom, auto, mission
|
||||
1, 4, 4, 0, 0, 0, 0)
|
||||
# make sure the first command doesn't get lost
|
||||
time.sleep(1)
|
||||
|
||||
self._srv_cmd_long(False, 400, False,
|
||||
# arm
|
||||
1, 0, 0, 0, 0, 0, 0)
|
||||
|
||||
def wait_until_ready(self):
|
||||
"""FIXME: hack to wait for simulation to be ready"""
|
||||
while not self.has_global_pos:
|
||||
self.rate.sleep()
|
||||
|
||||
def wait_on_landing(self, timeout, index):
|
||||
"""Wait for landed state"""
|
||||
|
||||
rospy.loginfo("waiting for landing " +
|
||||
"timeout: %d, index: %d" %
|
||||
(timeout, index))
|
||||
|
||||
count = 0
|
||||
while count < timeout:
|
||||
if self.extended_state.landed_state == ExtendedState.LANDED_STATE_ON_GROUND:
|
||||
def wait_for_topics(self, timeout):
|
||||
"""wait for simulation to be ready, make sure we're getting topic info
|
||||
from all topics by checking dictionary of flag values set in callbacks,
|
||||
timeout(int): seconds"""
|
||||
rospy.loginfo("waiting for simulation topics to be ready")
|
||||
loop_freq = 1 # Hz
|
||||
rate = rospy.Rate(loop_freq)
|
||||
simulation_ready = False
|
||||
for i in xrange(timeout * loop_freq):
|
||||
if all(value for value in self.sub_topics_ready.values()):
|
||||
simulation_ready = True
|
||||
rospy.loginfo("simulation topics ready | seconds: {0} of {1}".
|
||||
format(i / loop_freq, timeout))
|
||||
break
|
||||
|
||||
count = count + 1
|
||||
self.rate.sleep()
|
||||
rate.sleep()
|
||||
|
||||
self.assertTrue(count < timeout, ("(%s) landing not detected after landing WP " +
|
||||
"timeout: %d, index: %d") %
|
||||
(self.mission_name, timeout, index))
|
||||
self.assertTrue(simulation_ready, (
|
||||
"failed to hear from all subscribed simulation topics | topic ready flags: {0} | timeout(seconds): {1}".
|
||||
format(self.sub_topics_ready, timeout)))
|
||||
|
||||
def wait_on_landed_state(self, desired_landed_state, timeout, index):
|
||||
rospy.loginfo(
|
||||
"waiting for landed state | state: {0}, index: {1}".format(
|
||||
self.LAND_STATES.get(desired_landed_state), index))
|
||||
loop_freq = 10 # Hz
|
||||
rate = rospy.Rate(loop_freq)
|
||||
landed_state_confirmed = False
|
||||
for i in xrange(timeout * loop_freq):
|
||||
if self.extended_state.landed_state == desired_landed_state:
|
||||
landed_state_confirmed = True
|
||||
rospy.loginfo(
|
||||
"landed state confirmed | state: {0}, index: {1}".format(
|
||||
self.LAND_STATES.get(desired_landed_state), index))
|
||||
break
|
||||
|
||||
rate.sleep()
|
||||
|
||||
self.assertTrue(landed_state_confirmed, (
|
||||
"({0}) landed state not detected | desired: {1}, current: {2} | index: {3}, timeout(seconds): {4}".
|
||||
format(self.mission_name,
|
||||
self.LAND_STATES.get(desired_landed_state),
|
||||
self.LAND_STATES.get(self.extended_state.landed_state),
|
||||
index, timeout)))
|
||||
|
||||
def wait_on_transition(self, transition, timeout, index):
|
||||
"""Wait for VTOL transition"""
|
||||
|
||||
rospy.loginfo("waiting for VTOL transition " +
|
||||
"transition: %d, timeout: %d, index: %d" %
|
||||
(transition, timeout, index))
|
||||
|
||||
count = 0
|
||||
while count < timeout:
|
||||
# transition to MC
|
||||
if (transition == ExtendedState.VTOL_STATE_MC and
|
||||
self.extended_state.vtol_state == ExtendedState.VTOL_STATE_MC):
|
||||
"""Wait for VTOL transition, timeout(int): seconds"""
|
||||
rospy.loginfo(
|
||||
"waiting for VTOL transition | transition: {0}, index: {1}".format(
|
||||
self.VTOL_STATES.get(transition), index))
|
||||
loop_freq = 10 # Hz
|
||||
rate = rospy.Rate(loop_freq)
|
||||
transitioned = False
|
||||
for i in xrange(timeout * loop_freq):
|
||||
if transition == self.extended_state.vtol_state:
|
||||
rospy.loginfo(
|
||||
"transitioned | index: {0} | seconds: {1} of {2}".format(
|
||||
index, i / loop_freq, timeout))
|
||||
transitioned = True
|
||||
break
|
||||
|
||||
# transition to FW
|
||||
if (transition == ExtendedState.VTOL_STATE_FW and
|
||||
self.extended_state.vtol_state == ExtendedState.VTOL_STATE_FW):
|
||||
break
|
||||
rate.sleep()
|
||||
|
||||
count = count + 1
|
||||
self.rate.sleep()
|
||||
|
||||
self.assertTrue(count < timeout, ("(%s) transition not detected " +
|
||||
"timeout: %d, index: %d") %
|
||||
(self.mission_name, timeout, index))
|
||||
|
||||
def send_heartbeat(self, event=None):
|
||||
# mav type gcs
|
||||
mavmsg = mavutil.mavlink.MAVLink_heartbeat_message(6, 0, 0, 0, 0, 0)
|
||||
# XXX: hack: using header object to set mav properties
|
||||
mavmsg.pack(mavutil.mavlink.MAVLink_header(0, 0, 0, 2, 1))
|
||||
rosmsg = mavlink.convert_to_rosmsg(mavmsg)
|
||||
self.pub_mavlink.publish(rosmsg)
|
||||
self.assertTrue(transitioned, (
|
||||
"({0}) transition not detected | index: {1} | timeout(seconds): {2}, ".
|
||||
format(self.mission_name, index, timeout)))
|
||||
|
||||
#
|
||||
# Test method
|
||||
#
|
||||
def test_mission(self):
|
||||
"""Test mission"""
|
||||
|
||||
@@ -309,11 +439,11 @@ class MavrosMissionTest(unittest.TestCase):
|
||||
return
|
||||
|
||||
self.mission_name = sys.argv[1]
|
||||
mission_file = os.path.dirname(os.path.realpath(__file__)) + "/" + sys.argv[1]
|
||||
mission_file = os.path.dirname(
|
||||
os.path.realpath(__file__)) + "/" + sys.argv[1]
|
||||
|
||||
rospy.loginfo("reading mission %s", mission_file)
|
||||
rospy.loginfo("reading mission {0}".format(mission_file))
|
||||
wps = []
|
||||
|
||||
with open(mission_file, 'r') as f:
|
||||
mission_ext = os.path.splitext(mission_file)[1]
|
||||
if mission_ext == '.mission':
|
||||
@@ -330,62 +460,79 @@ class MavrosMissionTest(unittest.TestCase):
|
||||
else:
|
||||
raise IOError('unknown mission file extension', mission_ext)
|
||||
|
||||
rospy.loginfo("wait until ready")
|
||||
self.wait_until_ready()
|
||||
|
||||
rospy.loginfo("send mission")
|
||||
res = self._srv_wp_push(wps)
|
||||
rospy.loginfo(res)
|
||||
self.assertTrue(res.success, "(%s) mission could not be transfered" % self.mission_name)
|
||||
result = False
|
||||
try:
|
||||
res = self.wp_push_srv(start_index=0, waypoints=wps)
|
||||
result = res.success
|
||||
except rospy.ServiceException as e:
|
||||
rospy.logerr(e)
|
||||
self.assertTrue(
|
||||
result,
|
||||
"({0}) mission could not be transfered".format(self.mission_name))
|
||||
|
||||
# delay starting the mission
|
||||
self.wait_for_topics(30)
|
||||
|
||||
# make sure the simulation is ready to start the mission
|
||||
self.wait_on_landed_state(ExtendedState.LANDED_STATE_ON_GROUND, 10, -1)
|
||||
|
||||
rospy.loginfo("seting mission mode")
|
||||
self.set_mode("AUTO.MISSION", 5)
|
||||
rospy.loginfo("arming")
|
||||
self.set_arm(True, 5)
|
||||
|
||||
rospy.loginfo("run mission")
|
||||
self.run_mission()
|
||||
|
||||
index = 0
|
||||
for waypoint in wps:
|
||||
for index, waypoint in enumerate(wps):
|
||||
# only check position for waypoints where this makes sense
|
||||
if waypoint.frame == Waypoint.FRAME_GLOBAL_REL_ALT or waypoint.frame == Waypoint.FRAME_GLOBAL:
|
||||
alt = waypoint.z_alt
|
||||
if waypoint.frame == Waypoint.FRAME_GLOBAL_REL_ALT:
|
||||
alt += self.home_alt
|
||||
alt += self.altitude.amsl - self.altitude.relative
|
||||
|
||||
self.reach_position(waypoint.x_lat, waypoint.y_long, alt, 600, index)
|
||||
self.reach_position(waypoint.x_lat, waypoint.y_long, alt, 60,
|
||||
index)
|
||||
|
||||
# check if VTOL transition happens if applicable
|
||||
if waypoint.command == 84 or waypoint.command == 85 or waypoint.command == 3000:
|
||||
transition = waypoint.param1
|
||||
|
||||
if waypoint.command == 84: # VTOL takeoff implies transition to FW
|
||||
if waypoint.command == 84: # VTOL takeoff implies transition to FW
|
||||
transition = ExtendedState.VTOL_STATE_FW
|
||||
|
||||
if waypoint.command == 85: # VTOL takeoff implies transition to MC
|
||||
if waypoint.command == 85: # VTOL takeoff implies transition to MC
|
||||
transition = ExtendedState.VTOL_STATE_MC
|
||||
|
||||
self.wait_on_transition(transition, 600, index)
|
||||
self.wait_on_transition(transition, 60, index)
|
||||
|
||||
# after reaching position, wait for landing detection if applicable
|
||||
if waypoint.command == 85 or waypoint.command == 21:
|
||||
self.wait_on_landing(600, index)
|
||||
self.wait_on_landed_state(ExtendedState.LANDED_STATE_ON_GROUND,
|
||||
60, index)
|
||||
|
||||
index += 1
|
||||
rospy.loginfo("disarming")
|
||||
self.set_arm(False, 5)
|
||||
|
||||
rospy.loginfo("mission done, calculating performance metrics")
|
||||
last_log = get_last_log()
|
||||
rospy.loginfo("log file %s", last_log)
|
||||
data = px4tools.ulog.read_ulog(last_log).concat(dt=0.1)
|
||||
data = px4tools.ulog.compute_data(data)
|
||||
rospy.loginfo("log file {0}".format(last_log))
|
||||
data = px4tools.read_ulog(last_log).concat(dt=0.1)
|
||||
data = px4tools.compute_data(data)
|
||||
res = px4tools.estimator_analysis(data, False)
|
||||
|
||||
# enforce performance
|
||||
self.assertTrue(abs(res['roll_error_mean']) < 5.0, str(res))
|
||||
self.assertTrue(abs(res['roll_error_mean']) < 5.0, str(res))
|
||||
self.assertTrue(abs(res['pitch_error_mean']) < 5.0, str(res))
|
||||
self.assertTrue(abs(res['yaw_error_mean']) < 5.0, str(res))
|
||||
self.assertTrue(res['roll_error_std'] < 5.0, str(res))
|
||||
self.assertTrue(res['pitch_error_std'] < 5.0, str(res))
|
||||
self.assertTrue(res['yaw_error_std'] < 5.0, str(res))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
import rostest
|
||||
rospy.init_node('test_node', anonymous=True)
|
||||
|
||||
name = "mavros_mission_test"
|
||||
if len(sys.argv) > 1:
|
||||
name += "-%s" % sys.argv[1]
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Run container and start test execution
|
||||
#
|
||||
# License: according to LICENSE.md in the root directory of the PX4 Firmware repository
|
||||
set -e
|
||||
|
||||
if [ -z "$WORKSPACE" ]; then
|
||||
echo "\$WORKSPACE not set"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
IMAGE=px4io/px4-dev-ros:v1.0
|
||||
|
||||
# Pulling latest image
|
||||
echo "===> pull latest Docker image"
|
||||
docker pull $IMAGE
|
||||
|
||||
# removing some images might fail
|
||||
set +e
|
||||
docker rmi $(docker images --filter "dangling=true" -q --no-trunc)
|
||||
set -e
|
||||
echo "<==="
|
||||
|
||||
#
|
||||
# Running SITL testing container
|
||||
# Assuming that necessary source projects, including this one, are cloned in the build server workspace of this job.
|
||||
#
|
||||
echo "===> run container"
|
||||
docker run --rm -v "$WORKSPACE:/job:rw" $IMAGE bash "/job/Firmware/integrationtests/run_tests.bash" /job/Firmware
|
||||
echo "<==="
|
||||
@@ -1,140 +0,0 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Starts tests from within the container
|
||||
#
|
||||
# License: according to LICENSE.md in the root directory of the PX4 Firmware repository
|
||||
set -e
|
||||
|
||||
# TODO move to docker image
|
||||
pip install --upgrade numpy -q
|
||||
pip install px4tools pymavlink toml -q
|
||||
|
||||
# A POSIX variable
|
||||
OPTIND=1 # Reset in case getopts has been used previously in the shell.
|
||||
|
||||
# Initialize our own variables:
|
||||
do_clean=true
|
||||
gui=false
|
||||
|
||||
while getopts "h?og" opt; do
|
||||
case "$opt" in
|
||||
h|\?)
|
||||
echo """
|
||||
$0 [-h] [-o] [-g]
|
||||
-h show help
|
||||
-o don't clean before building (to save time)
|
||||
-g run gazebo gui
|
||||
"""
|
||||
exit 0
|
||||
;;
|
||||
o) do_clean=false
|
||||
echo "not cleaning"
|
||||
;;
|
||||
g) gui=true
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
|
||||
# determine the directory of the source given the directory of this script
|
||||
pushd `dirname $0` > /dev/null
|
||||
SCRIPTPATH=`pwd`
|
||||
popd > /dev/null
|
||||
ORIG_SRC=$(dirname $SCRIPTPATH)
|
||||
|
||||
# set paths
|
||||
JOB_DIR=$(dirname $ORIG_SRC)
|
||||
CATKIN_DIR=$JOB_DIR/catkin
|
||||
BUILD_DIR=$CATKIN_DIR/build/px4
|
||||
SRC_DIR=${CATKIN_DIR}/src/px4
|
||||
|
||||
echo setting up ROS paths
|
||||
if [ -f /opt/ros/indigo/setup.bash ]
|
||||
then
|
||||
source /opt/ros/indigo/setup.bash
|
||||
elif [ -f /opt/ros/kinetic/setup.bash ]
|
||||
then
|
||||
source /opt/ros/kinetic/setup.bash
|
||||
else
|
||||
echo "could not find /opt/ros/{ros-distro}/setup.bash"
|
||||
exit 1
|
||||
fi
|
||||
export ROS_HOME=$JOB_DIR/.ros
|
||||
export ROS_LOG_DIR=$ROS_HOME/log
|
||||
export ROS_TEST_RESULT_DIR=$ROS_HOME/test_results/px4
|
||||
export PX4_LOG_DIR=$ROS_HOME/rootfs/fs/microsd/log
|
||||
TEST_RESULT_TARGET_DIR=$JOB_DIR/test_results
|
||||
|
||||
# TODO
|
||||
# BAGS=$ROS_HOME
|
||||
# CHARTS=$ROS_HOME/charts
|
||||
# EXPORT_CHARTS=/sitl/testing/export_charts.py
|
||||
|
||||
if $do_clean
|
||||
then
|
||||
echo cleaning
|
||||
rm -rf $CATKIN_DIR
|
||||
rm -rf $ROS_HOME
|
||||
rm -rf $TEST_RESULT_TARGET_DIR
|
||||
else
|
||||
echo skipping clean step
|
||||
fi
|
||||
|
||||
echo "=====> compile ($SRC_DIR)"
|
||||
mkdir -p $ROS_HOME
|
||||
mkdir -p $CATKIN_DIR/src
|
||||
mkdir -p $TEST_RESULT_TARGET_DIR
|
||||
if ! [ -d $SRC_DIR ]
|
||||
then
|
||||
ln -s $ORIG_SRC $SRC_DIR
|
||||
ln -s $ORIG_SRC/Tools/sitl_gazebo ${CATKIN_DIR}/src/mavlink_sitl_gazebo
|
||||
fi
|
||||
cd $CATKIN_DIR
|
||||
catkin_make
|
||||
. ./devel/setup.bash
|
||||
echo "<====="
|
||||
|
||||
# print paths to user
|
||||
echo -e "JOB_DIR\t\t: $JOB_DIR"
|
||||
echo -e "ROS_HOME\t: $ROS_HOME"
|
||||
echo -e "CATKIN_DIR\t: $CATKIN_DIR"
|
||||
echo -e "BUILD_DIR\t: $BUILD_DIR"
|
||||
echo -e "SRC_DIR\t\t: $SRC_DIR"
|
||||
echo -e "ROS_TEST_RESULT_DIR\t: $ROS_TEST_RESULT_DIR"
|
||||
echo -e "ROS_LOG_DIR\t\t: $ROS_LOG_DIR"
|
||||
echo -e "PX4_LOG_DIR\t\t: $PX4_LOG_DIR"
|
||||
echo -e "TEST_RESULT_TARGET_DIR\t: $TEST_RESULT_TARGET_DIR"
|
||||
|
||||
# don't exit on error anymore (because single tests or exports might fail)
|
||||
# however, stop executing tests after the first failure
|
||||
set +e
|
||||
echo "=====> run tests"
|
||||
test $? -eq 0 && rostest px4 mavros_posix_tests_iris.launch gui:=$gui
|
||||
|
||||
# commented out optical flow test for now since ci server has
|
||||
# an issue producing the simulated flow camera currently
|
||||
#test $? -eq 0 && rostest px4 mavros_posix_tests_iris_opt_flow.launch gui:=$gui
|
||||
|
||||
test $? -eq 0 && rostest px4 mavros_posix_tests_standard_vtol.launch gui:=$gui
|
||||
TEST_RESULT=$?
|
||||
echo "<====="
|
||||
|
||||
# TODO
|
||||
echo "=====> process test results"
|
||||
# cd $BAGS
|
||||
# for bag in `ls *.bag`
|
||||
# do
|
||||
# echo "processing bag: $bag"
|
||||
# python $EXPORT_CHARTS $CHARTS $bag
|
||||
# done
|
||||
|
||||
echo "copy build test results to job directory"
|
||||
cp -r $ROS_TEST_RESULT_DIR/* ${TEST_RESULT_TARGET_DIR}
|
||||
cp -r $ROS_LOG_DIR/* ${TEST_RESULT_TARGET_DIR}
|
||||
cp -r $PX4_LOG_DIR/* ${TEST_RESULT_TARGET_DIR}
|
||||
# cp $BAGS/*.bag ${TEST_RESULT_TARGET_DIR}/
|
||||
# cp -r $CHARTS ${TEST_RESULT_TARGET_DIR}/
|
||||
echo "<====="
|
||||
|
||||
# need to return error if tests failed, else Jenkins won't notice the failure
|
||||
exit $TEST_RESULT
|
||||
@@ -1,19 +0,0 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Upload SITL CI logs to Flight Review
|
||||
#
|
||||
# License: according to LICENSE.md in the root directory of the PX4 Firmware repository
|
||||
|
||||
if [ -z "$WORKSPACE" ] || [ -z "${ghprbActualCommitAuthorEmail}" ] || [ -z "${ghprbPullDescription}" ]; then
|
||||
echo "Environment not set. Needs to be called from within Jenkins."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Uploading test logs to Flight Review"
|
||||
|
||||
CMD="$WORKSPACE/Firmware/Tools/upload_log.py"
|
||||
find "$WORKSPACE/test_results" -name \*.ulg -exec "$CMD" -q \
|
||||
--description "${ghprbPullDescription}" --source CI "{}" \;
|
||||
|
||||
# XXX: move up if we want email notifications
|
||||
# --email "${ghprbActualCommitAuthorEmail}" \
|
||||
@@ -9,7 +9,7 @@
|
||||
<arg name="Y" default="0"/>
|
||||
|
||||
|
||||
<arg name="est" default="lpe"/>
|
||||
<arg name="est" default="ekf2"/>
|
||||
<arg name="vehicle" default="iris"/>
|
||||
<arg name="world" default="$(find mavlink_sitl_gazebo)/worlds/empty.world"/>
|
||||
<arg name="sdf" default="$(find mavlink_sitl_gazebo)/models/$(arg vehicle)/$(arg vehicle).sdf"/>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<arg name="R" default="0"/>
|
||||
<arg name="P" default="0"/>
|
||||
<arg name="Y" default="0"/>
|
||||
<arg name="est" default="lpe"/>
|
||||
<arg name="est" default="ekf2"/>
|
||||
<arg name="vehicle" default="iris"/>
|
||||
<arg name="world" default="$(find mavlink_sitl_gazebo)/worlds/empty.world"/>
|
||||
<arg name="sdf" default="$(find mavlink_sitl_gazebo)/models/$(arg vehicle)/$(arg vehicle).sdf"/>
|
||||
@@ -40,4 +40,4 @@
|
||||
|
||||
</launch>
|
||||
|
||||
<!-- vim: set et ft=xml fenc=utf-8 ff=unix sts=0 sw=4 ts=4 : -->
|
||||
<!-- vim: set et ft=xml fenc=utf-8 ff=unix sts=0 sw=4 ts=4 : -->
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user