Compare commits

..

104 Commits

Author SHA1 Message Date
Oleg Kalachev
c254691f80 Adjust aruco.launch arguments 2020-08-05 02:06:41 +03:00
Oleg Kalachev
4fe6f24d16 Edit readme 2020-07-21 10:09:09 +03:00
Oleg Kalachev
cda7e62dbf Adjust launch file arguments for more convenient configuration 2020-07-21 10:03:40 +03:00
Oleg Kalachev
fa9a1794f6 Add roslaunch_editor 2020-07-21 10:00:33 +03:00
Alexey Rogachevskiy
ebec850010 docs: add contributed models for Jetson Nano (#250)
* docs: Add contributed models for Jetson Nano

* Add Vyacheslav Buzov’s contact link

Co-authored-by: Oleg Kalachev <okalachev@gmail.com>
2020-07-18 17:37:02 +03:00
Vasily Yuryev
5a8db188f6 docs: add English version of Innopolis Open 2020 L22_AERO team article (#252) 2020-07-17 21:41:07 +03:00
Vasily Yuryev
4376bbb723 docs: add Innopolis Open 2020 L22_AERO team article (#251)
* L22_AERO docs

* remove html code

* docs: edit Innopolis Open 2020 (L22_AERO) article

Co-authored-by: Oleg Kalachev <okalachev@gmail.com>
2020-07-17 19:25:01 +03:00
alamoris
1d694e1a15 docs: Delete m3x6 image 2020-07-16 16:31:26 +03:00
alamoris
54de7fca3a docs: Fix clover 4.2 equipment list 2020-07-16 16:21:51 +03:00
alamoris
32f5584082 docs: Fix text formatting 2020-07-14 21:57:51 +03:00
alamoris
d7c0fb33ff docs: Add hint about FC rotation 2020-07-14 19:27:38 +03:00
alamoris
d16e7bf155 docs: delete not used images 2020-07-14 15:02:06 +03:00
alamoris
26bd1e2d8f docs: Rework type size table 2020-07-13 22:05:31 +03:00
alamoris
e12577cf0e docs: Fix md typo 2020-07-13 13:49:59 +03:00
alamoris
12544a69af docs: add notification about ir sensors article 2020-07-10 20:45:16 +03:00
alamoris
f471280bef docs: Hidden articles about working with ir sensors 2020-07-10 17:33:08 +03:00
alamoris
2ff9887ac4 docs: fix broken image link 2020-07-09 22:24:49 +03:00
alamoris
2ecfb28a5d docs: Rename assembly clover 4 article 2020-07-09 20:43:51 +03:00
Alamoris
091483226f Update assembling clover 4 (#243)
* docs: Rewrite article  article about assembling clever 4

* docs: Update

* docs: Update clever 4 assemble

* docs: remove old assembly instructions images

* docs: Renamed and returned  assembly instruction about clover 4.0, some fixes

* docs: Add english version of the article, some fixes

* docs: resize assemble images

* docs: fix assets check errors

* docs: fix image size error
2020-07-09 19:39:45 +03:00
Oleg Kalachev
af16414c77 docs: fix links to PDF 2020-06-30 23:01:20 +03:00
Oleg Kalachev
696214e717 docs: add link to PDF docs 2020-06-30 22:44:55 +03:00
Alexey Rogachevskiy
15fd156ce0 travis: Install mscorefonts 2020-06-30 22:31:51 +03:00
Alexey Rogachevskiy
82bfb961a4 travis: Fix travis formatting 2020-06-30 22:04:59 +03:00
Alexey Rogachevskiy
c50637c60f travis: Generate pdf in _book 2020-06-30 22:04:59 +03:00
Alexey Rogachevskiy
a26f0f41e7 docs: Fix capitalization 2020-06-30 22:04:59 +03:00
Alexey Rogachevskiy
28690491c2 travis: Build gitbook pdf 2020-06-30 22:04:59 +03:00
Oleg Kalachev
ca1323faf3 Merge pull request #244 from goldarte/genmap-update
genmap.py: Add x0 and y0 shift for markers coordinates
2020-06-25 13:09:06 +03:00
Arthur Golubtsov
b713b49f3f genmap.py: Add x0 and y0 shift for markers coordinates 2020-06-25 00:31:56 +03:00
Alexey Rogachevskiy
62a77519e6 docs: Remove notice about compressed topics
Not relevant since web_video_server 0.2.1+.
2020-06-15 18:07:50 +03:00
Arthur Golubtsov
e1bf8aade5 docs: assemble Clover 4.1 version (#232)
* docs: Add russian instruction for building intermediate frame for Clover 4

* Update SUMMARY.md

* docs: Fix typos

* docs: Rename intermediate to 4.1

* Fix links, remove unnecessary asset

* Fix header

* Move article header down

* Small fixes

* Fix link to clover 4.0 frame
2020-06-15 17:00:20 +03:00
Oleg Kalachev
0172d6e892 docs: simplify leds examples 2020-06-13 02:11:45 +03:00
Oleg Kalachev
bcb7351a90 roswww_static: infrastructure for web-based Clover plugins (#230)
* Package for generating static web sites for ROS

* rosstatic: add CMakeLists.txt

* rosstatic: utilize rospkg, store static directory in ROS_HOME

* rosstatic: default_package param

* rosstatic: fix URLs in docs

* clover.launch: make clover the default package for www

* Unused import

* Rename rosstatic to roswww_static

* Fixes
2020-06-13 02:08:00 +03:00
Oleg Kalachev
c71a46ce9d Put CATKIN_IGNORE file to some directories 2020-06-11 16:13:07 +03:00
Oleg Kalachev
91dd7799ef image: remove unneeded ROS_DISTRO settings from .bashrc 2020-06-10 23:25:57 +03:00
Oleg Kalachev
3682e253a7 selfcheck.py: don’t fall when ROS_HOSTNAME is not set 2020-06-10 22:40:49 +03:00
Alexey Rogachevskiy
66ecbb4d09 docs: Update ROS repository keys (en/ru) 2020-06-10 14:51:13 +03:00
Alexey Rogachevskiy
f041b6125b Add Avahi services broadcasting (#231)
* Builder: Add Avahi services broadcasting

* avahi-services: Remove http.service

* builder: Expose sftp-ssh instead of just ssh, fix build
2020-06-03 22:35:48 +03:00
Oleg Kalachev
a4f2bab3d7 docs: fix incorrect order of focused and unfocused camera image 2020-06-03 08:15:35 +03:00
Oleg Kalachev
56bcfa5c87 Enable back publishing documentation 2020-06-02 11:54:08 +03:00
Alexey Rogachevskiy
998796045c aruco_pose nodelet cleanup (#239)
* aruco_pose: Unhardcode contour refinement

Besides, this was basically a no-op anyway, since dynamic parameters
overwrote that anyway.

* aruco_pose: Late-construct objects that use ROS

* aruco_map: Don't create/store node handle

* aruco_pose: Don't assume dist_coeffs size

* aruco_pose: more const == more better

* aruco_pose: Be more obvious about changing variables

* aruco_pose: Fix building for Kinetic

* aruco_pose: Remove global add_definitions
2020-05-30 01:59:51 +03:00
Alexey Rogachevskiy
c5e954b56a optical_flow: Use functional-style parameter fetching 2020-05-30 01:58:14 +03:00
Alexey Rogachevskiy
2814fea9cd optical_flow: Pass nodelet callback queue to TransformListener 2020-05-30 01:58:14 +03:00
Alexey Rogachevskiy
b85326c02a optical_flow: Use cv::Mat(std::vector, bool) ctor for dist_coeffs_ 2020-05-30 01:58:14 +03:00
Alexey Rogachevskiy
98d5d50607 aruco_pose: Prevent OpenCV from crashing (#238)
* aruco_pose: Add tests that crash OpenCV

* aruco_pose: Don't try to interpolate single points
2020-05-30 01:57:14 +03:00
Alexey Rogachevskiy
69c46786de builder: Set apt retries to 3
This should lower the number of builds that failed due to
repositories being unstable
2020-05-29 21:25:58 +03:00
Oleg Kalachev
abb495275b docs: translate robocross-2019 article 2020-05-26 06:54:42 +03:00
Oleg Kalachev
044d6c6d33 docs: switch lpe and ekf2 settings in aruco map navigation articles 2020-05-21 21:01:16 +03:00
Alexey Rogachevskiy
22d5a356b6 clover: Update ros3djs, THREE.js 2020-05-18 16:25:56 +03:00
Alexey Rogachevskiy
c7828557ca standalone_install: Fail on error 2020-05-16 15:49:38 +03:00
Alexey Rogachevskiy
514c0f1b65 Flysky FS-A8S article (#229)
* docs: Add FS-A8S article draft

* docs: Fix image links

* docs/flysky_a8s: Make images appear smaller

* docs: Add animated images

* docs/flysky_a8s: Proofreading

* docs/flysky_a8s: Sync up header to summary entry

* docs/flysky_a8s: Add Flysky FS-A8S article (en)

* docs/flysky_a8s: More proofreading
2020-05-12 12:35:05 +03:00
Oleg Kalachev
6a79b8292a docs: little fix 2020-05-08 17:02:36 +03:00
Oleg Kalachev
10b6661266 docs: add images for ROS javascript article 2020-05-08 16:54:10 +03:00
Oleg Kalachev
7f2cb1c63e docs: add using ROS with javascript article 2020-05-08 16:51:15 +03:00
Oleg Kalachev
1d48c79c52 docs: rename package and service to clover 2020-05-07 19:43:25 +03:00
Oleg Kalachev
ad46a0918c Temporarily disable documentation upload 2020-05-07 19:03:51 +03:00
Alexey Rogachevskiy
9487522992 clover: Use saner min marker perimeter rate 2020-05-07 18:07:11 +03:00
Oleg Kalachev
80b35d3b90 Change camera calibration name to main_camera_optical 2020-05-06 19:49:45 +03:00
VeneraDal
12e292c9d7 docs: add Russian and English trainer mode article (#219)
* Upload trainer_mode.md

Add an English version of the file

* Update trainer mode article

* Update SUMMARY.md

For adding the trainer mode article

* Update SUMMARY.md

For adding trainer mode article

* docs: edit trainer mode articles

* docs: move trainer mode article in summary

* docs: fix

* Update SUMMARY.md

Co-authored-by: Oleg Kalachev <okalachev@gmail.com>
2020-05-06 19:41:17 +03:00
Alexey Rogachevskiy
9b28e9cad2 travis: Resolve some issues with validation 2020-05-06 16:39:24 +03:00
Alexey Rogachevskiy
d57ab82f38 docs: Fix up according to MD037 2020-05-06 15:03:39 +03:00
Oleg Kalachev
c8da639eab Merge pull request #227 from goldarte/target-system-id
Add fcu_sys_id argument to clover.launch
2020-05-06 13:30:46 +03:00
Alexey Rogachevskiy
c7e7edec70 builder: Enable ROS services after first boot (#208)
Merging this into master, this should not break anything.
2020-05-06 13:24:53 +03:00
Oleg Kalachev
72869fcf2b Merge pull request #216 from CopterExpress/new-camera-calib
Average camera calibration
2020-05-01 01:04:04 +03:00
Oleg Kalachev
387d2c2341 Update documentation links 2020-05-01 00:39:38 +03:00
Oleg Kalachev
a665caeea3 docs: small fixes 2020-04-30 20:04:20 +03:00
Oleg Kalachev
3079d2a3e1 docs: typos 2020-04-30 20:02:31 +03:00
Alexey Rogachevskiy
7d5bdf4f22 docs/migrate20: Remove extra space character 2020-04-30 19:35:51 +03:00
Oleg Kalachev
2a3efa2908 docs: reflect camera frame configuration changes 2020-04-30 05:03:29 +03:00
Oleg Kalachev
ddee29a0e8 docs: English version on 0.20 image transition article + add to summary 2020-04-30 04:19:09 +03:00
Oleg Kalachev
8596be07c6 docs: add article on migration to v0.20 2020-04-30 04:02:52 +03:00
Oleg Kalachev
a480ebe80a Continue renaming to Clover 2020-04-30 03:41:14 +03:00
Oleg Kalachev
77ca50b901 docs: change rpi version on main page 2020-04-30 01:39:56 +03:00
Oleg Kalachev
ead9b904fa docs: small fix 2020-04-29 05:05:22 +03:00
Oleg Kalachev
90956ecd44 docs: English version of new camera calibration article 2020-04-29 04:44:52 +03:00
Oleg Kalachev
f070c60e14 docs: add example of wait_for_message for rangefinder 2020-04-28 05:53:45 +03:00
Arthur Golubtsov
68edf07f6e Add fcu_sys_id argument to clover.launch and mavros.launch to set up target_system_id parameter in mavros 2020-04-27 15:36:08 +03:00
Oleg Kalachev
7f161b1ad7 Fix Travis badge 2020-04-26 08:00:33 +03:00
Oleg Kalachev
a41a432ef3 Fixes 2020-04-23 21:20:29 +03:00
Oleg Kalachev
2b896b06d9 Move manual installation and running to clover/readme.md 2020-04-23 21:19:46 +03:00
Oleg Kalachev
5070cafbfb Update main readme 2020-04-23 20:54:15 +03:00
Oleg Kalachev
9c0af7285c docs: decrease video size in clever-show article 2020-04-20 18:25:06 +03:00
Arthur Golubtsov
c67d937842 docs: Add article about clever-show (#226)
* docs: Add article about clever-show

* docs: Fix mistake in sentence in en clever-show article

* docs: Add dots to clever-show article

* docs: resolve conflict

Co-authored-by: Oleg Kalachev <okalachev@gmail.com>
2020-04-20 13:30:20 +03:00
Oleg Kalachev
b79d87242f docs: add p4df2 team article in NTI olympics 2020 2020-04-18 15:42:55 +03:00
Alamoris
4d0ddcb319 docs: fix .html link typo 2020-04-18 15:12:09 +03:00
Oleg Kalachev
3ff4ee6c4c docs: add instructions on easy way of sending documentation updates 2020-04-18 14:36:15 +03:00
Oleg Kalachev
90049182cf image: add navigate_wait example 2020-04-09 15:44:48 +03:00
Oleg Kalachev
33f4601fdc docs: add example on retrieving one camera frame 2020-04-09 15:32:45 +03:00
Arthur Golubtsov
2a62891d60 Install pyzbar to image (#225)
* Install pyzbar to image

I suggest installing pyzbar to RPi image for making barcodes scanning easier during different competitions.

* pyzbar: Add libzbar0 install

* pyzbar: Add simple tests

* pyzbar: Update docs
2020-04-09 04:16:05 +03:00
Alamoris
b043737e91 docs: An article on how to configure an image for flying on wall markers (#221)
* docs: Add a draft of an article about flying using wall markers

* docs: Add paragraph about setting up launch files

* docs: Fix typos and add some links

* Small logic fixes

* docs: fix

* docs: Add description setting for earlier version

* docs: Fix sed string

* docs: Add article about wall aruco in summary
2020-04-08 15:13:32 +03:00
Oleg Kalachev
c61a0485ff docs: fix 2020-04-07 17:35:07 +03:00
Arthur Golubtsov
f1539177eb docs: Update install instruction for qr code scan 2020-04-07 17:25:11 +03:00
Arthur Golubtsov
6cbbb5580e docs (en): Update qr code scan instruction and script 2020-04-07 17:06:05 +03:00
Arthur Golubtsov
c2d22ae12a docs: fix codestyle in camera.md 2020-04-07 16:58:20 +03:00
Arthur Golubtsov
7160d804cd docs (ru): Update qr code scan instruction and script 2020-04-07 16:47:03 +03:00
Oleg Kalachev
3ac51baf7c docs: small fix
It’s better not to nest the throttled image topic as this way consumers would subscribe to appropriate camera_info topic automatically
2020-04-07 02:58:08 +03:00
Alamoris
48cc82001d docs: Fix typo 2020-04-03 21:15:04 +03:00
Alamoris
43eae885c6 docs: Add small mounting deck model 2020-04-03 21:13:14 +03:00
Alexey Rogachevskiy
2bb29ff389 clover: Add required OpenCV libraries 2020-03-31 23:56:04 +03:00
Oleg Kalachev
3811cbff3e examples: fix markers example link 2020-03-19 13:10:05 +03:00
Oleg Kalachev
2d49f58fb8 image: add markers flight example 2020-03-19 13:09:31 +03:00
Oleg Kalachev
bbcf75b806 docs: add gyro calibration snippet 2020-03-18 21:52:19 +03:00
Oleg Kalachev
3e79c25147 Camera info resolution matching camera resolution is not necessary with auto rescaling 2020-02-20 19:16:23 +03:00
Oleg Kalachev
2f69ad3f43 Keep only one calibration file 2020-02-13 23:17:52 +03:00
Oleg Kalachev
b08ad5a618 Camera calibration: set principal point strictly to the center 2020-02-13 23:14:05 +03:00
339 changed files with 78933 additions and 1273 deletions

View File

@@ -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
View File

@@ -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
[![Clover Drone Kit autonomy compilation](http://img.youtube.com/vi/u3omgsYC4Fk/hqdefault.jpg)](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 20162020](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).
[![Build Status](https://travis-ci.org/CopterExpress/clever.svg?branch=master)](https://travis-ci.org/CopterExpress/clever) [![Build Status](https://travis-ci.org/CopterExpress/clover.svg?branch=master)](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
View File

View 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()

View File

@@ -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;
} }

View File

@@ -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;
} }

View File

@@ -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

View File

@@ -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)

Binary file not shown.

After

Width:  |  Height:  |  Size: 159 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 156 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 165 KiB

View 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)

View 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>

View File

@@ -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)

View File

@@ -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>

View File

@@ -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]);
} }

View File

@@ -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"]]

View 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>

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env python3 #!/usr/bin/env python
from distutils.core import setup from distutils.core import setup

View File

@@ -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

View 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()

View File

@@ -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

View 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()

View File

@@ -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

View File

@@ -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:
# --------- # ---------

View File

@@ -1,9 +0,0 @@
python3-wxgtk:
debian:
buster: [python3-wxgtk4.0]
python3-serial:
debian:
buster: [python3-serial]
python3-requests:
debian:
buster: [python3-requests]

View File

@@ -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/'

View File

@@ -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

View File

@@ -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

View File

@@ -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"

View File

@@ -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

View File

@@ -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()

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env python3 #!/usr/bin/env python
# test backwards compatibility # test backwards compatibility

View File

@@ -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
View 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())

View File

@@ -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
View 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
```

View File

@@ -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.

View File

@@ -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.

View File

@@ -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)"/>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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')"/>

View File

@@ -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> -->

View File

@@ -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

View File

@@ -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;

View File

@@ -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

View File

@@ -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"); }

View File

@@ -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
View File

View 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
View File

BIN
docs/assets/5_D1_2.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 463 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 433 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 591 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 350 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 435 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 468 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 476 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 464 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 472 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 426 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 777 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 455 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 446 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 304 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 320 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 307 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 152 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 348 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 342 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 346 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 175 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 224 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 558 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 426 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 155 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 600 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 514 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 651 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 764 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 612 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 290 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 263 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 466 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 433 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 460 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 462 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 632 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 129 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 498 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 514 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 387 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 391 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Some files were not shown because too many files have changed in this diff Show More