Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
577da606d5 | ||
|
|
caf1f4c29d | ||
|
|
65a6369359 | ||
|
|
fd2814b66a | ||
|
|
63e7f9cf07 |
7
.github/workflows/build-image.yaml
vendored
@@ -7,24 +7,23 @@ on:
|
|||||||
branches: [ master ]
|
branches: [ master ]
|
||||||
release:
|
release:
|
||||||
types: [ created ]
|
types: [ created ]
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v2
|
||||||
- name: Build image
|
- name: Build image
|
||||||
run: |
|
run: |
|
||||||
docker run --privileged --rm -v /dev:/dev -v $(pwd):/builder/repo -e TRAVIS_TAG="${{ github.event.release.tag_name }}" sfalexrog/img-tool:qemu-update
|
docker run --privileged --rm -v /dev:/dev -v $(pwd):/builder/repo -e TRAVIS_TAG="${{ github.event.release.tag_name }}" sfalexrog/img-tool:qemu-update
|
||||||
- name: Compress image
|
- name: Compress image
|
||||||
run: |
|
run: |
|
||||||
cd images && sudo chmod -R 777 . && zip -9 $(echo *_*).zip *_* && ls -l . && unzip -l *_*.zip
|
cd images && sudo chmod -R 777 . && zip -9 $(echo clover_*).zip clover_* && ls -l . && unzip -l clover_*.zip
|
||||||
- name: Upload image
|
- name: Upload image
|
||||||
uses: softprops/action-gh-release@v1
|
uses: softprops/action-gh-release@v1
|
||||||
if: ${{ github.event_name == 'release' }}
|
if: ${{ github.event_name == 'release' }}
|
||||||
with:
|
with:
|
||||||
files: images/*_*.zip
|
files: images/clover_*.zip
|
||||||
prerelease: true
|
prerelease: true
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|||||||
39
.github/workflows/build.yml
vendored
@@ -5,48 +5,19 @@ on:
|
|||||||
branches: [ '*' ]
|
branches: [ '*' ]
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [ master ]
|
branches: [ master ]
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
# melodic:
|
# melodic:
|
||||||
# runs-on: ubuntu-latest
|
# runs-on: ubuntu-latest
|
||||||
# steps:
|
# steps:
|
||||||
# - uses: actions/checkout@v4
|
# - uses: actions/checkout@v2
|
||||||
# - name: Native Melodic build
|
# - name: Native Melodic build
|
||||||
# run: |
|
# run: |
|
||||||
# docker run --rm -v $(pwd):/root/catkin_ws/src/clover ros:melodic-ros-base /root/catkin_ws/src/clover/builder/standalone-install.sh
|
# docker run --rm -v $(pwd):/root/catkin_ws/src/clover ros:melodic-ros-base /root/catkin_ws/src/clover/builder/standalone-install.sh
|
||||||
noetic:
|
noetic:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
container: ros:noetic-ros-base
|
|
||||||
defaults:
|
|
||||||
run:
|
|
||||||
working-directory: catkin_ws
|
|
||||||
shell: bash
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v2
|
||||||
with:
|
- name: Native Noetic build
|
||||||
path: catkin_ws/src/clover
|
run: |
|
||||||
- name: Install requirements
|
docker run --rm -v $(pwd):/root/catkin_ws/src/clover ros:noetic-ros-base /root/catkin_ws/src/clover/builder/standalone-install.sh
|
||||||
run: apt-get update && apt-get -y install python3-pip fakeroot python3-bloom debhelper dpkg-dev
|
|
||||||
- name: Install dependencies
|
|
||||||
run: rosdep update && rosdep install --from-paths src --ignore-src -y
|
|
||||||
- name: Install GeographicLib datasets
|
|
||||||
run: wget -qO- https://raw.githubusercontent.com/mavlink/mavros/master/mavros/scripts/install_geographiclib_datasets.sh | bash
|
|
||||||
- name: catkin_make
|
|
||||||
run: source /opt/ros/$ROS_DISTRO/setup.bash && catkin_make
|
|
||||||
- name: Run tests
|
|
||||||
run: source devel/setup.bash && catkin_make run_tests && catkin_test_results
|
|
||||||
- name: Build Debian packages
|
|
||||||
run: |
|
|
||||||
source devel/setup.bash
|
|
||||||
for file in `find . -name "package.xml"`; do
|
|
||||||
cd $(dirname ${file})
|
|
||||||
bloom-generate rosdebian --os-name ubuntu --os-version $(lsb_release -cs) --ros-distro $ROS_DISTRO
|
|
||||||
fakeroot debian/rules binary
|
|
||||||
cd -
|
|
||||||
done
|
|
||||||
- uses: actions/upload-artifact@v3
|
|
||||||
with:
|
|
||||||
name: debian-packages
|
|
||||||
path: catkin_ws/src/clover/*.deb
|
|
||||||
retention-days: 1
|
|
||||||
|
|||||||
13
.github/workflows/docs.yml
vendored
@@ -5,13 +5,16 @@ on:
|
|||||||
branches: [ '*' ]
|
branches: [ '*' ]
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [ '*' ]
|
branches: [ '*' ]
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
pages: write
|
pages: write
|
||||||
id-token: write
|
id-token: write
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: "pages"
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
defaults:
|
defaults:
|
||||||
run:
|
run:
|
||||||
shell: bash
|
shell: bash
|
||||||
@@ -20,7 +23,7 @@ jobs:
|
|||||||
docs:
|
docs:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v2
|
||||||
- name: Use Node.js
|
- name: Use Node.js
|
||||||
uses: actions/setup-node@v1
|
uses: actions/setup-node@v1
|
||||||
with: { node-version: '10' }
|
with: { node-version: '10' }
|
||||||
@@ -72,9 +75,6 @@ jobs:
|
|||||||
|
|
||||||
deploy-docs:
|
deploy-docs:
|
||||||
if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }}
|
if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }}
|
||||||
concurrency:
|
|
||||||
group: "pages"
|
|
||||||
cancel-in-progress: true
|
|
||||||
environment:
|
environment:
|
||||||
name: github-pages
|
name: github-pages
|
||||||
url: ${{ steps.deployment.outputs.page_url }}
|
url: ${{ steps.deployment.outputs.page_url }}
|
||||||
@@ -82,8 +82,5 @@ jobs:
|
|||||||
needs: docs
|
needs: docs
|
||||||
steps:
|
steps:
|
||||||
- name: Deploy to GitHub Pages
|
- name: Deploy to GitHub Pages
|
||||||
env:
|
|
||||||
FREEZE_DOCS: ${{ secrets.FREEZE_DOCS }}
|
|
||||||
if: ${{ !env.FREEZE_DOCS }}
|
|
||||||
id: deployment
|
id: deployment
|
||||||
uses: actions/deploy-pages@v1
|
uses: actions/deploy-pages@v1
|
||||||
|
|||||||
5
.github/workflows/editorconfig.yaml
vendored
@@ -5,15 +5,14 @@ on:
|
|||||||
branches: [ '*' ]
|
branches: [ '*' ]
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [ master ]
|
branches: [ master ]
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
editorconfig:
|
editorconfig:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v2
|
||||||
- name: .editorconfig Linter
|
- name: .editorconfig Linter
|
||||||
run: |
|
run: |
|
||||||
wget --no-verbose https://github.com/okalachev/editorconfig-checker/releases/download/1.2.1-disable-spaces-amount/ec-linux-amd64
|
wget --no-verbose https://github.com/okalachev/editorconfig-checker/releases/download/1.2.1-disable-spaces-amount/ec-linux-amd64
|
||||||
chmod +x ec-linux-amd64
|
chmod +x ec-linux-amd64
|
||||||
./ec-linux-amd64 -spaces-after-tabs -e "roslib.js|ros3d.js|eventemitter2.js|yaml.js|draw.cpp|BinUtils.swift|\.idea|apps/android/app|blockly/|clover_blocks/programs/|highlight/|python.js|Assets.xcassets|test_parser_pass.txt|test_node_failure.txt|aruco_pose/vendor|\.stl|\.dxf|\.dae|\.material"
|
./ec-linux-amd64 -spaces-after-tabs -e "roslib.js|ros3d.js|eventemitter2.js|json-to-pretty-yaml.js|draw.cpp|BinUtils.swift|\.idea|apps/android/app|blockly/|clover_blocks/programs/|highlight/|python.js|Assets.xcassets|test_parser_pass.txt|test_node_failure.txt|aruco_pose/vendor|\.stl|\.dxf|\.dae|\.material"
|
||||||
|
|||||||
@@ -113,9 +113,7 @@
|
|||||||
"VMware",
|
"VMware",
|
||||||
"DuoCam"
|
"DuoCam"
|
||||||
],
|
],
|
||||||
"code_blocks": false,
|
"code_blocks": false
|
||||||
"html_elements": false
|
|
||||||
},
|
},
|
||||||
"MD045": false,
|
"MD045": false
|
||||||
"MD051": false
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ Clover drone is used on a wide range of educational events, including [Copter Ha
|
|||||||
|
|
||||||
Preconfigured image for Raspberry Pi with installed and configured software, ready to fly, is available [in the Releases section](https://github.com/CopterExpress/clover/releases).
|
Preconfigured image for Raspberry Pi with installed and configured software, ready to fly, is available [in the Releases section](https://github.com/CopterExpress/clover/releases).
|
||||||
|
|
||||||

|

|
||||||

|

|
||||||
|
|
||||||
Image features:
|
Image features:
|
||||||
|
|||||||
@@ -251,5 +251,4 @@ if (CATKIN_ENABLE_TESTING)
|
|||||||
add_rostest(test/test_node_failure.test)
|
add_rostest(test/test_node_failure.test)
|
||||||
add_rostest(test/largemap.test)
|
add_rostest(test/largemap.test)
|
||||||
add_rostest(test/crash_opencv.test)
|
add_rostest(test/crash_opencv.test)
|
||||||
add_rostest(test/duplicate.test)
|
|
||||||
endif()
|
endif()
|
||||||
|
|||||||
@@ -43,8 +43,7 @@ It's recommended to run it within the same nodelet manager with the camera nodel
|
|||||||
* `~frame_id_prefix` (*string*) – prefix for TF transforms names, marker's ID is appended (default: `aruco_`)
|
* `~frame_id_prefix` (*string*) – prefix for TF transforms names, marker's ID is appended (default: `aruco_`)
|
||||||
* `~length` (*double*) – markers' sides length
|
* `~length` (*double*) – markers' sides length
|
||||||
* `~length_override` (*map*) – lengths of markers with specified ids
|
* `~length_override` (*map*) – lengths of markers with specified ids
|
||||||
* `~known_vertical` (*string*) – known vertical (Z axis) of all the markers as a frame
|
* `~known_tilt` (*string*) – known tilt (pitch and roll) of all the markers as a frame
|
||||||
* `~flip_vertical` – flip vertical vector
|
|
||||||
|
|
||||||
### Topics
|
### Topics
|
||||||
|
|
||||||
@@ -72,8 +71,7 @@ It's recommended to run it within the same nodelet manager with the camera nodel
|
|||||||
|
|
||||||
* `~map` – path to text file with markers list
|
* `~map` – path to text file with markers list
|
||||||
* `~frame_id` – published frame id (default: `aruco_map`)
|
* `~frame_id` – published frame id (default: `aruco_map`)
|
||||||
* `~known_vertical` – known vertical (Z axis) of markers map as a frame
|
* `~known_tilt` – known tilt (pitch and roll) of markers map as a frame
|
||||||
* `~flip_vertical` – flip vertical vector
|
|
||||||
* `~image_width` – debug image width (default: 2000)
|
* `~image_width` – debug image width (default: 2000)
|
||||||
* `~image_height` – debug image height (default: 2000)
|
* `~image_height` – debug image height (default: 2000)
|
||||||
* `~image_margin` – debug image margin (default: 200)
|
* `~image_margin` – debug image margin (default: 200)
|
||||||
|
|||||||
@@ -4,10 +4,7 @@ PACKAGE = "aruco_pose"
|
|||||||
from dynamic_reconfigure.parameter_generator_catkin import *
|
from dynamic_reconfigure.parameter_generator_catkin import *
|
||||||
import cv2.aruco
|
import cv2.aruco
|
||||||
|
|
||||||
try:
|
p = cv2.aruco.DetectorParameters_create()
|
||||||
p = cv2.aruco.DetectorParameters_create()
|
|
||||||
except AttributeError:
|
|
||||||
p = cv2.aruco.DetectorParameters()
|
|
||||||
|
|
||||||
gen = ParameterGenerator()
|
gen = ParameterGenerator()
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<?xml version="1.0"?>
|
<?xml version="1.0"?>
|
||||||
<package format="3">
|
<package format="2">
|
||||||
<name>aruco_pose</name>
|
<name>aruco_pose</name>
|
||||||
<version>0.25.0</version>
|
<version>0.23.0</version>
|
||||||
<description>Positioning with ArUco markers</description>
|
<description>Positioning with ArUco markers</description>
|
||||||
|
|
||||||
<maintainer email="okalachev@gmail.com">Oleg Kalachev</maintainer>
|
<maintainer email="okalachev@gmail.com">Oleg Kalachev</maintainer>
|
||||||
@@ -28,8 +28,6 @@
|
|||||||
<depend>sensor_msgs</depend>
|
<depend>sensor_msgs</depend>
|
||||||
<depend>rostest</depend>
|
<depend>rostest</depend>
|
||||||
<depend>dynamic_reconfigure</depend>
|
<depend>dynamic_reconfigure</depend>
|
||||||
<depend condition="$ROS_PYTHON_VERSION == 2">python-docopt</depend>
|
|
||||||
<depend condition="$ROS_PYTHON_VERSION == 3">python3-docopt</depend>
|
|
||||||
|
|
||||||
<test_depend>image_publisher</test_depend>
|
<test_depend>image_publisher</test_depend>
|
||||||
<test_depend>ros_pytest</test_depend>
|
<test_depend>ros_pytest</test_depend>
|
||||||
|
|||||||
@@ -50,7 +50,6 @@
|
|||||||
#include <aruco_pose/DetectorConfig.h>
|
#include <aruco_pose/DetectorConfig.h>
|
||||||
#include <aruco_pose/SetMarkers.h>
|
#include <aruco_pose/SetMarkers.h>
|
||||||
|
|
||||||
#include "draw.h"
|
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
@@ -72,12 +71,11 @@ private:
|
|||||||
ros::Publisher markers_pub_, vis_markers_pub_;
|
ros::Publisher markers_pub_, vis_markers_pub_;
|
||||||
ros::Subscriber map_markers_sub_;
|
ros::Subscriber map_markers_sub_;
|
||||||
ros::ServiceServer set_markers_srv_;
|
ros::ServiceServer set_markers_srv_;
|
||||||
bool estimate_poses_, send_tf_, flip_vertical_, auto_flip_, use_map_markers_;
|
bool estimate_poses_, send_tf_, auto_flip_;
|
||||||
bool waiting_for_map_;
|
|
||||||
double length_;
|
double length_;
|
||||||
ros::Duration transform_timeout_;
|
ros::Duration transform_timeout_;
|
||||||
std::unordered_map<int, double> length_override_;
|
std::unordered_map<int, double> length_override_;
|
||||||
std::string frame_id_prefix_, known_vertical_;
|
std::string frame_id_prefix_, known_tilt_;
|
||||||
Mat camera_matrix_, dist_coeffs_;
|
Mat camera_matrix_, dist_coeffs_;
|
||||||
aruco_pose::MarkerArray array_;
|
aruco_pose::MarkerArray array_;
|
||||||
std::unordered_set<int> map_markers_ids_;
|
std::unordered_set<int> map_markers_ids_;
|
||||||
@@ -97,8 +95,6 @@ public:
|
|||||||
dictionary = nh_priv_.param("dictionary", 2);
|
dictionary = nh_priv_.param("dictionary", 2);
|
||||||
estimate_poses_ = nh_priv_.param("estimate_poses", true);
|
estimate_poses_ = nh_priv_.param("estimate_poses", true);
|
||||||
send_tf_ = nh_priv_.param("send_tf", true);
|
send_tf_ = nh_priv_.param("send_tf", true);
|
||||||
use_map_markers_ = nh_priv_.param("use_map_markers", false);
|
|
||||||
waiting_for_map_ = use_map_markers_;
|
|
||||||
if (estimate_poses_ && !nh_priv_.getParam("length", length_)) {
|
if (estimate_poses_ && !nh_priv_.getParam("length", length_)) {
|
||||||
NODELET_FATAL("can't estimate marker's poses as ~length parameter is not defined");
|
NODELET_FATAL("can't estimate marker's poses as ~length parameter is not defined");
|
||||||
ros::shutdown();
|
ros::shutdown();
|
||||||
@@ -106,8 +102,7 @@ public:
|
|||||||
readLengthOverride(nh_priv_);
|
readLengthOverride(nh_priv_);
|
||||||
transform_timeout_ = ros::Duration(nh_priv_.param("transform_timeout", 0.02));
|
transform_timeout_ = ros::Duration(nh_priv_.param("transform_timeout", 0.02));
|
||||||
|
|
||||||
known_vertical_ = nh_priv_.param("known_vertical", nh_priv_.param("known_tilt", std::string(""))); // known_tilt is an old name
|
known_tilt_ = nh_priv_.param<std::string>("known_tilt", "");
|
||||||
flip_vertical_ = nh_priv_.param<bool>("flip_vertical", false);
|
|
||||||
auto_flip_ = nh_priv_.param("auto_flip", false);
|
auto_flip_ = nh_priv_.param("auto_flip", false);
|
||||||
|
|
||||||
frame_id_prefix_ = nh_priv_.param<std::string>("frame_id_prefix", "aruco_");
|
frame_id_prefix_ = nh_priv_.param<std::string>("frame_id_prefix", "aruco_");
|
||||||
@@ -138,15 +133,14 @@ private:
|
|||||||
void imageCallback(const sensor_msgs::ImageConstPtr& msg, const sensor_msgs::CameraInfoConstPtr &cinfo)
|
void imageCallback(const sensor_msgs::ImageConstPtr& msg, const sensor_msgs::CameraInfoConstPtr &cinfo)
|
||||||
{
|
{
|
||||||
if (!enabled_) return;
|
if (!enabled_) return;
|
||||||
if (waiting_for_map_) return;
|
|
||||||
|
|
||||||
Mat image = cv_bridge::toCvShare(msg)->image;
|
Mat image = cv_bridge::toCvShare(msg, "bgr8")->image;
|
||||||
|
|
||||||
vector<int> ids;
|
vector<int> ids;
|
||||||
vector<vector<cv::Point2f>> corners, rejected;
|
vector<vector<cv::Point2f>> corners, rejected;
|
||||||
vector<cv::Vec3d> rvecs, tvecs;
|
vector<cv::Vec3d> rvecs, tvecs;
|
||||||
vector<cv::Point3f> obj_points;
|
vector<cv::Point3f> obj_points;
|
||||||
geometry_msgs::TransformStamped vertical;
|
geometry_msgs::TransformStamped snap_to;
|
||||||
|
|
||||||
// Detect markers
|
// Detect markers
|
||||||
cv::aruco::detectMarkers(image, dictionary_, corners, ids, parameters_, rejected);
|
cv::aruco::detectMarkers(image, dictionary_, corners, ids, parameters_, rejected);
|
||||||
@@ -181,20 +175,18 @@ private:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!known_vertical_.empty()) {
|
if (!known_tilt_.empty()) {
|
||||||
try {
|
try {
|
||||||
vertical = tf_buffer_->lookupTransform(msg->header.frame_id, known_vertical_,
|
snap_to = tf_buffer_->lookupTransform(msg->header.frame_id, known_tilt_,
|
||||||
msg->header.stamp, transform_timeout_);
|
msg->header.stamp, transform_timeout_);
|
||||||
} catch (const tf2::TransformException& e) {
|
} catch (const tf2::TransformException& e) {
|
||||||
NODELET_WARN_THROTTLE(5, "can't retrieve known vertical: %s", e.what());
|
NODELET_WARN_THROTTLE(5, "can't snap: %s", e.what());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
array_.markers.reserve(ids.size());
|
array_.markers.reserve(ids.size());
|
||||||
aruco_pose::Marker marker;
|
aruco_pose::Marker marker;
|
||||||
vector<geometry_msgs::TransformStamped> transforms;
|
|
||||||
transforms.reserve(ids.size());
|
|
||||||
geometry_msgs::TransformStamped transform;
|
geometry_msgs::TransformStamped transform;
|
||||||
transform.header.stamp = msg->header.stamp;
|
transform.header.stamp = msg->header.stamp;
|
||||||
transform.header.frame_id = msg->header.frame_id;
|
transform.header.frame_id = msg->header.frame_id;
|
||||||
@@ -207,38 +199,25 @@ private:
|
|||||||
if (estimate_poses_) {
|
if (estimate_poses_) {
|
||||||
fillPose(marker.pose, rvecs[i], tvecs[i]);
|
fillPose(marker.pose, rvecs[i], tvecs[i]);
|
||||||
|
|
||||||
// apply known vertical (if enabled and vertical frame available)
|
// snap orientation (if enabled and snap frame available)
|
||||||
if (!known_vertical_.empty() && !vertical.header.frame_id.empty()) {
|
if (!known_tilt_.empty() && !snap_to.header.frame_id.empty()) {
|
||||||
applyVertical(marker.pose.orientation, vertical.transform.rotation, false, auto_flip_);
|
snapOrientation(marker.pose.orientation, snap_to.transform.rotation, auto_flip_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: check IDs are unique
|
||||||
if (send_tf_) {
|
if (send_tf_) {
|
||||||
transform.child_frame_id = getChildFrameId(ids[i]);
|
transform.child_frame_id = getChildFrameId(ids[i]);
|
||||||
|
|
||||||
// check if such static transform is in the map
|
// check if such static transform is in the map
|
||||||
if (map_markers_ids_.find(ids[i]) == map_markers_ids_.end()) {
|
if (map_markers_ids_.find(ids[i]) == map_markers_ids_.end()) {
|
||||||
// check if a markers with that id is already added
|
transform.transform.rotation = marker.pose.orientation;
|
||||||
bool send = true;
|
fillTranslation(transform.transform.translation, tvecs[i]);
|
||||||
for (auto &t : transforms) {
|
br_->sendTransform(transform);
|
||||||
if (t.child_frame_id == transform.child_frame_id) {
|
|
||||||
send = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (send) {
|
|
||||||
transform.transform.rotation = marker.pose.orientation;
|
|
||||||
fillTranslation(transform.transform.translation, tvecs[i]);
|
|
||||||
transforms.push_back(transform);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
array_.markers.push_back(marker);
|
array_.markers.push_back(marker);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (send_tf_) {
|
|
||||||
br_->sendTransform(transforms);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
markers_pub_.publish(array_);
|
markers_pub_.publish(array_);
|
||||||
@@ -265,7 +244,8 @@ private:
|
|||||||
cv::aruco::drawDetectedMarkers(debug, corners, ids); // draw markers
|
cv::aruco::drawDetectedMarkers(debug, corners, ids); // draw markers
|
||||||
if (estimate_poses_)
|
if (estimate_poses_)
|
||||||
for (unsigned int i = 0; i < ids.size(); i++)
|
for (unsigned int i = 0; i < ids.size(); i++)
|
||||||
_drawAxis(debug, camera_matrix_, dist_coeffs_, rvecs[i], tvecs[i], getMarkerLength(ids[i]));
|
cv::aruco::drawAxis(debug, camera_matrix_, dist_coeffs_,
|
||||||
|
rvecs[i], tvecs[i], getMarkerLength(ids[i]));
|
||||||
|
|
||||||
cv_bridge::CvImage out_msg;
|
cv_bridge::CvImage out_msg;
|
||||||
out_msg.header.frame_id = msg->header.frame_id;
|
out_msg.header.frame_id = msg->header.frame_id;
|
||||||
@@ -400,13 +380,7 @@ private:
|
|||||||
map_markers_ids_.clear();
|
map_markers_ids_.clear();
|
||||||
for (auto const& marker : msg.markers) {
|
for (auto const& marker : msg.markers) {
|
||||||
map_markers_ids_.insert(marker.id);
|
map_markers_ids_.insert(marker.id);
|
||||||
if (use_map_markers_) {
|
|
||||||
if (length_override_.find(marker.id) == length_override_.end()) {
|
|
||||||
length_override_[marker.id] = marker.length;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
waiting_for_map_ = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void paramCallback(aruco_pose::DetectorConfig &config, uint32_t level)
|
void paramCallback(aruco_pose::DetectorConfig &config, uint32_t level)
|
||||||
|
|||||||
@@ -81,9 +81,9 @@ private:
|
|||||||
bool enabled_ = true;
|
bool enabled_ = true;
|
||||||
std::string type_;
|
std::string type_;
|
||||||
visualization_msgs::MarkerArray vis_array_;
|
visualization_msgs::MarkerArray vis_array_;
|
||||||
std::string known_vertical_, map_, markers_frame_, markers_parent_frame_;
|
std::string known_tilt_, map_, markers_frame_, markers_parent_frame_;
|
||||||
int image_width_, image_height_, image_margin_;
|
int image_width_, image_height_, image_margin_;
|
||||||
bool flip_vertical_, auto_flip_, image_axis_, put_markers_count_to_covariance_;
|
bool auto_flip_, image_axis_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
virtual void onInit()
|
virtual void onInit()
|
||||||
@@ -104,14 +104,12 @@ public:
|
|||||||
|
|
||||||
type_ = nh_priv_.param<std::string>("type", "map");
|
type_ = nh_priv_.param<std::string>("type", "map");
|
||||||
transform_.child_frame_id = nh_priv_.param<std::string>("frame_id", "aruco_map");
|
transform_.child_frame_id = nh_priv_.param<std::string>("frame_id", "aruco_map");
|
||||||
known_vertical_ = nh_priv_.param("known_vertical", nh_priv_.param("known_tilt", std::string(""))); // known_tilt is an old name
|
known_tilt_ = nh_priv_.param<std::string>("known_tilt", "");
|
||||||
flip_vertical_ = nh_priv_.param<bool>("flip_vertical", false);
|
|
||||||
auto_flip_ = nh_priv_.param("auto_flip", false);
|
auto_flip_ = nh_priv_.param("auto_flip", false);
|
||||||
image_width_ = nh_priv_.param("image_width" , 2000);
|
image_width_ = nh_priv_.param("image_width" , 2000);
|
||||||
image_height_ = nh_priv_.param("image_height", 2000);
|
image_height_ = nh_priv_.param("image_height", 2000);
|
||||||
image_margin_ = nh_priv_.param("image_margin", 200);
|
image_margin_ = nh_priv_.param("image_margin", 200);
|
||||||
image_axis_ = nh_priv_.param("image_axis", true);
|
image_axis_ = nh_priv_.param("image_axis", true);
|
||||||
put_markers_count_to_covariance_ = nh_priv_.param("put_markers_count_to_covariance", false);
|
|
||||||
markers_parent_frame_ = nh_priv_.param<std::string>("markers/frame_id", transform_.child_frame_id);
|
markers_parent_frame_ = nh_priv_.param<std::string>("markers/frame_id", transform_.child_frame_id);
|
||||||
markers_frame_ = nh_priv_.param<std::string>("markers/child_frame_id_prefix", "");
|
markers_frame_ = nh_priv_.param<std::string>("markers/child_frame_id_prefix", "");
|
||||||
|
|
||||||
@@ -179,21 +177,7 @@ public:
|
|||||||
corners.push_back(marker_corners);
|
corners.push_back(marker_corners);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (put_markers_count_to_covariance_) {
|
if (known_tilt_.empty()) {
|
||||||
// HACK: pass markers count using covariance field
|
|
||||||
int valid_markers = 0;
|
|
||||||
for (auto const &marker : markers->markers) {
|
|
||||||
for (auto const &board_marker : board_->ids) {
|
|
||||||
if (board_marker == marker.id) {
|
|
||||||
valid_markers++;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pose_.pose.covariance[0] = valid_markers;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (known_vertical_.empty()) {
|
|
||||||
// simple estimation
|
// simple estimation
|
||||||
valid = cv::aruco::estimatePoseBoard(corners, ids, board_, camera_matrix_, dist_coeffs_,
|
valid = cv::aruco::estimatePoseBoard(corners, ids, board_, camera_matrix_, dist_coeffs_,
|
||||||
rvec, tvec, false);
|
rvec, tvec, false);
|
||||||
@@ -207,7 +191,7 @@ public:
|
|||||||
|
|
||||||
} else {
|
} else {
|
||||||
Mat obj_points, img_points;
|
Mat obj_points, img_points;
|
||||||
// estimation with known vertical
|
// estimation with "snapping"
|
||||||
cv::aruco::getBoardObjectAndImagePoints(board_, corners, ids, obj_points, img_points);
|
cv::aruco::getBoardObjectAndImagePoints(board_, corners, ids, obj_points, img_points);
|
||||||
if (obj_points.empty()) goto publish_debug;
|
if (obj_points.empty()) goto publish_debug;
|
||||||
|
|
||||||
@@ -219,11 +203,11 @@ public:
|
|||||||
|
|
||||||
fillTransform(transform_.transform, rvec, tvec);
|
fillTransform(transform_.transform, rvec, tvec);
|
||||||
try {
|
try {
|
||||||
geometry_msgs::TransformStamped vertical = tf_buffer_.lookupTransform(markers->header.frame_id,
|
geometry_msgs::TransformStamped snap_to = tf_buffer_.lookupTransform(markers->header.frame_id,
|
||||||
known_vertical_, markers->header.stamp, ros::Duration(0.02));
|
known_tilt_, markers->header.stamp, ros::Duration(0.02));
|
||||||
applyVertical(transform_.transform.rotation, vertical.transform.rotation, flip_vertical_, auto_flip_);
|
snapOrientation(transform_.transform.rotation, snap_to.transform.rotation, auto_flip_);
|
||||||
} catch (const tf2::TransformException& e) {
|
} catch (const tf2::TransformException& e) {
|
||||||
NODELET_WARN_THROTTLE(1, "can't retrieve known vertical: %s", e.what());
|
NODELET_WARN_THROTTLE(1, "can't snap: %s", e.what());
|
||||||
}
|
}
|
||||||
|
|
||||||
geometry_msgs::TransformStamped shift;
|
geometry_msgs::TransformStamped shift;
|
||||||
@@ -519,7 +503,7 @@ publish_debug:
|
|||||||
vis_marker.pose.position.x = x;
|
vis_marker.pose.position.x = x;
|
||||||
vis_marker.pose.position.y = y;
|
vis_marker.pose.position.y = y;
|
||||||
vis_marker.pose.position.z = z;
|
vis_marker.pose.position.z = z;
|
||||||
tf::quaternionTFToMsg(q, vis_marker.pose.orientation);
|
tf::quaternionTFToMsg(q, marker.pose.orientation);
|
||||||
vis_marker.frame_locked = true;
|
vis_marker.frame_locked = true;
|
||||||
vis_array_.markers.push_back(vis_marker);
|
vis_array_.markers.push_back(vis_marker);
|
||||||
|
|
||||||
|
|||||||
@@ -106,25 +106,26 @@ inline bool isFlipped(tf::Quaternion& q)
|
|||||||
return (abs(pitch) > M_PI / 2) || (abs(roll) > M_PI / 2);
|
return (abs(pitch) > M_PI / 2) || (abs(roll) > M_PI / 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Apply a vertical to an orientation */
|
/* Set roll and pitch from "from" to "to", keeping yaw */
|
||||||
inline void applyVertical(geometry_msgs::Quaternion& orientation, const geometry_msgs::Quaternion& vertical,
|
inline void snapOrientation(geometry_msgs::Quaternion& to, const geometry_msgs::Quaternion& from, bool auto_flip = false)
|
||||||
bool flip_vertical = false, bool auto_flip = false) // editorconfig-checker-disable-line
|
|
||||||
{
|
{
|
||||||
tf::Quaternion _vertical, _orientation;
|
tf::Quaternion _from, _to;
|
||||||
tf::quaternionMsgToTF(vertical, _vertical);
|
tf::quaternionMsgToTF(from, _from);
|
||||||
tf::quaternionMsgToTF(orientation, _orientation);
|
tf::quaternionMsgToTF(to, _to);
|
||||||
|
|
||||||
if (flip_vertical || (auto_flip && !isFlipped(_orientation))) {
|
if (auto_flip) {
|
||||||
static const tf::Quaternion flip = tf::createQuaternionFromRPY(M_PI, 0, 0);
|
if (!isFlipped(_from)) {
|
||||||
_vertical *= flip; // flip vertical
|
static const tf::Quaternion flip = tf::createQuaternionFromRPY(M_PI, 0, 0);
|
||||||
|
_from *= flip; // flip "from"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto diff = tf::Matrix3x3(_orientation).transposeTimes(tf::Matrix3x3(_vertical));
|
auto diff = tf::Matrix3x3(_to).transposeTimes(tf::Matrix3x3(_from));
|
||||||
double _, yaw;
|
double _, yaw;
|
||||||
diff.getRPY(_, _, yaw);
|
diff.getRPY(_, _, yaw);
|
||||||
auto q = tf::createQuaternionFromRPY(0, 0, -yaw);
|
auto q = tf::createQuaternionFromRPY(0, 0, -yaw);
|
||||||
_vertical = _vertical * q; // set yaw from orientation to vertical
|
_from = _from * q; // set yaw from "to" to "from"
|
||||||
tf::quaternionTFToMsg(_vertical, orientation); // set vertical to orientation
|
tf::quaternionTFToMsg(_from, to); // set "from" to "to"
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void transformToPose(const geometry_msgs::Transform& transform, geometry_msgs::Pose& pose)
|
inline void transformToPose(const geometry_msgs::Transform& transform, geometry_msgs::Pose& pose)
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import tf2_geometry_msgs
|
|||||||
from geometry_msgs.msg import PoseWithCovarianceStamped
|
from geometry_msgs.msg import PoseWithCovarianceStamped
|
||||||
from sensor_msgs.msg import Image
|
from sensor_msgs.msg import Image
|
||||||
from aruco_pose.msg import MarkerArray
|
from aruco_pose.msg import MarkerArray
|
||||||
from visualization_msgs.msg import MarkerArray as VisMarkerArray, Marker as VisMarker
|
from visualization_msgs.msg import MarkerArray as VisMarkerArray
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
@@ -199,36 +199,6 @@ def test_map_markers(node):
|
|||||||
|
|
||||||
def test_map_visualization(node):
|
def test_map_visualization(node):
|
||||||
vis = rospy.wait_for_message('aruco_map/visualization', VisMarkerArray, timeout=5)
|
vis = rospy.wait_for_message('aruco_map/visualization', VisMarkerArray, timeout=5)
|
||||||
assert len(vis.markers) == 7
|
|
||||||
assert vis.markers[0].header.frame_id == 'aruco_map'
|
|
||||||
assert vis.markers[0].type == VisMarker.CUBE
|
|
||||||
assert vis.markers[0].action == VisMarker.ADD
|
|
||||||
assert vis.markers[0].pose.position.x == 0
|
|
||||||
assert vis.markers[0].pose.position.y == 0
|
|
||||||
assert vis.markers[0].pose.position.z == 0
|
|
||||||
assert vis.markers[0].pose.orientation.x == 0
|
|
||||||
assert vis.markers[0].pose.orientation.y == 0
|
|
||||||
assert vis.markers[0].pose.orientation.z == 0
|
|
||||||
assert vis.markers[0].pose.orientation.w == 1
|
|
||||||
assert vis.markers[0].scale.x == approx(0.33)
|
|
||||||
assert vis.markers[0].scale.y == approx(0.33)
|
|
||||||
assert vis.markers[0].scale.z == approx(0.001)
|
|
||||||
assert vis.markers[1].pose.position.x == 1
|
|
||||||
assert vis.markers[1].pose.position.y == 0
|
|
||||||
assert vis.markers[1].pose.position.z == 0
|
|
||||||
assert vis.markers[1].pose.orientation.x == 0
|
|
||||||
assert vis.markers[1].pose.orientation.y == 0
|
|
||||||
assert vis.markers[1].pose.orientation.z == 0
|
|
||||||
assert vis.markers[1].pose.orientation.w == 1
|
|
||||||
# non-zero yaw marker:
|
|
||||||
assert vis.markers[4].scale.x == approx(0.5)
|
|
||||||
assert vis.markers[4].pose.position.x == approx(0.5)
|
|
||||||
assert vis.markers[4].pose.position.y == 2
|
|
||||||
assert vis.markers[4].pose.position.z == 0
|
|
||||||
assert vis.markers[4].pose.orientation.x == 0
|
|
||||||
assert vis.markers[4].pose.orientation.y == 0
|
|
||||||
assert vis.markers[4].pose.orientation.z == approx(0.5646424733950354)
|
|
||||||
assert vis.markers[4].pose.orientation.w == approx(0.8253356149096783)
|
|
||||||
|
|
||||||
def test_map_debug(node):
|
def test_map_debug(node):
|
||||||
img = rospy.wait_for_message('aruco_map/debug', Image, timeout=5)
|
img = rospy.wait_for_message('aruco_map/debug', Image, timeout=5)
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 62 KiB |
@@ -1,8 +0,0 @@
|
|||||||
import pytest
|
|
||||||
import subprocess
|
|
||||||
|
|
||||||
def test_no_tf_repeated_data():
|
|
||||||
# `/rosout` acts weirdly inside rostest, so using a subprocess
|
|
||||||
cmd = """python -c 'import rospy, tf; rospy.init_node("foo"); listener = tf.TransformListener(); rospy.sleep(2)'"""
|
|
||||||
output = str(subprocess.check_output(cmd, shell=True, stderr=subprocess.STDOUT))
|
|
||||||
assert 'TF_REPEATED_DATA' not in output, 'TF_REPEATED_DATA was logged on duplicate markers'
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
<launch>
|
|
||||||
<node pkg="image_publisher" type="image_publisher" name="main_camera" args="$(find aruco_pose)/test/duplicate.png">
|
|
||||||
<param name="frame_id" value="main_camera_optical"/>
|
|
||||||
<param name="publish_rate" value="10"/>
|
|
||||||
<param name="camera_info_url" value="file://$(find aruco_pose)/test/camera_info.yaml" />
|
|
||||||
</node>
|
|
||||||
|
|
||||||
<node pkg="nodelet" type="nodelet" name="nodelet_manager" args="manager" required="true"/>
|
|
||||||
|
|
||||||
<node pkg="nodelet" clear_params="true" type="nodelet" name="aruco_detect" args="load aruco_pose/aruco_detect nodelet_manager" required="true">
|
|
||||||
<remap from="image_raw" to="main_camera/image_raw"/>
|
|
||||||
<remap from="camera_info" to="main_camera/camera_info"/>
|
|
||||||
<param name="length" value="0.33"/>
|
|
||||||
<param name="estimate_poses" value="true"/>
|
|
||||||
<param name="send_tf" value="true"/>
|
|
||||||
<param name="cornerRefinementMethod" value="1"/>
|
|
||||||
</node>
|
|
||||||
|
|
||||||
<param name="test_module" value="$(find aruco_pose)/test/duplicate.py"/>
|
|
||||||
<test test-name="aruco_pose_test" pkg="ros_pytest" type="ros_pytest_runner"/>
|
|
||||||
</launch>
|
|
||||||
@@ -16,726 +16,3 @@ web_video_server:
|
|||||||
ws281x:
|
ws281x:
|
||||||
debian:
|
debian:
|
||||||
buster: [ros-noetic-ws281x]
|
buster: [ros-noetic-ws281x]
|
||||||
catkin:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-catkin]
|
|
||||||
genmsg:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-genmsg]
|
|
||||||
gencpp:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-gencpp]
|
|
||||||
geneus:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-geneus]
|
|
||||||
genlisp:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-genlisp]
|
|
||||||
gennodejs:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-gennodejs]
|
|
||||||
genpy:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-genpy]
|
|
||||||
bond_core:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-bond-core]
|
|
||||||
cmake_modules:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-cmake-modules]
|
|
||||||
class_loader:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-class-loader]
|
|
||||||
common_msgs:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-common-msgs]
|
|
||||||
common_tutorials:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-common-tutorials]
|
|
||||||
cpp_common:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-cpp-common]
|
|
||||||
desktop:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-desktop]
|
|
||||||
diagnostics:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-diagnostics]
|
|
||||||
executive_smach:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-executive-smach]
|
|
||||||
geometry:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-geometry]
|
|
||||||
geometry_tutorials:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-geometry-tutorials]
|
|
||||||
gl_dependency:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-gl-dependency]
|
|
||||||
image_common:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-image-common]
|
|
||||||
image_pipeline:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-image-pipeline]
|
|
||||||
image_transport_plugins:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-image-transport-plugins]
|
|
||||||
laser_pipeline:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-laser-pipeline]
|
|
||||||
mavlink:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-mavlink]
|
|
||||||
media_export:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-media-export]
|
|
||||||
message_generation:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-message-generation]
|
|
||||||
message_runtime:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-message-runtime]
|
|
||||||
mk:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-mk]
|
|
||||||
nodelet_core:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-nodelet-core]
|
|
||||||
orocos_kdl:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-orocos-kdl]
|
|
||||||
perception:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-perception]
|
|
||||||
perception_pcl:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-perception-pcl]
|
|
||||||
python_orocos_kdl:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-python-orocos-kdl]
|
|
||||||
qt_dotgraph:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-qt-dotgraph]
|
|
||||||
qt_gui:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-qt-gui]
|
|
||||||
qt_gui_py_common:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-qt-gui-py-common]
|
|
||||||
qwt_dependency:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-qwt-dependency]
|
|
||||||
robot:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-robot]
|
|
||||||
ros:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-ros]
|
|
||||||
ros_base:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-ros-base]
|
|
||||||
ros_comm:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-ros-comm]
|
|
||||||
ros_core:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-ros-core]
|
|
||||||
ros_environment:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-ros-environment]
|
|
||||||
ros_tutorials:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-ros-tutorials]
|
|
||||||
rosapi:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-rosapi]
|
|
||||||
rosbag_migration_rule:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-rosbag-migration-rule]
|
|
||||||
rosbash:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-rosbash]
|
|
||||||
rosboost_cfg:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-rosboost-cfg]
|
|
||||||
rosbridge_server:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-rosbridge-server]
|
|
||||||
rosbridge_suite:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-rosbridge-suite]
|
|
||||||
rosbuild:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-rosbuild]
|
|
||||||
rosclean:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-rosclean]
|
|
||||||
roscpp_core:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-roscpp-core]
|
|
||||||
roscpp_traits:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-roscpp-traits]
|
|
||||||
roscreate:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-roscreate]
|
|
||||||
rosgraph:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-rosgraph]
|
|
||||||
roslang:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-roslang]
|
|
||||||
roslint:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-roslint]
|
|
||||||
roslisp:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-roslisp]
|
|
||||||
rosmake:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-rosmake]
|
|
||||||
rosmaster:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-rosmaster]
|
|
||||||
rospack:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-rospack]
|
|
||||||
roslib:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-roslib]
|
|
||||||
rosparam:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-rosparam]
|
|
||||||
rospy:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-rospy]
|
|
||||||
rosserial:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-rosserial]
|
|
||||||
rosserial_msgs:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-rosserial-msgs]
|
|
||||||
rosserial_python:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-rosserial-python]
|
|
||||||
rosservice:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-rosservice]
|
|
||||||
rostime:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-rostime]
|
|
||||||
roscpp_serialization:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-roscpp-serialization]
|
|
||||||
python_qt_binding:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-python-qt-binding]
|
|
||||||
roslaunch:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-roslaunch]
|
|
||||||
rosunit:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-rosunit]
|
|
||||||
angles:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-angles]
|
|
||||||
libmavconn:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-libmavconn]
|
|
||||||
rosconsole:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-rosconsole]
|
|
||||||
pluginlib:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-pluginlib]
|
|
||||||
qt_gui_cpp:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-qt-gui-cpp]
|
|
||||||
resource_retriever:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-resource-retriever]
|
|
||||||
rosconsole_bridge:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-rosconsole-bridge]
|
|
||||||
roslz4:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-roslz4]
|
|
||||||
rosserial_client:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-rosserial-client]
|
|
||||||
rostest:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-rostest]
|
|
||||||
rqt_action:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-rqt-action]
|
|
||||||
rqt_bag:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-rqt-bag]
|
|
||||||
rqt_bag_plugins:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-rqt-bag-plugins]
|
|
||||||
rqt_common_plugins:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-rqt-common-plugins]
|
|
||||||
rqt_console:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-rqt-console]
|
|
||||||
rqt_dep:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-rqt-dep]
|
|
||||||
rqt_graph:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-rqt-graph]
|
|
||||||
rqt_gui:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-rqt-gui]
|
|
||||||
rqt_logger_level:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-rqt-logger-level]
|
|
||||||
rqt_moveit:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-rqt-moveit]
|
|
||||||
rqt_msg:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-rqt-msg]
|
|
||||||
rqt_nav_view:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-rqt-nav-view]
|
|
||||||
rqt_plot:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-rqt-plot]
|
|
||||||
rqt_pose_view:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-rqt-pose-view]
|
|
||||||
rqt_publisher:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-rqt-publisher]
|
|
||||||
rqt_py_console:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-rqt-py-console]
|
|
||||||
rqt_reconfigure:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-rqt-reconfigure]
|
|
||||||
rqt_robot_dashboard:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-rqt-robot-dashboard]
|
|
||||||
rqt_robot_monitor:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-rqt-robot-monitor]
|
|
||||||
rqt_robot_plugins:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-rqt-robot-plugins]
|
|
||||||
rqt_robot_steering:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-rqt-robot-steering]
|
|
||||||
rqt_runtime_monitor:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-rqt-runtime-monitor]
|
|
||||||
rqt_service_caller:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-rqt-service-caller]
|
|
||||||
rqt_shell:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-rqt-shell]
|
|
||||||
rqt_srv:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-rqt-srv]
|
|
||||||
rqt_tf_tree:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-rqt-tf-tree]
|
|
||||||
rqt_top:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-rqt-top]
|
|
||||||
rqt_topic:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-rqt-topic]
|
|
||||||
rqt_web:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-rqt-web]
|
|
||||||
smach:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-smach]
|
|
||||||
smclib:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-smclib]
|
|
||||||
std_msgs:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-std-msgs]
|
|
||||||
actionlib_msgs:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-actionlib-msgs]
|
|
||||||
bond:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-bond]
|
|
||||||
diagnostic_msgs:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-diagnostic-msgs]
|
|
||||||
geometry_msgs:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-geometry-msgs]
|
|
||||||
eigen_conversions:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-eigen-conversions]
|
|
||||||
kdl_conversions:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-kdl-conversions]
|
|
||||||
nav_msgs:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-nav-msgs]
|
|
||||||
rosbridge_msgs:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-rosbridge-msgs]
|
|
||||||
rosgraph_msgs:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-rosgraph-msgs]
|
|
||||||
rosmsg:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-rosmsg]
|
|
||||||
rqt_py_common:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-rqt-py-common]
|
|
||||||
shape_msgs:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-shape-msgs]
|
|
||||||
smach_msgs:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-smach-msgs]
|
|
||||||
std_srvs:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-std-srvs]
|
|
||||||
tf2_msgs:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-tf2-msgs]
|
|
||||||
tf2:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-tf2]
|
|
||||||
tf2_eigen:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-tf2-eigen]
|
|
||||||
trajectory_msgs:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-trajectory-msgs]
|
|
||||||
control_msgs:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-control-msgs]
|
|
||||||
urdf_parser_plugin:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-urdf-parser-plugin]
|
|
||||||
urdfdom_py:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-urdfdom-py]
|
|
||||||
uuid_msgs:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-uuid-msgs]
|
|
||||||
geographic_msgs:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-geographic-msgs]
|
|
||||||
vision_opencv:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-vision-opencv]
|
|
||||||
visualization_msgs:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-visualization-msgs]
|
|
||||||
visualization_tutorials:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-visualization-tutorials]
|
|
||||||
viz:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-viz]
|
|
||||||
webkit_dependency:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-webkit-dependency]
|
|
||||||
xmlrpcpp:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-xmlrpcpp]
|
|
||||||
roscpp:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-roscpp]
|
|
||||||
bondcpp:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-bondcpp]
|
|
||||||
bondpy:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-bondpy]
|
|
||||||
nodelet:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-nodelet]
|
|
||||||
nodelet_tutorial_math:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-nodelet-tutorial-math]
|
|
||||||
pluginlib_tutorials:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-pluginlib-tutorials]
|
|
||||||
roscpp_tutorials:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-roscpp-tutorials]
|
|
||||||
rosout:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-rosout]
|
|
||||||
camera_calibration:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-camera-calibration]
|
|
||||||
diagnostic_aggregator:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-diagnostic-aggregator]
|
|
||||||
diagnostic_updater:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-diagnostic-updater]
|
|
||||||
diagnostic_common_diagnostics:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-diagnostic-common-diagnostics]
|
|
||||||
dynamic_reconfigure:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-dynamic-reconfigure]
|
|
||||||
filters:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-filters]
|
|
||||||
joint_state_publisher:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-joint-state-publisher]
|
|
||||||
message_filters:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-message-filters]
|
|
||||||
rosauth:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-rosauth]
|
|
||||||
rosbag_storage:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-rosbag-storage]
|
|
||||||
rosnode:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-rosnode]
|
|
||||||
rospy_tutorials:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-rospy-tutorials]
|
|
||||||
rosshow:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-rosshow]
|
|
||||||
rostopic:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-rostopic]
|
|
||||||
rqt_gui_cpp:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-rqt-gui-cpp]
|
|
||||||
rqt_gui_py:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-rqt-gui-py]
|
|
||||||
self_test:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-self-test]
|
|
||||||
smach_ros:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-smach-ros]
|
|
||||||
tf2_py:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-tf2-py]
|
|
||||||
topic_tools:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-topic-tools]
|
|
||||||
rosbag:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-rosbag]
|
|
||||||
actionlib:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-actionlib]
|
|
||||||
actionlib_tutorials:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-actionlib-tutorials]
|
|
||||||
diagnostic_analysis:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-diagnostic-analysis]
|
|
||||||
nodelet_topic_tools:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-nodelet-topic-tools]
|
|
||||||
roswtf:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-roswtf]
|
|
||||||
rqt_launch:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-rqt-launch]
|
|
||||||
sensor_msgs:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-sensor-msgs]
|
|
||||||
camera_calibration_parsers:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-camera-calibration-parsers]
|
|
||||||
cv_bridge:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-cv-bridge]
|
|
||||||
image_geometry:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-image-geometry]
|
|
||||||
image_transport:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-image-transport]
|
|
||||||
camera_info_manager:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-camera-info-manager]
|
|
||||||
compressed_depth_image_transport:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-compressed-depth-image-transport]
|
|
||||||
compressed_image_transport:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-compressed-image-transport]
|
|
||||||
cv_camera:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-cv-camera]
|
|
||||||
image_proc:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-image-proc]
|
|
||||||
image_publisher:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-image-publisher]
|
|
||||||
map_msgs:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-map-msgs]
|
|
||||||
mavros_msgs:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-mavros-msgs]
|
|
||||||
pcl_msgs:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-pcl-msgs]
|
|
||||||
pcl_conversions:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-pcl-conversions]
|
|
||||||
polled_camera:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-polled-camera]
|
|
||||||
rqt_image_view:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-rqt-image-view]
|
|
||||||
stereo_msgs:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-stereo-msgs]
|
|
||||||
image_view:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-image-view]
|
|
||||||
rosbridge_library:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-rosbridge-library]
|
|
||||||
stereo_image_proc:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-stereo-image-proc]
|
|
||||||
tf2_ros:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-tf2-ros]
|
|
||||||
depth_image_proc:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-depth-image-proc]
|
|
||||||
mavros:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-mavros]
|
|
||||||
tf:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-tf]
|
|
||||||
interactive_markers:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-interactive-markers]
|
|
||||||
interactive_marker_tutorials:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-interactive-marker-tutorials]
|
|
||||||
laser_geometry:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-laser-geometry]
|
|
||||||
laser_assembler:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-laser-assembler]
|
|
||||||
laser_filters:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-laser-filters]
|
|
||||||
pcl_ros:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-pcl-ros]
|
|
||||||
tf2_geometry_msgs:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-tf2-geometry-msgs]
|
|
||||||
image_rotate:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-image-rotate]
|
|
||||||
tf2_kdl:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-tf2-kdl]
|
|
||||||
tf2_web_republisher:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-tf2-web-republisher]
|
|
||||||
tf_conversions:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-tf-conversions]
|
|
||||||
theora_image_transport:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-theora-image-transport]
|
|
||||||
turtlesim:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-turtlesim]
|
|
||||||
turtle_actionlib:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-turtle-actionlib]
|
|
||||||
turtle_tf:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-turtle-tf]
|
|
||||||
turtle_tf2:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-turtle-tf2]
|
|
||||||
urdf:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-urdf]
|
|
||||||
kdl_parser:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-kdl-parser]
|
|
||||||
kdl_parser_py:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-kdl-parser-py]
|
|
||||||
mavros_extras:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-mavros-extras]
|
|
||||||
robot_state_publisher:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-robot-state-publisher]
|
|
||||||
rviz:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-rviz]
|
|
||||||
librviz_tutorial:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-librviz-tutorial]
|
|
||||||
rqt_rviz:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-rqt-rviz]
|
|
||||||
rviz_plugin_tutorials:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-rviz-plugin-tutorials]
|
|
||||||
rviz_python_tutorial:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-rviz-python-tutorial]
|
|
||||||
urdf_tutorial:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-urdf-tutorial]
|
|
||||||
usb_cam:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-usb-cam]
|
|
||||||
visualization_marker_tutorials:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-visualization-marker-tutorials]
|
|
||||||
vl53l1x:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-vl53l1x]
|
|
||||||
xacro:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-xacro]
|
|
||||||
ddynamic_reconfigure:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-ddynamic-reconfigure]
|
|
||||||
librealsense2:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-librealsense2]
|
|
||||||
realsense2_camera:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-realsense2-camera]
|
|
||||||
realsense2_description:
|
|
||||||
debian:
|
|
||||||
buster: [ros-noetic-realsense2-description]
|
|
||||||
geographiclib:
|
|
||||||
debian:
|
|
||||||
buster: [libgeographic-dev]
|
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ echo_stamp() {
|
|||||||
my_travis_retry() {
|
my_travis_retry() {
|
||||||
local result=0
|
local result=0
|
||||||
local count=1
|
local count=1
|
||||||
local max_count=5
|
local max_count=50
|
||||||
while [ $count -le $max_count ]; do
|
while [ $count -le $max_count ]; do
|
||||||
[ $result -ne 0 ] && {
|
[ $result -ne 0 ] && {
|
||||||
echo -e "\nThe command \"$@\" failed. Retrying, $count of $max_count.\n" >&2
|
echo -e "\nThe command \"$@\" failed. Retrying, $count of $max_count.\n" >&2
|
||||||
@@ -72,7 +72,7 @@ my_travis_retry() {
|
|||||||
echo_stamp "Init rosdep"
|
echo_stamp "Init rosdep"
|
||||||
my_travis_retry rosdep init
|
my_travis_retry rosdep init
|
||||||
# FIXME: Re-add this after missing packages are built
|
# FIXME: Re-add this after missing packages are built
|
||||||
echo "yaml file:///etc/ros/rosdep/${ROS_DISTRO}-rosdep-clover.yaml" >> /etc/ros/rosdep/sources.list.d/10-clover.list
|
echo "yaml file:///etc/ros/rosdep/${ROS_DISTRO}-rosdep-clover.yaml" >> /etc/ros/rosdep/sources.list.d/20-default.list
|
||||||
my_travis_retry rosdep update
|
my_travis_retry rosdep update
|
||||||
|
|
||||||
echo_stamp "Populate rosdep for ROS user"
|
echo_stamp "Populate rosdep for ROS user"
|
||||||
@@ -125,12 +125,11 @@ cd /home/pi/catkin_ws/src/clover
|
|||||||
builder/assets/install_gitbook.sh
|
builder/assets/install_gitbook.sh
|
||||||
gitbook install
|
gitbook install
|
||||||
gitbook build
|
gitbook build
|
||||||
# replace assets copy to assets symlink to save space
|
|
||||||
rm -rf _book/assets && ln -s ../docs/assets _book/assets
|
|
||||||
touch node_modules/CATKIN_IGNORE docs/CATKIN_IGNORE _book/CATKIN_IGNORE clover/www/CATKIN_IGNORE apps/CATKIN_IGNORE # ignore documentation files by catkin
|
touch node_modules/CATKIN_IGNORE docs/CATKIN_IGNORE _book/CATKIN_IGNORE clover/www/CATKIN_IGNORE apps/CATKIN_IGNORE # ignore documentation files by catkin
|
||||||
|
|
||||||
echo_stamp "Installing additional ROS packages"
|
echo_stamp "Installing additional ROS packages"
|
||||||
my_travis_retry apt-get install -y --no-install-recommends \
|
my_travis_retry apt-get install -y --no-install-recommends \
|
||||||
|
ros-${ROS_DISTRO}-dynamic-reconfigure \
|
||||||
ros-${ROS_DISTRO}-rosbridge-suite \
|
ros-${ROS_DISTRO}-rosbridge-suite \
|
||||||
ros-${ROS_DISTRO}-rosserial \
|
ros-${ROS_DISTRO}-rosserial \
|
||||||
ros-${ROS_DISTRO}-usb-cam \
|
ros-${ROS_DISTRO}-usb-cam \
|
||||||
@@ -138,11 +137,7 @@ my_travis_retry apt-get install -y --no-install-recommends \
|
|||||||
ros-${ROS_DISTRO}-ws281x \
|
ros-${ROS_DISTRO}-ws281x \
|
||||||
ros-${ROS_DISTRO}-rosshow \
|
ros-${ROS_DISTRO}-rosshow \
|
||||||
ros-${ROS_DISTRO}-cmake-modules \
|
ros-${ROS_DISTRO}-cmake-modules \
|
||||||
ros-${ROS_DISTRO}-image-view \
|
ros-${ROS_DISTRO}-image-view
|
||||||
ros-${ROS_DISTRO}-nodelet-topic-tools \
|
|
||||||
ros-${ROS_DISTRO}-stereo-msgs \
|
|
||||||
ros-${ROS_DISTRO}-vision-msgs \
|
|
||||||
ros-${ROS_DISTRO}-angles
|
|
||||||
|
|
||||||
# TODO move GeographicLib datasets to Mavros debian package
|
# TODO move GeographicLib datasets to Mavros debian package
|
||||||
echo_stamp "Install GeographicLib datasets (needed for mavros)" \
|
echo_stamp "Install GeographicLib datasets (needed for mavros)" \
|
||||||
@@ -156,9 +151,6 @@ catkin_make run_tests #&& catkin_test_results
|
|||||||
echo_stamp "Change permissions for catkin_ws"
|
echo_stamp "Change permissions for catkin_ws"
|
||||||
chown -Rf pi:pi /home/pi/catkin_ws
|
chown -Rf pi:pi /home/pi/catkin_ws
|
||||||
|
|
||||||
echo_stamp "Update www"
|
|
||||||
sudo -u pi sh -c ". devel/setup.sh && rosrun clover www"
|
|
||||||
|
|
||||||
echo_stamp "Make \$HOME/examples symlink"
|
echo_stamp "Make \$HOME/examples symlink"
|
||||||
ln -s "$(catkin_find clover examples --first-only)" /home/pi
|
ln -s "$(catkin_find clover examples --first-only)" /home/pi
|
||||||
chown -Rf pi:pi /home/pi/examples
|
chown -Rf pi:pi /home/pi/examples
|
||||||
|
|||||||
@@ -122,7 +122,7 @@ sed -i "s/updates_available//" /usr/share/byobu/status/status
|
|||||||
# sed -i "s/updates_available//" /home/pi/.byobu/status
|
# sed -i "s/updates_available//" /home/pi/.byobu/status
|
||||||
|
|
||||||
echo_stamp "Installing pip"
|
echo_stamp "Installing pip"
|
||||||
curl https://bootstrap.pypa.io/pip/3.7/get-pip.py -o get-pip.py
|
curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py
|
||||||
curl https://bootstrap.pypa.io/pip/2.7/get-pip.py -o get-pip2.py
|
curl https://bootstrap.pypa.io/pip/2.7/get-pip.py -o get-pip2.py
|
||||||
python3 get-pip.py
|
python3 get-pip.py
|
||||||
python get-pip2.py
|
python get-pip2.py
|
||||||
|
|||||||
@@ -37,7 +37,3 @@ apt-cache show openvpn
|
|||||||
|
|
||||||
echo "Move /etc/ld.so.preload back to its original position"
|
echo "Move /etc/ld.so.preload back to its original position"
|
||||||
mv /etc/ld.so.preload.disabled-for-build /etc/ld.so.preload
|
mv /etc/ld.so.preload.disabled-for-build /etc/ld.so.preload
|
||||||
|
|
||||||
echo "Largest packages installed"
|
|
||||||
sudo -E sh -c 'apt-get install -y debian-goodies'
|
|
||||||
dpigs -H -n 100
|
|
||||||
|
|||||||
@@ -2,14 +2,9 @@
|
|||||||
|
|
||||||
# validate all required modules installed
|
# validate all required modules installed
|
||||||
|
|
||||||
import os
|
|
||||||
import rospy
|
import rospy
|
||||||
from geometry_msgs.msg import PoseStamped
|
from geometry_msgs.msg import PoseStamped
|
||||||
from sensor_msgs.msg import Range, BatteryState
|
from sensor_msgs.msg import Range, BatteryState
|
||||||
from vision_msgs.msg import BoundingBox2D, BoundingBox2DArray, BoundingBox3D, BoundingBox3DArray, \
|
|
||||||
Classification2D, Classification3D, Detection2D, Detection2DArray, Detection3D, Detection3DArray, \
|
|
||||||
ObjectHypothesis, ObjectHypothesisWithPose, VisionInfo
|
|
||||||
import angles
|
|
||||||
|
|
||||||
import cv2
|
import cv2
|
||||||
import cv2.aruco
|
import cv2.aruco
|
||||||
@@ -27,7 +22,6 @@ from clover.srv import GetTelemetry, Navigate, NavigateGlobal, SetPosition, SetV
|
|||||||
from led_msgs.srv import SetLEDs
|
from led_msgs.srv import SetLEDs
|
||||||
from led_msgs.msg import LEDStateArray, LEDState
|
from led_msgs.msg import LEDStateArray, LEDState
|
||||||
from aruco_pose.msg import Marker, MarkerArray, Point2D
|
from aruco_pose.msg import Marker, MarkerArray, Point2D
|
||||||
from clover import long_callback
|
|
||||||
|
|
||||||
import dynamic_reconfigure.client
|
import dynamic_reconfigure.client
|
||||||
|
|
||||||
@@ -36,16 +30,11 @@ import tf2_geometry_msgs
|
|||||||
|
|
||||||
import VL53L1X
|
import VL53L1X
|
||||||
import pymavlink
|
import pymavlink
|
||||||
|
import lxml
|
||||||
from pymavlink import mavutil
|
from pymavlink import mavutil
|
||||||
from image_geometry import PinholeCameraModel, StereoCameraModel
|
import rpi_ws281x
|
||||||
|
import pigpio
|
||||||
# from espeak import espeak
|
# from espeak import espeak
|
||||||
from pyzbar import pyzbar
|
from pyzbar import pyzbar
|
||||||
import docopt
|
|
||||||
import geopy
|
|
||||||
import flask
|
|
||||||
|
|
||||||
print(cv2.getBuildInformation())
|
print(cv2.getBuildInformation())
|
||||||
|
|
||||||
if not os.environ.get('VM'):
|
|
||||||
import rpi_ws281x
|
|
||||||
import pigpio
|
|
||||||
|
|||||||
@@ -6,10 +6,16 @@ set -ex
|
|||||||
|
|
||||||
# validate required software is installed
|
# validate required software is installed
|
||||||
|
|
||||||
|
python --version
|
||||||
python2 --version
|
python2 --version
|
||||||
python3 --version
|
python3 --version
|
||||||
|
ipython --version
|
||||||
ipython3 --version
|
ipython3 --version
|
||||||
|
|
||||||
|
# ptvsd does not have a stand-alone binary
|
||||||
|
python -m ptvsd --version
|
||||||
|
python3 -m ptvsd --version
|
||||||
|
|
||||||
node -v
|
node -v
|
||||||
npm -v
|
npm -v
|
||||||
|
|
||||||
@@ -19,80 +25,42 @@ lsof -v
|
|||||||
git --version
|
git --version
|
||||||
vim --version
|
vim --version
|
||||||
pip --version
|
pip --version
|
||||||
|
pip2 --version
|
||||||
pip3 --version
|
pip3 --version
|
||||||
tcpdump --version
|
tcpdump --version
|
||||||
monkey --version
|
monkey --version
|
||||||
|
pigpiod -v
|
||||||
|
i2cdetect -V
|
||||||
|
butterfly -h
|
||||||
# espeak --version
|
# espeak --version
|
||||||
|
mjpg_streamer --version
|
||||||
systemctl --version
|
systemctl --version
|
||||||
|
|
||||||
if [ -z $VM ]; then
|
|
||||||
# rpi only software
|
|
||||||
python --version
|
|
||||||
ipython --version
|
|
||||||
pip2 --version
|
|
||||||
# `python` is python2 for now
|
|
||||||
[[ $(python -c 'import sys;print(sys.version_info.major)') == "2" ]]
|
|
||||||
|
|
||||||
# ptvsd does not have a stand-alone binary
|
|
||||||
python -m ptvsd --version
|
|
||||||
python3 -m ptvsd --version
|
|
||||||
|
|
||||||
pigpiod -v
|
|
||||||
i2cdetect -V
|
|
||||||
butterfly -h
|
|
||||||
mjpg_streamer --version
|
|
||||||
fi
|
|
||||||
|
|
||||||
# ros stuff
|
# ros stuff
|
||||||
|
|
||||||
roscore -h
|
roscore -h
|
||||||
rosversion clover
|
rosversion clover
|
||||||
rosversion aruco_pose
|
rosversion aruco_pose
|
||||||
|
rosversion vl53l1x
|
||||||
rosversion mavros
|
rosversion mavros
|
||||||
rosversion mavros_extras
|
rosversion mavros_extras
|
||||||
rosversion ws281x
|
rosversion ws281x
|
||||||
rosversion led_msgs
|
rosversion led_msgs
|
||||||
rosversion dynamic_reconfigure
|
rosversion dynamic_reconfigure
|
||||||
rosversion tf2_web_republisher
|
rosversion tf2_web_republisher
|
||||||
rosversion rosbridge_server
|
rosversion compressed_image_transport
|
||||||
|
rosversion rosbridge_suite
|
||||||
|
rosversion rosserial
|
||||||
rosversion usb_cam
|
rosversion usb_cam
|
||||||
rosversion cv_camera
|
rosversion cv_camera
|
||||||
rosversion web_video_server
|
rosversion web_video_server
|
||||||
|
rosversion rosshow
|
||||||
rosversion nodelet
|
rosversion nodelet
|
||||||
rosversion image_view
|
rosversion image_view
|
||||||
rosversion stereo_msgs
|
|
||||||
rosversion vision_msgs
|
|
||||||
rosversion angles
|
|
||||||
|
|
||||||
|
# validate some versions
|
||||||
|
[[ $(rosversion cv_camera) == "0.5.1" ]] # patched version with init fix
|
||||||
[[ $(rosversion ws281x) == "0.0.13" ]]
|
[[ $(rosversion ws281x) == "0.0.13" ]]
|
||||||
|
|
||||||
if [ -z $VM ]; then
|
|
||||||
rosversion compressed_image_transport
|
|
||||||
rosversion rosshow
|
|
||||||
rosversion vl53l1x
|
|
||||||
rosversion rosserial
|
|
||||||
[[ $(rosversion cv_camera) == "0.5.1" ]] # patched version with init fix
|
|
||||||
fi
|
|
||||||
|
|
||||||
# determine user home directory
|
|
||||||
[ $VM ] && H="/home/clover" || H="/home/pi"
|
|
||||||
|
|
||||||
# test basic ros tool work
|
|
||||||
source $H/catkin_ws/devel/setup.bash
|
|
||||||
roscd
|
|
||||||
rosrun
|
|
||||||
rosmsg
|
|
||||||
rossrv
|
|
||||||
rosnode || [ $? -eq 64 ] # usage output code is 64
|
|
||||||
rostopic || [ $? -eq 64 ]
|
|
||||||
rosservice || [ $? -eq 64 ]
|
|
||||||
rosparam
|
|
||||||
roslaunch -h
|
|
||||||
|
|
||||||
# validate examples are present
|
# validate examples are present
|
||||||
[[ $(ls $H/examples/*) ]]
|
[[ $(ls /home/pi/examples/*) ]]
|
||||||
|
|
||||||
# validate web tools present
|
|
||||||
[ -d $H/.ros/www ]
|
|
||||||
[ "$(readlink $H/.ros/www/clover)" = "$H/catkin_ws/src/clover/clover/www" ]
|
|
||||||
[ "$(readlink $H/.ros/www/clover_blocks)" = "$H/catkin_ws/src/clover/clover_blocks/www" ]
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ EXCLUDE = 'rviz.png', 'ssid.png', 'sitl_docker_demo.png', 'qgc-params.png', 'but
|
|||||||
'qgc-battery.png', 'qgc-radio.png', 'qgc-cal-acc.png', 'qgc-esc.png', 'qgc-cal-compass.png', \
|
'qgc-battery.png', 'qgc-radio.png', 'qgc-cal-acc.png', 'qgc-esc.png', 'qgc-cal-compass.png', \
|
||||||
'qgc.png', 'qgc-parameters.png', 'clever4-front-white-large.png', 'qgc-modes.png', \
|
'qgc.png', 'qgc-parameters.png', 'clever4-front-white-large.png', 'qgc-modes.png', \
|
||||||
'qgc-requires-setup.png', 'clever4-front-white.png', 'clever4-kit-white.png', '26_1.png', 'battery_holder.stl', \
|
'qgc-requires-setup.png', 'clever4-front-white.png', 'clever4-kit-white.png', '26_1.png', 'battery_holder.stl', \
|
||||||
'camera_case.stl', 'camera_mount.stl', 'grip_right.stl', 'grip_left.stl'
|
'camera_case.stl', 'camera_mount.stl'
|
||||||
|
|
||||||
code = 0
|
code = 0
|
||||||
|
|
||||||
|
|||||||
@@ -2,52 +2,6 @@
|
|||||||
Changelog for package clover
|
Changelog for package clover
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
0.25 (2024-07-28)
|
|
||||||
-----------------
|
|
||||||
* Optimize displaying newlines in the topic viewer, add width and indent parameters.
|
|
||||||
* Link assets instead of copying in documentation to save space.
|
|
||||||
* Install image_geometry and dynamic_reconfigure as clover dependencies.
|
|
||||||
* Add dictionary parameter to aruco.launch.
|
|
||||||
* Solve the issue with aruco_detect not running when aruco_map is not enabled.
|
|
||||||
* Documentation improvements.
|
|
||||||
* Rest changes.
|
|
||||||
|
|
||||||
0.24 (2023-10-11)
|
|
||||||
-----------------
|
|
||||||
* Significant update to autonomous flights API.
|
|
||||||
* Updates to selfcheck.py.
|
|
||||||
* Support PX4 v1.14 parameters.
|
|
||||||
* Added scripts for automatic testing of autonomous flights.
|
|
||||||
* Added new examples for working with the camera, including a red circle model and its recognition and following.
|
|
||||||
* Implemented long_callback Python decorator to address the issue #218.
|
|
||||||
* Implemented optical_flow/enabled dynamic parameter.
|
|
||||||
* Updated LED strip native library to support RPi 4 rev. 1.5.
|
|
||||||
* Show number of messages received in web topic viewer.
|
|
||||||
* Run main_camera/image_raw_throttled topic by default.
|
|
||||||
* Added rectify argument to main_camera.launch
|
|
||||||
* Added udev rules for all supported autopilots by PX4.
|
|
||||||
* Various changes.
|
|
||||||
|
|
||||||
0.23 (2022-02-10)
|
|
||||||
-----------------
|
|
||||||
* Web tool for topics monitoring.
|
|
||||||
* Publish optical flow when local position is not available.
|
|
||||||
* Force estimator init.
|
|
||||||
* Web viewer for Clover logs.
|
|
||||||
* selfcheck.py improvements.
|
|
||||||
* Various changes.
|
|
||||||
|
|
||||||
0.22 (2021-06-07)
|
|
||||||
-----------------
|
|
||||||
* Move to ROS Noetic and Python 3.
|
|
||||||
* aruco.launch: add placement, length and map arguments.
|
|
||||||
* Web: add link for viewing the error log.
|
|
||||||
* LED: add error/ignore parameter to not flash on some errors.
|
|
||||||
* Wait for FC and camera devices before launching mavros and camera driver.
|
|
||||||
* clover.launch: disable rc node by default.
|
|
||||||
* optical_flow: publish debug image even when calc_flow_gyro failed.
|
|
||||||
* Various changes.
|
|
||||||
|
|
||||||
0.21.1 (2020-11-17)
|
0.21.1 (2020-11-17)
|
||||||
-------------------
|
-------------------
|
||||||
* First release of clover package to ROS
|
* First release of clover package to ROS
|
||||||
|
|||||||
@@ -30,8 +30,6 @@ find_package(catkin REQUIRED COMPONENTS
|
|||||||
|
|
||||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake")
|
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake")
|
||||||
|
|
||||||
# https://github.com/mavlink/mavros/blob/7f1a8/mavros/CMakeLists.txt#L42
|
|
||||||
set(CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH};/usr/share/cmake/geographiclib")
|
|
||||||
find_package(GeographicLib REQUIRED)
|
find_package(GeographicLib REQUIRED)
|
||||||
|
|
||||||
# Workaround for OpenCV 3/4 support
|
# Workaround for OpenCV 3/4 support
|
||||||
@@ -55,7 +53,7 @@ find_package(OpenCV ${_opencv_version} REQUIRED
|
|||||||
## Uncomment this if the package has a setup.py. This macro ensures
|
## Uncomment this if the package has a setup.py. This macro ensures
|
||||||
## modules and global scripts declared therein get installed
|
## modules and global scripts declared therein get installed
|
||||||
## See http://ros.org/doc/api/catkin/html/user_guide/setup_dot_py.html
|
## See http://ros.org/doc/api/catkin/html/user_guide/setup_dot_py.html
|
||||||
catkin_python_setup()
|
# catkin_python_setup()
|
||||||
|
|
||||||
################################################
|
################################################
|
||||||
## Declare ROS messages, services and actions ##
|
## Declare ROS messages, services and actions ##
|
||||||
@@ -82,10 +80,11 @@ catkin_python_setup()
|
|||||||
## * add every package in MSG_DEP_SET to generate_messages(DEPENDENCIES ...)
|
## * add every package in MSG_DEP_SET to generate_messages(DEPENDENCIES ...)
|
||||||
|
|
||||||
## Generate messages in the 'msg' folder
|
## Generate messages in the 'msg' folder
|
||||||
add_message_files(
|
# add_message_files(
|
||||||
FILES
|
# FILES
|
||||||
State.msg
|
# Message1.msg
|
||||||
)
|
# Message2.msg
|
||||||
|
# )
|
||||||
|
|
||||||
## Generate services in the 'srv' folder
|
## Generate services in the 'srv' folder
|
||||||
add_service_files(
|
add_service_files(
|
||||||
@@ -93,9 +92,6 @@ add_service_files(
|
|||||||
GetTelemetry.srv
|
GetTelemetry.srv
|
||||||
Navigate.srv
|
Navigate.srv
|
||||||
NavigateGlobal.srv
|
NavigateGlobal.srv
|
||||||
SetAltitude.srv
|
|
||||||
SetYaw.srv
|
|
||||||
SetYawRate.srv
|
|
||||||
SetPosition.srv
|
SetPosition.srv
|
||||||
SetVelocity.srv
|
SetVelocity.srv
|
||||||
SetAttitude.srv
|
SetAttitude.srv
|
||||||
@@ -310,5 +306,4 @@ endif()
|
|||||||
if (CATKIN_ENABLE_TESTING)
|
if (CATKIN_ENABLE_TESTING)
|
||||||
find_package(rostest REQUIRED)
|
find_package(rostest REQUIRED)
|
||||||
add_rostest(test/basic.test)
|
add_rostest(test/basic.test)
|
||||||
add_rostest(test/offboard.test)
|
|
||||||
endif()
|
endif()
|
||||||
|
|||||||
18
clover/cmake/FindGeographicLib.cmake
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
# taken from: https://github.com/mavlink/mavros/blob/master/libmavconn/cmake/Modules/FindGeographicLib.cmake
|
||||||
|
|
||||||
|
# Look for GeographicLib
|
||||||
|
#
|
||||||
|
# Set
|
||||||
|
# GEOGRAPHICLIB_FOUND = TRUE
|
||||||
|
# GeographicLib_INCLUDE_DIRS = /usr/local/include
|
||||||
|
# GeographicLib_LIBRARIES = /usr/local/lib/libGeographic.so
|
||||||
|
# GeographicLib_LIBRARY_DIRS = /usr/local/lib
|
||||||
|
|
||||||
|
find_path (GeographicLib_INCLUDE_DIRS NAMES GeographicLib/Config.h)
|
||||||
|
|
||||||
|
find_library (GeographicLib_LIBRARIES NAMES Geographic)
|
||||||
|
|
||||||
|
include (FindPackageHandleStandardArgs)
|
||||||
|
find_package_handle_standard_args (GeographicLib DEFAULT_MSG
|
||||||
|
GeographicLib_LIBRARIES GeographicLib_INCLUDE_DIRS)
|
||||||
|
mark_as_advanced (GeographicLib_LIBRARIES GeographicLib_INCLUDE_DIRS)
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
# Information: https://clover.coex.tech/camera
|
|
||||||
|
|
||||||
# Example on basic working with the camera and image processing:
|
|
||||||
|
|
||||||
# - cuts out a central square from the camera image;
|
|
||||||
# - publishes this cropped image to the topic `/cv/center`;
|
|
||||||
# - computes the average color of it;
|
|
||||||
# - prints its name to the console.
|
|
||||||
|
|
||||||
import rospy
|
|
||||||
import cv2
|
|
||||||
from sensor_msgs.msg import Image
|
|
||||||
from cv_bridge import CvBridge
|
|
||||||
from clover import long_callback
|
|
||||||
|
|
||||||
rospy.init_node('cv')
|
|
||||||
bridge = CvBridge()
|
|
||||||
|
|
||||||
printed_color = None
|
|
||||||
center_pub = rospy.Publisher('~center', Image, queue_size=1)
|
|
||||||
|
|
||||||
def get_color_name(h):
|
|
||||||
if h < 15: return 'red'
|
|
||||||
elif h < 30: return 'orange'
|
|
||||||
elif h < 60: return 'yellow'
|
|
||||||
elif h < 90: return 'green'
|
|
||||||
elif h < 120: return 'cyan'
|
|
||||||
elif h < 150: return 'blue'
|
|
||||||
elif h < 170: return 'magenta'
|
|
||||||
else: return 'red'
|
|
||||||
|
|
||||||
|
|
||||||
@long_callback
|
|
||||||
def image_callback(msg):
|
|
||||||
img = bridge.imgmsg_to_cv2(msg, 'bgr8')
|
|
||||||
|
|
||||||
# convert to HSV to work with color hue
|
|
||||||
img_hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
|
|
||||||
|
|
||||||
# cut out a central square
|
|
||||||
w = img.shape[1]
|
|
||||||
h = img.shape[0]
|
|
||||||
r = 20
|
|
||||||
center = img_hsv[h // 2 - r:h // 2 + r, w // 2 - r:w // 2 + r]
|
|
||||||
|
|
||||||
# compute and print the average hue
|
|
||||||
mean_hue = center[:, :, 0].mean()
|
|
||||||
color = get_color_name(mean_hue)
|
|
||||||
global printed_color
|
|
||||||
if color != printed_color:
|
|
||||||
print(color)
|
|
||||||
printed_color = color
|
|
||||||
|
|
||||||
# publish the cropped image
|
|
||||||
center = cv2.cvtColor(center, cv2.COLOR_HSV2BGR)
|
|
||||||
center_pub.publish(bridge.cv2_to_imgmsg(center, 'bgr8'))
|
|
||||||
|
|
||||||
# process every frame:
|
|
||||||
image_sub = rospy.Subscriber('main_camera/image_raw', Image, image_callback, queue_size=1)
|
|
||||||
|
|
||||||
# process 5 frames per second:
|
|
||||||
# image_sub = rospy.Subscriber('main_camera/image_raw_throttled', Image, image_callback, queue_size=1)
|
|
||||||
|
|
||||||
rospy.spin()
|
|
||||||
@@ -16,8 +16,11 @@ set_attitude = rospy.ServiceProxy('set_attitude', srv.SetAttitude)
|
|||||||
set_rates = rospy.ServiceProxy('set_rates', srv.SetRates)
|
set_rates = rospy.ServiceProxy('set_rates', srv.SetRates)
|
||||||
land = rospy.ServiceProxy('land', Trigger)
|
land = rospy.ServiceProxy('land', Trigger)
|
||||||
|
|
||||||
def navigate_wait(x=0, y=0, z=0, yaw=math.nan, speed=0.5, frame_id='body', tolerance=0.2, auto_arm=False):
|
def navigate_wait(x=0, y=0, z=0, yaw=float('nan'), yaw_rate=0, speed=0.5, \
|
||||||
res = navigate(x=x, y=y, z=z, yaw=yaw, speed=speed, frame_id=frame_id, auto_arm=auto_arm)
|
frame_id='body', tolerance=0.2, auto_arm=False):
|
||||||
|
|
||||||
|
res = navigate(x=x, y=y, z=z, yaw=yaw, yaw_rate=yaw_rate, speed=speed, \
|
||||||
|
frame_id=frame_id, auto_arm=auto_arm)
|
||||||
|
|
||||||
if not res.success:
|
if not res.success:
|
||||||
return res
|
return res
|
||||||
|
|||||||
@@ -1,91 +0,0 @@
|
|||||||
# This example makes the drone find and follow the red circle.
|
|
||||||
# To test in the simulator, place 'Red Circle' model on the floor.
|
|
||||||
# More information: https://clover.coex.tech/red_circle
|
|
||||||
|
|
||||||
# Input topic: main_camera/image_raw (camera image)
|
|
||||||
# Output topics:
|
|
||||||
# cv/mask (red color mask)
|
|
||||||
# cv/red_circle (position of the center of the red circle in 3D space)
|
|
||||||
|
|
||||||
import rospy
|
|
||||||
import cv2
|
|
||||||
import numpy as np
|
|
||||||
from math import nan
|
|
||||||
from sensor_msgs.msg import Image, CameraInfo
|
|
||||||
from geometry_msgs.msg import PointStamped, Point
|
|
||||||
from cv_bridge import CvBridge
|
|
||||||
from clover import long_callback, srv
|
|
||||||
import tf2_ros
|
|
||||||
import tf2_geometry_msgs
|
|
||||||
import image_geometry
|
|
||||||
|
|
||||||
rospy.init_node('cv', disable_signals=True) # disable signals to allow interrupting with ctrl+c
|
|
||||||
|
|
||||||
get_telemetry = rospy.ServiceProxy('get_telemetry', srv.GetTelemetry)
|
|
||||||
set_position = rospy.ServiceProxy('set_position', srv.SetPosition)
|
|
||||||
|
|
||||||
bridge = CvBridge()
|
|
||||||
|
|
||||||
tf_buffer = tf2_ros.Buffer()
|
|
||||||
tf_listener = tf2_ros.TransformListener(tf_buffer)
|
|
||||||
|
|
||||||
mask_pub = rospy.Publisher('~mask', Image, queue_size=1)
|
|
||||||
point_pub = rospy.Publisher('~red_circle', PointStamped, queue_size=1)
|
|
||||||
|
|
||||||
# read camera info
|
|
||||||
camera_model = image_geometry.PinholeCameraModel()
|
|
||||||
camera_model.fromCameraInfo(rospy.wait_for_message('main_camera/camera_info', CameraInfo))
|
|
||||||
|
|
||||||
|
|
||||||
def img_xy_to_point(xy, dist):
|
|
||||||
xy_rect = camera_model.rectifyPoint(xy)
|
|
||||||
ray = camera_model.projectPixelTo3dRay(xy_rect)
|
|
||||||
return Point(x=ray[0] * dist, y=ray[1] * dist, z=dist)
|
|
||||||
|
|
||||||
def get_center_of_mass(mask):
|
|
||||||
M = cv2.moments(mask)
|
|
||||||
if M['m00'] == 0:
|
|
||||||
return None
|
|
||||||
return M['m10'] // M['m00'], M['m01'] // M['m00']
|
|
||||||
|
|
||||||
follow_red_circle = False
|
|
||||||
|
|
||||||
@long_callback
|
|
||||||
def image_callback(msg):
|
|
||||||
img = bridge.imgmsg_to_cv2(msg, 'bgr8')
|
|
||||||
img_hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
|
|
||||||
|
|
||||||
# we need to use two ranges for red color
|
|
||||||
mask1 = cv2.inRange(img_hsv, (0, 150, 150), (15, 255, 255))
|
|
||||||
mask2 = cv2.inRange(img_hsv, (160, 150, 150), (180, 255, 255))
|
|
||||||
|
|
||||||
# combine two masks using bitwise OR
|
|
||||||
mask = cv2.bitwise_or(mask1, mask2)
|
|
||||||
|
|
||||||
# publish the mask
|
|
||||||
if mask_pub.get_num_connections() > 0:
|
|
||||||
mask_pub.publish(bridge.cv2_to_imgmsg(mask, 'mono8'))
|
|
||||||
|
|
||||||
# calculate x and y of the circle
|
|
||||||
xy = get_center_of_mass(mask)
|
|
||||||
if xy is None:
|
|
||||||
return
|
|
||||||
|
|
||||||
# calculate and publish the position of the circle in 3D space
|
|
||||||
altitude = get_telemetry('terrain').z
|
|
||||||
xy3d = img_xy_to_point(xy, altitude)
|
|
||||||
target = PointStamped(header=msg.header, point=xy3d)
|
|
||||||
point_pub.publish(target)
|
|
||||||
|
|
||||||
if follow_red_circle:
|
|
||||||
# follow the target
|
|
||||||
setpoint = tf_buffer.transform(target, 'map', timeout=rospy.Duration(0.2))
|
|
||||||
set_position(x=setpoint.point.x, y=setpoint.point.y, z=nan, yaw=nan, frame_id=setpoint.header.frame_id)
|
|
||||||
|
|
||||||
# process each camera frame:
|
|
||||||
image_sub = rospy.Subscriber('main_camera/image_raw', Image, image_callback, queue_size=1)
|
|
||||||
|
|
||||||
rospy.loginfo('Hit enter to follow the red circle')
|
|
||||||
input()
|
|
||||||
follow_red_circle = True
|
|
||||||
rospy.spin()
|
|
||||||
@@ -16,12 +16,10 @@
|
|||||||
<remap from="image_raw" to="main_camera/image_raw"/>
|
<remap from="image_raw" to="main_camera/image_raw"/>
|
||||||
<remap from="camera_info" to="main_camera/camera_info"/>
|
<remap from="camera_info" to="main_camera/camera_info"/>
|
||||||
<remap from="map_markers" to="aruco_map/map"/>
|
<remap from="map_markers" to="aruco_map/map"/>
|
||||||
<param name="dictionary" value="2"/> <!-- DICT_4X4_250 -->
|
|
||||||
<param name="estimate_poses" value="true"/>
|
<param name="estimate_poses" value="true"/>
|
||||||
<param name="send_tf" value="true"/>
|
<param name="send_tf" value="true"/>
|
||||||
<param name="use_map_markers" value="$(arg aruco_map)"/>
|
<param name="known_tilt" value="map" if="$(eval placement == 'floor')"/>
|
||||||
<param name="known_vertical" value="map" if="$(eval placement == 'floor' or placement == 'ceiling')"/>
|
<param name="known_tilt" value="map_flipped" if="$(eval placement == 'ceiling')"/>
|
||||||
<param name="flip_vertical" value="true" if="$(eval placement == 'ceiling')"/>
|
|
||||||
<param name="length" value="$(arg length)"/>
|
<param name="length" value="$(arg length)"/>
|
||||||
<param name="transform_timeout" value="0.1"/>
|
<param name="transform_timeout" value="0.1"/>
|
||||||
<!-- aruco detector parameters -->
|
<!-- aruco detector parameters -->
|
||||||
@@ -37,8 +35,8 @@
|
|||||||
<remap from="camera_info" to="main_camera/camera_info"/>
|
<remap from="camera_info" to="main_camera/camera_info"/>
|
||||||
<remap from="markers" to="aruco_detect/markers"/>
|
<remap from="markers" to="aruco_detect/markers"/>
|
||||||
<param name="map" value="$(find aruco_pose)/map/$(arg map)"/>
|
<param name="map" value="$(find aruco_pose)/map/$(arg map)"/>
|
||||||
<param name="known_vertical" value="map" if="$(eval placement == 'floor' or placement == 'ceiling')"/>
|
<param name="known_tilt" value="map" if="$(eval placement == 'floor')"/>
|
||||||
<param name="flip_vertical" value="true" if="$(eval placement == 'ceiling')"/>
|
<param name="known_tilt" value="map_flipped" if="$(eval placement == 'ceiling')"/>
|
||||||
<param name="image_axis" value="true"/>
|
<param name="image_axis" value="true"/>
|
||||||
<param name="frame_id" value="aruco_map_detected" if="$(arg aruco_vpe)"/>
|
<param name="frame_id" value="aruco_map_detected" if="$(arg aruco_vpe)"/>
|
||||||
<param name="frame_id" value="aruco_map" unless="$(arg aruco_vpe)"/>
|
<param name="frame_id" value="aruco_map" unless="$(arg aruco_vpe)"/>
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
<arg name="led" default="true"/>
|
<arg name="led" default="true"/>
|
||||||
<arg name="blocks" default="false"/>
|
<arg name="blocks" default="false"/>
|
||||||
<arg name="rc" default="false"/>
|
<arg name="rc" default="false"/>
|
||||||
<arg name="force_init" default="true"/> <!-- force estimator to init by publishing zero pose -->
|
<arg name="force_init" value="true"/> <!-- force estimator to init by publishing zero pose -->
|
||||||
|
|
||||||
<arg name="simulator" default="false"/> <!-- flag that we are operating on a simulated drone -->
|
<arg name="simulator" default="false"/> <!-- flag that we are operating on a simulated drone -->
|
||||||
|
|
||||||
@@ -45,13 +45,13 @@
|
|||||||
<remap from="camera_info" to="main_camera/camera_info"/>
|
<remap from="camera_info" to="main_camera/camera_info"/>
|
||||||
<param name="calc_flow_gyro" value="true"/>
|
<param name="calc_flow_gyro" value="true"/>
|
||||||
<param name="roi_rad" value="0.8"/>
|
<param name="roi_rad" value="0.8"/>
|
||||||
<param name="disable_on_vpe" value="true"/>
|
|
||||||
</node>
|
</node>
|
||||||
|
|
||||||
|
<node pkg="tf2_ros" type="static_transform_publisher" name="map_flipped_frame" args="0 0 0 3.1415926 3.1415926 0 map map_flipped"/>
|
||||||
|
|
||||||
<!-- simplified offboard control -->
|
<!-- simplified offboard control -->
|
||||||
<node name="simple_offboard" pkg="clover" type="simple_offboard" output="screen" clear_params="true">
|
<node name="simple_offboard" pkg="clover" type="simple_offboard" output="screen" clear_params="true">
|
||||||
<param name="reference_frames/main_camera_optical" value="map"/>
|
<param name="reference_frames/main_camera_optical" value="map"/>
|
||||||
<param name="terrain_frame_mode" value="range"/>
|
|
||||||
</node>
|
</node>
|
||||||
|
|
||||||
<!-- main camera -->
|
<!-- main camera -->
|
||||||
@@ -72,9 +72,6 @@
|
|||||||
<param name="pass_statuses" type="yaml" value="[0, 6, 7, 11]"/>
|
<param name="pass_statuses" type="yaml" value="[0, 6, 7, 11]"/>
|
||||||
</node>
|
</node>
|
||||||
|
|
||||||
<!-- rangefinder's frame -->
|
|
||||||
<node pkg="tf2_ros" type="static_transform_publisher" name="rangefinder_frame" args="0 0 -0.05 0 1.5707963268 0 base_link rangefinder" if="$(arg rangefinder_vl53l1x)"/>
|
|
||||||
|
|
||||||
<!-- led strip -->
|
<!-- led strip -->
|
||||||
<include file="$(find clover)/launch/led.launch" if="$(arg led)">
|
<include file="$(find clover)/launch/led.launch" if="$(arg led)">
|
||||||
<arg name="simulator" value="$(arg simulator)"/>
|
<arg name="simulator" value="$(arg simulator)"/>
|
||||||
@@ -89,4 +86,8 @@
|
|||||||
<param name="use_fake_gcs" value="false"/>
|
<param name="use_fake_gcs" value="false"/>
|
||||||
</node>
|
</node>
|
||||||
|
|
||||||
|
<!-- Update static directory -->
|
||||||
|
<node pkg="roswww_static" name="roswww_static" type="main.py" clear_params="true">
|
||||||
|
<param name="default_package" value="clover"/>
|
||||||
|
</node>
|
||||||
</launch>
|
</launch>
|
||||||
|
|||||||
@@ -21,8 +21,7 @@
|
|||||||
</node>
|
</node>
|
||||||
|
|
||||||
<!-- high level led effects control, events notification with leds -->
|
<!-- high level led effects control, events notification with leds -->
|
||||||
<node pkg="clover" name="led_effect" type="led" clear_params="true" output="screen" if="$(arg led_effect)">
|
<node pkg="clover" name="led_effect" type="led" ns="led" clear_params="true" output="screen" if="$(arg led_effect)">
|
||||||
<param name="led" value="led"/>
|
|
||||||
<param name="blink_rate" value="2"/>
|
<param name="blink_rate" value="2"/>
|
||||||
<param name="fade_period" value="0.5"/>
|
<param name="fade_period" value="0.5"/>
|
||||||
<param name="rainbow_period" value="5"/>
|
<param name="rainbow_period" value="5"/>
|
||||||
|
|||||||
@@ -4,9 +4,6 @@
|
|||||||
<arg name="direction_z" default="down"/> <!-- direction the camera points: down, up -->
|
<arg name="direction_z" default="down"/> <!-- direction the camera points: down, up -->
|
||||||
<arg name="direction_y" default="backward"/> <!-- direction the camera cable points: backward, forward -->
|
<arg name="direction_y" default="backward"/> <!-- direction the camera cable points: backward, forward -->
|
||||||
<arg name="device" default="/dev/video0"/> <!-- v4l2 device -->
|
<arg name="device" default="/dev/video0"/> <!-- v4l2 device -->
|
||||||
<arg name="throttled_topic" default="true"/> <!-- enable throttled image topic -->
|
|
||||||
<arg name="throttled_topic_rate" default="5.0"/> <!-- throttled image topic rate -->
|
|
||||||
<arg name="rectify" default="false"/> <!-- enable rectification -->
|
|
||||||
<arg name="simulator" default="false"/>
|
<arg name="simulator" default="false"/>
|
||||||
|
|
||||||
<node if="$(eval direction_z == 'down' and direction_y == 'backward')" pkg="tf2_ros" type="static_transform_publisher" name="main_camera_frame" args="0.05 0 -0.07 -1.5707963 0 3.1415926 base_link main_camera_optical"/>
|
<node if="$(eval direction_z == 'down' and direction_y == 'backward')" pkg="tf2_ros" type="static_transform_publisher" name="main_camera_frame" args="0.05 0 -0.07 -1.5707963 0 3.1415926 base_link main_camera_optical"/>
|
||||||
@@ -46,15 +43,4 @@
|
|||||||
<node pkg="clover" type="camera_markers" ns="main_camera" name="main_camera_markers">
|
<node pkg="clover" type="camera_markers" ns="main_camera" name="main_camera_markers">
|
||||||
<param name="scale" value="3.0"/>
|
<param name="scale" value="3.0"/>
|
||||||
</node>
|
</node>
|
||||||
|
|
||||||
<!-- image topic throttled -->
|
|
||||||
<node pkg="topic_tools" name="main_camera_throttle" type="throttle" ns="main_camera"
|
|
||||||
args="messages image_raw $(arg throttled_topic_rate) image_raw_throttled" if="$(arg throttled_topic)"/>
|
|
||||||
|
|
||||||
<!-- rectified image topic -->
|
|
||||||
<node pkg="nodelet" type="nodelet" name="rectify" args="load image_proc/rectify main_camera_nodelet_manager" if="$(arg rectify)">
|
|
||||||
<remap from="image_mono" to="main_camera/image_raw"/>
|
|
||||||
<remap from="camera_info" to="main_camera/camera_info"/>
|
|
||||||
<remap from="image_rect" to="main_camera/image_rect"/>
|
|
||||||
</node>
|
|
||||||
</launch>
|
</launch>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<launch>
|
<launch>
|
||||||
<arg name="fcu_conn" default="usb"/> <!-- options: usb, uart, tcp, udp, sitl, hitl -->
|
<arg name="fcu_conn" default="usb"/> <!-- options: usb, uart, tcp, udp, sitl -->
|
||||||
<arg name="fcu_ip" default="127.0.0.1"/>
|
<arg name="fcu_ip" default="127.0.0.1"/>
|
||||||
<arg name="fcu_sys_id" default="1"/>
|
<arg name="fcu_sys_id" default="1"/>
|
||||||
<arg name="gcs_bridge" default="tcp"/>
|
<arg name="gcs_bridge" default="tcp"/>
|
||||||
@@ -23,9 +23,6 @@
|
|||||||
<!-- sitl since PX4 1.9.0 -->
|
<!-- sitl since PX4 1.9.0 -->
|
||||||
<param name="fcu_url" value="udp://@$(arg fcu_ip):14580" if="$(eval fcu_conn == 'sitl')"/>
|
<param name="fcu_url" value="udp://@$(arg fcu_ip):14580" if="$(eval fcu_conn == 'sitl')"/>
|
||||||
|
|
||||||
<!-- hitl connection (to gazebo_mavlink_interface plugin) -->
|
|
||||||
<param name="fcu_url" value="udp://$(arg fcu_ip):14540@" if="$(eval fcu_conn == 'hitl')"/>
|
|
||||||
|
|
||||||
<!-- set target_system_id -->
|
<!-- set target_system_id -->
|
||||||
<param name="target_system_id" value="$(arg fcu_sys_id)" />
|
<param name="target_system_id" value="$(arg fcu_sys_id)" />
|
||||||
|
|
||||||
@@ -77,6 +74,9 @@
|
|||||||
covariance: 1 # cm
|
covariance: 1 # cm
|
||||||
</rosparam>
|
</rosparam>
|
||||||
|
|
||||||
|
<!-- Rangefinders frame -->
|
||||||
|
<node pkg="tf2_ros" type="static_transform_publisher" name="rangefinder_frame" args="0 0 -0.05 0 1.5707963268 0 base_link rangefinder"/>
|
||||||
|
|
||||||
<!-- Copter visualization -->
|
<!-- Copter visualization -->
|
||||||
<node name="visualization" pkg="mavros_extras" type="visualization" if="$(arg viz)">
|
<node name="visualization" pkg="mavros_extras" type="visualization" if="$(arg viz)">
|
||||||
<remap to="mavros/local_position/pose" from="local_position"/>
|
<remap to="mavros/local_position/pose" from="local_position"/>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<launch>
|
<launch>
|
||||||
<!-- shortcut for running the simulation (`roslaunch clover simulator.launch`) -->
|
<!-- shurtcut for running the simulation (`roslaunch clover simulator.launch`) -->
|
||||||
<include file="$(find clover_simulation)/launch/simulator.launch"/>
|
<include file="$(find clover_simulation)/launch/simulator.launch"/>
|
||||||
</launch>
|
</launch>
|
||||||
|
|||||||
@@ -1,38 +0,0 @@
|
|||||||
uint8 MODE_NONE = 0
|
|
||||||
uint8 MODE_NAVIGATE = 1
|
|
||||||
uint8 MODE_NAVIGATE_GLOBAL = 2
|
|
||||||
uint8 MODE_POSITION = 3
|
|
||||||
uint8 MODE_VELOCITY = 4
|
|
||||||
uint8 MODE_ATTITUDE = 5
|
|
||||||
uint8 MODE_RATES = 6
|
|
||||||
|
|
||||||
uint8 YAW_MODE_YAW = 0
|
|
||||||
uint8 YAW_MODE_YAW_RATE = 1
|
|
||||||
uint8 YAW_MODE_YAW_TOWARDS = 2
|
|
||||||
|
|
||||||
# type of offboard control
|
|
||||||
uint8 mode
|
|
||||||
uint8 yaw_mode
|
|
||||||
|
|
||||||
# targets
|
|
||||||
float32 x
|
|
||||||
float32 y
|
|
||||||
float32 z
|
|
||||||
float32 speed
|
|
||||||
float32 lat
|
|
||||||
float32 lon
|
|
||||||
float32 vx
|
|
||||||
float32 vy
|
|
||||||
float32 vz
|
|
||||||
float32 roll
|
|
||||||
float32 pitch
|
|
||||||
float32 yaw
|
|
||||||
float32 roll_rate
|
|
||||||
float32 pitch_rate
|
|
||||||
float32 yaw_rate
|
|
||||||
float32 thrust
|
|
||||||
|
|
||||||
# frames of reference
|
|
||||||
string xy_frame_id
|
|
||||||
string z_frame_id
|
|
||||||
string yaw_frame_id
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<?xml version="1.0"?>
|
<?xml version="1.0"?>
|
||||||
<package format="3">
|
<package format="3">
|
||||||
<name>clover</name>
|
<name>clover</name>
|
||||||
<version>0.25.0</version>
|
<version>0.23.0</version>
|
||||||
<description>The Clover package</description>
|
<description>The Clover package</description>
|
||||||
|
|
||||||
<maintainer email="okalachev@gmail.com">Oleg Kalachev</maintainer>
|
<maintainer email="okalachev@gmail.com">Oleg Kalachev</maintainer>
|
||||||
@@ -37,15 +37,10 @@
|
|||||||
<depend>rosbridge_server</depend>
|
<depend>rosbridge_server</depend>
|
||||||
<depend>web_video_server</depend>
|
<depend>web_video_server</depend>
|
||||||
<depend>tf2_web_republisher</depend>
|
<depend>tf2_web_republisher</depend>
|
||||||
<depend>libxml2</depend>
|
|
||||||
<depend>libxslt</depend>
|
|
||||||
<depend condition="$ROS_PYTHON_VERSION == 2">python-lxml</depend>
|
|
||||||
<depend condition="$ROS_PYTHON_VERSION == 3">python3-lxml</depend>
|
|
||||||
<depend>dynamic_reconfigure</depend>
|
<depend>dynamic_reconfigure</depend>
|
||||||
<depend>image_proc</depend>
|
|
||||||
<depend>image_geometry</depend>
|
|
||||||
<exec_depend>python-pymavlink</exec_depend>
|
<exec_depend>python-pymavlink</exec_depend>
|
||||||
<test_depend>ros_pytest</test_depend>
|
<!-- Use test_depend for packages you need only for testing: -->
|
||||||
|
<!-- <test_depend>gtest</test_depend> -->
|
||||||
|
|
||||||
<!-- The export tag contains other, unspecified, tags -->
|
<!-- The export tag contains other, unspecified, tags -->
|
||||||
<export>
|
<export>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
flask
|
flask==1.1.1
|
||||||
geopy
|
docopt==0.6.2
|
||||||
smbus2
|
geopy==1.11.0
|
||||||
VL53L1X
|
smbus2==0.3.0
|
||||||
|
VL53L1X==0.0.5
|
||||||
|
|||||||
@@ -1,11 +0,0 @@
|
|||||||
## ! DO NOT MANUALLY INVOKE THIS setup.py, USE CATKIN INSTEAD
|
|
||||||
|
|
||||||
from distutils.core import setup
|
|
||||||
from catkin_pkg.python_setup import generate_distutils_setup
|
|
||||||
|
|
||||||
# fetch values from package.xml
|
|
||||||
setup_args = generate_distutils_setup(
|
|
||||||
packages=['clover'],
|
|
||||||
package_dir={'': 'src'})
|
|
||||||
|
|
||||||
setup(**setup_args)
|
|
||||||
@@ -13,12 +13,7 @@ from util import handle_response
|
|||||||
|
|
||||||
rospy.init_node('autotest_aruco', disable_signals=True) # disable signals to allow interrupting with ctrl+c
|
rospy.init_node('autotest_aruco', disable_signals=True) # disable signals to allow interrupting with ctrl+c
|
||||||
|
|
||||||
try:
|
flow_client = dynamic_reconfigure.client.Client('optical_flow')
|
||||||
flow_client = dynamic_reconfigure.client.Client('optical_flow', timeout=2)
|
|
||||||
except rospy.ROSException:
|
|
||||||
flow_client = None
|
|
||||||
print('Cannot configure optical flow, skip')
|
|
||||||
|
|
||||||
get_telemetry = rospy.ServiceProxy('get_telemetry', srv.GetTelemetry)
|
get_telemetry = rospy.ServiceProxy('get_telemetry', srv.GetTelemetry)
|
||||||
navigate = handle_response(rospy.ServiceProxy('navigate', srv.Navigate))
|
navigate = handle_response(rospy.ServiceProxy('navigate', srv.Navigate))
|
||||||
land = handle_response(rospy.ServiceProxy('land', Trigger))
|
land = handle_response(rospy.ServiceProxy('land', Trigger))
|
||||||
@@ -35,8 +30,11 @@ def print_current_map_position():
|
|||||||
dist = rospy.wait_for_message('rangefinder/range', Range).range
|
dist = rospy.wait_for_message('rangefinder/range', Range).range
|
||||||
print('Map position:\tx={:.1f}\ty={:.1f}\tz={:.1f}\tyaw={:.1f}\tdist={:.2f}'.format(telem.x, telem.y, telem.z, telem.yaw, dist))
|
print('Map position:\tx={:.1f}\ty={:.1f}\tz={:.1f}\tyaw={:.1f}\tdist={:.2f}'.format(telem.x, telem.y, telem.z, telem.yaw, dist))
|
||||||
|
|
||||||
def navigate_wait(x=0, y=0, z=0, yaw=math.nan, speed=0.5, frame_id='body', tolerance=0.2, auto_arm=False):
|
def navigate_wait(x=0, y=0, z=0, yaw=float('nan'), yaw_rate=0, speed=0.5, \
|
||||||
res = navigate(x=x, y=y, z=z, yaw=yaw, speed=speed, frame_id=frame_id, auto_arm=auto_arm)
|
frame_id='body', tolerance=0.2, auto_arm=False):
|
||||||
|
|
||||||
|
res = navigate(x=x, y=y, z=z, yaw=yaw, yaw_rate=yaw_rate, speed=speed, \
|
||||||
|
frame_id=frame_id, auto_arm=auto_arm)
|
||||||
|
|
||||||
if not res.success:
|
if not res.success:
|
||||||
return res
|
return res
|
||||||
@@ -69,13 +67,12 @@ input('Go to center %g %g 1.5 speed 5 [enter] ' % (center_x, center_y))
|
|||||||
navigate_wait(x=center_x, y=center_y, z=1.5, speed=5, frame_id='aruco_map')
|
navigate_wait(x=center_x, y=center_y, z=1.5, speed=5, frame_id='aruco_map')
|
||||||
print_current_map_position()
|
print_current_map_position()
|
||||||
|
|
||||||
if flow_client:
|
input('Disable optical flow and keep hovering [enter] ')
|
||||||
input('Disable optical flow and keep hovering [enter] ')
|
flow_client.update_configuration({'enabled': False})
|
||||||
flow_client.update_configuration({'enabled': False})
|
rospy.sleep(5)
|
||||||
rospy.sleep(5)
|
|
||||||
|
|
||||||
input('Enable optical flow back [enter] ')
|
input('Enable optical flow back [enter] ')
|
||||||
flow_client.update_configuration({'enabled': True})
|
flow_client.update_configuration({'enabled': True})
|
||||||
|
|
||||||
input('Go to side 1 %g 2 heading top [enter] ' % (center_y))
|
input('Go to side 1 %g 2 heading top [enter] ' % (center_y))
|
||||||
navigate_wait(x=1, y=center_y, z=2, yaw=1.57, frame_id='aruco_map')
|
navigate_wait(x=1, y=center_y, z=2, yaw=1.57, frame_id='aruco_map')
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
import rospy
|
import rospy
|
||||||
import math
|
import math
|
||||||
from math import nan, inf
|
from math import nan
|
||||||
import signal
|
import signal
|
||||||
import sys
|
import sys
|
||||||
from clover import srv
|
from clover import srv
|
||||||
@@ -15,8 +15,6 @@ rospy.init_node('autotest_flight', disable_signals=True) # disable signals to al
|
|||||||
get_telemetry = rospy.ServiceProxy('get_telemetry', srv.GetTelemetry)
|
get_telemetry = rospy.ServiceProxy('get_telemetry', srv.GetTelemetry)
|
||||||
navigate = handle_response(rospy.ServiceProxy('navigate', srv.Navigate))
|
navigate = handle_response(rospy.ServiceProxy('navigate', srv.Navigate))
|
||||||
navigate_global = handle_response(rospy.ServiceProxy('navigate_global', srv.NavigateGlobal))
|
navigate_global = handle_response(rospy.ServiceProxy('navigate_global', srv.NavigateGlobal))
|
||||||
set_yaw = handle_response(rospy.ServiceProxy('set_yaw', srv.SetYaw))
|
|
||||||
set_yaw_rate = handle_response(rospy.ServiceProxy('set_yaw_rate', srv.SetYawRate))
|
|
||||||
set_position = handle_response(rospy.ServiceProxy('set_position', srv.SetPosition))
|
set_position = handle_response(rospy.ServiceProxy('set_position', srv.SetPosition))
|
||||||
set_velocity = handle_response(rospy.ServiceProxy('set_velocity', srv.SetVelocity))
|
set_velocity = handle_response(rospy.ServiceProxy('set_velocity', srv.SetVelocity))
|
||||||
set_attitude = handle_response(rospy.ServiceProxy('set_attitude', srv.SetAttitude))
|
set_attitude = handle_response(rospy.ServiceProxy('set_attitude', srv.SetAttitude))
|
||||||
@@ -30,8 +28,11 @@ def interrupt(sig, frame):
|
|||||||
|
|
||||||
signal.signal(signal.SIGINT, interrupt)
|
signal.signal(signal.SIGINT, interrupt)
|
||||||
|
|
||||||
def navigate_wait(x=0, y=0, z=0, yaw=nan, speed=0.5, frame_id='body', tolerance=0.2, auto_arm=False):
|
def navigate_wait(x=0, y=0, z=0, yaw=nan, yaw_rate=0, speed=0.5, \
|
||||||
res = navigate(x=x, y=y, z=z, yaw=yaw, speed=speed, frame_id=frame_id, auto_arm=auto_arm)
|
frame_id='body', tolerance=0.2, auto_arm=False):
|
||||||
|
|
||||||
|
res = navigate(x=x, y=y, z=z, yaw=yaw, yaw_rate=yaw_rate, speed=speed, \
|
||||||
|
frame_id=frame_id, auto_arm=auto_arm)
|
||||||
|
|
||||||
if not res.success:
|
if not res.success:
|
||||||
return res
|
return res
|
||||||
@@ -68,17 +69,17 @@ set_velocity(vx=1, vy=0.0, vz=0, frame_id='body')
|
|||||||
rospy.sleep(2)
|
rospy.sleep(2)
|
||||||
set_position(frame_id='body')
|
set_position(frame_id='body')
|
||||||
|
|
||||||
input('Rotate right 90° using set_yaw [enter] ')
|
input('Rotate right 90° [enter] ')
|
||||||
set_yaw(yaw=-math.pi / 2, frame_id='navigate_target')
|
navigate(yaw=-math.pi / 2, frame_id='navigate_target')
|
||||||
rospy.sleep(3)
|
rospy.sleep(3)
|
||||||
|
|
||||||
input('Use set_attitude to fly backwards [enter]')
|
input('Use set_attitude to fly backwards [enter]')
|
||||||
set_attitude(roll=0, pitch=-0.3, yaw=0, thrust=0.5, frame_id='body')
|
set_attitude(pitch=-0.3, roll=0, yaw=0, thrust=0.5, frame_id='body')
|
||||||
rospy.sleep(0.3)
|
rospy.sleep(0.3)
|
||||||
set_position(frame_id='body')
|
set_position(frame_id='body')
|
||||||
|
|
||||||
input('Use set_attitude to fly right [enter]')
|
input('Use set_attitude to fly right [enter]')
|
||||||
set_attitude(roll=0.3, pitch=0, yaw=0, thrust=0.5, frame_id='body')
|
set_attitude(pitch=0, roll=0.3, yaw=0, thrust=0.5, frame_id='body')
|
||||||
rospy.sleep(0.5)
|
rospy.sleep(0.5)
|
||||||
set_position(frame_id='body')
|
set_position(frame_id='body')
|
||||||
|
|
||||||
@@ -87,13 +88,13 @@ set_rates(roll_rate=1.2, thrust=0.5)
|
|||||||
rospy.sleep(0.4)
|
rospy.sleep(0.4)
|
||||||
set_position(frame_id='body')
|
set_position(frame_id='body')
|
||||||
|
|
||||||
input('Rotate 360° to the right using set_yaw_rate [enter]')
|
input('Rotate 360° to the right using yaw_rate [enter]')
|
||||||
set_yaw_rate(yaw_rate=-1)
|
set_position(x=nan, y=nan, z=nan, frame_id='body', yaw=nan, yaw_rate=-1)
|
||||||
rospy.sleep(2 * math.pi)
|
rospy.sleep(2 * math.pi)
|
||||||
set_position(frame_id='body')
|
set_position(frame_id='body')
|
||||||
|
|
||||||
input('Return to start point heading forward [enter]')
|
input('Return to start point [enter]')
|
||||||
navigate_wait(x=start.x, y=start.y, z=start.z, yaw=inf, speed=1, frame_id='map')
|
navigate_wait(x=start.x, y=start.y, z=start.z, yaw=start.yaw, speed=1, frame_id='map')
|
||||||
|
|
||||||
input('Land [enter]')
|
input('Land [enter]')
|
||||||
land()
|
land()
|
||||||
|
|||||||
@@ -1,35 +0,0 @@
|
|||||||
import rospy
|
|
||||||
from threading import Thread, Event
|
|
||||||
|
|
||||||
def long_callback(fn):
|
|
||||||
"""
|
|
||||||
Decorator fixing a rospy issue for long-running topic callbacks, primarily
|
|
||||||
for image processing.
|
|
||||||
|
|
||||||
See: https://github.com/ros/ros_comm/issues/1901.
|
|
||||||
|
|
||||||
Usage example:
|
|
||||||
|
|
||||||
@long_callback
|
|
||||||
def image_callback(msg):
|
|
||||||
# perform image processing
|
|
||||||
# ...
|
|
||||||
|
|
||||||
rospy.Subscriber('main_camera/image_raw', Image, image_callback, queue_size=1)
|
|
||||||
"""
|
|
||||||
e = Event()
|
|
||||||
|
|
||||||
def thread():
|
|
||||||
while not rospy.is_shutdown():
|
|
||||||
e.wait()
|
|
||||||
e.clear()
|
|
||||||
fn(thread.current_msg)
|
|
||||||
|
|
||||||
thread.current_msg = None
|
|
||||||
Thread(target=thread, daemon=True).start()
|
|
||||||
|
|
||||||
def wrapper(msg):
|
|
||||||
thread.current_msg = msg
|
|
||||||
e.set()
|
|
||||||
|
|
||||||
return wrapper
|
|
||||||
@@ -309,22 +309,18 @@ int main(int argc, char **argv)
|
|||||||
nh_priv.param("notify/low_battery/threshold", low_battery_threshold, 3.7);
|
nh_priv.param("notify/low_battery/threshold", low_battery_threshold, 3.7);
|
||||||
nh_priv.param("notify/error/ignore", error_ignore, {});
|
nh_priv.param("notify/error/ignore", error_ignore, {});
|
||||||
|
|
||||||
std::string led; // led namespace
|
ros::service::waitForService("set_leds"); // cannot work without set_leds service
|
||||||
nh_priv.param("led", led, std::string("led"));
|
set_leds_srv = nh.serviceClient<led_msgs::SetLEDs>("set_leds", true);
|
||||||
if (!led.empty()) led += "/";
|
|
||||||
|
|
||||||
ros::service::waitForService(led + "set_leds"); // cannot work without set_leds service
|
|
||||||
set_leds_srv = nh.serviceClient<led_msgs::SetLEDs>(led + "set_leds", true);
|
|
||||||
|
|
||||||
// wait for leds count info
|
// wait for leds count info
|
||||||
handleState(*ros::topic::waitForMessage<led_msgs::LEDStateArray>(led + "state", nh));
|
handleState(*ros::topic::waitForMessage<led_msgs::LEDStateArray>("state", nh));
|
||||||
|
|
||||||
auto state_sub = nh.subscribe(led + "state", 1, &handleState);
|
auto state_sub = nh.subscribe("state", 1, &handleState);
|
||||||
|
|
||||||
auto set_effect = nh.advertiseService(led + "set_effect", &setEffect);
|
auto set_effect = nh.advertiseService("set_effect", &setEffect);
|
||||||
|
|
||||||
auto mavros_state_sub = nh.subscribe("mavros/state", 1, &handleMavrosState);
|
auto mavros_state_sub = nh.subscribe("/mavros/state", 1, &handleMavrosState);
|
||||||
auto battery_sub = nh.subscribe("mavros/battery", 1, &handleBattery);
|
auto battery_sub = nh.subscribe("/mavros/battery", 1, &handleBattery);
|
||||||
auto rosout_sub = nh.subscribe("/rosout_agg", 1, &handleLog);
|
auto rosout_sub = nh.subscribe("/rosout_agg", 1, &handleLog);
|
||||||
|
|
||||||
timer = nh.createTimer(ros::Duration(0), &proceed, false, false);
|
timer = nh.createTimer(ros::Duration(0), &proceed, false, false);
|
||||||
|
|||||||
@@ -25,7 +25,6 @@
|
|||||||
#include <dynamic_reconfigure/server.h>
|
#include <dynamic_reconfigure/server.h>
|
||||||
#include <mavros_msgs/OpticalFlowRad.h>
|
#include <mavros_msgs/OpticalFlowRad.h>
|
||||||
#include <sensor_msgs/Imu.h>
|
#include <sensor_msgs/Imu.h>
|
||||||
#include <geometry_msgs/PoseStamped.h>
|
|
||||||
#include <geometry_msgs/Vector3Stamped.h>
|
#include <geometry_msgs/Vector3Stamped.h>
|
||||||
#include <geometry_msgs/PointStamped.h>
|
#include <geometry_msgs/PointStamped.h>
|
||||||
#include <geometry_msgs/TwistStamped.h>
|
#include <geometry_msgs/TwistStamped.h>
|
||||||
@@ -58,9 +57,6 @@ private:
|
|||||||
std::unique_ptr<tf2_ros::TransformListener> tf_listener_;
|
std::unique_ptr<tf2_ros::TransformListener> tf_listener_;
|
||||||
bool calc_flow_gyro_;
|
bool calc_flow_gyro_;
|
||||||
float flow_gyro_default_;
|
float flow_gyro_default_;
|
||||||
bool disable_on_vpe_;
|
|
||||||
ros::Subscriber vpe_sub_;
|
|
||||||
ros::Time last_vpe_time_;
|
|
||||||
std::shared_ptr<dynamic_reconfigure::Server<clover::FlowConfig>> dyn_srv_;
|
std::shared_ptr<dynamic_reconfigure::Server<clover::FlowConfig>> dyn_srv_;
|
||||||
|
|
||||||
void onInit()
|
void onInit()
|
||||||
@@ -91,11 +87,6 @@ private:
|
|||||||
|
|
||||||
img_sub_ = it.subscribeCamera("image_raw", 1, &OpticalFlow::flow, this);
|
img_sub_ = it.subscribeCamera("image_raw", 1, &OpticalFlow::flow, this);
|
||||||
|
|
||||||
disable_on_vpe_ = nh_priv.param("disable_on_vpe", false);
|
|
||||||
if (disable_on_vpe_) {
|
|
||||||
vpe_sub_ = nh.subscribe("mavros/vision_pose/pose", 1, &OpticalFlow::vpeCallback, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
dyn_srv_ = std::make_shared<dynamic_reconfigure::Server<clover::FlowConfig>>(nh_priv);
|
dyn_srv_ = std::make_shared<dynamic_reconfigure::Server<clover::FlowConfig>>(nh_priv);
|
||||||
dynamic_reconfigure::Server<clover::FlowConfig>::CallbackType cb;
|
dynamic_reconfigure::Server<clover::FlowConfig>::CallbackType cb;
|
||||||
|
|
||||||
@@ -130,12 +121,6 @@ private:
|
|||||||
{
|
{
|
||||||
if (!enabled_) return;
|
if (!enabled_) return;
|
||||||
|
|
||||||
if (disable_on_vpe_ &&
|
|
||||||
!last_vpe_time_.isZero() &&
|
|
||||||
(msg->header.stamp - last_vpe_time_).toSec() < 0.1) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
parseCameraInfo(cinfo);
|
parseCameraInfo(cinfo);
|
||||||
|
|
||||||
auto img = cv_bridge::toCvShare(msg, "mono8")->image;
|
auto img = cv_bridge::toCvShare(msg, "mono8")->image;
|
||||||
@@ -169,7 +154,7 @@ private:
|
|||||||
|
|
||||||
img.convertTo(curr_, CV_32F);
|
img.convertTo(curr_, CV_32F);
|
||||||
|
|
||||||
if (prev_.empty() || (msg->header.stamp - prev_stamp_).toSec() > 0.1) { // outdated previous frame
|
if (prev_.empty()) {
|
||||||
prev_ = curr_.clone();
|
prev_ = curr_.clone();
|
||||||
prev_stamp_ = msg->header.stamp;
|
prev_stamp_ = msg->header.stamp;
|
||||||
cv::createHanningWindow(hann_, curr_.size(), CV_32F);
|
cv::createHanningWindow(hann_, curr_.size(), CV_32F);
|
||||||
@@ -251,14 +236,6 @@ private:
|
|||||||
prev_ = curr_.clone();
|
prev_ = curr_.clone();
|
||||||
prev_stamp_ = msg->header.stamp;
|
prev_stamp_ = msg->header.stamp;
|
||||||
|
|
||||||
// Publish estimated angular velocity
|
|
||||||
geometry_msgs::TwistStamped velo;
|
|
||||||
velo.header.stamp = msg->header.stamp;
|
|
||||||
velo.header.frame_id = fcu_frame_id_;
|
|
||||||
velo.twist.angular.x = flow_fcu.vector.x / integration_time.toSec();
|
|
||||||
velo.twist.angular.y = flow_fcu.vector.y / integration_time.toSec();
|
|
||||||
velo_pub_.publish(velo);
|
|
||||||
|
|
||||||
publish_debug:
|
publish_debug:
|
||||||
// Publish debug image
|
// Publish debug image
|
||||||
if (img_pub_.getNumSubscribers() > 0) {
|
if (img_pub_.getNumSubscribers() > 0) {
|
||||||
@@ -271,6 +248,14 @@ publish_debug:
|
|||||||
out_msg.image = img;
|
out_msg.image = img;
|
||||||
img_pub_.publish(out_msg.toImageMsg());
|
img_pub_.publish(out_msg.toImageMsg());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Publish estimated angular velocity
|
||||||
|
geometry_msgs::TwistStamped velo;
|
||||||
|
velo.header.stamp = msg->header.stamp;
|
||||||
|
velo.header.frame_id = fcu_frame_id_;
|
||||||
|
velo.twist.angular.x = flow_fcu.vector.x / integration_time.toSec();
|
||||||
|
velo.twist.angular.y = flow_fcu.vector.y / integration_time.toSec();
|
||||||
|
velo_pub_.publish(velo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -299,10 +284,6 @@ publish_debug:
|
|||||||
prev_ = Mat(); // clear previous frame
|
prev_ = Mat(); // clear previous frame
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void vpeCallback(const geometry_msgs::PoseStamped& vpe) {
|
|
||||||
last_vpe_time_ = vpe.header.stamp;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
PLUGINLIB_EXPORT_CLASS(OpticalFlow, nodelet::Nodelet)
|
PLUGINLIB_EXPORT_CLASS(OpticalFlow, nodelet::Nodelet)
|
||||||
|
|||||||
@@ -9,14 +9,13 @@
|
|||||||
# The above copyright notice and this permission notice shall be included in all
|
# The above copyright notice and this permission notice shall be included in all
|
||||||
# copies or substantial portions of the Software.
|
# copies or substantial portions of the Software.
|
||||||
|
|
||||||
import os, sys
|
import os
|
||||||
import math
|
import math
|
||||||
import subprocess
|
import subprocess
|
||||||
import re
|
import re
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
import traceback
|
import traceback
|
||||||
import threading
|
from threading import Event
|
||||||
from threading import Event, Thread, Lock
|
|
||||||
import numpy
|
import numpy
|
||||||
import rospy
|
import rospy
|
||||||
import tf2_ros
|
import tf2_ros
|
||||||
@@ -28,16 +27,24 @@ from mavros_msgs.msg import State, OpticalFlowRad, Mavlink
|
|||||||
from mavros_msgs.srv import ParamGet
|
from mavros_msgs.srv import ParamGet
|
||||||
from geometry_msgs.msg import PoseStamped, TwistStamped, PoseWithCovarianceStamped, Vector3Stamped
|
from geometry_msgs.msg import PoseStamped, TwistStamped, PoseWithCovarianceStamped, Vector3Stamped
|
||||||
from visualization_msgs.msg import MarkerArray as VisualizationMarkerArray
|
from visualization_msgs.msg import MarkerArray as VisualizationMarkerArray
|
||||||
from diagnostic_msgs.msg import DiagnosticArray
|
|
||||||
import tf.transformations as t
|
import tf.transformations as t
|
||||||
from aruco_pose.msg import MarkerArray
|
from aruco_pose.msg import MarkerArray
|
||||||
from mavros import mavlink
|
from mavros import mavlink
|
||||||
import locale
|
import locale
|
||||||
|
|
||||||
|
|
||||||
|
# TODO: check attitude is present
|
||||||
|
# TODO: disk free space
|
||||||
|
# TODO: map, base_link, body
|
||||||
|
# TODO: rc service
|
||||||
|
# TODO: perform commander check, ekf2 status on PX4
|
||||||
|
# TODO: check if FCU params setter succeed
|
||||||
|
# TODO: selfcheck ROS service (with blacklists for checks)
|
||||||
|
|
||||||
|
|
||||||
rospy.init_node('selfcheck')
|
rospy.init_node('selfcheck')
|
||||||
|
|
||||||
os.environ['ROSCONSOLE_FORMAT']='${message}'
|
os.environ['ROSCONSOLE_FORMAT']='[${severity}]: ${message}'
|
||||||
|
|
||||||
# use user's locale to convert numbers, etc
|
# use user's locale to convert numbers, etc
|
||||||
locale.setlocale(locale.LC_ALL, '')
|
locale.setlocale(locale.LC_ALL, '')
|
||||||
@@ -46,68 +53,46 @@ tf_buffer = tf2_ros.Buffer()
|
|||||||
tf_listener = tf2_ros.TransformListener(tf_buffer)
|
tf_listener = tf2_ros.TransformListener(tf_buffer)
|
||||||
|
|
||||||
|
|
||||||
thread_local = threading.local()
|
failures = []
|
||||||
reports_lock = Lock()
|
infos = []
|
||||||
|
current_check = None
|
||||||
|
|
||||||
# formatting colors
|
|
||||||
if sys.stdout.isatty():
|
|
||||||
GREY = '\033[90m'
|
|
||||||
GREEN = '\033[92m'
|
|
||||||
RED = '\033[31m'
|
|
||||||
END = '\033[0m'
|
|
||||||
else:
|
|
||||||
GREY = GREEN = RED = END = ''
|
|
||||||
|
|
||||||
|
|
||||||
def failure(text, *args):
|
def failure(text, *args):
|
||||||
msg = text % args
|
msg = text % args
|
||||||
thread_local.reports += [{'failure': msg}]
|
rospy.logwarn('%s: %s', current_check, msg)
|
||||||
|
failures.append(msg)
|
||||||
|
|
||||||
|
|
||||||
def info(text, *args):
|
def info(text, *args):
|
||||||
msg = text % args
|
msg = text % args
|
||||||
thread_local.reports += [{'info': msg}]
|
rospy.loginfo('%s: %s', current_check, msg)
|
||||||
|
infos.append(msg)
|
||||||
|
|
||||||
|
|
||||||
def check(name):
|
def check(name):
|
||||||
def inner(fn):
|
def inner(fn):
|
||||||
def wrapper(*args, **kwargs):
|
def wrapper(*args, **kwargs):
|
||||||
start = rospy.get_time()
|
failures[:] = []
|
||||||
thread_local.reports = []
|
infos[:] = []
|
||||||
|
global current_check
|
||||||
|
current_check = name
|
||||||
try:
|
try:
|
||||||
fn(*args, **kwargs)
|
fn(*args, **kwargs)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
rospy.logerr('%s: exception occurred', name)
|
rospy.logerr('%s: exception occurred', name)
|
||||||
with reports_lock:
|
return
|
||||||
for report in thread_local.reports:
|
if not failures and not infos:
|
||||||
if 'failure' in report:
|
rospy.loginfo('%s: OK', name)
|
||||||
rospy.logerr('%s: %s', name, report['failure'])
|
|
||||||
elif 'info' in report:
|
|
||||||
rospy.loginfo(GREY + name + END + ': ' + report['info'])
|
|
||||||
if not thread_local.reports:
|
|
||||||
rospy.loginfo(GREY + name + END + ': ' + GREEN + 'OK' + END)
|
|
||||||
if rospy.get_param('~time', False):
|
|
||||||
rospy.loginfo('%s: %.1f sec', name, rospy.get_time() - start)
|
|
||||||
return wrapper
|
return wrapper
|
||||||
return inner
|
return inner
|
||||||
|
|
||||||
|
|
||||||
def ff(value, precision=2):
|
|
||||||
# safely format float or int
|
|
||||||
if value is None:
|
|
||||||
return RED + '???' + END
|
|
||||||
if isinstance(value, float):
|
|
||||||
return ('{:.' + str(precision + 1) + '}').format(value)
|
|
||||||
elif isinstance(value, int):
|
|
||||||
return str(value)
|
|
||||||
|
|
||||||
|
|
||||||
param_get = rospy.ServiceProxy('mavros/param/get', ParamGet)
|
param_get = rospy.ServiceProxy('mavros/param/get', ParamGet)
|
||||||
|
|
||||||
|
|
||||||
def get_param(name, default=None, strict=True):
|
def get_param(name):
|
||||||
try:
|
try:
|
||||||
res = param_get(param_id=name)
|
res = param_get(param_id=name)
|
||||||
except rospy.ServiceException as e:
|
except rospy.ServiceException as e:
|
||||||
@@ -115,19 +100,13 @@ def get_param(name, default=None, strict=True):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
if not res.success:
|
if not res.success:
|
||||||
if strict:
|
failure('unable to retrieve PX4 parameter %s', name)
|
||||||
failure('unable to retrieve PX4 parameter %s', name)
|
|
||||||
return default
|
|
||||||
else:
|
else:
|
||||||
if res.value.integer != 0:
|
if res.value.integer != 0:
|
||||||
return res.value.integer
|
return res.value.integer
|
||||||
return res.value.real
|
return res.value.real
|
||||||
|
|
||||||
|
|
||||||
def get_paramf(name, precision=2):
|
|
||||||
return ff(get_param(name), precision)
|
|
||||||
|
|
||||||
|
|
||||||
recv_event = Event()
|
recv_event = Event()
|
||||||
link = mavutil.mavlink.MAVLink('', 255, 1)
|
link = mavutil.mavlink.MAVLink('', 255, 1)
|
||||||
mavlink_pub = rospy.Publisher('mavlink/to', Mavlink, queue_size=1)
|
mavlink_pub = rospy.Publisher('mavlink/to', Mavlink, queue_size=1)
|
||||||
@@ -172,24 +151,6 @@ def mavlink_exec(cmd, timeout=3.0):
|
|||||||
return mavlink_recv
|
return mavlink_recv
|
||||||
|
|
||||||
|
|
||||||
def read_diagnostics(name, key):
|
|
||||||
e = Event()
|
|
||||||
def cb(msg):
|
|
||||||
for status in msg.status:
|
|
||||||
if status.name.lower() == name.lower():
|
|
||||||
for value in status.values:
|
|
||||||
if value.key.lower() == key.lower():
|
|
||||||
cb.value = value.value
|
|
||||||
e.set()
|
|
||||||
return
|
|
||||||
|
|
||||||
cb.value = None
|
|
||||||
sub = rospy.Subscriber('/diagnostics', DiagnosticArray, cb)
|
|
||||||
e.wait(1.0) # wait to read all the diagnostics from nodes publishing them
|
|
||||||
sub.unregister()
|
|
||||||
return cb.value
|
|
||||||
|
|
||||||
|
|
||||||
BOARD_ROTATIONS = {
|
BOARD_ROTATIONS = {
|
||||||
0: 'no rotation',
|
0: 'no rotation',
|
||||||
1: 'yaw 45°',
|
1: 'yaw 45°',
|
||||||
@@ -235,36 +196,34 @@ def check_fcu():
|
|||||||
state = rospy.wait_for_message('mavros/state', State, timeout=3)
|
state = rospy.wait_for_message('mavros/state', State, timeout=3)
|
||||||
if not state.connected:
|
if not state.connected:
|
||||||
failure('no connection to the FCU (check wiring)')
|
failure('no connection to the FCU (check wiring)')
|
||||||
info('fcu_url = %s', rospy.get_param('mavros/fcu_url', '?'))
|
|
||||||
return
|
return
|
||||||
|
|
||||||
if not is_process_running('px4', exact=True): # can't use px4 console in SITL
|
clover_tag = re.compile(r'-cl[oe]ver\.\d+$')
|
||||||
clover_tag = re.compile(r'-cl[oe]ver\.\d+$')
|
clover_fw = False
|
||||||
clover_fw = False
|
|
||||||
|
|
||||||
# Make sure the console is available to us
|
# Make sure the console is available to us
|
||||||
mavlink_exec('\n')
|
mavlink_exec('\n')
|
||||||
version_str = mavlink_exec('ver all')
|
version_str = mavlink_exec('ver all')
|
||||||
if version_str == '':
|
if version_str == '':
|
||||||
info('no version data available from SITL')
|
info('no version data available from SITL')
|
||||||
|
|
||||||
for line in version_str.split('\n'):
|
for line in version_str.split('\n'):
|
||||||
if line.startswith('FW version: '):
|
if line.startswith('FW version: '):
|
||||||
info(line[len('FW version: '):])
|
info(line[len('FW version: '):])
|
||||||
elif line.startswith('FW git tag: '): # only Clover's firmware
|
elif line.startswith('FW git tag: '): # only Clover's firmware
|
||||||
tag = line[len('FW git tag: '):]
|
tag = line[len('FW git tag: '):]
|
||||||
clover_fw = clover_tag.search(tag)
|
clover_fw = clover_tag.search(tag)
|
||||||
info(tag)
|
info(tag)
|
||||||
elif line.startswith('HW arch: '):
|
elif line.startswith('HW arch: '):
|
||||||
info(line[len('HW arch: '):])
|
info(line[len('HW arch: '):])
|
||||||
|
|
||||||
if not clover_fw:
|
if not clover_fw:
|
||||||
info('not Clover PX4 firmware, check https://clover.coex.tech/firmware')
|
info('not Clover PX4 firmware, check https://clover.coex.tech/firmware')
|
||||||
|
|
||||||
est = get_param('SYS_MC_EST_GROUP')
|
est = get_param('SYS_MC_EST_GROUP')
|
||||||
if est == 1:
|
if est == 1:
|
||||||
info('selected estimator: LPE')
|
info('selected estimator: LPE')
|
||||||
fuse = int(get_param('LPE_FUSION'))
|
fuse = get_param('LPE_FUSION')
|
||||||
if fuse & (1 << 4):
|
if fuse & (1 << 4):
|
||||||
info('LPE_FUSION: land detector fusion is enabled')
|
info('LPE_FUSION: land detector fusion is enabled')
|
||||||
else:
|
else:
|
||||||
@@ -296,43 +255,21 @@ def check_fcu():
|
|||||||
if cbrk_usb_chk != 197848:
|
if cbrk_usb_chk != 197848:
|
||||||
failure('set parameter CBRK_USB_CHK to 197848 for flying with USB connected')
|
failure('set parameter CBRK_USB_CHK to 197848 for flying with USB connected')
|
||||||
|
|
||||||
if not is_process_running('px4', exact=True): # skip battery check in SITL
|
|
||||||
try:
|
|
||||||
battery = rospy.wait_for_message('mavros/battery', BatteryState, timeout=3)
|
|
||||||
if not battery.cell_voltage:
|
|
||||||
failure('cell voltage is not available, https://clover.coex.tech/power')
|
|
||||||
else:
|
|
||||||
cell = battery.cell_voltage[0]
|
|
||||||
# number of cells 1 means this is overall voltage
|
|
||||||
if len(battery.cell_voltage) == 1:
|
|
||||||
n_cells = get_param('BAT1_N_CELLS', strict=False)
|
|
||||||
if n_cells is None:
|
|
||||||
# older PX4
|
|
||||||
n_cells = get_param('BAT_N_CELLS', strict=True)
|
|
||||||
cell /= n_cells
|
|
||||||
|
|
||||||
if cell > 4.3 or cell < 3.0:
|
|
||||||
failure('incorrect cell voltage: %.2f V, https://clover.coex.tech/power', cell)
|
|
||||||
elif cell < 3.7:
|
|
||||||
failure('critically low cell voltage: %.2f V, recharge battery', cell)
|
|
||||||
except rospy.ROSException:
|
|
||||||
failure('no battery state')
|
|
||||||
|
|
||||||
# time sync check
|
|
||||||
try:
|
try:
|
||||||
info('time sync offset: %.2f s', float(read_diagnostics('mavros: Time Sync', 'Estimated time offset (s)')))
|
battery = rospy.wait_for_message('mavros/battery', BatteryState, timeout=3)
|
||||||
except:
|
if not battery.cell_voltage:
|
||||||
failure('cannot read time sync offset')
|
failure('cell voltage is not available, https://clover.coex.tech/power')
|
||||||
|
else:
|
||||||
|
cell = battery.cell_voltage[0]
|
||||||
|
if cell > 4.3 or cell < 3.0:
|
||||||
|
failure('incorrect cell voltage: %.2f V, https://clover.coex.tech/power', cell)
|
||||||
|
elif cell < 3.7:
|
||||||
|
failure('critically low cell voltage: %.2f V, recharge battery', cell)
|
||||||
|
except rospy.ROSException:
|
||||||
|
failure('no battery state')
|
||||||
|
|
||||||
except rospy.ROSException:
|
except rospy.ROSException:
|
||||||
failure('no MAVROS state')
|
failure('no MAVROS state (check wiring)')
|
||||||
fcu_url = rospy.get_param('mavros/fcu_url', '?')
|
|
||||||
if fcu_url == '/dev/px4fmu':
|
|
||||||
if not os.path.exists('/lib/udev/rules.d/99-px4fmu.rules'):
|
|
||||||
info('udev rules are not installed, install udev rules or change usb_device to /dev/ttyACM0 in mavros.launch')
|
|
||||||
else:
|
|
||||||
info('udev did\'t recognize px4fmu device, check wiring or change usb_device to /dev/ttyACM0 in mavros.launch')
|
|
||||||
info('fcu_url = %s', rospy.get_param('mavros/fcu_url', '?'))
|
|
||||||
|
|
||||||
|
|
||||||
def describe_direction(v):
|
def describe_direction(v):
|
||||||
@@ -409,24 +346,19 @@ def is_process_running(binary, exact=False, full=False):
|
|||||||
|
|
||||||
@check('ArUco markers')
|
@check('ArUco markers')
|
||||||
def check_aruco():
|
def check_aruco():
|
||||||
markers = None
|
|
||||||
|
|
||||||
if is_process_running('aruco_detect', full=True):
|
if is_process_running('aruco_detect', full=True):
|
||||||
try:
|
try:
|
||||||
info('aruco_detect/length = %g m', rospy.get_param('aruco_detect/length', '?'))
|
info('aruco_detect/length = %g m', rospy.get_param('aruco_detect/length'))
|
||||||
except KeyError:
|
except KeyError:
|
||||||
failure('aruco_detect/length parameter is not set')
|
failure('aruco_detect/length parameter is not set')
|
||||||
known_vertical = rospy.get_param('aruco_detect/known_vertical', '')
|
known_tilt = rospy.get_param('aruco_detect/known_tilt', '')
|
||||||
flip_vertical = rospy.get_param('aruco_detect/flip_vertical', False)
|
if known_tilt == 'map':
|
||||||
description = ''
|
known_tilt += ' (ALL markers are on the floor)'
|
||||||
if known_vertical == 'map' and not flip_vertical:
|
elif known_tilt == 'map_flipped':
|
||||||
description = ' (all markers are on the floor)'
|
known_tilt += ' (ALL markers are on the ceiling)'
|
||||||
elif known_vertical == 'map' and flip_vertical:
|
info('aruco_detector/known_tilt = %s', known_tilt)
|
||||||
description = ' (all markers are on the ceiling)'
|
|
||||||
info('aruco_detect/known_vertical = %s', known_vertical)
|
|
||||||
info('aruco_detect/flip_vertical = %s%s', flip_vertical, description)
|
|
||||||
try:
|
try:
|
||||||
markers = rospy.wait_for_message('aruco_detect/markers', MarkerArray, timeout=0.8)
|
rospy.wait_for_message('aruco_detect/markers', MarkerArray, timeout=1)
|
||||||
except rospy.ROSException:
|
except rospy.ROSException:
|
||||||
failure('no markers detection')
|
failure('no markers detection')
|
||||||
return
|
return
|
||||||
@@ -435,61 +367,42 @@ def check_aruco():
|
|||||||
return
|
return
|
||||||
|
|
||||||
if is_process_running('aruco_map', full=True):
|
if is_process_running('aruco_map', full=True):
|
||||||
known_vertical = rospy.get_param('aruco_map/known_vertical', '')
|
known_tilt = rospy.get_param('aruco_map/known_tilt', '')
|
||||||
flip_vertical = rospy.get_param('aruco_map/flip_vertical', False)
|
if known_tilt == 'map':
|
||||||
description = ''
|
known_tilt += ' (marker\'s map is on the floor)'
|
||||||
if known_vertical == 'map' and not flip_vertical:
|
elif known_tilt == 'map_flipped':
|
||||||
description += ' (markers map is on the floor)'
|
known_tilt += ' (marker\'s map is on the ceiling)'
|
||||||
elif known_vertical == 'map' and flip_vertical:
|
info('aruco_map/known_tilt = %s', known_tilt)
|
||||||
description += ' (markers map is on the ceiling)'
|
|
||||||
info('aruco_map/known_vertical = %s', known_vertical)
|
|
||||||
info('aruco_map/flip_vertical = %s%s', flip_vertical, description)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
visualization = rospy.wait_for_message('aruco_map/visualization', VisualizationMarkerArray, timeout=0.8)
|
visualization = rospy.wait_for_message('aruco_map/visualization', VisualizationMarkerArray, timeout=1)
|
||||||
info('map has %s markers', len(visualization.markers))
|
info('map has %s markers', len(visualization.markers))
|
||||||
except:
|
except:
|
||||||
failure('cannot read aruco_map/visualization topic')
|
failure('cannot read aruco_map/visualization topic')
|
||||||
|
|
||||||
try:
|
try:
|
||||||
rospy.wait_for_message('aruco_map/pose', PoseWithCovarianceStamped, timeout=0.8)
|
rospy.wait_for_message('aruco_map/pose', PoseWithCovarianceStamped, timeout=1)
|
||||||
except rospy.ROSException:
|
except rospy.ROSException:
|
||||||
if not markers:
|
failure('no map detection')
|
||||||
info('no map detection as no markers detection')
|
|
||||||
elif not markers.markers:
|
|
||||||
info('no map detection as no markers detected')
|
|
||||||
else:
|
|
||||||
failure('no map detection')
|
|
||||||
else:
|
else:
|
||||||
info('aruco_map is not running')
|
info('aruco_map is not running')
|
||||||
|
|
||||||
|
|
||||||
def is_on_the_floor():
|
|
||||||
try:
|
|
||||||
dist = rospy.wait_for_message('rangefinder/range', Range, timeout=1)
|
|
||||||
return dist.range < 0.3
|
|
||||||
except rospy.ROSException:
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
@check('Vision position estimate')
|
@check('Vision position estimate')
|
||||||
def check_vpe():
|
def check_vpe():
|
||||||
vis = None
|
vis = None
|
||||||
try:
|
try:
|
||||||
vis = rospy.wait_for_message('mavros/vision_pose/pose', PoseStamped, timeout=0.8)
|
vis = rospy.wait_for_message('mavros/vision_pose/pose', PoseStamped, timeout=1)
|
||||||
except rospy.ROSException:
|
except rospy.ROSException:
|
||||||
try:
|
try:
|
||||||
vis = rospy.wait_for_message('mavros/mocap/pose', PoseStamped, timeout=0.8)
|
vis = rospy.wait_for_message('mavros/mocap/pose', PoseStamped, timeout=1)
|
||||||
except rospy.ROSException:
|
except rospy.ROSException:
|
||||||
if not is_process_running('vpe_publisher', full=True):
|
failure('no VPE or MoCap messages')
|
||||||
info('no vision position estimate, vpe_publisher is not running')
|
# check if vpe_publisher is running
|
||||||
elif rospy.get_param('aruco_map/known_vertical', '') == 'map' \
|
try:
|
||||||
and rospy.get_param('aruco_map/flip_vertical', False):
|
subprocess.check_output(['pgrep', '-x', 'vpe_publisher'])
|
||||||
failure('no vision position estimate, markers are on the ceiling')
|
except subprocess.CalledProcessError:
|
||||||
elif is_on_the_floor():
|
return # it's not running, skip following checks
|
||||||
info('no vision position estimate, the drone is on the floor')
|
|
||||||
else:
|
|
||||||
failure('no vision position estimate')
|
|
||||||
|
|
||||||
# check PX4 settings
|
# check PX4 settings
|
||||||
est = get_param('SYS_MC_EST_GROUP')
|
est = get_param('SYS_MC_EST_GROUP')
|
||||||
@@ -501,37 +414,26 @@ def check_vpe():
|
|||||||
if vision_yaw_w == 0:
|
if vision_yaw_w == 0:
|
||||||
failure('vision yaw weight is zero, change ATT_W_EXT_HDG parameter')
|
failure('vision yaw weight is zero, change ATT_W_EXT_HDG parameter')
|
||||||
else:
|
else:
|
||||||
info('vision yaw weight: %s', ff(vision_yaw_w))
|
info('Vision yaw weight: %.2f', vision_yaw_w)
|
||||||
fuse = int(get_param('LPE_FUSION'))
|
fuse = get_param('LPE_FUSION')
|
||||||
if not fuse & (1 << 2):
|
if not fuse & (1 << 2):
|
||||||
failure('vision position fusion is disabled, change LPE_FUSION parameter')
|
failure('vision position fusion is disabled, change LPE_FUSION parameter')
|
||||||
delay = get_param('LPE_VIS_DELAY')
|
delay = get_param('LPE_VIS_DELAY')
|
||||||
if delay != 0:
|
if delay != 0:
|
||||||
failure('LPE_VIS_DELAY = %s, but it should be zero', delay)
|
failure('LPE_VIS_DELAY parameter is %s, but it should be zero', delay)
|
||||||
info('LPE_VIS_XY = %s m, LPE_VIS_Z = %s m', get_paramf('LPE_VIS_XY'), get_paramf('LPE_VIS_Z'))
|
info('LPE_VIS_XY is %.2f m, LPE_VIS_Z is %.2f m', get_param('LPE_VIS_XY'), get_param('LPE_VIS_Z'))
|
||||||
elif est == 2:
|
elif est == 2:
|
||||||
ev_ctrl = get_param('EKF2_EV_CTRL', strict=False)
|
fuse = get_param('EKF2_AID_MASK')
|
||||||
if ev_ctrl is not None: # PX4 after v1.14
|
if not fuse & (1 << 3):
|
||||||
ev_ctrl = int(ev_ctrl)
|
failure('vision position fusion is disabled, change EKF2_AID_MASK parameter')
|
||||||
if not ev_ctrl & (1 << 0):
|
if not fuse & (1 << 4):
|
||||||
failure('vision horizontal position fusion is disabled, change EKF2_EV_CTRL parameter')
|
failure('vision yaw fusion is disabled, change EKF2_AID_MASK parameter')
|
||||||
if not ev_ctrl & (1 << 1):
|
|
||||||
failure('vision vertical position fusion is disabled, change EKF2_EV_CTRL parameter')
|
|
||||||
if not ev_ctrl & (1 << 3):
|
|
||||||
failure('vision yaw fusion is disabled, change EKF2_EV_CTRL parameter')
|
|
||||||
else: # PX4 before v1.14
|
|
||||||
fuse = int(get_param('EKF2_AID_MASK'))
|
|
||||||
if not fuse & (1 << 3):
|
|
||||||
failure('vision position fusion is disabled, change EKF2_AID_MASK parameter')
|
|
||||||
if not fuse & (1 << 4):
|
|
||||||
failure('vision yaw fusion is disabled, change EKF2_AID_MASK parameter')
|
|
||||||
|
|
||||||
delay = get_param('EKF2_EV_DELAY')
|
delay = get_param('EKF2_EV_DELAY')
|
||||||
if delay != 0:
|
if delay != 0:
|
||||||
failure('EKF2_EV_DELAY = %.2f, but it should be zero', delay)
|
failure('EKF2_EV_DELAY is %.2f, but it should be zero', delay)
|
||||||
info('EKF2_EVA_NOISE = %s, EKF2_EVP_NOISE = %s',
|
info('EKF2_EVA_NOISE is %.3f, EKF2_EVP_NOISE is %.3f',
|
||||||
get_paramf('EKF2_EVA_NOISE', 3),
|
get_param('EKF2_EVA_NOISE'),
|
||||||
get_paramf('EKF2_EVP_NOISE', 3))
|
get_param('EKF2_EVP_NOISE'))
|
||||||
|
|
||||||
if not vis:
|
if not vis:
|
||||||
return
|
return
|
||||||
@@ -629,25 +531,15 @@ def check_velocity():
|
|||||||
@check('Global position (GPS)')
|
@check('Global position (GPS)')
|
||||||
def check_global_position():
|
def check_global_position():
|
||||||
try:
|
try:
|
||||||
rospy.wait_for_message('mavros/global_position/global', NavSatFix, timeout=0.8)
|
rospy.wait_for_message('mavros/global_position/global', NavSatFix, timeout=1)
|
||||||
except rospy.ROSException:
|
except rospy.ROSException:
|
||||||
info('no global position')
|
info('no global position')
|
||||||
if get_param('SYS_MC_EST_GROUP') == 2:
|
if get_param('SYS_MC_EST_GROUP') == 2 and (get_param('EKF2_AID_MASK') & (1 << 0)):
|
||||||
gps_ctrl = get_param('EKF2_GPS_CTRL', strict=False)
|
failure('enabled GPS fusion may suppress vision position aiding')
|
||||||
if gps_ctrl is not None: # PX4 after v1.14
|
|
||||||
if int(gps_ctrl) & (1 << 0):
|
|
||||||
failure('GPS fusion enabled may suppress vision position aiding, change EKF2_GPS_CTRL')
|
|
||||||
else: # PX4 before v1.14
|
|
||||||
if int(get_param('EKF2_AID_MASK', 0)) & (1 << 0):
|
|
||||||
failure('GPS fusion enabled may suppress vision position aiding, change EKF2_AID_MASK')
|
|
||||||
|
|
||||||
|
|
||||||
@check('Optical flow')
|
@check('Optical flow')
|
||||||
def check_optical_flow():
|
def check_optical_flow():
|
||||||
if not is_process_running('optical_flow', full=True):
|
|
||||||
info('optical_flow is not running')
|
|
||||||
return
|
|
||||||
|
|
||||||
# TODO:check FPS!
|
# TODO:check FPS!
|
||||||
try:
|
try:
|
||||||
rospy.wait_for_message('mavros/px4flow/raw/send', OpticalFlowRad, timeout=0.5)
|
rospy.wait_for_message('mavros/px4flow/raw/send', OpticalFlowRad, timeout=0.5)
|
||||||
@@ -655,49 +547,40 @@ def check_optical_flow():
|
|||||||
# check PX4 settings
|
# check PX4 settings
|
||||||
rot = get_param('SENS_FLOW_ROT')
|
rot = get_param('SENS_FLOW_ROT')
|
||||||
if rot != 0:
|
if rot != 0:
|
||||||
failure('SENS_FLOW_ROT = %s, but it should be zero', rot)
|
failure('SENS_FLOW_ROT parameter is %s, but it should be zero', rot)
|
||||||
est = get_param('SYS_MC_EST_GROUP')
|
est = get_param('SYS_MC_EST_GROUP')
|
||||||
if est == 1:
|
if est == 1:
|
||||||
fuse = int(get_param('LPE_FUSION'))
|
fuse = get_param('LPE_FUSION')
|
||||||
if not fuse & (1 << 1):
|
if not fuse & (1 << 1):
|
||||||
failure('optical flow fusion is disabled, change LPE_FUSION parameter')
|
failure('optical flow fusion is disabled, change LPE_FUSION parameter')
|
||||||
if not fuse & (1 << 1):
|
if not fuse & (1 << 1):
|
||||||
failure('flow gyro compensation is disabled, change LPE_FUSION parameter')
|
failure('flow gyro compensation is disabled, change LPE_FUSION parameter')
|
||||||
scale = get_param('LPE_FLW_SCALE', 1)
|
scale = get_param('LPE_FLW_SCALE')
|
||||||
if not numpy.isclose(scale, 1.0):
|
if not numpy.isclose(scale, 1.0):
|
||||||
failure('LPE_FLW_SCALE = %.2f, but it should be 1.0', scale)
|
failure('LPE_FLW_SCALE parameter is %.2f, but it should be 1.0', scale)
|
||||||
|
|
||||||
info('LPE_FLW_QMIN = %s, LPE_FLW_R = %s, LPE_FLW_RR = %s',
|
info('LPE_FLW_QMIN is %s, LPE_FLW_R is %.4f, LPE_FLW_RR is %.4f, SENS_FLOW_MINHGT is %.3f, SENS_FLOW_MAXHGT is %.3f',
|
||||||
get_paramf('LPE_FLW_QMIN'),
|
get_param('LPE_FLW_QMIN'),
|
||||||
get_paramf('LPE_FLW_R', 4),
|
get_param('LPE_FLW_R'),
|
||||||
get_paramf('LPE_FLW_RR', 4))
|
get_param('LPE_FLW_RR'),
|
||||||
|
get_param('SENS_FLOW_MINHGT'),
|
||||||
|
get_param('SENS_FLOW_MAXHGT'))
|
||||||
elif est == 2:
|
elif est == 2:
|
||||||
of_ctrl = get_param('EKF2_OF_CTRL', strict=False)
|
fuse = get_param('EKF2_AID_MASK')
|
||||||
if of_ctrl is not None: # PX4 after v1.14
|
if not fuse & (1 << 1):
|
||||||
if of_ctrl == 0:
|
failure('optical flow fusion is disabled, change EKF2_AID_MASK parameter')
|
||||||
failure('optical flow fusion is disabled, change EKF2_OF_CTRL')
|
delay = get_param('EKF2_OF_DELAY')
|
||||||
else: # PX4 before v1.14
|
|
||||||
fuse = int(get_param('EKF2_AID_MASK', 0))
|
|
||||||
if not fuse & (1 << 1):
|
|
||||||
failure('optical flow fusion is disabled, change EKF2_AID_MASK parameter')
|
|
||||||
delay = get_param('EKF2_OF_DELAY', 0)
|
|
||||||
if delay != 0:
|
if delay != 0:
|
||||||
failure('EKF2_OF_DELAY = %.2f, but it should be zero', delay)
|
failure('EKF2_OF_DELAY is %.2f, but it should be zero', delay)
|
||||||
info('EKF2_OF_QMIN = %s, EKF2_OF_N_MIN = %s, EKF2_OF_N_MAX = %s',
|
info('EKF2_OF_QMIN is %s, EKF2_OF_N_MIN is %.4f, EKF2_OF_N_MAX is %.4f, SENS_FLOW_MINHGT is %.3f, SENS_FLOW_MAXHGT is %.3f',
|
||||||
get_paramf('EKF2_OF_QMIN'),
|
get_param('EKF2_OF_QMIN'),
|
||||||
get_paramf('EKF2_OF_N_MIN', 4),
|
get_param('EKF2_OF_N_MIN'),
|
||||||
get_paramf('EKF2_OF_N_MAX', 4))
|
get_param('EKF2_OF_N_MAX'),
|
||||||
info('SENS_FLOW_MINHGT = %s, SENS_FLOW_MAXHGT = %s', get_paramf('SENS_FLOW_MINHGT', 3), get_paramf('SENS_FLOW_MAXHGT', 3))
|
get_param('SENS_FLOW_MINHGT'),
|
||||||
|
get_param('SENS_FLOW_MAXHGT'))
|
||||||
|
|
||||||
except rospy.ROSException:
|
except rospy.ROSException:
|
||||||
if rospy.get_param('optical_flow/disable_on_vpe', False):
|
failure('no optical flow data (from Raspberry)')
|
||||||
try:
|
|
||||||
rospy.wait_for_message('mavros/vision_pose/pose', PoseStamped, timeout=1)
|
|
||||||
info('no optical flow as disable_on_vpe is true')
|
|
||||||
except:
|
|
||||||
failure('no optical flow on RPi, disable_on_vpe is true, but no vision pose also')
|
|
||||||
else:
|
|
||||||
failure('no optical flow on RPi')
|
|
||||||
|
|
||||||
|
|
||||||
@check('Rangefinder')
|
@check('Rangefinder')
|
||||||
@@ -721,34 +604,27 @@ def check_rangefinder():
|
|||||||
|
|
||||||
est = get_param('SYS_MC_EST_GROUP')
|
est = get_param('SYS_MC_EST_GROUP')
|
||||||
if est == 1:
|
if est == 1:
|
||||||
fuse = int(get_param('LPE_FUSION', 0))
|
fuse = get_param('LPE_FUSION')
|
||||||
if not fuse & (1 << 5):
|
if not fuse & (1 << 5):
|
||||||
info('"pub agl as lpos down" in LPE_FUSION is disabled, NOT operating over flat surface')
|
info('"pub agl as lpos down" in LPE_FUSION is disabled, NOT operating over flat surface')
|
||||||
else:
|
else:
|
||||||
info('"pub agl as lpos down" in LPE_FUSION is enabled, operating over flat surface')
|
info('"pub agl as lpos down" in LPE_FUSION is enabled, operating over flat surface')
|
||||||
|
|
||||||
elif est == 2:
|
elif est == 2:
|
||||||
hgt = get_param('EKF2_HGT_REF', strict=False)
|
hgt = get_param('EKF2_HGT_MODE')
|
||||||
if hgt is None: # PX4 before v1.14
|
|
||||||
hgt = get_param('EKF2_HGT_MODE')
|
|
||||||
if hgt != 2:
|
if hgt != 2:
|
||||||
info('EKF2_HGT_MODE != Range sensor, NOT operating over flat surface')
|
info('EKF2_HGT_MODE != Range sensor, NOT operating over flat surface')
|
||||||
else:
|
else:
|
||||||
info('EKF2_HGT_MODE = Range sensor, operating over flat surface')
|
info('EKF2_HGT_MODE = Range sensor, operating over flat surface')
|
||||||
aid = get_param('EKF2_RNG_AID', strict=False)
|
aid = get_param('EKF2_RNG_AID')
|
||||||
if aid is not None: # PX4 before v1.14
|
if aid != 1:
|
||||||
if aid != 1:
|
info('EKF2_RNG_AID != 1, range sensor aiding disabled')
|
||||||
info('EKF2_RNG_AID != 1, range sensor aiding disabled')
|
else:
|
||||||
else:
|
info('EKF2_RNG_AID = 1, range sensor aiding enabled')
|
||||||
info('EKF2_RNG_AID = 1, range sensor aiding enabled')
|
|
||||||
|
|
||||||
|
|
||||||
@check('Boot duration')
|
@check('Boot duration')
|
||||||
def check_boot_duration():
|
def check_boot_duration():
|
||||||
if not os.path.exists('/etc/clover_version'):
|
|
||||||
info('skip check')
|
|
||||||
return # Don't check not on Clover's image
|
|
||||||
|
|
||||||
output = subprocess.check_output('systemd-analyze').decode()
|
output = subprocess.check_output('systemd-analyze').decode()
|
||||||
r = re.compile(r'([\d\.]+)s\s*$', flags=re.MULTILINE)
|
r = re.compile(r'([\d\.]+)s\s*$', flags=re.MULTILINE)
|
||||||
duration = float(r.search(output).groups()[0])
|
duration = float(r.search(output).groups()[0])
|
||||||
@@ -758,7 +634,7 @@ def check_boot_duration():
|
|||||||
|
|
||||||
@check('CPU usage')
|
@check('CPU usage')
|
||||||
def check_cpu_usage():
|
def check_cpu_usage():
|
||||||
WHITELIST = 'nodelet', 'gzclient', 'gzserver', 'selfcheck.py'
|
WHITELIST = 'nodelet', 'gzclient', 'gzserver'
|
||||||
CMD = "top -n 1 -b -i | tail -n +8 | awk '{ printf(\"%-8s\\t%-8s\\t%-8s\\n\", $1, $9, $12); }'"
|
CMD = "top -n 1 -b -i | tail -n +8 | awk '{ printf(\"%-8s\\t%-8s\\t%-8s\\n\", $1, $9, $12); }'"
|
||||||
output = subprocess.check_output(CMD, shell=True).decode()
|
output = subprocess.check_output(CMD, shell=True).decode()
|
||||||
processes = output.split('\n')
|
processes = output.split('\n')
|
||||||
@@ -827,10 +703,7 @@ def check_image():
|
|||||||
try:
|
try:
|
||||||
info('version: %s', open('/etc/clover_version').read().strip())
|
info('version: %s', open('/etc/clover_version').read().strip())
|
||||||
except IOError:
|
except IOError:
|
||||||
try:
|
info('no /etc/clover_version file, not the Clover image?')
|
||||||
info('VM version: %s', open('/etc/clover_vm_version').read().strip())
|
|
||||||
except IOError:
|
|
||||||
info('no /etc/clover_version file, not the Clover image?')
|
|
||||||
|
|
||||||
|
|
||||||
@check('Preflight status')
|
@check('Preflight status')
|
||||||
@@ -941,47 +814,26 @@ def check_board():
|
|||||||
info('could not open /proc/device-tree/model, not a Raspberry Pi?')
|
info('could not open /proc/device-tree/model, not a Raspberry Pi?')
|
||||||
|
|
||||||
|
|
||||||
def parallel_for(fns):
|
|
||||||
threads = []
|
|
||||||
for fn in fns:
|
|
||||||
thread = Thread(target=fn)
|
|
||||||
thread.start()
|
|
||||||
threads.append(thread)
|
|
||||||
for thread in threads:
|
|
||||||
thread.join()
|
|
||||||
|
|
||||||
|
|
||||||
def consequentially_for(fns):
|
|
||||||
for fn in fns:
|
|
||||||
fn()
|
|
||||||
|
|
||||||
|
|
||||||
def selfcheck():
|
def selfcheck():
|
||||||
checks = [
|
check_image()
|
||||||
check_image,
|
check_board()
|
||||||
check_board,
|
check_clover_service()
|
||||||
check_clover_service,
|
check_network()
|
||||||
check_network,
|
check_fcu()
|
||||||
check_fcu,
|
check_imu()
|
||||||
check_imu,
|
check_local_position()
|
||||||
check_local_position,
|
check_velocity()
|
||||||
check_velocity,
|
check_global_position()
|
||||||
check_global_position,
|
check_preflight_status()
|
||||||
check_preflight_status,
|
check_main_camera()
|
||||||
check_main_camera,
|
check_aruco()
|
||||||
check_aruco,
|
check_simpleoffboard()
|
||||||
check_simpleoffboard,
|
check_optical_flow()
|
||||||
check_optical_flow,
|
check_vpe()
|
||||||
check_vpe,
|
check_rangefinder()
|
||||||
check_rangefinder,
|
check_rpi_health()
|
||||||
check_rpi_health,
|
check_cpu_usage()
|
||||||
check_cpu_usage,
|
check_boot_duration()
|
||||||
check_boot_duration,
|
|
||||||
]
|
|
||||||
if rospy.get_param('~parallel', False):
|
|
||||||
parallel_for(checks)
|
|
||||||
else:
|
|
||||||
consequentially_for(checks)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
@@ -23,14 +23,12 @@
|
|||||||
#include <tf2_ros/static_transform_broadcaster.h>
|
#include <tf2_ros/static_transform_broadcaster.h>
|
||||||
#include <tf2_geometry_msgs/tf2_geometry_msgs.h>
|
#include <tf2_geometry_msgs/tf2_geometry_msgs.h>
|
||||||
#include <std_srvs/Trigger.h>
|
#include <std_srvs/Trigger.h>
|
||||||
#include <geometry_msgs/PointStamped.h>
|
|
||||||
#include <geometry_msgs/PoseStamped.h>
|
#include <geometry_msgs/PoseStamped.h>
|
||||||
#include <geometry_msgs/TwistStamped.h>
|
#include <geometry_msgs/TwistStamped.h>
|
||||||
#include <geometry_msgs/Vector3Stamped.h>
|
#include <geometry_msgs/Vector3Stamped.h>
|
||||||
#include <geometry_msgs/QuaternionStamped.h>
|
#include <geometry_msgs/QuaternionStamped.h>
|
||||||
#include <sensor_msgs/NavSatFix.h>
|
#include <sensor_msgs/NavSatFix.h>
|
||||||
#include <sensor_msgs/BatteryState.h>
|
#include <sensor_msgs/BatteryState.h>
|
||||||
#include <sensor_msgs/Range.h>
|
|
||||||
#include <mavros_msgs/CommandBool.h>
|
#include <mavros_msgs/CommandBool.h>
|
||||||
#include <mavros_msgs/SetMode.h>
|
#include <mavros_msgs/SetMode.h>
|
||||||
#include <mavros_msgs/PositionTarget.h>
|
#include <mavros_msgs/PositionTarget.h>
|
||||||
@@ -39,19 +37,14 @@
|
|||||||
#include <mavros_msgs/State.h>
|
#include <mavros_msgs/State.h>
|
||||||
#include <mavros_msgs/StatusText.h>
|
#include <mavros_msgs/StatusText.h>
|
||||||
#include <mavros_msgs/ManualControl.h>
|
#include <mavros_msgs/ManualControl.h>
|
||||||
#include <mavros_msgs/Altitude.h>
|
|
||||||
|
|
||||||
#include <clover/GetTelemetry.h>
|
#include <clover/GetTelemetry.h>
|
||||||
#include <clover/Navigate.h>
|
#include <clover/Navigate.h>
|
||||||
#include <clover/NavigateGlobal.h>
|
#include <clover/NavigateGlobal.h>
|
||||||
#include <clover/SetAltitude.h>
|
|
||||||
#include <clover/SetYaw.h>
|
|
||||||
#include <clover/SetYawRate.h>
|
|
||||||
#include <clover/SetPosition.h>
|
#include <clover/SetPosition.h>
|
||||||
#include <clover/SetVelocity.h>
|
#include <clover/SetVelocity.h>
|
||||||
#include <clover/SetAttitude.h>
|
#include <clover/SetAttitude.h>
|
||||||
#include <clover/SetRates.h>
|
#include <clover/SetRates.h>
|
||||||
#include <clover/State.h>
|
|
||||||
|
|
||||||
using std::string;
|
using std::string;
|
||||||
using std::isnan;
|
using std::isnan;
|
||||||
@@ -61,7 +54,6 @@ using namespace clover;
|
|||||||
using mavros_msgs::PositionTarget;
|
using mavros_msgs::PositionTarget;
|
||||||
using mavros_msgs::AttitudeTarget;
|
using mavros_msgs::AttitudeTarget;
|
||||||
using mavros_msgs::Thrust;
|
using mavros_msgs::Thrust;
|
||||||
using mavros_msgs::Altitude;
|
|
||||||
|
|
||||||
// tf2
|
// tf2
|
||||||
tf2_ros::Buffer tf_buffer;
|
tf2_ros::Buffer tf_buffer;
|
||||||
@@ -87,43 +79,35 @@ float default_speed;
|
|||||||
bool auto_release;
|
bool auto_release;
|
||||||
bool land_only_in_offboard, nav_from_sp, check_kill_switch;
|
bool land_only_in_offboard, nav_from_sp, check_kill_switch;
|
||||||
std::map<string, string> reference_frames;
|
std::map<string, string> reference_frames;
|
||||||
string terrain_frame_mode;
|
|
||||||
|
|
||||||
// Publishers
|
// Publishers
|
||||||
ros::Publisher attitude_pub, attitude_raw_pub, position_pub, position_raw_pub, rates_pub, thrust_pub, state_pub;
|
ros::Publisher attitude_pub, attitude_raw_pub, position_pub, position_raw_pub, rates_pub, thrust_pub;
|
||||||
|
|
||||||
// Service clients
|
// Service clients
|
||||||
ros::ServiceClient arming, set_mode;
|
ros::ServiceClient arming, set_mode;
|
||||||
|
|
||||||
// Containers
|
// Containers
|
||||||
ros::Timer setpoint_timer;
|
ros::Timer setpoint_timer;
|
||||||
|
tf::Quaternion tq;
|
||||||
PoseStamped position_msg;
|
PoseStamped position_msg;
|
||||||
PositionTarget position_raw_msg;
|
PositionTarget position_raw_msg;
|
||||||
//TwistStamped rates_msg;
|
AttitudeTarget att_raw_msg;
|
||||||
|
Thrust thrust_msg;
|
||||||
|
TwistStamped rates_msg;
|
||||||
TransformStamped target, setpoint;
|
TransformStamped target, setpoint;
|
||||||
geometry_msgs::TransformStamped body;
|
geometry_msgs::TransformStamped body;
|
||||||
geometry_msgs::TransformStamped terrain;
|
|
||||||
|
|
||||||
// State
|
// State
|
||||||
PoseStamped nav_start;
|
PoseStamped nav_start;
|
||||||
PointStamped setpoint_position;
|
PoseStamped setpoint_position, setpoint_position_transformed;
|
||||||
PointStamped setpoint_altitude;
|
Vector3Stamped setpoint_velocity, setpoint_velocity_transformed;
|
||||||
Vector3Stamped setpoint_velocity;
|
QuaternionStamped setpoint_attitude, setpoint_attitude_transformed;
|
||||||
float setpoint_yaw, setpoint_roll, setpoint_pitch;
|
float setpoint_yaw_rate;
|
||||||
Vector3 setpoint_rates;
|
|
||||||
string yaw_frame_id;
|
|
||||||
float setpoint_thrust;
|
|
||||||
float nav_speed;
|
float nav_speed;
|
||||||
float setpoint_lat = NAN, setpoint_lon = NAN;
|
|
||||||
bool busy = false;
|
bool busy = false;
|
||||||
bool wait_armed = false;
|
bool wait_armed = false;
|
||||||
bool nav_from_sp_flag = false;
|
bool nav_from_sp_flag = false;
|
||||||
|
|
||||||
// Last published
|
|
||||||
PoseStamped setpoint_pose_local;
|
|
||||||
Vector3Stamped setpoint_velocity_local;
|
|
||||||
float yaw_local;
|
|
||||||
|
|
||||||
enum setpoint_type_t {
|
enum setpoint_type_t {
|
||||||
NONE,
|
NONE,
|
||||||
NAVIGATE,
|
NAVIGATE,
|
||||||
@@ -131,10 +115,7 @@ enum setpoint_type_t {
|
|||||||
POSITION,
|
POSITION,
|
||||||
VELOCITY,
|
VELOCITY,
|
||||||
ATTITUDE,
|
ATTITUDE,
|
||||||
RATES,
|
RATES
|
||||||
_ALTITUDE,
|
|
||||||
_YAW,
|
|
||||||
_YAW_RATE,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
enum setpoint_type_t setpoint_type = NONE;
|
enum setpoint_type_t setpoint_type = NONE;
|
||||||
@@ -189,7 +170,7 @@ void handleLocalPosition(const PoseStamped& pose)
|
|||||||
{
|
{
|
||||||
local_position = pose;
|
local_position = pose;
|
||||||
publishBodyFrame();
|
publishBodyFrame();
|
||||||
// TODO: home?
|
// TODO: terrain?, home?
|
||||||
}
|
}
|
||||||
|
|
||||||
// wait for transform without interrupting publishing setpoints
|
// wait for transform without interrupting publishing setpoints
|
||||||
@@ -207,29 +188,6 @@ inline bool waitTransform(const string& target, const string& source,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void publishTerrain(const double distance, const ros::Time& stamp)
|
|
||||||
{
|
|
||||||
if (!waitTransform(local_frame, body.child_frame_id, stamp, ros::Duration(0.1))) return;
|
|
||||||
|
|
||||||
auto t = tf_buffer.lookupTransform(local_frame, body.child_frame_id, stamp);
|
|
||||||
t.child_frame_id = terrain.child_frame_id;
|
|
||||||
t.transform.translation.z -= distance;
|
|
||||||
static_transform_broadcaster->sendTransform(t);
|
|
||||||
}
|
|
||||||
|
|
||||||
void handleAltitude(const Altitude& alt)
|
|
||||||
{
|
|
||||||
if (!std::isfinite(alt.bottom_clearance)) return;
|
|
||||||
publishTerrain(alt.bottom_clearance, alt.header.stamp);
|
|
||||||
}
|
|
||||||
|
|
||||||
void handleRange(const Range& range)
|
|
||||||
{
|
|
||||||
if (!std::isfinite(range.range)) return;
|
|
||||||
// TODO: check it's facing down
|
|
||||||
publishTerrain(range.range, range.header.stamp);
|
|
||||||
}
|
|
||||||
|
|
||||||
#define TIMEOUT(msg, timeout) (msg.header.stamp.isZero() || (ros::Time::now() - msg.header.stamp > timeout))
|
#define TIMEOUT(msg, timeout) (msg.header.stamp.isZero() || (ros::Time::now() - msg.header.stamp > timeout))
|
||||||
|
|
||||||
bool getTelemetry(GetTelemetry::Request& req, GetTelemetry::Response& res)
|
bool getTelemetry(GetTelemetry::Request& req, GetTelemetry::Response& res)
|
||||||
@@ -249,11 +207,11 @@ bool getTelemetry(GetTelemetry::Request& req, GetTelemetry::Response& res)
|
|||||||
res.vx = NAN;
|
res.vx = NAN;
|
||||||
res.vy = NAN;
|
res.vy = NAN;
|
||||||
res.vz = NAN;
|
res.vz = NAN;
|
||||||
res.roll = NAN;
|
|
||||||
res.pitch = NAN;
|
res.pitch = NAN;
|
||||||
|
res.roll = NAN;
|
||||||
res.yaw = NAN;
|
res.yaw = NAN;
|
||||||
res.roll_rate = NAN;
|
|
||||||
res.pitch_rate = NAN;
|
res.pitch_rate = NAN;
|
||||||
|
res.roll_rate = NAN;
|
||||||
res.yaw_rate = NAN;
|
res.yaw_rate = NAN;
|
||||||
res.voltage = NAN;
|
res.voltage = NAN;
|
||||||
res.cell_voltage = NAN;
|
res.cell_voltage = NAN;
|
||||||
@@ -383,20 +341,20 @@ inline float getDistance(const Point& from, const Point& to)
|
|||||||
return hypot(from.x - to.x, from.y - to.y, from.z - to.z);
|
return hypot(from.x - to.x, from.y - to.y, from.z - to.z);
|
||||||
}
|
}
|
||||||
|
|
||||||
void getNavigateSetpoint(const ros::Time& stamp, const float speed, Point& nav_setpoint)
|
void getNavigateSetpoint(const ros::Time& stamp, float speed, Point& nav_setpoint)
|
||||||
{
|
{
|
||||||
if (wait_armed) {
|
if (wait_armed) {
|
||||||
// don't start navigating if we're waiting arming
|
// don't start navigating if we're waiting arming
|
||||||
nav_start.header.stamp = stamp;
|
nav_start.header.stamp = stamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
float distance = getDistance(nav_start.pose.position, setpoint_pose_local.pose.position);
|
float distance = getDistance(nav_start.pose.position, setpoint_position_transformed.pose.position);
|
||||||
float time = distance / speed;
|
float time = distance / speed;
|
||||||
float passed = std::min((stamp - nav_start.header.stamp).toSec() / time, 1.0);
|
float passed = std::min((stamp - nav_start.header.stamp).toSec() / time, 1.0);
|
||||||
|
|
||||||
nav_setpoint.x = nav_start.pose.position.x + (setpoint_pose_local.pose.position.x - nav_start.pose.position.x) * passed;
|
nav_setpoint.x = nav_start.pose.position.x + (setpoint_position_transformed.pose.position.x - nav_start.pose.position.x) * passed;
|
||||||
nav_setpoint.y = nav_start.pose.position.y + (setpoint_pose_local.pose.position.y - nav_start.pose.position.y) * passed;
|
nav_setpoint.y = nav_start.pose.position.y + (setpoint_position_transformed.pose.position.y - nav_start.pose.position.y) * passed;
|
||||||
nav_setpoint.z = nav_start.pose.position.z + (setpoint_pose_local.pose.position.z - nav_start.pose.position.z) * passed;
|
nav_setpoint.z = nav_start.pose.position.z + (setpoint_position_transformed.pose.position.z - nav_start.pose.position.z) * passed;
|
||||||
}
|
}
|
||||||
|
|
||||||
PoseStamped globalToLocal(double lat, double lon)
|
PoseStamped globalToLocal(double lat, double lon)
|
||||||
@@ -427,101 +385,44 @@ PoseStamped globalToLocal(double lat, double lon)
|
|||||||
return pose;
|
return pose;
|
||||||
}
|
}
|
||||||
|
|
||||||
// publish navigate_target frame
|
|
||||||
void publishTarget(ros::Time stamp, bool _static = false)
|
|
||||||
{
|
|
||||||
bool single_frame = (setpoint_position.header.frame_id == setpoint_altitude.header.frame_id);
|
|
||||||
|
|
||||||
// handle yaw for target frame
|
|
||||||
if (setpoint_yaw_type == YAW || setpoint_yaw_type == YAW_RATE) { // use last set yaw for yaw_rate
|
|
||||||
if (setpoint_altitude.header.frame_id == yaw_frame_id) {
|
|
||||||
target.transform.rotation = tf::createQuaternionMsgFromYaw(setpoint_yaw);
|
|
||||||
} else {
|
|
||||||
single_frame = false;
|
|
||||||
target.transform.rotation = tf::createQuaternionMsgFromYaw(yaw_local);
|
|
||||||
}
|
|
||||||
} else if (setpoint_yaw_type == TOWARDS) {
|
|
||||||
single_frame = false;
|
|
||||||
target.transform.rotation = tf::createQuaternionMsgFromYaw(yaw_local);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_static && single_frame) {
|
|
||||||
// publish at user's command, if all frames are the same
|
|
||||||
target.header.frame_id = setpoint_position.header.frame_id;
|
|
||||||
target.header.stamp = stamp;
|
|
||||||
target.transform.translation.x = setpoint_position.point.x;
|
|
||||||
target.transform.translation.y = setpoint_position.point.y;
|
|
||||||
target.transform.translation.z = setpoint_position.point.z;
|
|
||||||
|
|
||||||
} else if (!_static) {
|
|
||||||
// publish at each iteration, if frames are different
|
|
||||||
target.header = setpoint_pose_local.header;
|
|
||||||
target.transform.translation.x = setpoint_pose_local.pose.position.x;
|
|
||||||
target.transform.translation.y = setpoint_pose_local.pose.position.y;
|
|
||||||
target.transform.translation.z = setpoint_pose_local.pose.position.z;
|
|
||||||
}
|
|
||||||
|
|
||||||
static_transform_broadcaster->sendTransform(target);
|
|
||||||
}
|
|
||||||
|
|
||||||
void publish(const ros::Time stamp)
|
void publish(const ros::Time stamp)
|
||||||
{
|
{
|
||||||
if (setpoint_type == NONE) return;
|
if (setpoint_type == NONE) return;
|
||||||
|
|
||||||
position_raw_msg.header.stamp = stamp;
|
position_raw_msg.header.stamp = stamp;
|
||||||
|
thrust_msg.header.stamp = stamp;
|
||||||
|
rates_msg.header.stamp = stamp;
|
||||||
|
|
||||||
// transform position
|
try {
|
||||||
if (setpoint_type == NAVIGATE || setpoint_type == NAVIGATE_GLOBAL || setpoint_type == POSITION) {
|
// transform position and/or yaw
|
||||||
setpoint_position.header.stamp = stamp;
|
if (setpoint_type == NAVIGATE || setpoint_type == NAVIGATE_GLOBAL || setpoint_type == POSITION || setpoint_type == VELOCITY || setpoint_type == ATTITUDE) {
|
||||||
setpoint_altitude.header.stamp = stamp;
|
setpoint_position.header.stamp = stamp;
|
||||||
// transform xy
|
tf_buffer.transform(setpoint_position, setpoint_position_transformed, local_frame, ros::Duration(0.05));
|
||||||
try {
|
|
||||||
auto xy = tf_buffer.transform(setpoint_position, local_frame, ros::Duration(0.05));
|
|
||||||
setpoint_pose_local.header = xy.header;
|
|
||||||
setpoint_pose_local.pose.position.x = xy.point.x;
|
|
||||||
setpoint_pose_local.pose.position.y = xy.point.y;
|
|
||||||
} catch (tf2::TransformException& ex) {
|
|
||||||
// can't transform xy, use last known
|
|
||||||
ROS_WARN_THROTTLE(10, "can't transform: %s", ex.what());
|
|
||||||
}
|
}
|
||||||
// transform altitude
|
|
||||||
try {
|
// transform velocity
|
||||||
setpoint_pose_local.pose.position.z = tf_buffer.transform(setpoint_altitude, local_frame, ros::Duration(0.05)).point.z;
|
if (setpoint_type == VELOCITY) {
|
||||||
} catch (tf2::TransformException& ex) {
|
setpoint_velocity.header.stamp = stamp;
|
||||||
// can't transform altitude, use last known
|
tf_buffer.transform(setpoint_velocity, setpoint_velocity_transformed, local_frame, ros::Duration(0.05));
|
||||||
ROS_WARN_THROTTLE(10, "can't transform: %s", ex.what());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} catch (const tf2::TransformException& e) {
|
||||||
|
ROS_WARN_THROTTLE(10, "can't transform");
|
||||||
}
|
}
|
||||||
|
|
||||||
// transform yaw
|
|
||||||
if (setpoint_yaw_type == YAW) {
|
|
||||||
try {
|
|
||||||
QuaternionStamped q;
|
|
||||||
q.header.stamp = stamp;
|
|
||||||
q.header.frame_id = yaw_frame_id;
|
|
||||||
q.quaternion = tf::createQuaternionMsgFromYaw(setpoint_yaw);
|
|
||||||
yaw_local = tf2::getYaw(tf_buffer.transform(q, local_frame, ros::Duration(0.05)).quaternion);
|
|
||||||
} catch (tf2::TransformException& ex) {
|
|
||||||
// can't transform yaw, use last known
|
|
||||||
ROS_WARN_THROTTLE(10, "can't transform: %s", ex.what());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// compute navigate setpoint
|
|
||||||
if (setpoint_type == NAVIGATE || setpoint_type == NAVIGATE_GLOBAL) {
|
if (setpoint_type == NAVIGATE || setpoint_type == NAVIGATE_GLOBAL) {
|
||||||
|
position_msg.pose.orientation = setpoint_position_transformed.pose.orientation; // copy yaw
|
||||||
getNavigateSetpoint(stamp, nav_speed, position_msg.pose.position);
|
getNavigateSetpoint(stamp, nav_speed, position_msg.pose.position);
|
||||||
|
|
||||||
if (setpoint_yaw_type == TOWARDS) {
|
if (setpoint_yaw_type == TOWARDS) {
|
||||||
yaw_local = atan2(position_msg.pose.position.y - nav_start.pose.position.y,
|
double yaw_towards = atan2(position_msg.pose.position.y - nav_start.pose.position.y,
|
||||||
position_msg.pose.position.x - nav_start.pose.position.x);
|
position_msg.pose.position.x - nav_start.pose.position.x);
|
||||||
|
position_msg.pose.orientation = tf::createQuaternionMsgFromRollPitchYaw(0, 0, yaw_towards);
|
||||||
}
|
}
|
||||||
|
|
||||||
position_msg.pose.orientation = tf::createQuaternionMsgFromYaw(yaw_local);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (setpoint_type == POSITION) {
|
if (setpoint_type == POSITION) {
|
||||||
position_msg = setpoint_pose_local;
|
position_msg = setpoint_position_transformed;
|
||||||
position_msg.pose.orientation = tf::createQuaternionMsgFromYaw(yaw_local);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (setpoint_type == POSITION || setpoint_type == NAVIGATE || setpoint_type == NAVIGATE_GLOBAL) {
|
if (setpoint_type == POSITION || setpoint_type == NAVIGATE || setpoint_type == NAVIGATE_GLOBAL) {
|
||||||
@@ -538,14 +439,14 @@ void publish(const ros::Time stamp)
|
|||||||
PositionTarget::IGNORE_AFY +
|
PositionTarget::IGNORE_AFY +
|
||||||
PositionTarget::IGNORE_AFZ +
|
PositionTarget::IGNORE_AFZ +
|
||||||
PositionTarget::IGNORE_YAW;
|
PositionTarget::IGNORE_YAW;
|
||||||
position_raw_msg.yaw_rate = setpoint_rates.z;
|
position_raw_msg.yaw_rate = setpoint_yaw_rate;
|
||||||
position_raw_msg.position = position_msg.pose.position;
|
position_raw_msg.position = position_msg.pose.position;
|
||||||
position_raw_pub.publish(position_raw_msg);
|
position_raw_pub.publish(position_raw_msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
// publish setpoint frame
|
// publish setpoint frame
|
||||||
if (!setpoint.child_frame_id.empty()) {
|
if (!setpoint.child_frame_id.empty()) {
|
||||||
if (setpoint.header.stamp >= position_msg.header.stamp) {
|
if (setpoint.header.stamp == position_msg.header.stamp) {
|
||||||
return; // avoid TF_REPEATED_DATA warnings
|
return; // avoid TF_REPEATED_DATA warnings
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -557,22 +458,9 @@ void publish(const ros::Time stamp)
|
|||||||
setpoint.header.stamp = position_msg.header.stamp;
|
setpoint.header.stamp = position_msg.header.stamp;
|
||||||
transform_broadcaster->sendTransform(setpoint);
|
transform_broadcaster->sendTransform(setpoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
// publish dynamic target frame
|
|
||||||
publishTarget(stamp);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (setpoint_type == VELOCITY) {
|
if (setpoint_type == VELOCITY) {
|
||||||
// transform velocity to local frame
|
|
||||||
setpoint_velocity.header.stamp = stamp;
|
|
||||||
try {
|
|
||||||
setpoint_velocity_local = tf_buffer.transform(setpoint_velocity, local_frame, ros::Duration(0.05));
|
|
||||||
} catch (tf2::TransformException& ex) {
|
|
||||||
// can't transform velocity, use last known
|
|
||||||
ROS_WARN_THROTTLE(10, "can't transform: %s", ex.what());
|
|
||||||
}
|
|
||||||
|
|
||||||
// publish velocity
|
|
||||||
position_raw_msg.type_mask = PositionTarget::IGNORE_PX +
|
position_raw_msg.type_mask = PositionTarget::IGNORE_PX +
|
||||||
PositionTarget::IGNORE_PY +
|
PositionTarget::IGNORE_PY +
|
||||||
PositionTarget::IGNORE_PZ +
|
PositionTarget::IGNORE_PZ +
|
||||||
@@ -580,22 +468,14 @@ void publish(const ros::Time stamp)
|
|||||||
PositionTarget::IGNORE_AFY +
|
PositionTarget::IGNORE_AFY +
|
||||||
PositionTarget::IGNORE_AFZ;
|
PositionTarget::IGNORE_AFZ;
|
||||||
position_raw_msg.type_mask += setpoint_yaw_type == YAW ? PositionTarget::IGNORE_YAW_RATE : PositionTarget::IGNORE_YAW;
|
position_raw_msg.type_mask += setpoint_yaw_type == YAW ? PositionTarget::IGNORE_YAW_RATE : PositionTarget::IGNORE_YAW;
|
||||||
position_raw_msg.velocity = setpoint_velocity_local.vector;
|
position_raw_msg.velocity = setpoint_velocity_transformed.vector;
|
||||||
position_raw_msg.yaw = yaw_local;
|
position_raw_msg.yaw = tf2::getYaw(setpoint_position_transformed.pose.orientation);
|
||||||
position_raw_msg.yaw_rate = setpoint_rates.z;
|
position_raw_msg.yaw_rate = setpoint_yaw_rate;
|
||||||
position_raw_pub.publish(position_raw_msg);
|
position_raw_pub.publish(position_raw_msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (setpoint_type == ATTITUDE) {
|
if (setpoint_type == ATTITUDE) {
|
||||||
PoseStamped msg;
|
attitude_pub.publish(setpoint_position_transformed);
|
||||||
msg.header.stamp = stamp;
|
|
||||||
msg.header.frame_id = local_frame;
|
|
||||||
msg.pose.orientation = tf::createQuaternionMsgFromRollPitchYaw(setpoint_roll, setpoint_pitch, yaw_local);
|
|
||||||
attitude_pub.publish(msg);
|
|
||||||
|
|
||||||
Thrust thrust_msg;
|
|
||||||
thrust_msg.header.stamp = stamp;
|
|
||||||
thrust_msg.thrust = setpoint_thrust;
|
|
||||||
thrust_pub.publish(thrust_msg);
|
thrust_pub.publish(thrust_msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -604,12 +484,11 @@ void publish(const ros::Time stamp)
|
|||||||
// thrust_pub.publish(thrust_msg);
|
// thrust_pub.publish(thrust_msg);
|
||||||
// mavros rates topics waits for rates in local frame
|
// mavros rates topics waits for rates in local frame
|
||||||
// use rates in body frame for simplicity
|
// use rates in body frame for simplicity
|
||||||
AttitudeTarget att_raw_msg;
|
|
||||||
att_raw_msg.header.stamp = stamp;
|
att_raw_msg.header.stamp = stamp;
|
||||||
att_raw_msg.header.frame_id = fcu_frame;
|
att_raw_msg.header.frame_id = fcu_frame;
|
||||||
att_raw_msg.type_mask = AttitudeTarget::IGNORE_ATTITUDE;
|
att_raw_msg.type_mask = AttitudeTarget::IGNORE_ATTITUDE;
|
||||||
att_raw_msg.body_rate = setpoint_rates;
|
att_raw_msg.body_rate = rates_msg.twist.angular;
|
||||||
att_raw_msg.thrust = setpoint_thrust;
|
att_raw_msg.thrust = thrust_msg.thrust;
|
||||||
attitude_raw_pub.publish(att_raw_msg);
|
attitude_raw_pub.publish(att_raw_msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -649,59 +528,10 @@ inline void checkState()
|
|||||||
throw std::runtime_error("No connection to FCU, https://clover.coex.tech/connection");
|
throw std::runtime_error("No connection to FCU, https://clover.coex.tech/connection");
|
||||||
}
|
}
|
||||||
|
|
||||||
void publishState()
|
|
||||||
{
|
|
||||||
clover::State msg;
|
|
||||||
msg.mode = setpoint_type;
|
|
||||||
msg.yaw_mode = setpoint_yaw_type;
|
|
||||||
|
|
||||||
if (setpoint_position.header.frame_id.empty()) {
|
|
||||||
msg.x = NAN;
|
|
||||||
msg.y = NAN;
|
|
||||||
msg.z = NAN;
|
|
||||||
} else {
|
|
||||||
msg.x = setpoint_position.point.x;
|
|
||||||
msg.y = setpoint_position.point.y;
|
|
||||||
msg.z = setpoint_altitude.point.z;
|
|
||||||
}
|
|
||||||
|
|
||||||
msg.speed = nav_speed;
|
|
||||||
msg.lat = setpoint_lat;
|
|
||||||
msg.lon = setpoint_lon;
|
|
||||||
msg.vx = setpoint_velocity.vector.x;
|
|
||||||
msg.vy = setpoint_velocity.vector.y;
|
|
||||||
msg.vz = setpoint_velocity.vector.z;
|
|
||||||
msg.roll = setpoint_roll;
|
|
||||||
msg.pitch = setpoint_pitch;
|
|
||||||
msg.yaw = !yaw_frame_id.empty() ? setpoint_yaw : NAN;
|
|
||||||
|
|
||||||
msg.roll_rate = setpoint_rates.x;
|
|
||||||
msg.pitch_rate = setpoint_rates.y;
|
|
||||||
msg.yaw_rate = setpoint_rates.z;
|
|
||||||
msg.thrust = setpoint_thrust;
|
|
||||||
|
|
||||||
if (setpoint_type == VELOCITY) {
|
|
||||||
msg.xy_frame_id = setpoint_velocity.header.frame_id;
|
|
||||||
msg.z_frame_id = setpoint_velocity.header.frame_id;
|
|
||||||
} else {
|
|
||||||
msg.xy_frame_id = setpoint_position.header.frame_id;
|
|
||||||
msg.z_frame_id = setpoint_altitude.header.frame_id;
|
|
||||||
}
|
|
||||||
msg.yaw_frame_id = yaw_frame_id;
|
|
||||||
|
|
||||||
state_pub.publish(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline float safe(float value) {
|
|
||||||
return std::isfinite(value) ? value : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define ENSURE_FINITE(var) { if (!std::isfinite(var)) throw std::runtime_error(#var " argument cannot be NaN or Inf"); }
|
#define ENSURE_FINITE(var) { if (!std::isfinite(var)) throw std::runtime_error(#var " argument cannot be NaN or Inf"); }
|
||||||
|
|
||||||
#define ENSURE_NON_INF(var) { if (std::isinf(var)) throw std::runtime_error(#var " argument cannot be Inf"); }
|
|
||||||
|
|
||||||
bool serve(enum setpoint_type_t sp_type, float x, float y, float z, float vx, float vy, float vz,
|
bool serve(enum setpoint_type_t sp_type, float x, float y, float z, float vx, float vy, float vz,
|
||||||
float roll, float pitch, float yaw, float roll_rate, float pitch_rate, float yaw_rate, // editorconfig-checker-disable-line
|
float pitch, float roll, float yaw, float pitch_rate, float roll_rate, float yaw_rate, // editorconfig-checker-disable-line
|
||||||
float lat, float lon, float thrust, float speed, string frame_id, bool auto_arm, // editorconfig-checker-disable-line
|
float lat, float lon, float thrust, float speed, string frame_id, bool auto_arm, // editorconfig-checker-disable-line
|
||||||
uint8_t& success, string& message) // editorconfig-checker-disable-line
|
uint8_t& success, string& message) // editorconfig-checker-disable-line
|
||||||
{
|
{
|
||||||
@@ -728,40 +558,69 @@ bool serve(enum setpoint_type_t sp_type, float x, float y, float z, float vx, fl
|
|||||||
auto search = reference_frames.find(frame_id);
|
auto search = reference_frames.find(frame_id);
|
||||||
const string& reference_frame = search == reference_frames.end() ? frame_id : search->second;
|
const string& reference_frame = search == reference_frames.end() ? frame_id : search->second;
|
||||||
|
|
||||||
ENSURE_NON_INF(x);
|
// Serve "partial" commands
|
||||||
ENSURE_NON_INF(y);
|
|
||||||
ENSURE_NON_INF(z);
|
|
||||||
ENSURE_NON_INF(speed); // TODO: allow inf
|
|
||||||
ENSURE_NON_INF(vx);
|
|
||||||
ENSURE_NON_INF(vy);
|
|
||||||
ENSURE_NON_INF(vz);
|
|
||||||
ENSURE_NON_INF(roll);
|
|
||||||
ENSURE_NON_INF(pitch);
|
|
||||||
ENSURE_NON_INF(roll_rate);
|
|
||||||
ENSURE_NON_INF(pitch_rate);
|
|
||||||
ENSURE_NON_INF(yaw_rate);
|
|
||||||
ENSURE_NON_INF(thrust);
|
|
||||||
|
|
||||||
if (sp_type == NAVIGATE_GLOBAL) {
|
if (!auto_arm && std::isfinite(yaw) &&
|
||||||
|
isnan(x) && isnan(y) && isnan(z) && isnan(vx) && isnan(vy) && isnan(vz) &&
|
||||||
|
isnan(pitch) && isnan(roll) && isnan(thrust) &&
|
||||||
|
isnan(lat) && isnan(lon)) {
|
||||||
|
// change only the yaw
|
||||||
|
if (setpoint_type == POSITION || setpoint_type == NAVIGATE || setpoint_type == NAVIGATE_GLOBAL || setpoint_type == VELOCITY) {
|
||||||
|
if (!waitTransform(setpoint_position.header.frame_id, frame_id, stamp, transform_timeout))
|
||||||
|
throw std::runtime_error("Can't transform from " + frame_id + " to " + setpoint_position.header.frame_id);
|
||||||
|
|
||||||
|
message = "Changing yaw only";
|
||||||
|
|
||||||
|
QuaternionStamped q;
|
||||||
|
q.header.frame_id = frame_id;
|
||||||
|
q.header.stamp = stamp;
|
||||||
|
q.quaternion = tf::createQuaternionMsgFromYaw(yaw); // TODO: pitch=0, roll=0 is not totally correct
|
||||||
|
setpoint_position.pose.orientation = tf_buffer.transform(q, setpoint_position.header.frame_id).quaternion;
|
||||||
|
setpoint_yaw_type = YAW;
|
||||||
|
goto publish_setpoint;
|
||||||
|
} else {
|
||||||
|
throw std::runtime_error("Setting yaw is possible only when position or velocity setpoints active");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!auto_arm && std::isfinite(yaw_rate) &&
|
||||||
|
isnan(x) && isnan(y) && isnan(z) && isnan(vx) && isnan(vy) && isnan(vz) &&
|
||||||
|
isnan(pitch) && isnan(roll) && isnan(yaw) && isnan(thrust) &&
|
||||||
|
isnan(lat) && isnan(lon)) {
|
||||||
|
// change only the yaw rate
|
||||||
|
if (setpoint_type == POSITION || setpoint_type == NAVIGATE || setpoint_type == NAVIGATE_GLOBAL || setpoint_type == VELOCITY) {
|
||||||
|
message = "Changing yaw rate only";
|
||||||
|
|
||||||
|
setpoint_yaw_type = YAW_RATE;
|
||||||
|
setpoint_yaw_rate = yaw_rate;
|
||||||
|
goto publish_setpoint;
|
||||||
|
} else {
|
||||||
|
throw std::runtime_error("Setting yaw rate is possible only when position or velocity setpoints active");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serve normal commands
|
||||||
|
|
||||||
|
if (sp_type == NAVIGATE || sp_type == POSITION) {
|
||||||
|
ENSURE_FINITE(x);
|
||||||
|
ENSURE_FINITE(y);
|
||||||
|
ENSURE_FINITE(z);
|
||||||
|
} else if (sp_type == NAVIGATE_GLOBAL) {
|
||||||
ENSURE_FINITE(lat);
|
ENSURE_FINITE(lat);
|
||||||
ENSURE_FINITE(lon);
|
ENSURE_FINITE(lon);
|
||||||
}
|
ENSURE_FINITE(z);
|
||||||
|
} else if (sp_type == VELOCITY) {
|
||||||
if (isfinite(x) != isfinite(y)) {
|
ENSURE_FINITE(vx);
|
||||||
throw std::runtime_error("x and y can be set only together");
|
ENSURE_FINITE(vy);
|
||||||
}
|
ENSURE_FINITE(vz);
|
||||||
|
} else if (sp_type == ATTITUDE) {
|
||||||
if (isfinite(yaw_rate)) {
|
ENSURE_FINITE(pitch);
|
||||||
if (sp_type > RATES && setpoint_type == ATTITUDE) {
|
ENSURE_FINITE(roll);
|
||||||
throw std::runtime_error("Yaw rate cannot be set in attitude mode.");
|
ENSURE_FINITE(thrust);
|
||||||
}
|
} else if (sp_type == RATES) {
|
||||||
}
|
ENSURE_FINITE(pitch_rate);
|
||||||
|
ENSURE_FINITE(roll_rate);
|
||||||
// set_altitude
|
ENSURE_FINITE(thrust);
|
||||||
if (sp_type == _ALTITUDE) {
|
|
||||||
if (setpoint_type == VELOCITY || setpoint_type == ATTITUDE || setpoint_type == RATES) {
|
|
||||||
throw std::runtime_error("Altitude cannot be set in velocity, attitude or rates mode.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sp_type == NAVIGATE || sp_type == NAVIGATE_GLOBAL) {
|
if (sp_type == NAVIGATE || sp_type == NAVIGATE_GLOBAL) {
|
||||||
@@ -775,13 +634,20 @@ bool serve(enum setpoint_type_t sp_type, float x, float y, float z, float vx, fl
|
|||||||
speed = default_speed;
|
speed = default_speed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (sp_type == NAVIGATE || sp_type == NAVIGATE_GLOBAL || sp_type == POSITION || sp_type == VELOCITY) {
|
||||||
|
if (yaw_rate != 0 && !std::isnan(yaw))
|
||||||
|
throw std::runtime_error("Yaw value should be NaN for setting yaw rate");
|
||||||
|
|
||||||
|
if (std::isnan(yaw_rate) && std::isnan(yaw))
|
||||||
|
throw std::runtime_error("Both yaw and yaw_rate cannot be NaN");
|
||||||
|
}
|
||||||
|
|
||||||
if (sp_type == NAVIGATE_GLOBAL) {
|
if (sp_type == NAVIGATE_GLOBAL) {
|
||||||
if (TIMEOUT(global_position, global_position_timeout))
|
if (TIMEOUT(global_position, global_position_timeout))
|
||||||
throw std::runtime_error("No global position");
|
throw std::runtime_error("No global position");
|
||||||
}
|
}
|
||||||
|
|
||||||
// if any value need to be transformed to reference frame
|
if (sp_type == NAVIGATE || sp_type == NAVIGATE_GLOBAL || sp_type == POSITION || sp_type == VELOCITY || sp_type == ATTITUDE) {
|
||||||
if (isfinite(x) || isfinite(y) || isfinite(z) || isfinite(vx) || isfinite(vy) || isfinite(vz) || isfinite(yaw)) {
|
|
||||||
// make sure transform from frame_id to reference frame available
|
// make sure transform from frame_id to reference frame available
|
||||||
if (!waitTransform(reference_frame, frame_id, stamp, transform_timeout))
|
if (!waitTransform(reference_frame, frame_id, stamp, transform_timeout))
|
||||||
throw std::runtime_error("Can't transform from " + frame_id + " to " + reference_frame);
|
throw std::runtime_error("Can't transform from " + frame_id + " to " + reference_frame);
|
||||||
@@ -798,27 +664,15 @@ bool serve(enum setpoint_type_t sp_type, float x, float y, float z, float vx, fl
|
|||||||
auto xy_in_req_frame = tf_buffer.transform(pose_local, frame_id);
|
auto xy_in_req_frame = tf_buffer.transform(pose_local, frame_id);
|
||||||
x = xy_in_req_frame.pose.position.x;
|
x = xy_in_req_frame.pose.position.x;
|
||||||
y = xy_in_req_frame.pose.position.y;
|
y = xy_in_req_frame.pose.position.y;
|
||||||
setpoint_lat = lat;
|
|
||||||
setpoint_lon = lon;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Everything fine - switch setpoint type
|
// Everything fine - switch setpoint type
|
||||||
if (sp_type <= RATES) {
|
setpoint_type = sp_type;
|
||||||
setpoint_type = sp_type;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (setpoint_type != NAVIGATE && setpoint_type != NAVIGATE_GLOBAL) {
|
if (sp_type != NAVIGATE && sp_type != NAVIGATE_GLOBAL) {
|
||||||
nav_from_sp_flag = false;
|
nav_from_sp_flag = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool to_auto_arm = auto_arm && (state.mode != "OFFBOARD" || !state.armed);
|
|
||||||
if (to_auto_arm || setpoint_type == VELOCITY || setpoint_type == ATTITUDE || setpoint_type == RATES) {
|
|
||||||
// invalidate position setpoint
|
|
||||||
setpoint_position.header.frame_id = "";
|
|
||||||
setpoint_altitude.header.frame_id = "";
|
|
||||||
yaw_frame_id = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sp_type == NAVIGATE || sp_type == NAVIGATE_GLOBAL) {
|
if (sp_type == NAVIGATE || sp_type == NAVIGATE_GLOBAL) {
|
||||||
// starting point
|
// starting point
|
||||||
if (nav_from_sp && nav_from_sp_flag) {
|
if (nav_from_sp && nav_from_sp_flag) {
|
||||||
@@ -827,139 +681,89 @@ bool serve(enum setpoint_type_t sp_type, float x, float y, float z, float vx, fl
|
|||||||
} else {
|
} else {
|
||||||
nav_start = local_position;
|
nav_start = local_position;
|
||||||
}
|
}
|
||||||
|
nav_speed = speed;
|
||||||
if (!isnan(speed)) {
|
|
||||||
nav_speed = speed;
|
|
||||||
}
|
|
||||||
|
|
||||||
nav_from_sp_flag = true;
|
nav_from_sp_flag = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// handle position
|
// if (sp_type == NAVIGATE || sp_type == NAVIGATE_GLOBAL || sp_type == POSITION || sp_type == VELOCITY) {
|
||||||
if (setpoint_type == NAVIGATE || setpoint_type == NAVIGATE_GLOBAL || setpoint_type == POSITION) {
|
// if (std::isnan(yaw) && yaw_rate == 0) {
|
||||||
|
// // keep yaw unchanged
|
||||||
|
// // TODO: this is incorrect, because we need yaw in desired frame
|
||||||
|
// yaw = tf2::getYaw(local_position.pose.orientation);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
PointStamped desired;
|
if (sp_type == POSITION || sp_type == NAVIGATE || sp_type == NAVIGATE_GLOBAL || sp_type == VELOCITY || sp_type == ATTITUDE) {
|
||||||
desired.header.frame_id = frame_id;
|
// destination point and/or attitude
|
||||||
desired.header.stamp = stamp;
|
PoseStamped ps;
|
||||||
desired.point.x = safe(x);
|
ps.header.frame_id = frame_id;
|
||||||
desired.point.y = safe(y);
|
ps.header.stamp = stamp;
|
||||||
desired.point.z = safe(z);
|
ps.pose.position.x = x;
|
||||||
|
ps.pose.position.y = y;
|
||||||
|
ps.pose.position.z = z;
|
||||||
|
ps.pose.orientation.w = 1.0; // Ensure quaternion is always valid
|
||||||
|
|
||||||
// transform to reference frame
|
if (sp_type == ATTITUDE) {
|
||||||
desired = tf_buffer.transform(desired, reference_frame);
|
ps.pose.position.x = 0;
|
||||||
|
ps.pose.position.y = 0;
|
||||||
// set horizontal position
|
ps.pose.position.z = 0;
|
||||||
if (isfinite(x) && isfinite(y)) {
|
ps.pose.orientation = tf::createQuaternionMsgFromRollPitchYaw(roll, pitch, yaw);
|
||||||
setpoint_position = desired;
|
} else if (std::isnan(yaw)) {
|
||||||
} else if (setpoint_position.header.frame_id.empty()) {
|
setpoint_yaw_type = YAW_RATE;
|
||||||
// TODO: use transform for current stamp
|
setpoint_yaw_rate = yaw_rate;
|
||||||
setpoint_position.header = local_position.header;
|
} else if (std::isinf(yaw) && yaw > 0) {
|
||||||
setpoint_position.point = local_position.pose.position;
|
|
||||||
}
|
|
||||||
|
|
||||||
// set altitude
|
|
||||||
if (isfinite(z)) {
|
|
||||||
setpoint_altitude = desired;
|
|
||||||
} else if (setpoint_altitude.header.frame_id.empty()) {
|
|
||||||
setpoint_altitude.header = local_position.header;
|
|
||||||
setpoint_altitude.point = local_position.pose.position;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// handle velocity
|
|
||||||
if (sp_type == VELOCITY) {
|
|
||||||
// TODO: allow setting different modes by altitude and xy
|
|
||||||
Vector3Stamped desired;
|
|
||||||
desired.header.frame_id = frame_id;
|
|
||||||
desired.header.stamp = stamp;
|
|
||||||
desired.vector.x = safe(vx);
|
|
||||||
desired.vector.y = safe(vy);
|
|
||||||
desired.vector.z = safe(vz);
|
|
||||||
|
|
||||||
// transform to reference frame
|
|
||||||
desired = tf_buffer.transform(desired, reference_frame);
|
|
||||||
setpoint_velocity.header = desired.header;
|
|
||||||
|
|
||||||
// set horizontal velocity
|
|
||||||
if (isfinite(vx) && isfinite(vy)) {
|
|
||||||
setpoint_velocity.vector.x = desired.vector.x;
|
|
||||||
setpoint_velocity.vector.y = desired.vector.y;
|
|
||||||
}
|
|
||||||
|
|
||||||
// set vertical velocity
|
|
||||||
if (isfinite(vz)) {
|
|
||||||
setpoint_velocity.vector.z = desired.vector.z;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// handle yaw
|
|
||||||
if (sp_type == NAVIGATE || sp_type == NAVIGATE_GLOBAL || sp_type == POSITION || sp_type == VELOCITY || sp_type == ATTITUDE || sp_type == _YAW) {
|
|
||||||
if (isfinite(yaw)) {
|
|
||||||
setpoint_yaw_type = YAW;
|
|
||||||
QuaternionStamped desired;
|
|
||||||
desired.header.frame_id = frame_id;
|
|
||||||
desired.header.stamp = stamp;
|
|
||||||
desired.quaternion = tf::createQuaternionMsgFromYaw(yaw);
|
|
||||||
|
|
||||||
// transform to reference frame
|
|
||||||
desired = tf_buffer.transform(desired, reference_frame);
|
|
||||||
setpoint_yaw = tf2::getYaw(desired.quaternion);
|
|
||||||
yaw_frame_id = reference_frame;
|
|
||||||
|
|
||||||
} else if (isinf(yaw) && yaw > 0) {
|
|
||||||
// yaw towards
|
// yaw towards
|
||||||
setpoint_yaw_type = TOWARDS;
|
setpoint_yaw_type = TOWARDS;
|
||||||
|
yaw = 0;
|
||||||
} else if (yaw_frame_id.empty() || sp_type == _YAW) {
|
setpoint_yaw_rate = 0;
|
||||||
// yaw is nan and not set previously OR set_yaw(yaw=nan) was called
|
} else {
|
||||||
setpoint_yaw_type = YAW;
|
setpoint_yaw_type = YAW;
|
||||||
setpoint_yaw = tf2::getYaw(local_position.pose.orientation); // set yaw to current yaw
|
setpoint_yaw_rate = 0;
|
||||||
yaw_frame_id = local_position.header.frame_id;
|
ps.pose.orientation = tf::createQuaternionMsgFromYaw(yaw);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tf_buffer.transform(ps, setpoint_position, reference_frame);
|
||||||
}
|
}
|
||||||
|
|
||||||
// handle roll
|
if (sp_type == VELOCITY) {
|
||||||
if (isfinite(roll)) {
|
Vector3Stamped vel;
|
||||||
setpoint_roll = roll;
|
vel.header.frame_id = frame_id;
|
||||||
|
vel.header.stamp = stamp;
|
||||||
|
vel.vector.x = vx;
|
||||||
|
vel.vector.y = vy;
|
||||||
|
vel.vector.z = vz;
|
||||||
|
tf_buffer.transform(vel, setpoint_velocity, reference_frame);
|
||||||
}
|
}
|
||||||
|
|
||||||
// handle pitch
|
if (sp_type == ATTITUDE || sp_type == RATES) {
|
||||||
if (isfinite(pitch)) {
|
thrust_msg.thrust = thrust;
|
||||||
setpoint_pitch = pitch;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// handle yaw rate
|
if (sp_type == RATES) {
|
||||||
if (isfinite(yaw_rate)) {
|
rates_msg.twist.angular.x = roll_rate;
|
||||||
setpoint_yaw_type = YAW_RATE;
|
rates_msg.twist.angular.y = pitch_rate;
|
||||||
setpoint_rates.z = yaw_rate;
|
rates_msg.twist.angular.z = yaw_rate;
|
||||||
}
|
|
||||||
|
|
||||||
// handle pitch rate
|
|
||||||
if (isfinite(roll_rate)) {
|
|
||||||
setpoint_rates.x = roll_rate;
|
|
||||||
}
|
|
||||||
|
|
||||||
// handle roll rate
|
|
||||||
if (isfinite(pitch_rate)) {
|
|
||||||
setpoint_rates.y = pitch_rate;
|
|
||||||
}
|
|
||||||
|
|
||||||
// handle thrust
|
|
||||||
if (isfinite(thrust)) {
|
|
||||||
setpoint_thrust = thrust;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
wait_armed = auto_arm;
|
wait_armed = auto_arm;
|
||||||
|
|
||||||
|
publish_setpoint:
|
||||||
publish(stamp); // calculate initial transformed messages first
|
publish(stamp); // calculate initial transformed messages first
|
||||||
setpoint_timer.start();
|
setpoint_timer.start();
|
||||||
|
|
||||||
if (setpoint_type == NAVIGATE || setpoint_type == NAVIGATE_GLOBAL || setpoint_type == POSITION) {
|
// publish target frame
|
||||||
publishTarget(stamp, true);
|
if (!target.child_frame_id.empty()) {
|
||||||
|
if (setpoint_type == NAVIGATE || setpoint_type == NAVIGATE_GLOBAL || setpoint_type == POSITION) {
|
||||||
|
target.header.frame_id = setpoint_position.header.frame_id;
|
||||||
|
target.header.stamp = stamp;
|
||||||
|
target.transform.translation.x = setpoint_position.pose.position.x;
|
||||||
|
target.transform.translation.y = setpoint_position.pose.position.y;
|
||||||
|
target.transform.translation.z = setpoint_position.pose.position.z;
|
||||||
|
target.transform.rotation = setpoint_position.pose.orientation;
|
||||||
|
static_transform_broadcaster->sendTransform(target);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
publishState();
|
|
||||||
|
|
||||||
if (auto_arm) {
|
if (auto_arm) {
|
||||||
offboardAndArm();
|
offboardAndArm();
|
||||||
wait_armed = false;
|
wait_armed = false;
|
||||||
@@ -984,39 +788,27 @@ bool serve(enum setpoint_type_t sp_type, float x, float y, float z, float vx, fl
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool navigate(Navigate::Request& req, Navigate::Response& res) {
|
bool navigate(Navigate::Request& req, Navigate::Response& res) {
|
||||||
return serve(NAVIGATE, req.x, req.y, req.z, NAN, NAN, NAN, NAN, NAN, req.yaw, NAN, NAN, NAN, NAN, NAN, NAN, req.speed, req.frame_id, req.auto_arm, res.success, res.message);
|
return serve(NAVIGATE, req.x, req.y, req.z, NAN, NAN, NAN, NAN, NAN, req.yaw, NAN, NAN, req.yaw_rate, NAN, NAN, NAN, req.speed, req.frame_id, req.auto_arm, res.success, res.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool navigateGlobal(NavigateGlobal::Request& req, NavigateGlobal::Response& res) {
|
bool navigateGlobal(NavigateGlobal::Request& req, NavigateGlobal::Response& res) {
|
||||||
return serve(NAVIGATE_GLOBAL, NAN, NAN, req.z, NAN, NAN, NAN, NAN, NAN, req.yaw, NAN, NAN, NAN, req.lat, req.lon, NAN, req.speed, req.frame_id, req.auto_arm, res.success, res.message);
|
return serve(NAVIGATE_GLOBAL, NAN, NAN, req.z, NAN, NAN, NAN, NAN, NAN, req.yaw, NAN, NAN, req.yaw_rate, req.lat, req.lon, NAN, req.speed, req.frame_id, req.auto_arm, res.success, res.message);
|
||||||
}
|
|
||||||
|
|
||||||
bool setAltitude(SetAltitude::Request& req, SetAltitude::Response& res) {
|
|
||||||
return serve(_ALTITUDE, NAN, NAN, req.z, NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN, req.frame_id, false, res.success, res.message);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool setYaw(SetYaw::Request& req, SetYaw::Response& res) {
|
|
||||||
return serve(_YAW, NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN, req.yaw, NAN, NAN, NAN, NAN, NAN, NAN, NAN, req.frame_id, false, res.success, res.message);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool setYawRate(SetYawRate::Request& req, SetYawRate::Response& res) {
|
|
||||||
return serve(_YAW_RATE, NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN, req.yaw_rate, NAN, NAN, NAN, NAN, "", false, res.success, res.message);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool setPosition(SetPosition::Request& req, SetPosition::Response& res) {
|
bool setPosition(SetPosition::Request& req, SetPosition::Response& res) {
|
||||||
return serve(POSITION, req.x, req.y, req.z, NAN, NAN, NAN, NAN, NAN, req.yaw, NAN, NAN, NAN, NAN, NAN, NAN, NAN, req.frame_id, req.auto_arm, res.success, res.message);
|
return serve(POSITION, req.x, req.y, req.z, NAN, NAN, NAN, NAN, NAN, req.yaw, NAN, NAN, req.yaw_rate, NAN, NAN, NAN, NAN, req.frame_id, req.auto_arm, res.success, res.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool setVelocity(SetVelocity::Request& req, SetVelocity::Response& res) {
|
bool setVelocity(SetVelocity::Request& req, SetVelocity::Response& res) {
|
||||||
return serve(VELOCITY, NAN, NAN, NAN, req.vx, req.vy, req.vz, NAN, NAN, req.yaw, NAN, NAN, NAN, NAN, NAN, NAN, NAN, req.frame_id, req.auto_arm, res.success, res.message);
|
return serve(VELOCITY, NAN, NAN, NAN, req.vx, req.vy, req.vz, NAN, NAN, req.yaw, NAN, NAN, req.yaw_rate, NAN, NAN, NAN, NAN, req.frame_id, req.auto_arm, res.success, res.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool setAttitude(SetAttitude::Request& req, SetAttitude::Response& res) {
|
bool setAttitude(SetAttitude::Request& req, SetAttitude::Response& res) {
|
||||||
return serve(ATTITUDE, NAN, NAN, NAN, NAN, NAN, NAN, req.roll, req.pitch, req.yaw, NAN, NAN, NAN, NAN, NAN, req.thrust, NAN, req.frame_id, req.auto_arm, res.success, res.message);
|
return serve(ATTITUDE, NAN, NAN, NAN, NAN, NAN, NAN, req.pitch, req.roll, req.yaw, NAN, NAN, NAN, NAN, NAN, req.thrust, NAN, req.frame_id, req.auto_arm, res.success, res.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool setRates(SetRates::Request& req, SetRates::Response& res) {
|
bool setRates(SetRates::Request& req, SetRates::Response& res) {
|
||||||
return serve(RATES, NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN, req.roll_rate, req.pitch_rate, req.yaw_rate, NAN, NAN, req.thrust, NAN, "", req.auto_arm, res.success, res.message);
|
return serve(RATES, NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN, req.pitch_rate, req.roll_rate, req.yaw_rate, NAN, NAN, req.thrust, NAN, "", req.auto_arm, res.success, res.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool land(std_srvs::Trigger::Request& req, std_srvs::Trigger::Response& res)
|
bool land(std_srvs::Trigger::Request& req, std_srvs::Trigger::Response& res)
|
||||||
@@ -1048,7 +840,9 @@ bool land(std_srvs::Trigger::Request& req, std_srvs::Trigger::Response& res)
|
|||||||
auto start = ros::Time::now();
|
auto start = ros::Time::now();
|
||||||
while (ros::ok()) {
|
while (ros::ok()) {
|
||||||
if (state.mode == "AUTO.LAND") {
|
if (state.mode == "AUTO.LAND") {
|
||||||
break;
|
res.success = true;
|
||||||
|
busy = false;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
if (ros::Time::now() - start > land_timeout)
|
if (ros::Time::now() - start > land_timeout)
|
||||||
throw std::runtime_error("Land request timed out");
|
throw std::runtime_error("Land request timed out");
|
||||||
@@ -1057,18 +851,6 @@ bool land(std_srvs::Trigger::Request& req, std_srvs::Trigger::Response& res)
|
|||||||
r.sleep();
|
r.sleep();
|
||||||
}
|
}
|
||||||
|
|
||||||
// stop setpoints and invalidate position setpoint
|
|
||||||
setpoint_timer.stop();
|
|
||||||
setpoint_type = NONE;
|
|
||||||
setpoint_position.header.frame_id = "";
|
|
||||||
setpoint_altitude.header.frame_id = "";
|
|
||||||
yaw_frame_id = "";
|
|
||||||
publishState();
|
|
||||||
|
|
||||||
res.success = true;
|
|
||||||
busy = false;
|
|
||||||
return true;
|
|
||||||
|
|
||||||
} catch (const std::exception& e) {
|
} catch (const std::exception& e) {
|
||||||
res.message = e.what();
|
res.message = e.what();
|
||||||
ROS_INFO("%s", e.what());
|
ROS_INFO("%s", e.what());
|
||||||
@@ -1078,18 +860,6 @@ bool land(std_srvs::Trigger::Request& req, std_srvs::Trigger::Response& res)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool release(std_srvs::Trigger::Request& req, std_srvs::Trigger::Response& res)
|
|
||||||
{
|
|
||||||
setpoint_timer.stop();
|
|
||||||
setpoint_type = NONE;
|
|
||||||
setpoint_position.header.frame_id = "";
|
|
||||||
setpoint_altitude.header.frame_id = "";
|
|
||||||
yaw_frame_id = "";
|
|
||||||
publishState();
|
|
||||||
res.success = true;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
ros::init(argc, argv, "simple_offboard");
|
ros::init(argc, argv, "simple_offboard");
|
||||||
@@ -1111,8 +881,6 @@ int main(int argc, char **argv)
|
|||||||
nh_priv.param("check_kill_switch", check_kill_switch, true);
|
nh_priv.param("check_kill_switch", check_kill_switch, true);
|
||||||
nh_priv.param("default_speed", default_speed, 0.5f);
|
nh_priv.param("default_speed", default_speed, 0.5f);
|
||||||
nh_priv.param<string>("body_frame", body.child_frame_id, "body");
|
nh_priv.param<string>("body_frame", body.child_frame_id, "body");
|
||||||
nh_priv.param<string>("terrain_frame", terrain.child_frame_id, "terrain");
|
|
||||||
nh_priv.param<string>("terrain_frame_mode", terrain_frame_mode, "altitude");
|
|
||||||
nh_priv.getParam("reference_frames", reference_frames);
|
nh_priv.getParam("reference_frames", reference_frames);
|
||||||
|
|
||||||
// Default reference frames
|
// Default reference frames
|
||||||
@@ -1148,20 +916,6 @@ int main(int argc, char **argv)
|
|||||||
auto manual_control_sub = nh.subscribe(mavros + "/manual_control/control", 1, &handleMessage<mavros_msgs::ManualControl, manual_control>);
|
auto manual_control_sub = nh.subscribe(mavros + "/manual_control/control", 1, &handleMessage<mavros_msgs::ManualControl, manual_control>);
|
||||||
auto local_position_sub = nh.subscribe(mavros + "/local_position/pose", 1, &handleLocalPosition);
|
auto local_position_sub = nh.subscribe(mavros + "/local_position/pose", 1, &handleLocalPosition);
|
||||||
|
|
||||||
ros::Subscriber altitude_sub;
|
|
||||||
if (!body.child_frame_id.empty() && !terrain.child_frame_id.empty()) {
|
|
||||||
terrain.header.frame_id = local_frame;
|
|
||||||
if (terrain_frame_mode == "altitude") {
|
|
||||||
altitude_sub = nh.subscribe(mavros + "/altitude", 1, &handleAltitude);
|
|
||||||
} else if (terrain_frame_mode == "range") {
|
|
||||||
string range_topic = nh_priv.param("range_topic", string("rangefinder/range"));
|
|
||||||
altitude_sub = nh.subscribe(range_topic, 1, &handleRange);
|
|
||||||
} else {
|
|
||||||
ROS_FATAL("Unknown terrain_frame_mode: %s, valid values: altitude, range", terrain_frame_mode.c_str());
|
|
||||||
ros::shutdown();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Setpoint publishers
|
// Setpoint publishers
|
||||||
position_pub = nh.advertise<PoseStamped>(mavros + "/setpoint_position/local", 1);
|
position_pub = nh.advertise<PoseStamped>(mavros + "/setpoint_position/local", 1);
|
||||||
position_raw_pub = nh.advertise<PositionTarget>(mavros + "/setpoint_raw/local", 1);
|
position_raw_pub = nh.advertise<PositionTarget>(mavros + "/setpoint_raw/local", 1);
|
||||||
@@ -1170,22 +924,15 @@ int main(int argc, char **argv)
|
|||||||
rates_pub = nh.advertise<TwistStamped>(mavros + "/setpoint_attitude/cmd_vel", 1);
|
rates_pub = nh.advertise<TwistStamped>(mavros + "/setpoint_attitude/cmd_vel", 1);
|
||||||
thrust_pub = nh.advertise<Thrust>(mavros + "/setpoint_attitude/thrust", 1);
|
thrust_pub = nh.advertise<Thrust>(mavros + "/setpoint_attitude/thrust", 1);
|
||||||
|
|
||||||
// State publisher
|
|
||||||
state_pub = nh_priv.advertise<clover::State>("state", 1, true);
|
|
||||||
|
|
||||||
// Service servers
|
// Service servers
|
||||||
auto gt_serv = nh.advertiseService("get_telemetry", &getTelemetry);
|
auto gt_serv = nh.advertiseService("get_telemetry", &getTelemetry);
|
||||||
auto na_serv = nh.advertiseService("navigate", &navigate);
|
auto na_serv = nh.advertiseService("navigate", &navigate);
|
||||||
auto ng_serv = nh.advertiseService("navigate_global", &navigateGlobal);
|
auto ng_serv = nh.advertiseService("navigate_global", &navigateGlobal);
|
||||||
auto sl_serv = nh.advertiseService("set_altitude", &setAltitude);
|
|
||||||
auto ya_serv = nh.advertiseService("set_yaw", &setYaw);
|
|
||||||
auto yr_serv = nh.advertiseService("set_yaw_rate", &setYawRate);
|
|
||||||
auto sp_serv = nh.advertiseService("set_position", &setPosition);
|
auto sp_serv = nh.advertiseService("set_position", &setPosition);
|
||||||
auto sv_serv = nh.advertiseService("set_velocity", &setVelocity);
|
auto sv_serv = nh.advertiseService("set_velocity", &setVelocity);
|
||||||
auto sa_serv = nh.advertiseService("set_attitude", &setAttitude);
|
auto sa_serv = nh.advertiseService("set_attitude", &setAttitude);
|
||||||
auto sr_serv = nh.advertiseService("set_rates", &setRates);
|
auto sr_serv = nh.advertiseService("set_rates", &setRates);
|
||||||
auto ld_serv = nh.advertiseService("land", &land);
|
auto ld_serv = nh.advertiseService("land", &land);
|
||||||
auto rl_serv = nh_priv.advertiseService("release", &release);
|
|
||||||
|
|
||||||
// Setpoint timer
|
// Setpoint timer
|
||||||
setpoint_timer = nh.createTimer(ros::Duration(1 / nh_priv.param("setpoint_rate", 30.0)), &publishSetpoint, false, false);
|
setpoint_timer = nh.createTimer(ros::Duration(1 / nh_priv.param("setpoint_rate", 30.0)), &publishSetpoint, false, false);
|
||||||
@@ -1193,7 +940,7 @@ int main(int argc, char **argv)
|
|||||||
position_msg.header.frame_id = local_frame;
|
position_msg.header.frame_id = local_frame;
|
||||||
position_raw_msg.header.frame_id = local_frame;
|
position_raw_msg.header.frame_id = local_frame;
|
||||||
position_raw_msg.coordinate_frame = PositionTarget::FRAME_LOCAL_NED;
|
position_raw_msg.coordinate_frame = PositionTarget::FRAME_LOCAL_NED;
|
||||||
//rates_msg.header.frame_id = fcu_frame;
|
rates_msg.header.frame_id = fcu_frame;
|
||||||
|
|
||||||
ROS_INFO("ready");
|
ROS_INFO("ready");
|
||||||
ros::spin();
|
ros::spin();
|
||||||
|
|||||||
@@ -11,14 +11,12 @@
|
|||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <ros/ros.h>
|
#include <ros/ros.h>
|
||||||
#include <tf/transform_datatypes.h>
|
|
||||||
#include <tf2/transform_datatypes.h>
|
#include <tf2/transform_datatypes.h>
|
||||||
#include <tf2_ros/buffer.h>
|
#include <tf2_ros/buffer.h>
|
||||||
#include <tf2_ros/transform_listener.h>
|
#include <tf2_ros/transform_listener.h>
|
||||||
#include <tf2_ros/static_transform_broadcaster.h>
|
#include <tf2_ros/static_transform_broadcaster.h>
|
||||||
#include <tf2_geometry_msgs/tf2_geometry_msgs.h>
|
#include <tf2_geometry_msgs/tf2_geometry_msgs.h>
|
||||||
#include <geometry_msgs/TransformStamped.h>
|
#include <geometry_msgs/TransformStamped.h>
|
||||||
#include <geometry_msgs/Quaternion.h>
|
|
||||||
#include <geometry_msgs/PoseStamped.h>
|
#include <geometry_msgs/PoseStamped.h>
|
||||||
#include <geometry_msgs/PoseWithCovarianceStamped.h>
|
#include <geometry_msgs/PoseWithCovarianceStamped.h>
|
||||||
#include <std_srvs/Trigger.h>
|
#include <std_srvs/Trigger.h>
|
||||||
@@ -27,7 +25,7 @@
|
|||||||
using std::string;
|
using std::string;
|
||||||
using namespace geometry_msgs;
|
using namespace geometry_msgs;
|
||||||
|
|
||||||
bool reset_flag = true; // offset should be reset on the start
|
bool reset_flag = false;
|
||||||
string local_frame_id, frame_id, child_frame_id, offset_frame_id;
|
string local_frame_id, frame_id, child_frame_id, offset_frame_id;
|
||||||
tf2_ros::Buffer tf_buffer;
|
tf2_ros::Buffer tf_buffer;
|
||||||
ros::Publisher vpe_pub;
|
ros::Publisher vpe_pub;
|
||||||
@@ -68,13 +66,6 @@ inline Pose getPose(const PoseStampedConstPtr& pose) { return pose->pose; }
|
|||||||
|
|
||||||
inline Pose getPose(const PoseWithCovarianceStampedConstPtr& pose) { return pose->pose.pose; }
|
inline Pose getPose(const PoseWithCovarianceStampedConstPtr& pose) { return pose->pose.pose; }
|
||||||
|
|
||||||
inline void keepYaw(Quaternion& quaternion)
|
|
||||||
{
|
|
||||||
tf::Quaternion q;
|
|
||||||
q.setRPY(0, 0, tf::getYaw(quaternion));
|
|
||||||
tf::quaternionTFToMsg(q, quaternion);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void callback(const T& msg)
|
void callback(const T& msg)
|
||||||
{
|
{
|
||||||
@@ -97,29 +88,10 @@ void callback(const T& msg)
|
|||||||
if (!offset_frame_id.empty()) {
|
if (!offset_frame_id.empty()) {
|
||||||
if (reset_flag || msg->header.stamp - vpe.header.stamp > offset_timeout) {
|
if (reset_flag || msg->header.stamp - vpe.header.stamp > offset_timeout) {
|
||||||
// calculate the offset
|
// calculate the offset
|
||||||
if (!frame_id.empty()) {
|
offset = tf_buffer.lookupTransform(local_frame_id, frame_id,
|
||||||
// calculate from TF
|
msg->header.stamp, ros::Duration(0.02));
|
||||||
offset = tf_buffer.lookupTransform(local_frame_id, frame_id,
|
// offset.header.frame_id = vpe.header.frame_id;
|
||||||
msg->header.stamp, ros::Duration(0.02));
|
offset.child_frame_id = offset_frame_id;
|
||||||
// offset.header.frame_id = vpe.header.frame_id;
|
|
||||||
offset.child_frame_id = offset_frame_id;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// calculate transform between pose in vpe frame and pose in local frame
|
|
||||||
TransformStamped local_pose = tf_buffer.lookupTransform(local_frame_id, child_frame_id,
|
|
||||||
msg->header.stamp, ros::Duration(0.02));
|
|
||||||
keepYaw(local_pose.transform.rotation);
|
|
||||||
|
|
||||||
tf::Transform vpeTransform, poseTransform;
|
|
||||||
tf::poseMsgToTF(vpe.pose, vpeTransform);
|
|
||||||
tf::transformMsgToTF(local_pose.transform, poseTransform);
|
|
||||||
tf::Transform offset_tf = vpeTransform.inverseTimes(poseTransform);
|
|
||||||
tf::transformTFToMsg(offset_tf, offset.transform);
|
|
||||||
offset.header.frame_id = local_frame_id;
|
|
||||||
offset.header.stamp = msg->header.stamp;
|
|
||||||
offset.child_frame_id = offset_frame_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
br.sendTransform(offset);
|
br.sendTransform(offset);
|
||||||
reset_flag = false;
|
reset_flag = false;
|
||||||
ROS_INFO("offset reset");
|
ROS_INFO("offset reset");
|
||||||
@@ -150,9 +122,8 @@ int main(int argc, char **argv) {
|
|||||||
|
|
||||||
tf2_ros::TransformListener tf_listener(tf_buffer);
|
tf2_ros::TransformListener tf_listener(tf_buffer);
|
||||||
|
|
||||||
nh_priv.param<string>("frame_id", frame_id, ""); // name for used visual pose frame
|
nh_priv.param<string>("frame_id", frame_id, "");
|
||||||
nh_priv.param<string>("offset_frame_id", offset_frame_id, ""); // name for published offset frame
|
nh_priv.param<string>("offset_frame_id", offset_frame_id, "");
|
||||||
|
|
||||||
nh.param<string>("mavros/local_position/frame_id", local_frame_id, "map");
|
nh.param<string>("mavros/local_position/frame_id", local_frame_id, "map");
|
||||||
nh.param<string>("mavros/local_position/tf/child_frame_id", child_frame_id, "base_link");
|
nh.param<string>("mavros/local_position/tf/child_frame_id", child_frame_id, "base_link");
|
||||||
offset_timeout = ros::Duration(nh_priv.param("offset_timeout", 3.0));
|
offset_timeout = ros::Duration(nh_priv.param("offset_timeout", 3.0));
|
||||||
|
|||||||
@@ -1,4 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
export ROSWWW_DEFAULT=clover
|
|
||||||
rosrun roswww_static update
|
|
||||||
@@ -13,11 +13,11 @@ float32 alt
|
|||||||
float32 vx
|
float32 vx
|
||||||
float32 vy
|
float32 vy
|
||||||
float32 vz
|
float32 vz
|
||||||
float32 roll
|
|
||||||
float32 pitch
|
float32 pitch
|
||||||
|
float32 roll
|
||||||
float32 yaw
|
float32 yaw
|
||||||
float32 roll_rate
|
|
||||||
float32 pitch_rate
|
float32 pitch_rate
|
||||||
|
float32 roll_rate
|
||||||
float32 yaw_rate
|
float32 yaw_rate
|
||||||
float32 voltage
|
float32 voltage
|
||||||
float32 cell_voltage
|
float32 cell_voltage
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ float32 x
|
|||||||
float32 y
|
float32 y
|
||||||
float32 z
|
float32 z
|
||||||
float32 yaw
|
float32 yaw
|
||||||
|
float32 yaw_rate
|
||||||
float32 speed
|
float32 speed
|
||||||
string frame_id
|
string frame_id
|
||||||
bool auto_arm
|
bool auto_arm
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ float64 lat
|
|||||||
float64 lon
|
float64 lon
|
||||||
float32 z
|
float32 z
|
||||||
float32 yaw
|
float32 yaw
|
||||||
|
float32 yaw_rate
|
||||||
float32 speed
|
float32 speed
|
||||||
string frame_id
|
string frame_id
|
||||||
bool auto_arm
|
bool auto_arm
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
float32 z
|
|
||||||
string frame_id
|
|
||||||
---
|
|
||||||
bool success
|
|
||||||
string message
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
float32 roll
|
|
||||||
float32 pitch
|
float32 pitch
|
||||||
|
float32 roll
|
||||||
float32 yaw
|
float32 yaw
|
||||||
float32 thrust
|
float32 thrust
|
||||||
string frame_id
|
string frame_id
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ float32 x
|
|||||||
float32 y
|
float32 y
|
||||||
float32 z
|
float32 z
|
||||||
float32 yaw
|
float32 yaw
|
||||||
|
float32 yaw_rate
|
||||||
string frame_id
|
string frame_id
|
||||||
bool auto_arm
|
bool auto_arm
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
float32 roll_rate
|
|
||||||
float32 pitch_rate
|
float32 pitch_rate
|
||||||
|
float32 roll_rate
|
||||||
float32 yaw_rate
|
float32 yaw_rate
|
||||||
float32 thrust
|
float32 thrust
|
||||||
bool auto_arm
|
bool auto_arm
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ float32 vx
|
|||||||
float32 vy
|
float32 vy
|
||||||
float32 vz
|
float32 vz
|
||||||
float32 yaw
|
float32 yaw
|
||||||
|
float32 yaw_rate
|
||||||
string frame_id
|
string frame_id
|
||||||
bool auto_arm
|
bool auto_arm
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
float32 yaw
|
|
||||||
string frame_id
|
|
||||||
---
|
|
||||||
bool success
|
|
||||||
string message
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
float32 yaw_rate
|
|
||||||
---
|
|
||||||
bool success
|
|
||||||
string message
|
|
||||||
@@ -3,7 +3,6 @@ import rospy
|
|||||||
import pytest
|
import pytest
|
||||||
from mavros_msgs.msg import State
|
from mavros_msgs.msg import State
|
||||||
from clover import srv
|
from clover import srv
|
||||||
import time
|
|
||||||
|
|
||||||
@pytest.fixture()
|
@pytest.fixture()
|
||||||
def node():
|
def node():
|
||||||
@@ -25,7 +24,6 @@ def test_simple_offboard_services_available():
|
|||||||
rospy.wait_for_service('set_attitude', timeout=5)
|
rospy.wait_for_service('set_attitude', timeout=5)
|
||||||
rospy.wait_for_service('set_rates', timeout=5)
|
rospy.wait_for_service('set_rates', timeout=5)
|
||||||
rospy.wait_for_service('land', timeout=5)
|
rospy.wait_for_service('land', timeout=5)
|
||||||
rospy.wait_for_service('simple_offboard/release', timeout=5)
|
|
||||||
|
|
||||||
def test_web_video_server(node):
|
def test_web_video_server(node):
|
||||||
try:
|
try:
|
||||||
@@ -61,18 +59,3 @@ def test_blocks(node):
|
|||||||
|
|
||||||
t.join()
|
t.join()
|
||||||
assert wait_print.result == 'test'
|
assert wait_print.result == 'test'
|
||||||
|
|
||||||
def test_long_callback():
|
|
||||||
from clover import long_callback
|
|
||||||
from time import sleep
|
|
||||||
|
|
||||||
# very basic test for long_callback
|
|
||||||
@long_callback
|
|
||||||
def cb(i):
|
|
||||||
cb.counter += i
|
|
||||||
cb.counter = 0
|
|
||||||
cb(2)
|
|
||||||
sleep(0.1)
|
|
||||||
cb(3)
|
|
||||||
sleep(1)
|
|
||||||
assert cb.counter == 5
|
|
||||||
|
|||||||
@@ -37,19 +37,6 @@
|
|||||||
|
|
||||||
<node name="clover_blocks" pkg="clover_blocks" type="clover_blocks" output="screen" required="true"/>
|
<node name="clover_blocks" pkg="clover_blocks" type="clover_blocks" output="screen" required="true"/>
|
||||||
|
|
||||||
<node pkg="topic_tools" name="main_camera_throttle" type="throttle" ns="main_camera"
|
|
||||||
args="messages image_raw 5.0 image_raw_throttled" required="true"/>
|
|
||||||
|
|
||||||
<node pkg="nodelet" type="nodelet" name="main_camera_nodelet_manager" args="manager" output="screen" required="true">
|
|
||||||
<param name="num_worker_threads" value="2"/>
|
|
||||||
</node>
|
|
||||||
|
|
||||||
<node pkg="nodelet" type="nodelet" name="rectify" args="load image_proc/rectify main_camera_nodelet_manager" required="true">
|
|
||||||
<remap from="image_mono" to="main_camera/image_raw"/>
|
|
||||||
<remap from="camera_info" to="main_camera/camera_info"/>
|
|
||||||
<remap from="image_rect" to="main_camera/image_rect"/>
|
|
||||||
</node>
|
|
||||||
|
|
||||||
<param name="test_module" value="$(find clover)/test/basic.py"/>
|
<param name="test_module" value="$(find clover)/test/basic.py"/>
|
||||||
<test test-name="basic_test" pkg="ros_pytest" type="ros_pytest_runner"/>
|
<test test-name="basic_test" pkg="ros_pytest" type="ros_pytest_runner"/>
|
||||||
</launch>
|
</launch>
|
||||||
|
|||||||
@@ -1,437 +0,0 @@
|
|||||||
import rospy
|
|
||||||
import pytest
|
|
||||||
from pytest import approx
|
|
||||||
import threading
|
|
||||||
import mavros_msgs.msg
|
|
||||||
from mavros_msgs.srv import SetMode
|
|
||||||
from geometry_msgs.msg import PoseStamped
|
|
||||||
from clover import srv
|
|
||||||
from clover.msg import State
|
|
||||||
from std_srvs.srv import Trigger
|
|
||||||
from math import nan, inf
|
|
||||||
import tf2_ros
|
|
||||||
import tf2_geometry_msgs
|
|
||||||
|
|
||||||
@pytest.fixture()
|
|
||||||
def node():
|
|
||||||
return rospy.init_node('offboard_test', anonymous=True)
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def tf_buffer():
|
|
||||||
buf = tf2_ros.Buffer()
|
|
||||||
tf2_ros.TransformListener(buf)
|
|
||||||
return buf
|
|
||||||
|
|
||||||
def get_state():
|
|
||||||
return rospy.wait_for_message('/simple_offboard/state', State, timeout=1)
|
|
||||||
|
|
||||||
def get_navigate_target(tf_buffer):
|
|
||||||
target = tf_buffer.lookup_transform('map', 'navigate_target', rospy.get_rostime(), rospy.Duration(1))
|
|
||||||
assert target.child_frame_id == 'navigate_target'
|
|
||||||
return target
|
|
||||||
|
|
||||||
def test_offboard(node, tf_buffer):
|
|
||||||
navigate = rospy.ServiceProxy('navigate', srv.Navigate)
|
|
||||||
set_position = rospy.ServiceProxy('set_position', srv.SetPosition)
|
|
||||||
set_altitude = rospy.ServiceProxy('set_altitude', srv.SetAltitude)
|
|
||||||
set_yaw = rospy.ServiceProxy('set_yaw', srv.SetYaw)
|
|
||||||
set_yaw_rate = rospy.ServiceProxy('set_yaw_rate', srv.SetYawRate)
|
|
||||||
set_velocity = rospy.ServiceProxy('set_velocity', srv.SetVelocity)
|
|
||||||
set_attitude = rospy.ServiceProxy('set_attitude', srv.SetAttitude)
|
|
||||||
set_rates = rospy.ServiceProxy('set_rates', srv.SetRates)
|
|
||||||
get_telemetry = rospy.ServiceProxy('get_telemetry', srv.GetTelemetry)
|
|
||||||
land = rospy.ServiceProxy('land', Trigger)
|
|
||||||
|
|
||||||
res = navigate()
|
|
||||||
assert res.success == False
|
|
||||||
assert res.message.startswith('State timeout')
|
|
||||||
|
|
||||||
telem = get_telemetry()
|
|
||||||
assert telem.connected == False
|
|
||||||
|
|
||||||
# mocked state publisher
|
|
||||||
state_pub = rospy.Publisher('/mavros/state', mavros_msgs.msg.State, latch=True, queue_size=1)
|
|
||||||
state_msg = mavros_msgs.msg.State(mode='OFFBOARD', armed=True)
|
|
||||||
|
|
||||||
def publish_state():
|
|
||||||
r = rospy.Rate(2)
|
|
||||||
while not rospy.is_shutdown():
|
|
||||||
state_msg.header.stamp = rospy.Time.now()
|
|
||||||
state_pub.publish(state_msg)
|
|
||||||
r.sleep()
|
|
||||||
|
|
||||||
# start publishing state
|
|
||||||
threading.Thread(target=publish_state, daemon=True).start()
|
|
||||||
rospy.sleep(0.5)
|
|
||||||
|
|
||||||
# set_mode service mock
|
|
||||||
def set_mode(req):
|
|
||||||
state_msg.mode = req.custom_mode # set mocked mode to requested
|
|
||||||
return True,
|
|
||||||
|
|
||||||
rospy.Service('/mavros/set_mode', SetMode, set_mode)
|
|
||||||
|
|
||||||
telem = get_telemetry()
|
|
||||||
assert telem.connected == False
|
|
||||||
|
|
||||||
res = navigate()
|
|
||||||
assert res.success == False
|
|
||||||
assert res.message.startswith('No connection to FCU')
|
|
||||||
|
|
||||||
state_msg.connected = True
|
|
||||||
rospy.sleep(1)
|
|
||||||
|
|
||||||
telem = get_telemetry()
|
|
||||||
assert telem.connected == True
|
|
||||||
|
|
||||||
res = navigate()
|
|
||||||
assert res.success == False
|
|
||||||
assert res.message.startswith('No local position')
|
|
||||||
|
|
||||||
local_position_pub = rospy.Publisher('/mavros/local_position/pose', PoseStamped, latch=True, queue_size=1)
|
|
||||||
local_position_msg = PoseStamped()
|
|
||||||
local_position_msg.header.frame_id = 'map'
|
|
||||||
local_position_msg.pose.position.x = 1
|
|
||||||
local_position_msg.pose.position.y = 2
|
|
||||||
local_position_msg.pose.position.z = 3
|
|
||||||
local_position_msg.pose.orientation.w = 1
|
|
||||||
|
|
||||||
def publish_local_position():
|
|
||||||
r = rospy.Rate(30)
|
|
||||||
while not rospy.is_shutdown():
|
|
||||||
local_position_msg.header.stamp = rospy.Time.now()
|
|
||||||
local_position_pub.publish(local_position_msg)
|
|
||||||
r.sleep()
|
|
||||||
|
|
||||||
# start publishing local position
|
|
||||||
threading.Thread(target=publish_local_position, daemon=True).start()
|
|
||||||
rospy.sleep(0.5)
|
|
||||||
|
|
||||||
# check body frame
|
|
||||||
body = tf_buffer.lookup_transform('map', 'body', rospy.get_rostime(), rospy.Duration(1))
|
|
||||||
assert body.child_frame_id == 'body'
|
|
||||||
assert body.transform.translation.x == approx(1)
|
|
||||||
assert body.transform.translation.y == approx(2)
|
|
||||||
assert body.transform.translation.z == approx(3)
|
|
||||||
|
|
||||||
res = navigate(x=3, y=2, z=1, frame_id='map')
|
|
||||||
assert res.success == True
|
|
||||||
state = get_state()
|
|
||||||
assert state.mode == State.MODE_NAVIGATE
|
|
||||||
assert state.yaw_mode == State.YAW_MODE_YAW
|
|
||||||
assert state.x == 3
|
|
||||||
assert state.y == 2
|
|
||||||
assert state.z == 1
|
|
||||||
assert state.yaw == 0
|
|
||||||
assert state.xy_frame_id == 'map'
|
|
||||||
assert state.z_frame_id == 'map'
|
|
||||||
assert state.yaw_frame_id == 'map'
|
|
||||||
target = get_navigate_target(tf_buffer)
|
|
||||||
assert target.header.frame_id == 'map'
|
|
||||||
assert target.transform.translation.x == approx(3)
|
|
||||||
assert target.transform.translation.y == approx(2)
|
|
||||||
assert target.transform.translation.z == approx(1)
|
|
||||||
assert target.transform.rotation.x == 0
|
|
||||||
assert target.transform.rotation.y == 0
|
|
||||||
assert target.transform.rotation.z == 0
|
|
||||||
assert target.transform.rotation.w == 1
|
|
||||||
|
|
||||||
# try to set only the y
|
|
||||||
res = navigate(x=nan, y=1, z=nan)
|
|
||||||
assert res.success == False
|
|
||||||
assert res.message.startswith('x and y can be set only together')
|
|
||||||
|
|
||||||
# set z in body frame
|
|
||||||
res = navigate(x=nan, y=nan, z=1, frame_id='body')
|
|
||||||
assert res.success == True
|
|
||||||
state = get_state()
|
|
||||||
assert state.mode == State.MODE_NAVIGATE
|
|
||||||
assert state.yaw_mode == State.YAW_MODE_YAW
|
|
||||||
assert state.x == 3
|
|
||||||
assert state.y == 2
|
|
||||||
assert state.z == 4
|
|
||||||
assert state.yaw == 0
|
|
||||||
assert state.xy_frame_id == 'map'
|
|
||||||
assert state.z_frame_id == 'map'
|
|
||||||
assert state.yaw_frame_id == 'map'
|
|
||||||
|
|
||||||
# set xy in test frame
|
|
||||||
res = navigate(x=1, y=2, z=nan, frame_id='test')
|
|
||||||
assert res.success == True
|
|
||||||
state = get_state()
|
|
||||||
assert state.mode == State.MODE_NAVIGATE
|
|
||||||
assert state.yaw_mode == State.YAW_MODE_YAW
|
|
||||||
assert state.x == 1
|
|
||||||
assert state.y == 2
|
|
||||||
assert state.z == 4
|
|
||||||
assert state.yaw == 0
|
|
||||||
assert state.xy_frame_id == 'test'
|
|
||||||
assert state.z_frame_id == 'map'
|
|
||||||
assert state.yaw_frame_id == 'test'
|
|
||||||
|
|
||||||
# auto_arm should not invalidate the setpoint if not effective
|
|
||||||
res = navigate(x=nan, y=nan, z=1, frame_id='map', auto_arm=True)
|
|
||||||
assert res.success == True
|
|
||||||
state = get_state()
|
|
||||||
assert state.mode == State.MODE_NAVIGATE
|
|
||||||
assert state.yaw_mode == State.YAW_MODE_YAW
|
|
||||||
assert state.x == 1
|
|
||||||
assert state.y == 2
|
|
||||||
assert state.z == 1
|
|
||||||
assert state.yaw == 0
|
|
||||||
assert state.xy_frame_id == 'test'
|
|
||||||
assert state.z_frame_id == 'map'
|
|
||||||
assert state.yaw_frame_id == 'map'
|
|
||||||
|
|
||||||
# auto_arm should invalidate the setpoint if effective
|
|
||||||
state_msg.mode = 'STABILIZED' # pretend we are not in OFFBOARD mode
|
|
||||||
rospy.sleep(1)
|
|
||||||
res = navigate(x=nan, y=nan, z=1, frame_id='map', auto_arm=True)
|
|
||||||
assert res.success == True
|
|
||||||
state = get_state()
|
|
||||||
assert state.mode == State.MODE_NAVIGATE
|
|
||||||
assert state.yaw_mode == State.YAW_MODE_YAW
|
|
||||||
assert state.x == 1
|
|
||||||
assert state.y == 2
|
|
||||||
assert state.z == 1
|
|
||||||
assert state.yaw == 0
|
|
||||||
assert state.xy_frame_id == 'map'
|
|
||||||
assert state.z_frame_id == 'map'
|
|
||||||
assert state.yaw_frame_id == 'map'
|
|
||||||
state_msg.mode = 'OFFBOARD'
|
|
||||||
rospy.sleep(1)
|
|
||||||
|
|
||||||
# set_attitude should invalidate the setpoint
|
|
||||||
res = set_attitude()
|
|
||||||
assert res.success == True
|
|
||||||
|
|
||||||
res = navigate(x=5, y=6, z=nan, yaw=nan, frame_id='map')
|
|
||||||
assert res.success == True
|
|
||||||
state = get_state()
|
|
||||||
assert state.mode == State.MODE_NAVIGATE
|
|
||||||
assert state.yaw_mode == State.YAW_MODE_YAW
|
|
||||||
assert state.x == 5
|
|
||||||
assert state.y == 6
|
|
||||||
assert state.z == 3
|
|
||||||
assert state.yaw == 0
|
|
||||||
assert state.xy_frame_id == 'map'
|
|
||||||
assert state.z_frame_id == 'map'
|
|
||||||
assert state.yaw_frame_id == 'map'
|
|
||||||
|
|
||||||
# test set_altitude
|
|
||||||
res = set_altitude(z=7, frame_id='test')
|
|
||||||
assert res.success == True
|
|
||||||
state = get_state()
|
|
||||||
assert state.mode == State.MODE_NAVIGATE
|
|
||||||
assert state.yaw_mode == State.YAW_MODE_YAW
|
|
||||||
assert state.x == 5
|
|
||||||
assert state.y == 6
|
|
||||||
assert state.z == 7
|
|
||||||
assert state.yaw == 0
|
|
||||||
assert state.xy_frame_id == 'map'
|
|
||||||
assert state.z_frame_id == 'test'
|
|
||||||
assert state.yaw_frame_id == 'map'
|
|
||||||
|
|
||||||
# test set_yaw
|
|
||||||
res = set_yaw(yaw=0.5, frame_id='test2')
|
|
||||||
assert res.success == True
|
|
||||||
state = get_state()
|
|
||||||
assert state.mode == State.MODE_NAVIGATE
|
|
||||||
assert state.yaw_mode == State.YAW_MODE_YAW
|
|
||||||
assert state.x == 5
|
|
||||||
assert state.y == 6
|
|
||||||
assert state.z == 7
|
|
||||||
assert state.yaw == 0.5
|
|
||||||
assert state.xy_frame_id == 'map'
|
|
||||||
assert state.z_frame_id == 'test'
|
|
||||||
assert state.yaw_frame_id == 'test2'
|
|
||||||
|
|
||||||
# test set_yaw_rate
|
|
||||||
res = set_yaw_rate(yaw_rate=2)
|
|
||||||
assert res.success == True
|
|
||||||
state = get_state()
|
|
||||||
assert state.mode == State.MODE_NAVIGATE
|
|
||||||
assert state.yaw_mode == State.YAW_MODE_YAW_RATE
|
|
||||||
assert state.x == 5
|
|
||||||
assert state.y == 6
|
|
||||||
assert state.z == 7
|
|
||||||
assert state.yaw_rate == 2
|
|
||||||
assert state.xy_frame_id == 'map'
|
|
||||||
assert state.z_frame_id == 'test'
|
|
||||||
|
|
||||||
# navigate(yaw=nan) should keep yaw rate mode
|
|
||||||
res = navigate(x=nan, y=nan, z=nan, yaw=nan)
|
|
||||||
assert res.success == True
|
|
||||||
state = get_state()
|
|
||||||
assert state.mode == State.MODE_NAVIGATE
|
|
||||||
assert state.yaw_mode == State.YAW_MODE_YAW_RATE
|
|
||||||
assert state.x == 5
|
|
||||||
assert state.y == 6
|
|
||||||
assert state.z == 7
|
|
||||||
assert state.yaw_rate == 2
|
|
||||||
assert state.xy_frame_id == 'map'
|
|
||||||
assert state.z_frame_id == 'test'
|
|
||||||
|
|
||||||
# set_yaw(nan) should change back to yaw mode
|
|
||||||
res = set_yaw(yaw=nan)
|
|
||||||
assert res.success == True
|
|
||||||
state = get_state()
|
|
||||||
assert state.mode == State.MODE_NAVIGATE
|
|
||||||
assert state.yaw_mode == State.YAW_MODE_YAW
|
|
||||||
assert state.yaw == 0
|
|
||||||
assert state.yaw_frame_id == 'map'
|
|
||||||
|
|
||||||
# test set_position
|
|
||||||
res = set_position(x=nan, y=nan, z=13, yaw=nan, frame_id='test2')
|
|
||||||
assert res.success == True
|
|
||||||
state = get_state()
|
|
||||||
assert state.mode == State.MODE_POSITION
|
|
||||||
assert state.yaw_mode == State.YAW_MODE_YAW
|
|
||||||
assert state.x == 5
|
|
||||||
assert state.y == 6
|
|
||||||
assert state.z == 13
|
|
||||||
assert state.yaw == 0
|
|
||||||
assert state.xy_frame_id == 'map'
|
|
||||||
assert state.z_frame_id == 'test2'
|
|
||||||
assert state.yaw_frame_id == 'map'
|
|
||||||
|
|
||||||
# set_altitude should not change the mode
|
|
||||||
res = set_altitude(z=3, frame_id='test')
|
|
||||||
assert res.success == True
|
|
||||||
state = get_state()
|
|
||||||
assert state.mode == State.MODE_POSITION
|
|
||||||
assert state.yaw_mode == State.YAW_MODE_YAW
|
|
||||||
assert state.x == 5
|
|
||||||
assert state.y == 6
|
|
||||||
assert state.z == 3
|
|
||||||
assert state.yaw == 0
|
|
||||||
assert state.xy_frame_id == 'map'
|
|
||||||
assert state.z_frame_id == 'test'
|
|
||||||
assert state.yaw_frame_id == 'map'
|
|
||||||
|
|
||||||
# set_yaw should not change the main mode
|
|
||||||
res = set_yaw(yaw=1, frame_id='test2')
|
|
||||||
assert res.success == True
|
|
||||||
state = get_state()
|
|
||||||
assert state.mode == State.MODE_POSITION
|
|
||||||
assert state.yaw_mode == State.YAW_MODE_YAW
|
|
||||||
assert state.x == 5
|
|
||||||
assert state.y == 6
|
|
||||||
assert state.z == 3
|
|
||||||
assert state.yaw == 1
|
|
||||||
assert state.xy_frame_id == 'map'
|
|
||||||
assert state.z_frame_id == 'test'
|
|
||||||
assert state.yaw_frame_id == 'test2'
|
|
||||||
|
|
||||||
# test set_velocity
|
|
||||||
res = set_velocity(vx=1, frame_id='body')
|
|
||||||
state = get_state()
|
|
||||||
assert state.mode == State.MODE_VELOCITY
|
|
||||||
assert state.yaw_mode == State.YAW_MODE_YAW
|
|
||||||
assert state.vx == 1
|
|
||||||
assert state.vy == 0
|
|
||||||
assert state.vz == 0
|
|
||||||
assert state.yaw == 0
|
|
||||||
assert state.xy_frame_id == 'map'
|
|
||||||
assert state.z_frame_id == 'map'
|
|
||||||
assert state.yaw_frame_id == 'map'
|
|
||||||
|
|
||||||
# set_altitude should not work in velocity mode
|
|
||||||
res = set_altitude(z=3, frame_id='test')
|
|
||||||
assert res.success == False
|
|
||||||
assert res.message.startswith('Altitude cannot be set in')
|
|
||||||
|
|
||||||
# test set_attitude
|
|
||||||
res = set_attitude(roll=0.1, pitch=0.2, yaw=0.3, thrust=0.5)
|
|
||||||
assert res.success == True
|
|
||||||
state = get_state()
|
|
||||||
assert state.mode == State.MODE_ATTITUDE
|
|
||||||
assert state.yaw_mode == State.YAW_MODE_YAW
|
|
||||||
assert state.roll == approx(0.1)
|
|
||||||
assert state.pitch == approx(0.2)
|
|
||||||
assert state.yaw == approx(0.3)
|
|
||||||
assert state.thrust == approx(0.5)
|
|
||||||
assert state.yaw_frame_id == 'map'
|
|
||||||
msg = rospy.wait_for_message('/mavros/setpoint_attitude/attitude', PoseStamped, timeout=3)
|
|
||||||
# Tait-Bryan ZYX angle (rzyx) converted to quaternion
|
|
||||||
assert msg.pose.orientation.x == approx(0.0342708)
|
|
||||||
assert msg.pose.orientation.y == approx(0.10602051)
|
|
||||||
assert msg.pose.orientation.z == approx(0.14357218)
|
|
||||||
assert msg.pose.orientation.w == approx(0.98334744)
|
|
||||||
msg = rospy.wait_for_message('/mavros/setpoint_attitude/thrust', mavros_msgs.msg.Thrust, timeout=3)
|
|
||||||
assert msg.thrust == approx(0.5)
|
|
||||||
|
|
||||||
# set_yaw should work in attitude mode
|
|
||||||
res = set_yaw(yaw=0.7, frame_id='test2')
|
|
||||||
assert res.success == True
|
|
||||||
state = get_state()
|
|
||||||
assert state.mode == State.MODE_ATTITUDE
|
|
||||||
assert state.yaw_mode == State.YAW_MODE_YAW
|
|
||||||
assert state.roll == approx(0.1)
|
|
||||||
assert state.pitch == approx(0.2)
|
|
||||||
assert state.yaw == approx(0.7)
|
|
||||||
assert state.thrust == approx(0.5)
|
|
||||||
assert state.yaw_frame_id == 'test2'
|
|
||||||
|
|
||||||
# set_yaw_rate should not work in attitude mode
|
|
||||||
res = set_yaw_rate(yaw_rate=0.3)
|
|
||||||
assert res.success == False
|
|
||||||
assert res.message.startswith('Yaw rate cannot be set in')
|
|
||||||
|
|
||||||
# test set_rates
|
|
||||||
res = set_rates(roll_rate=nan, pitch_rate=nan, yaw_rate=0.3, thrust=0.6)
|
|
||||||
assert res.success == True
|
|
||||||
state = get_state()
|
|
||||||
assert state.mode == State.MODE_RATES
|
|
||||||
assert state.yaw_mode == State.YAW_MODE_YAW_RATE
|
|
||||||
assert state.roll_rate == approx(0)
|
|
||||||
assert state.pitch_rate == approx(0)
|
|
||||||
assert state.yaw_rate == approx(0.3)
|
|
||||||
assert state.thrust == approx(0.6)
|
|
||||||
msg = rospy.wait_for_message('/mavros/setpoint_raw/attitude', mavros_msgs.msg.AttitudeTarget, timeout=3)
|
|
||||||
assert msg.thrust == approx(0.6)
|
|
||||||
|
|
||||||
res = set_rates(roll_rate=0.3, pitch_rate=0.2, yaw_rate=0.1, thrust=0.4)
|
|
||||||
assert res.success == True
|
|
||||||
state = get_state()
|
|
||||||
assert state.mode == State.MODE_RATES
|
|
||||||
assert state.yaw_mode == State.YAW_MODE_YAW_RATE
|
|
||||||
assert state.roll_rate == approx(0.3)
|
|
||||||
assert state.pitch_rate == approx(0.2)
|
|
||||||
assert state.yaw_rate == approx(0.1)
|
|
||||||
assert state.thrust == approx(0.4)
|
|
||||||
|
|
||||||
res = set_rates(roll_rate=nan, pitch_rate=nan, yaw_rate=nan, thrust=0.3)
|
|
||||||
assert res.success == True
|
|
||||||
state = get_state()
|
|
||||||
assert state.mode == State.MODE_RATES
|
|
||||||
assert state.yaw_mode == State.YAW_MODE_YAW_RATE
|
|
||||||
assert state.roll_rate == approx(0.3)
|
|
||||||
assert state.pitch_rate == approx(0.2)
|
|
||||||
assert state.yaw_rate == approx(0.1)
|
|
||||||
assert state.thrust == approx(0.3)
|
|
||||||
msg = rospy.wait_for_message('/mavros/setpoint_raw/attitude', mavros_msgs.msg.AttitudeTarget, timeout=3)
|
|
||||||
assert msg.type_mask == mavros_msgs.msg.AttitudeTarget.IGNORE_ATTITUDE
|
|
||||||
assert msg.body_rate.x == approx(0.3)
|
|
||||||
assert msg.body_rate.y == approx(0.2)
|
|
||||||
assert msg.body_rate.z == approx(0.1)
|
|
||||||
|
|
||||||
# set_yaw_rate should work in rates mode
|
|
||||||
res = set_yaw_rate(yaw_rate=0.4)
|
|
||||||
assert res.success == True
|
|
||||||
state = get_state()
|
|
||||||
assert state.mode == State.MODE_RATES
|
|
||||||
assert state.yaw_mode == State.YAW_MODE_YAW_RATE
|
|
||||||
assert state.roll_rate == approx(0.3)
|
|
||||||
assert state.pitch_rate == approx(0.2)
|
|
||||||
assert state.yaw_rate == approx(0.4)
|
|
||||||
assert state.thrust == approx(0.3)
|
|
||||||
|
|
||||||
res = set_rates(roll_rate=inf)
|
|
||||||
assert res.success == False
|
|
||||||
assert res.message == 'roll_rate argument cannot be Inf'
|
|
||||||
|
|
||||||
# test land service
|
|
||||||
res = land()
|
|
||||||
assert res.success == True
|
|
||||||
assert state_msg.mode == 'AUTO.LAND' # check that the mode was set correctly
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
<launch>
|
|
||||||
<node name="simple_offboard" pkg="clover" type="simple_offboard" required="true" output="screen"/>
|
|
||||||
|
|
||||||
<node pkg="tf2_ros" type="static_transform_publisher" name="test_frame" args="10 20 30 0 0 0 map test"/>
|
|
||||||
|
|
||||||
<node pkg="tf2_ros" type="static_transform_publisher" name="test2_frame" args="100 200 300 0 0 0 map test2"/>
|
|
||||||
|
|
||||||
<param name="test_module" value="$(find clover)/test/offboard.py"/>
|
|
||||||
<test test-name="offboard_test" pkg="ros_pytest" type="ros_pytest_runner"/>
|
|
||||||
</launch>
|
|
||||||
@@ -1,54 +1,17 @@
|
|||||||
# Source files: PX4-Autopilot/boards/**/nuttx-config/nsh/defconfig
|
# PixHawk (px4fmu-v2), px4fmu-v3
|
||||||
|
|
||||||
# Additional info:
|
|
||||||
# https://docs.px4.io/main/en/flight_controller/
|
|
||||||
# https://github.com/mavlink/qgroundcontrol/blob/master/src/comm/USBBoardInfo.json
|
|
||||||
|
|
||||||
SUBSYSTEM=="tty", ATTRS{idVendor}=="26ac", ATTRS{idProduct}=="0001", ATTRS{product}=="PX4 GNF405", SYMLINK+="px4fmu"
|
|
||||||
SUBSYSTEM=="tty", ATTRS{idVendor}=="26ac", ATTRS{idProduct}=="0001", ATTRS{product}=="PX4 OmnibusF4SD", SYMLINK+="px4fmu"
|
|
||||||
SUBSYSTEM=="tty", ATTRS{idVendor}=="26ac", ATTRS{idProduct}=="0016", ATTRS{product}=="PX4 Crazyflie v2.0", SYMLINK+="px4fmu"
|
|
||||||
SUBSYSTEM=="tty", ATTRS{idVendor}=="1FC9", ATTRS{idProduct}=="001c", ATTRS{product}=="PX4 FMUK66 v3.x", SYMLINK+="px4fmu"
|
|
||||||
SUBSYSTEM=="tty", ATTRS{idVendor}=="1FC9", ATTRS{idProduct}=="001c", ATTRS{product}=="PX4 FMUK66 E", SYMLINK+="px4fmu"
|
|
||||||
SUBSYSTEM=="tty", ATTRS{idVendor}=="1FC9", ATTRS{idProduct}=="001d", ATTRS{product}=="PX4 FMURT1062 v1.x", SYMLINK+="px4fmu"
|
|
||||||
SUBSYSTEM=="tty", ATTRS{idVendor}=="26ac", ATTRS{idProduct}=="0001", ATTRS{product}=="DiatoneMambaF405 MK2", SYMLINK+="px4fmu"
|
|
||||||
SUBSYSTEM=="tty", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="a32f", ATTRS{product}=="PX4 FMU ModalAI FCv1", SYMLINK+="px4fmu"
|
|
||||||
SUBSYSTEM=="tty", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="a330", ATTRS{product}=="PX4 FMU ModalAI FCv2", SYMLINK+="px4fmu"
|
|
||||||
SUBSYSTEM=="tty", ATTRS{idVendor}=="26ac", ATTRS{idProduct}=="0012", ATTRS{product}=="PX4 FMU UVify Core", SYMLINK+="px4fmu"
|
|
||||||
SUBSYSTEM=="tty", ATTRS{idVendor}=="3162", ATTRS{idProduct}=="0050", ATTRS{product}=="PX4 KakuteH7", SYMLINK+="px4fmu"
|
|
||||||
SUBSYSTEM=="tty", ATTRS{idVendor}=="3162", ATTRS{idProduct}=="0050", ATTRS{product}=="PX4 KakuteH7v2", SYMLINK+="px4fmu"
|
|
||||||
SUBSYSTEM=="tty", ATTRS{idVendor}=="3162", ATTRS{idProduct}=="004b", ATTRS{product}=="PX4 DurandalV1", SYMLINK+="px4fmu"
|
|
||||||
SUBSYSTEM=="tty", ATTRS{idVendor}=="26ac", ATTRS{idProduct}=="0050", ATTRS{product}=="PX4 KakuteF7", SYMLINK+="px4fmu"
|
|
||||||
SUBSYSTEM=="tty", ATTRS{idVendor}=="3162", ATTRS{idProduct}=="0050", ATTRS{product}=="PX4 KakuteH7Mini-nand", SYMLINK+="px4fmu"
|
|
||||||
SUBSYSTEM=="tty", ATTRS{idVendor}=="3162", ATTRS{idProduct}=="004E", ATTRS{product}=="PX4 PIX32V5", SYMLINK+="px4fmu"
|
|
||||||
SUBSYSTEM=="tty", ATTRS{idVendor}=="26ac", ATTRS{idProduct}=="0061", ATTRS{product}=="PX4 ATL Mantis-EDU", SYMLINK+="px4fmu"
|
|
||||||
SUBSYSTEM=="tty", ATTRS{idVendor}=="3163", ATTRS{idProduct}=="004c", ATTRS{product}=="PX4 CUAV Nora", SYMLINK+="px4fmu"
|
|
||||||
SUBSYSTEM=="tty", ATTRS{idVendor}=="3163", ATTRS{idProduct}=="004c", ATTRS{product}=="PX4 CUAV X7Pro", SYMLINK+="px4fmu"
|
|
||||||
SUBSYSTEM=="tty", ATTRS{idVendor}=="1B8C", ATTRS{idProduct}=="0036", ATTRS{product}=="MatekH743-mini", SYMLINK+="px4fmu"
|
|
||||||
SUBSYSTEM=="tty", ATTRS{idVendor}=="1B8C", ATTRS{idProduct}=="0036", ATTRS{product}=="MatekH743", SYMLINK+="px4fmu"
|
|
||||||
SUBSYSTEM=="tty", ATTRS{idVendor}=="120A", ATTRS{idProduct}=="1004", ATTRS{product}=="Matekgnssm9nf4", SYMLINK+="px4fmu"
|
|
||||||
SUBSYSTEM=="tty", ATTRS{idVendor}=="1209", ATTRS{idProduct}=="1013", ATTRS{product}=="MatekH743", SYMLINK+="px4fmu"
|
|
||||||
SUBSYSTEM=="tty", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="0037", ATTRS{product}=="PX4 FMU SmartAP AIRLink", SYMLINK+="px4fmu"
|
|
||||||
SUBSYSTEM=="tty", ATTRS{idVendor}=="2DAE", ATTRS{idProduct}=="1058", ATTRS{product}=="CubeOrange+", SYMLINK+="px4fmu"
|
|
||||||
SUBSYSTEM=="tty", ATTRS{idVendor}=="2DAE", ATTRS{idProduct}=="1012", ATTRS{product}=="CubeYellow", SYMLINK+="px4fmu"
|
|
||||||
SUBSYSTEM=="tty", ATTRS{idVendor}=="2DAE", ATTRS{idProduct}=="1016", ATTRS{product}=="CubeOrange", SYMLINK+="px4fmu"
|
|
||||||
SUBSYSTEM=="tty", ATTRS{idVendor}=="3185", ATTRS{idProduct}=="0035", ATTRS{product}=="PX4 FMU v6X.x", SYMLINK+="px4fmu"
|
|
||||||
SUBSYSTEM=="tty", ATTRS{idVendor}=="3185", ATTRS{idProduct}=="0038", ATTRS{product}=="PX4 FMU v6C.x", SYMLINK+="px4fmu"
|
|
||||||
SUBSYSTEM=="tty", ATTRS{idVendor}=="3185", ATTRS{idProduct}=="0033", ATTRS{product}=="PX4 FMU v5X.x", SYMLINK+="px4fmu"
|
|
||||||
SUBSYSTEM=="tty", ATTRS{idVendor}=="1B8C", ATTRS{idProduct}=="0036", ATTRS{product}=="PX4 FMU v6U.x", SYMLINK+="px4fmu"
|
|
||||||
SUBSYSTEM=="tty", ATTRS{idVendor}=="26ac", ATTRS{idProduct}=="0013", ATTRS{product}=="PX4 FMU v4.x PRO", SYMLINK+="px4fmu"
|
|
||||||
SUBSYSTEM=="tty", ATTRS{idVendor}=="26ac", ATTRS{idProduct}=="0011", ATTRS{product}=="PX4 FMU v2.x", SYMLINK+="px4fmu"
|
SUBSYSTEM=="tty", ATTRS{idVendor}=="26ac", ATTRS{idProduct}=="0011", ATTRS{product}=="PX4 FMU v2.x", SYMLINK+="px4fmu"
|
||||||
|
# PixRacer (px4fmu-v4)
|
||||||
SUBSYSTEM=="tty", ATTRS{idVendor}=="26ac", ATTRS{idProduct}=="0012", ATTRS{product}=="PX4 FMU v4.x", SYMLINK+="px4fmu"
|
SUBSYSTEM=="tty", ATTRS{idVendor}=="26ac", ATTRS{idProduct}=="0012", ATTRS{product}=="PX4 FMU v4.x", SYMLINK+="px4fmu"
|
||||||
|
# px4fmu-v5
|
||||||
SUBSYSTEM=="tty", ATTRS{idVendor}=="26ac", ATTRS{idProduct}=="0032", ATTRS{product}=="PX4 FMU v5.x", SYMLINK+="px4fmu"
|
SUBSYSTEM=="tty", ATTRS{idVendor}=="26ac", ATTRS{idProduct}=="0032", ATTRS{product}=="PX4 FMU v5.x", SYMLINK+="px4fmu"
|
||||||
SUBSYSTEM=="tty", ATTRS{idVendor}=="3162", ATTRS{idProduct}=="004b", ATTRS{product}=="PX4 SP RACING H7 EXTREME", SYMLINK+="px4fmu"
|
# auav
|
||||||
SUBSYSTEM=="tty", ATTRS{idVendor}=="26ac", ATTRS{idProduct}=="0030", ATTRS{product}=="MindPX FMU v2.x", SYMLINK+="px4fmu"
|
SUBSYSTEM=="tty", ATTRS{idVendor}=="26ac", ATTRS{idProduct}=="0021", ATTRS{product}=="PX4 AUAV x2.1", SYMLINK+="px4fmu"
|
||||||
SUBSYSTEM=="tty", ATTRS{idVendor}=="3185", ATTRS{idProduct}=="0039", ATTRS{product}=="ARK FMU v6X.x", SYMLINK+="px4fmu"
|
# crazyflie
|
||||||
SUBSYSTEM=="tty", ATTRS{idVendor}=="26ac", ATTRS{idProduct}=="0016", ATTRS{product}=="PX4 FreeFly RTK GPS", SYMLINK+="px4fmu"
|
SUBSYSTEM=="tty", ATTRS{idVendor}=="26ac", ATTRS{idProduct}=="0016", ATTRS{product}=="PX4 Crazyflie v2.0", SYMLINK+="px4fmu"
|
||||||
SUBSYSTEM=="tty", ATTRS{idVendor}=="26ac", ATTRS{idProduct}=="1024", ATTRS{product}=="mRoControlZeroH7 OEM", SYMLINK+="px4fmu"
|
# px4fmu-v4pro
|
||||||
SUBSYSTEM=="tty", ATTRS{idVendor}=="26ac", ATTRS{idProduct}=="1017", ATTRS{product}=="mRoPixracerPro", SYMLINK+="px4fmu"
|
SUBSYSTEM=="tty", ATTRS{idVendor}=="26ac", ATTRS{idProduct}=="0013", ATTRS{product}=="PX4 FMU v4.x PRO", SYMLINK+="px4fmu"
|
||||||
SUBSYSTEM=="tty", ATTRS{idVendor}=="26ac", ATTRS{idProduct}=="1023", ATTRS{product}=="mRoControlZeroH7", SYMLINK+="px4fmu"
|
# Omnibus
|
||||||
SUBSYSTEM=="tty", ATTRS{idVendor}=="26ac", ATTRS{idProduct}=="008D", ATTRS{product}=="mRoControlZeroF7", SYMLINK+="px4fmu"
|
SUBSYSTEM=="tty", ATTRS{idVendor}=="26ac", ATTRS{idProduct}=="0001", ATTRS{product}=="PX4 OmnibusF4SD", SYMLINK+="px4fmu"
|
||||||
SUBSYSTEM=="tty", ATTRS{idVendor}=="26ac", ATTRS{idProduct}=="0021", ATTRS{product}=="PX4 AUAV X2.1", SYMLINK+="px4fmu"
|
# CUAV X7 Pro
|
||||||
SUBSYSTEM=="tty", ATTRS{idVendor}=="26ac", ATTRS{idProduct}=="1022", ATTRS{product}=="mRoControlZero Classic", SYMLINK+="px4fmu"
|
SUBSYSTEM=="tty", ATTRS{idVendor}=="3163", ATTRS{idProduct}=="004c", ATTRS{product}=="PX4 CUAV X7Pro", SYMLINK+="px4fmu"
|
||||||
SUBSYSTEM=="tty", ATTRS{idVendor}=="26ac", ATTRS{idProduct}=="0088", ATTRS{product}=="mRo x2.1-777", SYMLINK+="px4fmu"
|
|
||||||
SUBSYSTEM=="tty", ATTRS{idVendor}=="35a7", ATTRS{idProduct}=="0002", ATTRS{product}=="FCC-R1", SYMLINK+="px4fmu"
|
|
||||||
SUBSYSTEM=="tty", ATTRS{idVendor}=="35a7", ATTRS{idProduct}=="0001", ATTRS{product}=="FCC-K1", SYMLINK+="px4fmu"
|
|
||||||
|
|||||||
236
clover/www/js/json-to-pretty-yaml.js
vendored
Normal file
@@ -0,0 +1,236 @@
|
|||||||
|
// Browserified https://www.npmjs.com/package/json-to-pretty-yaml module
|
||||||
|
|
||||||
|
(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
|
||||||
|
(function() {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var typeOf = require('remedial').typeOf;
|
||||||
|
var trimWhitespace = require('remove-trailing-spaces');
|
||||||
|
|
||||||
|
function stringify(data) {
|
||||||
|
var handlers, indentLevel = '';
|
||||||
|
|
||||||
|
handlers = {
|
||||||
|
"undefined": function() {
|
||||||
|
// objects will not have `undefined` converted to `null`
|
||||||
|
// as this may have unintended consequences
|
||||||
|
// For arrays, however, this behavior seems appropriate
|
||||||
|
return 'null';
|
||||||
|
},
|
||||||
|
"null": function() {
|
||||||
|
return 'null';
|
||||||
|
},
|
||||||
|
"number": function(x) {
|
||||||
|
return x;
|
||||||
|
},
|
||||||
|
"boolean": function(x) {
|
||||||
|
return x ? 'true' : 'false';
|
||||||
|
},
|
||||||
|
"string": function(x) {
|
||||||
|
// to avoid the string "true" being confused with the
|
||||||
|
// the literal `true`, we always wrap strings in quotes
|
||||||
|
return JSON.stringify(x);
|
||||||
|
},
|
||||||
|
"array": function(x) {
|
||||||
|
var output = '';
|
||||||
|
|
||||||
|
if (0 === x.length) {
|
||||||
|
output += '[]';
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
indentLevel = indentLevel.replace(/$/, ' ');
|
||||||
|
x.forEach(function(y, i) {
|
||||||
|
// TODO how should `undefined` be handled?
|
||||||
|
var handler = handlers[typeOf(y)];
|
||||||
|
|
||||||
|
if (!handler) {
|
||||||
|
throw new Error('what the crap: ' + typeOf(y));
|
||||||
|
}
|
||||||
|
|
||||||
|
output += '\n' + indentLevel + '- ' + handler(y, true);
|
||||||
|
|
||||||
|
});
|
||||||
|
indentLevel = indentLevel.replace(/ /, '');
|
||||||
|
|
||||||
|
return output;
|
||||||
|
},
|
||||||
|
"object": function(x, inArray, rootNode) {
|
||||||
|
var output = '';
|
||||||
|
|
||||||
|
if (0 === Object.keys(x).length) {
|
||||||
|
output += '{}';
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!rootNode) {
|
||||||
|
indentLevel = indentLevel.replace(/$/, ' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.keys(x).forEach(function(k, i) {
|
||||||
|
var val = x[k],
|
||||||
|
handler = handlers[typeOf(val)];
|
||||||
|
|
||||||
|
if ('undefined' === typeof val) {
|
||||||
|
// the user should do
|
||||||
|
// delete obj.key
|
||||||
|
// and not
|
||||||
|
// obj.key = undefined
|
||||||
|
// but we'll error on the side of caution
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!handler) {
|
||||||
|
throw new Error('what the crap: ' + typeOf(val));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(inArray && i === 0)) {
|
||||||
|
output += '\n' + indentLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
output += k + ': ' + handler(val);
|
||||||
|
});
|
||||||
|
indentLevel = indentLevel.replace(/ /, '');
|
||||||
|
|
||||||
|
return output;
|
||||||
|
},
|
||||||
|
"function": function() {
|
||||||
|
// TODO this should throw or otherwise be ignored
|
||||||
|
return '[object Function]';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return trimWhitespace(handlers[typeOf(data)](data, true, true) + '\n');
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
window.yamlStringify = stringify;
|
||||||
|
module.exports.stringify = stringify;
|
||||||
|
}());
|
||||||
|
|
||||||
|
},{"remedial":2,"remove-trailing-spaces":3}],2:[function(require,module,exports){
|
||||||
|
/*jslint onevar: true, undef: true, nomen: true, eqeqeq: true, plusplus: true, bitwise: true, regexp: true, newcap: true, immed: true */
|
||||||
|
(function () {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var global = Function('return this')()
|
||||||
|
, classes = "Boolean Number String Function Array Date RegExp Object".split(" ")
|
||||||
|
, i
|
||||||
|
, name
|
||||||
|
, class2type = {}
|
||||||
|
;
|
||||||
|
|
||||||
|
for (i in classes) {
|
||||||
|
if (classes.hasOwnProperty(i)) {
|
||||||
|
name = classes[i];
|
||||||
|
class2type["[object " + name + "]"] = name.toLowerCase();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function typeOf(obj) {
|
||||||
|
return (null === obj || undefined === obj) ? String(obj) : class2type[Object.prototype.toString.call(obj)] || "object";
|
||||||
|
}
|
||||||
|
|
||||||
|
function isEmpty(o) {
|
||||||
|
var i, v;
|
||||||
|
if (typeOf(o) === 'object') {
|
||||||
|
for (i in o) { // fails jslint
|
||||||
|
v = o[i];
|
||||||
|
if (v !== undefined && typeOf(v) !== 'function') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!String.prototype.entityify) {
|
||||||
|
String.prototype.entityify = function () {
|
||||||
|
return this.replace(/&/g, "&").replace(/</g,
|
||||||
|
"<").replace(/>/g, ">");
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!String.prototype.quote) {
|
||||||
|
String.prototype.quote = function () {
|
||||||
|
var c, i, l = this.length, o = '"';
|
||||||
|
for (i = 0; i < l; i += 1) {
|
||||||
|
c = this.charAt(i);
|
||||||
|
if (c >= ' ') {
|
||||||
|
if (c === '\\' || c === '"') {
|
||||||
|
o += '\\';
|
||||||
|
}
|
||||||
|
o += c;
|
||||||
|
} else {
|
||||||
|
switch (c) {
|
||||||
|
case '\b':
|
||||||
|
o += '\\b';
|
||||||
|
break;
|
||||||
|
case '\f':
|
||||||
|
o += '\\f';
|
||||||
|
break;
|
||||||
|
case '\n':
|
||||||
|
o += '\\n';
|
||||||
|
break;
|
||||||
|
case '\r':
|
||||||
|
o += '\\r';
|
||||||
|
break;
|
||||||
|
case '\t':
|
||||||
|
o += '\\t';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
c = c.charCodeAt();
|
||||||
|
o += '\\u00' + Math.floor(c / 16).toString(16) +
|
||||||
|
(c % 16).toString(16);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return o + '"';
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!String.prototype.supplant) {
|
||||||
|
String.prototype.supplant = function (o) {
|
||||||
|
return this.replace(/{([^{}]*)}/g,
|
||||||
|
function (a, b) {
|
||||||
|
var r = o[b];
|
||||||
|
return typeof r === 'string' || typeof r === 'number' ? r : a;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!String.prototype.trim) {
|
||||||
|
String.prototype.trim = function () {
|
||||||
|
return this.replace(/^\s*(\S*(?:\s+\S+)*)\s*$/, "$1");
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// CommonJS / npm / Ender.JS
|
||||||
|
module.exports = {
|
||||||
|
typeOf: typeOf,
|
||||||
|
isEmpty: isEmpty
|
||||||
|
};
|
||||||
|
global.typeOf = global.typeOf || typeOf;
|
||||||
|
global.isEmpty = global.isEmpty || isEmpty;
|
||||||
|
}());
|
||||||
|
|
||||||
|
},{}],3:[function(require,module,exports){
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* removeTrailingSpaces
|
||||||
|
* Remove the trailing spaces from a string.
|
||||||
|
*
|
||||||
|
* @name removeTrailingSpaces
|
||||||
|
* @function
|
||||||
|
* @param {String} input The input string.
|
||||||
|
* @returns {String} The output string.
|
||||||
|
*/
|
||||||
|
|
||||||
|
module.exports = function removeTrailingSpaces(input) {
|
||||||
|
// TODO If possible, use a regex
|
||||||
|
return input.split("\n").map(function (x) {
|
||||||
|
return x.trimRight();
|
||||||
|
}).join("\n");
|
||||||
|
};
|
||||||
|
},{}]},{},[1]);
|
||||||
@@ -40,7 +40,6 @@ function viewTopicsList() {
|
|||||||
let rosdistro;
|
let rosdistro;
|
||||||
|
|
||||||
function viewTopic(topic) {
|
function viewTopic(topic) {
|
||||||
let counter = 0;
|
|
||||||
let index = '<a href=topics.html>Topics</a>';
|
let index = '<a href=topics.html>Topics</a>';
|
||||||
title.innerHTML = `${index}: ${topic}`;
|
title.innerHTML = `${index}: ${topic}`;
|
||||||
topicMessage.style.display = 'block';
|
topicMessage.style.display = 'block';
|
||||||
@@ -52,11 +51,10 @@ function viewTopic(topic) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
new ROSLIB.Topic({ ros: ros, name: topic }).subscribe(function(msg) {
|
new ROSLIB.Topic({ ros: ros, name: topic }).subscribe(function(msg) {
|
||||||
counter++;
|
|
||||||
document.title = topic;
|
document.title = topic;
|
||||||
if (mouseDown) return;
|
if (mouseDown) return;
|
||||||
|
|
||||||
if (msg.header && msg.header.stamp) {
|
if (msg.header.stamp) {
|
||||||
if (params.date || params.offset) {
|
if (params.date || params.offset) {
|
||||||
let date = new Date(msg.header.stamp.secs * 1e3 + msg.header.stamp.nsecs * 1e-6);
|
let date = new Date(msg.header.stamp.secs * 1e3 + msg.header.stamp.nsecs * 1e-6);
|
||||||
if (params.date) msg.header.date = date.toISOString();
|
if (params.date) msg.header.date = date.toISOString();
|
||||||
@@ -64,11 +62,7 @@ function viewTopic(topic) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let width = Number(params.width) || 100;
|
topicMessage.innerHTML = yamlStringify(msg); // JSON.stringify(msg, null, 4);
|
||||||
let indent = Number(params.indent) || 2;
|
|
||||||
let txt = YAML.stringify(msg, { lineWidth: width, indent: indent });
|
|
||||||
let html = `<div class=counter>${counter} received</div>${txt}`; // JSON.stringify(msg, null, 4);
|
|
||||||
topicMessage.innerHTML = html;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<script src="js/roslib.js"></script>
|
<script src="js/roslib.js"></script>
|
||||||
<link rel="icon" href="data:,"> <!-- make chrome don't request icon -->
|
<link rel="icon" href="data:,"> <!-- make chrome don't request icon -->
|
||||||
<script type="module" src="js/topics.js"></script>
|
<script type="module" src="js/topics.js"></script>
|
||||||
<script src="js/yaml.js"></script>
|
<script src="js/json-to-pretty-yaml.js"></script>
|
||||||
<style>
|
<style>
|
||||||
#topics { line-height: 1.2em; }
|
#topics { line-height: 1.2em; }
|
||||||
#topic-view {
|
#topic-view {
|
||||||
@@ -15,7 +15,6 @@
|
|||||||
white-space: pre;
|
white-space: pre;
|
||||||
font-family: monospace;
|
font-family: monospace;
|
||||||
}
|
}
|
||||||
.counter { color: #b9b9b9; margin-bottom: 1em; }
|
|
||||||
#topic-type { font-family: monospace; font-size: 0.5em; vertical-align: super; font-weight: normal; }
|
#topic-type { font-family: monospace; font-size: 0.5em; vertical-align: super; font-weight: normal; }
|
||||||
.topic { font-family: monospace; }
|
.topic { font-family: monospace; }
|
||||||
body.closed { background-color: rgb(207, 207, 207); }
|
body.closed { background-color: rgb(207, 207, 207); }
|
||||||
|
|||||||
@@ -47,7 +47,6 @@ http://<hostname>/clover_blocks/?navigate_tolerance=0.5&sleep_time=0.1
|
|||||||
|
|
||||||
* `~running` ([*std_msgs/Bool*](http://docs.ros.org/noetic/api/std_msgs/html/msg/Bool.html)) – indicates if the program is currently running.
|
* `~running` ([*std_msgs/Bool*](http://docs.ros.org/noetic/api/std_msgs/html/msg/Bool.html)) – indicates if the program is currently running.
|
||||||
* `~block` ([*std_msgs/String*](http://docs.ros.org/noetic/api/std_msgs/html/msg/String.html)) – current executing block (maximum topic rate is limited).
|
* `~block` ([*std_msgs/String*](http://docs.ros.org/noetic/api/std_msgs/html/msg/String.html)) – current executing block (maximum topic rate is limited).
|
||||||
* `~print` ([*std_msgs/String*](http://docs.ros.org/noetic/api/std_msgs/html/msg/String.html)) – user program output messages (published in *print* blocks).
|
|
||||||
* `~error` ([*std_msgs/String*](http://docs.ros.org/noetic/api/std_msgs/html/msg/String.html)) – user program errors and exceptions.
|
* `~error` ([*std_msgs/String*](http://docs.ros.org/noetic/api/std_msgs/html/msg/String.html)) – user program errors and exceptions.
|
||||||
* `~prompt` ([*clover_blocks/Prompt*](msg/Prompt.msg)) – user input request (includes random request ID string).
|
* `~prompt` ([*clover_blocks/Prompt*](msg/Prompt.msg)) – user input request (includes random request ID string).
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<?xml version="1.0"?>
|
<?xml version="1.0"?>
|
||||||
<package format="2">
|
<package format="2">
|
||||||
<name>clover_blocks</name>
|
<name>clover_blocks</name>
|
||||||
<version>0.25.0</version>
|
<version>0.23.0</version>
|
||||||
<description>Blockly programming support for Clover</description>
|
<description>Blockly programming support for Clover</description>
|
||||||
<maintainer email="okalachev@gmail.com">Oleg Kalachev</maintainer>
|
<maintainer email="okalachev@gmail.com">Oleg Kalachev</maintainer>
|
||||||
<license>MIT</license>
|
<license>MIT</license>
|
||||||
|
|||||||
@@ -166,7 +166,7 @@ def load(req):
|
|||||||
return {'names': [], 'programs': [], 'message': str(e)}
|
return {'names': [], 'programs': [], 'message': str(e)}
|
||||||
|
|
||||||
|
|
||||||
name_regexp = re.compile(r'^[a-zA-Z1-9-_.]{0,30}$')
|
name_regexp = re.compile(r'^[a-zA-Z-_.]{0,20}$')
|
||||||
|
|
||||||
def store(req):
|
def store(req):
|
||||||
if not name_regexp.match(req.name):
|
if not name_regexp.match(req.name):
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ const COLOR_GPIO = 200;
|
|||||||
const DOCS_URL = 'https://clover.coex.tech/en/blocks.html';
|
const DOCS_URL = 'https://clover.coex.tech/en/blocks.html';
|
||||||
|
|
||||||
var frameIds = [["body", "BODY"], ["markers map", "ARUCO_MAP"], ["marker", "ARUCO"], ["last navigate target", "NAVIGATE_TARGET"], ["map", "MAP"]];
|
var frameIds = [["body", "BODY"], ["markers map", "ARUCO_MAP"], ["marker", "ARUCO"], ["last navigate target", "NAVIGATE_TARGET"], ["map", "MAP"]];
|
||||||
var frameIdsWithTerrain = frameIds.concat([["terrain", "TERRAIN"]]);
|
|
||||||
|
|
||||||
function considerFrameId(e) {
|
function considerFrameId(e) {
|
||||||
if (!(e instanceof Blockly.Events.Change || e instanceof Blockly.Events.Create)) return;
|
if (!(e instanceof Blockly.Events.Change || e instanceof Blockly.Events.Create)) return;
|
||||||
@@ -23,7 +22,7 @@ function considerFrameId(e) {
|
|||||||
var frameId = this.getFieldValue('FRAME_ID');
|
var frameId = this.getFieldValue('FRAME_ID');
|
||||||
// set appropriate coordinates labels
|
// set appropriate coordinates labels
|
||||||
if (this.getInput('X')) { // block has x-y-z fields
|
if (this.getInput('X')) { // block has x-y-z fields
|
||||||
if (frameId == 'BODY' || frameId == 'NAVIGATE_TARGET' || frameId == 'BASE_LINK' || frameId == 'TERRAIN') {
|
if (frameId == 'BODY' || frameId == 'NAVIGATE_TARGET' || frameId == 'BASE_LINK') {
|
||||||
this.getInput('X').fieldRow[0].setValue('forward');
|
this.getInput('X').fieldRow[0].setValue('forward');
|
||||||
this.getInput('Y').fieldRow[0].setValue('left');
|
this.getInput('Y').fieldRow[0].setValue('left');
|
||||||
this.getInput('Z').fieldRow[0].setValue('up');
|
this.getInput('Z').fieldRow[0].setValue('up');
|
||||||
@@ -60,8 +59,8 @@ function updateSetpointBlock(e) {
|
|||||||
this.getInput('VY').setVisible(velocity);
|
this.getInput('VY').setVisible(velocity);
|
||||||
this.getInput('VZ').setVisible(velocity);
|
this.getInput('VZ').setVisible(velocity);
|
||||||
this.getInput('YAW').setVisible(attitude);
|
this.getInput('YAW').setVisible(attitude);
|
||||||
this.getInput('ROLL').setVisible(attitude);
|
|
||||||
this.getInput('PITCH').setVisible(attitude);
|
this.getInput('PITCH').setVisible(attitude);
|
||||||
|
this.getInput('ROLL').setVisible(attitude);
|
||||||
this.getInput('THRUST').setVisible(attitude);
|
this.getInput('THRUST').setVisible(attitude);
|
||||||
this.getInput('RELATIVE_TO').setVisible(type != 'RATES');
|
this.getInput('RELATIVE_TO').setVisible(type != 'RATES');
|
||||||
|
|
||||||
@@ -74,7 +73,7 @@ function updateSetpointBlock(e) {
|
|||||||
|
|
||||||
Blockly.Blocks['navigate'] = {
|
Blockly.Blocks['navigate'] = {
|
||||||
init: function () {
|
init: function () {
|
||||||
let navFrameId = frameIdsWithTerrain.slice();
|
let navFrameId = frameIds.slice();
|
||||||
navFrameId.push(['global', 'GLOBAL_LOCAL'])
|
navFrameId.push(['global', 'GLOBAL_LOCAL'])
|
||||||
navFrameId.push(['global, WGS 84 alt.', 'GLOBAL'])
|
navFrameId.push(['global, WGS 84 alt.', 'GLOBAL'])
|
||||||
this.appendDummyInput()
|
this.appendDummyInput()
|
||||||
@@ -164,14 +163,14 @@ Blockly.Blocks['setpoint'] = {
|
|||||||
this.appendValueInput("VZ")
|
this.appendValueInput("VZ")
|
||||||
.setCheck("Number")
|
.setCheck("Number")
|
||||||
.appendField("vz");
|
.appendField("vz");
|
||||||
this.appendValueInput("ROLL")
|
|
||||||
.setCheck("Number")
|
|
||||||
.appendField("roll")
|
|
||||||
.setVisible(false);
|
|
||||||
this.appendValueInput("PITCH")
|
this.appendValueInput("PITCH")
|
||||||
.setCheck("Number")
|
.setCheck("Number")
|
||||||
.appendField("pitch")
|
.appendField("pitch")
|
||||||
.setVisible(false);
|
.setVisible(false);
|
||||||
|
this.appendValueInput("ROLL")
|
||||||
|
.setCheck("Number")
|
||||||
|
.appendField("roll")
|
||||||
|
.setVisible(false);
|
||||||
this.appendValueInput("YAW")
|
this.appendValueInput("YAW")
|
||||||
.setCheck("Number")
|
.setCheck("Number")
|
||||||
.appendField("yaw")
|
.appendField("yaw")
|
||||||
@@ -214,7 +213,7 @@ Blockly.Blocks['get_position'] = {
|
|||||||
.appendField("current")
|
.appendField("current")
|
||||||
.appendField(new Blockly.FieldDropdown([["x", "X"], ["y", "Y"], ["z", "Z"], ["vx", "VX"], ["vy", "VY"], ["vz", "VZ"]]), "FIELD")
|
.appendField(new Blockly.FieldDropdown([["x", "X"], ["y", "Y"], ["z", "Z"], ["vx", "VX"], ["vy", "VY"], ["vz", "VZ"]]), "FIELD")
|
||||||
.appendField("relative to")
|
.appendField("relative to")
|
||||||
.appendField(new Blockly.FieldDropdown(frameIdsWithTerrain), "FRAME_ID");
|
.appendField(new Blockly.FieldDropdown(frameIds), "FRAME_ID");
|
||||||
this.appendValueInput("ID")
|
this.appendValueInput("ID")
|
||||||
.setCheck("Number")
|
.setCheck("Number")
|
||||||
.appendField("with ID")
|
.appendField("with ID")
|
||||||
@@ -248,7 +247,7 @@ Blockly.Blocks['get_attitude'] = {
|
|||||||
init: function () {
|
init: function () {
|
||||||
this.appendDummyInput()
|
this.appendDummyInput()
|
||||||
.appendField("current")
|
.appendField("current")
|
||||||
.appendField(new Blockly.FieldDropdown([["roll", "ROLL"], ["pitch", "PITCH"], ["roll rate", "ROLL_RATE"], ["pitch rate", "PITCH_RATE"], ["yaw rate", "YAW_RATE"]]), "FIELD");
|
.appendField(new Blockly.FieldDropdown([["pitch", "PITCH"], ["roll", "ROLL"], ["pitch rate", "PITCH_RATE"], ["roll rate", "ROLL_RATE"], ["yaw rate", "YAW_RATE"]]), "FIELD");
|
||||||
this.setOutput(true, "Number");
|
this.setOutput(true, "Number");
|
||||||
this.setColour(COLOR_STATE);
|
this.setColour(COLOR_STATE);
|
||||||
this.setTooltip("Returns current orientation or angle rates in degree or degree per second (not radian).");
|
this.setTooltip("Returns current orientation or angle rates in degree or degree per second (not radian).");
|
||||||
@@ -269,19 +268,6 @@ Blockly.Blocks['voltage'] = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Blockly.Blocks['get_rc'] = {
|
|
||||||
init: function () {
|
|
||||||
this.appendDummyInput()
|
|
||||||
.appendField("RC channel")
|
|
||||||
this.appendValueInput("CHANNEL")
|
|
||||||
.setCheck("Number");
|
|
||||||
this.setInputsInline(true);
|
|
||||||
this.setOutput(true, "Number");
|
|
||||||
this.setColour(COLOR_STATE);
|
|
||||||
this.setTooltip("Returns current RC channel value.");
|
|
||||||
this.setHelpUrl(DOCS_URL + '#' + this.type);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Blockly.Blocks['armed'] = {
|
Blockly.Blocks['armed'] = {
|
||||||
init: function () {
|
init: function () {
|
||||||
@@ -523,7 +509,7 @@ Blockly.Blocks['distance'] = {
|
|||||||
.appendField("z");
|
.appendField("z");
|
||||||
this.appendDummyInput()
|
this.appendDummyInput()
|
||||||
.appendField("relative to")
|
.appendField("relative to")
|
||||||
.appendField(new Blockly.FieldDropdown([["markers map", "ARUCO_MAP"], ["marker", "ARUCO"], ["last navigate target", "NAVIGATE_TARGET"], ["terrain", "TERRAIN"]]), "FRAME_ID");
|
.appendField(new Blockly.FieldDropdown([["markers map", "ARUCO_MAP"], ["marker", "ARUCO"], ["last navigate target", "NAVIGATE_TARGET"]]), "FRAME_ID");
|
||||||
this.appendValueInput("ID")
|
this.appendValueInput("ID")
|
||||||
.setCheck("Number")
|
.setCheck("Number")
|
||||||
.appendField("with ID")
|
.appendField("with ID")
|
||||||
|
|||||||
@@ -69,8 +69,8 @@
|
|||||||
<value name="VX"><shadow type="math_number"><field name="NUM">0</field></shadow></value>
|
<value name="VX"><shadow type="math_number"><field name="NUM">0</field></shadow></value>
|
||||||
<value name="VY"><shadow type="math_number"><field name="NUM">0</field></shadow></value>
|
<value name="VY"><shadow type="math_number"><field name="NUM">0</field></shadow></value>
|
||||||
<value name="VZ"><shadow type="math_number"><field name="NUM">0</field></shadow></value>
|
<value name="VZ"><shadow type="math_number"><field name="NUM">0</field></shadow></value>
|
||||||
<value name="ROLL"><shadow type="math_number"><field name="NUM">0</field></shadow></value>
|
|
||||||
<value name="PITCH"><shadow type="math_number"><field name="NUM">0</field></shadow></value>
|
<value name="PITCH"><shadow type="math_number"><field name="NUM">0</field></shadow></value>
|
||||||
|
<value name="ROLL"><shadow type="math_number"><field name="NUM">0</field></shadow></value>
|
||||||
<value name="YAW"><shadow type="math_number"><field name="NUM">0</field></shadow></value>
|
<value name="YAW"><shadow type="math_number"><field name="NUM">0</field></shadow></value>
|
||||||
<value name="THRUST"><shadow type="math_number"><field name="NUM">0.5</field></shadow></value>
|
<value name="THRUST"><shadow type="math_number"><field name="NUM">0.5</field></shadow></value>
|
||||||
<value name="ID"><shadow type="math_number"><field name="NUM">0</field></shadow></value>
|
<value name="ID"><shadow type="math_number"><field name="NUM">0</field></shadow></value>
|
||||||
@@ -100,9 +100,6 @@
|
|||||||
<block type="mode"></block>
|
<block type="mode"></block>
|
||||||
<block type="armed"></block>
|
<block type="armed"></block>
|
||||||
<block type="voltage"></block>
|
<block type="voltage"></block>
|
||||||
<block type="get_rc">
|
|
||||||
<value name="CHANNEL"><shadow type="math_number"><field name="NUM">0</field></shadow></value>
|
|
||||||
</block>
|
|
||||||
</category>
|
</category>
|
||||||
<category name="LED" colour="#02d754">
|
<category name="LED" colour="#02d754">
|
||||||
<block type="set_effect">
|
<block type="set_effect">
|
||||||
|
|||||||
@@ -81,10 +81,7 @@ function generateROSDefinitions() {
|
|||||||
code += `get_telemetry = rospy.ServiceProxy('get_telemetry', srv.GetTelemetry)\n`;
|
code += `get_telemetry = rospy.ServiceProxy('get_telemetry', srv.GetTelemetry)\n`;
|
||||||
code += `navigate = rospy.ServiceProxy('navigate', srv.Navigate)\n`;
|
code += `navigate = rospy.ServiceProxy('navigate', srv.Navigate)\n`;
|
||||||
if (rosDefinitions.navigateGlobal) {
|
if (rosDefinitions.navigateGlobal) {
|
||||||
code += `navigate_global = rospy.ServiceProxy('navigate_global', srv.NavigateGlobal)\n`;
|
code += `navigate_global = rospy.ServiceProxy('navigate_global', srv.NavigateGlobal)\n`;
|
||||||
}
|
|
||||||
if (rosDefinitions.setYaw) {
|
|
||||||
code += `set_yaw = rospy.ServiceProxy('set_yaw', srv.SetYaw)\n`;
|
|
||||||
}
|
}
|
||||||
if (rosDefinitions.setVelocity) {
|
if (rosDefinitions.setVelocity) {
|
||||||
code += `set_velocity = rospy.ServiceProxy('set_velocity', srv.SetVelocity)\n`;
|
code += `set_velocity = rospy.ServiceProxy('set_velocity', srv.SetVelocity)\n`;
|
||||||
@@ -279,11 +276,10 @@ Blockly.Python.angle = function(block) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Blockly.Python.set_yaw = function(block) {
|
Blockly.Python.set_yaw = function(block) {
|
||||||
rosDefinitions.setYaw = true;
|
|
||||||
simpleOffboard();
|
simpleOffboard();
|
||||||
let yaw = Blockly.Python.valueToCode(block, 'YAW', Blockly.Python.ORDER_NONE);
|
let yaw = Blockly.Python.valueToCode(block, 'YAW', Blockly.Python.ORDER_NONE);
|
||||||
let frameId = buildFrameId(block);
|
let frameId = buildFrameId(block);
|
||||||
let code = `set_yaw(yaw=${yaw}, frame_id=${frameId})\n`;
|
let code = `navigate(x=float('nan'), y=float('nan'), z=float('nan'), yaw=${yaw}, frame_id=${frameId})\n`;
|
||||||
if (block.getFieldValue('WAIT') == 'TRUE') {
|
if (block.getFieldValue('WAIT') == 'TRUE') {
|
||||||
rosDefinitions.waitYaw = true;
|
rosDefinitions.waitYaw = true;
|
||||||
simpleOffboard();
|
simpleOffboard();
|
||||||
@@ -332,11 +328,11 @@ Blockly.Python.setpoint = function(block) {
|
|||||||
} else if (type == 'ATTITUDE') {
|
} else if (type == 'ATTITUDE') {
|
||||||
rosDefinitions.setAttitude = true;
|
rosDefinitions.setAttitude = true;
|
||||||
simpleOffboard();
|
simpleOffboard();
|
||||||
return `set_attitude(roll=${roll}, pitch=${pitch}, yaw=${yaw}, thrust=${thrust}, frame_id=${frameId})\n`;
|
return `set_attitude(pitch=${pitch}, roll=${roll}, yaw=${yaw}, thrust=${thrust}, frame_id=${frameId})\n`;
|
||||||
} else if (type == 'RATES') {
|
} else if (type == 'RATES') {
|
||||||
rosDefinitions.setRates = true;
|
rosDefinitions.setRates = true;
|
||||||
simpleOffboard();
|
simpleOffboard();
|
||||||
return `set_rates(roll_rate=${roll}, pitch_rate=${pitch}, yaw_rate=${yaw}, thrust=${thrust})\n`;
|
return `set_rates(pitch_rate=${pitch}, roll_rate=${roll}, yaw_rate=${yaw}, thrust=${thrust})\n`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -402,12 +398,6 @@ Blockly.Python.voltage = function(block) {
|
|||||||
return [code, Blockly.Python.ORDER_FUNCTION_CALL];
|
return [code, Blockly.Python.ORDER_FUNCTION_CALL];
|
||||||
}
|
}
|
||||||
|
|
||||||
Blockly.Python.get_rc = function(block) {
|
|
||||||
Blockly.Python.definitions_['import_rcin'] = 'from mavros_msgs.msg import RCIn';
|
|
||||||
var channel = Blockly.Python.valueToCode(block, 'CHANNEL', Blockly.Python.ORDER_NONE);
|
|
||||||
return [`rospy.wait_for_message('mavros/rc/in', RCIn).channels[${channel}]`, Blockly.Python.ORDER_FUNCTION_CALL]
|
|
||||||
}
|
|
||||||
|
|
||||||
function parseColor(color) {
|
function parseColor(color) {
|
||||||
return {
|
return {
|
||||||
r: parseInt(color.substr(2, 2), 16),
|
r: parseInt(color.substr(2, 2), 16),
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<package format="2">
|
<package format="2">
|
||||||
<name>clover_description</name>
|
<name>clover_description</name>
|
||||||
<version>0.25.0</version>
|
<version>0.23.0</version>
|
||||||
<description>The clover_description package provides URDF models of the Clover series of quadcopters.</description>
|
<description>The clover_description package provides URDF models of the Clover series of quadcopters.</description>
|
||||||
|
|
||||||
<maintainer email="sfalexrog@gmail.com">Alexey Rogachevskiy</maintainer>
|
<maintainer email="sfalexrog@gmail.com">Alexey Rogachevskiy</maintainer>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<robot name="rpi_camera" xmlns:xacro="http://ros.org/wiki/xacro">
|
<robot name="rpi_camera" xmlns:xacro="http://ros.org/wiki/xacro">
|
||||||
<xacro:include filename="../camera_sensor.urdf.xacro"/>
|
<xacro:include filename="../camera_sensor.urdf.xacro"/>
|
||||||
|
|
||||||
<xacro:macro name="distance_sensor" params="name:=lidar_vl53l1x parent x:=0 y:=0 z:=0 roll:=0 pitch:=0 yaw:=0 range_min:=0.01 range_max:=4.0 resolution:=0.01 rate:=10 fov:=0.471239">
|
<xacro:macro name="distance_sensor" params="name:=lidar_vl53l1x parent x:=0 y:=0 z:=0 roll:=0 pitch:=0 yaw:=0 range_min:=0.01 range_max:=4.0 resolution:=0.01 rate:=10">
|
||||||
<joint name="${name}_joint" type="fixed">
|
<joint name="${name}_joint" type="fixed">
|
||||||
<origin xyz="${x} ${y} ${z}"
|
<origin xyz="${x} ${y} ${z}"
|
||||||
rpy="${roll} ${pitch} ${yaw}"/>
|
rpy="${roll} ${pitch} ${yaw}"/>
|
||||||
@@ -58,7 +58,7 @@
|
|||||||
<topicName>/rangefinder/range</topicName>
|
<topicName>/rangefinder/range</topicName>
|
||||||
<frameName>rangefinder</frameName>
|
<frameName>rangefinder</frameName>
|
||||||
<radiation>infrared</radiation>
|
<radiation>infrared</radiation>
|
||||||
<fov>${fov}</fov>
|
<fov>0.01</fov>
|
||||||
<gaussianNoise>0.001</gaussianNoise>
|
<gaussianNoise>0.001</gaussianNoise>
|
||||||
<updateRate>${rate}</updateRate>
|
<updateRate>${rate}</updateRate>
|
||||||
<min_distance>${range_min}</min_distance>
|
<min_distance>${range_min}</min_distance>
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ The simulation may be configured by a set of arguments:
|
|||||||
|
|
||||||
* `mav_id` (*integer*, default: *0*) - MAVLink identifier of the vehicle. **Note**: Multi-vehicle simulation is possible, but requires extensive changes to launch files;
|
* `mav_id` (*integer*, default: *0*) - MAVLink identifier of the vehicle. **Note**: Multi-vehicle simulation is possible, but requires extensive changes to launch files;
|
||||||
* `est` (*string*, default: *lpe*, possible values: *lpe*, *ekf2*) - PX4 estimator selection. Note that this may be overriden in the startup scripts for your craft;
|
* `est` (*string*, default: *lpe*, possible values: *lpe*, *ekf2*) - PX4 estimator selection. Note that this may be overriden in the startup scripts for your craft;
|
||||||
* `vehicle` (*string*, default: *clover*) - PX4 vehicle name. Depending on this parameter, different PX4 presets will be loaded.
|
* `vehicle` (*string*, default: *clover*) - PX4 vehicle name. Depending on this parameter, different PX4 presets will be loaded. **Note**: The default value, *clover*, requires you to use [Clover-specific PX4 branch](https://github.com/CopterExpress/Firmware/tree/v1.10.1-clever);
|
||||||
* `main_camera` (*boolean*, default: *true*) - controls whether the drone will have a vision position estimation camera;
|
* `main_camera` (*boolean*, default: *true*) - controls whether the drone will have a vision position estimation camera;
|
||||||
* `rangefinder` (*boolean*, default: *true*) - controls whether the drone will have a laser rangefinder;
|
* `rangefinder` (*boolean*, default: *true*) - controls whether the drone will have a laser rangefinder;
|
||||||
* `led` (*boolean*, default: *true*) - controls whether the drone will have a programmable LED strip;
|
* `led` (*boolean*, default: *true*) - controls whether the drone will have a programmable LED strip;
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ param set-default EKF2_OF_DELAY 0
|
|||||||
param set-default EKF2_OF_QMIN 10
|
param set-default EKF2_OF_QMIN 10
|
||||||
param set-default EKF2_OF_N_MIN 0.05
|
param set-default EKF2_OF_N_MIN 0.05
|
||||||
param set-default EKF2_OF_N_MAX 0.2
|
param set-default EKF2_OF_N_MAX 0.2
|
||||||
param set-default EKF2_HGT_MODE 3 # 0 = baro, 1 = gps, 2 = range, 3 = vision
|
param set-default EKF2_HGT_MODE 2 # 0 = baro, 1 = gps, 2 = range, 3 = vision
|
||||||
param set-default EKF2_EVA_NOISE 0.1
|
param set-default EKF2_EVA_NOISE 0.1
|
||||||
param set-default EKF2_EVP_NOISE 0.1
|
param set-default EKF2_EVP_NOISE 0.1
|
||||||
param set-default EKF2_EV_DELAY 0
|
param set-default EKF2_EV_DELAY 0
|
||||||
|
|||||||
@@ -1,16 +0,0 @@
|
|||||||
material red_circle
|
|
||||||
{
|
|
||||||
technique
|
|
||||||
{
|
|
||||||
pass
|
|
||||||
{
|
|
||||||
scene_blend alpha_blend
|
|
||||||
texture_unit
|
|
||||||
{
|
|
||||||
texture red_circle.png
|
|
||||||
filtering none
|
|
||||||
scale 1.0 1.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
Before Width: | Height: | Size: 7.9 KiB |
@@ -1,13 +0,0 @@
|
|||||||
<?xml version="1.0"?>
|
|
||||||
<model>
|
|
||||||
<name>Red Circle</name>
|
|
||||||
<version>1.0</version>
|
|
||||||
<sdf version="1.5">red_circle.sdf</sdf>
|
|
||||||
<author>
|
|
||||||
<name>Oleg Kalachev</name>
|
|
||||||
<email>okalachev@gmail.com</email>
|
|
||||||
</author>
|
|
||||||
<description>
|
|
||||||
Red circle of size 40 cm on the floor for recognizing by a drone
|
|
||||||
</description>
|
|
||||||
</model>
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
<?xml version="1.0"?>
|
|
||||||
<sdf version="1.5">
|
|
||||||
<model name="red_circle">
|
|
||||||
<static>true</static>
|
|
||||||
<link name="red_circle_link">
|
|
||||||
<pose>0 0 1e-3 0 0 0</pose>
|
|
||||||
<visual name="red_circle_texture">
|
|
||||||
<cast_shadows>false</cast_shadows>
|
|
||||||
<geometry>
|
|
||||||
<box>
|
|
||||||
<size>0.4 0.4 1e-3</size>
|
|
||||||
</box>
|
|
||||||
</geometry>
|
|
||||||
<material>
|
|
||||||
<script>
|
|
||||||
<uri>model://red_circle/materials/scripts</uri>
|
|
||||||
<uri>model://red_circle/materials/textures</uri>
|
|
||||||
<name>red_circle</name>
|
|
||||||
</script>
|
|
||||||
</material>
|
|
||||||
</visual>
|
|
||||||
</link>
|
|
||||||
</model>
|
|
||||||
</sdf>
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
|
|
||||||
<title>
|
|
||||||
red_circle
|
|
||||||
</title><g fill="red">
|
|
||||||
<circle cx="10.05" cy="10.05" r="9.9"/>
|
|
||||||
</g></svg>
|
|
||||||
|
Before Width: | Height: | Size: 221 B |
@@ -1,6 +1,6 @@
|
|||||||
<package format="3">
|
<package format="2">
|
||||||
<name>clover_simulation</name>
|
<name>clover_simulation</name>
|
||||||
<version>0.25.0</version>
|
<version>0.23.0</version>
|
||||||
<description>The clover_simulation package provides worlds and launch files for Gazebo.</description>
|
<description>The clover_simulation package provides worlds and launch files for Gazebo.</description>
|
||||||
|
|
||||||
<maintainer email="okalachev@gmail.com">Oleg Kalachev</maintainer>
|
<maintainer email="okalachev@gmail.com">Oleg Kalachev</maintainer>
|
||||||
@@ -22,8 +22,6 @@
|
|||||||
<depend>gazebo_ros</depend>
|
<depend>gazebo_ros</depend>
|
||||||
<depend>gazebo_plugins</depend>
|
<depend>gazebo_plugins</depend>
|
||||||
<depend>rospy</depend>
|
<depend>rospy</depend>
|
||||||
<depend condition="$ROS_PYTHON_VERSION == 2">python-docopt</depend>
|
|
||||||
<depend condition="$ROS_PYTHON_VERSION == 3">python3-docopt</depend>
|
|
||||||
|
|
||||||
<export>
|
<export>
|
||||||
<gazebo_ros gazebo_media_path="${prefix}"/>
|
<gazebo_ros gazebo_media_path="${prefix}"/>
|
||||||
|
|||||||
@@ -65,8 +65,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
role = (ros::this_node::getName() == "/gazebo") ? Role::Server : Role::Client;
|
role = (ros::this_node::getName() == "/gazebo") ? Role::Server : Role::Client;
|
||||||
ROS_INFO_NAMED(("LedController_" + robotNamespace).c_str(), "LedController has started (as %s) in namespace '%s'",
|
ROS_INFO_NAMED(("LedController_" + robotNamespace).c_str(), "LedController has started (as %s)", role == Role::Client ? "client" : "server");
|
||||||
role == Role::Client ? "client" : "server", robotNamespace.c_str());
|
|
||||||
|
|
||||||
nh.reset(new ros::NodeHandle(robotNamespace));
|
nh.reset(new ros::NodeHandle(robotNamespace));
|
||||||
|
|
||||||
@@ -110,6 +109,7 @@ LedController& get(std::string robotNamespace)
|
|||||||
std::lock_guard<std::mutex> lock(controllerMutex);
|
std::lock_guard<std::mutex> lock(controllerMutex);
|
||||||
auto it = controllers.find(robotNamespace);
|
auto it = controllers.find(robotNamespace);
|
||||||
if (it == controllers.end()) {
|
if (it == controllers.end()) {
|
||||||
|
gzwarn << "Creating new LED controller for namespace " << robotNamespace << "\n";
|
||||||
controllers[robotNamespace].reset(new LedController(robotNamespace));
|
controllers[robotNamespace].reset(new LedController(robotNamespace));
|
||||||
return *controllers[robotNamespace];
|
return *controllers[robotNamespace];
|
||||||
}
|
}
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 73 KiB |
|
Before Width: | Height: | Size: 95 KiB |
|
Before Width: | Height: | Size: 85 KiB |
|
Before Width: | Height: | Size: 201 KiB |
|
Before Width: | Height: | Size: 368 KiB |
|
Before Width: | Height: | Size: 383 KiB |
|
Before Width: | Height: | Size: 322 KiB |
|
Before Width: | Height: | Size: 75 KiB |
|
Before Width: | Height: | Size: 325 KiB |
|
Before Width: | Height: | Size: 84 KiB |
|
Before Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 326 KiB |
@@ -36,7 +36,7 @@
|
|||||||
* [Optical Flow](optical_flow.md)
|
* [Optical Flow](optical_flow.md)
|
||||||
* [Autonomous flight (OFFBOARD)](simple_offboard.md)
|
* [Autonomous flight (OFFBOARD)](simple_offboard.md)
|
||||||
* [Coordinate systems (frames)](frames.md)
|
* [Coordinate systems (frames)](frames.md)
|
||||||
* [Code examples](snippets.md)
|
* [Code snippets](snippets.md)
|
||||||
* [Interfacing with a laser rangefinder](laser.md)
|
* [Interfacing with a laser rangefinder](laser.md)
|
||||||
* [LED strip](leds.md)
|
* [LED strip](leds.md)
|
||||||
* [Working with GPIO](gpio.md)
|
* [Working with GPIO](gpio.md)
|
||||||
@@ -57,7 +57,6 @@
|
|||||||
* [COEX Pix](coex_pix.md)
|
* [COEX Pix](coex_pix.md)
|
||||||
* [COEX PDB](coex_pdb.md)
|
* [COEX PDB](coex_pdb.md)
|
||||||
* [COEX GPS](coex_gps.md)
|
* [COEX GPS](coex_gps.md)
|
||||||
* [Using SSH keys](ssh_keys.md)
|
|
||||||
* [Guide on autonomous flight](auto_setup.md)
|
* [Guide on autonomous flight](auto_setup.md)
|
||||||
* [Hostname](hostname.md)
|
* [Hostname](hostname.md)
|
||||||
* [PX4 Simulation](sitl.md)
|
* [PX4 Simulation](sitl.md)
|
||||||
@@ -106,12 +105,6 @@
|
|||||||
* [Video contest](video_contest.md)
|
* [Video contest](video_contest.md)
|
||||||
* [Educational contests](educational_contests.md)
|
* [Educational contests](educational_contests.md)
|
||||||
* [Clover-based projects](projects.md)
|
* [Clover-based projects](projects.md)
|
||||||
* [Clover Cloud Platform](clover-cloud-platform.md)
|
|
||||||
* [Autonomous Racing Drone](djs_phoenix_chetak.md)
|
|
||||||
* [Motion Capture System](mocap_clover.md)
|
|
||||||
* [Swarm in Blocks 2](swarm_in_blocks_2.md)
|
|
||||||
* [Advanced Clover 2](advanced_clover_simulator_platform.md)
|
|
||||||
* [Network of charging stations](liceu128.md)
|
|
||||||
* [Swarm-in-blocks](swarm_in_blocks.md)
|
* [Swarm-in-blocks](swarm_in_blocks.md)
|
||||||
* [Obstacle avoidance using artificial potential fields method](obstacle-avoidance-potential-fields.md)
|
* [Obstacle avoidance using artificial potential fields method](obstacle-avoidance-potential-fields.md)
|
||||||
* [The Clover Rescue Project](clover-rescue-team.md)
|
* [The Clover Rescue Project](clover-rescue-team.md)
|
||||||
|
|||||||