Compare commits
104 Commits
v0.20-alph
...
roslaunch_
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c254691f80 | ||
|
|
4fe6f24d16 | ||
|
|
cda7e62dbf | ||
|
|
fa9a1794f6 | ||
|
|
ebec850010 | ||
|
|
5a8db188f6 | ||
|
|
4376bbb723 | ||
|
|
1d694e1a15 | ||
|
|
54de7fca3a | ||
|
|
32f5584082 | ||
|
|
d7c0fb33ff | ||
|
|
d16e7bf155 | ||
|
|
26bd1e2d8f | ||
|
|
e12577cf0e | ||
|
|
12544a69af | ||
|
|
f471280bef | ||
|
|
2ff9887ac4 | ||
|
|
2ecfb28a5d | ||
|
|
091483226f | ||
|
|
af16414c77 | ||
|
|
696214e717 | ||
|
|
15fd156ce0 | ||
|
|
82bfb961a4 | ||
|
|
c50637c60f | ||
|
|
a26f0f41e7 | ||
|
|
28690491c2 | ||
|
|
ca1323faf3 | ||
|
|
b713b49f3f | ||
|
|
62a77519e6 | ||
|
|
e1bf8aade5 | ||
|
|
0172d6e892 | ||
|
|
bcb7351a90 | ||
|
|
c71a46ce9d | ||
|
|
91dd7799ef | ||
|
|
3682e253a7 | ||
|
|
66ecbb4d09 | ||
|
|
f041b6125b | ||
|
|
a4f2bab3d7 | ||
|
|
56bcfa5c87 | ||
|
|
998796045c | ||
|
|
c5e954b56a | ||
|
|
2814fea9cd | ||
|
|
b85326c02a | ||
|
|
98d5d50607 | ||
|
|
69c46786de | ||
|
|
abb495275b | ||
|
|
044d6c6d33 | ||
|
|
22d5a356b6 | ||
|
|
c7828557ca | ||
|
|
514c0f1b65 | ||
|
|
6a79b8292a | ||
|
|
10b6661266 | ||
|
|
7f2cb1c63e | ||
|
|
1d48c79c52 | ||
|
|
ad46a0918c | ||
|
|
9487522992 | ||
|
|
80b35d3b90 | ||
|
|
12e292c9d7 | ||
|
|
9b28e9cad2 | ||
|
|
d57ab82f38 | ||
|
|
c8da639eab | ||
|
|
c7e7edec70 | ||
|
|
72869fcf2b | ||
|
|
387d2c2341 | ||
|
|
a665caeea3 | ||
|
|
3079d2a3e1 | ||
|
|
7d5bdf4f22 | ||
|
|
2a3efa2908 | ||
|
|
ddee29a0e8 | ||
|
|
8596be07c6 | ||
|
|
a480ebe80a | ||
|
|
77ca50b901 | ||
|
|
ead9b904fa | ||
|
|
90956ecd44 | ||
|
|
f070c60e14 | ||
|
|
68edf07f6e | ||
|
|
7f161b1ad7 | ||
|
|
a41a432ef3 | ||
|
|
2b896b06d9 | ||
|
|
5070cafbfb | ||
|
|
9c0af7285c | ||
|
|
c67d937842 | ||
|
|
b79d87242f | ||
|
|
4d0ddcb319 | ||
|
|
3ff4ee6c4c | ||
|
|
90049182cf | ||
|
|
33f4601fdc | ||
|
|
2a62891d60 | ||
|
|
b043737e91 | ||
|
|
c61a0485ff | ||
|
|
f1539177eb | ||
|
|
6cbbb5580e | ||
|
|
c2d22ae12a | ||
|
|
7160d804cd | ||
|
|
3ac51baf7c | ||
|
|
48cc82001d | ||
|
|
43eae885c6 | ||
|
|
2bb29ff389 | ||
|
|
3811cbff3e | ||
|
|
2d49f58fb8 | ||
|
|
bbcf75b806 | ||
|
|
3e79c25147 | ||
|
|
2f69ad3f43 | ||
|
|
b08ad5a618 |
23
.travis.yml
@@ -1,4 +1,5 @@
|
|||||||
sudo: required
|
os: linux
|
||||||
|
dist: xenial
|
||||||
language: generic
|
language: generic
|
||||||
services:
|
services:
|
||||||
- docker
|
- docker
|
||||||
@@ -43,7 +44,7 @@ jobs:
|
|||||||
- cd images && zip ${IMAGE_NAME}.zip ${IMAGE_NAME}
|
- cd images && zip ${IMAGE_NAME}.zip ${IMAGE_NAME}
|
||||||
deploy:
|
deploy:
|
||||||
provider: releases
|
provider: releases
|
||||||
api_key: ${GITHUB_OAUTH_TOKEN}
|
token: ${GITHUB_OAUTH_TOKEN}
|
||||||
file: ${IMAGE_NAME}.zip
|
file: ${IMAGE_NAME}.zip
|
||||||
skip_cleanup: true
|
skip_cleanup: true
|
||||||
on:
|
on:
|
||||||
@@ -72,8 +73,11 @@ jobs:
|
|||||||
node_js:
|
node_js:
|
||||||
- "10"
|
- "10"
|
||||||
before_script:
|
before_script:
|
||||||
|
- sudo sh -c "echo ttf-mscorefonts-installer msttcorefonts/accepted-mscorefonts-eula select true | debconf-set-selections"
|
||||||
|
- sudo apt update && sudo apt install -y calibre msttcorefonts
|
||||||
- npm install gitbook-cli -g
|
- npm install gitbook-cli -g
|
||||||
- npm install markdownlint-cli -g
|
- npm install markdownlint-cli -g
|
||||||
|
- npm install svgexport -g
|
||||||
- gitbook -V
|
- gitbook -V
|
||||||
- markdownlint -V
|
- markdownlint -V
|
||||||
script:
|
script:
|
||||||
@@ -82,15 +86,16 @@ jobs:
|
|||||||
- ./check_unused_assets.py
|
- ./check_unused_assets.py
|
||||||
- gitbook install
|
- gitbook install
|
||||||
- gitbook build
|
- gitbook build
|
||||||
|
- gitbook pdf ./ _book/clover.pdf
|
||||||
deploy:
|
deploy:
|
||||||
provider: pages
|
provider: pages
|
||||||
local-dir: _book
|
local_dir: _book
|
||||||
skip-cleanup: true
|
skip_cleanup: true
|
||||||
github-token: ${GITHUB_OAUTH_TOKEN}
|
token: ${GITHUB_OAUTH_TOKEN}
|
||||||
keep-history: true
|
keep_history: true
|
||||||
target-branch: master
|
target_branch: master
|
||||||
repo: CopterExpress/clever.coex.tech
|
repo: CopterExpress/clover.coex.tech
|
||||||
fqdn: clever.coex.tech
|
fqdn: clover.coex.tech
|
||||||
verbose: true
|
verbose: true
|
||||||
on:
|
on:
|
||||||
branch: master
|
branch: master
|
||||||
|
|||||||
105
README.md
@@ -1,103 +1,40 @@
|
|||||||
# CLEVER
|
# COEX Clover Drone Kit
|
||||||
|
|
||||||
<img src="docs/assets/clever4-front-white.png" align="right" width="400px" alt="CLEVER drone">
|
<img src="docs/assets/clever4-front-white.png" align="right" width="400px" alt="Clover Drone">
|
||||||
|
|
||||||
CLEVER (Russian: *"Клевер"*, meaning *"Clover"*) is an educational programmable drone kit consisting of an unassembled quadcopter, open source software and documentation. The kit includes Pixhawk/Pixracer autopilot running PX4 firmware, Raspberry Pi 3 as companion computer, a camera for computer vision navigation as well as additional sensors and peripheral devices.
|
Clover is an educational programmable drone kit consisting of an unassembled quadcopter, open source software and documentation. The kit includes Pixracer-compatible autopilot running PX4 firmware, Raspberry Pi 4 as companion computer, a camera for computer vision navigation as well as additional sensors and peripheral devices.
|
||||||
|
|
||||||
Copter Express has implemented a large number of different autonomous drone projects using exactly the same platform: [automated pizza delivery](https://www.youtube.com/watch?v=hmkAoZOtF58) in Samara and Kazan, coffee delivery in Skolkovo Innovation Center, [autonomous quadcopter with charging station](https://www.youtube.com/watch?v=RjX6nUqw1mI) for site monitoring and security, winning drones on [Robocross-2016](https://www.youtube.com/watch?v=dGbDaz_VmYU) and [Robocross-2017](https://youtu.be/AQnd2CRczbQ) competitions and many others.
|
The main documentation is available [on Gitbook](https://clover.coex.tech/).
|
||||||
|
|
||||||
**The main documentation is available [on Gitbook](https://clever.coex.tech/).**
|
Official website: <a href="https://coex.tech/clover">coex.tech/clover</a>.
|
||||||
|
|
||||||
Use it to learn how to assemble, configure, pilot and program autonomous CLEVER drone.
|
## Video compilation
|
||||||
|
|
||||||
|
[](https://youtu.be/u3omgsYC4Fk)
|
||||||
|
|
||||||
|
Clover drone is used on a wide range of educational events, including [Copter Hack](https://www.youtube.com/watch?v=xgXheg3TTs4), WorldSkills Drone Operation competition, [Autonomous Vehicles Track of NTI Olympics 2016–2020](https://www.youtube.com/watch?v=E1_ehvJRKxg), Quadro Hack 2019 (National University of Science and Technology MISiS), Russian Robot Olympiad (autonomous flights), and others.
|
||||||
|
|
||||||
## Raspberry Pi image
|
## Raspberry Pi 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 with installed and configured software, ready to fly, is available [in the Releases section](https://github.com/CopterExpress/clover/releases).
|
||||||
|
|
||||||
[](https://travis-ci.org/CopterExpress/clever)
|
[](https://travis-ci.org/CopterExpress/clover)
|
||||||
|
|
||||||
Image includes:
|
Image features:
|
||||||
|
|
||||||
* Raspbian Buster
|
* Raspbian Buster
|
||||||
* ROS Melodic
|
* [ROS Melodic](http://wiki.ros.org/melodic)
|
||||||
* Configured networking
|
* Configured networking
|
||||||
* OpenCV
|
* OpenCV
|
||||||
* mavros
|
* [`mavros`](http://wiki.ros.org/mavros)
|
||||||
* Periphery drivers (`pigpiod`, `rpi_ws281x`, etc)
|
* Periphery drivers for ROS ([GPIO](https://clover.coex.tech/en/gpio.html), [LED strip](https://clover.coex.tech/en/leds.html), etc)
|
||||||
* CLEVER software bundle for autonomous drone control
|
* `aruco_pose` package for marker-assisted navigation
|
||||||
|
* `clover` package for autonomous drone control
|
||||||
|
|
||||||
API description (in Russian) for autonomous flights is available [on GitBook](https://clever.coex.tech/simple_offboard.html).
|
API description for autonomous flights is available [on GitBook](https://clover.coex.tech/en/simple_offboard.html).
|
||||||
|
|
||||||
## Manual installation
|
For manual package installation and running see [`clover` package documentation](clover/README.md).
|
||||||
|
|
||||||
Install ROS Melodic according to the [documentation](http://wiki.ros.org/melodic/Installation), then [create a Catkin workspace](http://wiki.ros.org/catkin/Tutorials/create_a_workspace).
|
|
||||||
|
|
||||||
Clone this repo to directory `~/catkin_ws/src/clever`:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd ~/catkin_ws/src
|
|
||||||
git clone https://github.com/CopterExpress/clever.git clever
|
|
||||||
```
|
|
||||||
|
|
||||||
All the required ROS packages (including `mavros` and `opencv`) can be installed using `rosdep`:
|
|
||||||
|
|
||||||
```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
|
|
||||||
cd ~/catkin_ws
|
|
||||||
catkin_make -j1
|
|
||||||
```
|
|
||||||
|
|
||||||
To complete `mavros` install you'll need to install `geographiclib` datasets:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
curl https://raw.githubusercontent.com/mavlink/mavros/master/mavros/scripts/install_geographiclib_datasets.sh | sudo bash
|
|
||||||
```
|
|
||||||
|
|
||||||
You may optionally install udev rules to provide `/dev/px4fmu` symlink to your PX4-based flight controller connected over USB. Copy `99-px4fmu.rules` to your `/lib/udev/rules.d` folder:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd ~/catkin_ws/src/clever/clever/config
|
|
||||||
sudo cp 99-px4fmu.rules /lib/udev/rules.d
|
|
||||||
```
|
|
||||||
|
|
||||||
Alternatively you may change the `fcu_url` property in `mavros.launch` file to point to your flight controller device.
|
|
||||||
|
|
||||||
## Running
|
|
||||||
|
|
||||||
Enable systemd service `roscore` (if not running):
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo systemctl enable /home/<username>/catkin_ws/src/clever/builder/assets/roscore.service
|
|
||||||
sudo systemctl start roscore
|
|
||||||
```
|
|
||||||
|
|
||||||
To start connection to SITL, use:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
roslaunch clever sitl.launch
|
|
||||||
```
|
|
||||||
|
|
||||||
To start connection to the flight controller, use:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
roslaunch clever clever.launch
|
|
||||||
```
|
|
||||||
|
|
||||||
> Note that the package is configured to connect to `/dev/px4fmu` by default (see [previous section](#manual-installation)). Install udev rules or specify path to your FCU device in `mavros.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
|
|
||||||
```
|
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
While the Clever platform source code is available under the MIT License, note, that the [documentation](docs/) is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
|
While the Clover platform source code is available under the MIT License, note, that the [documentation](docs/) is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
|
||||||
|
|||||||
0
apps/CATKIN_IGNORE
Normal file
@@ -1,8 +1,6 @@
|
|||||||
cmake_minimum_required(VERSION 3.0)
|
cmake_minimum_required(VERSION 3.0)
|
||||||
project(aruco_pose)
|
project(aruco_pose)
|
||||||
|
|
||||||
add_definitions(-std=c++11 -Wall -g)
|
|
||||||
|
|
||||||
## Compile as C++11, supported in ROS Kinetic and newer
|
## Compile as C++11, supported in ROS Kinetic and newer
|
||||||
add_compile_options(-std=c++11)
|
add_compile_options(-std=c++11)
|
||||||
|
|
||||||
@@ -25,7 +23,7 @@ find_package(catkin REQUIRED COMPONENTS
|
|||||||
)
|
)
|
||||||
|
|
||||||
find_package(OpenCV 3 REQUIRED COMPONENTS core imgproc calib3d)
|
find_package(OpenCV 3 REQUIRED COMPONENTS core imgproc calib3d)
|
||||||
if ("${OpenCV_VERSION_MINOR}" LESS "3")
|
if ("${OpenCV_VERSION_MINOR}" LESS "9")
|
||||||
message(STATUS "OpenCV version too low, using vendored ArUco package")
|
message(STATUS "OpenCV version too low, using vendored ArUco package")
|
||||||
include(vendor/VendorOpenCV.cmake)
|
include(vendor/VendorOpenCV.cmake)
|
||||||
else()
|
else()
|
||||||
@@ -229,4 +227,5 @@ if (CATKIN_ENABLE_TESTING)
|
|||||||
add_rostest(test/test_parser_empty_map.test)
|
add_rostest(test/test_parser_empty_map.test)
|
||||||
add_rostest(test/test_node_failure.test)
|
add_rostest(test/test_node_failure.test)
|
||||||
add_rostest(test/largemap.test)
|
add_rostest(test/largemap.test)
|
||||||
|
add_rostest(test/crash_opencv.test)
|
||||||
endif()
|
endif()
|
||||||
|
|||||||
@@ -58,10 +58,9 @@ using cv::Mat;
|
|||||||
|
|
||||||
class ArucoDetect : public nodelet::Nodelet {
|
class ArucoDetect : public nodelet::Nodelet {
|
||||||
private:
|
private:
|
||||||
ros::NodeHandle nh_, nh_priv_;
|
std::unique_ptr<tf2_ros::TransformBroadcaster> br_;
|
||||||
tf2_ros::TransformBroadcaster br_;
|
std::unique_ptr<tf2_ros::Buffer> tf_buffer_;
|
||||||
tf2_ros::Buffer tf_buffer_;
|
std::unique_ptr<tf2_ros::TransformListener> tf_listener_;
|
||||||
tf2_ros::TransformListener tf_listener_{tf_buffer_};
|
|
||||||
std::shared_ptr<dynamic_reconfigure::Server<aruco_pose::DetectorConfig>> dyn_srv_;
|
std::shared_ptr<dynamic_reconfigure::Server<aruco_pose::DetectorConfig>> dyn_srv_;
|
||||||
cv::Ptr<cv::aruco::Dictionary> dictionary_;
|
cv::Ptr<cv::aruco::Dictionary> dictionary_;
|
||||||
cv::Ptr<cv::aruco::DetectorParameters> parameters_;
|
cv::Ptr<cv::aruco::DetectorParameters> parameters_;
|
||||||
@@ -81,30 +80,32 @@ private:
|
|||||||
public:
|
public:
|
||||||
virtual void onInit()
|
virtual void onInit()
|
||||||
{
|
{
|
||||||
nh_ = getNodeHandle();
|
ros::NodeHandle& nh_ = getNodeHandle();
|
||||||
nh_priv_ = getPrivateNodeHandle();
|
ros::NodeHandle& nh_priv_ = getPrivateNodeHandle();
|
||||||
|
|
||||||
|
br_.reset(new tf2_ros::TransformBroadcaster());
|
||||||
|
tf_buffer_.reset(new tf2_ros::Buffer());
|
||||||
|
tf_listener_.reset(new tf2_ros::TransformListener(*tf_buffer_, nh_));
|
||||||
|
|
||||||
int dictionary;
|
int dictionary;
|
||||||
nh_priv_.param("dictionary", dictionary, 2);
|
dictionary = nh_priv_.param("dictionary", 2);
|
||||||
nh_priv_.param("estimate_poses", estimate_poses_, true);
|
estimate_poses_ = nh_priv_.param("estimate_poses", true);
|
||||||
nh_priv_.param("send_tf", send_tf_, true);
|
send_tf_ = nh_priv_.param("send_tf", true);
|
||||||
if (estimate_poses_ && !nh_priv_.getParam("length", length_)) {
|
if (estimate_poses_ && !nh_priv_.getParam("length", length_)) {
|
||||||
NODELET_FATAL("can't estimate marker's poses as ~length parameter is not defined");
|
NODELET_FATAL("can't estimate marker's poses as ~length parameter is not defined");
|
||||||
ros::shutdown();
|
ros::shutdown();
|
||||||
}
|
}
|
||||||
readLengthOverride();
|
readLengthOverride(nh_priv_);
|
||||||
|
|
||||||
nh_priv_.param<std::string>("known_tilt", known_tilt_, "");
|
known_tilt_ = nh_priv_.param<std::string>("known_tilt", "");
|
||||||
nh_priv_.param("auto_flip", auto_flip_, false);
|
auto_flip_ = nh_priv_.param("auto_flip", false);
|
||||||
|
|
||||||
nh_priv_.param<std::string>("frame_id_prefix", frame_id_prefix_, "aruco_");
|
frame_id_prefix_ = nh_priv_.param<std::string>("frame_id_prefix", "aruco_");
|
||||||
|
|
||||||
camera_matrix_ = cv::Mat::zeros(3, 3, CV_64F);
|
camera_matrix_ = cv::Mat::zeros(3, 3, CV_64F);
|
||||||
dist_coeffs_ = cv::Mat::zeros(8, 1, CV_64F);
|
|
||||||
|
|
||||||
dictionary_ = cv::aruco::getPredefinedDictionary(static_cast<cv::aruco::PREDEFINED_DICTIONARY_NAME>(dictionary));
|
dictionary_ = cv::aruco::getPredefinedDictionary(static_cast<cv::aruco::PREDEFINED_DICTIONARY_NAME>(dictionary));
|
||||||
parameters_ = cv::aruco::DetectorParameters::create();
|
parameters_ = cv::aruco::DetectorParameters::create();
|
||||||
parameters_->cornerRefinementMethod = cv::aruco::CORNER_REFINE_SUBPIX;
|
|
||||||
|
|
||||||
image_transport::ImageTransport it(nh_);
|
image_transport::ImageTransport it(nh_);
|
||||||
image_transport::ImageTransport it_priv(nh_priv_);
|
image_transport::ImageTransport it_priv(nh_priv_);
|
||||||
@@ -170,8 +171,8 @@ private:
|
|||||||
|
|
||||||
if (!known_tilt_.empty()) {
|
if (!known_tilt_.empty()) {
|
||||||
try {
|
try {
|
||||||
snap_to = tf_buffer_.lookupTransform(msg->header.frame_id, known_tilt_,
|
snap_to = tf_buffer_->lookupTransform(msg->header.frame_id, known_tilt_,
|
||||||
msg->header.stamp, ros::Duration(0.02));
|
msg->header.stamp, ros::Duration(0.02));
|
||||||
} catch (const tf2::TransformException& e) {
|
} catch (const tf2::TransformException& e) {
|
||||||
NODELET_WARN_THROTTLE(5, "can't snap: %s", e.what());
|
NODELET_WARN_THROTTLE(5, "can't snap: %s", e.what());
|
||||||
}
|
}
|
||||||
@@ -205,7 +206,7 @@ private:
|
|||||||
if (map_markers_ids_.find(ids[i]) == map_markers_ids_.end()) {
|
if (map_markers_ids_.find(ids[i]) == map_markers_ids_.end()) {
|
||||||
transform.transform.rotation = marker.pose.orientation;
|
transform.transform.rotation = marker.pose.orientation;
|
||||||
fillTranslation(transform.transform.translation, tvecs[i]);
|
fillTranslation(transform.transform.translation, tvecs[i]);
|
||||||
br_.sendTransform(transform);
|
br_->sendTransform(transform);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -326,10 +327,10 @@ private:
|
|||||||
return frame_id_prefix_ + std::to_string(id);
|
return frame_id_prefix_ + std::to_string(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
void readLengthOverride()
|
void readLengthOverride(ros::NodeHandle& nh)
|
||||||
{
|
{
|
||||||
std::map<std::string, double> length_override;
|
std::map<std::string, double> length_override;
|
||||||
nh_priv_.getParam("length_override", length_override);
|
nh.getParam("length_override", length_override);
|
||||||
for (auto const& item : length_override) {
|
for (auto const& item : length_override) {
|
||||||
length_override_[std::stoi(item.first)] = item.second;
|
length_override_[std::stoi(item.first)] = item.second;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,7 +58,6 @@ typedef message_filters::sync_policies::ExactTime<Image, CameraInfo, MarkerArray
|
|||||||
|
|
||||||
class ArucoMap : public nodelet::Nodelet {
|
class ArucoMap : public nodelet::Nodelet {
|
||||||
private:
|
private:
|
||||||
ros::NodeHandle nh_, nh_priv_;
|
|
||||||
ros::Publisher img_pub_, pose_pub_, markers_pub_, vis_markers_pub_;
|
ros::Publisher img_pub_, pose_pub_, markers_pub_, vis_markers_pub_;
|
||||||
image_transport::Publisher debug_pub_;
|
image_transport::Publisher debug_pub_;
|
||||||
message_filters::Subscriber<Image> image_sub_;
|
message_filters::Subscriber<Image> image_sub_;
|
||||||
@@ -83,8 +82,8 @@ private:
|
|||||||
public:
|
public:
|
||||||
virtual void onInit()
|
virtual void onInit()
|
||||||
{
|
{
|
||||||
nh_ = getNodeHandle();
|
ros::NodeHandle &nh_ = getNodeHandle();
|
||||||
nh_priv_ = getPrivateNodeHandle();
|
ros::NodeHandle &nh_priv_ = getPrivateNodeHandle();
|
||||||
|
|
||||||
image_transport::ImageTransport it_priv(nh_priv_);
|
image_transport::ImageTransport it_priv(nh_priv_);
|
||||||
|
|
||||||
@@ -96,19 +95,18 @@ public:
|
|||||||
board_->dictionary = cv::aruco::getPredefinedDictionary(
|
board_->dictionary = cv::aruco::getPredefinedDictionary(
|
||||||
static_cast<cv::aruco::PREDEFINED_DICTIONARY_NAME>(nh_priv_.param("dictionary", 2)));
|
static_cast<cv::aruco::PREDEFINED_DICTIONARY_NAME>(nh_priv_.param("dictionary", 2)));
|
||||||
camera_matrix_ = cv::Mat::zeros(3, 3, CV_64F);
|
camera_matrix_ = cv::Mat::zeros(3, 3, CV_64F);
|
||||||
dist_coeffs_ = cv::Mat::zeros(8, 1, CV_64F);
|
|
||||||
|
|
||||||
std::string type, map;
|
std::string type, map;
|
||||||
nh_priv_.param<std::string>("type", type, "map");
|
type = nh_priv_.param<std::string>("type", "map");
|
||||||
nh_priv_.param<std::string>("frame_id", transform_.child_frame_id, "aruco_map");
|
transform_.child_frame_id = nh_priv_.param<std::string>("frame_id", "aruco_map");
|
||||||
nh_priv_.param<std::string>("known_tilt", known_tilt_, "");
|
known_tilt_ = nh_priv_.param<std::string>("known_tilt", "");
|
||||||
nh_priv_.param("auto_flip", auto_flip_, false);
|
auto_flip_ = nh_priv_.param("auto_flip", false);
|
||||||
nh_priv_.param("image_width", image_width_, 2000);
|
image_width_ = nh_priv_.param("image_width" , 2000);
|
||||||
nh_priv_.param("image_height", image_height_, 2000);
|
image_height_ = nh_priv_.param("image_height", 2000);
|
||||||
nh_priv_.param("image_margin", image_margin_, 200);
|
image_margin_ = nh_priv_.param("image_margin", 200);
|
||||||
nh_priv_.param("image_axis", image_axis_, true);
|
image_axis_ = nh_priv_.param("image_axis", true);
|
||||||
nh_priv_.param<std::string>("markers/frame_id", markers_parent_frame_, transform_.child_frame_id);
|
markers_parent_frame_ = nh_priv_.param<std::string>("markers/frame_id", transform_.child_frame_id);
|
||||||
nh_priv_.param<std::string>("markers/child_frame_id_prefix", markers_frame_, "");
|
markers_frame_ = nh_priv_.param<std::string>("markers/child_frame_id_prefix", "");
|
||||||
|
|
||||||
// createStripLine();
|
// createStripLine();
|
||||||
|
|
||||||
@@ -116,7 +114,7 @@ public:
|
|||||||
param(nh_priv_, "map", map);
|
param(nh_priv_, "map", map);
|
||||||
loadMap(map);
|
loadMap(map);
|
||||||
} else if (type == "gridboard") {
|
} else if (type == "gridboard") {
|
||||||
createGridBoard();
|
createGridBoard(nh_priv_);
|
||||||
} else {
|
} else {
|
||||||
NODELET_FATAL("unknown type: %s", type.c_str());
|
NODELET_FATAL("unknown type: %s", type.c_str());
|
||||||
ros::shutdown();
|
ros::shutdown();
|
||||||
@@ -331,7 +329,7 @@ publish_debug:
|
|||||||
NODELET_INFO("loading %s complete (%d markers)", filename.c_str(), static_cast<int>(board_->ids.size()));
|
NODELET_INFO("loading %s complete (%d markers)", filename.c_str(), static_cast<int>(board_->ids.size()));
|
||||||
}
|
}
|
||||||
|
|
||||||
void createGridBoard()
|
void createGridBoard(ros::NodeHandle& nh)
|
||||||
{
|
{
|
||||||
NODELET_INFO("generate gridboard");
|
NODELET_INFO("generate gridboard");
|
||||||
NODELET_WARN("gridboard maps are deprecated");
|
NODELET_WARN("gridboard maps are deprecated");
|
||||||
@@ -339,15 +337,15 @@ publish_debug:
|
|||||||
int markers_x, markers_y, first_marker;
|
int markers_x, markers_y, first_marker;
|
||||||
double markers_side, markers_sep_x, markers_sep_y;
|
double markers_side, markers_sep_x, markers_sep_y;
|
||||||
std::vector<int> marker_ids;
|
std::vector<int> marker_ids;
|
||||||
nh_priv_.param<int>("markers_x", markers_x, 10);
|
markers_x = nh.param("markers_x", 10);
|
||||||
nh_priv_.param<int>("markers_y", markers_y, 10);
|
markers_y = nh.param("markers_y", 10);
|
||||||
nh_priv_.param<int>("first_marker", first_marker, 0);
|
first_marker = nh.param("first_marker", 0);
|
||||||
|
|
||||||
param(nh_priv_, "markers_side", markers_side);
|
param(nh, "markers_side", markers_side);
|
||||||
param(nh_priv_, "markers_sep_x", markers_sep_x);
|
param(nh, "markers_sep_x", markers_sep_x);
|
||||||
param(nh_priv_, "markers_sep_y", markers_sep_y);
|
param(nh, "markers_sep_y", markers_sep_y);
|
||||||
|
|
||||||
if (nh_priv_.getParam("marker_ids", marker_ids)) {
|
if (nh.getParam("marker_ids", marker_ids)) {
|
||||||
if ((unsigned int)(markers_x * markers_y) != marker_ids.size()) {
|
if ((unsigned int)(markers_x * markers_y) != marker_ids.size()) {
|
||||||
NODELET_FATAL("~marker_ids length should be equal to ~markers_x * ~markers_y");
|
NODELET_FATAL("~marker_ids length should be equal to ~markers_x * ~markers_y");
|
||||||
ros::shutdown();
|
ros::shutdown();
|
||||||
@@ -394,7 +392,7 @@ publish_debug:
|
|||||||
int num_markers = board_->dictionary->bytesList.rows;
|
int num_markers = board_->dictionary->bytesList.rows;
|
||||||
if (num_markers <= id) {
|
if (num_markers <= id) {
|
||||||
NODELET_ERROR("Marker id %d is not in dictionary; current dictionary contains %d markers. "
|
NODELET_ERROR("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",
|
"Please see https://github.com/CopterExpress/clover/blob/master/aruco_pose/README.md#parameters for details",
|
||||||
id, num_markers);
|
id, num_markers);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python
|
||||||
|
|
||||||
# Copyright (C) 2018 Copter Express Technologies
|
# Copyright (C) 2018 Copter Express Technologies
|
||||||
#
|
#
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
Generate map file for aruco_map nodelet.
|
Generate map file for aruco_map nodelet.
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
genmap.py <length> <x> <y> <dist_x> <dist_y> [<first>] [--top-left | --bottom-left]
|
genmap.py <length> <x> <y> <dist_x> <dist_y> [<first>] [<x0>] [<y0>] [--top-left | --bottom-left]
|
||||||
genmap.py (-h | --help)
|
genmap.py (-h | --help)
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
@@ -23,6 +23,8 @@ Options:
|
|||||||
<dist_x> Distance between markers along X axis
|
<dist_x> Distance between markers along X axis
|
||||||
<dist_y> Distance between markers along Y axis
|
<dist_y> Distance between markers along Y axis
|
||||||
<first> First marker ID [default: 0]
|
<first> First marker ID [default: 0]
|
||||||
|
<x0> X coordinate for the first marker [default: 0]
|
||||||
|
<y0> Y coordinate for the first marker [default: 0]
|
||||||
--top-left First marker is on top-left (default)
|
--top-left First marker is on top-left (default)
|
||||||
--bottom-left First marker is on bottom-left
|
--bottom-left First marker is on bottom-left
|
||||||
|
|
||||||
@@ -39,20 +41,22 @@ arguments = docopt(__doc__)
|
|||||||
|
|
||||||
length = float(arguments['<length>'])
|
length = float(arguments['<length>'])
|
||||||
first = int(arguments['<first>'] if arguments['<first>'] is not None else 0)
|
first = int(arguments['<first>'] if arguments['<first>'] is not None else 0)
|
||||||
|
x0 = float(arguments['<x0>'] if arguments['<x0>'] is not None else 0)
|
||||||
|
y0 = float(arguments['<y0>'] if arguments['<y0>'] is not None else 0)
|
||||||
markers_x = int(arguments['<x>'])
|
markers_x = int(arguments['<x>'])
|
||||||
markers_y = int(arguments['<y>'])
|
markers_y = int(arguments['<y>'])
|
||||||
dist_x = float(arguments['<dist_x>'])
|
dist_x = float(arguments['<dist_x>'])
|
||||||
dist_y = float(arguments['<dist_y>'])
|
dist_y = float(arguments['<dist_y>'])
|
||||||
bottom_left = arguments['--bottom-left']
|
bottom_left = arguments['--bottom-left']
|
||||||
|
|
||||||
max_y = (markers_y - 1) * dist_y
|
max_y = y0 + (markers_y - 1) * dist_y
|
||||||
|
|
||||||
print('# id\tlength\tx\ty\tz\trot_z\trot_y\trot_x')
|
print('# id\tlength\tx\ty\tz\trot_z\trot_y\trot_x')
|
||||||
for y in range(markers_y):
|
for y in range(markers_y):
|
||||||
for x in range(markers_x):
|
for x in range(markers_x):
|
||||||
pos_x = x * dist_x
|
pos_x = x0 + x * dist_x
|
||||||
pos_y = y * dist_y
|
pos_y = y0 + y * dist_y
|
||||||
if not bottom_left:
|
if not bottom_left:
|
||||||
pos_y = max_y - pos_y
|
pos_y = max_y - pos_y
|
||||||
print('{}\t{}\t{}\t{}\t{}\t{}\t{}\t{}\t'.format(first, length, pos_x, pos_y, 0, 0, 0, 0))
|
print('{}\t{}\t{}\t{}\t{}\t{}\t{}\t{}'.format(first, length, pos_x, pos_y, 0, 0, 0, 0))
|
||||||
first += 1
|
first += 1
|
||||||
|
|||||||
@@ -35,9 +35,7 @@ static void parseCameraInfo(const sensor_msgs::CameraInfoConstPtr& cinfo, cv::Ma
|
|||||||
for (unsigned int i = 0; i < 3; ++i)
|
for (unsigned int i = 0; i < 3; ++i)
|
||||||
for (unsigned int j = 0; j < 3; ++j)
|
for (unsigned int j = 0; j < 3; ++j)
|
||||||
matrix.at<double>(i, j) = cinfo->K[3 * i + j];
|
matrix.at<double>(i, j) = cinfo->K[3 * i + j];
|
||||||
|
dist = cv::Mat(cinfo->D, true);
|
||||||
for (unsigned int k = 0; k < cinfo->D.size(); k++)
|
|
||||||
dist.at<double>(k) = cinfo->D[k];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void rotatePoint(cv::Point3f& p, cv::Point3f origin, float angle)
|
inline void rotatePoint(cv::Point3f& p, cv::Point3f origin, float angle)
|
||||||
|
|||||||
BIN
aruco_pose/test/crash_image_01.png
Normal file
|
After Width: | Height: | Size: 159 KiB |
BIN
aruco_pose/test/crash_image_02.png
Normal file
|
After Width: | Height: | Size: 156 KiB |
BIN
aruco_pose/test/crash_image_03.png
Normal file
|
After Width: | Height: | Size: 165 KiB |
18
aruco_pose/test/crash_opencv.py
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import rospy
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from visualization_msgs.msg import MarkerArray as VisMarkerArray
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def node():
|
||||||
|
return rospy.init_node('aruco_pose_opencv_crash', anonymous=True)
|
||||||
|
|
||||||
|
def test_opencv_crashes_img01(node):
|
||||||
|
rospy.wait_for_message('aruco_detect_01/visualization', VisMarkerArray, timeout=5)
|
||||||
|
|
||||||
|
def test_opencv_crashes_img02(node):
|
||||||
|
rospy.wait_for_message('aruco_detect_02/visualization', VisMarkerArray, timeout=5)
|
||||||
|
|
||||||
|
def test_opencv_crashes_img03(node):
|
||||||
|
rospy.wait_for_message('aruco_detect_03/visualization', VisMarkerArray, timeout=5)
|
||||||
51
aruco_pose/test/crash_opencv.test
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
<launch>
|
||||||
|
<arg name="corner_method" default="2"/>
|
||||||
|
|
||||||
|
<node pkg="image_publisher" type="image_publisher" name="imgpub_01" args="$(find aruco_pose)/test/crash_image_01.png">
|
||||||
|
<param name="frame_id" value="main_camera_optical"/>
|
||||||
|
<param name="publish_rate" value="10"/>
|
||||||
|
<param name="camera_info_url" value="file://$(find aruco_pose)/test/camera_info.yaml" />
|
||||||
|
</node>
|
||||||
|
|
||||||
|
<node pkg="image_publisher" type="image_publisher" name="imgpub_02" args="$(find aruco_pose)/test/crash_image_02.png">
|
||||||
|
<param name="frame_id" value="main_camera_optical"/>
|
||||||
|
<param name="publish_rate" value="10"/>
|
||||||
|
<param name="camera_info_url" value="file://$(find aruco_pose)/test/camera_info.yaml" />
|
||||||
|
</node>
|
||||||
|
|
||||||
|
<node pkg="image_publisher" type="image_publisher" name="imgpub_03" args="$(find aruco_pose)/test/crash_image_03.png">
|
||||||
|
<param name="frame_id" value="main_camera_optical"/>
|
||||||
|
<param name="publish_rate" value="10"/>
|
||||||
|
<param name="camera_info_url" value="file://$(find aruco_pose)/test/camera_info.yaml" />
|
||||||
|
</node>
|
||||||
|
|
||||||
|
<node pkg="nodelet" type="nodelet" name="nodelet_manager_01" args="manager"/>
|
||||||
|
|
||||||
|
<node pkg="nodelet" clear_params="true" type="nodelet" name="aruco_detect_01" args="load aruco_pose/aruco_detect nodelet_manager_01">
|
||||||
|
<remap from="image_raw" to="imgpub_01/image_raw"/>
|
||||||
|
<remap from="camera_info" to="imgpub_01/camera_info"/>
|
||||||
|
<param name="length" value="0.33"/>
|
||||||
|
<param name="cornerRefinementMethod" value="$(arg corner_method)"/>
|
||||||
|
</node>
|
||||||
|
|
||||||
|
<node pkg="nodelet" type="nodelet" name="nodelet_manager_02" args="manager"/>
|
||||||
|
|
||||||
|
<node pkg="nodelet" clear_params="true" type="nodelet" name="aruco_detect_02" args="load aruco_pose/aruco_detect nodelet_manager_02">
|
||||||
|
<remap from="image_raw" to="imgpub_02/image_raw"/>
|
||||||
|
<remap from="camera_info" to="imgpub_02/camera_info"/>
|
||||||
|
<param name="length" value="0.33"/>
|
||||||
|
<param name="cornerRefinementMethod" value="$(arg corner_method)"/>
|
||||||
|
</node>
|
||||||
|
|
||||||
|
<node pkg="nodelet" type="nodelet" name="nodelet_manager_03" args="manager"/>
|
||||||
|
|
||||||
|
<node pkg="nodelet" clear_params="true" type="nodelet" name="aruco_detect_03" args="load aruco_pose/aruco_detect nodelet_manager_03">
|
||||||
|
<remap from="image_raw" to="imgpub_03/image_raw"/>
|
||||||
|
<remap from="camera_info" to="imgpub_03/camera_info"/>
|
||||||
|
<param name="length" value="0.33"/>
|
||||||
|
<param name="cornerRefinementMethod" value="$(arg corner_method)"/>
|
||||||
|
</node>
|
||||||
|
|
||||||
|
<param name="test_module" value="$(find aruco_pose)/test/crash_opencv.py"/>
|
||||||
|
<test test-name="crash_opencv" pkg="ros_pytest" type="ros_pytest_runner"/>
|
||||||
|
</launch>
|
||||||
@@ -1,19 +1,26 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
import sys
|
||||||
|
import unittest
|
||||||
|
import json
|
||||||
import rospy
|
import rospy
|
||||||
import pytest
|
import rostest
|
||||||
|
|
||||||
from sensor_msgs.msg import Image
|
from sensor_msgs.msg import Image
|
||||||
from visualization_msgs.msg import MarkerArray as VisMarkerArray
|
from visualization_msgs.msg import MarkerArray as VisMarkerArray
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def node():
|
|
||||||
return rospy.init_node('test_aruco_largemap', anonymous=True)
|
|
||||||
|
|
||||||
def test_large_map_image(node):
|
class TestArucoPose(unittest.TestCase):
|
||||||
img = rospy.wait_for_message('aruco_map/image', Image, timeout=5)
|
def setUp(self):
|
||||||
assert img.width == 2000
|
rospy.init_node('test_aruco_largemap', anonymous=True)
|
||||||
assert img.height == 2000
|
|
||||||
assert img.encoding in ('mono8', 'rgb8')
|
|
||||||
|
|
||||||
def test_large_map_visualization(node):
|
def test_map_image(self):
|
||||||
vis = rospy.wait_for_message('aruco_map/visualization', VisMarkerArray, timeout=5)
|
img = rospy.wait_for_message('aruco_map/image', Image, timeout=5)
|
||||||
assert len(vis.markers) == 11
|
self.assertEqual(img.width, 2000)
|
||||||
|
self.assertEqual(img.height, 2000)
|
||||||
|
self.assertIn(img.encoding, ('mono8', 'rgb8'))
|
||||||
|
|
||||||
|
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)
|
||||||
|
|||||||
@@ -9,6 +9,5 @@
|
|||||||
<param name="map" value="$(find aruco_pose)/test/largemap.txt"/>
|
<param name="map" value="$(find aruco_pose)/test/largemap.txt"/>
|
||||||
</node>
|
</node>
|
||||||
|
|
||||||
<param name="test_module" value="$(find aruco_pose)/test/largemap.py"/>
|
<test test-name="test_aruco_pose_largemap" pkg="aruco_pose" type="largemap.py"/>
|
||||||
<test test-name="test_node_pass" pkg="ros_pytest" type="ros_pytest_runner"/>
|
|
||||||
</launch>
|
</launch>
|
||||||
|
|||||||
2
aruco_pose/vendor/aruco/src/aruco.cpp
vendored
@@ -924,6 +924,8 @@ static void _refineCandidateLines(std::vector<Point>& nContours, std::vector<Poi
|
|||||||
// calculate the line :: who passes through the grouped points
|
// calculate the line :: who passes through the grouped points
|
||||||
Point3f lines[4];
|
Point3f lines[4];
|
||||||
for(int i=0; i<4; i++){
|
for(int i=0; i<4; i++){
|
||||||
|
// Don't try to "interpolate" single points
|
||||||
|
if (cntPts[i].size() < 2) return;
|
||||||
lines[i]=_interpolate2Dline(cntPts[i]);
|
lines[i]=_interpolate2Dline(cntPts[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
12
book.json
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"title": "Clever",
|
"title": "Clover",
|
||||||
"description": "Конструктор квадрокоптера «Клевер»",
|
"description": "Конструктор квадрокоптера «Клевер»",
|
||||||
"author": "Copter Express",
|
"author": "Copter Express",
|
||||||
"language": "en",
|
"language": "en",
|
||||||
@@ -28,7 +28,7 @@
|
|||||||
"blank": true
|
"blank": true
|
||||||
},
|
},
|
||||||
"sitemap": {
|
"sitemap": {
|
||||||
"hostname": "https://clever.coex.tech"
|
"hostname": "https://clover.coex.tech"
|
||||||
},
|
},
|
||||||
"toolbar": {
|
"toolbar": {
|
||||||
"buttons":
|
"buttons":
|
||||||
@@ -37,19 +37,19 @@
|
|||||||
"label": "Edit page on github",
|
"label": "Edit page on github",
|
||||||
"icon": "fa fa-pencil-square-o",
|
"icon": "fa fa-pencil-square-o",
|
||||||
"position" : "left",
|
"position" : "left",
|
||||||
"url": "https://github.com/CopterExpress/clever/edit/master/docs/{{filepath_lang}}"
|
"url": "https://github.com/CopterExpress/clover/edit/master/docs/{{filepath_lang}}"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"label": "GitHub",
|
"label": "GitHub",
|
||||||
"icon": "fa fa-github",
|
"icon": "fa fa-github",
|
||||||
"position" : "left",
|
"position" : "left",
|
||||||
"url": "https://github.com/CopterExpress/clever"
|
"url": "https://github.com/CopterExpress/clover"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"addcssjs": {
|
"addcssjs": {
|
||||||
"css": ["../clever.css"],
|
"css": ["../clover.css"],
|
||||||
"js": ["../clever.js"]
|
"js": ["../clover.js"]
|
||||||
},
|
},
|
||||||
"language-picker": {
|
"language-picker": {
|
||||||
"languages": [["ru", "Russian"], ["en", "English"]]
|
"languages": [["ru", "Russian"], ["en", "English"]]
|
||||||
|
|||||||
34
builder/assets/avahi-services/sftp-ssh.service
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
<?xml version="1.0" standalone='no'?><!--*-nxml-*-->
|
||||||
|
<!DOCTYPE service-group SYSTEM "avahi-service.dtd">
|
||||||
|
|
||||||
|
<!--
|
||||||
|
This file is part of avahi.
|
||||||
|
|
||||||
|
avahi is free software; you can redistribute it and/or modify it
|
||||||
|
under the terms of the GNU Lesser General Public License as
|
||||||
|
published by the Free Software Foundation; either version 2 of the
|
||||||
|
License, or (at your option) any later version.
|
||||||
|
|
||||||
|
avahi is distributed in the hope that it will be useful, but
|
||||||
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with avahi; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||||
|
02111-1307 USA.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!-- See avahi.service(5) for more information about this configuration file -->
|
||||||
|
|
||||||
|
<service-group>
|
||||||
|
|
||||||
|
<name replace-wildcards="yes">%h</name>
|
||||||
|
|
||||||
|
<service>
|
||||||
|
<type>_sftp-ssh._tcp</type>
|
||||||
|
<port>22</port>
|
||||||
|
</service>
|
||||||
|
|
||||||
|
</service-group>
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python
|
||||||
|
|
||||||
from distutils.core import setup
|
from distutils.core import setup
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# Information: https://clever.coex.tech/en/programming.html
|
# Information: https://clover.coex.tech/programming
|
||||||
|
|
||||||
import rospy
|
import rospy
|
||||||
from clover import srv
|
from clover import srv
|
||||||
@@ -15,7 +15,7 @@ set_attitude = rospy.ServiceProxy('set_attitude', srv.SetAttitude)
|
|||||||
set_rates = rospy.ServiceProxy('set_rates', srv.SetRates)
|
set_rates = rospy.ServiceProxy('set_rates', srv.SetRates)
|
||||||
land = rospy.ServiceProxy('land', Trigger)
|
land = rospy.ServiceProxy('land', Trigger)
|
||||||
|
|
||||||
# Takeoff and hover 1 m above the ground
|
# Take off and hover 1 m above the ground
|
||||||
navigate(x=0, y=0, z=1, frame_id='body', auto_arm=True)
|
navigate(x=0, y=0, z=1, frame_id='body', auto_arm=True)
|
||||||
|
|
||||||
# Wait for 3 seconds
|
# Wait for 3 seconds
|
||||||
|
|||||||
37
builder/assets/examples/flight_marker.py
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
# Information: https://clover.coex.tech/en/aruco.html
|
||||||
|
|
||||||
|
import rospy
|
||||||
|
from clover import srv
|
||||||
|
from std_srvs.srv import Trigger
|
||||||
|
|
||||||
|
rospy.init_node('flight')
|
||||||
|
|
||||||
|
get_telemetry = rospy.ServiceProxy('get_telemetry', srv.GetTelemetry)
|
||||||
|
navigate = rospy.ServiceProxy('navigate', srv.Navigate)
|
||||||
|
navigate_global = rospy.ServiceProxy('navigate_global', srv.NavigateGlobal)
|
||||||
|
set_position = rospy.ServiceProxy('set_position', srv.SetPosition)
|
||||||
|
set_velocity = rospy.ServiceProxy('set_velocity', srv.SetVelocity)
|
||||||
|
set_attitude = rospy.ServiceProxy('set_attitude', srv.SetAttitude)
|
||||||
|
set_rates = rospy.ServiceProxy('set_rates', srv.SetRates)
|
||||||
|
land = rospy.ServiceProxy('land', Trigger)
|
||||||
|
|
||||||
|
# Take off and hover 1 m above the ground
|
||||||
|
navigate(x=0, y=0, z=1, frame_id='body', auto_arm=True)
|
||||||
|
|
||||||
|
# Wait for 3 seconds
|
||||||
|
rospy.sleep(3)
|
||||||
|
|
||||||
|
# Fly 1 meter above ArUco marker 0
|
||||||
|
navigate(x=0, y=0, z=1, frame_id='aruco_0')
|
||||||
|
|
||||||
|
# Wait for 3 seconds
|
||||||
|
rospy.sleep(3)
|
||||||
|
|
||||||
|
# Fly to x=1 y=1 z=1 relative to ArUco markers map
|
||||||
|
navigate(x=1, y=1, z=1, frame_id='aruco_map')
|
||||||
|
|
||||||
|
# Wait for 3 seconds
|
||||||
|
rospy.sleep(3)
|
||||||
|
|
||||||
|
# Perform landing
|
||||||
|
land()
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
# Information: https://clever.coex.tech/en/leds.html
|
# Information: https://clover.coex.tech/en/leds.html
|
||||||
|
|
||||||
import rospy
|
import rospy
|
||||||
from clover.srv import SetLEDEffect
|
from clover.srv import SetLEDEffect
|
||||||
|
|||||||
41
builder/assets/examples/navigate_wait.py
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
# Information: https://clover.coex.tech/en/snippets.html#block-nav
|
||||||
|
|
||||||
|
import math
|
||||||
|
import rospy
|
||||||
|
from clover import srv
|
||||||
|
from std_srvs.srv import Trigger
|
||||||
|
|
||||||
|
rospy.init_node('flight')
|
||||||
|
|
||||||
|
get_telemetry = rospy.ServiceProxy('get_telemetry', srv.GetTelemetry)
|
||||||
|
navigate = rospy.ServiceProxy('navigate', srv.Navigate)
|
||||||
|
navigate_global = rospy.ServiceProxy('navigate_global', srv.NavigateGlobal)
|
||||||
|
set_position = rospy.ServiceProxy('set_position', srv.SetPosition)
|
||||||
|
set_velocity = rospy.ServiceProxy('set_velocity', srv.SetVelocity)
|
||||||
|
set_attitude = rospy.ServiceProxy('set_attitude', srv.SetAttitude)
|
||||||
|
set_rates = rospy.ServiceProxy('set_rates', srv.SetRates)
|
||||||
|
land = rospy.ServiceProxy('land', Trigger)
|
||||||
|
|
||||||
|
def navigate_wait(x=0, y=0, z=0, yaw=float('nan'), yaw_rate=0, speed=0.5, \
|
||||||
|
frame_id='body', tolerance=0.2, auto_arm=False):
|
||||||
|
|
||||||
|
res = navigate(x=x, y=y, z=z, yaw=yaw, yaw_rate=yaw_rate, speed=speed, \
|
||||||
|
frame_id=frame_id, auto_arm=auto_arm)
|
||||||
|
|
||||||
|
if not res.success:
|
||||||
|
return res
|
||||||
|
|
||||||
|
while not rospy.is_shutdown():
|
||||||
|
telem = get_telemetry(frame_id='navigate_target')
|
||||||
|
if math.sqrt(telem.x ** 2 + telem.y ** 2 + telem.z ** 2) < tolerance:
|
||||||
|
return res
|
||||||
|
rospy.sleep(0.2)
|
||||||
|
|
||||||
|
# Take off 1 meter
|
||||||
|
navigate_wait(z=1, frame_id='body', auto_arm=True)
|
||||||
|
|
||||||
|
# Fly forward 1 m
|
||||||
|
navigate_wait(x=1, frame_id='body')
|
||||||
|
|
||||||
|
# Land
|
||||||
|
land()
|
||||||
@@ -62,6 +62,10 @@ hostnamectl set-hostname $NEW_HOSTNAME
|
|||||||
sed -i 's/127\.0\.1\.1.*/127.0.1.1\t'${NEW_HOSTNAME}' '${NEW_HOSTNAME}'.local/g' /etc/hosts
|
sed -i 's/127\.0\.1\.1.*/127.0.1.1\t'${NEW_HOSTNAME}' '${NEW_HOSTNAME}'.local/g' /etc/hosts
|
||||||
# .local (mdns) hostname added to make it accesable when wlan and ethernet interfaces are down
|
# .local (mdns) hostname added to make it accesable when wlan and ethernet interfaces are down
|
||||||
|
|
||||||
|
echo_stamp "Enable ROS services"
|
||||||
|
systemctl enable roscore
|
||||||
|
systemctl enable clover
|
||||||
|
|
||||||
echo_stamp "Harware setup"
|
echo_stamp "Harware setup"
|
||||||
/root/hardware_setup.sh
|
/root/hardware_setup.sh
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
# Example:
|
# Example:
|
||||||
# DocumentRoot /home/krypton/htdocs
|
# DocumentRoot /home/krypton/htdocs
|
||||||
|
|
||||||
DocumentRoot /home/pi/catkin_ws/src/clover/clover/www
|
DocumentRoot /home/pi/.ros/www
|
||||||
|
|
||||||
# Redirect:
|
# Redirect:
|
||||||
# ---------
|
# ---------
|
||||||
|
|||||||
@@ -1,9 +0,0 @@
|
|||||||
python3-wxgtk:
|
|
||||||
debian:
|
|
||||||
buster: [python3-wxgtk4.0]
|
|
||||||
python3-serial:
|
|
||||||
debian:
|
|
||||||
buster: [python3-serial]
|
|
||||||
python3-requests:
|
|
||||||
debian:
|
|
||||||
buster: [python3-requests]
|
|
||||||
@@ -108,6 +108,8 @@ ${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} exec ${SCRIPTS_DIR}'/image-software
|
|||||||
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/examples' '/home/pi/'
|
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/examples' '/home/pi/'
|
||||||
# network setup
|
# network setup
|
||||||
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} exec ${SCRIPTS_DIR}'/image-network.sh'
|
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} exec ${SCRIPTS_DIR}'/image-network.sh'
|
||||||
|
# avahi setup
|
||||||
|
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/avahi-services/sftp-ssh.service' '/etc/avahi/services'
|
||||||
|
|
||||||
# If RPi then use a one thread to build a ROS package on RPi, else use all
|
# If RPi then use a one thread to build a ROS package on RPi, else use all
|
||||||
[[ $(arch) == 'armv7l' ]] && NUMBER_THREADS=1 || NUMBER_THREADS=$(nproc --all)
|
[[ $(arch) == 'armv7l' ]] && NUMBER_THREADS=1 || NUMBER_THREADS=$(nproc --all)
|
||||||
@@ -115,7 +117,6 @@ ${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} exec ${SCRIPTS_DIR}'/image-network.
|
|||||||
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/clover.service' '/lib/systemd/system/'
|
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/clover.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/roscore.service' '/lib/systemd/system/'
|
||||||
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/melodic-rosdep-clover.yaml' '/etc/ros/rosdep/'
|
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/melodic-rosdep-clover.yaml' '/etc/ros/rosdep/'
|
||||||
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/python3.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/pigpiod.service' '/lib/systemd/system/'
|
||||||
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/launch.nanorc' '/usr/share/nano/'
|
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/launch.nanorc' '/usr/share/nano/'
|
||||||
|
|||||||
@@ -68,8 +68,7 @@ my_travis_retry() {
|
|||||||
# TODO: 'kinetic-rosdep-clover.yaml' should add only if we use our repo?
|
# TODO: 'kinetic-rosdep-clover.yaml' should add only if we use our repo?
|
||||||
echo_stamp "Init rosdep"
|
echo_stamp "Init rosdep"
|
||||||
my_travis_retry rosdep init
|
my_travis_retry rosdep init
|
||||||
echo "yaml file:///etc/ros/rosdep/melodic-rosdep-clover.yaml" > /etc/ros/rosdep/sources.list.d/30-clover.list
|
echo "yaml file:///etc/ros/rosdep/melodic-rosdep-clover.yaml" >> /etc/ros/rosdep/sources.list.d/20-default.list
|
||||||
echo "yaml file:///etc/ros/rosdep/python3.yaml" > /etc/ros/rosdep/sources.list.d/40-python3.list
|
|
||||||
my_travis_retry rosdep update
|
my_travis_retry rosdep update
|
||||||
|
|
||||||
echo_stamp "Populate rosdep for ROS user"
|
echo_stamp "Populate rosdep for ROS user"
|
||||||
@@ -88,7 +87,6 @@ resolve_rosdep() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export ROS_IP='127.0.0.1' # needed for running tests
|
export ROS_IP='127.0.0.1' # needed for running tests
|
||||||
export ROS_PYTHON_VERSION=3
|
|
||||||
|
|
||||||
echo_stamp "Reconfiguring Clover repository for simplier unshallowing"
|
echo_stamp "Reconfiguring Clover repository for simplier unshallowing"
|
||||||
cd /home/pi/catkin_ws/src/clover
|
cd /home/pi/catkin_ws/src/clover
|
||||||
@@ -97,15 +95,11 @@ git config remote.origin.fetch "+refs/heads/*:refs/remotes/origin/*"
|
|||||||
echo_stamp "Build and install Clover"
|
echo_stamp "Build and install Clover"
|
||||||
cd /home/pi/catkin_ws
|
cd /home/pi/catkin_ws
|
||||||
resolve_rosdep $(pwd)
|
resolve_rosdep $(pwd)
|
||||||
my_travis_retry pip3 install wheel
|
my_travis_retry pip install wheel
|
||||||
my_travis_retry pip3 install -r /home/pi/catkin_ws/src/clover/clover/requirements.txt
|
my_travis_retry pip install -r /home/pi/catkin_ws/src/clover/clover/requirements.txt
|
||||||
source /opt/ros/melodic/setup.bash
|
source /opt/ros/melodic/setup.bash
|
||||||
catkin_make -j2 -DCMAKE_BUILD_TYPE=Release
|
catkin_make -j2 -DCMAKE_BUILD_TYPE=Release
|
||||||
|
|
||||||
echo_stamp "Enable ROS services"
|
|
||||||
systemctl enable roscore
|
|
||||||
systemctl enable clover
|
|
||||||
|
|
||||||
echo_stamp "Install clever package (for backwards compatibility)"
|
echo_stamp "Install clever package (for backwards compatibility)"
|
||||||
cd /home/pi/catkin_ws/src/clover/builder/assets/clever
|
cd /home/pi/catkin_ws/src/clover/builder/assets/clever
|
||||||
./setup.py install
|
./setup.py install
|
||||||
@@ -135,7 +129,7 @@ echo_stamp "Install GeographicLib datasets (needed for mavros)" \
|
|||||||
|
|
||||||
# FIXME: Buster comes with tornado==5.1.1 but we need tornado==4.2.1 for rosbridge_suite
|
# FIXME: Buster comes with tornado==5.1.1 but we need tornado==4.2.1 for rosbridge_suite
|
||||||
# (note that Python 3 will still have a more recent version)
|
# (note that Python 3 will still have a more recent version)
|
||||||
pip3 install tornado==4.2.1
|
pip install tornado==4.2.1
|
||||||
|
|
||||||
echo_stamp "Running tests"
|
echo_stamp "Running tests"
|
||||||
cd /home/pi/catkin_ws
|
cd /home/pi/catkin_ws
|
||||||
@@ -149,7 +143,6 @@ echo_stamp "Setup ROS environment"
|
|||||||
cat << EOF >> /home/pi/.bashrc
|
cat << EOF >> /home/pi/.bashrc
|
||||||
LANG='C.UTF-8'
|
LANG='C.UTF-8'
|
||||||
LC_ALL='C.UTF-8'
|
LC_ALL='C.UTF-8'
|
||||||
ROS_DISTRO='melodic'
|
|
||||||
export ROS_HOSTNAME=\`hostname\`.local
|
export ROS_HOSTNAME=\`hostname\`.local
|
||||||
source /opt/ros/melodic/setup.bash
|
source /opt/ros/melodic/setup.bash
|
||||||
source /home/pi/catkin_ws/devel/setup.bash
|
source /home/pi/catkin_ws/devel/setup.bash
|
||||||
|
|||||||
@@ -57,6 +57,10 @@ my_travis_retry() {
|
|||||||
return $result
|
return $result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
echo_stamp "Increase apt retries"
|
||||||
|
|
||||||
|
echo "APT::Acquire::Retries \"3\";" > /etc/apt/apt.conf.d/80-retries
|
||||||
|
|
||||||
echo_stamp "Install apt keys & repos"
|
echo_stamp "Install apt keys & repos"
|
||||||
|
|
||||||
# TODO: This STDOUT consist 'OK'
|
# TODO: This STDOUT consist 'OK'
|
||||||
@@ -67,7 +71,7 @@ apt-get update \
|
|||||||
|
|
||||||
echo "deb http://packages.ros.org/ros/ubuntu buster main" > /etc/apt/sources.list.d/ros-latest.list
|
echo "deb http://packages.ros.org/ros/ubuntu buster main" > /etc/apt/sources.list.d/ros-latest.list
|
||||||
echo "deb http://deb.coex.tech/opencv3 buster main" > /etc/apt/sources.list.d/opencv3.list
|
echo "deb http://deb.coex.tech/opencv3 buster main" > /etc/apt/sources.list.d/opencv3.list
|
||||||
echo "deb http://deb.coex.tech/melodic-py3 buster main" > /etc/apt/sources.list.d/rpi-ros-melodic.list
|
echo "deb http://deb.coex.tech/rpi-ros-melodic buster main" > /etc/apt/sources.list.d/rpi-ros-melodic.list
|
||||||
echo "deb http://deb.coex.tech/clover buster main" > /etc/apt/sources.list.d/clover.list
|
echo "deb http://deb.coex.tech/clover buster main" > /etc/apt/sources.list.d/clover.list
|
||||||
|
|
||||||
echo_stamp "Update apt cache"
|
echo_stamp "Update apt cache"
|
||||||
@@ -95,21 +99,21 @@ libjpeg8 \
|
|||||||
tcpdump \
|
tcpdump \
|
||||||
ltrace \
|
ltrace \
|
||||||
libpoco-dev \
|
libpoco-dev \
|
||||||
python3-rosdep \
|
libzbar0 \
|
||||||
python3-rosinstall-generator \
|
python-rosdep \
|
||||||
python3-wstool \
|
python-rosinstall-generator \
|
||||||
python3-rosinstall \
|
python-wstool \
|
||||||
|
python-rosinstall \
|
||||||
build-essential \
|
build-essential \
|
||||||
libffi-dev \
|
libffi-dev \
|
||||||
monkey \
|
monkey \
|
||||||
pigpio python-pigpio python3-pigpio \
|
pigpio python-pigpio python3-pigpio \
|
||||||
i2c-tools \
|
i2c-tools \
|
||||||
espeak espeak-data python3-espeak \
|
espeak espeak-data python-espeak \
|
||||||
ntpdate \
|
ntpdate \
|
||||||
python-dev \
|
python-dev \
|
||||||
python3-dev \
|
python3-dev \
|
||||||
python3-venv \
|
python-systemd \
|
||||||
python3-systemd \
|
|
||||||
mjpg-streamer \
|
mjpg-streamer \
|
||||||
python3-opencv \
|
python3-opencv \
|
||||||
&& echo_stamp "Everything was installed!" "SUCCESS" \
|
&& echo_stamp "Everything was installed!" "SUCCESS" \
|
||||||
@@ -133,14 +137,13 @@ pip3 --version
|
|||||||
|
|
||||||
echo_stamp "Install and enable Butterfly (web terminal)"
|
echo_stamp "Install and enable Butterfly (web terminal)"
|
||||||
echo_stamp "Workaround for tornado >= 6.0 breaking butterfly"
|
echo_stamp "Workaround for tornado >= 6.0 breaking butterfly"
|
||||||
my_travis_retry pip3 install tornado==4.2.1
|
my_travis_retry pip3 install tornado==5.1.1
|
||||||
my_travis_retry pip3 install butterfly
|
my_travis_retry pip3 install butterfly
|
||||||
my_travis_retry pip3 install butterfly[systemd]
|
my_travis_retry pip3 install butterfly[systemd]
|
||||||
systemctl enable butterfly.socket
|
systemctl enable butterfly.socket
|
||||||
|
|
||||||
echo_stamp "Install ws281x library"
|
echo_stamp "Install ws281x library"
|
||||||
my_travis_retry pip2 install --prefer-binary rpi_ws281x
|
my_travis_retry pip install --prefer-binary rpi_ws281x
|
||||||
my_travis_retry pip3 install --prefer-binary rpi_ws281x
|
|
||||||
|
|
||||||
echo_stamp "Setup Monkey"
|
echo_stamp "Setup Monkey"
|
||||||
mv /etc/monkey/sites/default /etc/monkey/sites/default.orig
|
mv /etc/monkey/sites/default /etc/monkey/sites/default.orig
|
||||||
@@ -156,9 +159,13 @@ rm -rf node-v10.15.0-linux-armv6l/
|
|||||||
rm node-v10.15.0-linux-armv6l.tar.gz
|
rm node-v10.15.0-linux-armv6l.tar.gz
|
||||||
|
|
||||||
echo_stamp "Installing ptvsd"
|
echo_stamp "Installing ptvsd"
|
||||||
my_travis_retry pip2 install ptvsd
|
my_travis_retry pip install ptvsd
|
||||||
my_travis_retry pip3 install ptvsd
|
my_travis_retry pip3 install ptvsd
|
||||||
|
|
||||||
|
echo_stamp "Installing pyzbar"
|
||||||
|
my_travis_retry pip install pyzbar
|
||||||
|
my_travis_retry pip3 install pyzbar
|
||||||
|
|
||||||
echo_stamp "Add .vimrc"
|
echo_stamp "Add .vimrc"
|
||||||
cat << EOF > /home/pi/.vimrc
|
cat << EOF > /home/pi/.vimrc
|
||||||
set mouse-=a
|
set mouse-=a
|
||||||
|
|||||||
@@ -18,15 +18,13 @@ echo "Run image tests"
|
|||||||
|
|
||||||
export ROS_DISTRO='melodic'
|
export ROS_DISTRO='melodic'
|
||||||
export ROS_IP='127.0.0.1'
|
export ROS_IP='127.0.0.1'
|
||||||
export ROS_PYTHON_VERSION=3
|
|
||||||
source /opt/ros/melodic/setup.bash
|
source /opt/ros/melodic/setup.bash
|
||||||
source /home/pi/catkin_ws/devel/setup.bash
|
source /home/pi/catkin_ws/devel/setup.bash
|
||||||
|
|
||||||
cd /home/pi/catkin_ws/src/clover/builder/test/
|
cd /home/pi/catkin_ws/src/clover/builder/test/
|
||||||
./tests.sh
|
./tests.sh
|
||||||
./tests.py
|
./tests.py
|
||||||
# Disable Python 2 tests for image - we're dropping support, right?
|
./tests_py3.py
|
||||||
# ./tests_py2.py
|
|
||||||
[[ $(./tests_clever.py) == "Warning: clever package is renamed to clover" ]] # test backwards compatibility
|
[[ $(./tests_clever.py) == "Warning: clever package is renamed to clover" ]] # test backwards compatibility
|
||||||
|
|
||||||
echo "Move /etc/ld.so.preload back to its original position"
|
echo "Move /etc/ld.so.preload back to its original position"
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
# Perform a "standalone install" in a Docker container
|
# Perform a "standalone install" in a Docker container
|
||||||
|
set -e
|
||||||
# Step 1: Install pip
|
# Step 1: Install pip
|
||||||
apt update
|
apt update
|
||||||
apt install -y curl
|
apt install -y curl
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python
|
||||||
|
|
||||||
# validate all required modules installed
|
# validate all required modules installed
|
||||||
|
|
||||||
@@ -17,8 +17,6 @@ from std_srvs.srv import Trigger
|
|||||||
from clover.srv import GetTelemetry, Navigate, NavigateGlobal, SetPosition, SetVelocity, \
|
from clover.srv import GetTelemetry, Navigate, NavigateGlobal, SetPosition, SetVelocity, \
|
||||||
SetAttitude, SetRates, SetLEDEffect
|
SetAttitude, SetRates, SetLEDEffect
|
||||||
|
|
||||||
get_telemetry = rospy.ServiceProxy('get_telemetry', GetTelemetry)
|
|
||||||
|
|
||||||
import tf2_ros
|
import tf2_ros
|
||||||
import tf2_geometry_msgs
|
import tf2_geometry_msgs
|
||||||
|
|
||||||
@@ -28,5 +26,6 @@ from pymavlink import mavutil
|
|||||||
import rpi_ws281x
|
import rpi_ws281x
|
||||||
import pigpio
|
import pigpio
|
||||||
from espeak import espeak
|
from espeak import espeak
|
||||||
|
from pyzbar import pyzbar
|
||||||
|
|
||||||
print(cv2.getBuildInformation())
|
print cv2.getBuildInformation()
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python
|
||||||
|
|
||||||
# test backwards compatibility
|
# test backwards compatibility
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
|
|
||||||
# Make sure our Python 2 software is installed
|
|
||||||
|
|
||||||
import cv2
|
|
||||||
|
|
||||||
print(cv2.getBuildInformation())
|
|
||||||
8
builder/test/tests_py3.py
Executable file
@@ -0,0 +1,8 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
# Make sure our Python 3 software is installed
|
||||||
|
|
||||||
|
import cv2
|
||||||
|
from pyzbar import pyzbar
|
||||||
|
|
||||||
|
print(cv2.getBuildInformation())
|
||||||
@@ -30,6 +30,12 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake")
|
|||||||
|
|
||||||
find_package(GeographicLib REQUIRED)
|
find_package(GeographicLib REQUIRED)
|
||||||
|
|
||||||
|
find_package(OpenCV 3 REQUIRED
|
||||||
|
COMPONENTS
|
||||||
|
calib3d
|
||||||
|
imgproc
|
||||||
|
)
|
||||||
|
|
||||||
## System dependencies are found with CMake's conventions
|
## System dependencies are found with CMake's conventions
|
||||||
# find_package(Boost REQUIRED COMPONENTS system)
|
# find_package(Boost REQUIRED COMPONENTS system)
|
||||||
|
|
||||||
@@ -204,6 +210,7 @@ add_dependencies(shell ${PROJECT_NAME}_generate_messages_cpp)
|
|||||||
## Specify libraries to link a library or executable target against
|
## Specify libraries to link a library or executable target against
|
||||||
target_link_libraries(${PROJECT_NAME}
|
target_link_libraries(${PROJECT_NAME}
|
||||||
${catkin_LIBRARIES}
|
${catkin_LIBRARIES}
|
||||||
|
${OpenCV_LIBRARIES}
|
||||||
)
|
)
|
||||||
|
|
||||||
#############
|
#############
|
||||||
|
|||||||
73
clover/README.md
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
# `clover` ROS package
|
||||||
|
|
||||||
|
A bundle for autonomous navigation and drone control.
|
||||||
|
|
||||||
|
## Manual installation
|
||||||
|
|
||||||
|
Install ROS Melodic according to the [documentation](http://wiki.ros.org/melodic/Installation), then [create a Catkin workspace](http://wiki.ros.org/catkin/Tutorials/create_a_workspace).
|
||||||
|
|
||||||
|
Clone this repo to directory `~/catkin_ws/src/clover`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd ~/catkin_ws/src
|
||||||
|
git clone https://github.com/CopterExpress/clover.git clover
|
||||||
|
```
|
||||||
|
|
||||||
|
All the required ROS packages (including `mavros` and `opencv`) can be installed using `rosdep`:
|
||||||
|
|
||||||
|
```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
|
||||||
|
cd ~/catkin_ws
|
||||||
|
catkin_make -j1
|
||||||
|
```
|
||||||
|
|
||||||
|
To complete `mavros` install you'll need to install `geographiclib` datasets:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl https://raw.githubusercontent.com/mavlink/mavros/master/mavros/scripts/install_geographiclib_datasets.sh | sudo bash
|
||||||
|
```
|
||||||
|
|
||||||
|
You may optionally install udev rules to provide `/dev/px4fmu` symlink to your PX4-based flight controller connected over USB. Copy `99-px4fmu.rules` to your `/lib/udev/rules.d` folder:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd ~/catkin_ws/src/clover/clover/config
|
||||||
|
sudo cp 99-px4fmu.rules /lib/udev/rules.d
|
||||||
|
```
|
||||||
|
|
||||||
|
Alternatively you may change the `fcu_url` property in `mavros.launch` file to point to your flight controller device.
|
||||||
|
|
||||||
|
## Running
|
||||||
|
|
||||||
|
Enable systemd service `roscore` (if not running):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo systemctl enable /home/<username>/catkin_ws/src/clover/builder/assets/roscore.service
|
||||||
|
sudo systemctl start roscore
|
||||||
|
```
|
||||||
|
|
||||||
|
To start connection to SITL, use:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
roslaunch clover sitl.launch
|
||||||
|
```
|
||||||
|
|
||||||
|
To start connection to the flight controller, use:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
roslaunch clover clover.launch
|
||||||
|
```
|
||||||
|
|
||||||
|
> Note that the package is configured to connect to `/dev/px4fmu` by default (see [previous section](#manual-installation)). Install udev rules or specify path to your FCU device in `mavros.launch`.
|
||||||
|
|
||||||
|
Also, you can enable and start the systemd service:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo systemctl enable /home/<username>/catkin_ws/src/clover/deploy/clover.service
|
||||||
|
sudo systemctl start clover
|
||||||
|
```
|
||||||
@@ -1,17 +1,17 @@
|
|||||||
image_width: 640
|
image_width: 640
|
||||||
image_height: 480
|
image_height: 480
|
||||||
distortion_model: plumb_bob
|
distortion_model: plumb_bob
|
||||||
camera_name: raspicam
|
camera_name: main_camera_optical
|
||||||
camera_matrix:
|
camera_matrix:
|
||||||
rows: 3
|
rows: 3
|
||||||
cols: 3
|
cols: 3
|
||||||
data:
|
data:
|
||||||
- 332.47884746146343
|
- 332.47884746146343
|
||||||
- 0.
|
- 0.
|
||||||
- 324.38022493658536
|
- 320.0
|
||||||
- 0.
|
- 0.
|
||||||
- 333.1761847948052
|
- 333.1761847948052
|
||||||
- 219.6445547142857
|
- 240.0
|
||||||
- 0.
|
- 0.
|
||||||
- 0.
|
- 0.
|
||||||
- 1.
|
- 1.
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
image_width: 320
|
|
||||||
image_height: 240
|
|
||||||
distortion_model: plumb_bob
|
|
||||||
camera_name: raspicam
|
|
||||||
camera_matrix:
|
|
||||||
rows: 3
|
|
||||||
cols: 3
|
|
||||||
data:
|
|
||||||
- 166.23942373073172
|
|
||||||
- 0.
|
|
||||||
- 162.19011246829268
|
|
||||||
- 0.
|
|
||||||
- 166.5880923974026
|
|
||||||
- 109.82227735714285
|
|
||||||
- 0.
|
|
||||||
- 0.
|
|
||||||
- 1.
|
|
||||||
distortion_coefficients:
|
|
||||||
rows: 1
|
|
||||||
cols: 8
|
|
||||||
data: [ 2.15356885e-01, -1.17472846e-01, -3.06197672e-04,
|
|
||||||
-1.09444025e-04, -4.53657258e-03, 5.73090623e-01,
|
|
||||||
-1.27574577e-01, -2.86125589e-02, 0.00000000e+00,
|
|
||||||
0.00000000e+00, 0.00000000e+00, 0.00000000e+00,
|
|
||||||
0.00000000e+00, 0.00000000e+00]
|
|
||||||
rectification_matrix:
|
|
||||||
rows: 3
|
|
||||||
cols: 3
|
|
||||||
data: [1, 0, 0, 0, 1, 0, 0, 0, 1]
|
|
||||||
projection_matrix:
|
|
||||||
rows: 3
|
|
||||||
cols: 4
|
|
||||||
data:
|
|
||||||
- 166.23942373073172
|
|
||||||
- 0.
|
|
||||||
- 162.19011246829268
|
|
||||||
- 0.
|
|
||||||
- 0.
|
|
||||||
- 166.5880923974026
|
|
||||||
- 109.82227735714285
|
|
||||||
- 0.
|
|
||||||
- 0.
|
|
||||||
- 0.
|
|
||||||
- 1.
|
|
||||||
- 0.
|
|
||||||
@@ -1,20 +1,26 @@
|
|||||||
<launch>
|
<launch>
|
||||||
<arg name="aruco_detect" default="true"/>
|
<arg name="aruco_detect" default="false"/> <!-- markers recognition enabled -->
|
||||||
<arg name="aruco_map" default="false"/>
|
<arg name="aruco_map" default="false"/> <!-- markers map recognition enabled -->
|
||||||
<arg name="aruco_vpe" default="false"/>
|
<arg name="aruco_vpe" default="false"/> <!-- markers map positioning enabled -->
|
||||||
|
<arg name="placement" default="floor"/> <!-- markers placement: floor, ceiling, unknown -->
|
||||||
|
<arg name="length" default="0.33"/> <!-- not-in-map markers length, m -->
|
||||||
|
<arg name="map" default="map.txt"/> <!-- markers map file name -->
|
||||||
|
|
||||||
<!-- For additional help go to https://clever.coex.tech/aruco -->
|
<!-- For additional help go to https://clover.coex.tech/aruco -->
|
||||||
|
|
||||||
<!-- aruco_detect: detect aruco markers, estimate poses -->
|
<!-- aruco_detect: detect aruco markers, estimate poses -->
|
||||||
<node name="aruco_detect" pkg="nodelet" if="$(arg aruco_detect)" type="nodelet" args="load aruco_pose/aruco_detect nodelet_manager" output="screen" clear_params="true">
|
<node name="aruco_detect" pkg="nodelet" if="$(arg aruco_detect)" type="nodelet" args="load aruco_pose/aruco_detect nodelet_manager" output="screen" 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="map_markers" to="aruco_map/markers" if="$(arg aruco_map)"/>
|
<remap from="map_markers" to="aruco_map/markers" if="$(arg aruco_map)"/>
|
||||||
<param name="cornerRefinementMethod" value="2"/>
|
|
||||||
<param name="estimate_poses" value="true"/>
|
<param name="estimate_poses" value="true"/>
|
||||||
<param name="send_tf" value="true"/>
|
<param name="send_tf" value="true"/>
|
||||||
<param name="known_tilt" value="map"/>
|
<param name="known_tilt" value="map" if="$(eval placement == 'floor')"/>
|
||||||
<param name="length" value="0.33"/>
|
<param name="known_tilt" value="map_flipped" if="$(eval position == 'ceiling')"/>
|
||||||
|
<param name="length" value="$(arg length)"/>
|
||||||
|
<!-- aruco detector parameters -->
|
||||||
|
<param name="cornerRefinementMethod" value="2"/> <!-- contour refinement -->
|
||||||
|
<param name="minMarkerPerimeterRate" value="0.075"/> <!-- 0.075 for 320x240, 0.0375 for 640x480 -->
|
||||||
</node>
|
</node>
|
||||||
|
|
||||||
<!-- aruco_map: estimate aruco map pose -->
|
<!-- aruco_map: estimate aruco map pose -->
|
||||||
@@ -22,8 +28,9 @@
|
|||||||
<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="map" value="$(find aruco_pose)/map/map.txt"/>
|
<param name="map" value="$(find aruco_pose)/map/$(arg map)"/>
|
||||||
<param name="known_tilt" value="map"/>
|
<param name="known_tilt" value="map" if="$(eval placement == 'floor')"/>
|
||||||
|
<param name="known_tilt" value="map_flipped" if="$(eval placement == 'ceiling')"/>
|
||||||
<param name="image_axis" value="true"/>
|
<param name="image_axis" value="true"/>
|
||||||
<param name="frame_id" value="aruco_map_detected" if="$(arg aruco_vpe)"/>
|
<param name="frame_id" value="aruco_map_detected" if="$(arg aruco_vpe)"/>
|
||||||
<param name="frame_id" value="aruco_map" unless="$(arg aruco_vpe)"/>
|
<param name="frame_id" value="aruco_map" unless="$(arg aruco_vpe)"/>
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
<launch>
|
<launch>
|
||||||
<arg name="fcu_conn" default="usb"/>
|
<arg name="fcu_conn" default="usb"/> <!-- FCU connection type: usb, uart, tcp, udp, sitl -->
|
||||||
<arg name="fcu_ip" default="127.0.0.1"/>
|
<arg name="fcu_ip" default="127.0.0.1"/> <!-- FCU IP adddress (if using TCP/UDP) -->
|
||||||
<arg name="gcs_bridge" default="tcp"/>
|
<arg name="fcu_sys_id" default="1"/> <!-- MAVLink system ID, noeditor -->
|
||||||
<arg name="web_video_server" default="true"/>
|
<arg name="gcs_bridge" default="tcp"/> <!-- GCS bridge type: tcp, udp, udp-b, udp-pb -->
|
||||||
<arg name="rosbridge" default="true"/>
|
<arg name="web_video_server" default="true"/> <!-- web video server enabled -->
|
||||||
<arg name="main_camera" default="true"/>
|
<arg name="rosbridge" default="true"/> <!-- rosbridge_suite enabled, noeditor -->
|
||||||
<arg name="optical_flow" default="true"/>
|
<arg name="main_camera" default="true"/> <!-- main camera enabled -->
|
||||||
<arg name="aruco" default="false"/>
|
<arg name="optical_flow" default="true"/> <!-- optical flow enabled -->
|
||||||
<arg name="rangefinder_vl53l1x" default="true"/>
|
<arg name="rangefinder_vl53l1x" default="true"/> <!-- VL53l1X rangefinder enabled -->
|
||||||
<arg name="led" default="true"/>
|
<arg name="led" default="true"/> <!-- LED strip driver enabled -->
|
||||||
<arg name="rc" default="true"/>
|
<arg name="rc" default="true"/> <!-- support for mobile RC enabled -->
|
||||||
<arg name="shell" default="true"/>
|
<arg name="shell" default="true"/>
|
||||||
|
|
||||||
<!-- log formatting -->
|
<!-- log formatting -->
|
||||||
@@ -19,6 +19,7 @@
|
|||||||
<include file="$(find clover)/launch/mavros.launch">
|
<include file="$(find clover)/launch/mavros.launch">
|
||||||
<arg name="fcu_conn" value="$(arg fcu_conn)"/>
|
<arg name="fcu_conn" value="$(arg fcu_conn)"/>
|
||||||
<arg name="fcu_ip" value="$(arg fcu_ip)"/>
|
<arg name="fcu_ip" value="$(arg fcu_ip)"/>
|
||||||
|
<arg name="fcu_sys_id" value="$(arg fcu_sys_id)"/>
|
||||||
<arg name="gcs_bridge" value="$(arg gcs_bridge)"/>
|
<arg name="gcs_bridge" value="$(arg gcs_bridge)"/>
|
||||||
</include>
|
</include>
|
||||||
|
|
||||||
@@ -29,7 +30,7 @@
|
|||||||
</node>
|
</node>
|
||||||
|
|
||||||
<!-- aruco markers -->
|
<!-- aruco markers -->
|
||||||
<include file="$(find clover)/launch/aruco.launch" if="$(arg aruco)"/>
|
<include file="$(find clover)/launch/aruco.launch"/>
|
||||||
|
|
||||||
<!-- optical flow -->
|
<!-- optical flow -->
|
||||||
<node pkg="nodelet" type="nodelet" name="optical_flow" args="load clover/optical_flow nodelet_manager" if="$(arg optical_flow)" clear_params="true" output="screen">
|
<node pkg="nodelet" type="nodelet" name="optical_flow" args="load clover/optical_flow nodelet_manager" if="$(arg optical_flow)" clear_params="true" output="screen">
|
||||||
@@ -80,4 +81,16 @@
|
|||||||
|
|
||||||
<!-- Shell access through ROS service -->
|
<!-- Shell access through ROS service -->
|
||||||
<node name="shell" pkg="clover" type="shell" output="screen" if="$(arg shell)"/>
|
<node name="shell" pkg="clover" type="shell" output="screen" if="$(arg shell)"/>
|
||||||
|
|
||||||
|
<!-- Update static directory -->
|
||||||
|
<node pkg="roswww_static" name="roswww_static" type="main.py" clear_params="true">
|
||||||
|
<param name="default_package" value="clover"/>
|
||||||
|
</node>
|
||||||
|
|
||||||
|
<!-- roslaunch editor parameters -->
|
||||||
|
<group ns="roslaunch_editor">
|
||||||
|
<param name="items" type="yaml" value="[clover/clover.launch, clover/main_camera.launch, clover/aruco.launch, clover/led.launch]"/>
|
||||||
|
<param name="apply_command" value="sudo systemctl restart clover"/>
|
||||||
|
<param name="hide_uncommented" value="true"/>
|
||||||
|
</group>
|
||||||
</launch>
|
</launch>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<launch>
|
<launch>
|
||||||
<arg name="ws281x" default="true"/>
|
<arg name="ws281x" default="true"/> <!-- LED strip driver enabled -->
|
||||||
<arg name="led_effect" default="true"/>
|
<arg name="led_effect" default="true"/> <!-- LED effect API enabled -->
|
||||||
<arg name="led_notify" default="true"/>
|
<arg name="led_notify" default="all"/> <!-- LED strip notifications: all, battery, none -->
|
||||||
|
|
||||||
<!-- For additional help go to https://clover.coex.tech/led -->
|
<!-- For additional help go to https://clover.coex.tech/led -->
|
||||||
|
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
<param name="fade_period" value="0.5"/>
|
<param name="fade_period" value="0.5"/>
|
||||||
<param name="rainbow_period" value="5"/>
|
<param name="rainbow_period" value="5"/>
|
||||||
<!-- events effects table -->
|
<!-- events effects table -->
|
||||||
<rosparam param="notify" if="$(arg led_notify)">
|
<rosparam param="notify" if="$(eval led_notify == 'all')">
|
||||||
startup: { r: 255, g: 255, b: 255 }
|
startup: { r: 255, g: 255, b: 255 }
|
||||||
connected: { effect: rainbow }
|
connected: { effect: rainbow }
|
||||||
disconnected: { effect: blink, r: 255, g: 50, b: 50 }
|
disconnected: { effect: blink, r: 255, g: 50, b: 50 }
|
||||||
@@ -34,5 +34,8 @@
|
|||||||
low_battery: { threshold: 3.7, effect: blink_fast, r: 255, g: 0, b: 0 }
|
low_battery: { threshold: 3.7, effect: blink_fast, r: 255, g: 0, b: 0 }
|
||||||
error: { effect: flash, r: 255, g: 0, b: 0 }
|
error: { effect: flash, r: 255, g: 0, b: 0 }
|
||||||
</rosparam>
|
</rosparam>
|
||||||
|
<rosparam param="notify" if="$(eval led_notify == 'battery')">
|
||||||
|
low_battery: { threshold: 3.7, effect: blink_fast, r: 255, g: 0, b: 0 }
|
||||||
|
</rosparam>
|
||||||
</node>
|
</node>
|
||||||
</launch>
|
</launch>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<launch>
|
<launch>
|
||||||
<!-- article about camera setup: https://clever.coex.tech/camera_frame -->
|
<!-- article about camera setup: https://clover.coex.tech/camera_setup -->
|
||||||
|
|
||||||
<arg name="direction_z" default="down"/> <!-- direction the camera points: down, up -->
|
<arg name="direction_z" default="down"/> <!-- direction the camera points: down, up -->
|
||||||
<arg name="direction_y" default="backward"/> <!-- direction the camera cable points: backward, forward -->
|
<arg name="direction_y" default="backward"/> <!-- direction the camera cable points: backward, forward -->
|
||||||
@@ -18,14 +18,14 @@
|
|||||||
<node pkg="nodelet" type="nodelet" name="main_camera" args="load cv_camera/CvCameraNodelet nodelet_manager" clear_params="true">
|
<node pkg="nodelet" type="nodelet" name="main_camera" args="load cv_camera/CvCameraNodelet nodelet_manager" clear_params="true">
|
||||||
<param name="device_path" value="/dev/video0"/> <!-- v4l2 device -->
|
<param name="device_path" value="/dev/video0"/> <!-- v4l2 device -->
|
||||||
<param name="frame_id" value="main_camera_optical"/>
|
<param name="frame_id" value="main_camera_optical"/>
|
||||||
<param name="camera_info_url" value="file://$(find clover)/camera_info/fisheye_cam_320.yaml"/>
|
<param name="camera_info_url" value="file://$(find clover)/camera_info/fisheye_cam.yaml"/>
|
||||||
|
|
||||||
<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 -->
|
<param name="rescale_camera_info" value="true"/> <!-- automatically rescale camera calibration info -->
|
||||||
|
|
||||||
<!-- camera resolution, NOTE: camera_info file should match it -->
|
<!-- camera resolution -->
|
||||||
<param name="image_width" value="320"/>
|
<param name="image_width" value="320"/>
|
||||||
<param name="image_height" value="240"/>
|
<param name="image_height" value="240"/>
|
||||||
</node>
|
</node>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<launch>
|
<launch>
|
||||||
<arg name="fcu_conn" default="usb"/> <!-- options: usb, uart, tcp, udp, sitl -->
|
<arg name="fcu_conn" default="usb"/> <!-- options: usb, uart, tcp, udp, sitl -->
|
||||||
<arg name="fcu_ip" default="127.0.0.1"/>
|
<arg name="fcu_ip" default="127.0.0.1"/>
|
||||||
|
<arg name="fcu_sys_id" default="1"/>
|
||||||
<arg name="gcs_bridge" default="tcp"/>
|
<arg name="gcs_bridge" default="tcp"/>
|
||||||
<arg name="viz" default="true"/>
|
<arg name="viz" default="true"/>
|
||||||
<arg name="respawn" default="true"/>
|
<arg name="respawn" default="true"/>
|
||||||
@@ -19,6 +20,9 @@
|
|||||||
<!-- sitl since PX4 1.9.0 -->
|
<!-- sitl since PX4 1.9.0 -->
|
||||||
<param name="fcu_url" value="udp://@$(arg fcu_ip):14580" if="$(eval fcu_conn == 'sitl')"/>
|
<param name="fcu_url" value="udp://@$(arg fcu_ip):14580" if="$(eval fcu_conn == 'sitl')"/>
|
||||||
|
|
||||||
|
<!-- set target_system_id -->
|
||||||
|
<param name="target_system_id" value="$(arg fcu_sys_id)" />
|
||||||
|
|
||||||
<!-- gcs bridge -->
|
<!-- gcs bridge -->
|
||||||
<param name="gcs_url" value="tcp-l://0.0.0.0:5760" if="$(eval gcs_bridge == 'tcp')"/>
|
<param name="gcs_url" value="tcp-l://0.0.0.0:5760" if="$(eval gcs_bridge == 'tcp')"/>
|
||||||
<param name="gcs_url" value="udp://0.0.0.0:14550@14550" if="$(eval gcs_bridge == 'udp')"/>
|
<param name="gcs_url" value="udp://0.0.0.0:14550@14550" if="$(eval gcs_bridge == 'udp')"/>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0"?>
|
<?xml version="1.0"?>
|
||||||
<package format="3">
|
<package format="2">
|
||||||
<name>clover</name>
|
<name>clover</name>
|
||||||
<version>0.0.1</version>
|
<version>0.0.1</version>
|
||||||
<description>The Clover package</description>
|
<description>The Clover package</description>
|
||||||
@@ -7,7 +7,7 @@
|
|||||||
<maintainer email="okalachev@gmail.com">Oleg Kalachev</maintainer>
|
<maintainer email="okalachev@gmail.com">Oleg Kalachev</maintainer>
|
||||||
<license>MIT</license>
|
<license>MIT</license>
|
||||||
|
|
||||||
<url type="website">https://clever.coex.tech/</url>
|
<url type="website">https://clover.coex.tech/</url>
|
||||||
<author email="okalachev@gmail.com">Oleg Kalachev</author>
|
<author email="okalachev@gmail.com">Oleg Kalachev</author>
|
||||||
<author email="urpylka@gmail.com">Artem Smirnov</author>
|
<author email="urpylka@gmail.com">Artem Smirnov</author>
|
||||||
|
|
||||||
@@ -37,10 +37,8 @@
|
|||||||
<depend>rosbridge_server</depend>
|
<depend>rosbridge_server</depend>
|
||||||
<depend>web_video_server</depend>
|
<depend>web_video_server</depend>
|
||||||
<depend>tf2_web_republisher</depend>
|
<depend>tf2_web_republisher</depend>
|
||||||
<depend condition="$ROS_PYTHON_VERSION == 2">python-lxml</depend>
|
<depend>python-lxml</depend>
|
||||||
<depend condition="$ROS_PYTHON_VERSION == 3">python3-lxml</depend>
|
<exec_depend>python-pymavlink</exec_depend>
|
||||||
<exec_depend condition="$ROS_PYTHON_VERSION == 2">python-pymavlink</exec_depend>
|
|
||||||
<exec_depend condition="$ROS_PYTHON_VERSION == 3">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,5 @@
|
|||||||
flask==1.1.1
|
flask==1.1.1
|
||||||
docopt==0.6.2
|
docopt==0.6.2
|
||||||
geopy==1.20.0
|
geopy==1.11.0
|
||||||
smbus2==0.3.0
|
smbus2==0.2.1
|
||||||
VL53L1X==0.0.4
|
VL53L1X==0.0.2
|
||||||
|
|||||||
@@ -34,9 +34,7 @@ class OpticalFlow : public nodelet::Nodelet
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
OpticalFlow():
|
OpticalFlow():
|
||||||
camera_matrix_(3, 3, CV_64F),
|
camera_matrix_(3, 3, CV_64F)
|
||||||
dist_coeffs_(8, 1, CV_64F),
|
|
||||||
tf_listener_(tf_buffer_)
|
|
||||||
{}
|
{}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -52,8 +50,8 @@ private:
|
|||||||
Mat hann_;
|
Mat hann_;
|
||||||
Mat prev_, curr_;
|
Mat prev_, curr_;
|
||||||
Mat camera_matrix_, dist_coeffs_;
|
Mat camera_matrix_, dist_coeffs_;
|
||||||
tf2_ros::Buffer tf_buffer_;
|
std::unique_ptr<tf2_ros::Buffer> tf_buffer_;
|
||||||
tf2_ros::TransformListener tf_listener_;
|
std::unique_ptr<tf2_ros::TransformListener> tf_listener_;
|
||||||
bool calc_flow_gyro_;
|
bool calc_flow_gyro_;
|
||||||
|
|
||||||
void onInit()
|
void onInit()
|
||||||
@@ -63,11 +61,14 @@ private:
|
|||||||
image_transport::ImageTransport it(nh);
|
image_transport::ImageTransport it(nh);
|
||||||
image_transport::ImageTransport it_priv(nh_priv);
|
image_transport::ImageTransport it_priv(nh_priv);
|
||||||
|
|
||||||
nh.param<std::string>("mavros/local_position/tf/frame_id", local_frame_id_, "map");
|
tf_buffer_.reset(new tf2_ros::Buffer());
|
||||||
nh.param<std::string>("mavros/local_position/tf/child_frame_id", fcu_frame_id_, "base_link");
|
tf_listener_.reset(new tf2_ros::TransformListener(*tf_buffer_, nh));
|
||||||
nh_priv.param("roi", roi_px_, 128);
|
|
||||||
nh_priv.param("roi_rad", roi_rad_, 0.0);
|
local_frame_id_ = nh.param<std::string>("mavros/local_position/tf/frame_id", "map");
|
||||||
nh_priv.param("calc_flow_gyro", calc_flow_gyro_, false);
|
fcu_frame_id_ = nh.param<std::string>("mavros/local_position/tf/child_frame_id", "base_link");
|
||||||
|
roi_px_ = nh_priv.param("roi", 128);
|
||||||
|
roi_rad_ = nh_priv.param("roi_rad", 0.0);
|
||||||
|
calc_flow_gyro_ = nh_priv.param("calc_flow_gyro", false);
|
||||||
|
|
||||||
img_sub_ = it.subscribeCamera("image_raw", 1, &OpticalFlow::flow, this);
|
img_sub_ = it.subscribeCamera("image_raw", 1, &OpticalFlow::flow, this);
|
||||||
img_pub_ = it_priv.advertise("debug", 1);
|
img_pub_ = it_priv.advertise("debug", 1);
|
||||||
@@ -91,9 +92,7 @@ private:
|
|||||||
camera_matrix_.at<double>(i, j) = cinfo->K[3 * i + j];
|
camera_matrix_.at<double>(i, j) = cinfo->K[3 * i + j];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (int k = 0; k < cinfo->D.size(); k++) {
|
dist_coeffs_ = cv::Mat(cinfo->D, true);
|
||||||
dist_coeffs_.at<double>(k) = cinfo->D[k];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void drawFlow(Mat& frame, double x, double y, double quality) const
|
void drawFlow(Mat& frame, double x, double y, double quality) const
|
||||||
@@ -186,7 +185,7 @@ private:
|
|||||||
flow_camera.vector.x = flow_y; // +y means counter-clockwise rotation around Y axis
|
flow_camera.vector.x = flow_y; // +y means counter-clockwise rotation around Y axis
|
||||||
flow_camera.vector.y = -flow_x; // +x means clockwise rotation around X axis
|
flow_camera.vector.y = -flow_x; // +x means clockwise rotation around X axis
|
||||||
try {
|
try {
|
||||||
tf_buffer_.transform(flow_camera, flow_fcu, fcu_frame_id_);
|
tf_buffer_->transform(flow_camera, flow_fcu, fcu_frame_id_);
|
||||||
} catch (const tf2::TransformException& e) {
|
} catch (const tf2::TransformException& e) {
|
||||||
// transform is not available yet
|
// transform is not available yet
|
||||||
return;
|
return;
|
||||||
@@ -200,7 +199,7 @@ private:
|
|||||||
try {
|
try {
|
||||||
auto flow_gyro_camera = calcFlowGyro(msg->header.frame_id, prev_stamp_, msg->header.stamp);
|
auto flow_gyro_camera = calcFlowGyro(msg->header.frame_id, prev_stamp_, msg->header.stamp);
|
||||||
static geometry_msgs::Vector3Stamped flow_gyro_fcu;
|
static geometry_msgs::Vector3Stamped flow_gyro_fcu;
|
||||||
tf_buffer_.transform(flow_gyro_camera, flow_gyro_fcu, fcu_frame_id_);
|
tf_buffer_->transform(flow_gyro_camera, flow_gyro_fcu, fcu_frame_id_);
|
||||||
flow_.integrated_xgyro = flow_gyro_fcu.vector.x;
|
flow_.integrated_xgyro = flow_gyro_fcu.vector.x;
|
||||||
flow_.integrated_ygyro = flow_gyro_fcu.vector.y;
|
flow_.integrated_ygyro = flow_gyro_fcu.vector.y;
|
||||||
flow_.integrated_zgyro = flow_gyro_fcu.vector.z;
|
flow_.integrated_zgyro = flow_gyro_fcu.vector.z;
|
||||||
@@ -247,8 +246,8 @@ private:
|
|||||||
geometry_msgs::Vector3Stamped calcFlowGyro(const std::string& frame_id, const ros::Time& prev, const ros::Time& curr)
|
geometry_msgs::Vector3Stamped calcFlowGyro(const std::string& frame_id, const ros::Time& prev, const ros::Time& curr)
|
||||||
{
|
{
|
||||||
tf2::Quaternion prev_rot, curr_rot;
|
tf2::Quaternion prev_rot, curr_rot;
|
||||||
tf2::fromMsg(tf_buffer_.lookupTransform(frame_id, local_frame_id_, prev).transform.rotation, prev_rot);
|
tf2::fromMsg(tf_buffer_->lookupTransform(frame_id, local_frame_id_, prev).transform.rotation, prev_rot);
|
||||||
tf2::fromMsg(tf_buffer_.lookupTransform(frame_id, local_frame_id_, curr, ros::Duration(0.1)).transform.rotation, curr_rot);
|
tf2::fromMsg(tf_buffer_->lookupTransform(frame_id, local_frame_id_, curr, ros::Duration(0.1)).transform.rotation, curr_rot);
|
||||||
|
|
||||||
geometry_msgs::Vector3Stamped flow;
|
geometry_msgs::Vector3Stamped flow;
|
||||||
flow.header.frame_id = frame_id;
|
flow.header.frame_id = frame_id;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python
|
||||||
# coding=utf-8
|
# coding=utf-8
|
||||||
|
|
||||||
# Copyright (C) 2018 Copter Express Technologies
|
# Copyright (C) 2018 Copter Express Technologies
|
||||||
@@ -138,7 +138,7 @@ def mavlink_exec(cmd, timeout=3.0):
|
|||||||
timeout=3,
|
timeout=3,
|
||||||
baudrate=0,
|
baudrate=0,
|
||||||
count=len(cmd),
|
count=len(cmd),
|
||||||
data=[ord(c) for c in cmd.ljust(70, '\0')])
|
data=map(ord, cmd.ljust(70, '\0')))
|
||||||
msg.pack(link)
|
msg.pack(link)
|
||||||
ros_msg = mavlink.convert_to_rosmsg(msg)
|
ros_msg = mavlink.convert_to_rosmsg(msg)
|
||||||
mavlink_pub.publish(ros_msg)
|
mavlink_pub.publish(ros_msg)
|
||||||
@@ -210,7 +210,7 @@ def check_fcu():
|
|||||||
is_clover_firmware = True
|
is_clover_firmware = True
|
||||||
|
|
||||||
if not is_clover_firmware:
|
if not is_clover_firmware:
|
||||||
failure('not running Clover PX4 firmware, https://clever.coex.tech/firmware')
|
failure('not running Clover PX4 firmware, https://clover.coex.tech/firmware')
|
||||||
|
|
||||||
est = get_param('SYS_MC_EST_GROUP')
|
est = get_param('SYS_MC_EST_GROUP')
|
||||||
if est == 1:
|
if est == 1:
|
||||||
@@ -250,11 +250,11 @@ def check_fcu():
|
|||||||
try:
|
try:
|
||||||
battery = rospy.wait_for_message('mavros/battery', BatteryState, timeout=3)
|
battery = rospy.wait_for_message('mavros/battery', BatteryState, timeout=3)
|
||||||
if not battery.cell_voltage:
|
if not battery.cell_voltage:
|
||||||
failure('cell voltage is not available, https://clever.coex.tech/power')
|
failure('cell voltage is not available, https://clover.coex.tech/power')
|
||||||
else:
|
else:
|
||||||
cell = battery.cell_voltage[0]
|
cell = battery.cell_voltage[0]
|
||||||
if cell > 4.3 or cell < 3.0:
|
if cell > 4.3 or cell < 3.0:
|
||||||
failure('incorrect cell voltage: %.2f V, https://clever.coex.tech/power', cell)
|
failure('incorrect cell voltage: %.2f V, https://clover.coex.tech/power', cell)
|
||||||
elif cell < 3.7:
|
elif cell < 3.7:
|
||||||
failure('critically low cell voltage: %.2f V, recharge battery', cell)
|
failure('critically low cell voltage: %.2f V, recharge battery', cell)
|
||||||
except rospy.ROSException:
|
except rospy.ROSException:
|
||||||
@@ -609,7 +609,7 @@ def check_rangefinder():
|
|||||||
|
|
||||||
@check('Boot duration')
|
@check('Boot duration')
|
||||||
def check_boot_duration():
|
def check_boot_duration():
|
||||||
output = subprocess.check_output('systemd-analyze').decode()
|
output = subprocess.check_output('systemd-analyze')
|
||||||
r = re.compile(r'([\d\.]+)s\s*$', flags=re.MULTILINE)
|
r = re.compile(r'([\d\.]+)s\s*$', flags=re.MULTILINE)
|
||||||
duration = float(r.search(output).groups()[0])
|
duration = float(r.search(output).groups()[0])
|
||||||
if duration > 15:
|
if duration > 15:
|
||||||
@@ -620,7 +620,7 @@ def check_boot_duration():
|
|||||||
def check_cpu_usage():
|
def check_cpu_usage():
|
||||||
WHITELIST = 'nodelet',
|
WHITELIST = 'nodelet',
|
||||||
CMD = "top -n 1 -b -i | tail -n +8 | awk '{ printf(\"%-8s\\t%-8s\\t%-8s\\n\", $1, $9, $12); }'"
|
CMD = "top -n 1 -b -i | tail -n +8 | awk '{ printf(\"%-8s\\t%-8s\\t%-8s\\n\", $1, $9, $12); }'"
|
||||||
output = subprocess.check_output(CMD, shell=True).decode()
|
output = subprocess.check_output(CMD, shell=True)
|
||||||
processes = output.split('\n')
|
processes = output.split('\n')
|
||||||
for process in processes:
|
for process in processes:
|
||||||
if not process:
|
if not process:
|
||||||
@@ -636,7 +636,7 @@ def check_cpu_usage():
|
|||||||
def check_clover_service():
|
def check_clover_service():
|
||||||
try:
|
try:
|
||||||
output = subprocess.check_output('systemctl show -p ActiveState --value clover.service'.split(),
|
output = subprocess.check_output('systemctl show -p ActiveState --value clover.service'.split(),
|
||||||
stderr=subprocess.STDOUT).decode()
|
stderr=subprocess.STDOUT)
|
||||||
except subprocess.CalledProcessError as e:
|
except subprocess.CalledProcessError as e:
|
||||||
failure('systemctl returned %s: %s', e.returncode, e.output)
|
failure('systemctl returned %s: %s', e.returncode, e.output)
|
||||||
return
|
return
|
||||||
@@ -701,7 +701,7 @@ def check_preflight_status():
|
|||||||
|
|
||||||
@check('Network')
|
@check('Network')
|
||||||
def check_network():
|
def check_network():
|
||||||
ros_hostname = os.environ.get('ROS_HOSTNAME').strip()
|
ros_hostname = os.environ.get('ROS_HOSTNAME', '').strip()
|
||||||
|
|
||||||
if not ros_hostname:
|
if not ros_hostname:
|
||||||
failure('no ROS_HOSTNAME is set')
|
failure('no ROS_HOSTNAME is set')
|
||||||
@@ -718,7 +718,7 @@ def check_network():
|
|||||||
if ros_hostname in parts:
|
if ros_hostname in parts:
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
failure('not found %s in /etc/hosts, ROS will malfunction if network interfaces are down, https://clever.coex.tech/hostname', ros_hostname)
|
failure('not found %s in /etc/hosts, ROS will malfunction if network interfaces are down, https://clover.coex.tech/hostname', ros_hostname)
|
||||||
|
|
||||||
|
|
||||||
@check('RPi health')
|
@check('RPi health')
|
||||||
@@ -751,7 +751,7 @@ def check_rpi_health():
|
|||||||
# <parameter>=<value>
|
# <parameter>=<value>
|
||||||
# In case of `get_throttled`, <value> is a hexadecimal number
|
# In case of `get_throttled`, <value> is a hexadecimal number
|
||||||
# with some of the FLAGs OR'ed together
|
# with some of the FLAGs OR'ed together
|
||||||
output = subprocess.check_output(['vcgencmd', 'get_throttled']).decode()
|
output = subprocess.check_output(['vcgencmd', 'get_throttled'])
|
||||||
except OSError:
|
except OSError:
|
||||||
failure('could not call vcgencmd binary; not a Raspberry Pi?')
|
failure('could not call vcgencmd binary; not a Raspberry Pi?')
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -490,7 +490,7 @@ inline void checkState()
|
|||||||
throw std::runtime_error("State timeout, check mavros settings");
|
throw std::runtime_error("State timeout, check mavros settings");
|
||||||
|
|
||||||
if (!state.connected)
|
if (!state.connected)
|
||||||
throw std::runtime_error("No connection to FCU, https://clever.coex.tech/connection");
|
throw std::runtime_error("No connection to FCU, https://clover.coex.tech/connection");
|
||||||
}
|
}
|
||||||
|
|
||||||
#define ENSURE_FINITE(var) { if (!std::isfinite(var)) throw std::runtime_error(#var " argument cannot be NaN or Inf"); }
|
#define ENSURE_FINITE(var) { if (!std::isfinite(var)) throw std::runtime_error(#var " argument cannot be NaN or Inf"); }
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
import rospy
|
import rospy
|
||||||
import pytest
|
import pytest
|
||||||
from mavros_msgs.msg import State
|
from mavros_msgs.msg import State
|
||||||
|
|||||||
0
clover/www/CATKIN_IGNORE
Normal file
@@ -1,7 +1,7 @@
|
|||||||
<h1>Clover Drone Kit Tools</h1>
|
<h1>Clover Drone Kit Tools</h1>
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
<li><a href="docs">View documentation</a> (snapshot of <a href="https://clever.coex.tech">clever.coex.tech</a>)</li>
|
<li><a href="docs">View documentation</a> (snapshot of <a href="https://clover.coex.tech">clover.coex.tech</a>)</li>
|
||||||
<li><a href="" id="wvs">View image topics</a> (<code>web_video_server</code>)</li>
|
<li><a href="" id="wvs">View image topics</a> (<code>web_video_server</code>)</li>
|
||||||
<li><a href="" id="butterfly">Open web terminal</a> (<code>Butterfly</code>)</li>
|
<li><a href="" id="butterfly">Open web terminal</a> (<code>Butterfly</code>)</li>
|
||||||
<li><a href="viz.html">View 3D visualization</a> (<code>ros3djs</code>)</li>
|
<li><a href="viz.html">View 3D visualization</a> (<code>ros3djs</code>)</li>
|
||||||
@@ -12,8 +12,8 @@
|
|||||||
|
|
||||||
<script src="js/roslib.js"></script>
|
<script src="js/roslib.js"></script>
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
document.querySelector("#wvs").href = location.origin + ':8080';
|
document.querySelector("#wvs").href = location.protocol + '//' + location.hostname + ':8080';
|
||||||
document.querySelector("#butterfly").href = location.origin + ':57575';
|
document.querySelector("#butterfly").href = location.protocol + '//' + location.hostname + ':57575';
|
||||||
|
|
||||||
// Determine image version
|
// Determine image version
|
||||||
var ros = new ROSLIB.Ros({ url: 'ws://' + location.hostname + ':9090' });
|
var ros = new ROSLIB.Ros({ url: 'ws://' + location.hostname + ':9090' });
|
||||||
|
|||||||
0
docs/CATKIN_IGNORE
Normal file
BIN
docs/assets/5_D1_2.jpg
Normal file
|
After Width: | Height: | Size: 65 KiB |
BIN
docs/assets/assembling_clever4_2/coex_pix.png
Normal file
|
After Width: | Height: | Size: 463 KiB |
BIN
docs/assets/assembling_clever4_2/esc_1.png
Normal file
|
After Width: | Height: | Size: 433 KiB |
BIN
docs/assets/assembling_clever4_2/esc_2.png
Normal file
|
After Width: | Height: | Size: 591 KiB |
BIN
docs/assets/assembling_clever4_2/fc_connection_1.png
Normal file
|
After Width: | Height: | Size: 350 KiB |
BIN
docs/assets/assembling_clever4_2/fc_connection_2.png
Normal file
|
After Width: | Height: | Size: 435 KiB |
BIN
docs/assets/assembling_clever4_2/fc_connection_3.png
Normal file
|
After Width: | Height: | Size: 468 KiB |
BIN
docs/assets/assembling_clever4_2/fc_connection_4.png
Normal file
|
After Width: | Height: | Size: 476 KiB |
BIN
docs/assets/assembling_clever4_2/fc_connection_5.png
Normal file
|
After Width: | Height: | Size: 464 KiB |
BIN
docs/assets/assembling_clever4_2/fc_connection_6.png
Normal file
|
After Width: | Height: | Size: 472 KiB |
BIN
docs/assets/assembling_clever4_2/final_1.png
Normal file
|
After Width: | Height: | Size: 426 KiB |
BIN
docs/assets/assembling_clever4_2/final_2.png
Normal file
|
After Width: | Height: | Size: 777 KiB |
BIN
docs/assets/assembling_clever4_2/final_3.png
Normal file
|
After Width: | Height: | Size: 455 KiB |
BIN
docs/assets/assembling_clever4_2/final_4.png
Normal file
|
After Width: | Height: | Size: 446 KiB |
BIN
docs/assets/assembling_clever4_2/frame_1.png
Normal file
|
After Width: | Height: | Size: 304 KiB |
BIN
docs/assets/assembling_clever4_2/frame_2.png
Normal file
|
After Width: | Height: | Size: 320 KiB |
BIN
docs/assets/assembling_clever4_2/frame_3.png
Normal file
|
After Width: | Height: | Size: 307 KiB |
BIN
docs/assets/assembling_clever4_2/frame_4.png
Normal file
|
After Width: | Height: | Size: 152 KiB |
BIN
docs/assets/assembling_clever4_2/frame_5.png
Normal file
|
After Width: | Height: | Size: 348 KiB |
BIN
docs/assets/assembling_clever4_2/frame_6.png
Normal file
|
After Width: | Height: | Size: 342 KiB |
BIN
docs/assets/assembling_clever4_2/frame_7.png
Normal file
|
After Width: | Height: | Size: 346 KiB |
BIN
docs/assets/assembling_clever4_2/guard_1.png
Normal file
|
After Width: | Height: | Size: 175 KiB |
BIN
docs/assets/assembling_clever4_2/guard_2.png
Normal file
|
After Width: | Height: | Size: 224 KiB |
BIN
docs/assets/assembling_clever4_2/guard_3.png
Normal file
|
After Width: | Height: | Size: 558 KiB |
BIN
docs/assets/assembling_clever4_2/guard_4.png
Normal file
|
After Width: | Height: | Size: 426 KiB |
BIN
docs/assets/assembling_clever4_2/led_1.png
Normal file
|
After Width: | Height: | Size: 95 KiB |
BIN
docs/assets/assembling_clever4_2/led_2.png
Normal file
|
After Width: | Height: | Size: 155 KiB |
BIN
docs/assets/assembling_clever4_2/led_3.png
Normal file
|
After Width: | Height: | Size: 600 KiB |
BIN
docs/assets/assembling_clever4_2/led_4.png
Normal file
|
After Width: | Height: | Size: 514 KiB |
BIN
docs/assets/assembling_clever4_2/led_5.png
Normal file
|
After Width: | Height: | Size: 651 KiB |
BIN
docs/assets/assembling_clever4_2/led_6.png
Normal file
|
After Width: | Height: | Size: 764 KiB |
BIN
docs/assets/assembling_clever4_2/led_7.png
Normal file
|
After Width: | Height: | Size: 612 KiB |
BIN
docs/assets/assembling_clever4_2/motor_1.png
Normal file
|
After Width: | Height: | Size: 290 KiB |
BIN
docs/assets/assembling_clever4_2/motor_2.png
Normal file
|
After Width: | Height: | Size: 263 KiB |
BIN
docs/assets/assembling_clever4_2/pdb_1.png
Normal file
|
After Width: | Height: | Size: 466 KiB |
BIN
docs/assets/assembling_clever4_2/pdb_2.png
Normal file
|
After Width: | Height: | Size: 433 KiB |
BIN
docs/assets/assembling_clever4_2/pixracer_1.png
Normal file
|
After Width: | Height: | Size: 460 KiB |
BIN
docs/assets/assembling_clever4_2/pixracer_2.png
Normal file
|
After Width: | Height: | Size: 462 KiB |
BIN
docs/assets/assembling_clever4_2/radio.png
Normal file
|
After Width: | Height: | Size: 632 KiB |
BIN
docs/assets/assembling_clever4_2/raspberry_1.png
Normal file
|
After Width: | Height: | Size: 129 KiB |
BIN
docs/assets/assembling_clever4_2/raspberry_10.png
Normal file
|
After Width: | Height: | Size: 498 KiB |
BIN
docs/assets/assembling_clever4_2/raspberry_11.png
Normal file
|
After Width: | Height: | Size: 514 KiB |
BIN
docs/assets/assembling_clever4_2/raspberry_2.png
Normal file
|
After Width: | Height: | Size: 387 KiB |
BIN
docs/assets/assembling_clever4_2/raspberry_3.png
Normal file
|
After Width: | Height: | Size: 391 KiB |
BIN
docs/assets/assembling_clever4_2/raspberry_5.png
Normal file
|
After Width: | Height: | Size: 82 KiB |
BIN
docs/assets/assembling_clever4_2/raspberry_6.png
Normal file
|
After Width: | Height: | Size: 102 KiB |
BIN
docs/assets/assembling_clever4_2/raspberry_7.png
Normal file
|
After Width: | Height: | Size: 47 KiB |