Compare commits
4 Commits
md-indent-
...
clever4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
20a229a954 | ||
|
|
aae9eec42f | ||
|
|
6e4e25a2cb | ||
|
|
11555d7d70 |
22
.travis.yml
@@ -23,16 +23,7 @@ jobs:
|
|||||||
# Check if there are any cached images, copy them to our "images" directory
|
# Check if there are any cached images, copy them to our "images" directory
|
||||||
- if [ -n "$(ls -A imgcache/*.zip)" ]; then mkdir -p images && cp imgcache/*.zip images; fi
|
- if [ -n "$(ls -A imgcache/*.zip)" ]; then mkdir -p images && cp imgcache/*.zip images; fi
|
||||||
script:
|
script:
|
||||||
- if [[ -z ${TRAVIS_TAG} && "${TRAVIS_PULL_REQUEST}" != "false" ]]; then
|
- docker run --privileged --rm -v /dev:/dev -v $(pwd):/builder/repo -e TRAVIS_TAG="${TRAVIS_TAG}" ${DOCKER}
|
||||||
echo "Commit range is ${TRAVIS_COMMIT_RANGE}" &&
|
|
||||||
if [ $(git diff --name-only ${TRAVIS_COMMIT_RANGE} | grep -v ^"docs/" | wc -l) -eq 0 ]; then
|
|
||||||
echo " === Docs-only change; skipping build ===" &&
|
|
||||||
export SKIP_BUILD=true;
|
|
||||||
fi;
|
|
||||||
fi
|
|
||||||
- if [ -z ${SKIP_BUILD} ]; then
|
|
||||||
docker run --privileged --rm -v /dev:/dev -v $(pwd):/builder/repo -e TRAVIS_TAG="${TRAVIS_TAG}" ${DOCKER};
|
|
||||||
fi
|
|
||||||
before_cache:
|
before_cache:
|
||||||
- cp images/*.zip imgcache
|
- cp images/*.zip imgcache
|
||||||
before_deploy:
|
before_deploy:
|
||||||
@@ -76,6 +67,17 @@ jobs:
|
|||||||
verbose: true
|
verbose: true
|
||||||
on:
|
on:
|
||||||
branch: master
|
branch: master
|
||||||
|
deploy:
|
||||||
|
provider: pages
|
||||||
|
local-dir: _book
|
||||||
|
skip-cleanup: true
|
||||||
|
github-token: ${GITHUB_OAUTH_TOKEN}
|
||||||
|
keep-history: false
|
||||||
|
target-branch: master
|
||||||
|
repo: okalachev/cl4wip
|
||||||
|
verbose: true
|
||||||
|
on:
|
||||||
|
branch: clever4
|
||||||
- stage: Annotate
|
- stage: Annotate
|
||||||
name: Auto-generate changelog
|
name: Auto-generate changelog
|
||||||
language: python
|
language: python
|
||||||
|
|||||||
60
README.md
@@ -10,7 +10,7 @@ Copter Express has implemented a large number of different autonomous drone proj
|
|||||||
|
|
||||||
Use it to learn how to assemble, configure, pilot and program autonomous CLEVER drone.
|
Use it to learn how to assemble, configure, pilot and program autonomous CLEVER drone.
|
||||||
|
|
||||||
## Raspberry Pi image
|
## Preconfigured RPi 3 image
|
||||||
|
|
||||||
**Preconfigured image for Raspberry Pi 3 with installed and configured software, ready to fly, is available [in the Releases section](https://github.com/CopterExpress/clever/releases).**
|
**Preconfigured image for Raspberry Pi 3 with installed and configured software, ready to fly, is available [in the Releases section](https://github.com/CopterExpress/clever/releases).**
|
||||||
|
|
||||||
@@ -30,59 +30,49 @@ API description (in Russian) for autonomous flights is available [on GitBook](ht
|
|||||||
|
|
||||||
## Manual installation
|
## Manual installation
|
||||||
|
|
||||||
Install ROS Kinetic according to the [documentation](http://wiki.ros.org/kinetic/Installation), then [create a Catkin workspace](http://wiki.ros.org/catkin/Tutorials/create_a_workspace).
|
Install ROS Kinetic according to the [documentation](http://wiki.ros.org/kinetic/Installation).
|
||||||
|
|
||||||
Clone this repo to directory `~/catkin_ws/src/clever`:
|
Clone repo to directory `/home/pi/catkin_ws/src/clever`:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd ~/catkin_ws/src
|
cd ~/catkin_ws/src
|
||||||
git clone https://github.com/CopterExpress/clever.git clever
|
git clone https://github.com/CopterExpress/clever.git clever
|
||||||
```
|
```
|
||||||
|
|
||||||
All the required ROS packages (including `mavros` and `opencv`) can be installed using `rosdep`:
|
Build ROS packages:
|
||||||
|
|
||||||
```bash
|
|
||||||
cd ~/catkin_ws/
|
|
||||||
rosdep install -y --from-paths src --ignore-src
|
|
||||||
```
|
|
||||||
|
|
||||||
Build ROS packages (on memory constrained platforms you might be going to need to use `-j1` key):
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd ~/catkin_ws
|
cd ~/catkin_ws
|
||||||
catkin_make -j1
|
catkin_make -j1
|
||||||
```
|
```
|
||||||
|
|
||||||
To complete `mavros` install you'll need to install `geographiclib` datasets:
|
Enable systemd service `roscore` (if not enabled):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl https://raw.githubusercontent.com/mavlink/mavros/master/mavros/scripts/install_geographiclib_datasets.sh | sudo bash
|
sudo systemctl enable /home/pi/catkin_ws/src/clever/deploy/roscore.service
|
||||||
```
|
|
||||||
|
|
||||||
## Running
|
|
||||||
|
|
||||||
Enable systemd service `roscore` (if not running):
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo systemctl enable /home/<username>/catkin_ws/src/clever/deploy/roscore.service
|
|
||||||
sudo systemctl start roscore
|
sudo systemctl start roscore
|
||||||
```
|
```
|
||||||
|
|
||||||
To start connection to SITL, use:
|
Enable systemd service `clever`:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
roslaunch clever sitl.launch
|
sudo systemctl enable /home/pi/catkin_ws/src/clever/deploy/clever.service
|
||||||
```
|
|
||||||
|
|
||||||
To start connection to the flight controller, use:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
roslaunch clever clever.launch
|
|
||||||
```
|
|
||||||
|
|
||||||
Also, you can enable and start the systemd service:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo systemctl enable /home/<username>/catkin_ws/src/clever/deploy/clever.service
|
|
||||||
sudo systemctl start clever
|
sudo systemctl start clever
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
|
||||||
|
[ROS Kinetic](http://wiki.ros.org/kinetic).
|
||||||
|
|
||||||
|
Necessary ROS packages:
|
||||||
|
|
||||||
|
* `opencv3`
|
||||||
|
* `mavros`
|
||||||
|
* `rosbridge_suite`
|
||||||
|
* `web_video_server`
|
||||||
|
* `cv_camera`
|
||||||
|
* `nodelet`
|
||||||
|
* `dynamic_reconfigure`
|
||||||
|
* `bondcpp`, branch `master`
|
||||||
|
* `roslint`
|
||||||
|
* `rosserial`
|
||||||
|
|||||||
@@ -216,8 +216,4 @@ target_link_libraries(aruco_pose
|
|||||||
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/test_parser_pass.test)
|
|
||||||
add_rostest(test/test_parser_empty_map.test)
|
|
||||||
add_rostest(test/test_node_failure.test)
|
|
||||||
add_rostest(test/largemap.test)
|
|
||||||
endif()
|
endif()
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ See examples in [`map`](map/) directory.
|
|||||||
Command for running tests:
|
Command for running tests:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
catkin_make run_tests && catkin_test_results
|
rostest aruco_pose basic.test
|
||||||
```
|
```
|
||||||
|
|
||||||
## Copyright
|
## Copyright
|
||||||
|
|||||||
@@ -1,100 +0,0 @@
|
|||||||
0 0.33 0.0 9.0 0 0 0 0
|
|
||||||
1 0.33 1.0 9.0 0 0 0 0
|
|
||||||
2 0.33 2.0 9.0 0 0 0 0
|
|
||||||
3 0.33 3.0 9.0 0 0 0 0
|
|
||||||
4 0.33 4.0 9.0 0 0 0 0
|
|
||||||
5 0.33 5.0 9.0 0 0 0 0
|
|
||||||
6 0.33 6.0 9.0 0 0 0 0
|
|
||||||
7 0.33 7.0 9.0 0 0 0 0
|
|
||||||
8 0.33 8.0 9.0 0 0 0 0
|
|
||||||
9 0.33 9.0 9.0 0 0 0 0
|
|
||||||
10 0.33 0.0 8.0 0 0 0 0
|
|
||||||
11 0.33 1.0 8.0 0 0 0 0
|
|
||||||
12 0.33 2.0 8.0 0 0 0 0
|
|
||||||
13 0.33 3.0 8.0 0 0 0 0
|
|
||||||
14 0.33 4.0 8.0 0 0 0 0
|
|
||||||
15 0.33 5.0 8.0 0 0 0 0
|
|
||||||
16 0.33 6.0 8.0 0 0 0 0
|
|
||||||
#17 0.33 7.0 8.0 0 0 0 0
|
|
||||||
18 0.33 8.0 8.0 0 0 0 0
|
|
||||||
19 0.33 9.0 8.0 0 0 0 0
|
|
||||||
20 0.33 0.0 7.0 0 0 0 0
|
|
||||||
21 0.33 1.0 7.0 0 0 0 0
|
|
||||||
22 0.33 2.0 7.0 0 0 0 0
|
|
||||||
23 0.33 3.0 7.0 0 0 0 0
|
|
||||||
24 0.33 4.0 7.0 0 0 0 0
|
|
||||||
25 0.33 5.0 7.0 0 0 0 0
|
|
||||||
26 0.33 6.0 7.0 0 0 0 0
|
|
||||||
27 0.33 7.0 7.0 0 0 0 0
|
|
||||||
28 0.33 8.0 7.0 0 0 0 0
|
|
||||||
29 0.33 9.0 7.0 0 0 0 0
|
|
||||||
30 0.33 0.0 6.0 0 0 0 0
|
|
||||||
31 0.33 1.0 6.0 0 0 0 0
|
|
||||||
32 0.33 2.0 6.0 0 0 0 0
|
|
||||||
33 0.33 3.0 6.0 0 0 0 0
|
|
||||||
34 0.33 4.0 6.0 0 0 0 0
|
|
||||||
35 0.33 5.0 6.0 0 0 0 0
|
|
||||||
36 0.33 6.0 6.0 0 0 0 0
|
|
||||||
37 0.33 7.0 6.0 0 0 0 0
|
|
||||||
38 0.33 8.0 6.0 0 0 0 0
|
|
||||||
39 0.33 9.0 6.0 0 0 0 0
|
|
||||||
40 0.33 0.0 5.0 0 0 0 0
|
|
||||||
41 0.33 1.0 5.0 0 0 0 0
|
|
||||||
42 0.33 2.0 5.0 0 0 0 0
|
|
||||||
43 0.33 3.0 5.0 0 0 0 0
|
|
||||||
44 0.33 4.0 5.0 0 0 0 0
|
|
||||||
45 0.33 5.0 5.0 0 0 0 0
|
|
||||||
46 0.33 6.0 5.0 0 0 0 0
|
|
||||||
47 0.33 7.0 5.0 0 0 0 0
|
|
||||||
48 0.33 8.0 5.0 0 0 0 0
|
|
||||||
49 0.33 9.0 5.0 0 0 0 0
|
|
||||||
50 0.33 0.0 4.0 0 0 0 0
|
|
||||||
51 0.33 1.0 4.0 0 0 0 0
|
|
||||||
52 0.33 2.0 4.0 0 0 0 0
|
|
||||||
53 0.33 3.0 4.0 0 0 0 0
|
|
||||||
54 0.33 4.0 4.0 0 0 0 0
|
|
||||||
55 0.33 5.0 4.0 0 0 0 0
|
|
||||||
56 0.33 6.0 4.0 0 0 0 0
|
|
||||||
57 0.33 7.0 4.0 0 0 0 0
|
|
||||||
58 0.33 8.0 4.0 0 0 0 0
|
|
||||||
59 0.33 9.0 4.0 0 0 0 0
|
|
||||||
60 0.33 0.0 3.0 0 0 0 0
|
|
||||||
61 0.33 1.0 3.0 0 0 0 0
|
|
||||||
62 0.33 2.0 3.0 0 0 0 0
|
|
||||||
63 0.33 3.0 3.0 0 0 0 0
|
|
||||||
64 0.33 4.0 3.0 0 0 0 0
|
|
||||||
65 0.33 5.0 3.0 0 0 0 0
|
|
||||||
66 0.33 6.0 3.0 0 0 0 0
|
|
||||||
67 0.33 7.0 3.0 0 0 0 0
|
|
||||||
68 0.33 8.0 3.0 0 0 0 0
|
|
||||||
69 0.33 9.0 3.0 0 0 0 0
|
|
||||||
70 0.33 0.0 2.0 0 0 0 0
|
|
||||||
71 0.33 1.0 2.0 0 0 0 0
|
|
||||||
72 0.33 2.0 2.0 0 0 0 0
|
|
||||||
73 0.33 3.0 2.0 0 0 0 0
|
|
||||||
74 0.33 4.0 2.0 0 0 0 0
|
|
||||||
75 0.33 5.0 2.0 0 0 0 0
|
|
||||||
76 0.33 6.0 2.0 0 0 0 0
|
|
||||||
77 0.33 7.0 2.0 0 0 0 0
|
|
||||||
78 0.33 8.0 2.0 0 0 0 0
|
|
||||||
79 0.33 9.0 2.0 0 0 0 0
|
|
||||||
80 0.33 0.0 1.0 0 0 0 0
|
|
||||||
81 0.33 1.0 1.0 0 0 0 0
|
|
||||||
82 0.33 2.0 1.0 0 0 0 0
|
|
||||||
83 0.33 3.0 1.0 0 0 0 0
|
|
||||||
84 0.33 4.0 1.0 0 0 0 0
|
|
||||||
85 0.33 5.0 1.0 0 0 0 0
|
|
||||||
86 0.33 6.0 1.0 0 0 0 0
|
|
||||||
87 0.33 7.0 1.0 0 0 0 0
|
|
||||||
88 0.33 8.0 1.0 0 0 0 0
|
|
||||||
89 0.33 9.0 1.0 0 0 0 0
|
|
||||||
90 0.33 0.0 0.0 0 0 0 0
|
|
||||||
91 0.33 1.0 0.0 0 0 0 0
|
|
||||||
92 0.33 2.0 0.0 0 0 0 0
|
|
||||||
93 0.33 3.0 0.0 0 0 0 0
|
|
||||||
94 0.33 4.0 0.0 0 0 0 0
|
|
||||||
95 0.33 5.0 0.0 0 0 0 0
|
|
||||||
96 0.33 6.0 0.0 0 0 0 0
|
|
||||||
97 0.33 7.0 0.0 0 0 0 0
|
|
||||||
98 0.33 8.0 0.0 0 0 0 0
|
|
||||||
99 0.33 9.0 0.0 0 0 0 0
|
|
||||||
@@ -30,7 +30,6 @@
|
|||||||
<depend>rostest</depend>
|
<depend>rostest</depend>
|
||||||
|
|
||||||
<test_depend>image_publisher</test_depend>
|
<test_depend>image_publisher</test_depend>
|
||||||
<test_depend>ros_pytest</test_depend>
|
|
||||||
|
|
||||||
<!-- The export tag contains other, unspecified, tags -->
|
<!-- The export tag contains other, unspecified, tags -->
|
||||||
<export>
|
<export>
|
||||||
|
|||||||
@@ -18,7 +18,6 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <algorithm>
|
|
||||||
#include <ros/ros.h>
|
#include <ros/ros.h>
|
||||||
#include <nodelet/nodelet.h>
|
#include <nodelet/nodelet.h>
|
||||||
#include <pluginlib/class_list_macros.h>
|
#include <pluginlib/class_list_macros.h>
|
||||||
@@ -28,7 +27,6 @@
|
|||||||
#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/transform_broadcaster.h>
|
#include <tf2_ros/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 <message_filters/subscriber.h>
|
#include <message_filters/subscriber.h>
|
||||||
#include <message_filters/synchronizer.h>
|
#include <message_filters/synchronizer.h>
|
||||||
@@ -69,13 +67,11 @@ private:
|
|||||||
Mat camera_matrix_, dist_coeffs_;
|
Mat camera_matrix_, dist_coeffs_;
|
||||||
geometry_msgs::TransformStamped transform_;
|
geometry_msgs::TransformStamped transform_;
|
||||||
geometry_msgs::PoseWithCovarianceStamped pose_;
|
geometry_msgs::PoseWithCovarianceStamped pose_;
|
||||||
vector<geometry_msgs::TransformStamped> markers_transforms_;
|
|
||||||
tf2_ros::TransformBroadcaster br_;
|
tf2_ros::TransformBroadcaster br_;
|
||||||
tf2_ros::StaticTransformBroadcaster static_br_;
|
|
||||||
tf2_ros::Buffer tf_buffer_;
|
tf2_ros::Buffer tf_buffer_;
|
||||||
tf2_ros::TransformListener tf_listener_{tf_buffer_};
|
tf2_ros::TransformListener tf_listener_{tf_buffer_};
|
||||||
visualization_msgs::MarkerArray vis_array_;
|
visualization_msgs::MarkerArray vis_array_;
|
||||||
std::string known_tilt_, map_, markers_frame_, markers_parent_frame_;
|
std::string known_tilt_;
|
||||||
int image_width_, image_height_, image_margin_;
|
int image_width_, image_height_, image_margin_;
|
||||||
bool auto_flip_;
|
bool auto_flip_;
|
||||||
|
|
||||||
@@ -104,8 +100,6 @@ public:
|
|||||||
nh_priv_.param("image_width", image_width_, 2000);
|
nh_priv_.param("image_width", image_width_, 2000);
|
||||||
nh_priv_.param("image_height", image_height_, 2000);
|
nh_priv_.param("image_height", image_height_, 2000);
|
||||||
nh_priv_.param("image_margin", image_margin_, 200);
|
nh_priv_.param("image_margin", image_margin_, 200);
|
||||||
nh_priv_.param<std::string>("markers/frame_id", markers_parent_frame_, transform_.child_frame_id);
|
|
||||||
nh_priv_.param<std::string>("markers/child_frame_id_prefix", markers_frame_, "");
|
|
||||||
|
|
||||||
// createStripLine();
|
// createStripLine();
|
||||||
|
|
||||||
@@ -130,7 +124,6 @@ public:
|
|||||||
sync_.reset(new message_filters::Synchronizer<SyncPolicy>(SyncPolicy(10), image_sub_, info_sub_, markers_sub_));
|
sync_.reset(new message_filters::Synchronizer<SyncPolicy>(SyncPolicy(10), image_sub_, info_sub_, markers_sub_));
|
||||||
sync_->registerCallback(boost::bind(&ArucoMap::callback, this, _1, _2, _3));
|
sync_->registerCallback(boost::bind(&ArucoMap::callback, this, _1, _2, _3));
|
||||||
|
|
||||||
publishMarkersFrames();
|
|
||||||
publishMapImage();
|
publishMapImage();
|
||||||
vis_markers_pub_.publish(vis_array_);
|
vis_markers_pub_.publish(vis_array_);
|
||||||
|
|
||||||
@@ -277,50 +270,10 @@ publish_debug:
|
|||||||
|
|
||||||
std::istringstream s(line);
|
std::istringstream s(line);
|
||||||
|
|
||||||
// Read first character to see whether it's a comment
|
if (!(s >> id >> length >> x >> y >> z >> yaw >> pitch >> roll)) {
|
||||||
char first = 0;
|
ROS_ERROR("aruco_map: cannot parse line: %s", line.c_str());
|
||||||
if (!(s >> first)) {
|
|
||||||
// No non-whitespace characters, must be a blank line
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (first == '#') {
|
|
||||||
ROS_DEBUG("aruco_map: Skipping line as a comment: %s", line.c_str());
|
|
||||||
continue;
|
|
||||||
} else if (isdigit(first)) {
|
|
||||||
// Put the digit back into the stream
|
|
||||||
// Note that this is a non-modifying putback, so this should work with istreams
|
|
||||||
// (see https://en.cppreference.com/w/cpp/io/basic_istream/putback)
|
|
||||||
s.putback(first);
|
|
||||||
} else {
|
|
||||||
// Probably garbage data; inform user and throw an exception, possibly killing nodelet
|
|
||||||
ROS_FATAL("aruco_map: Malformed input: %s", line.c_str());
|
|
||||||
ros::shutdown();
|
|
||||||
throw std::runtime_error("Malformed input");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!(s >> id >> length >> x >> y)) {
|
|
||||||
ROS_ERROR("aruco_map: Not enough data in line: %s; "
|
|
||||||
"Each marker must have at least id, length, x, y fields", line.c_str());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// Be less strict about z, yaw, pitch roll
|
|
||||||
if (!(s >> z)) {
|
|
||||||
ROS_DEBUG("aruco_map: No z coordinate provided for marker %d, assuming 0", id);
|
|
||||||
z = 0;
|
|
||||||
}
|
|
||||||
if (!(s >> yaw)) {
|
|
||||||
ROS_DEBUG("aruco_map: No yaw provided for marker %d, assuming 0", id);
|
|
||||||
yaw = 0;
|
|
||||||
}
|
|
||||||
if (!(s >> pitch)) {
|
|
||||||
ROS_DEBUG("aruco_map: No pitch provided for marker %d, assuming 0", id);
|
|
||||||
pitch = 0;
|
|
||||||
}
|
|
||||||
if (!(s >> roll)) {
|
|
||||||
ROS_DEBUG("aruco_map: No roll provided for marker %d, assuming 0", id);
|
|
||||||
roll = 0;
|
|
||||||
}
|
|
||||||
addMarker(id, length, x, y, z, yaw, pitch, roll);
|
addMarker(id, length, x, y, z, yaw, pitch, roll);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -386,19 +339,6 @@ publish_debug:
|
|||||||
void addMarker(int id, double length, double x, double y, double z,
|
void addMarker(int id, double length, double x, double y, double z,
|
||||||
double yaw, double pitch, double roll)
|
double yaw, double pitch, double roll)
|
||||||
{
|
{
|
||||||
// Check whether the id is in range for current dictionary
|
|
||||||
int num_markers = board_->dictionary->bytesList.rows;
|
|
||||||
if (num_markers <= id) {
|
|
||||||
ROS_ERROR("aruco_map: Marker id %d is not in dictionary; current dictionary contains %d markers. "
|
|
||||||
"Please see https://github.com/CopterExpress/clever/blob/master/aruco_pose/README.md#parameters for details",
|
|
||||||
id, num_markers);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Check if marker is already in the board
|
|
||||||
if (std::count(board_->ids.begin(), board_->ids.end(), id) > 0) {
|
|
||||||
ROS_ERROR("aruco_map: Marker id %d is already in the map", id);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Create transform
|
// Create transform
|
||||||
tf::Quaternion q;
|
tf::Quaternion q;
|
||||||
q.setRPY(roll, pitch, yaw);
|
q.setRPY(roll, pitch, yaw);
|
||||||
@@ -428,15 +368,6 @@ publish_debug:
|
|||||||
board_->ids.push_back(id);
|
board_->ids.push_back(id);
|
||||||
board_->objPoints.push_back(obj_points);
|
board_->objPoints.push_back(obj_points);
|
||||||
|
|
||||||
// Add marker's static transform
|
|
||||||
if (!markers_frame_.empty()) {
|
|
||||||
geometry_msgs::TransformStamped marker_transform;
|
|
||||||
marker_transform.header.frame_id = markers_parent_frame_;
|
|
||||||
marker_transform.child_frame_id = markers_frame_ + std::to_string(id);
|
|
||||||
tf::transformTFToMsg(transform, marker_transform.transform);
|
|
||||||
markers_transforms_.push_back(marker_transform);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add visualization marker
|
// Add visualization marker
|
||||||
visualization_msgs::Marker marker;
|
visualization_msgs::Marker marker;
|
||||||
marker.header.frame_id = transform_.child_frame_id;
|
marker.header.frame_id = transform_.child_frame_id;
|
||||||
@@ -467,13 +398,6 @@ publish_debug:
|
|||||||
// vis_array_.markers.at(0).points.push_back(p);
|
// vis_array_.markers.at(0).points.push_back(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
void publishMarkersFrames()
|
|
||||||
{
|
|
||||||
if (!markers_transforms_.empty()) {
|
|
||||||
static_br_.sendTransform(markers_transforms_);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void publishMapImage()
|
void publishMapImage()
|
||||||
{
|
{
|
||||||
cv::Size size(image_width_, image_height_);
|
cv::Size size(image_width_, image_height_);
|
||||||
|
|||||||
@@ -87,7 +87,6 @@ void _drawPlanarBoard(Board *_board, Size outSize, OutputArray _img, int marginS
|
|||||||
// dst_sz.width = dst_sz.height = std::min(dst_sz.width, dst_sz.height); //marker should be square
|
// dst_sz.width = dst_sz.height = std::min(dst_sz.width, dst_sz.height); //marker should be square
|
||||||
double diag = std::round(std::hypot(dst_sz.width, dst_sz.height));
|
double diag = std::round(std::hypot(dst_sz.width, dst_sz.height));
|
||||||
int side = std::round(diag / std::sqrt(2));
|
int side = std::round(diag / std::sqrt(2));
|
||||||
side = std::max(side, 10);
|
|
||||||
|
|
||||||
dictionary.drawMarker(_board->ids[m], side, marker, borderBits);
|
dictionary.drawMarker(_board->ids[m], side, marker, borderBits);
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,5 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
|
|
||||||
# Copyright (C) 2018 Copter Express Technologies
|
|
||||||
#
|
|
||||||
# Author: Oleg Kalachev <okalachev@gmail.com>
|
|
||||||
#
|
|
||||||
# Distributed under MIT License (available at https://opensource.org/licenses/MIT).
|
|
||||||
# The above copyright notice and this permission notice shall be included in all
|
|
||||||
# copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
"""Markers map generator
|
"""Markers map generator
|
||||||
|
|
||||||
Generate map file for aruco_map nodelet.
|
Generate map file for aruco_map nodelet.
|
||||||
|
|||||||
@@ -1,14 +1,3 @@
|
|||||||
/*
|
|
||||||
* Utility functions
|
|
||||||
* Copyright (C) 2018 Copter Express Technologies
|
|
||||||
*
|
|
||||||
* Author: Oleg Kalachev <okalachev@gmail.com>
|
|
||||||
*
|
|
||||||
* Distributed under MIT License (available at https://opensource.org/licenses/MIT).
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|||||||
@@ -1,131 +1,101 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
import sys
|
||||||
|
import unittest
|
||||||
|
import json
|
||||||
import rospy
|
import rospy
|
||||||
import pytest
|
import rostest
|
||||||
|
|
||||||
import tf2_ros
|
|
||||||
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
|
from visualization_msgs.msg import MarkerArray as VisMarkerArray
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
class TestArucoPose(unittest.TestCase):
|
||||||
def node():
|
def setUp(self):
|
||||||
return rospy.init_node('aruco_pose_test', anonymous=True)
|
rospy.init_node('test_aruco_detect', anonymous=True)
|
||||||
|
|
||||||
@pytest.fixture
|
def test_markers(self):
|
||||||
def tf_buffer():
|
markers = rospy.wait_for_message('aruco_detect/markers', MarkerArray, timeout=5)
|
||||||
buf = tf2_ros.Buffer()
|
self.assertEqual(len(markers.markers), 4)
|
||||||
tf2_ros.TransformListener(buf)
|
self.assertEqual(markers.header.frame_id, 'main_camera_optical')
|
||||||
return buf
|
|
||||||
|
|
||||||
def approx(expected):
|
self.assertEqual(markers.markers[0].id, 2)
|
||||||
return pytest.approx(expected, abs=1e-4) # compare floats more roughly
|
self.assertAlmostEqual(markers.markers[0].length, 0.33, places=4)
|
||||||
|
self.assertAlmostEqual(markers.markers[0].pose.position.x, 0.36706567854, places=4)
|
||||||
|
self.assertAlmostEqual(markers.markers[0].pose.position.y, 0.290484516644, places=4)
|
||||||
|
self.assertAlmostEqual(markers.markers[0].pose.position.z, 2.18787602301, places=4)
|
||||||
|
self.assertAlmostEqual(markers.markers[0].pose.orientation.x, 0.993997406299, places=4)
|
||||||
|
self.assertAlmostEqual(markers.markers[0].pose.orientation.y, -0.00532003481626, places=4)
|
||||||
|
self.assertAlmostEqual(markers.markers[0].pose.orientation.z, -0.107390951553, places=4)
|
||||||
|
self.assertAlmostEqual(markers.markers[0].pose.orientation.w, 0.0201999263402, places=4)
|
||||||
|
self.assertAlmostEqual(markers.markers[0].c1.x, 415.557739258, places=4)
|
||||||
|
self.assertAlmostEqual(markers.markers[0].c1.y, 335.557739258, places=4)
|
||||||
|
self.assertAlmostEqual(markers.markers[0].c2.x, 509.442260742, places=4)
|
||||||
|
self.assertAlmostEqual(markers.markers[0].c2.y, 335.557739258, places=4)
|
||||||
|
self.assertAlmostEqual(markers.markers[0].c3.x, 509.442260742, places=4)
|
||||||
|
self.assertAlmostEqual(markers.markers[0].c3.y, 429.442260742, places=4)
|
||||||
|
self.assertAlmostEqual(markers.markers[0].c4.x, 415.557739258, places=4)
|
||||||
|
self.assertAlmostEqual(markers.markers[0].c4.y, 429.442260742, places=4)
|
||||||
|
|
||||||
def test_markers(node):
|
self.assertEqual(markers.markers[3].id, 3)
|
||||||
markers = rospy.wait_for_message('aruco_detect/markers', MarkerArray, timeout=5)
|
self.assertAlmostEqual(markers.markers[3].length, 0.1, places=4)
|
||||||
assert len(markers.markers) == 4
|
self.assertAlmostEqual(markers.markers[3].pose.position.x, -0.1805169666, places=4)
|
||||||
assert markers.header.frame_id == 'main_camera_optical'
|
self.assertAlmostEqual(markers.markers[3].pose.position.y, -0.200697302327, places=4)
|
||||||
|
self.assertAlmostEqual(markers.markers[3].pose.position.z, 0.585767514823, places=4)
|
||||||
|
self.assertAlmostEqual(markers.markers[3].pose.orientation.x, -0.961738074009, places=4)
|
||||||
|
self.assertAlmostEqual(markers.markers[3].pose.orientation.y, -0.0375180244707, places=4)
|
||||||
|
self.assertAlmostEqual(markers.markers[3].pose.orientation.z, -0.0115387773672, places=4)
|
||||||
|
self.assertAlmostEqual(markers.markers[3].pose.orientation.w, 0.271144115664, places=4)
|
||||||
|
self.assertAlmostEqual(markers.markers[3].c1.x, 129.557723999, places=4)
|
||||||
|
self.assertAlmostEqual(markers.markers[3].c1.y, 49.557723999, places=4)
|
||||||
|
self.assertAlmostEqual(markers.markers[3].c2.x, 223.442276001, places=4)
|
||||||
|
self.assertAlmostEqual(markers.markers[3].c2.y, 49.557723999, places=4)
|
||||||
|
self.assertAlmostEqual(markers.markers[3].c3.x, 223.442276001, places=4)
|
||||||
|
self.assertAlmostEqual(markers.markers[3].c3.y, 143.442276001, places=4)
|
||||||
|
self.assertAlmostEqual(markers.markers[3].c4.x, 129.557723999, places=4)
|
||||||
|
self.assertAlmostEqual(markers.markers[3].c4.y, 143.442276001, places=4)
|
||||||
|
|
||||||
assert markers.markers[0].id == 2
|
self.assertEqual(markers.markers[1].id, 1)
|
||||||
assert markers.markers[0].length == approx(0.33)
|
self.assertAlmostEqual(markers.markers[1].length, 0.33, places=4)
|
||||||
assert markers.markers[0].pose.position.x == approx(0.36706567854)
|
self.assertEqual(markers.markers[2].id, 4)
|
||||||
assert markers.markers[0].pose.position.y == approx(0.290484516644)
|
self.assertAlmostEqual(markers.markers[2].length, 0.33, places=4)
|
||||||
assert markers.markers[0].pose.position.z == approx(2.18787602301)
|
|
||||||
assert markers.markers[0].pose.orientation.x == approx(0.993997406299)
|
|
||||||
assert markers.markers[0].pose.orientation.y == approx(-0.00532003481626)
|
|
||||||
assert markers.markers[0].pose.orientation.z == approx(-0.107390951553)
|
|
||||||
assert markers.markers[0].pose.orientation.w == approx(0.0201999263402)
|
|
||||||
assert markers.markers[0].c1.x == approx(415.557739258)
|
|
||||||
assert markers.markers[0].c1.y == approx(335.557739258)
|
|
||||||
assert markers.markers[0].c2.x == approx(509.442260742)
|
|
||||||
assert markers.markers[0].c2.y == approx(335.557739258)
|
|
||||||
assert markers.markers[0].c3.x == approx(509.442260742)
|
|
||||||
assert markers.markers[0].c3.y == approx(429.442260742)
|
|
||||||
assert markers.markers[0].c4.x == approx(415.557739258)
|
|
||||||
assert markers.markers[0].c4.y == approx(429.442260742)
|
|
||||||
|
|
||||||
assert markers.markers[3].id == 3
|
def test_visualization(self):
|
||||||
assert markers.markers[3].length == approx(0.1)
|
vis = rospy.wait_for_message('aruco_detect/visualization', VisMarkerArray, timeout=5)
|
||||||
assert markers.markers[3].pose.position.x == approx(-0.1805169666)
|
self.assertEqual(len(vis.markers), 9)
|
||||||
assert markers.markers[3].pose.position.y == approx(-0.200697302327)
|
|
||||||
assert markers.markers[3].pose.position.z == approx(0.585767514823)
|
|
||||||
assert markers.markers[3].pose.orientation.x == approx(-0.961738074009)
|
|
||||||
assert markers.markers[3].pose.orientation.y == approx(-0.0375180244707)
|
|
||||||
assert markers.markers[3].pose.orientation.z == approx(-0.0115387773672)
|
|
||||||
assert markers.markers[3].pose.orientation.w == approx(0.271144115664)
|
|
||||||
assert markers.markers[3].c1.x == approx(129.557723999)
|
|
||||||
assert markers.markers[3].c1.y == approx(49.557723999)
|
|
||||||
assert markers.markers[3].c2.x == approx(223.442276001)
|
|
||||||
assert markers.markers[3].c2.y == approx(49.557723999)
|
|
||||||
assert markers.markers[3].c3.x == approx(223.442276001)
|
|
||||||
assert markers.markers[3].c3.y == approx(143.442276001)
|
|
||||||
assert markers.markers[3].c4.x == approx(129.557723999)
|
|
||||||
assert markers.markers[3].c4.y == approx(143.442276001)
|
|
||||||
|
|
||||||
assert markers.markers[1].id == 1
|
def test_debug(self):
|
||||||
assert markers.markers[1].length == approx(0.33)
|
img = rospy.wait_for_message('aruco_detect/debug', Image, timeout=5)
|
||||||
assert markers.markers[2].id == 4
|
self.assertEqual(img.width, 640)
|
||||||
assert markers.markers[2].length == approx(0.33)
|
self.assertEqual(img.height, 480)
|
||||||
|
self.assertEqual(img.header.frame_id, 'main_camera_optical')
|
||||||
|
|
||||||
def test_markers_frames(node, tf_buffer):
|
def test_map(self):
|
||||||
marker_2 = tf_buffer.lookup_transform('main_camera_optical', 'aruco_2', rospy.Time(), rospy.Duration(5))
|
pose = rospy.wait_for_message('aruco_map/pose', PoseWithCovarianceStamped, timeout=5)
|
||||||
assert marker_2.transform.translation.x == approx(0.36706567854)
|
self.assertEqual(pose.header.frame_id, 'main_camera_optical')
|
||||||
assert marker_2.transform.translation.y == approx(0.290484516644)
|
self.assertAlmostEqual(pose.pose.pose.position.x, -0.629167753342, places=4)
|
||||||
assert marker_2.transform.translation.z == approx(2.18787602301)
|
self.assertAlmostEqual(pose.pose.pose.position.y, 0.293822650809, places=4)
|
||||||
assert marker_2.transform.rotation.x == approx(0.993997406299)
|
self.assertAlmostEqual(pose.pose.pose.position.z, 2.12641343155, places=4)
|
||||||
assert marker_2.transform.rotation.y == approx(-0.00532003481626)
|
self.assertAlmostEqual(pose.pose.pose.orientation.x, -0.998383794799, places=4)
|
||||||
assert marker_2.transform.rotation.z == approx(-0.107390951553)
|
self.assertAlmostEqual(pose.pose.pose.orientation.y, -5.20919098575e-06, places=4)
|
||||||
assert marker_2.transform.rotation.w == approx(0.0201999263402)
|
self.assertAlmostEqual(pose.pose.pose.orientation.z, -0.0300861070302, places=4)
|
||||||
|
self.assertAlmostEqual(pose.pose.pose.orientation.w, 0.0482143590507, places=4)
|
||||||
|
|
||||||
def test_map_markers_frames(node, tf_buffer):
|
def test_map_image(self):
|
||||||
stamp = rospy.get_rostime()
|
img = rospy.wait_for_message('aruco_map/image', Image, timeout=5)
|
||||||
timeout = rospy.Duration(5)
|
self.assertEqual(img.width, 2000)
|
||||||
|
self.assertEqual(img.height, 2000)
|
||||||
|
self.assertEqual(img.encoding, 'mono8')
|
||||||
|
|
||||||
marker_1 = tf_buffer.lookup_transform('aruco_map', 'aruco_map_1', stamp, timeout)
|
def test_map_visualization(self):
|
||||||
assert marker_1.transform.translation.x == approx(0)
|
vis = rospy.wait_for_message('aruco_map/visualization', VisMarkerArray, timeout=5)
|
||||||
assert marker_1.transform.translation.y == approx(0)
|
|
||||||
assert marker_1.transform.translation.z == approx(0)
|
|
||||||
|
|
||||||
marker_4 = tf_buffer.lookup_transform('aruco_map', 'aruco_map_4', stamp, timeout)
|
def test_map_debug(self):
|
||||||
assert marker_4.transform.translation.x == approx(1)
|
img = rospy.wait_for_message('aruco_map/debug', Image, timeout=5)
|
||||||
assert marker_4.transform.translation.y == approx(1)
|
|
||||||
assert marker_4.transform.translation.z == approx(0)
|
|
||||||
|
|
||||||
marker_12 = tf_buffer.lookup_transform('aruco_map', 'aruco_map_12', stamp, timeout)
|
# def test_transforms(self):
|
||||||
assert marker_12.transform.translation.x == approx(0.2)
|
# pass
|
||||||
assert marker_12.transform.translation.y == approx(0.5)
|
|
||||||
assert marker_12.transform.translation.z == approx(0)
|
|
||||||
|
|
||||||
def test_visualization(node):
|
|
||||||
vis = rospy.wait_for_message('aruco_detect/visualization', VisMarkerArray, timeout=5)
|
|
||||||
assert len(vis.markers) == 9
|
|
||||||
|
|
||||||
def test_debug(node):
|
rostest.rosrun('aruco_pose', 'test_aruco_detect', TestArucoPose, sys.argv)
|
||||||
img = rospy.wait_for_message('aruco_detect/debug', Image, timeout=5)
|
|
||||||
assert img.width == 640
|
|
||||||
assert img.height == 480
|
|
||||||
assert img.header.frame_id == 'main_camera_optical'
|
|
||||||
|
|
||||||
def test_map(node):
|
|
||||||
pose = rospy.wait_for_message('aruco_map/pose', PoseWithCovarianceStamped, timeout=5)
|
|
||||||
assert pose.header.frame_id == 'main_camera_optical'
|
|
||||||
assert pose.pose.pose.position.x == approx(-0.629167753342)
|
|
||||||
assert pose.pose.pose.position.y == approx(0.293822650809)
|
|
||||||
assert pose.pose.pose.position.z == approx(2.12641343155)
|
|
||||||
assert pose.pose.pose.orientation.x == approx(-0.998383794799)
|
|
||||||
assert pose.pose.pose.orientation.y == approx(-5.20919098575e-06)
|
|
||||||
assert pose.pose.pose.orientation.z == approx(-0.0300861070302)
|
|
||||||
assert pose.pose.pose.orientation.w == approx(0.0482143590507)
|
|
||||||
|
|
||||||
def test_map_image(node):
|
|
||||||
img = rospy.wait_for_message('aruco_map/image', Image, timeout=5)
|
|
||||||
assert img.width == 2000
|
|
||||||
assert img.height == 2000
|
|
||||||
assert img.encoding == 'mono8'
|
|
||||||
|
|
||||||
def test_map_visualization(node):
|
|
||||||
vis = rospy.wait_for_message('aruco_map/visualization', VisMarkerArray, timeout=5)
|
|
||||||
|
|
||||||
def test_map_debug(node):
|
|
||||||
img = rospy.wait_for_message('aruco_map/debug', Image, timeout=5)
|
|
||||||
|
|||||||
@@ -5,27 +5,23 @@
|
|||||||
<param name="camera_info_url" value="file://$(find aruco_pose)/test/camera_info.yaml" />
|
<param name="camera_info_url" value="file://$(find aruco_pose)/test/camera_info.yaml" />
|
||||||
</node>
|
</node>
|
||||||
|
|
||||||
<node pkg="nodelet" type="nodelet" name="nodelet_manager" args="manager" required="true"/>
|
<node pkg="nodelet" type="nodelet" name="nodelet_manager" args="manager"/>
|
||||||
|
|
||||||
<node pkg="nodelet" clear_params="true" type="nodelet" name="aruco_detect" args="load aruco_pose/aruco_detect nodelet_manager" required="true">
|
<node pkg="nodelet" clear_params="true" type="nodelet" name="aruco_detect" args="load aruco_pose/aruco_detect nodelet_manager">
|
||||||
<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"/>
|
||||||
<param name="length" value="0.33"/>
|
<param name="length" value="0.33"/>
|
||||||
<param name="length_override/3" value="0.1"/>
|
<param name="length_override/3" value="0.1"/>
|
||||||
<param name="estimate_poses" value="true"/>
|
<param name="estimate_poses" value="true"/>
|
||||||
<param name="send_tf" value="true"/>
|
|
||||||
</node>
|
</node>
|
||||||
|
|
||||||
<node name="aruco_map" pkg="nodelet" type="nodelet" args="load aruco_pose/aruco_map nodelet_manager" clear_params="true" required="true">
|
<node name="aruco_map" pkg="nodelet" type="nodelet" args="load aruco_pose/aruco_map nodelet_manager" clear_params="true">
|
||||||
<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="markers" to="aruco_detect/markers"/>
|
<remap from="markers" to="aruco_detect/markers"/>
|
||||||
<param name="type" value="map"/>
|
<param name="type" value="map"/>
|
||||||
<param name="map" value="$(find aruco_pose)/test/basic.txt"/>
|
<param name="map" value="$(find aruco_pose)/test/basic.txt"/>
|
||||||
<param name="markers/frame_id" value="aruco_map"/>
|
|
||||||
<param name="markers/child_frame_id_prefix" value="aruco_map_"/>
|
|
||||||
</node>
|
</node>
|
||||||
|
|
||||||
<param name="test_module" value="$(find aruco_pose)/test/basic.py"/>
|
<test test-name="test_aruco_pose" pkg="aruco_pose" type="basic.py"/>
|
||||||
<test test-name="aruco_pose_test" pkg="ros_pytest" type="ros_pytest_runner"/>
|
|
||||||
</launch>
|
</launch>
|
||||||
|
|||||||
@@ -1,11 +1,7 @@
|
|||||||
# Default markers
|
|
||||||
1 0.33 0 0 0 0 0 0
|
1 0.33 0 0 0 0 0 0
|
||||||
2 0.33 1 0 0 0 0 0
|
2 0.33 1 0 0 0 0 0
|
||||||
3 0.33 0 1 0 0 0 0
|
3 0.33 0 1 0 0 0 0
|
||||||
4 0.33 1 1 0 0 0 0
|
4 0.33 1 1 0 0 0 0
|
||||||
# Marker with non-zero yaw rotation
|
|
||||||
10 0.5 0.5 2 0 1.2 0 0
|
10 0.5 0.5 2 0 1.2 0 0
|
||||||
# Marker with non-zero pitch and roll rotation
|
|
||||||
11 0.2 0.5 0.5 0 0.0 -1 1
|
11 0.2 0.5 0.5 0 0.0 -1 1
|
||||||
# Marker with yaw, pitch and roll rotation
|
|
||||||
12 0.4 0.2 0.5 0 0.1 -1.2 -0.5
|
12 0.4 0.2 0.5 0 0.1 -1.2 -0.5
|
||||||
|
|||||||
@@ -1,26 +0,0 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
import sys
|
|
||||||
import unittest
|
|
||||||
import json
|
|
||||||
import rospy
|
|
||||||
import rostest
|
|
||||||
|
|
||||||
from sensor_msgs.msg import Image
|
|
||||||
from visualization_msgs.msg import MarkerArray as VisMarkerArray
|
|
||||||
|
|
||||||
|
|
||||||
class TestArucoPose(unittest.TestCase):
|
|
||||||
def setUp(self):
|
|
||||||
rospy.init_node('test_aruco_largemap', anonymous=True)
|
|
||||||
|
|
||||||
def test_map_image(self):
|
|
||||||
img = rospy.wait_for_message('aruco_map/image', Image, timeout=5)
|
|
||||||
self.assertEqual(img.width, 2000)
|
|
||||||
self.assertEqual(img.height, 2000)
|
|
||||||
self.assertEqual(img.encoding, 'mono8')
|
|
||||||
|
|
||||||
def test_map_visualization(self):
|
|
||||||
vis = rospy.wait_for_message('aruco_map/visualization', VisMarkerArray, timeout=5)
|
|
||||||
|
|
||||||
|
|
||||||
rostest.rosrun('aruco_pose', 'test_aruco_largemap', TestArucoPose, sys.argv)
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
<launch>
|
|
||||||
<node pkg="nodelet" type="nodelet" name="nodelet_manager" args="manager" required="true"/>
|
|
||||||
|
|
||||||
<node name="aruco_map" pkg="nodelet" type="nodelet" args="load aruco_pose/aruco_map nodelet_manager" clear_params="true" required="true">
|
|
||||||
<remap from="image_raw" to="main_camera/image_raw"/>
|
|
||||||
<remap from="camera_info" to="main_camera/camera_info"/>
|
|
||||||
<remap from="markers" to="aruco_detect/markers"/>
|
|
||||||
<param name="type" value="map"/>
|
|
||||||
<param name="map" value="$(find aruco_pose)/test/largemap.txt"/>
|
|
||||||
</node>
|
|
||||||
|
|
||||||
<test test-name="test_aruco_pose_largemap" pkg="aruco_pose" type="largemap.py"/>
|
|
||||||
</launch>
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
0 0.2 0 0 0 0 0 0
|
|
||||||
1 0.2 10 0 0 0 0 0
|
|
||||||
2 0.2 20 0 0 0 0 0
|
|
||||||
3 0.2 30 0 0 0 0 0
|
|
||||||
4 0.2 40 0 0 0 0 0
|
|
||||||
5 0.2 50 0 0 0 0 0
|
|
||||||
6 0.2 60 0 0 0 0 0
|
|
||||||
7 0.2 70 0 0 0 0 0
|
|
||||||
8 0.2 80 0 0 0 0 0
|
|
||||||
9 0.2 90 0 0 0 0 0
|
|
||||||
10 0.2 100 0 0 0 0 0
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
import rospy
|
|
||||||
import pytest
|
|
||||||
|
|
||||||
from visualization_msgs.msg import MarkerArray as VisMarkerArray
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def node():
|
|
||||||
return rospy.init_node('aruco_pose_test', anonymous=True)
|
|
||||||
|
|
||||||
def test_node_failure(node):
|
|
||||||
with pytest.raises(rospy.exceptions.ROSException):
|
|
||||||
rospy.wait_for_message('aruco_map/visualization', VisMarkerArray, timeout=5)
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
<launch>
|
|
||||||
<node pkg="nodelet" type="nodelet" name="nodelet_manager" args="manager"/>
|
|
||||||
|
|
||||||
<node name="aruco_map" pkg="nodelet" type="nodelet" args="load aruco_pose/aruco_map nodelet_manager" clear_params="true">
|
|
||||||
<remap from="image_raw" to="main_camera/image_raw"/>
|
|
||||||
<remap from="camera_info" to="main_camera/camera_info"/>
|
|
||||||
<remap from="markers" to="aruco_detect/markers"/>
|
|
||||||
<param name="type" value="map"/>
|
|
||||||
<param name="map" value="$(find aruco_pose)/test/test_node_failure.txt"/>
|
|
||||||
</node>
|
|
||||||
|
|
||||||
<param name="test_module" value="$(find aruco_pose)/test/test_node_failure.py"/>
|
|
||||||
<test test-name="test_node_failure" pkg="ros_pytest" type="ros_pytest_runner"/>
|
|
||||||
</launch>
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
# Any garbage data (pretty much anything apart from a comment starting with a hash starting
|
|
||||||
# with a hash sign or a number) will be interpreted as garbage data; the node should fail
|
|
||||||
# after reading it.
|
|
||||||
// Don't try to put your comments this way, it will kill your node!
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
import rospy
|
|
||||||
import pytest
|
|
||||||
|
|
||||||
from visualization_msgs.msg import MarkerArray as VisMarkerArray
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def node():
|
|
||||||
return rospy.init_node('aruco_pose_test_empty_map', anonymous=True)
|
|
||||||
|
|
||||||
def test_empty_map(node):
|
|
||||||
markers = rospy.wait_for_message('aruco_map/visualization', VisMarkerArray, timeout=5)
|
|
||||||
assert len(markers.markers) == 0
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
<launch>
|
|
||||||
<node pkg="nodelet" type="nodelet" name="nodelet_manager" args="manager"/>
|
|
||||||
|
|
||||||
<node name="aruco_map" pkg="nodelet" type="nodelet" args="load aruco_pose/aruco_map nodelet_manager" clear_params="true">
|
|
||||||
<remap from="image_raw" to="main_camera/image_raw"/>
|
|
||||||
<remap from="camera_info" to="main_camera/camera_info"/>
|
|
||||||
<remap from="markers" to="aruco_detect/markers"/>
|
|
||||||
<param name="type" value="map"/>
|
|
||||||
<param name="map" value="$(find aruco_pose)/test/test_parser_empty_map.txt"/>
|
|
||||||
</node>
|
|
||||||
|
|
||||||
<param name="test_module" value="$(find aruco_pose)/test/test_parser_empty_map.py"/>
|
|
||||||
<test test-name="test_node_empty_map" pkg="ros_pytest" type="ros_pytest_runner"/>
|
|
||||||
</launch>
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
# Failing markers: Not enough parameters to add a marker
|
|
||||||
1
|
|
||||||
2 1
|
|
||||||
3 0.5 1
|
|
||||||
# Failing markers: Marker IDs outside of dictionary range
|
|
||||||
1001 1 1 0
|
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
import rospy
|
|
||||||
import pytest
|
|
||||||
|
|
||||||
from sensor_msgs.msg import Image
|
|
||||||
from aruco_pose.msg import MarkerArray
|
|
||||||
from visualization_msgs.msg import MarkerArray as VisMarkerArray
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def node():
|
|
||||||
return rospy.init_node('aruco_pose_test', anonymous=True)
|
|
||||||
|
|
||||||
def approx(expected):
|
|
||||||
return pytest.approx(expected, abs=1e-4) # compare floats more roughly
|
|
||||||
|
|
||||||
def test_markers(node):
|
|
||||||
markers = rospy.wait_for_message('aruco_map/visualization', VisMarkerArray, timeout=5)
|
|
||||||
assert len(markers.markers) == 6
|
|
||||||
|
|
||||||
assert markers.markers[0].pose.position.x == approx(0)
|
|
||||||
assert markers.markers[0].pose.position.y == approx(0)
|
|
||||||
assert markers.markers[0].pose.position.z == approx(0)
|
|
||||||
|
|
||||||
assert markers.markers[1].pose.position.x == approx(1)
|
|
||||||
assert markers.markers[1].pose.position.y == approx(1)
|
|
||||||
assert markers.markers[1].pose.position.z == approx(1)
|
|
||||||
|
|
||||||
assert markers.markers[2].pose.position.x == approx(1)
|
|
||||||
assert markers.markers[2].pose.position.y == approx(0)
|
|
||||||
assert markers.markers[2].pose.position.z == approx(0.5)
|
|
||||||
|
|
||||||
assert markers.markers[3].pose.position.x == approx(0)
|
|
||||||
assert markers.markers[3].pose.position.y == approx(1)
|
|
||||||
assert markers.markers[3].pose.position.z == approx(0)
|
|
||||||
|
|
||||||
assert markers.markers[4].pose.position.x == approx(1)
|
|
||||||
assert markers.markers[4].pose.position.y == approx(0.5)
|
|
||||||
assert markers.markers[4].pose.position.z == approx(0)
|
|
||||||
|
|
||||||
assert markers.markers[5].pose.position.x == approx(2.2)
|
|
||||||
assert markers.markers[5].pose.position.y == approx(0.2)
|
|
||||||
assert markers.markers[5].pose.position.z == approx(0)
|
|
||||||
|
|
||||||
assert markers.markers[0].scale.x == approx(0.33)
|
|
||||||
assert markers.markers[0].scale.y == approx(0.33)
|
|
||||||
assert markers.markers[1].scale.x == approx(0.225)
|
|
||||||
assert markers.markers[1].scale.y == approx(0.225)
|
|
||||||
assert markers.markers[2].scale.x == approx(0.45)
|
|
||||||
assert markers.markers[2].scale.y == approx(0.45)
|
|
||||||
assert markers.markers[3].scale.x == approx(0.15)
|
|
||||||
assert markers.markers[3].scale.y == approx(0.15)
|
|
||||||
assert markers.markers[4].scale.x == approx(0.25)
|
|
||||||
assert markers.markers[4].scale.y == approx(0.25)
|
|
||||||
assert markers.markers[5].scale.x == approx(0.35)
|
|
||||||
assert markers.markers[5].scale.y == approx(0.35)
|
|
||||||
|
|
||||||
def test_map_image(node):
|
|
||||||
img = rospy.wait_for_message('aruco_map/image', Image, timeout=5)
|
|
||||||
assert img.width == 2000
|
|
||||||
assert img.height == 2000
|
|
||||||
assert img.encoding == 'mono8'
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
<launch>
|
|
||||||
<node pkg="nodelet" type="nodelet" name="nodelet_manager" args="manager" required="true"/>
|
|
||||||
|
|
||||||
<node name="aruco_map" pkg="nodelet" type="nodelet" args="load aruco_pose/aruco_map nodelet_manager" clear_params="true" required="true">
|
|
||||||
<remap from="image_raw" to="main_camera/image_raw"/>
|
|
||||||
<remap from="camera_info" to="main_camera/camera_info"/>
|
|
||||||
<remap from="markers" to="aruco_detect/markers"/>
|
|
||||||
<param name="type" value="map"/>
|
|
||||||
<param name="map" value="$(find aruco_pose)/test/test_parser_pass.txt"/>
|
|
||||||
</node>
|
|
||||||
|
|
||||||
<param name="test_module" value="$(find aruco_pose)/test/test_parser_pass.py"/>
|
|
||||||
<test test-name="test_node_pass" pkg="ros_pytest" type="ros_pytest_runner"/>
|
|
||||||
</launch>
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
# Parser test #1 - correct file
|
|
||||||
# 1. Commentary test
|
|
||||||
#Commentary text without space after pound sign
|
|
||||||
# Commentary text with space after pound sign
|
|
||||||
# Commentary text with spaces before pound sign
|
|
||||||
# Commentary text with tab before pound sign
|
|
||||||
# Text with tabs before pound sign
|
|
||||||
# Empty line test:
|
|
||||||
|
|
||||||
# All-whitespace line test:
|
|
||||||
|
|
||||||
# 2. Default coordinates test
|
|
||||||
# Fully filled marker description, tab-delimited:
|
|
||||||
1 0.33 0 0 0 0 0 0
|
|
||||||
# Fully filled marker description, space-delimited:
|
|
||||||
2 0.225 1 1 1 0 0 0
|
|
||||||
# Default roll, pitch, yaw angles
|
|
||||||
3 0.45 1 0 0.5
|
|
||||||
# Default roll, pitch, yaw, z
|
|
||||||
4 0.15 0 1
|
|
||||||
# Inline commentary
|
|
||||||
5 0.25 1 0.5# Comment straight after digit
|
|
||||||
6 0.35 2.2 0.2 # Comment with a space after digit
|
|
||||||
@@ -8,10 +8,6 @@
|
|||||||
#
|
#
|
||||||
# Author: Artem Smirnov <urpylka@gmail.com>
|
# Author: Artem Smirnov <urpylka@gmail.com>
|
||||||
#
|
#
|
||||||
# Distributed under MIT License (available at https://opensource.org/licenses/MIT).
|
|
||||||
# The above copyright notice and this permission notice shall be included in all
|
|
||||||
# copies or substantial portions of the Software.
|
|
||||||
#
|
|
||||||
|
|
||||||
set -e # Exit immidiately on non-zero result
|
set -e # Exit immidiately on non-zero result
|
||||||
|
|
||||||
|
|||||||
@@ -8,10 +8,6 @@
|
|||||||
#
|
#
|
||||||
# Author: Artem Smirnov <urpylka@gmail.com>
|
# Author: Artem Smirnov <urpylka@gmail.com>
|
||||||
#
|
#
|
||||||
# Distributed under MIT License (available at https://opensource.org/licenses/MIT).
|
|
||||||
# The above copyright notice and this permission notice shall be included in all
|
|
||||||
# copies or substantial portions of the Software.
|
|
||||||
#
|
|
||||||
|
|
||||||
set -e # Exit immidiately on non-zero result
|
set -e # Exit immidiately on non-zero result
|
||||||
|
|
||||||
|
|||||||
@@ -541,15 +541,3 @@ tf2_web_republisher:
|
|||||||
image_publisher:
|
image_publisher:
|
||||||
debian:
|
debian:
|
||||||
stretch: [ros-kinetic-image-publisher]
|
stretch: [ros-kinetic-image-publisher]
|
||||||
raspberry-kernel-headers:
|
|
||||||
debian:
|
|
||||||
stretch: [raspberry-kernel-headers]
|
|
||||||
ddynamic_reconfigure:
|
|
||||||
debian:
|
|
||||||
stretch: [ros-kinetic-ddynamic-reconfigure]
|
|
||||||
realsense2_camera:
|
|
||||||
debian:
|
|
||||||
stretch: [ros-kinetic-realsense2-camera]
|
|
||||||
ros_pytest:
|
|
||||||
debian:
|
|
||||||
stretch: [ros-kinetic-ros-pytest]
|
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
[Unit]
|
|
||||||
Description=Daemon required to control GPIO pins via pigpio
|
|
||||||
[Service]
|
|
||||||
ExecStart=/usr/bin/pigpiod -l -t 0 -x 0x0FFF3FF0
|
|
||||||
ExecStop=/bin/systemctl kill pigpiod
|
|
||||||
Type=forking
|
|
||||||
[Install]
|
|
||||||
WantedBy=multi-user.target
|
|
||||||
@@ -1,21 +1,17 @@
|
|||||||
#! /usr/bin/env bash
|
#! /usr/bin/env bash
|
||||||
|
|
||||||
#
|
#
|
||||||
# Script for build the image. Used builder script of the target repo.
|
# Script for build the image. Used builder script of the target repo
|
||||||
# For build: docker run --privileged -it --rm -v /dev:/dev -v $(pwd):/builder/repo smirart/builder
|
# For build: docker run --privileged -it --rm -v /dev:/dev -v $(pwd):/builder/repo smirart/builder
|
||||||
#
|
#
|
||||||
# Copyright (C) 2018 Copter Express Technologies
|
# Copyright (C) 2018 Copter Express Technologies
|
||||||
#
|
#
|
||||||
# Author: Artem Smirnov <urpylka@gmail.com>
|
# Author: Artem Smirnov <urpylka@gmail.com>
|
||||||
#
|
#
|
||||||
# Distributed under MIT License (available at https://opensource.org/licenses/MIT).
|
|
||||||
# The above copyright notice and this permission notice shall be included in all
|
|
||||||
# copies or substantial portions of the Software.
|
|
||||||
#
|
|
||||||
|
|
||||||
set -e # Exit immidiately on non-zero result
|
set -e # Exit immidiately on non-zero result
|
||||||
|
|
||||||
SOURCE_IMAGE="https://downloads.raspberrypi.org/raspbian_lite/images/raspbian_lite-2019-04-09/2019-04-08-raspbian-stretch-lite.zip"
|
SOURCE_IMAGE="https://downloads.raspberrypi.org/raspbian_lite/images/raspbian_lite-2018-11-15/2018-11-13-raspbian-stretch-lite.zip"
|
||||||
|
|
||||||
export DEBIAN_FRONTEND=${DEBIAN_FRONTEND:='noninteractive'}
|
export DEBIAN_FRONTEND=${DEBIAN_FRONTEND:='noninteractive'}
|
||||||
export LANG=${LANG:='C.UTF-8'}
|
export LANG=${LANG:='C.UTF-8'}
|
||||||
@@ -113,7 +109,6 @@ ${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/roscore
|
|||||||
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/roscore.service' '/lib/systemd/system/'
|
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/roscore.service' '/lib/systemd/system/'
|
||||||
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/kinetic-rosdep-clever.yaml' '/etc/ros/rosdep/'
|
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/kinetic-rosdep-clever.yaml' '/etc/ros/rosdep/'
|
||||||
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/ros_python_paths' '/etc/sudoers.d/'
|
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/ros_python_paths' '/etc/sudoers.d/'
|
||||||
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/pigpiod.service' '/lib/systemd/system/'
|
|
||||||
# ${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/kinetic-ros-clever.rosinstall' '/home/pi/ros_catkin_ws/'
|
# ${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/kinetic-ros-clever.rosinstall' '/home/pi/ros_catkin_ws/'
|
||||||
# Add PX4 udev rules
|
# Add PX4 udev rules
|
||||||
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/99-px4fmu.rules' '/lib/udev/rules.d/'
|
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/99-px4fmu.rules' '/lib/udev/rules.d/'
|
||||||
|
|||||||
@@ -1,16 +1,12 @@
|
|||||||
#! /usr/bin/env bash
|
#! /usr/bin/env bash
|
||||||
|
|
||||||
#
|
#
|
||||||
# Script for image initialisation
|
# Script for initialisation image
|
||||||
#
|
#
|
||||||
# Copyright (C) 2018 Copter Express Technologies
|
# Copyright (C) 2018 Copter Express Technologies
|
||||||
#
|
#
|
||||||
# Author: Artem Smirnov <urpylka@gmail.com>
|
# Author: Artem Smirnov <urpylka@gmail.com>
|
||||||
#
|
#
|
||||||
# Distributed under MIT License (available at https://opensource.org/licenses/MIT).
|
|
||||||
# The above copyright notice and this permission notice shall be included in all
|
|
||||||
# copies or substantial portions of the Software.
|
|
||||||
#
|
|
||||||
|
|
||||||
set -e # Exit immidiately on non-zero result
|
set -e # Exit immidiately on non-zero result
|
||||||
|
|
||||||
|
|||||||
@@ -1,16 +1,12 @@
|
|||||||
#! /usr/bin/env bash
|
#! /usr/bin/env bash
|
||||||
|
|
||||||
#
|
#
|
||||||
# Script for network configuration
|
# Script for network configure
|
||||||
#
|
#
|
||||||
# Copyright (C) 2018 Copter Express Technologies
|
# Copyright (C) 2018 Copter Express Technologies
|
||||||
#
|
#
|
||||||
# Author: Artem Smirnov <urpylka@gmail.com>
|
# Author: Artem Smirnov <urpylka@gmail.com>
|
||||||
#
|
#
|
||||||
# Distributed under MIT License (available at https://opensource.org/licenses/MIT).
|
|
||||||
# The above copyright notice and this permission notice shall be included in all
|
|
||||||
# copies or substantial portions of the Software.
|
|
||||||
#
|
|
||||||
|
|
||||||
set -e # Exit immidiately on non-zero result
|
set -e # Exit immidiately on non-zero result
|
||||||
|
|
||||||
|
|||||||
@@ -8,10 +8,6 @@
|
|||||||
#
|
#
|
||||||
# Author: Artem Smirnov <urpylka@gmail.com>
|
# Author: Artem Smirnov <urpylka@gmail.com>
|
||||||
#
|
#
|
||||||
# Distributed under MIT License (available at https://opensource.org/licenses/MIT).
|
|
||||||
# The above copyright notice and this permission notice shall be included in all
|
|
||||||
# copies or substantial portions of the Software.
|
|
||||||
#
|
|
||||||
|
|
||||||
set -e # Exit immidiately on non-zero result
|
set -e # Exit immidiately on non-zero result
|
||||||
|
|
||||||
@@ -142,10 +138,6 @@ fi
|
|||||||
|
|
||||||
export ROS_IP='127.0.0.1' # needed for running tests
|
export ROS_IP='127.0.0.1' # needed for running tests
|
||||||
|
|
||||||
echo_stamp "Reconfiguring Clever repository for simplier unshallowing"
|
|
||||||
cd /home/pi/catkin_ws/src/clever
|
|
||||||
git config remote.origin.fetch "+refs/heads/*:refs/remotes/origin/*"
|
|
||||||
|
|
||||||
echo_stamp "Installing CLEVER" \
|
echo_stamp "Installing CLEVER" \
|
||||||
&& cd /home/pi/catkin_ws/src/clever \
|
&& cd /home/pi/catkin_ws/src/clever \
|
||||||
&& git status \
|
&& git status \
|
||||||
@@ -155,6 +147,8 @@ echo_stamp "Installing CLEVER" \
|
|||||||
&& my_travis_retry pip install -r /home/pi/catkin_ws/src/clever/clever/requirements.txt \
|
&& my_travis_retry pip install -r /home/pi/catkin_ws/src/clever/clever/requirements.txt \
|
||||||
&& source /opt/ros/kinetic/setup.bash \
|
&& source /opt/ros/kinetic/setup.bash \
|
||||||
&& catkin_make -j2 -DCMAKE_BUILD_TYPE=Release \
|
&& catkin_make -j2 -DCMAKE_BUILD_TYPE=Release \
|
||||||
|
&& catkin_make run_tests \
|
||||||
|
&& catkin_test_results \
|
||||||
&& systemctl enable roscore \
|
&& systemctl enable roscore \
|
||||||
&& systemctl enable clever \
|
&& systemctl enable clever \
|
||||||
&& echo_stamp "All CLEVER was installed!" "SUCCESS" \
|
&& echo_stamp "All CLEVER was installed!" "SUCCESS" \
|
||||||
@@ -169,6 +163,7 @@ gitbook build
|
|||||||
echo_stamp "Installing additional ROS packages"
|
echo_stamp "Installing additional ROS packages"
|
||||||
apt-get install -y --no-install-recommends \
|
apt-get install -y --no-install-recommends \
|
||||||
ros-kinetic-dynamic-reconfigure \
|
ros-kinetic-dynamic-reconfigure \
|
||||||
|
ros-kinetic-tf2-web-republisher \
|
||||||
ros-kinetic-compressed-image-transport \
|
ros-kinetic-compressed-image-transport \
|
||||||
ros-kinetic-rosbridge-suite \
|
ros-kinetic-rosbridge-suite \
|
||||||
ros-kinetic-rosserial \
|
ros-kinetic-rosserial \
|
||||||
@@ -178,12 +173,9 @@ apt-get install -y --no-install-recommends \
|
|||||||
ros-kinetic-rosshow
|
ros-kinetic-rosshow
|
||||||
|
|
||||||
# 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 (needs for mavros)" \
|
||||||
&& wget -qO- https://raw.githubusercontent.com/mavlink/mavros/master/mavros/scripts/install_geographiclib_datasets.sh | bash
|
&& wget -qO- https://raw.githubusercontent.com/mavlink/mavros/master/mavros/scripts/install_geographiclib_datasets.sh | bash
|
||||||
|
|
||||||
echo_stamp "Running tests"
|
|
||||||
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
|
||||||
|
|
||||||
|
|||||||
@@ -1,16 +1,12 @@
|
|||||||
#! /usr/bin/env bash
|
#! /usr/bin/env bash
|
||||||
|
|
||||||
#
|
#
|
||||||
# Script for installing software to the image.
|
# Script for install software to the image.
|
||||||
#
|
#
|
||||||
# Copyright (C) 2018 Copter Express Technologies
|
# Copyright (C) 2018 Copter Express Technologies
|
||||||
#
|
#
|
||||||
# Author: Artem Smirnov <urpylka@gmail.com>
|
# Author: Artem Smirnov <urpylka@gmail.com>
|
||||||
#
|
#
|
||||||
# Distributed under MIT License (available at https://opensource.org/licenses/MIT).
|
|
||||||
# The above copyright notice and this permission notice shall be included in all
|
|
||||||
# copies or substantial portions of the Software.
|
|
||||||
#
|
|
||||||
|
|
||||||
set -e # Exit immidiately on non-zero result
|
set -e # Exit immidiately on non-zero result
|
||||||
|
|
||||||
@@ -63,7 +59,7 @@ echo_stamp "Install apt keys & repos"
|
|||||||
curl http://repo.coex.space/aptly_repo_signing.key 2> /dev/null | apt-key add -
|
curl http://repo.coex.space/aptly_repo_signing.key 2> /dev/null | apt-key add -
|
||||||
apt-get update \
|
apt-get update \
|
||||||
&& apt-get install --no-install-recommends -y -qq dirmngr=2.1.18-8~deb9u4 > /dev/null \
|
&& apt-get install --no-install-recommends -y -qq dirmngr=2.1.18-8~deb9u4 > /dev/null \
|
||||||
&& apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-key C1CF6E31E6BADE8868B172B4F42ED6FBAB17C654
|
&& apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-key 421C365BD9FF1F717815A3895523BAEEB01FA116
|
||||||
|
|
||||||
echo "deb http://packages.ros.org/ros/ubuntu stretch main" > /etc/apt/sources.list.d/ros-latest.list
|
echo "deb http://packages.ros.org/ros/ubuntu stretch main" > /etc/apt/sources.list.d/ros-latest.list
|
||||||
echo "deb http://repo.coex.space/rpi-ros-kinetic stretch main" > /etc/apt/sources.list.d/rpi-ros-kinetic.list
|
echo "deb http://repo.coex.space/rpi-ros-kinetic stretch main" > /etc/apt/sources.list.d/rpi-ros-kinetic.list
|
||||||
@@ -88,9 +84,9 @@ lsof=4.89+dfsg-0.1 \
|
|||||||
git \
|
git \
|
||||||
dnsmasq=2.76-5+rpt1+deb9u1 \
|
dnsmasq=2.76-5+rpt1+deb9u1 \
|
||||||
tmux=2.3-4 \
|
tmux=2.3-4 \
|
||||||
vim \
|
vim=2:8.0.0197-4+deb9u1 \
|
||||||
cmake=3.7.2-1 \
|
cmake=3.7.2-1 \
|
||||||
libjpeg8=8d1-2 \
|
libjpeg8-dev=8d1-2 \
|
||||||
tcpdump \
|
tcpdump \
|
||||||
ltrace \
|
ltrace \
|
||||||
libpoco-dev=1.7.6+dfsg1-5+deb9u1 \
|
libpoco-dev=1.7.6+dfsg1-5+deb9u1 \
|
||||||
@@ -108,19 +104,11 @@ ntpdate \
|
|||||||
python-dev \
|
python-dev \
|
||||||
python3-dev \
|
python3-dev \
|
||||||
python-systemd \
|
python-systemd \
|
||||||
mjpg-streamer=2.0 \
|
|
||||||
&& echo_stamp "Everything was installed!" "SUCCESS" \
|
&& echo_stamp "Everything was installed!" "SUCCESS" \
|
||||||
|| (echo_stamp "Some packages wasn't installed!" "ERROR"; exit 1)
|
|| (echo_stamp "Some packages wasn't installed!" "ERROR"; exit 1)
|
||||||
|
|
||||||
echo_stamp "Updating kernel to fix camera bug"
|
echo_stamp "Updating kernel to fix camera bug"
|
||||||
apt-get install --no-install-recommends -y \
|
apt-get install --no-install-recommends -y raspberrypi-kernel=1.20190401-1
|
||||||
raspberrypi-kernel=1.20190517-1 \
|
|
||||||
raspberrypi-bootloader=1.20190517-1 \
|
|
||||||
libraspberrypi-bin=1.20190517-1 \
|
|
||||||
libraspberrypi-dev=1.20190517-1 \
|
|
||||||
libraspberrypi0=1.20190517-1 \
|
|
||||||
wireless-regdb=2018.05.09-0~rpt1 \
|
|
||||||
wpasupplicant=2:2.6-21~bpo9~rpt1
|
|
||||||
|
|
||||||
# Deny byobu to check available updates
|
# Deny byobu to check available updates
|
||||||
sed -i "s/updates_available//" /usr/share/byobu/status/status
|
sed -i "s/updates_available//" /usr/share/byobu/status/status
|
||||||
@@ -168,9 +156,6 @@ syntax on
|
|||||||
autocmd BufNewFile,BufRead *.launch set syntax=xml
|
autocmd BufNewFile,BufRead *.launch set syntax=xml
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
echo_stamp "Change default keyboard layout to US"
|
|
||||||
sed -i 's/XKBLAYOUT="gb"/XKBLAYOUT="us"/g' /etc/default/keyboard
|
|
||||||
|
|
||||||
echo_stamp "Attempting to kill dirmngr"
|
echo_stamp "Attempting to kill dirmngr"
|
||||||
gpgconf --kill dirmngr
|
gpgconf --kill dirmngr
|
||||||
# dirmngr is only used by apt-key, so we can safely kill it.
|
# dirmngr is only used by apt-key, so we can safely kill it.
|
||||||
|
|||||||
@@ -7,10 +7,6 @@
|
|||||||
#
|
#
|
||||||
# Author: Oleg Kalachev <okalachev@gmail.com>
|
# Author: Oleg Kalachev <okalachev@gmail.com>
|
||||||
#
|
#
|
||||||
# Distributed under MIT License (available at https://opensource.org/licenses/MIT).
|
|
||||||
# The above copyright notice and this permission notice shall be included in all
|
|
||||||
# copies or substantial portions of the Software.
|
|
||||||
#
|
|
||||||
|
|
||||||
set -ex
|
set -ex
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,6 @@ pigpiod -v
|
|||||||
i2cdetect -V
|
i2cdetect -V
|
||||||
butterfly -h
|
butterfly -h
|
||||||
espeak --version
|
espeak --version
|
||||||
mjpg_streamer --version
|
|
||||||
|
|
||||||
# ros stuff
|
# ros stuff
|
||||||
|
|
||||||
|
|||||||
@@ -162,6 +162,8 @@ add_executable(rc src/rc.cpp)
|
|||||||
|
|
||||||
add_executable(camera_markers src/camera_markers.cpp)
|
add_executable(camera_markers src/camera_markers.cpp)
|
||||||
|
|
||||||
|
add_executable(frames src/frames.cpp)
|
||||||
|
|
||||||
add_executable(vpe_publisher src/vpe_publisher.cpp)
|
add_executable(vpe_publisher src/vpe_publisher.cpp)
|
||||||
|
|
||||||
target_link_libraries(simple_offboard
|
target_link_libraries(simple_offboard
|
||||||
@@ -173,6 +175,8 @@ target_link_libraries(rc ${catkin_LIBRARIES})
|
|||||||
|
|
||||||
target_link_libraries(camera_markers ${catkin_LIBRARIES})
|
target_link_libraries(camera_markers ${catkin_LIBRARIES})
|
||||||
|
|
||||||
|
target_link_libraries(frames ${catkin_LIBRARIES})
|
||||||
|
|
||||||
target_link_libraries(vpe_publisher ${catkin_LIBRARIES})
|
target_link_libraries(vpe_publisher ${catkin_LIBRARIES})
|
||||||
|
|
||||||
add_dependencies(simple_offboard clever_generate_messages_cpp)
|
add_dependencies(simple_offboard clever_generate_messages_cpp)
|
||||||
@@ -239,8 +243,3 @@ target_link_libraries(clever
|
|||||||
|
|
||||||
## Add folders to be run by python nosetests
|
## Add folders to be run by python nosetests
|
||||||
# catkin_add_nosetests(test)
|
# catkin_add_nosetests(test)
|
||||||
|
|
||||||
if (CATKIN_ENABLE_TESTING)
|
|
||||||
find_package(rostest REQUIRED)
|
|
||||||
add_rostest(test/basic.test)
|
|
||||||
endif()
|
|
||||||
|
|||||||
@@ -24,8 +24,6 @@
|
|||||||
<param name="known_tilt" value="map"/>
|
<param name="known_tilt" value="map"/>
|
||||||
<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)"/>
|
||||||
<param name="markers/frame_id" value="aruco_map"/>
|
|
||||||
<param name="markers/child_frame_id_prefix" value="aruco_map_"/>
|
|
||||||
</node>
|
</node>
|
||||||
|
|
||||||
<!-- vpe publisher from aruco markers -->
|
<!-- vpe publisher from aruco markers -->
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
<arg name="aruco" default="false"/>
|
<arg name="aruco" default="false"/>
|
||||||
<arg name="rc" default="true"/>
|
<arg name="rc" default="true"/>
|
||||||
<arg name="rangefinder_vl53l1x" default="false"/>
|
<arg name="rangefinder_vl53l1x" default="false"/>
|
||||||
|
<arg name="arduino" default="false"/>
|
||||||
|
|
||||||
<!-- mavros -->
|
<!-- mavros -->
|
||||||
<include file="$(find clever)/launch/mavros.launch">
|
<include file="$(find clever)/launch/mavros.launch">
|
||||||
@@ -20,7 +21,6 @@
|
|||||||
<!-- web video server -->
|
<!-- web video server -->
|
||||||
<node name="web_video_server" pkg="web_video_server" type="web_video_server" if="$(arg web_video_server)" required="false" respawn="true" respawn_delay="5">
|
<node name="web_video_server" pkg="web_video_server" type="web_video_server" if="$(arg web_video_server)" required="false" respawn="true" respawn_delay="5">
|
||||||
<param name="default_stream_type" value="ros_compressed"/>
|
<param name="default_stream_type" value="ros_compressed"/>
|
||||||
<param name="publish_rate" value="1.0"/>
|
|
||||||
</node>
|
</node>
|
||||||
|
|
||||||
<!-- aruco markers -->
|
<!-- aruco markers -->
|
||||||
@@ -46,6 +46,11 @@
|
|||||||
<param name="reference_frames/base_link" value="map"/>
|
<param name="reference_frames/base_link" value="map"/>
|
||||||
</node>
|
</node>
|
||||||
|
|
||||||
|
<!-- Auxiliary frames -->
|
||||||
|
<node name="frames" pkg="clever" type="frames" output="screen">
|
||||||
|
<param name="body/frame_id" value="body"/>
|
||||||
|
</node>
|
||||||
|
|
||||||
<!-- main camera -->
|
<!-- main camera -->
|
||||||
<include file="$(find clever)/launch/main_camera.launch" if="$(arg main_camera)"/>
|
<include file="$(find clever)/launch/main_camera.launch" if="$(arg main_camera)"/>
|
||||||
|
|
||||||
@@ -63,4 +68,9 @@
|
|||||||
|
|
||||||
<!-- rc backend -->
|
<!-- rc backend -->
|
||||||
<node name="rc" pkg="clever" type="rc" output="screen" if="$(arg rc)"/>
|
<node name="rc" pkg="clever" type="rc" output="screen" if="$(arg rc)"/>
|
||||||
|
|
||||||
|
<!-- Arduino bridge -->
|
||||||
|
<node pkg="rosserial_python" type="serial_node.py" name="serial_node" output="screen" if="$(arg arduino)">
|
||||||
|
<param name="port" value="/dev/serial/by-id/usb-1a86_USB2.0-Serial-if00-port0"/>
|
||||||
|
</node>
|
||||||
</launch>
|
</launch>
|
||||||
|
|||||||
@@ -24,7 +24,6 @@
|
|||||||
<param name="rate" value="100"/> <!-- poll rate -->
|
<param name="rate" value="100"/> <!-- poll rate -->
|
||||||
<param name="cv_cap_prop_fps" value="40"/> <!-- camera FPS -->
|
<param name="cv_cap_prop_fps" value="40"/> <!-- camera FPS -->
|
||||||
<param name="capture_delay" value="0.02"/> <!-- approximate delay on frame retrieving -->
|
<param name="capture_delay" value="0.02"/> <!-- approximate delay on frame retrieving -->
|
||||||
<param name="rescale_camera_info" value="true"/> <!-- automatically rescale camera calibration info -->
|
|
||||||
|
|
||||||
<!-- camera resolution, NOTE: camera_info file should match it -->
|
<!-- camera resolution, NOTE: camera_info file should match it -->
|
||||||
<param name="image_width" value="320"/>
|
<param name="image_width" value="320"/>
|
||||||
|
|||||||
@@ -56,7 +56,7 @@
|
|||||||
<node pkg="tf2_ros" type="static_transform_publisher" name="rangefinder_frame" args="0 0 -0.05 0 1.5707963268 0 base_link rangefinder"/>
|
<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="copter_visualization" pkg="mavros_extras" type="copter_visualization" if="$(arg viz)">
|
||||||
<remap to="mavros/local_position/pose" from="local_position"/>
|
<remap to="mavros/local_position/pose" from="local_position"/>
|
||||||
<remap to="mavros/setpoint_position/local" from="local_setpoint"/>
|
<remap to="mavros/setpoint_position/local" from="local_setpoint"/>
|
||||||
<param name="fixed_frame_id" value="map"/>
|
<param name="fixed_frame_id" value="map"/>
|
||||||
|
|||||||
@@ -30,14 +30,13 @@
|
|||||||
<depend>nodelet</depend>
|
<depend>nodelet</depend>
|
||||||
<depend>mavros</depend>
|
<depend>mavros</depend>
|
||||||
<depend>mavros_extras</depend>
|
<depend>mavros_extras</depend>
|
||||||
|
<depend>lxml</depend>
|
||||||
<depend>cv_camera</depend>
|
<depend>cv_camera</depend>
|
||||||
<depend>cv_bridge</depend>
|
<depend>cv_bridge</depend>
|
||||||
<depend>opencv3</depend>
|
<depend>opencv3</depend>
|
||||||
|
<depend>mjpg-streamer</depend>
|
||||||
<depend>rosbridge_server</depend>
|
<depend>rosbridge_server</depend>
|
||||||
<depend>web_video_server</depend>
|
<depend>web_video_server</depend>
|
||||||
<depend>tf2_web_republisher</depend>
|
|
||||||
<depend>python-lxml</depend>
|
|
||||||
<exec_depend>python-pymavlink</exec_depend>
|
|
||||||
<!-- Use test_depend for packages you need only for testing: -->
|
<!-- Use test_depend for packages you need only for testing: -->
|
||||||
<!-- <test_depend>gtest</test_depend> -->
|
<!-- <test_depend>gtest</test_depend> -->
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
flask==0.12.3
|
flask==0.12.3
|
||||||
docopt==0.6.2
|
docopt==0.6.2
|
||||||
geopy==1.11.0
|
geopy==1.11.0
|
||||||
|
pymavlink==2.2.10
|
||||||
smbus2==0.2.1
|
smbus2==0.2.1
|
||||||
VL53L1X==0.0.2
|
VL53L1X==0.0.2
|
||||||
|
|||||||
63
clever/src/frames.cpp
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
/*
|
||||||
|
* Auxiliary TF frames for CLEVER drone kit:
|
||||||
|
* - Body frame (drone body with zero pitch and roll).
|
||||||
|
* - TODO: REP-0105 `odom` frame emulation: continuous frame without discrete jumps.
|
||||||
|
* - TODO: Terrain frame (base on ALTITUDE message).
|
||||||
|
* - TODO: map_upside_down frame
|
||||||
|
* - TODO: home frame?
|
||||||
|
*
|
||||||
|
* Copyright (C) 2018 Copter Express Technologies
|
||||||
|
*
|
||||||
|
* Author: Oleg Kalachev <okalachev@gmail.com>
|
||||||
|
*
|
||||||
|
* Distributed under MIT License (available at https://opensource.org/licenses/MIT).
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// TODO: consider implementing as a mavros plugin
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <memory>
|
||||||
|
#include <ros/ros.h>
|
||||||
|
#include <tf/transform_datatypes.h>
|
||||||
|
#include <tf2_ros/transform_broadcaster.h>
|
||||||
|
#include <geometry_msgs/TransformStamped.h>
|
||||||
|
#include <geometry_msgs/PoseStamped.h>
|
||||||
|
|
||||||
|
using std::string;
|
||||||
|
|
||||||
|
static std::shared_ptr<tf2_ros::TransformBroadcaster> br;
|
||||||
|
static geometry_msgs::TransformStamped body;
|
||||||
|
|
||||||
|
inline void publishBody(const geometry_msgs::PoseStamped& pose)
|
||||||
|
{
|
||||||
|
// Get only yaw from pose
|
||||||
|
tf::Quaternion q;
|
||||||
|
q.setRPY(0, 0, tf::getYaw(pose.pose.orientation));
|
||||||
|
tf::quaternionTFToMsg(q, body.transform.rotation);
|
||||||
|
|
||||||
|
body.transform.translation.x = pose.pose.position.x;
|
||||||
|
body.transform.translation.y = pose.pose.position.y;
|
||||||
|
body.transform.translation.z = pose.pose.position.z;
|
||||||
|
body.header.frame_id = pose.header.frame_id;
|
||||||
|
body.header.stamp = pose.header.stamp;
|
||||||
|
br->sendTransform(body);
|
||||||
|
}
|
||||||
|
|
||||||
|
void poseCallback(const geometry_msgs::PoseStamped& pose)
|
||||||
|
{
|
||||||
|
publishBody(pose);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
ros::init(argc, argv, "frames");
|
||||||
|
ros::NodeHandle nh, nh_priv("~");
|
||||||
|
|
||||||
|
nh_priv.param<string>("body/frame_id", body.child_frame_id, "body");
|
||||||
|
|
||||||
|
br = std::make_shared<tf2_ros::TransformBroadcaster>();
|
||||||
|
ros::Subscriber pose_sub = nh.subscribe("mavros/local_position/pose", 1, &poseCallback);
|
||||||
|
ROS_INFO("frames: ready");
|
||||||
|
ros::spin();
|
||||||
|
}
|
||||||
@@ -1,33 +1,19 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
|
|
||||||
# Copyright (C) 2018 Copter Express Technologies
|
|
||||||
#
|
|
||||||
# Author: Oleg Kalachev <okalachev@gmail.com>
|
|
||||||
#
|
|
||||||
# Distributed under MIT License (available at https://opensource.org/licenses/MIT).
|
|
||||||
# The above copyright notice and this permission notice shall be included in all
|
|
||||||
# copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
import math
|
import math
|
||||||
import subprocess
|
import subprocess
|
||||||
import re
|
import re
|
||||||
import traceback
|
import traceback
|
||||||
from threading import Event
|
|
||||||
import numpy
|
import numpy
|
||||||
import rospy
|
import rospy
|
||||||
from systemd import journal
|
|
||||||
import tf2_ros
|
|
||||||
import tf2_geometry_msgs
|
|
||||||
from pymavlink import mavutil
|
|
||||||
from std_srvs.srv import Trigger
|
from std_srvs.srv import Trigger
|
||||||
from sensor_msgs.msg import Image, CameraInfo, NavSatFix, Imu, Range
|
from sensor_msgs.msg import Image, CameraInfo, NavSatFix, Imu, Range
|
||||||
from mavros_msgs.msg import State, OpticalFlowRad, Mavlink
|
from mavros_msgs.msg import State, OpticalFlowRad
|
||||||
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
|
||||||
from visualization_msgs.msg import MarkerArray as VisualizationMarkerArray
|
|
||||||
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 systemd import journal
|
||||||
|
|
||||||
|
|
||||||
# TODO: check attitude is present
|
# TODO: check attitude is present
|
||||||
@@ -42,41 +28,28 @@ from mavros import mavlink
|
|||||||
rospy.init_node('selfcheck')
|
rospy.init_node('selfcheck')
|
||||||
|
|
||||||
|
|
||||||
tf_buffer = tf2_ros.Buffer()
|
|
||||||
tf_listener = tf2_ros.TransformListener(tf_buffer)
|
|
||||||
|
|
||||||
|
|
||||||
failures = []
|
failures = []
|
||||||
infos = []
|
|
||||||
current_check = None
|
|
||||||
|
|
||||||
|
|
||||||
def failure(text, *args):
|
def failure(text, *args):
|
||||||
msg = text % args
|
failures.append(text % args)
|
||||||
rospy.logwarn('%s: %s', current_check, msg)
|
|
||||||
failures.append(msg)
|
|
||||||
|
|
||||||
|
|
||||||
def info(text, *args):
|
|
||||||
msg = text % args
|
|
||||||
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):
|
||||||
failures[:] = []
|
failures[:] = []
|
||||||
infos[:] = []
|
|
||||||
global current_check
|
|
||||||
current_check = name
|
|
||||||
try:
|
try:
|
||||||
fn(*args, **kwargs)
|
fn(*args, **kwargs)
|
||||||
|
for f in failures:
|
||||||
|
rospy.logwarn('%s: %s', name, f)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
for f in failures:
|
||||||
|
rospy.logwarn('%s: %s', name, f)
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
rospy.logerr('%s: exception occurred', name)
|
rospy.logerr('%s: exception occurred', name)
|
||||||
return
|
return
|
||||||
if not failures and not infos:
|
if not failures:
|
||||||
rospy.loginfo('%s: OK', name)
|
rospy.loginfo('%s: OK', name)
|
||||||
return wrapper
|
return wrapper
|
||||||
return inner
|
return inner
|
||||||
@@ -100,116 +73,36 @@ def get_param(name):
|
|||||||
return res.value.real
|
return res.value.real
|
||||||
|
|
||||||
|
|
||||||
recv_event = Event()
|
|
||||||
link = mavutil.mavlink.MAVLink('', 255, 1)
|
|
||||||
mavlink_pub = rospy.Publisher('mavlink/to', Mavlink, queue_size=1)
|
|
||||||
mavlink_recv = ''
|
|
||||||
|
|
||||||
|
|
||||||
def mavlink_message_handler(msg):
|
|
||||||
global mavlink_recv
|
|
||||||
if msg.msgid == 126:
|
|
||||||
mav_bytes_msg = mavlink.convert_to_bytes(msg)
|
|
||||||
mav_msg = link.decode(mav_bytes_msg)
|
|
||||||
mavlink_recv += ''.join(chr(x) for x in mav_msg.data[:mav_msg.count])
|
|
||||||
if 'nsh>' in mavlink_recv:
|
|
||||||
# Remove the last line, including newline before prompt
|
|
||||||
mavlink_recv = mavlink_recv[:mavlink_recv.find('nsh>') - 1]
|
|
||||||
recv_event.set()
|
|
||||||
|
|
||||||
|
|
||||||
mavlink_sub = rospy.Subscriber('mavlink/from', Mavlink, mavlink_message_handler)
|
|
||||||
# FIXME: not sleeping here still breaks things
|
|
||||||
rospy.sleep(0.5)
|
|
||||||
|
|
||||||
|
|
||||||
def mavlink_exec(cmd, timeout=3.0):
|
|
||||||
global mavlink_recv
|
|
||||||
mavlink_recv = ''
|
|
||||||
recv_event.clear()
|
|
||||||
if not cmd.endswith('\n'):
|
|
||||||
cmd += '\n'
|
|
||||||
msg = mavutil.mavlink.MAVLink_serial_control_message(
|
|
||||||
device=mavutil.mavlink.SERIAL_CONTROL_DEV_SHELL,
|
|
||||||
flags=mavutil.mavlink.SERIAL_CONTROL_FLAG_RESPOND | mavutil.mavlink.SERIAL_CONTROL_FLAG_EXCLUSIVE |
|
|
||||||
mavutil.mavlink.SERIAL_CONTROL_FLAG_MULTI,
|
|
||||||
timeout=3,
|
|
||||||
baudrate=0,
|
|
||||||
count=len(cmd),
|
|
||||||
data=map(ord, cmd.ljust(70, '\0')))
|
|
||||||
msg.pack(link)
|
|
||||||
ros_msg = mavlink.convert_to_rosmsg(msg)
|
|
||||||
mavlink_pub.publish(ros_msg)
|
|
||||||
recv_event.wait(timeout)
|
|
||||||
return mavlink_recv
|
|
||||||
|
|
||||||
|
|
||||||
@check('FCU')
|
@check('FCU')
|
||||||
def check_fcu():
|
def check_fcu():
|
||||||
try:
|
try:
|
||||||
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)')
|
||||||
return
|
|
||||||
|
|
||||||
# Make sure the console is available to us
|
|
||||||
mavlink_exec('\n')
|
|
||||||
version_str = mavlink_exec('ver all')
|
|
||||||
if version_str == '':
|
|
||||||
info('no version data available from SITL')
|
|
||||||
|
|
||||||
r = re.compile(r'^FW (git tag|version): (v?\d\.\d\.\d.*)$')
|
|
||||||
is_clever_firmware = False
|
|
||||||
for ver_line in version_str.split('\n'):
|
|
||||||
match = r.search(ver_line)
|
|
||||||
if match is not None:
|
|
||||||
field, version = match.groups()
|
|
||||||
info('firmware %s: %s' % (field, version))
|
|
||||||
if 'clever' in version:
|
|
||||||
is_clever_firmware = True
|
|
||||||
|
|
||||||
if not is_clever_firmware:
|
|
||||||
failure('not running Clever PX4 firmware, check http://clever.copterexpress.com/firmware.html')
|
|
||||||
|
|
||||||
est = get_param('SYS_MC_EST_GROUP')
|
est = get_param('SYS_MC_EST_GROUP')
|
||||||
if est == 1:
|
if est == 1:
|
||||||
info('selected estimator: LPE')
|
rospy.loginfo('Selected estimator: LPE')
|
||||||
fuse = 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')
|
rospy.loginfo('LPE_FUSION: land detector fusion is enabled')
|
||||||
else:
|
else:
|
||||||
info('LPE_FUSION: land detector fusion is disabled')
|
rospy.loginfo('LPE_FUSION: land detector fusion is disabled')
|
||||||
if fuse & (1 << 7):
|
if fuse & (1 << 7):
|
||||||
info('LPE_FUSION: barometer fusion is enabled')
|
rospy.loginfo('LPE_FUSION: barometer fusion is enabled')
|
||||||
else:
|
else:
|
||||||
info('LPE_FUSION: barometer fusion is disabled')
|
rospy.loginfo('LPE_FUSION: barometer fusion is disabled')
|
||||||
|
|
||||||
elif est == 2:
|
elif est == 2:
|
||||||
info('selected estimator: EKF2')
|
rospy.loginfo('Selected estimator: EKF2')
|
||||||
else:
|
else:
|
||||||
failure('unknown selected estimator: %s', est)
|
failure('Unknown selected estimator: %s', est)
|
||||||
|
|
||||||
except rospy.ROSException:
|
except rospy.ROSException:
|
||||||
failure('no MAVROS state (check wiring)')
|
failure('no MAVROS state (check wiring)')
|
||||||
|
|
||||||
|
|
||||||
def describe_direction(v):
|
@check('Camera')
|
||||||
if v.x > 0.9:
|
|
||||||
return 'forward'
|
|
||||||
elif v.x < - 0.9:
|
|
||||||
return 'backward'
|
|
||||||
elif v.y > 0.9:
|
|
||||||
return 'left'
|
|
||||||
elif v.y < -0.9:
|
|
||||||
return 'right'
|
|
||||||
elif v.z > 0.9:
|
|
||||||
return 'upward'
|
|
||||||
elif v.z < -0.9:
|
|
||||||
return 'downward'
|
|
||||||
else:
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def check_camera(name):
|
def check_camera(name):
|
||||||
try:
|
try:
|
||||||
img = rospy.wait_for_message(name + '/image_raw', Image, timeout=1)
|
img = rospy.wait_for_message(name + '/image_raw', Image, timeout=1)
|
||||||
@@ -217,93 +110,28 @@ def check_camera(name):
|
|||||||
failure('%s: no images (is the camera connected properly?)', name)
|
failure('%s: no images (is the camera connected properly?)', name)
|
||||||
return
|
return
|
||||||
try:
|
try:
|
||||||
camera_info = rospy.wait_for_message(name + '/camera_info', CameraInfo, timeout=1)
|
info = rospy.wait_for_message(name + '/camera_info', CameraInfo, timeout=1)
|
||||||
except rospy.ROSException:
|
except rospy.ROSException:
|
||||||
failure('%s: no calibration info', name)
|
failure('%s: no calibration info', name)
|
||||||
return
|
return
|
||||||
|
|
||||||
if img.width != camera_info.width:
|
if img.width != info.width:
|
||||||
failure('%s: calibration width doesn\'t match image width (%d != %d)', name, camera_info.width, img.width)
|
failure('%s: calibration width doesn\'t match image width (%d != %d)', name, info.width, img.width)
|
||||||
if img.height != camera_info.height:
|
if img.height != info.height:
|
||||||
failure('%s: calibration height doesn\'t match image height (%d != %d))', name, camera_info.height, img.height)
|
failure('%s: calibration height doesn\'t match image height (%d != %d))', name, info.height, img.height)
|
||||||
|
|
||||||
try:
|
|
||||||
optical = Vector3Stamped()
|
|
||||||
optical.header.frame_id = img.header.frame_id
|
|
||||||
optical.vector.z = 1
|
|
||||||
cable = Vector3Stamped()
|
|
||||||
cable.header.frame_id = img.header.frame_id
|
|
||||||
cable.vector.y = 1
|
|
||||||
|
|
||||||
optical = describe_direction(tf_buffer.transform(optical, 'base_link').vector)
|
|
||||||
cable = describe_direction(tf_buffer.transform(cable, 'base_link').vector)
|
|
||||||
if not optical or not cable:
|
|
||||||
info('%s: custom camera orientation detected', name)
|
|
||||||
else:
|
|
||||||
info('camera is oriented %s, camera cable goes %s', optical, cable)
|
|
||||||
|
|
||||||
except tf2_ros.TransformException:
|
|
||||||
failure('cannot transform from base_link to camera frame')
|
|
||||||
|
|
||||||
|
|
||||||
@check('Main camera')
|
@check('ArUco detector')
|
||||||
def check_main_camera():
|
|
||||||
check_camera('main_camera')
|
|
||||||
|
|
||||||
|
|
||||||
def is_process_running(binary, exact=False, full=False):
|
|
||||||
try:
|
|
||||||
args = ['pgrep']
|
|
||||||
if exact:
|
|
||||||
args.append('-x') # match exactly with the command name
|
|
||||||
if full:
|
|
||||||
args.append('-f') # use full process name to match
|
|
||||||
args.append(binary)
|
|
||||||
subprocess.check_output(args)
|
|
||||||
return True
|
|
||||||
except subprocess.CalledProcessError:
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
@check('ArUco markers')
|
|
||||||
def check_aruco():
|
def check_aruco():
|
||||||
if is_process_running('aruco_detect', full=True):
|
try:
|
||||||
info('aruco_detect/length = %g m', rospy.get_param('aruco_detect/length'))
|
rospy.wait_for_message('aruco_detect/markers', MarkerArray, timeout=1)
|
||||||
known_tilt = rospy.get_param('aruco_detect/known_tilt')
|
except rospy.ROSException:
|
||||||
if known_tilt == 'map':
|
failure('no markers detection')
|
||||||
known_tilt += ' (ALL markers are on the floor)'
|
|
||||||
elif known_tilt == 'map_flipped':
|
|
||||||
known_tilt += ' (ALL markers are on the ceiling)'
|
|
||||||
info('aruco_detector/known_tilt = %s', known_tilt)
|
|
||||||
try:
|
|
||||||
rospy.wait_for_message('aruco_detect/markers', MarkerArray, timeout=1)
|
|
||||||
except rospy.ROSException:
|
|
||||||
failure('no markers detection')
|
|
||||||
return
|
|
||||||
else:
|
|
||||||
info('aruco_detect is not running')
|
|
||||||
return
|
return
|
||||||
|
try:
|
||||||
if is_process_running('aruco_map', full=True):
|
rospy.wait_for_message('aruco_map/pose', PoseWithCovarianceStamped, timeout=1)
|
||||||
known_tilt = rospy.get_param('aruco_map/known_tilt')
|
except rospy.ROSException:
|
||||||
if known_tilt == 'map':
|
failure('no map detection')
|
||||||
known_tilt += ' (marker\'s map is on the floor)'
|
|
||||||
elif known_tilt == 'map_flipped':
|
|
||||||
known_tilt += ' (marker\'s map is on the ceiling)'
|
|
||||||
info('aruco_map/known_tilt = %s', known_tilt)
|
|
||||||
|
|
||||||
try:
|
|
||||||
visualization = rospy.wait_for_message('aruco_map/visualization', VisualizationMarkerArray, timeout=1)
|
|
||||||
info('map has %s markers', len(visualization.markers))
|
|
||||||
except:
|
|
||||||
failure('cannot read aruco_map/visualization topic')
|
|
||||||
|
|
||||||
try:
|
|
||||||
rospy.wait_for_message('aruco_map/pose', PoseWithCovarianceStamped, timeout=1)
|
|
||||||
except rospy.ROSException:
|
|
||||||
failure('no map detection')
|
|
||||||
else:
|
|
||||||
info('aruco_map is not running')
|
|
||||||
|
|
||||||
|
|
||||||
@check('Vision position estimate')
|
@check('Vision position estimate')
|
||||||
@@ -332,14 +160,14 @@ 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: %.2f', vision_yaw_w)
|
rospy.loginfo('Vision yaw weight: %.2f', vision_yaw_w)
|
||||||
fuse = 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 parameter is %s, but it should be zero', delay)
|
failure('LPE_VIS_DELAY parameter is %s, but it should be zero', delay)
|
||||||
info('LPE_VIS_XY is %.2f m, LPE_VIS_Z is %.2f m', get_param('LPE_VIS_XY'), get_param('LPE_VIS_Z'))
|
rospy.loginfo('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:
|
||||||
fuse = get_param('EKF2_AID_MASK')
|
fuse = get_param('EKF2_AID_MASK')
|
||||||
if not fuse & (1 << 3):
|
if not fuse & (1 << 3):
|
||||||
@@ -349,7 +177,7 @@ def check_vpe():
|
|||||||
delay = get_param('EKF2_EV_DELAY')
|
delay = get_param('EKF2_EV_DELAY')
|
||||||
if delay != 0:
|
if delay != 0:
|
||||||
failure('EKF2_EV_DELAY is %.2f, but it should be zero', delay)
|
failure('EKF2_EV_DELAY is %.2f, but it should be zero', delay)
|
||||||
info('EKF2_EVA_NOISE is %.3f, EKF2_EVP_NOISE is %.3f',
|
rospy.loginfo('EKF2_EVA_NOISE is %.3f, EKF2_EVP_NOISE is %.3f',
|
||||||
get_param('EKF2_EVA_NOISE'),
|
get_param('EKF2_EVA_NOISE'),
|
||||||
get_param('EKF2_EVP_NOISE'))
|
get_param('EKF2_EVP_NOISE'))
|
||||||
|
|
||||||
@@ -468,7 +296,7 @@ def check_optical_flow():
|
|||||||
if not numpy.isclose(scale, 1.0):
|
if not numpy.isclose(scale, 1.0):
|
||||||
failure('LPE_FLW_SCALE parameter is %.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 is %s, LPE_FLW_R is %.4f, LPE_FLW_RR is %.4f, SENS_FLOW_MINHGT is %.3f, SENS_FLOW_MAXHGT is %.3f',
|
rospy.loginfo('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_param('LPE_FLW_QMIN'),
|
get_param('LPE_FLW_QMIN'),
|
||||||
get_param('LPE_FLW_R'),
|
get_param('LPE_FLW_R'),
|
||||||
get_param('LPE_FLW_RR'),
|
get_param('LPE_FLW_RR'),
|
||||||
@@ -481,7 +309,7 @@ def check_optical_flow():
|
|||||||
delay = get_param('EKF2_OF_DELAY')
|
delay = get_param('EKF2_OF_DELAY')
|
||||||
if delay != 0:
|
if delay != 0:
|
||||||
failure('EKF2_OF_DELAY is %.2f, but it should be zero', delay)
|
failure('EKF2_OF_DELAY is %.2f, but it should be zero', delay)
|
||||||
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',
|
rospy.loginfo('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_param('EKF2_OF_QMIN'),
|
get_param('EKF2_OF_QMIN'),
|
||||||
get_param('EKF2_OF_N_MIN'),
|
get_param('EKF2_OF_N_MIN'),
|
||||||
get_param('EKF2_OF_N_MAX'),
|
get_param('EKF2_OF_N_MAX'),
|
||||||
@@ -515,21 +343,21 @@ def check_rangefinder():
|
|||||||
if est == 1:
|
if est == 1:
|
||||||
fuse = get_param('LPE_FUSION')
|
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')
|
rospy.loginfo('"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')
|
rospy.loginfo('"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_MODE')
|
hgt = get_param('EKF2_HGT_MODE')
|
||||||
if hgt != 2:
|
if hgt != 2:
|
||||||
info('EKF2_HGT_MODE != Range sensor, NOT operating over flat surface')
|
rospy.loginfo('EKF2_HGT_MODE != Range sensor, NOT operating over flat surface')
|
||||||
else:
|
else:
|
||||||
info('EKF2_HGT_MODE = Range sensor, operating over flat surface')
|
rospy.loginfo('EKF2_HGT_MODE = Range sensor, operating over flat surface')
|
||||||
aid = get_param('EKF2_RNG_AID')
|
aid = get_param('EKF2_RNG_AID')
|
||||||
if aid != 1:
|
if aid != 1:
|
||||||
info('EKF2_RNG_AID != 1, range sensor aiding disabled')
|
rospy.loginfo('EKF2_RNG_AID != 1, range sensor aiding disabled')
|
||||||
else:
|
else:
|
||||||
info('EKF2_RNG_AID = 1, range sensor aiding enabled')
|
rospy.loginfo('EKF2_RNG_AID = 1, range sensor aiding enabled')
|
||||||
|
|
||||||
|
|
||||||
@check('Boot duration')
|
@check('Boot duration')
|
||||||
@@ -583,42 +411,14 @@ def check_clever_service():
|
|||||||
failure(error)
|
failure(error)
|
||||||
|
|
||||||
|
|
||||||
@check('Image')
|
|
||||||
def check_image():
|
|
||||||
info('version: %s', open('/etc/clever_version').read().strip())
|
|
||||||
|
|
||||||
|
|
||||||
@check('Preflight status')
|
|
||||||
def check_preflight_status():
|
|
||||||
# Make sure the console is available to us
|
|
||||||
mavlink_exec('\n')
|
|
||||||
cmdr_output = mavlink_exec('commander check')
|
|
||||||
if cmdr_output == '':
|
|
||||||
failure('No data from FCU')
|
|
||||||
return
|
|
||||||
cmdr_lines = cmdr_output.split('\n')
|
|
||||||
r = re.compile(r'^(.*)(Preflight|Prearm) check: (.*)')
|
|
||||||
for line in cmdr_lines:
|
|
||||||
if 'WARN' in line:
|
|
||||||
failure(line[line.find(']') + 2:])
|
|
||||||
continue
|
|
||||||
match = r.search(line)
|
|
||||||
if match is not None:
|
|
||||||
check_status = match.groups()[2]
|
|
||||||
if check_status != 'OK':
|
|
||||||
failure(' '.join([match.groups()[1], 'check:', check_status]))
|
|
||||||
|
|
||||||
|
|
||||||
def selfcheck():
|
def selfcheck():
|
||||||
check_image()
|
|
||||||
check_clever_service()
|
check_clever_service()
|
||||||
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_camera('main_camera')
|
||||||
check_main_camera()
|
|
||||||
check_aruco()
|
check_aruco()
|
||||||
check_simpleoffboard()
|
check_simpleoffboard()
|
||||||
check_optical_flow()
|
check_optical_flow()
|
||||||
|
|||||||
@@ -54,7 +54,6 @@ using mavros_msgs::Thrust;
|
|||||||
|
|
||||||
// tf2
|
// tf2
|
||||||
tf2_ros::Buffer tf_buffer;
|
tf2_ros::Buffer tf_buffer;
|
||||||
std::shared_ptr<tf2_ros::TransformBroadcaster> transform_broadcaster;
|
|
||||||
|
|
||||||
// Parameters
|
// Parameters
|
||||||
string local_frame;
|
string local_frame;
|
||||||
@@ -89,7 +88,6 @@ AttitudeTarget att_raw_msg;
|
|||||||
Thrust thrust_msg;
|
Thrust thrust_msg;
|
||||||
TwistStamped rates_msg;
|
TwistStamped rates_msg;
|
||||||
TransformStamped target;
|
TransformStamped target;
|
||||||
geometry_msgs::TransformStamped body;
|
|
||||||
|
|
||||||
// State
|
// State
|
||||||
PoseStamped nav_start;
|
PoseStamped nav_start;
|
||||||
@@ -123,36 +121,13 @@ TwistStamped velocity;
|
|||||||
NavSatFix global_position;
|
NavSatFix global_position;
|
||||||
BatteryState battery;
|
BatteryState battery;
|
||||||
|
|
||||||
// Common subscriber callback template that stores message to the variable
|
// Common subcriber callback template that stores message to the variable
|
||||||
template<typename T, T& STORAGE>
|
template<typename T, T& STORAGE>
|
||||||
void handleMessage(const T& msg)
|
void handleMessage(const T& msg)
|
||||||
{
|
{
|
||||||
STORAGE = msg;
|
STORAGE = msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void publishBodyFrame()
|
|
||||||
{
|
|
||||||
if (body.child_frame_id.empty()) return;
|
|
||||||
|
|
||||||
tf::Quaternion q;
|
|
||||||
q.setRPY(0, 0, tf::getYaw(local_position.pose.orientation));
|
|
||||||
tf::quaternionTFToMsg(q, body.transform.rotation);
|
|
||||||
|
|
||||||
body.transform.translation.x = local_position.pose.position.x;
|
|
||||||
body.transform.translation.y = local_position.pose.position.y;
|
|
||||||
body.transform.translation.z = local_position.pose.position.z;
|
|
||||||
body.header.frame_id = local_position.header.frame_id;
|
|
||||||
body.header.stamp = local_position.header.stamp;
|
|
||||||
transform_broadcaster->sendTransform(body);
|
|
||||||
}
|
|
||||||
|
|
||||||
void handleLocalPosition(const PoseStamped& pose)
|
|
||||||
{
|
|
||||||
local_position = pose;
|
|
||||||
publishBodyFrame();
|
|
||||||
// TODO: terrain?, home?
|
|
||||||
}
|
|
||||||
|
|
||||||
// wait for transform without interrupting publishing setpoints
|
// wait for transform without interrupting publishing setpoints
|
||||||
inline bool waitTransform(const string& target, const string& source,
|
inline bool waitTransform(const string& target, const string& source,
|
||||||
const ros::Time& stamp, const ros::Duration& timeout)
|
const ros::Time& stamp, const ros::Duration& timeout)
|
||||||
@@ -389,12 +364,13 @@ void publish(const ros::Time stamp)
|
|||||||
|
|
||||||
if (!target.child_frame_id.empty()) {
|
if (!target.child_frame_id.empty()) {
|
||||||
if (setpoint_type == NAVIGATE || setpoint_type == NAVIGATE_GLOBAL || setpoint_type == POSITION) {
|
if (setpoint_type == NAVIGATE || setpoint_type == NAVIGATE_GLOBAL || setpoint_type == POSITION) {
|
||||||
|
static tf2_ros::TransformBroadcaster tf_broadcaster;
|
||||||
target.header = setpoint_position_transformed.header;
|
target.header = setpoint_position_transformed.header;
|
||||||
target.transform.translation.x = setpoint_position_transformed.pose.position.x;
|
target.transform.translation.x = setpoint_position_transformed.pose.position.x;
|
||||||
target.transform.translation.y = setpoint_position_transformed.pose.position.y;
|
target.transform.translation.y = setpoint_position_transformed.pose.position.y;
|
||||||
target.transform.translation.z = setpoint_position_transformed.pose.position.z;
|
target.transform.translation.z = setpoint_position_transformed.pose.position.z;
|
||||||
target.transform.rotation = setpoint_position_transformed.pose.orientation;
|
target.transform.rotation = setpoint_position_transformed.pose.orientation;
|
||||||
transform_broadcaster->sendTransform(target);
|
tf_broadcaster.sendTransform(target);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -713,7 +689,6 @@ int main(int argc, char **argv)
|
|||||||
ros::NodeHandle nh, nh_priv("~");
|
ros::NodeHandle nh, nh_priv("~");
|
||||||
|
|
||||||
tf2_ros::TransformListener tf_listener(tf_buffer);
|
tf2_ros::TransformListener tf_listener(tf_buffer);
|
||||||
transform_broadcaster = std::make_shared<tf2_ros::TransformBroadcaster>();
|
|
||||||
|
|
||||||
// Params
|
// Params
|
||||||
nh.param<string>("mavros/local_position/tf/frame_id", local_frame, "map");
|
nh.param<string>("mavros/local_position/tf/frame_id", local_frame, "map");
|
||||||
@@ -722,7 +697,6 @@ int main(int argc, char **argv)
|
|||||||
nh_priv.param("auto_release", auto_release, true);
|
nh_priv.param("auto_release", auto_release, true);
|
||||||
nh_priv.param("land_only_in_offboard", land_only_in_offboard, true);
|
nh_priv.param("land_only_in_offboard", land_only_in_offboard, 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.getParam("reference_frames", reference_frames);
|
nh_priv.getParam("reference_frames", reference_frames);
|
||||||
|
|
||||||
state_timeout = ros::Duration(nh_priv.param("state_timeout", 3.0));
|
state_timeout = ros::Duration(nh_priv.param("state_timeout", 3.0));
|
||||||
@@ -743,11 +717,11 @@ int main(int argc, char **argv)
|
|||||||
|
|
||||||
// Telemetry subscribers
|
// Telemetry subscribers
|
||||||
auto state_sub = nh.subscribe("mavros/state", 1, &handleMessage<mavros_msgs::State, state>);
|
auto state_sub = nh.subscribe("mavros/state", 1, &handleMessage<mavros_msgs::State, state>);
|
||||||
|
auto local_position_sub = nh.subscribe("mavros/local_position/pose", 1, &handleMessage<PoseStamped, local_position>);
|
||||||
auto velocity_sub = nh.subscribe("mavros/local_position/velocity", 1, &handleMessage<TwistStamped, velocity>);
|
auto velocity_sub = nh.subscribe("mavros/local_position/velocity", 1, &handleMessage<TwistStamped, velocity>);
|
||||||
auto global_position_sub = nh.subscribe("mavros/global_position/global", 1, &handleMessage<NavSatFix, global_position>);
|
auto global_position_sub = nh.subscribe("mavros/global_position/global", 1, &handleMessage<NavSatFix, global_position>);
|
||||||
auto battery_sub = nh.subscribe("mavros/battery", 1, &handleMessage<BatteryState, battery>);
|
auto battery_sub = nh.subscribe("mavros/battery", 1, &handleMessage<BatteryState, battery>);
|
||||||
auto statustext_sub = nh.subscribe("mavros/statustext/recv", 1, &handleMessage<mavros_msgs::StatusText, statustext>);
|
auto statustext_sub = nh.subscribe("mavros/statustext/recv", 1, &handleMessage<mavros_msgs::StatusText, statustext>);
|
||||||
auto local_position_sub = nh.subscribe("mavros/local_position/pose", 1, &handleLocalPosition);
|
|
||||||
|
|
||||||
// Setpoint publishers
|
// Setpoint publishers
|
||||||
position_pub = nh.advertise<PoseStamped>("mavros/setpoint_position/local", 1);
|
position_pub = nh.advertise<PoseStamped>("mavros/setpoint_position/local", 1);
|
||||||
|
|||||||
@@ -1,29 +0,0 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
import rospy
|
|
||||||
import pytest
|
|
||||||
from mavros_msgs.msg import State
|
|
||||||
|
|
||||||
@pytest.fixture()
|
|
||||||
def node():
|
|
||||||
return rospy.init_node('clever_test', anonymous=True)
|
|
||||||
|
|
||||||
def test_state(node):
|
|
||||||
state = rospy.wait_for_message('mavros/state', State, timeout=10)
|
|
||||||
assert state.connected == False
|
|
||||||
assert state.armed == False
|
|
||||||
assert state.guided == False
|
|
||||||
assert state.mode == ''
|
|
||||||
|
|
||||||
def test_simple_offboard_services_available():
|
|
||||||
rospy.wait_for_service('get_telemetry', timeout=5)
|
|
||||||
rospy.wait_for_service('navigate', timeout=5)
|
|
||||||
rospy.wait_for_service('navigate_global', timeout=5)
|
|
||||||
rospy.wait_for_service('set_position', timeout=5)
|
|
||||||
rospy.wait_for_service('set_velocity', timeout=5)
|
|
||||||
rospy.wait_for_service('set_attitude', timeout=5)
|
|
||||||
rospy.wait_for_service('set_rates', timeout=5)
|
|
||||||
rospy.wait_for_service('land', timeout=5)
|
|
||||||
|
|
||||||
def test_web_video_server(node):
|
|
||||||
import urllib2
|
|
||||||
urllib2.urlopen("http://localhost:8080").read()
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
<launch>
|
|
||||||
<!-- Verify all the required nodes basically work -->
|
|
||||||
|
|
||||||
<node pkg="mavros" type="mavros_node" name="mavros" required="true" output="screen">
|
|
||||||
<param name="fcu_url" value="udp://@127.0.1:14557"/>
|
|
||||||
<rosparam command="load" file="$(find clever)/launch/mavros_config.yaml"/>
|
|
||||||
</node>
|
|
||||||
|
|
||||||
<node name="visualization" pkg="mavros_extras" type="visualization" required="true">
|
|
||||||
<remap to="mavros/local_position/pose" from="local_position"/>
|
|
||||||
<remap to="mavros/setpoint_position/local" from="local_setpoint"/>
|
|
||||||
<param name="fixed_frame_id" value="map"/>
|
|
||||||
<param name="child_frame_id" value="base_link"/>
|
|
||||||
<param name="marker_scale" value="1"/>
|
|
||||||
<param name="max_track_size" value="20"/>
|
|
||||||
<param name="num_rotors" value="4"/>
|
|
||||||
</node>
|
|
||||||
|
|
||||||
<node name="web_video_server" pkg="web_video_server" type="web_video_server" required="true" output="screen">
|
|
||||||
<param name="default_stream_type" value="ros_compressed"/>
|
|
||||||
<param name="publish_rate" value="1.0"/>
|
|
||||||
</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" required="true"/>
|
|
||||||
|
|
||||||
<node name="simple_offboard" pkg="clever" type="simple_offboard" required="true" output="screen">
|
|
||||||
<param name="reference_frames/body" value="map"/>
|
|
||||||
<param name="reference_frames/base_link" value="map"/>
|
|
||||||
</node>
|
|
||||||
|
|
||||||
<node name="tf2_web_republisher" pkg="tf2_web_republisher" type="tf2_web_republisher" required="true"/>
|
|
||||||
|
|
||||||
<node name="rc" pkg="clever" type="rc" required="true" output="screen"/>
|
|
||||||
|
|
||||||
<param name="test_module" value="$(find clever)/test/basic.py"/>
|
|
||||||
<test test-name="basic_test" pkg="ros_pytest" type="ros_pytest_runner"/>
|
|
||||||
</launch>
|
|
||||||
|
Before Width: | Height: | Size: 83 KiB After Width: | Height: | Size: 83 KiB |
|
Before Width: | Height: | Size: 68 KiB |
|
Before Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 236 KiB |
|
Before Width: | Height: | Size: 338 KiB |
|
Before Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 272 KiB |
|
Before Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 79 KiB |
|
Before Width: | Height: | Size: 143 KiB |
|
Before Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 268 KiB |
|
Before Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 431 KiB |
|
Before Width: | Height: | Size: 656 KiB |
|
Before Width: | Height: | Size: 350 KiB |
|
Before Width: | Height: | Size: 683 KiB |
|
Before Width: | Height: | Size: 305 KiB |
|
Before Width: | Height: | Size: 265 KiB |
|
Before Width: | Height: | Size: 171 KiB |
|
Before Width: | Height: | Size: 318 KiB |
|
Before Width: | Height: | Size: 102 KiB |
|
Before Width: | Height: | Size: 61 KiB |
|
Before Width: | Height: | Size: 224 KiB |
|
Before Width: | Height: | Size: 627 KiB |
|
Before Width: | Height: | Size: 467 KiB |
|
Before Width: | Height: | Size: 228 KiB |
|
Before Width: | Height: | Size: 70 KiB |
|
Before Width: | Height: | Size: 61 KiB |
|
Before Width: | Height: | Size: 52 KiB |
|
Before Width: | Height: | Size: 140 KiB |
|
Before Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 9.7 KiB |
|
Before Width: | Height: | Size: 46 KiB |
|
Before Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 192 KiB |
|
Before Width: | Height: | Size: 6.1 KiB |
@@ -27,4 +27,4 @@ Using Fig. 1a, 1b, 2a, 2b, map its own control signal to each motor, and connect
|
|||||||
|
|
||||||
For example, motor M3 that rotates counter-clockwise (top left corner) is controlled by signal S4 (green wire). It is connected to port 3.
|
For example, motor M3 that rotates counter-clockwise (top left corner) is controlled by signal S4 (green wire). It is connected to port 3.
|
||||||
|
|
||||||

|

|
||||||
|
|||||||
@@ -3,7 +3,7 @@ Clever
|
|||||||
|
|
||||||
<img src="../assets/clever3.png" align="right" width="300px" alt="Klever">
|
<img src="../assets/clever3.png" align="right" width="300px" alt="Klever">
|
||||||
|
|
||||||
CLEVER (Russian: *"Клевер"*, meaning *"Clover"*) is an educational kit of a programmable quadcopter that consists of popular open source components, and a set of necessary documentation and libraries for working with it.
|
"Clever" is an educational constructor set of a programmable quadcopter that consists of popular open source components, and a set of necessary documentation and libraries for working with it.
|
||||||
|
|
||||||
The kit includes a Pixhawk/Pixracer flight controller with the PX4 flight stack, a Raspberry Pi 3 as a controlling onboard computer, and a camera module for performing flights with the use of computer vision, and a set of various sensors and other peripherals.
|
The kit includes a Pixhawk/Pixracer flight controller with the PX4 flight stack, a Raspberry Pi 3 as a controlling onboard computer, and a camera module for performing flights with the use of computer vision, and a set of various sensors and other peripherals.
|
||||||
|
|
||||||
|
|||||||
@@ -4,14 +4,14 @@
|
|||||||
* Assembly
|
* Assembly
|
||||||
* [Clever 2 assembly](assemble_2.md)
|
* [Clever 2 assembly](assemble_2.md)
|
||||||
* [Clever 3 assembly](assemble_3.md)
|
* [Clever 3 assembly](assemble_3.md)
|
||||||
* [FPV Setup](fpv.md)
|
* [Installation of FPV](fpv.md)
|
||||||
* [Safety tips](safety.md)
|
* [Safety instruction](safety.md)
|
||||||
* [Connecting 4 in 1 ESCs](4in1.md)
|
* [Connecting 4 in 1 ESCs](4in1.md)
|
||||||
* [Types of power connectors](connectortypes.md)
|
* [Types of power connectors](connectortypes.md)
|
||||||
* [Blanching](zap.md)
|
* [Blanching](zap.md)
|
||||||
* [Soldering safety](tb.md)
|
* [Soldering safety](tb.md)
|
||||||
* [Multimeter usage](test_connection.md)
|
* [Using multimeter](test_connection.md)
|
||||||
* [RC Troubleshooting](radioerrors.md)
|
* [Possible radio failures](radioerrors.md)
|
||||||
* [Connecting GPS](gps.md)
|
* [Connecting GPS](gps.md)
|
||||||
* Configuration
|
* Configuration
|
||||||
* [Initial setup](setup.md)
|
* [Initial setup](setup.md)
|
||||||
@@ -19,34 +19,31 @@
|
|||||||
* [Pixhawk / Pixracer Firmware](firmware.md)
|
* [Pixhawk / Pixracer Firmware](firmware.md)
|
||||||
* [PX4 Parameters](px4_parameters.md)
|
* [PX4 Parameters](px4_parameters.md)
|
||||||
* [PID Setup](calibratePID.md)
|
* [PID Setup](calibratePID.md)
|
||||||
* Working with Raspberry Pi
|
* Work with Raspberry Pi
|
||||||
* [Raspberry Pi](raspberry.md)
|
* [Raspberry Pi](raspberry.md)
|
||||||
* [RPi Image](microsd_images.md)
|
* [RPi Image](microsd_images.md)
|
||||||
* [RPi Connection to the Pixhawk](connection.md)
|
* [RPi Connection to the Pixhawk](connection.md)
|
||||||
* [Wi-Fi connection](wifi.md)
|
* [Wi-Fi connection](wifi.md)
|
||||||
* [Remote shell](ssh.md)
|
* [SSH access to Raspberry Pi](ssh.md)
|
||||||
* [Wi-Fi Configuration](network.md)
|
* [Configuring Wi-Fi](network.md)
|
||||||
* [Using QGroundControl over Wi-Fi](gcs_bridge.md)
|
* [Using QGroundControl via Wi-Fi](gcs_bridge.md)
|
||||||
* [Remote control app](rc.md)
|
* [Controlling Clever from a smartphone](rc.md)
|
||||||
* [UART settings](uart.md)
|
* [UART settings](uart.md)
|
||||||
* [Viewing images from cameras](web_video_server.md)
|
* [Viewing images from cameras](web_video_server.md)
|
||||||
* [Coordinate systems (frames)](frames.md)
|
* [Coordinate systems (frames)](frames.md)
|
||||||
* Programming
|
* Coding
|
||||||
* [ROS](ros.md)
|
* [ROS](ros.md)
|
||||||
* [MAVROS](mavros.md)
|
* [MAVROS](mavros.md)
|
||||||
* [Simple OFFBOARD](simple_offboard.md)
|
* [Simple OFFBOARD](simple_offboard.md)
|
||||||
* Fiducial markers (ArUco)
|
* [Navigation using ArUco markers](aruco.md)
|
||||||
* [Overview](aruco.md)
|
* [Automatic check](selfcheck.md)
|
||||||
* [Marker detection](aruco_marker.md)
|
* [Code examples](snippets.md)
|
||||||
* [Map-based navigation](aruco_map.md)
|
|
||||||
* [Automated self-checks](selfcheck.md)
|
|
||||||
* [Code snippets](snippets.md)
|
|
||||||
* [Adjusting the position of the main camera](camera_frame.md)
|
* [Adjusting the position of the main camera](camera_frame.md)
|
||||||
* [Computer vision basics](camera.md)
|
* [Working with the camera](camera.md)
|
||||||
* [LED strip](leds.md)
|
* [Working with a LED strip on Raspberry 3](leds.md)
|
||||||
* [Using rviz and rqt](rviz.md)
|
* [Using rviz and rqt](rviz.md)
|
||||||
* [Interfacing with a sonar](sonar.md)
|
* [Working with the ultrasonic distance gage](sonar.md)
|
||||||
* [Interfacing with a laser rangefinder](laser.md)
|
* [Working with a laser rangefinder](laser.md)
|
||||||
* [PX4 Simulation](sitl.md)
|
* [PX4 Simulation](sitl.md)
|
||||||
* [Software autorun](autolaunch.md)
|
* [Software autorun](autolaunch.md)
|
||||||
* [Controlling the copter from Arduino](arduino.md)
|
* [Controlling the copter from Arduino](arduino.md)
|
||||||
@@ -54,11 +51,11 @@
|
|||||||
* Clever-based projects
|
* Clever-based projects
|
||||||
* [Copter spheric guard](shield.md)
|
* [Copter spheric guard](shield.md)
|
||||||
* [Face recognition system](face_recognition.md)
|
* [Face recognition system](face_recognition.md)
|
||||||
* [Android RC app](android.md)
|
* [An Android transmitter](android.md)
|
||||||
* [Copter Hack 2018](copterhack2018.md)
|
* [Copter Hack 2018](copterhack2018.md)
|
||||||
* [Copter Hack 2017](copterhack2017.md)
|
* [Copter Hack 2017](copterhack2017.md)
|
||||||
* Supplementary materials
|
* Supplementary materials
|
||||||
* [Contribution Guidelines](contributing.md)
|
* [Contribution to Clever](contributing.md)
|
||||||
* [Flashing ESCs using BLHeliSuite](esc_firmware.md)
|
* [Flashing ESCs using BLHeliSuite](esc_firmware.md)
|
||||||
* [MAVLink](mavlink.md)
|
* [MAVLink](mavlink.md)
|
||||||
* [PX4 Logs and Topics](flight_logs.md)
|
* [PX4 Logs and Topics](flight_logs.md)
|
||||||
|
|||||||
165
docs/en/aruco.md
@@ -1,8 +1,9 @@
|
|||||||
# ArUco markers
|
# Navigation using ArUco markers
|
||||||
|
|
||||||
> **Note** The following applies to [image versions](microsd_images.md) **0.16** and up. Older documentation is still available [for version **0.15.1**](https://github.com/CopterExpress/clever/blob/v0.15.1/docs/ru/aruco.md).
|
> **Note** Documentation for the versions [of image](microsd_images.md), starting with **0.15**. For earlier versions, see [documentation for version **0.14**](https://github.com/CopterExpress/clever/blob/v0.14/docs/ru/aruco.md).
|
||||||
|
|
||||||
[ArUco markers](https://docs.opencv.org/3.2.0/d5/dae/tutorial_aruco_detection.html) are commonly used for vision-based position estimation.
|
[ArUco-Markers](https://docs.opencv.org/3.2.0/d5/dae/tutorial_aruco_detection.html) is a popular technology for positioning
|
||||||
|
robotic systems using computer vision.
|
||||||
|
|
||||||
Examples of ArUco markers:
|
Examples of ArUco markers:
|
||||||
|
|
||||||
@@ -12,13 +13,159 @@ Examples of ArUco markers:
|
|||||||
|
|
||||||
For rapid generation of markers for printing, you may use an online tool: http://chev.me/arucogen/.
|
For rapid generation of markers for printing, you may use an online tool: http://chev.me/arucogen/.
|
||||||
|
|
||||||
[Clever Raspberry Pi image](microsd_images.md) contains a pre-installed `aruco_pose` ROS package, which can be used for marker detection.
|
## aruco\_pose
|
||||||
|
|
||||||
## Modes of operation
|
The `aruco_pose` module allows restoring the position of the copter relative to the map of ArUco markers and communicating it to the flight controller using the [Vision Position Estimation](https://dev.px4.io/en/ros/external_position_estimation.html) mechanism.
|
||||||
|
|
||||||
There are several preconfigured modes of operation for ArUco markers on the Clever drone:
|
If the source of the copter position by the markers is available, the option appears for precise autonomous indoor navigation by the positions using the [simple_offboard](simple_offboard.md) module.
|
||||||
|
|
||||||
* [single marker detection and navigation](aruco_marker.md);
|
### Turning on
|
||||||
* [map-based navigation](aruco_map.md).
|
|
||||||
|
|
||||||
> **Info** Additional documentation for the `aruco_pose` ROS package is available [on GitHub](https://github.com/CopterExpress/clever/blob/master/aruco_pose/README.md).
|
Make sure that in the clever launch file \(`~/catkin_ws/src/clever/clever/launch/clever.launch`\), the start of aruco\_pose and [computer vision cameras](camera.md) is turned on:
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<arg name="main_camera" default="true"/>
|
||||||
|
```
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<arg name="aruco" default="true"/>
|
||||||
|
```
|
||||||
|
|
||||||
|
After the launch-file is edited, restart package `clever`:
|
||||||
|
|
||||||
|
```(bash)
|
||||||
|
sudo systemctl restart clever
|
||||||
|
```
|
||||||
|
|
||||||
|
### Calibrating the ArUco marker map
|
||||||
|
|
||||||
|
An automatically generated [ArUco-board](https://docs.opencv.org/trunk/db/da9/tutorial_aruco_board_detection.html) may be used as a map of marks.
|
||||||
|
|
||||||
|
The map of marks is adjusted using file `~/catkin_ws/src/clever/clever/launch/aruco.launch`. To use ArUco-board, enter its parameters:
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<node pkg="nodelet" type="nodelet" name="aruco_pose" args="load aruco_pose/aruco_pose nodelet_manager">
|
||||||
|
<param name="frame_id" value="aruco_map_raw"/>
|
||||||
|
<!-- the type of the marker field -->
|
||||||
|
<param name="type" value="gridboard"/>
|
||||||
|
|
||||||
|
<!-- the number of markets along x -->
|
||||||
|
<param name="markers_x" value="1"/>
|
||||||
|
|
||||||
|
<!-- the number of markers along y -->
|
||||||
|
<param name="markers_y" value="6"/>
|
||||||
|
|
||||||
|
<!-- ID of the first marker (left top) -->
|
||||||
|
<param name="first_marker" value="240"/>
|
||||||
|
|
||||||
|
<!-- the length of the marker side in meters -->
|
||||||
|
<param name="markers_side" value="0.3362"/>
|
||||||
|
|
||||||
|
<!-- distance between the murders -->
|
||||||
|
<param name="markers_sep" value="0.46"/>
|
||||||
|
</node>
|
||||||
|
```
|
||||||
|
|
||||||
|
The vertical and horizontal distance between the markers may be set separately:
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<!-- the horizontal distance between the markers -->
|
||||||
|
<param name="markers_sep_x" value="0.97"/>
|
||||||
|
|
||||||
|
<!-- the vertical distance between the marker -->
|
||||||
|
<param name="markers_sep_y" value="1.435"/>
|
||||||
|
```
|
||||||
|
|
||||||
|
If a map with a custom order of marker IDs is used, parameter `marker_ids` may be used:
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<rosparam param="marker_ids">[5, 7, 9, 11, 13, 15]</rosparam>
|
||||||
|
```
|
||||||
|
|
||||||
|
The markers are numbered from the top left corner of the field.
|
||||||
|
|
||||||
|
For monitoring the map that is currently used by the copter for navigation, one can watch the content of topic `aruco_pose/map_image`. In a browser, it may be viewed with [web\_video\_server](web_video_server.md) by following the link [http://192.168.11.1:8080/snapshot?topic=/aruco\_pose/map\_image](http://192.168.11.1:8080/snapshot?topic=/aruco_pose/map_image):
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
When flying, make sure that the markers glued to the floor correspond to the map.
|
||||||
|
|
||||||
|
In topic `aruco_pose/debug` \([http://192.168.11.1:8080/snapshot?topic=/aruco\_pose/debug](http://192.168.11.1:8080/snapshot?topic=/aruco_pose/debug)\) the current result of markers recognitions is available:
|
||||||
|
|
||||||
|
TODO
|
||||||
|
|
||||||
|
### The system of coordinates
|
||||||
|
|
||||||
|
According to [agreement](http://www.ros.org/reps/rep-0103.html), the standard ENU system of coordinates is used in the marker field:
|
||||||
|
|
||||||
|
* x — rightward \(conditional East\);
|
||||||
|
* y — forward \(conditional North\);
|
||||||
|
* z — upward.
|
||||||
|
|
||||||
|
_Note_: the definition above is provided for a situation when the marker field is on the floor.
|
||||||
|
|
||||||
|
First, the zero is the bottom left point of the marker field. The yaw angle is considered zero when the copter is faced rightward\(along the x-axis\).
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### Configuring the flight controller
|
||||||
|
|
||||||
|
Correct Vision Position Estimation requires making sure \(via [QGroundControl](gcs_bridge.md)\) that:
|
||||||
|
|
||||||
|
* **For Pixhawk**: Firmware with LPE \(local position estimator\) is installed. For Pixhawk [download firmware `px4fmu-v2_lpe.px4`](https://github.com/PX4/Firmware/releases).
|
||||||
|
|
||||||
|
**For Pixracer**: parameter `SYS_MC_EST_GROUP` should be set to`local_position_estimator, attitude_estimator_q`.
|
||||||
|
|
||||||
|
> **Note** After changing the value of parameter `SYS_MC_EST_GROUP` restart the flight controller.
|
||||||
|
* In parameter `LPE_FUSION` **only** flags `vision position`, `land detector` are enabled. The final value _20_.
|
||||||
|
* Compass disabled: `ATT_W_MAG` = 0
|
||||||
|
* Complimentary filter external heading weight: `ATT_W_EXT_HDG` = 0.5
|
||||||
|
* Orientation by yaw by vision enabled: `ATT_EXT_HDG_M` = 2 `MOCAP`.
|
||||||
|
* VPE settings: `LPE_VIS_DELAY` = 0 sec, `LPE_VIS_XY` = 0.1 m, `LPE_VIS_Z` = 0.15 m.
|
||||||
|
* Recommended settings for land detector:
|
||||||
|
* `COM_DISARM_LAND` = 1 s
|
||||||
|
* `LNDMC_ROT_MAX` = 45 deg
|
||||||
|
* `LNDMC_THR_RANGE` = 0.5
|
||||||
|
* `LNDMC_Z_VEL_MAX` = 1 m/s
|
||||||
|
|
||||||
|
<!--
|
||||||
|
For the ease of configuring, you may use a ready settings file for [Clever 2](https://github.com/CopterExpress/clever/blob/master/docs/assets/Clever2LPE_160118.params) or for [Clever 3](https://github.com/CopterExpress/clever/blob/master/docs/assets/Clever3_LPE_020218.params) and upload it to the controller using menu Tools - Load from file in tab Parameters in QGroundControl.
|
||||||
|
|
||||||
|

|
||||||
|
-->
|
||||||
|
|
||||||
|
### Flight
|
||||||
|
|
||||||
|
A properly configured copter starts holding position by VPE \(in [modes](modes.md) `POSCTL` or `OFFBOARD`\) automatically.
|
||||||
|
|
||||||
|
For [autonomous flights](simple_offboard.md) do you will be able to use functions `navigate`, `set_position`, `set_velocity`. For flying to specific coordinates of the marker field, use frame `aruco_map`:
|
||||||
|
|
||||||
|
```python
|
||||||
|
# First, the copter has to take off to see the map of marks
|
||||||
|
# and for frame aruco_map to appear:
|
||||||
|
navigate(0, 0, 2, frame_id='body', speed=0.5, auto_arm=True) # take off to the altitude of 2 meters
|
||||||
|
|
||||||
|
time.sleep(5)
|
||||||
|
|
||||||
|
# Flying to coordinate 2:2 of the marker field at the altitude of 2 meters
|
||||||
|
navigate(2, 2, 2, speed=1, frame_id='aruco_map') # flying to coordinate 2:2 at the altitude of 3 meters
|
||||||
|
```
|
||||||
|
|
||||||
|
See [other functions](simple_offboard.md) simple_offboard.
|
||||||
|
|
||||||
|
### Location of markers on the ceiling
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
To navigate by the markers placed on the ceiling, you need to set the main camera facing up and [set the corresponding frame of the camera](camera_frame.md).
|
||||||
|
|
||||||
|
To set the map of markers in a "turned over" system of coordinates, change parameter `aruco_orientation` in file `~/catkin_ws/src/clever/clever/aruco.launch`:
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<param name="aruco_orientation" value="map_upside_down"/>
|
||||||
|
```
|
||||||
|
|
||||||
|
When this parameter is set, frame aruco\_map will also be "turned over". Thus, to fly at the altitude of 2 meters below the ceiling, argument `z` should be set to 2:
|
||||||
|
|
||||||
|
```python
|
||||||
|
navigate(x=1, y=2, z=1.1, speed=0.5, frame_id='aruco_map')
|
||||||
|
|||||||
@@ -1,139 +0,0 @@
|
|||||||
# Map-based navigation with ArUco markers
|
|
||||||
|
|
||||||
> **Info** Marker detection requires the camera module to be correctly plugged in and [configured](camera.md).
|
|
||||||
|
|
||||||
<!-- -->
|
|
||||||
|
|
||||||
> **Hint** We recommend using our [custom PX4 firmware](firmware.md).
|
|
||||||
|
|
||||||
`aruco_map` module detects whole ArUco-based maps. Map-based navigation is possible using vision position estimate (VPE).
|
|
||||||
|
|
||||||
## Configuration
|
|
||||||
|
|
||||||
In order to enable map detection set `aruco_map` and `aruco_detect` arguments to `true` in `~/catkin_ws/src/clever/clever/launch/aruco.launch`:
|
|
||||||
|
|
||||||
```xml
|
|
||||||
<arg name="aruco_detect" default="true"/>
|
|
||||||
<arg name="aruco_map" default="true"/>
|
|
||||||
```
|
|
||||||
|
|
||||||
Set `aruco_vpe` to `true` to publish detected camera position to the flight controller as VPE data:
|
|
||||||
|
|
||||||
```xml
|
|
||||||
<arg name="aruco_vpe" default="true"/>
|
|
||||||
```
|
|
||||||
|
|
||||||
## Marker map definition
|
|
||||||
|
|
||||||
Map is defined in a text file; each line has the following format:
|
|
||||||
|
|
||||||
```
|
|
||||||
marker_id marker_size x y z z_angle y_angle x_angle
|
|
||||||
```
|
|
||||||
|
|
||||||
`N_angle` is the angle of rotation along the `N` axis in radians.
|
|
||||||
|
|
||||||
Map path is defined in the `map` parameter:
|
|
||||||
|
|
||||||
```xml
|
|
||||||
<param name="map" value="$(find aruco_pose)/map/map.txt"/>
|
|
||||||
```
|
|
||||||
|
|
||||||
Some map examples are provided in [`~/catkin_ws/src/clever/aruco_pose/map`](https://github.com/CopterExpress/clever/tree/master/aruco_pose/map).
|
|
||||||
|
|
||||||
Grid maps may be generated using the `genmap.py` script:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
rosrun aruco_pose genmap.py length x y dist_x dist_y first > ~/catkin_ws/src/clever/aruco_pose/map/test_map.txt
|
|
||||||
```
|
|
||||||
|
|
||||||
`length` is the size of each marker, `x` is the marker count along the *x* axis, `y` is the marker count along the *y* axis, `dist_x` is the distance between the centers of adjacent markers along the *x* axis, `dist_y` is the distance between the centers of the *y* axis, `first` is the ID of the first marker (bottom left marker, unless `--top-left` is specified), `test_map.txt` is the name of the generated map file. The optional `--top-left` parameter changes the numbering of markers, making the top left marker the first one.
|
|
||||||
|
|
||||||
Usage example:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
rosrun aruco_pose genmap.py 0.33 2 4 1 1 0 > ~/catkin_ws/src/clever/aruco_pose/map/test_map.txt
|
|
||||||
```
|
|
||||||
|
|
||||||
### Checking the map
|
|
||||||
|
|
||||||
The currently active map is posted in the `/aruco_map/image` ROS topic. It can be viewed using [web_video_server](web_video_server.md) by opening the following link: http://192.168.11.1:8080/snapshot?topic=/aruco_map/image
|
|
||||||
|
|
||||||
<img src="../assets/aruco-map.png" width=600>
|
|
||||||
|
|
||||||
Current estimated pose (relative to the detected map) is published in the `aruco_map/pose` ROS topic. If the VPE is disabled, the `aruco_map` [TF frame](frames.md) is created; otherwise, the `aruco_map_detected` frame is created instead. Visualization for the current map is also posted in the `aruco_map/visualization` ROS topic; it may be visualized in [rviz](rviz.md).
|
|
||||||
|
|
||||||
An easy to understand detected map visualization is posted in the `aruco_map/debug` ROS topic (live view is available on http://192.168.11.1:8080/stream_viewer?topic=/aruco_map/debug):
|
|
||||||
|
|
||||||
<img src="../assets/aruco-map-debug.png" width=600>
|
|
||||||
|
|
||||||
## Coordinate system
|
|
||||||
|
|
||||||
The marker map adheres to the [ROS coordinate system convention](http://www.ros.org/reps/rep-0103.html), using the <abbr title="East-North-Up">ENU</abbr> coordinate system:
|
|
||||||
|
|
||||||
* the **<font color=red>x</font>** axis points to the right side of the map;
|
|
||||||
* the **<font color=green>y</font>** axis points to the top side of the map;
|
|
||||||
* the **<font color=blue>z</font>** axis points outwards from the plane of the marker
|
|
||||||
|
|
||||||
<img src="../assets/aruco-map-axis.png" width="600">
|
|
||||||
|
|
||||||
## VPE setup
|
|
||||||
|
|
||||||
In order to enable vision position estimation you should use the following [PX4 parameters](px4_parameters.md).
|
|
||||||
|
|
||||||
If you're using **EKF2** estimator (`SYS_MC_EST_GROUP` parameter is set to `ekf2`), make sure the following is set:
|
|
||||||
|
|
||||||
* `EKF2_AID_MASK` should have `vision position fusion` and `vision yaw fusion` flags set.
|
|
||||||
* Vision angle observations noise: `EKF2_EVA_NOISE` = 0.1 rad.
|
|
||||||
* Vision position observations noise: `EKF2_EVP_NOISE` = 0.1 m.
|
|
||||||
* `EKF2_EV_DELAY` = 0.
|
|
||||||
|
|
||||||
If you're using **LPE** (`SYS_MC_EST_GROUP` parameter is set to `local_position_estimator,attitude_estimator_q`):
|
|
||||||
|
|
||||||
* `LPE_FUSION` should have `vision position` and `land detector` flags set. We suggest unsetting the `baro` flag for indoor flights.
|
|
||||||
* External heading (yaw) weight: `ATT_W_EXT_HDG` = 0.5.
|
|
||||||
* External heading (yaw) mode: `ATT_EXT_HDG_M` = 1 (`Vision`).
|
|
||||||
* Vision position standard deviations: `LPE_VIS_XY` = 0.1 m, `LPE_VIS_Z` = 0.1 m.
|
|
||||||
* `LPE_VIS_DELAY` = 0 sec.
|
|
||||||
|
|
||||||
> **Hint** In order to use LPE with the Pixhawk v1 hardware you should download the [`px4fmu-v2_lpe.px4` firmware](firmware.md)
|
|
||||||
|
|
||||||
## Flight
|
|
||||||
|
|
||||||
If the setup is done correctly, the drone will hold its position in `POSCTL` and `OFFBOARD` [flight modes](modes.md) automatically.
|
|
||||||
|
|
||||||
You will also be able to use `navigate`, `set_position` and `set_velocity` ROS services for [autonomous flights](simple_offboard.md). In order to fly to a specific coordinate within the ArUco map you should use the `aruco_map` frame:
|
|
||||||
|
|
||||||
```python
|
|
||||||
# Takeoff should be performed in the "body" frame; "aruco_map" frame will appear as soon as the drone detects the marker field
|
|
||||||
navigate(0, 0, 2, frame_id='body', speed=0.5, auto_arm=True) # Takeoff and hover 2 metres above the ground
|
|
||||||
|
|
||||||
time.sleep(5)
|
|
||||||
|
|
||||||
# Fly to the (2, 2) point on the marker field while being 2 metres above it
|
|
||||||
navigate(2, 2, 2, speed=1, frame_id='aruco_map')
|
|
||||||
```
|
|
||||||
|
|
||||||
## Additional settings
|
|
||||||
|
|
||||||
If the drone's position is not stable when VPE is used, try increasing the *P* term in the velocity PID regulator: increase the `MPC_XY_VEL_P` and `MPC_Z_VEL_P` parameters.
|
|
||||||
|
|
||||||
If the drone's altitude is not stable, try increasing the `MPC_Z_VEL_P` parameter and adjusting hover thrust via `MPC_THR_HOVER`.
|
|
||||||
|
|
||||||
## Placing markers on the ceiling
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
In order to navigate using markers on the ceiling, mount the onboard camera so that it points up and [adjust the camera frame accordingly](camera_frame.md).
|
|
||||||
|
|
||||||
You should also set the `known_tilt` parameter to `map_flipped` in both `aruco_detect` and `aruco_map` sections of `~/catkin_ws/src/clever/clever/launch/aruco.launch`:
|
|
||||||
|
|
||||||
```xml
|
|
||||||
<param name="known_tilt" value="map_flipped"/>
|
|
||||||
```
|
|
||||||
|
|
||||||
This will flip the `aruco_map` frame (making its **<font color=blue>z</font>** axis point downward). Thus, in order to fly 2 metres below ceiling, the `z` argument for the `navigate` service should be set to 2:
|
|
||||||
|
|
||||||
```python
|
|
||||||
navigate(x=1, y=1.1, z=2, speed=0.5, frame_id='aruco_map')
|
|
||||||
```
|
|
||||||
@@ -1,111 +0,0 @@
|
|||||||
# ArUco marker detection
|
|
||||||
|
|
||||||
> **Info** Marker detection requires the camera module to be correctly plugged in and [configured](camera.md).
|
|
||||||
|
|
||||||
`aruco_detect` module detects ArUco markers and publishes their positions in ROS topics and as [TF frames](frames.md).
|
|
||||||
|
|
||||||
This is useful in conjunction with other positioning systems, such as [GPS](gps.md), [Optical Flow](optical_flow.md), PX4Flow, visual odometry, ultrasonic ([Marvelmind](https://marvelmind.com)) or UWB-based ([Pozyx](https://www.pozyx.io)) localization.
|
|
||||||
|
|
||||||
Using this module along with [map-based navigation](aruco_map.md) is also possible.
|
|
||||||
|
|
||||||
## Setup
|
|
||||||
|
|
||||||
Set the `aruco_detect` argument in `~/catkin_ws/src/clever/clever/launch/aruco.launch` to `true` to automatically launch the module:
|
|
||||||
|
|
||||||
```xml
|
|
||||||
<arg name="aruco_detect" default="true"/>
|
|
||||||
```
|
|
||||||
|
|
||||||
For the module to work correctly the following parameters should be set:
|
|
||||||
|
|
||||||
```xml
|
|
||||||
<param name="length" value="0.32"/> <!-- length of a single marker, in meters (excluding the white border) -->
|
|
||||||
<param name="estimate_poses" value="true"/> <!-- position estimation for single markers -->
|
|
||||||
<param name="send_tf" value="true"/> <!-- TF frame creation for markers -->
|
|
||||||
<param name="known_tilt" value="map"/> <!-- Marker tilt, explained below -->
|
|
||||||
```
|
|
||||||
|
|
||||||
`known_tilt` should be set to:
|
|
||||||
|
|
||||||
* `map` if *all* markers are on the ground;
|
|
||||||
* `map_flipped` if *all* markers are on the ceiling;
|
|
||||||
* an empty string otherwise.
|
|
||||||
|
|
||||||
You may specify length for each marker individually by using the `length_override` parameter:
|
|
||||||
|
|
||||||
```xml
|
|
||||||
<param name="length_override/3" value="0.1"/> <!-- marker with id=3 has a side of 0.1m -->
|
|
||||||
<param name="length_override/17" value="0.25"/> <!-- marker with id=17 has a side of 0.25m -->
|
|
||||||
```
|
|
||||||
|
|
||||||
## Coordinate system
|
|
||||||
|
|
||||||
Each marker has its own coordinate systems. It is aligned as follows:
|
|
||||||
|
|
||||||
* the **<font color=red>x</font>** axis points to the right side of the marker;
|
|
||||||
* the **<font color=green>y</font>** axis points to the top side of the marker;
|
|
||||||
* the **<font color=blue>z</font>** axis points outwards from the plane of the marker
|
|
||||||
|
|
||||||
<img src="../assets/aruco-axis.png" width="300">
|
|
||||||
|
|
||||||
## Working with detected markers
|
|
||||||
|
|
||||||
Navigation within the marker-based TF frames is possible with `simple_offboard` node.
|
|
||||||
|
|
||||||
Sample code to fly to a point 1 metre above marker with id 5:
|
|
||||||
|
|
||||||
```python
|
|
||||||
navigate(frame_id='aruco_5', x=0, y=0, z=1)
|
|
||||||
```
|
|
||||||
|
|
||||||
Sample code to fly to a point 1 metre to the left and 2 metres above marker with id 7:
|
|
||||||
|
|
||||||
```python
|
|
||||||
navigate(frame_id='aruco_7', x=-1, y=0, z=2)
|
|
||||||
```
|
|
||||||
|
|
||||||
Sample code to rotate counterclockwise while hovering 1.5 metres above marker id 10:
|
|
||||||
|
|
||||||
```python
|
|
||||||
navigate(frame_id='aruco_10', x=0, y=0, z=1.5, yaw_rate=0.5)
|
|
||||||
```
|
|
||||||
|
|
||||||
Note that if the required marker isn't detected for 0.5 seconds after the `navigate` command, the command will be ignored.
|
|
||||||
|
|
||||||
These frames may also be used in other services that accept TF frames (like `get_telemetry`). The following code will get the drone's position relative to the marker with id 3:
|
|
||||||
|
|
||||||
```python
|
|
||||||
telem = get_telemetry(frame_id='aruco_3')
|
|
||||||
```
|
|
||||||
|
|
||||||
Note that if the required marker isn't detected for 0.5 seconds, the `telem.x`, `telem.y`, `telem.z`, `telem.yaw` fields will contain `NaN`.
|
|
||||||
|
|
||||||
## Handling marker detection in Python
|
|
||||||
|
|
||||||
The following snippet shows how to read the `aruco_detect/markers` topic in Python:
|
|
||||||
|
|
||||||
```python
|
|
||||||
import rospy
|
|
||||||
from aruco_pose.msg import MarkerArray
|
|
||||||
rospy.init_node('my_node')
|
|
||||||
|
|
||||||
# ...
|
|
||||||
|
|
||||||
def markers_callback(msg):
|
|
||||||
print 'Detected markers:':
|
|
||||||
for marker in msg.markers:
|
|
||||||
print 'Marker: %s' % marker
|
|
||||||
|
|
||||||
# Create a Subscription object. Each time a message is posted in aruco_detect/markers, the markers_callback function is called with this message as its argument.
|
|
||||||
rospy.Subscriber('aruco_detect/markers', MarkerArray, markers_callback)
|
|
||||||
|
|
||||||
# ...
|
|
||||||
|
|
||||||
rospy.spin()
|
|
||||||
```
|
|
||||||
|
|
||||||
Each message contains the marker ID, its corner points on the image and its position relative to the camera.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
Suggested reading: [map-based navigation](aruco_map.md)
|
|
||||||
@@ -9,30 +9,30 @@ Clever 2 construction kit assembly instruction
|
|||||||
|
|
||||||
* Central frame ×2.
|
* Central frame ×2.
|
||||||
* Additional frame ×4.
|
* Additional frame ×4.
|
||||||
* Motor mount ×8.
|
* Beam ×8.
|
||||||
* Legs x8.
|
* Legs x8.
|
||||||
* Motor mount guard ×8.
|
* Beam guard ×8.
|
||||||
* Propeller guard ×16.
|
* Propeller guard ×16.
|
||||||
* Side guard ×16.
|
* Side guard ×16.
|
||||||
* Dalprop 5045 plastic propeller ×4.
|
* Dalprop 5045 plastic propeller ×4.
|
||||||
* Racerstar BR2205 2300kV brushless motor ×4.
|
* Racerstar BR2205 2300kV brushless motor ×4.
|
||||||
* Speed controllers ESC, DYS XSD20А ×4.
|
* Speed adjusters ESC, DYS XSD20А ×4.
|
||||||
* Power controller XT60 pin ×1.
|
* Power controller XT60 pin ×1.
|
||||||
* Power connector XT60 socket ×1.
|
* Power connector XT60 socket ×1.
|
||||||
* Three-wire female-female flat cable ×2.
|
* Three-wire female-female flat cable ×2.
|
||||||
* Multicore silicone insulated copper wire 14AWG (red, black), 50 cm long
|
* Wire copper multicore silicone insulated cable 14AWG (red, black), 50 cm long
|
||||||
* Power distribution board PDB BeeRotor Power Distribution Board V2.0 ×1.
|
* Power distribution board PDB BeeRotor Power Distribution Board V2.0 ×1.
|
||||||
* Li-ion rechargeable battery (battery) 18650 ×8.
|
* Li-ion rechargeable battery (battery) 18650 ×8.
|
||||||
* EFEST Luc V4 Li-lon Charger ×1.
|
* EFEST Luc V4 Li-lon Charger ×1.
|
||||||
* Protective case for regulators ×4.
|
* Regulators protective case ×4.
|
||||||
* Legs attachment ×8.
|
* Legs attachment ×8.
|
||||||
* Pixhawk flight controller ×1.
|
* Pixhawk flight controller ×1.
|
||||||
* FlySky i6 radio receiver ×1.
|
* FlySky i6 radio receiver×1.
|
||||||
* FlySky i6 radio transmitter ×1.
|
* FlySky i6 radio transmitter ×1.
|
||||||
* EFEST LUC V4 Charger ×1.
|
* EFEST LUC V4 Charger ×1.
|
||||||
* Micro USB to USB Cable ×1
|
* Micro USB to USB Cable ×1
|
||||||
* Battery compartment 18650 li-Ion ×1
|
* Battery compartment 18650 li-Ion ×1
|
||||||
* Multicore silicone insulated copper wire 18AWG (red, black), 100 cm long
|
* Wire copper multicore silicone insulated cable 18AWG (red, black), 100 cm long
|
||||||
* AA battery ×4
|
* AA battery ×4
|
||||||
* Jumper, Bind-plug
|
* Jumper, Bind-plug
|
||||||
|
|
||||||
@@ -45,16 +45,16 @@ Clever 2 construction kit assembly instruction
|
|||||||
* М3х16 screws ×40.
|
* М3х16 screws ×40.
|
||||||
* Plastic nuts ×8.
|
* Plastic nuts ×8.
|
||||||
* Metal nuts ×48.
|
* Metal nuts ×48.
|
||||||
* Stickers for the battery compartment ×8.
|
* Stickers for the compartment battery ×8.
|
||||||
* Thermal contraction tube ⌀15, .50 cm
|
* Thermal contraction tube ⌀15, .50 cm
|
||||||
* Thermal contraction tube ⌀5, 100 cm
|
* Thermal contraction tube ⌀5, 100 cm
|
||||||
* Double-sided 3M adhesive tape ×16.
|
* Double-sided 3M adhesive tape ×16.
|
||||||
* Screwdriver ×1 (visualization needed)
|
* Screwdriver ×1 (visualization needed)
|
||||||
* Insulation tape ×1
|
* Insulation tape ×1
|
||||||
* Scissors ×1
|
* Stationery scissors ×1
|
||||||
* Strap for the battery 250 mm ×1
|
* Strap for the battery 250 mm ×1
|
||||||
|
|
||||||
## Flysky i6 transmitter
|
## Functionality of the Flysky i6 transmitter
|
||||||
|
|
||||||
1. Switch A (SwA).
|
1. Switch A (SwA).
|
||||||
2. Switch B (SwB).
|
2. Switch B (SwB).
|
||||||
@@ -97,7 +97,7 @@ Clever 2 construction kit assembly instruction
|
|||||||
|
|
||||||
### Installation of motors
|
### Installation of motors
|
||||||
|
|
||||||
* Unpack the motors. Using pliers, shorten the wires on the motors by cutting half their length (leaving about 25 mm).
|
* Unpack the motors. Using pliers, shorten the wires on the motors by cutting half the length (leaving about 25 mm).
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
@@ -107,44 +107,44 @@ Strip
|
|||||||
|
|
||||||
Twist the wires.
|
Twist the wires.
|
||||||
|
|
||||||
Tin wires
|
Blanch
|
||||||
|
|
||||||
* Apply flux to the exposed part of the wire.
|
* Apply flux to the exposed part of the wire.
|
||||||
* Cover the solder using tweezers.
|
* Cover the solder using tweezers.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
#### Fix the motor on the mount
|
#### Fix the motor on the beam
|
||||||
|
|
||||||
* Install the motor on the engraved side of the mount.
|
* Install the motor on the engraved side of the beam.
|
||||||
* Attach the motors to the mounts with М3х8 screws using a screwdriver.
|
* Attach the motors to the beams with М3х8 screws using a screwdriver.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
* Mounts with motors should be arranged according to the diagram. The arrows indicate the direction of motor rotation direction.
|
* Beams with motors should be arranged according to the diagram. The arrows indicate the direction of motors rotation.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
### Tin three contact pads of the speed controller
|
### Blanch three contact pads of the adjuster
|
||||||
|
|
||||||
* Apply flux
|
* Apply flux
|
||||||
* Apply solder
|
* Apply solder
|
||||||
|
|
||||||
By warming up the contact pads of the controller, the tin will evenly fill the entire pad. To do so, apply heat by holding the soldering iron on the contact pads for 2 sencods (or more if needed).
|
To make solder neatly fill the entire pad, warm up the contact pad of the adjuster. For this purpose, hold the tip of the soldering gun to the contact pad for 2 seconds (or more if needed)
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
* Repeat this operation for the remaining three ESC
|
* Repeat this operation for the remaining three ESC
|
||||||
|
|
||||||
### Solder the wires of the motors to the ESC
|
### Solder the wires of the motors to the ESC
|
||||||
|
|
||||||
Solder the prepared wires of the motors to the pads of the controllers.
|
Solder the prepared wires of the motors to the pads of ESC.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
* Repeat this operation for the remaining three ESC
|
* Repeat this operation for the remaining three ESC
|
||||||
|
|
||||||
### Power connectors installation
|
### Installation of power connectors
|
||||||
|
|
||||||
#### Preparing wires for XT60 power connectors
|
#### Preparing wires for XT60 power connectors
|
||||||
|
|
||||||
@@ -154,7 +154,7 @@ Solder the prepared wires of the motors to the pads of the controllers.
|
|||||||
* Length 7 cm (XT60 pin power connector) - 1 red, 1 black
|
* Length 7 cm (XT60 pin power connector) - 1 red, 1 black
|
||||||
* Length 9 cm (XT60 socket power connector) - 1 red, 1 black
|
* Length 9 cm (XT60 socket power connector) - 1 red, 1 black
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
#### Preparing XT60 pin and XT60 socket high-power connectors
|
#### Preparing XT60 pin and XT60 socket high-power connectors
|
||||||
|
|
||||||
@@ -162,23 +162,23 @@ Solder the prepared wires of the motors to the pads of the controllers.
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
1. Tin two red and black 14AWG 7 cm long power wires for the XT60 pin connector.
|
1. Blanch two red and black 14AWG power wires 7 cm long for the XT60 pin connector .
|
||||||
2. Tin contact pads of the XT60 pin connector.
|
2. Blanch contact pads of the XT60 pin connector.
|
||||||
3. Solder the black wire to the “-” contact of the connector.
|
3. Solder the black wire to the “-” contact of the connector.
|
||||||
4. Solder the red wire to the “+” contact of the connector.
|
4. Solder the red wire to the “+” contact of the connector .
|
||||||
5. Cut ⌀5 heat-shrink tubing (2 sections × 10 mm).
|
5. Cut ⌀5 thermal contraction tube (2 sections × 10 mm).
|
||||||
6. Slip the ⌀5 heat-shrink tubing tube on the wires so that they cover the contact pads of the wires from XT60.
|
6. Put the ⌀5 thermal contraction tube on the wires so that it covers the contact pads of the wires from XT60.
|
||||||
7. Shrink the heat-shrink tubing with a hot air gun. 
|
7. Shrink the thermal contraction tube with a hot air gun. 
|
||||||
8. Repeat the procedure for XT60 socket connector.
|
8. Repeat the procedure for XT60 socket connector.
|
||||||
|
|
||||||
#### Preparation of the 5V power connectors for the control circuit
|
#### Preparation of the power connector for the 5V control circuit
|
||||||
|
|
||||||
1. Trim/pull out all pins from one of the connectors. Disconnect it.
|
1. Trim/pull all pins from one of the connectors. Disconnect it.
|
||||||
2. Using an utility knife, pry the retainer off on the remaining connector to release the 3rd wire.
|
2. Using a utility knife, pry the retainer off on the remaining connector to release the 3rd wire.
|
||||||
3. Remove the 3rd (orange) wire from the connector, since it is not needed.
|
3. Remove the 3rd (orange) wire from the connector, since it is not needed.
|
||||||
4. The length of the remaining black and red wires should be of 10 – 12 cm.
|
4. The length of the remaining black and red wires should be 10 – 12 cm.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
### Installation of the power distribution board
|
### Installation of the power distribution board
|
||||||
|
|
||||||
@@ -188,29 +188,29 @@ Solder the prepared wires of the motors to the pads of the controllers.
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
Check OPEN CONDITION of the following circuits (the multimeter does not beep):
|
Check OPEN CONDITION of the following circuits (absence of the multimeter sound signal):
|
||||||
|
|
||||||
* “BAT+” and “BAT-”
|
* “BAT+” and “BAT-”
|
||||||
* “12V” and “GND”
|
* “12V” and “GND”
|
||||||
* “5V” and “GND”
|
* “5V” and “GND”
|
||||||
|
|
||||||
Check CLOSED CONDITION of the following circuits (the multimeter beeps):
|
Check CLOSED CONDITION of the following circuits (presence of the multimeter sound signal):
|
||||||
|
|
||||||
* “BAT-” with every contact marked “-” and “GND”
|
* “BAT-” with every contact marked “-” and “GND”
|
||||||
* “BAT+”, with every contact marked “+”
|
* “BAT+”, with every contact marked “+”
|
||||||
|
|
||||||
### Tin the contact pads of the power board
|
### Blanch the contact pads of the power board
|
||||||
|
|
||||||
1. [Tin*] (zap.md) the contact pads of the power board.
|
1. [Blanch*] (zap.md) the contact pads of the power board
|
||||||
2. Using a multimeter, check absence of short-circuits on the PCB (check continuity).
|
2. Using a multimeter, check absence of contact closure on the PCB (check continuity)
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
By warming up the contact pads of the controller, the tin will evenly fill the entire pad. To do so, apply heat by holding the soldering iron on the contact pads for 2 sencods (or more if needed).
|
To make solder neatly fill the entire pad, it should be warmed up. For this purpose, hold the tip of the soldering gun to the contact pad for 2 seconds (or more if needed)
|
||||||
|
|
||||||
#### Soldering the XT60 high power connector
|
#### Soldering the XT60 high power connector
|
||||||
|
|
||||||
Solder the connector for battery, taking into account the polarity on the contact pads.
|
Solder the connector for battery, observing polarity on the contact pads.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
@@ -221,7 +221,7 @@ IMPORTANT NOTE about polarity
|
|||||||
|
|
||||||
#### Soldering of the power connector for the 5V control circuit
|
#### Soldering of the power connector for the 5V control circuit
|
||||||
|
|
||||||
Solder the 5V connector, taking into account the polarity on the contact pads.
|
Solder the 5V connector, observing polarity on the contact pads.
|
||||||
(in the picture: the red wire is “+”)
|
(in the picture: the red wire is “+”)
|
||||||
|
|
||||||

|

|
||||||
@@ -232,32 +232,32 @@ Solder the 5V connector, taking into account the polarity on the contact pads.
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
* Cut off 2 cm of high-power wire.
|
* Cut off 2 cm length of the high-power wire
|
||||||
* Strip on both ends.
|
* Strip on both ends.
|
||||||
* Tin.
|
* Blanch
|
||||||
* Make 3 jumpers.
|
* Make 3 jumpers
|
||||||
* Solder the jumpers according to the diagram.
|
* Solder the jumpers according to the diagram.
|
||||||
* Check for continuity with a multimeter. If necessary, clean with sand paper.
|
* Check continuity with a multimeter. If necessary, clean with sand paper.
|
||||||
|
|
||||||
#### Preparation of the battery compartment
|
#### Preparation of the battery compartment
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
* Conforming to the polarity, glue the sticker with markings inside the battery compartment.
|
* Glue a sticker with marking to the inside of the battery compartment in accordance with the polarity.
|
||||||
* Stick a strip of adhesive tape to the bottom of the compartment.
|
* Stick a strip of adhesive tape to the bottom of the compartment.
|
||||||
|
|
||||||
### Installation of the power distribution board
|
### Installation of the power distribution board
|
||||||
|
|
||||||
* Fix the power board to the frame with М3х8 screws and plastic nuts. 
|
* Fix the power board to the frame with М3х8 screws and plastic nuts. 
|
||||||
> **IMPORTANT** The white arrow on the BeeRotor board points towards the fore cutout.
|
> **IMPORTANT** An arrow on the board points to the fore cutout
|
||||||

|

|
||||||
|
|
||||||
#### Installation of elements
|
#### Installation of elements
|
||||||
|
|
||||||
1. Install the nuts into plastic holders. 
|
1. Install the nuts into plastic holders. 
|
||||||
2. Fix the motor mounts to the frame with М3х16 screws.
|
2. Fix the beams to the frame with М3х16 screws
|
||||||
* The mounts are installed above the frame.
|
* The beams are installed on top of the frame
|
||||||
* Plastic holders are installed beneath the frame. 
|
* Plastic holders are installed on the bottom of the frame. 
|
||||||
3. Arrangement of motors. Check arrangement of the motors (the motors with black nuts should be in the top left and lower right corners). 
|
3. Arrangement of motors. Check arrangement of the motors (the motors with black nuts should be in the top left and lower right corners). 
|
||||||
4. Put the power wires of the ESC through the holes. 
|
4. Put the power wires of the ESC through the holes. 
|
||||||
|
|
||||||
@@ -274,19 +274,19 @@ IMPORTANT NOTE about polarity
|
|||||||
|
|
||||||
### Pairing the receiver and transmitter
|
### Pairing the receiver and transmitter
|
||||||
|
|
||||||
1. Connect the radio receiver to the 5V connector. In any connector the GND is in the bottom. In the diagram, the power is labeled 5V 
|
1. Connect the radio receiver to the 5V connector. In any connector, GND in the bottom. In the diagram, the power is labeled 5V 
|
||||||
2. Connect the battery. The LED on the radio receiver should be flashing. ![Connecting the battery]
|
2. Connect the battery. The LED on the radio receiver should be flashing. ![Connecting the battery]
|
||||||
|
|
||||||
#### SAFETY when working with the battery
|
#### SAFETY when working with the battery
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
#### Enabling the transmitter
|
#### Enabling the transmitter
|
||||||
|
|
||||||
1. Insert the jumper into B/VCC of the radio receiver (short "ground" and "signal")
|
1. Insert the jumper into B/VCC of the radio receiver (short-circuit "ground" and "signal")
|
||||||
2. On the transmitter, hold down the BIND KEY button.
|
2. On the transmitter, hold down the BIND KEY button.
|
||||||
3. Power up the transmitter (flip the POWER switch, do not release BIND KEY).
|
3. Power up the transmitter (flip the POWER switch, do not release BIND KEY).
|
||||||
4. Connect the battery to the drone.
|
4. Connect the battery to the copter.
|
||||||
5. Wait for synchronization.
|
5. Wait for synchronization.
|
||||||
6. Disconnect the jumper.
|
6. Disconnect the jumper.
|
||||||
7. The LED will remain ON continuously.
|
7. The LED will remain ON continuously.
|
||||||
@@ -295,25 +295,25 @@ IMPORTANT NOTE about polarity
|
|||||||
|
|
||||||
[Radio equipment troubleshooting manual](radioerrors.md)
|
[Radio equipment troubleshooting manual](radioerrors.md)
|
||||||
|
|
||||||
### Checking the motors direction of rotation
|
### Checking the motors rotation direction
|
||||||
|
|
||||||
1. Apply stickers to the 18650 batteries.
|
1. Apply stickers to the 18650 battery.
|
||||||
2. Install the 18650 batteries into the compartment observing polarity. 
|
2. Install the 18650 battery into the compartment observing polarity. 
|
||||||
3. Check that the 5V power plug is connected to the receiver according to the circuit diagram.
|
3. Check that the 5V power plug is connected to the receiver according to the circuit diagram.
|
||||||
4. Connect the motor ESC to channel 3, marked as CH3 on the receiver as on the circuit diagram. 
|
4. Connect the motor ESC to channel 3 of the CH3 receiver according to the circuit diagram. 
|
||||||
5. Connect external power (battery).
|
5. Connect external power (battery).
|
||||||
6. Turn the transmitter ON.
|
6. Turn the transmitter ON
|
||||||
7. Using the left stick, set throttle to 10 %.
|
7. Using the left stick, set throttle to 10 %.
|
||||||
8. Check the motor direction of rotation according to the scheme. 
|
8. Check the motor rotation direction according to the scheme. 
|
||||||
9. If you have to change the rotation direction, swap any two phase wires of the motor (needs resoldering). 
|
9. If you have to change the rotation direction, toggle any two phase wires of the motor (needs resoldering). 
|
||||||
|
|
||||||
### Installation of the radio receiver
|
### Installation of the radio receiver
|
||||||
|
|
||||||
1. Install the 30 mm plastic legs on the frame with М3х8 screws.
|
1. Install the 30 mm plastic legs on the frame with М3х8 screws.
|
||||||
2. Pass the 5V power connector through the slit. 
|
2. Pass the 5V power connector through the slot. 
|
||||||
3. Attach the receiver to the bottom of the additional frame using double-sided adhesive tape and following the orientation of the engraved arrow. The antennas are to be pointing forward. 
|
3. Attach the receiver to the bottom of the additional frame using double-sided adhesive tape and noting the engraving. The antennas are to be pointing forward. 
|
||||||
4. Install the 3-wire flat cable into the PPM / CH1 channel. 
|
4. Install the 3-wire flat cable into the PPM / CH1 channel. 
|
||||||
5. Pass them through the slit to the 5 V connector.
|
5. Pass through the slot to the 5 V connector.
|
||||||
6. Screw the bottom an additional frame to the legs on the central frame with М3х8 screws. 
|
6. Screw the bottom an additional frame to the legs on the central frame with М3х8 screws. 
|
||||||
> **IMPORTANT** The directions of the arrows on the power supply board and the additional frame should coincide
|
> **IMPORTANT** The directions of the arrows on the power supply board and the additional frame should coincide
|
||||||
|
|
||||||
@@ -337,16 +337,16 @@ should be increased up to 4 – 5.
|
|||||||
2. Motors to MAIN OUT ports 1,2,3,4, according to the circuit diagram
|
2. Motors to MAIN OUT ports 1,2,3,4, according to the circuit diagram
|
||||||
3. Power by PDB (5V/VCC) to any port except for SB (SBUS)
|
3. Power by PDB (5V/VCC) to any port except for SB (SBUS)
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
### ESC assembly
|
### ESC assembly
|
||||||
|
|
||||||
1. Stick the double-sided adhesive tape to the base of the ESC protective case 
|
1. Stick the double-sided adhesive tape to the base of ESC protective case 
|
||||||
2. Put the ESCs into protective cases. Fasten the assembly to the motor mounts of the frame. 
|
2. Put the ESCs into protective cases. Fasten the assembly to the beams of the frame. 
|
||||||
|
|
||||||
### Installation of guard
|
### Installation of guard
|
||||||
|
|
||||||
1. Attach the lower guard with М3х16 screw to the motor mounts of the frame. 
|
1. Attach the lower guard with М3х16 screw to the beams of the frame. 
|
||||||
2. Attach the feet to the plastic holders with М3х16 screws. 
|
2. Attach the feet to the plastic holders with М3х16 screws. 
|
||||||
3. Attach the 30 mm long legs to the holes of the lower guard with М3х12 screw. 
|
3. Attach the 30 mm long legs to the holes of the lower guard with М3х12 screw. 
|
||||||
4. Attach the top guard with М3х12 screws. 
|
4. Attach the top guard with М3х12 screws. 
|
||||||
@@ -360,7 +360,7 @@ Requires the following components:
|
|||||||
* Additional frame (1 pc)
|
* Additional frame (1 pc)
|
||||||
* Battery compartment (1 pc)
|
* Battery compartment (1 pc)
|
||||||
|
|
||||||
1. Attach the battery compartment on top of the additional frame with М3х12 screws and nuts. 
|
1. Attach the battery compartment on the top additional frame with М3х12 screws and nuts. 
|
||||||
2. Attach the top additional frame to the legs with М3х8 screws. 
|
2. Attach the top additional frame to the legs with М3х8 screws. 
|
||||||
3. Install the battery into the battery compartment.
|
3. Install the battery into the battery compartment.
|
||||||
|
|
||||||
@@ -370,13 +370,13 @@ Requires the following components:
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
The drone is ready for configuration!
|
The copter is ready for configuration!
|
||||||
|
|
||||||
## Safety notes for assembly and configuration
|
## Safety notes for assembly ans configuration
|
||||||
|
|
||||||
1. Remove the propellers.“All ground operations are to be performed with propellers removed. Propellers are to be installed on the motors before the flight only.”
|
1. Remove the propellers.“All ground operations are to be performed with propellers removed. Propellers are to be installed on the motors before the flight only.”
|
||||||
2. Disconnect the battery. Keep the power off. “Assembly, configuration, and maintenance should be performed with power disconnected. Connect power only for testing electronic components of the drone. After testing, power is to be disconnected before other works.”
|
2. Disconnect the battery. Keep the power off. “Assembly, configuration, and maintenance should be performed with power disconnected. Connect power only for testing electronic components of the copter. After testing, power is to be disconnected before other works.”
|
||||||
3. Call for help. “If you experience problem when working with the drone, contact the instructor or the teacher, do not try to solve the problem yourself.”
|
3. Call for help. “If you experience problem when completing the task, contact the instructor or the teacher, do not try to solve the problem yourself.”
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
@@ -384,6 +384,6 @@ The drone is ready for configuration!
|
|||||||
|
|
||||||
1. Handle batteries carefully. Avoid falls, bumps, and deformations.
|
1. Handle batteries carefully. Avoid falls, bumps, and deformations.
|
||||||
2. When connecting (disconnecting) batteries, hold only the connectors, never pull or tug the wires.
|
2. When connecting (disconnecting) batteries, hold only the connectors, never pull or tug the wires.
|
||||||
3. If you see open connectors, violation of insulation or battery compartment integrity, do not touch it, and immediately inform the instructor or teacher.
|
3. If you see open connectors, violation of insulation or battery compartment integrity, do not touch it, and immediately inform the instructor.
|
||||||
|
|
||||||
See article [safety precautions when soldering and during drone flight operation](safety.md)
|
See article [safety precautions when soldering and during copter flight operation](safety.md)
|
||||||
|
|||||||
@@ -14,62 +14,62 @@ TODO
|
|||||||
|
|
||||||
## Additional equipment
|
## Additional equipment
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## Conventional symbols
|
## Conventional symbols
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## Motor installation
|
## Installation of motors
|
||||||
|
|
||||||
1. Unpack the motors.
|
1. Unpack the motors.
|
||||||
2. Attach a motor to the motor mount with М3х6 hex screws (the shortest screws supplied with the motors).
|
2. Attach a motor to the beam with М3х6 hex screws (the shortest screws supplied with the motors).
|
||||||
|
|
||||||
A hex wrench is included.
|
A hex wrench included.
|
||||||
|
|
||||||
3. Insert M3 nuts (4 pcs) into the plastic holder.
|
3. Insert M3 nuts (4 pcs) into the plastic holder.
|
||||||
|
|
||||||
The choice is yours to use a long screw or pliers.
|
For convenience, you can use a long screw or pliers
|
||||||
|
|
||||||
4. Secure the motor mount, the lower motor mount guard and the holder with М3х12 screws, using a Phillips screwdriver.
|
4. Secure the beam, the lower beam guard and the holder with М3х12 screws, using a Phillips screwdriver.
|
||||||
5. Using a clamp connect the motor mount and its bottom guard.
|
5. Using a clamp connect the beam and the bottom guard of the beam.
|
||||||
|
|
||||||
Cut the remaining part of the clamp (cable tie) with scissors.
|
Cut the remaining part of the clamp (tie wrap) with scissors.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## Frame elements installation
|
## Installation of frame elements
|
||||||
|
|
||||||
1. Insert the M3 plastic nuts (4 pcs) for mounting the PDB on the frame with М3х8 screws.
|
1. Insert the M3 plastic nuts (4 pcs) for mounting the PDB on the frame with М3х8 screws.
|
||||||
2. Install 6 mm legs (4 pcs) for attaching the Raspberry Pi to the frame with М3х8 screws.
|
2. Install 6 mm legs (4 pcs) for attaching the Raspberry Pi to the frame with М3х8 screws.
|
||||||
3. Attach the assembled unit to the frame with М3х16 screws, complying with the layout.
|
3. Attach the assembled unit to the frame with М3х16 screws, complying with the layout.
|
||||||
4. Install the frame for the LED strip, using the slots in the leg holders.
|
4. Install the frame for the LED strip, using the slots in the leg holders.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## BEC voltage converter installation(to be soldered and tested)
|
## Installation of the BEC voltage converter (to be soldered and tested)
|
||||||
|
|
||||||
1. Unpack the power board and install the power ribbon cable.
|
1. Unpack the power board and install the power flat cable.
|
||||||
2. Switch the multimeter in the DC voltage measurement mode (20V or 200V range).
|
2. Switch the multimeter in the DC voltage measurement mode (20V or 200V range).
|
||||||
3. Check the correct functionning of the power board by connecting the battery.
|
3. Check operation of the power board by connecting the battery
|
||||||
* Voltage measurements are to be made between black and red wires.
|
|
||||||
* Output voltage at the XT30 connector should be equal to the battery voltage (10 V to 12.6 V).
|
* Output voltage at the XT30 connector should be equal to the battery voltage (10 V to 12.6 V).
|
||||||
* The output voltage at the power ribbon cable should be between 4.9 V to 5.3 V.
|
* The output voltage at the power flat cable should be between 4.9 V to 5.3 V.
|
||||||
|
* To be measured between the black and the red wires.
|
||||||
4. Unpack the voltage converter and remove the transparent insulation.
|
4. Unpack the voltage converter and remove the transparent insulation.
|
||||||
5. Solder two additional wires to the BEC
|
5. Solder two additional wires to the BEC
|
||||||
* Take 3 male-female wires from the kit (red, black, and any color)
|
* Take 3 male-female wires from the kit (red, black, and any color)
|
||||||
* The red and black wires [are to be tinned](zap.md) on both ends using tweezers. The blue wire is to be tinned from the side of the MALE connector.
|
* The red and black wires [are to be blanched](zap.md) on both ends using tweezers. The blue wire is to be blanched from the side of the MALE connector.
|
||||||
|
|
||||||
To tin means:
|
To blanch means:
|
||||||
* Apply flux to the exposed part of the wire.
|
* Apply flux to the exposed part of the wire.
|
||||||
* Cover with tin.
|
* Cover with solder.
|
||||||
|
|
||||||
* Solder the red and the black wires to BEC:
|
* Solder the red and the black wires to BEC:
|
||||||
|
|
||||||
BLACK -> OUT-
|
BLACK -> OUT-
|
||||||
RED -> OUT+
|
RED -> OUT+
|
||||||
|
|
||||||
6. Check BEC functionning.
|
6. Check BEC operation.
|
||||||
* Solder the BEC to the power board:
|
* Solder the BEC to the power board:
|
||||||
|
|
||||||
BLACK -> -
|
BLACK -> -
|
||||||
@@ -77,25 +77,25 @@ TODO
|
|||||||
|
|
||||||
* Connect the battery and check the voltage at the wires soldered to BEC (from step 5).
|
* Connect the battery and check the voltage at the wires soldered to BEC (from step 5).
|
||||||
|
|
||||||
5 V - great, everything is working properly!
|
5 V - everything is okay!
|
||||||
|
|
||||||
more than 10 V - disconnect the power and move the yellow jumper to the other tweezers.
|
more than 10 V - disconnect the power and move the yellow jumper to the other tweezers.
|
||||||
|
|
||||||
0 V - not soldered properly.
|
0 V - poorly soldered.
|
||||||
* If the BEC outputs 5 V, isolate the soldered connection with a black heat-shrink tubing.
|
* If the BEC outputs 5 V, isolate the soldered connection with a black thermal contraction tube.
|
||||||
7. LED strip installation
|
7. Installation of the LED strip.
|
||||||
|
|
||||||
* Solder the wires from BEC (from step 5) to the LED strip.
|
* Solder the wires from BEC (from step 5) to the LED strip.
|
||||||
* Remove the silicone layer on the strip (make an incision with a knife and tear).
|
* Remove the silicone layer on the strip (make an incision with a knife and tear).
|
||||||
* [Tin](zap.md) the contacts of the LED strip.
|
* [Blanch](zap.md) the contacts of the LED strip.
|
||||||
|
|
||||||
Red -> +5V
|
Red -> +5V
|
||||||
Black -> GND
|
Black -> GND
|
||||||
Blue -> Din
|
Blue -> Din
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## 4 in 1 ESC board and the PDB power-board installation
|
## Installation of the 4 in 1 ESC board and the PDB power-board
|
||||||
|
|
||||||
1. Install the 4 in 1 ESC circuit-board as shown in the picture.
|
1. Install the 4 in 1 ESC circuit-board as shown in the picture.
|
||||||
|
|
||||||
@@ -105,7 +105,7 @@ TODO
|
|||||||
|
|
||||||
Screw M3 plastic nuts (4 PCs.) to the legs.
|
Screw M3 plastic nuts (4 PCs.) to the legs.
|
||||||
|
|
||||||
3. Install the PDB power distribution board as shown in the picture (the XT60 connector should point to the tail of the drone).
|
3. Install the PDB power distribution board as shown in the picture (the XT60 connector should point to the tail of the copter).
|
||||||
4. Connect the wires of the PCB power supply board and ESC XT30 board.
|
4. Connect the wires of the PCB power supply board and ESC XT30 board.
|
||||||
|
|
||||||

|

|
||||||
@@ -130,7 +130,7 @@ TODO
|
|||||||
> **Hint** If the remote cannot be powered on, or is blocked, see
|
> **Hint** If the remote cannot be powered on, or is blocked, see
|
||||||
article [remote faults](radioerrors.md).
|
article [remote faults](radioerrors.md).
|
||||||
|
|
||||||
## Checking the directions of motors rotation
|
## Checking the motors rotation direction
|
||||||
|
|
||||||
1. Turn the transmitter ON
|
1. Turn the transmitter ON
|
||||||
|
|
||||||
@@ -143,14 +143,14 @@ article [remote faults](radioerrors.md).
|
|||||||
2. Connect the S1 orange wire from the ESC board to CH3 on the receiver. Connect external power.
|
2. Connect the S1 orange wire from the ESC board to CH3 on the receiver. Connect external power.
|
||||||
3. Using the left stick, set throttle to 10 %.
|
3. Using the left stick, set throttle to 10 %.
|
||||||
4. Check the motor rotation direction according to the scheme. Repeat for each motor. Thus, it will be clear which motor is controlled.
|
4. Check the motor rotation direction according to the scheme. Repeat for each motor. Thus, it will be clear which motor is controlled.
|
||||||
5. If you have to change the rotation direction, swap any two phase wires of the motor (needs re-connection).
|
5. If you have to change the rotation direction, toggle any two phase wires of the motor (needs re-connection).
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## Installation and connection of the Pixracer flight controller
|
## Installation and connection of the Pixracer flight controller
|
||||||
|
|
||||||
1. Install the Pixracer flight controller on double-sided 3M adhesive tape (2 – 3 layers).
|
1. Install the Pixracer flight controller on double-sided 3M adhesive tape (2 – 3 layers).
|
||||||
The flight controller may also be removed from the housing and firmly mounted on the М3х6 leg.
|
The flight controller may also be removed from the housing and rigidly mounted on the М3х6 leg.
|
||||||
|
|
||||||
2. Install 40 mm legs using М3х87 screws.
|
2. Install 40 mm legs using М3х87 screws.
|
||||||
|
|
||||||
@@ -160,23 +160,23 @@ article [remote faults](radioerrors.md).
|
|||||||
|
|
||||||
More [about connecting 4 in 1 ESCs](4in1.md).
|
More [about connecting 4 in 1 ESCs](4in1.md).
|
||||||
|
|
||||||
4. Connect the ribbon cable from the radio receiver to the RCIN connector in Pixracer.
|
4. Connect the flat cable from the radio receiver to the RCIN connector in Pixracer.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## Raspberry installation
|
## Installation of Raspberry
|
||||||
|
|
||||||
1. Turn the drone upside down.
|
1. Turn the copter upside down.
|
||||||
|
|
||||||
Install the Raspberry on the legs using Raspberry mounting holes.
|
Install Raspberry on the legs using Raspberry mounting holes.
|
||||||
|
|
||||||
USB connectors should point to the tail of the drone.
|
USB connectors should point to the tail of the copter.
|
||||||
|
|
||||||
2. Installation of the ribbon cable for the camera:
|
2. Installation of the flat cable for the camera:
|
||||||
* lift the latch;
|
* lift the latch;
|
||||||
* connect the ribbon cable;
|
* connect the flat cable;
|
||||||
* close the latch.
|
* close the latch.
|
||||||
3. Connecting Raspberry to power supply:
|
3. Connecting Raspberry power:
|
||||||
|
|
||||||
5V -> pin 04 (DC power 5 V)
|
5V -> pin 04 (DC power 5 V)
|
||||||
GND -> pin 06 (Ground)
|
GND -> pin 06 (Ground)
|
||||||
@@ -188,24 +188,24 @@ article [remote faults](radioerrors.md).
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
## Arduino and FlySky radio receiver installation
|
## Installation of Arduino and the FlySky radio receiver
|
||||||
|
|
||||||
1. Solder Arduino Nano micro-controller pins to its board.
|
1. Mount the pins of the Arduino Nano micro-controller using soldering.
|
||||||
2. Install the micro-controller into a special mount, and attach to the lower deck using М3х16 screws (4 pcs).
|
2. Install the micro-controller into a special mount, and attach to the lower deck using М3х16 screws (4 pcs).
|
||||||
3. Using double-sided tape, attach the receiver as shown in the picture.
|
3. Using double-sided tape, attach the receiver as shown in the picture.
|
||||||
4. Connect the ribbon cable from the radio receiver to Pixracer as shown in the picture.
|
4. Connect the flat cable from the radio receiver to Pixracer as shown in the picture.
|
||||||
|
|
||||||
white -> PPM
|
white -> PPM
|
||||||
red -> 5V
|
red -> 5V
|
||||||
black -> GND
|
black -> GND
|
||||||
orange, green -> currently not used. They are set to the unused pins of the radio receiver.
|
orange, green -> currently not used. They are set to the unused pins of the radio receiver
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## RPi camera installation
|
## Installation of the RPi camera
|
||||||
|
|
||||||
1. Attach the mount for the RPi camera assembly to the lower deck with М3х12 screws (2 pcs.)
|
1. Attach the mount for the RPi camera assembly to the lower deck with М3х12 screws (2 pcs.)
|
||||||
2. Connect the ribbon cable to the RPi camera.
|
2. Connect the flat cable to the RPi camera.
|
||||||
3. Install the camera into the mount, secure it with M2 self-tappers.
|
3. Install the camera into the mount, secure it with M2 self-tappers.
|
||||||
4. Attach Raspberry with 30 mm legs (4 pcs.).
|
4. Attach Raspberry with 30 mm legs (4 pcs.).
|
||||||
|
|
||||||
@@ -213,7 +213,7 @@ article [remote faults](radioerrors.md).
|
|||||||
|
|
||||||
5. Install the legs into the mounts (4 pcs).
|
5. Install the legs into the mounts (4 pcs).
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## Installation of the remaining structural elements
|
## Installation of the remaining structural elements
|
||||||
|
|
||||||
@@ -225,11 +225,11 @@ article [remote faults](radioerrors.md).
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
## USB connectors installation
|
## Installation of USB connectors
|
||||||
|
|
||||||
1. Connect Pixracer to Raspberry using the micro USB - USB cable.
|
1. Connect Pixracer to Raspberry using the micro USB - USB cable.
|
||||||
2. Connect Arduino to Raspberry using the micro USB - USB cable.
|
2. Connect Arduino to Raspberry using the micro USB - USB cable.
|
||||||
|
|
||||||
.
|
.
|
||||||
|
|
||||||
Read more about connection in [article](connection.md).
|
Read more about connection in [article](connection.md).
|
||||||
|
|||||||
@@ -13,4 +13,4 @@ The Rate Pitch and Rate Roll parameters should be the same.
|
|||||||
|
|
||||||
YAW parameters should be changed individually, according to the above instruction (usually the yaw doesn't require serious adjustment, you may leave it default).
|
YAW parameters should be changed individually, according to the above instruction (usually the yaw doesn't require serious adjustment, you may leave it default).
|
||||||
|
|
||||||

|

|
||||||
|
|||||||
@@ -3,76 +3,34 @@ Pixhawk / Pixracer firmware flashing
|
|||||||
|
|
||||||
Pixhawk or Pixracer firmware may be flashed using QGroundControl or command line utilities.
|
Pixhawk or Pixracer firmware may be flashed using QGroundControl or command line utilities.
|
||||||
|
|
||||||
Modified firmware for Clever
|
Various releases of stable PX4 firmwares are available from [Releases at GitHub] (https://github.com/PX4/Firmware/releases).
|
||||||
---
|
|
||||||
|
|
||||||
It is advisable to use a specialized build of PX4 with the necessary fixes and better defaults for the Clever drone. Use the latest stable release in our [GitHub repository](https://github.com/CopterExpress/Firmware/releases) with the word `clever`, for example, `v1.8.2-clever.5`.
|
The name of the firmware file contains encoded information about the target circuit-board and the release. Examples:
|
||||||
|
|
||||||
<div id="release" style="display:none">
|
|
||||||
<p>Latest stable release: <strong><a id="download-latest-release"></a></strong>.</p>
|
|
||||||
|
|
||||||
<ul>
|
|
||||||
<li>Firmware for Pixracer (<strong>Clever 4 / Clever 3</strong>) – <a id="firmware-pixracer" href=""><code>px4fmu-v4_default.px4</code></a>.</li>
|
|
||||||
<li>Firmware for Pixhawk (<strong>Clever 2</strong>) – <a id="firmware-pixhawk" href=""><code>px4fmu-v2_lpe.px4</code></a>.</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script type="text/javascript">
|
|
||||||
// get latest release from GitHub
|
|
||||||
fetch('https://api.github.com/repos/CopterExpress/Firmware/releases').then(function(res) {
|
|
||||||
return res.json();
|
|
||||||
}).then(function(data) {
|
|
||||||
// look for stable release
|
|
||||||
let stable;
|
|
||||||
for (let release of data) {
|
|
||||||
let clever = release.name.indexOf('clever') != -1;
|
|
||||||
if (clever && !release.prerelease && !release.draft) {
|
|
||||||
stable = release;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let el = document.querySelector('#download-latest-release');
|
|
||||||
el.innerHTML = stable.name;
|
|
||||||
el.href = stable.html_url;
|
|
||||||
document.querySelector('#release').style.display = 'block';
|
|
||||||
for (let asset of stable.assets) {
|
|
||||||
console.log(asset.name);
|
|
||||||
if (asset.name == 'px4fmu-v4_default.px4') {
|
|
||||||
document.querySelector('#firmware-pixracer').href = asset.browser_download_url;
|
|
||||||
} else if (asset.name == 'px4fmu-v2_lpe.px4') {
|
|
||||||
document.querySelector('#firmware-pixhawk').href = asset.browser_download_url;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
QGroundControl
|
|
||||||
---
|
|
||||||
|
|
||||||
Open the Firmware section in QGroundControl. Then, connect your Pixhawk or Pixracer via USB.
|
|
||||||
|
|
||||||
Choose PX4 Flight Stack. If you wish to install the official firmware (with EKF2 for Pixhawk), choose "Standard version". In order to flash custom firmware, choose "Custom firmware file..." and click OK.
|
|
||||||
|
|
||||||
> **Warning** Do not unplug your flight controller from USB during flashing!
|
|
||||||
|
|
||||||
Firmware variants
|
|
||||||
---
|
|
||||||
|
|
||||||
The name of the firmware file contains information about the target flight controller and build variant. For example:
|
|
||||||
|
|
||||||
* `px4fmu-v4_default.px4` — firmware for Pixhawk with EKF2 and LPE (**Clever 3** / **Clever 4**).
|
|
||||||
* `px4fmu-v2_lpe.px4` — firmware for Pixhawk with LPE (**Clever 2**).
|
|
||||||
* `px4fmu-v2_default.px4` — firmware for Pixhawk with EKF2.
|
* `px4fmu-v2_default.px4` — firmware for Pixhawk with EKF2.
|
||||||
|
* `px4fmu-v2_lpe.px4` — firmware for Pixhawk with LPE.
|
||||||
|
* `px4fmu-v4_default.px4` — firmware for Pixhawk with EKF2 and LPE (*Clever 3*).
|
||||||
* `px4fmu-v3_default.px4` — firmware for newer Pixhawk versions (rev. 3 chip, see Fig. + Bootloader v5) with EKF2 and LPE.
|
* `px4fmu-v3_default.px4` — firmware for newer Pixhawk versions (rev. 3 chip, see Fig. + Bootloader v5) with EKF2 and LPE.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
> **Note** In order to flash the `px4fmu-v3_default.px4` file, you may need to use the `force_upload` command in the command prompt.
|
> **Note** To download the `px4fmu-v3_default.px4` file, you may need to use the `force_upload` command in the command prompt.
|
||||||
|
|
||||||
|
QGroundControl
|
||||||
|
---
|
||||||
|
|
||||||
|
In QGroundControl, go to Firmware. **After** that, connect Pixhawk / Pixracer via USB.
|
||||||
|
|
||||||
|
Select PX4 Flight Stack. To download and upload the standard firmware (the version with EKF2 for Pixhawk), select the "Standard Version" menu item, to load your own firmware file, select "Custom firmware file...", then click OK.
|
||||||
|
|
||||||
|
> **Warning** Do not disconnect the USB cable until the flashing process is complete.
|
||||||
|
|
||||||
|
TODO: Figure.
|
||||||
|
|
||||||
Command prompt
|
Command prompt
|
||||||
---
|
---
|
||||||
|
|
||||||
PX4 may be compiled from the source and automatically flashed to the flight controller from the command prompt.
|
PX4 may be compiled from the source and automatically loaded to the circuit-board from the command prompt.
|
||||||
|
|
||||||
To do this, clone the PX4 repository:
|
To do this, clone the PX4 repository:
|
||||||
|
|
||||||
@@ -86,9 +44,9 @@ Select the appropriate version (tag) using `git checkout`. Then compile and uplo
|
|||||||
make px4fmu-v4_default upload
|
make px4fmu-v4_default upload
|
||||||
```
|
```
|
||||||
|
|
||||||
Where `px4fmu-v4_default` is the required firmware variant.
|
Where `px4fmu-v4_default` is the required firmware version.
|
||||||
|
|
||||||
In order to upload the `v3` firmware to Pixhawk, you may need to use the `force_upload` option:
|
To upload the `v3` firmware to Pixhawk, you may need the `force_upload` command:
|
||||||
|
|
||||||
```
|
```
|
||||||
make px4fmu-v3_default force-upload
|
make px4fmu-v3_default force-upload
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
|
|
||||||
\* The distance between the power distribution board and the estimated location of the camera should be determined in advance!
|
\* The distance between the power distribution board and the estimated location of the camera should be determined in advance!
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## Preparation of the transmitter
|
## Preparation of the transmitter
|
||||||
|
|
||||||
@@ -26,20 +26,20 @@ The same procedure applies here:
|
|||||||
|
|
||||||
\* The distance between the power distribution board and the estimated location of the transmitter should be determined in advance!
|
\* The distance between the power distribution board and the estimated location of the transmitter should be determined in advance!
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## Connection of FPV
|
## Connection of FPV
|
||||||
|
|
||||||
Prepared connectors are to be inserted into appropriate sockets, and power wires are to be soldered to the power distribution board according to the circuit diagram:
|
Prepared connectors are to be inserted into appropriate sockets, and power wires are to be soldered to the power distribution board according to the circuit diagram:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
> **Warning** In this circuit diagram, the camera is powered from 12 V (however, it is possible to use 5 V).
|
> **Warning** In this circuit diagram, the camera is powered from 12 V (however, it is possible to use 5 V).
|
||||||
> The transmitter is powered from the ESC power (however, it is possible to use 12 V).
|
> The transmitter is powered from the ESC power (however, it is possible to use 12 V).
|
||||||
|
|
||||||
## Installing FPV components
|
## Installing FPV components
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
The following may be used as fastening materials:
|
The following may be used as fastening materials:
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Working with a laser rangefinder
|
# Working with a laser rangefinder
|
||||||
|
|
||||||
## VL53L1X Rangefinder
|
## Rangefinder VL53L1X
|
||||||
|
|
||||||
The rangefinder model recommended for Clever is STM VL53L1X. This rangefinder can measure distances from 0 to 4 m while ensuring high measurement accuracy.
|
The rangefinder model recommended for Clever is STM VL53L1X. This rangefinder can measure distances from 0 to 4 m while ensuring high measurement accuracy.
|
||||||
|
|
||||||
@@ -8,7 +8,7 @@ The [image for Raspberry Pi](microsd_images.md) contains pre-installed correspon
|
|||||||
|
|
||||||
### Connecting to Raspberry Pi
|
### Connecting to Raspberry Pi
|
||||||
|
|
||||||
> **Note** You need to flash a <a id="download-firmware" href="https://github.com/CopterExpress/Firmware/releases">custom PX4 firmware</a> on your flight controller for the rangefinder to work correctly. See more about firmware in the [corresponding article](firmware.md).
|
> **Note** For correct operation of a laser rangefinder with a flight countroller <a id="download-firmware" href="https://github.com/CopterExpress/Firmware/releases">custom PX4 firmware</a> is needed. See more about firmware in [corresponding article](firmware.md).
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
fetch('https://api.github.com/repos/CopterExpress/Firmware/releases').then(res => res.json()).then(function(data) {
|
fetch('https://api.github.com/repos/CopterExpress/Firmware/releases').then(res => res.json()).then(function(data) {
|
||||||
@@ -21,44 +21,44 @@ The [image for Raspberry Pi](microsd_images.md) contains pre-installed correspon
|
|||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
Connect the rangefinder to the 3V, GND, SCL and SDA pins via the I²C interface:
|
Connect the rangefinder to pins 3V, GND, SCL and SDA via the I²C interface:
|
||||||
|
|
||||||
<img src="../assets/raspberry-vl53l1x.png" alt="Connecting VL53L1X" height=600>
|
<img src="../assets/raspberry-vl53l1x.png" alt="Connecting VL53L1X" height=600>
|
||||||
|
|
||||||
If the pin marked GND is occupied, you can use any other ground pin (look at the [pinout](https://pinout.xyz) for reference).
|
If the pin marked GND is occupied, you can use another free one using the [pinout](https://pinout.xyz).
|
||||||
|
|
||||||
> **Hint** You can connect several peripheral devices via the I²C interface simultaneously. Use a parallel connection for that.
|
> **Hint** Via the I²C interface, you can connect several peripheral devices simultaneously. For this purpose, use a parallel connection.
|
||||||
|
|
||||||
### Enabling the rangefinder
|
### Enabling
|
||||||
|
|
||||||
[Connect via SSH](ssh.md) and edit file `~/catkin_ws/src/clever/clever/launch/clever.launch` so that the VL53L1X driver is enabled:
|
[Connect via SSH](ssh.md) and edit file `~/catkin_ws/src/clever/clever/launch/clever.launch` so that driver VL53L1X is enabled:
|
||||||
|
|
||||||
```xml
|
```xml
|
||||||
<arg name="rangefinder_vl53l1x" default="true"/>
|
<arg name="rangefinder_vl53l1x" default="true"/>
|
||||||
```
|
```
|
||||||
|
|
||||||
By default, the rangefinder driver sends the data to Pixhawk via the `/mavros/distance_sensor/rangefinder_sub` topic. To view data from the topic, use the following command:
|
By default, the rangefinder driver sends the data to Pixhawk (via topic `/mavros/distance_sensor/rangefinder_sub`). To view data from the topic, use command:
|
||||||
|
|
||||||
```bash
|
```(bash)
|
||||||
rostopic echo mavros/distance_sensor/rangefinder_sub
|
rostopic echo mavros/distance_sensor/rangefinder_sub
|
||||||
```
|
```
|
||||||
|
|
||||||
### PX4 settings
|
### PX4 settings
|
||||||
|
|
||||||
PX4 should be properly [configured](px4_parameters.md) to use the rangefinder data.
|
To use the rangefinder data in [PX4 must be configured](px4_parameters.md).
|
||||||
|
|
||||||
Set the following parameters when EKF2 is used (`SYS_MC_EST_GROUP` = `ekf2`):
|
When using EKF2 (`SYS_MC_EST_GROUP` = `ekf2`):
|
||||||
|
|
||||||
* `EKF2_HGT_MODE` = `2` (Range sensor) – for flights over horizontal floor;
|
* `EKF2_HGT_MODE` = `2` (Range sensor) – when flying over horizontal floor;
|
||||||
* `EKF2_RNG_AID` = `1` (Range aid enabled) – in other cases.
|
* `EKF2_RNG_AID` = `1` (Range aid enabled) – in other cases.
|
||||||
|
|
||||||
Set the following parameters when LPE is used (`SYS_MC_EST_GROUP` = `local_position_estimator, attitude_estimator_q`):
|
When using LPE (`SYS_MC_EST_GROUP` = `local_position_estimator, attitude_estimator_q`):
|
||||||
|
|
||||||
* The "pub agl as lpos down" flag should be set in the `LPE_FUSION` parameter – for flights over horizontal floor.
|
* The "pub agl as lpos down" flag is ticked in the `LPE_FUSION` parameter – when flying over horizontal floor.
|
||||||
|
|
||||||
### Receiving data in Python
|
### Obtaining data from Python
|
||||||
|
|
||||||
In order to receive data from the topic, create a subscriber:
|
To obtain data from the topic, create a subscriber:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
from sensor_msgs.msg import Range
|
from sensor_msgs.msg import Range
|
||||||
@@ -66,7 +66,7 @@ from sensor_msgs.msg import Range
|
|||||||
# ...
|
# ...
|
||||||
|
|
||||||
def range_callback(msg):
|
def range_callback(msg):
|
||||||
# Process data from the rangefinder
|
# Processing new data from the rangefinder
|
||||||
print 'Rangefinder distance:', msg.range
|
print 'Rangefinder distance:', msg.range
|
||||||
|
|
||||||
rospy.Subscriber('mavros/distance_sensor/rangefinder_sub', Range, range_callback)
|
rospy.Subscriber('mavros/distance_sensor/rangefinder_sub', Range, range_callback)
|
||||||
@@ -74,13 +74,13 @@ rospy.Subscriber('mavros/distance_sensor/rangefinder_sub', Range, range_callback
|
|||||||
|
|
||||||
### Data visualization
|
### Data visualization
|
||||||
|
|
||||||
You may use rqt_multiplot tool to plot rangefinder data.
|
To build a chart using the data from the rangefinder, one can use rqt_multiplot.
|
||||||
|
|
||||||
rviz may be used for data visualization. To do this, add a topic of the `sensor_msgs/Range` type to visualization:
|
rviz may be used for data visualization. To do this, add a topic of the `sensor_msgs/Range` type to visualization:
|
||||||
|
|
||||||
<img src="../assets/rviz-range.png" alt="Range in rviz">
|
<img src="../assets/rviz-range.png" alt="Range in rviz">
|
||||||
|
|
||||||
Read more [about rviz and rqt](rviz.md).
|
See [read more about rviz and rqt](rviz.md).
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
### Connecting to Pixhawk / Pixracer
|
### Connecting to Pixhawk / Pixracer
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ Control
|
|||||||
|
|
||||||
The copter is controlled from a transmitter that sends commands to the radio receiver. The transmitter is powered by batteries, and the radio receiver is powered from the flight controller. The communication is often one-way, from the transmitter to the receiver. The receiver is connected to the flight controller with at least five wires which are used for transmitting the turn signals around 3 axes, the throttle command, and the flight mode command.
|
The copter is controlled from a transmitter that sends commands to the radio receiver. The transmitter is powered by batteries, and the radio receiver is powered from the flight controller. The communication is often one-way, from the transmitter to the receiver. The receiver is connected to the flight controller with at least five wires which are used for transmitting the turn signals around 3 axes, the throttle command, and the flight mode command.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
**Throttle** — translated as "throttle", "thrust", or "gas" in everyday life. A multicopter throttle is the mean arithmetical between the rotation speeds of all motors is it more the throttle, the higher the total thrust of the engines, and the stronger they pull the copter upwards (in other words, "Step on it" means the fastest ascent possible). It is usually measured as percentage: 0 % — the motors are stopped, 100 % — the motors are rotating at maximum speed. Hovering throttle is the minimum throttle required for the copter to stay at certain altitude.
|
**Throttle** — translated as "throttle", "thrust", or "gas" in everyday life. A multicopter throttle is the mean arithmetical between the rotation speeds of all motors is it more the throttle, the higher the total thrust of the engines, and the stronger they pull the copter upwards (in other words, "Step on it" means the fastest ascent possible). It is usually measured as percentage: 0 % — the motors are stopped, 100 % — the motors are rotating at maximum speed. Hovering throttle is the minimum throttle required for the copter to stay at certain altitude.
|
||||||
|
|
||||||
@@ -77,15 +77,15 @@ The axes of the copter (pitch, roll, and yaw) are the angles used to determine a
|
|||||||
|
|
||||||
**Yaw** The multicopter nose turn. conditionally — turning right or left
|
**Yaw** The multicopter nose turn. conditionally — turning right or left
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
**Pitch**. In copters, manipulation with this moment of force allows the copter to move forward or backward due to tilting the nose in the appropriate direction
|
**Pitch**. In copters, manipulation with this moment of force allows the copter to move forward or backward due to tilting the nose in the appropriate direction
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
**Roll** Multicopter tilting to the left or to the right. Due to the roll, the copter can move sideways in the appropriate direction.
|
**Roll** Multicopter tilting to the left or to the right. Due to the roll, the copter can move sideways in the appropriate direction.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
If you can control throttle, pitch, roll and yaw, you can control the quadcopter. They are also sometimes called control channels. There are many flight modes. GPS, barometer, and distance gage are also used, as well as stabilization mode (stab, stabilize, flying and stab), in which the copter keeps the angles set from the transmitter regardless of external factors. Without wind, the copter can hang almost in place in this mode. And the wind will have to be compensated for by the pilot.
|
If you can control throttle, pitch, roll and yaw, you can control the quadcopter. They are also sometimes called control channels. There are many flight modes. GPS, barometer, and distance gage are also used, as well as stabilization mode (stab, stabilize, flying and stab), in which the copter keeps the angles set from the transmitter regardless of external factors. Without wind, the copter can hang almost in place in this mode. And the wind will have to be compensated for by the pilot.
|
||||||
The propellers rotation directions are not chosen randomly. If all motors rotated in the same direction, the quadcopter would rotate in the opposite direction due to the generated moments. Therefore, two opposite motors always rotate in the same direction, and other two motors rotate the opposite direction. The effect of rotation moments is used to change the yaw: one pair of motors starts rotating a bit faster than the other, and the quadcopter slowly turns towards us:
|
The propellers rotation directions are not chosen randomly. If all motors rotated in the same direction, the quadcopter would rotate in the opposite direction due to the generated moments. Therefore, two opposite motors always rotate in the same direction, and other two motors rotate the opposite direction. The effect of rotation moments is used to change the yaw: one pair of motors starts rotating a bit faster than the other, and the quadcopter slowly turns towards us:
|
||||||
@@ -105,7 +105,7 @@ Elements of the copter
|
|||||||
|
|
||||||
Usually, when it comes to controlling a model of boat or an aircraft, the operator has absolute, precise control over the engine. Pressing the joystick on the transmitter results in proportional increasing the speed of the screws (rpm). A distinctive feature of multi-propeller aircraft (regardless of whether it is an advantage or a disadvantage) is in the fact that no one can simultaneously control the rotation speed of 3 and more motors precisely enough to keep the aircraft in the air. That is where the flight controllers come into play.
|
Usually, when it comes to controlling a model of boat or an aircraft, the operator has absolute, precise control over the engine. Pressing the joystick on the transmitter results in proportional increasing the speed of the screws (rpm). A distinctive feature of multi-propeller aircraft (regardless of whether it is an advantage or a disadvantage) is in the fact that no one can simultaneously control the rotation speed of 3 and more motors precisely enough to keep the aircraft in the air. That is where the flight controllers come into play.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
**Flight controller** is the most important part. Ninety percent of flight stability and controllability depends on the characteristics of the flight controller.
|
**Flight controller** is the most important part. Ninety percent of flight stability and controllability depends on the characteristics of the flight controller.
|
||||||
A flight controller is intended for translating commands from the transmitter into the signals that set the rotation speed of the motor. It also has inertial measuring sensors that allow keeping an eye on the current position of the platform and performing automatic adjustment
|
A flight controller is intended for translating commands from the transmitter into the signals that set the rotation speed of the motor. It also has inertial measuring sensors that allow keeping an eye on the current position of the platform and performing automatic adjustment
|
||||||
@@ -114,11 +114,11 @@ A flight controller is intended for translating commands from the transmitter in
|
|||||||
|
|
||||||
**ESC** are regulators for adjusting the motors rotation. The fact is that multi copters use special brushless motors that can rotate at very high speeds. To control such motors, it is sometimes necessary to form three phase voltage and relatively high currents, which is performed by ESCs. Each motor requires its own ESC. All ESCs are connected to the flight controller. The ESCs are powered directly from the battery. Each motor is connected to its own ESC with three wires. The order of connecting the wires determines the direction of motor rotation.
|
**ESC** are regulators for adjusting the motors rotation. The fact is that multi copters use special brushless motors that can rotate at very high speeds. To control such motors, it is sometimes necessary to form three phase voltage and relatively high currents, which is performed by ESCs. Each motor requires its own ESC. All ESCs are connected to the flight controller. The ESCs are powered directly from the battery. Each motor is connected to its own ESC with three wires. The order of connecting the wires determines the direction of motor rotation.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
**Motor**. Copters use brushless motors. They feature outstanding characteristics and survivability due to the absence of friction units (brushes) for transmitting the current. Unlike a conventional motor, which has a moving part — the rotor, and a stationary part — the stator, in a brushless motor, the moving part is the stator with permanent magnets, and the stationary part is the rotor with windings of three phases. In order to rotate such a system, the direction of the magnetic field in the windings of the rotor is changed in specific order, whereby permanent magnets in the rotor interact with magnetic fields of the stator and start rotating. This rotation is caused by the ability of magnets with the same poles to repel from each other, and magnets with opposite poles to attract to each other.
|
**Motor**. Copters use brushless motors. They feature outstanding characteristics and survivability due to the absence of friction units (brushes) for transmitting the current. Unlike a conventional motor, which has a moving part — the rotor, and a stationary part — the stator, in a brushless motor, the moving part is the stator with permanent magnets, and the stationary part is the rotor with windings of three phases. In order to rotate such a system, the direction of the magnetic field in the windings of the rotor is changed in specific order, whereby permanent magnets in the rotor interact with magnetic fields of the stator and start rotating. This rotation is caused by the ability of magnets with the same poles to repel from each other, and magnets with opposite poles to attract to each other.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
**Radio control equipment**. It includes a transmitter with a control unit, and a receiver. They may have various numbers of channels and frequencies. Most transmitters operate at the frequency of 2.40 GHz, there are also several other frequency bands available in the market.
|
**Radio control equipment**. It includes a transmitter with a control unit, and a receiver. They may have various numbers of channels and frequencies. Most transmitters operate at the frequency of 2.40 GHz, there are also several other frequency bands available in the market.
|
||||||
|
|
||||||
|
|||||||