Compare commits

...

105 Commits

Author SHA1 Message Date
Oleg Kalachev
355196c127 Add some simple_offboard tests for testing with SITL 2019-08-04 19:48:06 +03:00
Oleg Kalachev
0acbf61c8f docs: fix ROS_HOSTNAME value 2019-08-04 18:36:19 +03:00
Oleg Kalachev
c5bcb165cc builder: fix ROS_HOSTNAME value in .bashrc 2019-08-04 18:34:05 +03:00
Oleg Kalachev
1538ec33f7 Rework assigning hostname and service files (#150)
* Rework assigning hostname and service files

* docs: small fix in hostname article

* Correct path to setup.bash in clever.service

* docs: correct ip in hostname article

* init_rpi: put normal and .local hostname on one line in hosts

* docs: add English version of hostname article

* clever.service: use sh instead of bash

* docs: Spellcheck english version, add note about hostname vs SSID

* clever.service: return roscore requirement back
2019-08-04 00:40:27 +03:00
Oleg Kalachev
05af14a792 Add editorconfig-checker (#149)
* Add editorconfig-checker

* Editorconfig-check fix

* Remove temporal cat
2019-08-01 23:27:04 +03:00
Arthur Golubtsov
9873c24cae docs: add information about writing new article (#148)
* docs: Add information about writing new article

* docs: Add illustrations for pull request

* docs: Trying to make markdown linter happy

* docs: Trying to make markdown linter happy 2

* docs: Trying to make markdown linter happy 3

* docs: Finall corrections of article about writing new article

* docs: Finall corrections of article about writing new article 2
2019-08-01 19:00:05 +03:00
Tenessinum
8956b3459e docs: add article about "Big Challenges 2019" (#147)
* Added article about "Big Challenges 2019"

* Fixed md errors

* Changed article name

* Update bigchallenges.md

* Update bigchallenges.md

* Added markdownlint-disable

* Added videos to Aerotaxi article
2019-08-01 17:39:02 +03:00
sfalexrog
a236574642 docs: Add links to eLinux 2019-08-01 15:43:02 +03:00
sfalexrog
45f637e9a8 docs: GPIO proofreading 2019-08-01 15:43:02 +03:00
Oleg Kalachev
f05e0cc33e docs: add English version of GPIO article 2019-08-01 15:43:02 +03:00
Oleg Kalachev
9b1d58143e aruco.launch: define image_axis parameter 2019-08-01 04:07:31 +03:00
Oleg Kalachev
60fd4f9c0f aruco_map: add image_axis parameter for drawing axis on ~image topic 2019-08-01 04:06:18 +03:00
Oleg Kalachev
47bc3b90da simple_offboard: use velocity_body topic (for supporting mavros > 0.29) 2019-07-31 20:20:12 +03:00
Oleg Kalachev
991d7c9798 docs: add note about setting aruco to true in clever.launch 2019-07-31 19:03:31 +03:00
sfalexrog
d9aa62e2dd clever: Move udev rule file, add note to readme 2019-07-31 17:19:41 +03:00
timkondratiev
c590302130 docs: edit "setup" article (#145)
* Added a new parameter setup

Empty Voltage (per cell) - 3.50V.

* Added a new parameter setup

Empty Voltage (per cell) - 3.50V.

* Deleted outdated screenshot

* Added updated screenshot

* deleted wrong screenshot

* Added updated screenshot
2019-07-31 17:04:41 +03:00
sfalexrog
32cdce47c4 simple_offboard: Use string literal as format string 2019-07-31 13:30:00 +03:00
Oleg Kalachev
99e6518c90 docs: check images size is not larger than 600K 2019-07-31 03:11:43 +03:00
Alamoris
f953b1bbd9 Changed the condition of the roll flip 2019-07-30 19:38:13 +03:00
Oleg Kalachev
f5da6bc11c docs: add more proper names to markdownlint 2019-07-30 18:03:42 +03:00
Hany Hamed
b25a58c047 docs: fix EOL sequence 2019-07-30 16:15:45 +03:00
Oleg Kalachev
977c69908e docs: small fix 2019-07-30 04:11:00 +03:00
Oleg Kalachev
22940e266f docs: little fix 2019-07-30 04:05:46 +03:00
Oleg Kalachev
df134841c3 docs: fix article name 2019-07-30 03:57:06 +03:00
Oleg Kalachev
d94d3cc88d Merge pull request #142 from CopterExpress/robocross-2019-article
[WIP] docs: add article about robocross-2019
2019-07-30 03:52:51 +03:00
Oleg Kalachev
3db1f653bc docs: add article about robocross-2019 2019-07-30 03:50:44 +03:00
Oleg Kalachev
4cf825e004 simple_offboard: obtain vehicle pose using lookupTransform 2019-07-30 00:26:43 +03:00
Oleg Kalachev
6ea693eb85 docs: while True -> while not rospy.is_shutdown() in snippets 2019-07-29 22:48:49 +03:00
Hany Hamed
76a358e3bb docs: adding the documentation of human pose estimation drone control project in the English docs of Git book (#141)
* Add Human pose estimation drone control project to the documentation in Gitbook

* Update human_pose_estimation_drone_control.md

* Update human_pose_estimation_drone_control.md

* Change the videos links

* Update human_pose_estimation_drone_control.md

Fix markdownlint warnings

* Reduce the size of poses image
2019-07-26 18:50:04 +03:00
timkondratiev
6a6f26aff7 docs: fix the 3d-scanning drone article (#140)
* Fixed broken video link

* Fixed broken embedded video and image width

* Fixed image width on mobile
2019-07-26 16:32:37 +03:00
timkondratiev
e83c284313 docs: add article about 3D-scanning drone (#139)
* Added a photo of 3d-scanning drone

* Added a new article about 3d-scanning drone.

* Updated the article

* Included the new article about 3d-scanning drone.

* Added a new article.

* Included a new article

3D-scanning drone

* Fixed bugs

* Fixed md syntax
2019-07-25 19:07:20 +03:00
Oleg Kalachev
2e4b1e2637 simple_offboard: ensure all the arguments are finite 2019-07-23 22:07:28 +03:00
Oleg Kalachev
b3e2158250 simple_offboard: quick fix for /navigate_global sometimes not working 2019-07-22 15:09:46 +03:00
Oleg Kalachev
06a79f8d66 simple_offboard: descrease timeout for local position in /navigate_global 2019-07-22 15:09:46 +03:00
sfalexrog
18fff51181 docs: Fix MAVLink links, revert global setpoint note 2019-07-21 23:26:36 +03:00
Oleg Kalachev
eae36ab22d simple_offboard: fix transform timeout in /navigate_global 2019-07-21 21:46:52 +03:00
Oleg Kalachev
82f2a2df50 simple_offboard: increase the rate of checking in waitTransform 2019-07-21 21:25:51 +03:00
Oleg Kalachev
ea072ad01a selfcheck.py: check results must not be capitalized 2019-07-20 20:52:51 +03:00
Oleg Kalachev
0801ea2b67 selfcheck.py: catch some more exceptions 2019-07-20 20:02:14 +03:00
Oleg Kalachev
f7d3122f58 www: use location.hostname for hostname 2019-07-20 19:50:43 +03:00
Oleg Kalachev
bc0e45740f Use aruco_map_ prefix for markers in the map 2019-07-20 17:30:06 +03:00
Oleg Kalachev
57c22fccf7 docs: add takeoff_wait function to snippets 2019-07-20 15:33:06 +03:00
Oleg Kalachev
09d06a517f Update Flask version 2019-07-19 23:51:42 +03:00
sfalexrog
865b999431 builder: Update kernel version 2019-07-15 19:40:56 +03:00
Oleg Kalachev
0522622cea docs: add land_wait function to snippets 2019-07-14 12:35:17 +03:00
Oleg Kalachev
7b6103b941 docs: unneeded note about Cyrillic in English version 2019-07-14 12:28:51 +03:00
sfalexrog
95a509cd60 docs: Fix repository link 2019-07-12 17:19:54 +03:00
Oleg Kalachev
4cca8b2d84 selfcheck.py: print board rotation 2019-07-11 15:57:06 +03:00
sfalexrog
33ff7ea694 docs: Sync network docs across languages 2019-07-09 16:03:38 +03:00
Hany Hamed
d958d565a7 Fix network documentation in the English gitbook (#136)
Fix network documentation in the English gitbook
2019-07-09 15:06:14 +03:00
Oleg Kalachev
96cc0c7ad9 Forgotten lines 2019-07-03 05:38:10 +03:00
Oleg Kalachev
997484cd1f aruco_map: fix includes order 2019-07-03 05:23:44 +03:00
Oleg Kalachev
48b24a5fef aruco_map: possibility to publish static transforms for map's markers 2019-07-03 05:18:44 +03:00
Oleg Kalachev
2ae5ffe09f aruco_pose: add testing markers' tf frames 2019-07-02 05:18:33 +03:00
Oleg Kalachev
da29beda47 builder: run tests after GeographicLib datasets is installed 2019-07-02 02:54:20 +03:00
Oleg Kalachev
0303e645b7 Fix typo 2019-07-02 01:26:07 +03:00
Oleg Kalachev
979c863033 Add some test for clever package 2019-07-02 01:21:49 +03:00
Oleg Kalachev
46b8390c03 Little fix 2019-07-02 01:01:07 +03:00
Oleg Kalachev
e5df1cd1a0 aruco_pose: require all the nodelets not to crash in tests 2019-07-02 00:39:47 +03:00
Oleg Kalachev
32c04ef58d Bring back lxml to package.xml 2019-07-01 23:54:26 +03:00
Oleg Kalachev
596fa9aecf Add tf2_web_republisher to package dependencies 2019-07-01 22:47:47 +03:00
Oleg Kalachev
f883825def clever.launch: support copter_visualization renamed to visualization 2019-07-01 22:24:04 +03:00
Oleg Kalachev
d65df5d1ba Improve manual installation instruction and make some related fixes 2019-07-01 22:20:15 +03:00
tinderad
a183be2708 docs: add new version of camera calibration article (#134) 2019-06-29 15:34:57 +03:00
Oleg Kalachev
9c9ac3150d Use pytest for tests (#133)
* aruco_pose: use pytest

* Use ros_pytest

* Add ros_pytest to rosdep

* aruco_pose: compare floats more roughly in pytest

* aruco_pose: rewrite all the rest tests in pytest
2019-06-28 17:40:40 +03:00
Oleg Kalachev
82649cbe20 aruco_pose: remove unused lines from tests 2019-06-26 23:02:32 +03:00
Oleg Kalachev
b542851b24 aruco_pose: fix crashing the nodelet if markers on the map are to small 2019-06-26 23:00:49 +03:00
Oleg Kalachev
65d359b5c2 aruco_pose: fix command for running tests in readme 2019-06-26 22:42:08 +03:00
Oleg Kalachev
1b6f38f8cf docs: small fix 2019-06-26 21:15:58 +03:00
Oleg Kalachev
81f0dfd530 docs: other fixes to trainer mode article 2019-06-26 19:52:05 +03:00
Oleg Kalachev
52ed11ef8c docs: fixes to trainer mode article 2019-06-26 19:50:05 +03:00
Oleg Kalachev
4b384d9f61 selfcheck.py: show the number of markers in the map 2019-06-26 19:29:43 +03:00
VeneraDal
d37bd8ee87 docs: add trainer mode article
* Add files via upload

* Add files via upload

* Добавила статью по настройке  режима тренера.

В разделе "Настройки", после статьи Полётные режимы.

* Update Trainer_mode.md

* Update Trainer_mode.md

* Update Trainer_mode.md
2019-06-26 17:36:14 +03:00
Oleg Kalachev
8d606c2ed1 image: unlock vim version 2019-06-24 00:05:20 +03:00
Tenessinum
d2a405cb79 Aruco Map Generator markdown (#130)
* Add Aruco Map Generator markdown

* Add Aruco Map Generator link

* Fixed errors in arucogenmap.md
2019-06-23 18:34:59 +03:00
Andrei Korigodski
1b97bfa5a0 docs: fix en/README 2019-06-21 13:44:38 +03:00
garinegor
c2254c52d4 Fixed ap mode commands error (#131)
* fixed ap mode commands error

* removed unexpected spaces
2019-06-21 11:23:08 +02:00
Oleg Kalachev
8f304b628f Merge branch 'proof_reading' 2019-06-21 02:53:32 +02:00
sfalexrog
90c8fb5bac docs: firmware article 2019-06-20 01:25:00 +03:00
sfalexrog
bcd48bbd90 docs: linter 2019-06-20 01:03:42 +03:00
thomashamain
e0ed27875f doc: snippets.md 2019-06-19 22:34:32 +03:00
thomashamain
6f49b6dfda doc: ros-install.md ros.md
line 109: "working on serval PCs?" means that ROS is working on several PCs? I think a specification is needed.
2019-06-19 21:49:14 +03:00
sfalexrog
6a17217fbd docs: laser.md article 2019-06-19 21:47:22 +03:00
sfalexrog
2090f0a1ae docs: selfcheck.md proofreading 2019-06-19 21:29:41 +03:00
sfalexrog
fdae8ee2aa docs: mavros article 2019-06-19 21:16:18 +03:00
sfalexrog
7f802d3efd docs: make linter happy 2019-06-19 19:03:53 +03:00
thomashamain
5237ccf590 doc : optical_flow.md
i'm affraid i did not translate correctly the part at line 92:
the speed will be controlled such as Optical Flow "values"? do not exceed 50% of the "given parameter"?
2019-06-17 23:45:53 +03:00
Oleg Kalachev
25f69596fc Move body frame publishing to simple_offboard.cpp 2019-06-16 15:43:53 +03:00
Oleg Kalachev
7610f02b38 main_camera.launch: enable automatic rescaling camera calibration 2019-06-15 17:58:00 +03:00
sfalexrog
9218460d52 docs: Finish aruco_map translation 2019-06-14 19:36:56 +03:00
sfalexrog
db692f1484 docs: Start updating ArUco articles 2019-06-14 19:36:37 +03:00
sfalexrog
6d4663e4f4 docs: Rename some of the english articles 2019-06-14 19:36:26 +03:00
thomashamain
cfcb7ce652 lint fixes 2019-06-14 19:34:03 +03:00
Oleg Kalachev
7e10d0d17c docs: update snippets 2019-06-13 20:25:45 +03:00
sfalexrog
3a1e95a551 docs: Fix aruco_map ambiguity 2019-06-12 00:53:13 +03:00
thomashamain
cd34277c64 This commit is a correction of previous version
copter = helicopter, use drone.
blanch is not the correct term, use to tin which is widely used.
Flat cable is used but I beleive ribbon cable is more common (not sure)
Unclear sections (did not understand in russian neither, coming from an unadviced reader it might be usefull to clarify those points)
- l.61 "the red and black wires are to be tinned on both ends using tweezers" (did you mean soldering iron?)
- l.82 "disconnect the power and move yellow jumper to the other tweezer" (did you mean pins)
- l.145 "thus, it will be clear which motor is controlled" (unsure if by going through the procedure we activate the motors individually or by setting the throttle to 10% we simply see the direction of spin as it's slow)

not linked to the proof reading:
do you power the rgb strip directly from the raspberry pi with pin 40 (gpio 21) ?because it can be dangerous as the led strip is drawing x mA  or even x A, and the RPI is not designed to withstand such currents (I beleive not more than 500mA), be carefull :o
2019-06-12 00:28:35 +03:00
sfalexrog
88f4b4c10e docs: Specify actual topic and callback names 2019-06-11 23:03:20 +03:00
thomashamain
314e313947 Proofing for English documentation
copter = helicopter
2019-06-11 19:57:09 +03:00
thomashamain
596c111199 This commit is a correction of the previous version. A slight modification is suggested.
- Copter in English is a synonyme of helicopter, use drone or quadrocopter.
- I would modify ther term "additionnal frame" as it might be understood as spare frame. I would describe them as "top frame" "bottom frame" "main frame" etc.
2019-06-11 18:48:16 +03:00
sfalexrog
0b35c9902d buider: Update key (again) 2019-06-11 16:39:53 +03:00
sfalexrog
fd8425c6a7 builder: Update ROS key 2019-06-11 16:02:58 +03:00
sfalexrog
793f3630ef builder: Fix pull request check 2019-06-11 15:41:51 +03:00
sfalexrog
3915dd09bb docs: Fix axis ambiguity 2019-06-11 15:39:44 +03:00
Oleg Kalachev
789e09b7b9 docs: fixes to flip snippet 2019-06-07 18:35:11 +03:00
sfalexrog
0fb88bafb4 docs: web_video_server type parameter 2019-06-06 21:05:53 +03:00
118 changed files with 2816 additions and 1824 deletions

View File

@@ -13,17 +13,32 @@
"MD040": false,
"MD044": {
"names": [
"COEX",
"Copter Express",
"Коптер Экспресс",
"Клевер",
"MAVLink",
"ROS",
"ROS Kinetic",
"OpenCV",
"Gazebo",
"GitHub",
"FPV",
"PPM",
"PWM",
"Python",
"C++",
"JavaScript",
"Node.js",
"Django",
"Flask",
"HTTP",
"HTTPS",
"WebSocket",
"RPC",
"PX4",
"ArduPilot",
"jMAVSim",
"px4.io",
"logs.px4.io",
"QGroundControl",
@@ -31,16 +46,23 @@
"WireShark",
"FlightPlot",
"OFFBOARD",
"ACRO",
"RPY",
"LPE",
"EKF2",
"IMU",
"VPE",
"SITL",
"PID",
"Wi-Fi",
"Raspberry Pi",
"RPi",
"Linux",
"GNU",
"GNU/Linux",
"Windows",
"Docker",
"RFC",
"Travis",
"travis-ci.org",
"travis-ci.com",
@@ -51,6 +73,7 @@
"Raspbian",
"Raspbian Jesse",
"Raspbian Stretch",
"Raspbian Buster",
"Pixhawk",
"Pixracer",
"Arduino",
@@ -59,12 +82,19 @@
"LIRC",
"GPIO",
"HC-SR04",
"RCW-0001",
"RealSense",
"NUC",
"NVIDIA",
"Jetson",
"Jetson Nano",
"STM",
"LED",
"USB",
"FAT32",
"uORB",
"SSH",
"PuTTY",
"API",
"UART",
"GND",

View File

@@ -23,7 +23,7 @@ jobs:
# Check if there are any cached images, copy them to our "images" directory
- if [ -n "$(ls -A imgcache/*.zip)" ]; then mkdir -p images && cp imgcache/*.zip images; fi
script:
- if [[ -z ${TRAVIS_TAG} && ${TRAVIS_PULL_REQUEST} ]]; then
- if [[ -z ${TRAVIS_TAG} && "${TRAVIS_PULL_REQUEST}" != "false" ]]; then
echo "Commit range is ${TRAVIS_COMMIT_RANGE}" &&
if [ $(git diff --name-only ${TRAVIS_COMMIT_RANGE} | grep -v ^"docs/" | wc -l) -eq 0 ]; then
echo " === Docs-only change; skipping build ===" &&
@@ -62,6 +62,7 @@ jobs:
- markdownlint -V
script:
- markdownlint docs
- ./check_files_size.py
- gitbook install
- gitbook build
deploy:
@@ -84,6 +85,14 @@ jobs:
- pip install GitPython PyGithub
script:
- PYTHONUNBUFFERED=1 python ./gen_changelog.py
- stage: Build
name: Editorconfig-lint
language: generic
before_script:
- wget https://github.com/okalachev/editorconfig-checker/releases/download/1.2.1-disable-spaces-amount/ec-linux-amd64
- chmod +x ec-linux-amd64
script:
- ./ec-linux-amd64 -spaces-after-tabs -e "roslib.js|ros3d.js|eventemitter2.js|draw.cpp|BinUtils.swift|\.idea|apps/android/app|Assets.xcassets|test_parser_pass.txt|test_node_failure.txt"
stages:
- Build
- Annotate

View File

@@ -10,7 +10,7 @@ Copter Express has implemented a large number of different autonomous drone proj
Use it to learn how to assemble, configure, pilot and program autonomous CLEVER drone.
## Preconfigured RPi 3 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).**
@@ -30,49 +30,70 @@ API description (in Russian) for autonomous flights is available [on GitBook](ht
## Manual installation
Install ROS Kinetic according to the [documentation](http://wiki.ros.org/kinetic/Installation).
Install ROS Kinetic according to the [documentation](http://wiki.ros.org/kinetic/Installation), then [create a Catkin workspace](http://wiki.ros.org/catkin/Tutorials/create_a_workspace).
Clone repo to directory `/home/pi/catkin_ws/src/clever`:
Clone this repo to directory `~/catkin_ws/src/clever`:
```bash
cd ~/catkin_ws/src
git clone https://github.com/CopterExpress/clever.git clever
```
Build ROS packages:
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
```
Enable systemd service `roscore` (if not enabled):
To complete `mavros` install you'll need to install `geographiclib` datasets:
```bash
sudo systemctl enable /home/pi/catkin_ws/src/clever/deploy/roscore.service
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/deploy/roscore.service
sudo systemctl start roscore
```
Enable systemd service `clever`:
To start connection to SITL, use:
```bash
sudo systemctl enable /home/pi/catkin_ws/src/clever/deploy/clever.service
sudo systemctl start clever
roslaunch clever sitl.launch
```
### Dependencies
To start connection to the flight controller, use:
[ROS Kinetic](http://wiki.ros.org/kinetic).
```bash
roslaunch clever clever.launch
```
Necessary ROS packages:
> 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`.
* `opencv3`
* `mavros`
* `rosbridge_suite`
* `web_video_server`
* `cv_camera`
* `nodelet`
* `dynamic_reconfigure`
* `bondcpp`, branch `master`
* `roslint`
* `rosserial`
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
```

View File

@@ -23,19 +23,18 @@ function throttle(func, ms) {
}
function postAppMessage(msg) {
if (window.webkit != undefined) {
if (window.webkit.messageHandlers.appInterface != undefined) {
window.webkit.messageHandlers.appInterface.postMessage(JSON.stringify(msg));
}
}
else if (window.appInterface != undefined) {
window.appInterface.postMessage(JSON.stringify(msg));
}
if (window.webkit != undefined) {
if (window.webkit.messageHandlers.appInterface != undefined) {
window.webkit.messageHandlers.appInterface.postMessage(JSON.stringify(msg));
}
} else if (window.appInterface != undefined) {
window.appInterface.postMessage(JSON.stringify(msg));
}
}
function callNativeApp(name, msg) {
try {
postAppMessage(msg);
postAppMessage(msg);
return true;
} catch(err) {
console.warn('The native context does not exist yet');
@@ -109,12 +108,12 @@ function stickTouchStart(e) {
function stickTouchMove(e) {
for (touch of e.changedTouches) {
onStickTouchMove(touch);
}
//onStickTouchMove(e.changedTouches[0]);
rcPublishThrottled();
e.stopPropagation();
e.preventDefault();
onStickTouchMove(touch);
}
//onStickTouchMove(e.changedTouches[0]);
rcPublishThrottled();
e.stopPropagation();
e.preventDefault();
}
function stickTouchEnd(e) {
@@ -136,4 +135,4 @@ stickRight.addEventListener('touchmove', stickTouchMove);
stickLeft.addEventListener('touchstart', stickTouchStart);
stickRight.addEventListener('touchstart', stickTouchStart);
stickLeft.addEventListener('touchend', stickTouchEnd);
stickRight.addEventListener('touchend', stickTouchEnd);
stickRight.addEventListener('touchend', stickTouchEnd);

View File

@@ -173,7 +173,7 @@ target_link_libraries(aruco_pose
# See http://ros.org/doc/api/catkin/html/adv_user_guide/variables.html
## Mark executable scripts (Python etc.) for installation
## in contrast to setup.py, you can choose the destination
## in contrast to setup.py, you can choose the destination
# install(PROGRAMS
# scripts/my_python_script
# DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
@@ -219,4 +219,5 @@ if (CATKIN_ENABLE_TESTING)
add_rostest(test/test_parser_pass.test)
add_rostest(test/test_parser_empty_map.test)
add_rostest(test/test_node_failure.test)
add_rostest(test/largemap.test)
endif()

View File

@@ -110,7 +110,7 @@ See examples in [`map`](map/) directory.
Command for running tests:
```bash
rostest aruco_pose basic.test
catkin_make run_tests && catkin_test_results
```
## Copyright

View File

@@ -30,6 +30,7 @@
<depend>rostest</depend>
<test_depend>image_publisher</test_depend>
<test_depend>ros_pytest</test_depend>
<!-- The export tag contains other, unspecified, tags -->
<export>

View File

@@ -185,9 +185,13 @@ private:
// TODO: check IDs are unique
if (send_tf_) {
transform.child_frame_id = getChildFrameId(ids[i]);
transform.transform.rotation = marker.pose.orientation;
fillTranslation(transform.transform.translation, tvecs[i]);
br_.sendTransform(transform);
// check if such static transform exists
if (!tf_buffer_.canTransform(transform.header.frame_id, transform.child_frame_id, transform.header.stamp)) {
transform.transform.rotation = marker.pose.orientation;
fillTranslation(transform.transform.translation, tvecs[i]);
br_.sendTransform(transform);
}
}
}
array_.markers.push_back(marker);

View File

@@ -18,6 +18,7 @@
#include <string>
#include <vector>
#include <fstream>
#include <algorithm>
#include <ros/ros.h>
#include <nodelet/nodelet.h>
#include <pluginlib/class_list_macros.h>
@@ -27,6 +28,7 @@
#include <tf2_ros/buffer.h>
#include <tf2_ros/transform_listener.h>
#include <tf2_ros/transform_broadcaster.h>
#include <tf2_ros/static_transform_broadcaster.h>
#include <tf2_geometry_msgs/tf2_geometry_msgs.h>
#include <message_filters/subscriber.h>
#include <message_filters/synchronizer.h>
@@ -36,7 +38,6 @@
#include <sensor_msgs/Image.h>
#include <visualization_msgs/Marker.h>
#include <visualization_msgs/MarkerArray.h>
#include <algorithm>
#include <aruco_pose/MarkerArray.h>
#include <aruco_pose/Marker.h>
@@ -68,13 +69,15 @@ private:
Mat camera_matrix_, dist_coeffs_;
geometry_msgs::TransformStamped transform_;
geometry_msgs::PoseWithCovarianceStamped pose_;
vector<geometry_msgs::TransformStamped> markers_transforms_;
tf2_ros::TransformBroadcaster br_;
tf2_ros::StaticTransformBroadcaster static_br_;
tf2_ros::Buffer tf_buffer_;
tf2_ros::TransformListener tf_listener_{tf_buffer_};
visualization_msgs::MarkerArray vis_array_;
std::string known_tilt_;
std::string known_tilt_, map_, markers_frame_, markers_parent_frame_;
int image_width_, image_height_, image_margin_;
bool auto_flip_;
bool auto_flip_, image_axis_;
public:
virtual void onInit()
@@ -101,6 +104,9 @@ public:
nh_priv_.param("image_width", image_width_, 2000);
nh_priv_.param("image_height", image_height_, 2000);
nh_priv_.param("image_margin", image_margin_, 200);
nh_priv_.param("image_axis", image_axis_, true);
nh_priv_.param<std::string>("markers/frame_id", markers_parent_frame_, transform_.child_frame_id);
nh_priv_.param<std::string>("markers/child_frame_id_prefix", markers_frame_, "");
// createStripLine();
@@ -125,6 +131,7 @@ public:
sync_.reset(new message_filters::Synchronizer<SyncPolicy>(SyncPolicy(10), image_sub_, info_sub_, markers_sub_));
sync_->registerCallback(boost::bind(&ArucoMap::callback, this, _1, _2, _3));
publishMarkersFrames();
publishMapImage();
vis_markers_pub_.publish(vis_array_);
@@ -422,6 +429,15 @@ publish_debug:
board_->ids.push_back(id);
board_->objPoints.push_back(obj_points);
// Add marker's static transform
if (!markers_frame_.empty()) {
geometry_msgs::TransformStamped marker_transform;
marker_transform.header.frame_id = markers_parent_frame_;
marker_transform.child_frame_id = markers_frame_ + std::to_string(id);
tf::transformTFToMsg(transform, marker_transform.transform);
markers_transforms_.push_back(marker_transform);
}
// Add visualization marker
visualization_msgs::Marker marker;
marker.header.frame_id = transform_.child_frame_id;
@@ -452,6 +468,13 @@ publish_debug:
// vis_array_.markers.at(0).points.push_back(p);
}
void publishMarkersFrames()
{
if (!markers_transforms_.empty()) {
static_br_.sendTransform(markers_transforms_);
}
}
void publishMapImage()
{
cv::Size size(image_width_, image_height_);
@@ -459,14 +482,15 @@ publish_debug:
cv_bridge::CvImage msg;
if (!board_->ids.empty()) {
_drawPlanarBoard(board_, size, image, image_margin_, 1);
_drawPlanarBoard(board_, size, image, image_margin_, 1, image_axis_);
msg.encoding = image_axis_ ? sensor_msgs::image_encodings::RGB8 : sensor_msgs::image_encodings::MONO8;
} else {
// empty map
image.create(size, CV_8UC1);
image.setTo(cv::Scalar::all(255));
msg.encoding = sensor_msgs::image_encodings::MONO8;
}
msg.encoding = sensor_msgs::image_encodings::MONO8;
msg.image = image;
img_pub_.publish(msg.toImageMsg());
}

View File

@@ -23,12 +23,12 @@ static void _projectPoints( InputArray objectPoints,
void _drawPlanarBoard(Board *_board, Size outSize, OutputArray _img, int marginSize,
int borderBits) {
int borderBits, bool drawAxis) {
CV_Assert(outSize.area() > 0);
CV_Assert(marginSize >= 0);
_img.create(outSize, CV_8UC1);
_img.create(outSize, drawAxis ? CV_8UC3 : CV_8UC1);
Mat out = _img.getMat();
out.setTo(Scalar::all(255));
out.adjustROI(-marginSize, -marginSize, -marginSize, -marginSize);
@@ -87,8 +87,12 @@ void _drawPlanarBoard(Board *_board, Size outSize, OutputArray _img, int marginS
// dst_sz.width = dst_sz.height = std::min(dst_sz.width, dst_sz.height); //marker should be square
double diag = std::round(std::hypot(dst_sz.width, dst_sz.height));
int side = std::round(diag / std::sqrt(2));
side = std::max(side, 10);
dictionary.drawMarker(_board->ids[m], side, marker, borderBits);
if (drawAxis) {
cvtColor(marker, marker, COLOR_GRAY2RGB);
}
// interpolate tiny marker to marker position in markerZone
inCorners[0] = Point2f(-0.5f, -0.5f);
@@ -100,74 +104,81 @@ void _drawPlanarBoard(Board *_board, Size outSize, OutputArray _img, int marginS
warpAffine(marker, out, transformation, out.size(), INTER_LINEAR,
BORDER_TRANSPARENT);
}
// draw axis
if (drawAxis) {
Size wholeSize; Point ofs;
out.locateROI(wholeSize, ofs);
auto out_copy = _img.getMat();
cv::Point center(ofs.x - minX / sizeX * float(out.cols), ofs.y + out.rows + minY / sizeY * float(out.rows));
line(out_copy, center, center + Point(300, 0), Scalar(255, 0, 0), 10); // x axis
line(out_copy, center, center + Point(0, -300), Scalar(0, 255, 0), 10); // y axis
line(out_copy, center, center + Point(-200, 200), Scalar(0, 0, 255), 10); // z axis
}
}
/* Draw a (potentially partially visible) line. */
static void linePartial(InputOutputArray image, Point3f p1, Point3f p2, const Scalar& color,
int thickness = 1, int lineType = LINE_8, int shift = 0)
int thickness = 1, int lineType = LINE_8, int shift = 0)
{
// If both points are behind the screen, don't draw anything
if (p1.z <= 0 && p2.z <= 0)
{
return;
}
Point2f p1p{p1.x, p1.y};
Point2f p2p{p2.x, p2.y};
// If points are on the different sides of the plane, compute intersection point
if (p1.z * p2.z < 0)
{
// Compute intersection point with the screen
// We denote alpha as such:
// xi = (1 - alpha) * x1 + alpha * x2
// yi = (1 - alpha) * y1 + alpha * y2
// zi = (1 - alpha) * z1 + alpha * z2 = 0
// Thus, alpha can be expressed as
// alpha = z1 / (z1 - z2)
float alpha = p1.z / (p1.z - p2.z);
Point2f pi{(1 - alpha) * p1.x + alpha * p2.x, (1 - alpha) * p1.y + alpha * p2.y};
// Now, if z1 is negative, we draw the line from (xi, yi) to (x2, y2), else we draw from (x1, y1) to (xi, yi)
if (p1.z < 0)
{
p1p = pi;
}
else
{
p2p = pi;
}
}
line(image, p1p, p2p, color, thickness, lineType, shift);
// If both points are behind the screen, don't draw anything
if (p1.z <= 0 && p2.z <= 0) {
return;
}
Point2f p1p{p1.x, p1.y};
Point2f p2p{p2.x, p2.y};
// If points are on the different sides of the plane, compute intersection point
if (p1.z * p2.z < 0) {
// Compute intersection point with the screen
// We denote alpha as such:
// xi = (1 - alpha) * x1 + alpha * x2
// yi = (1 - alpha) * y1 + alpha * y2
// zi = (1 - alpha) * z1 + alpha * z2 = 0
// Thus, alpha can be expressed as
// alpha = z1 / (z1 - z2)
float alpha = p1.z / (p1.z - p2.z);
Point2f pi{(1 - alpha) * p1.x + alpha * p2.x, (1 - alpha) * p1.y + alpha * p2.y};
// Now, if z1 is negative, we draw the line from (xi, yi) to (x2, y2), else we draw from (x1, y1) to (xi, yi)
if (p1.z < 0) {
p1p = pi;
} else {
p2p = pi;
}
}
line(image, p1p, p2p, color, thickness, lineType, shift);
}
void _drawAxis(InputOutputArray _image, InputArray _cameraMatrix, InputArray _distCoeffs,
InputArray _rvec, InputArray _tvec, float length) {
CV_Assert(_image.getMat().total() != 0 &&
(_image.getMat().channels() == 1 || _image.getMat().channels() == 3));
CV_Assert(length > 0);
CV_Assert(_image.getMat().total() != 0 &&
(_image.getMat().channels() == 1 || _image.getMat().channels() == 3));
CV_Assert(length > 0);
// project axis points
std::vector< Point3f > axisPoints;
axisPoints.push_back(Point3f(0, 0, 0));
axisPoints.push_back(Point3f(length, 0, 0));
axisPoints.push_back(Point3f(0, length, 0));
axisPoints.push_back(Point3f(0, 0, length));
std::vector< Point3f > imagePointsZ;
_projectPoints(axisPoints, _rvec, _tvec, _cameraMatrix, _distCoeffs, imagePointsZ);
// project axis points
std::vector<Point3f> axisPoints;
axisPoints.push_back(Point3f(0, 0, 0));
axisPoints.push_back(Point3f(length, 0, 0));
axisPoints.push_back(Point3f(0, length, 0));
axisPoints.push_back(Point3f(0, 0, length));
std::vector<Point3f> imagePointsZ;
_projectPoints(axisPoints, _rvec, _tvec, _cameraMatrix, _distCoeffs, imagePointsZ);
// draw axis lines
linePartial(_image, imagePointsZ[0], imagePointsZ[1], Scalar(0, 0, 255), 3);
linePartial(_image, imagePointsZ[0], imagePointsZ[2], Scalar(0, 255, 0), 3);
linePartial(_image, imagePointsZ[0], imagePointsZ[3], Scalar(255, 0, 0), 3);
// draw axis lines
linePartial(_image, imagePointsZ[0], imagePointsZ[1], Scalar(0, 0, 255), 3);
linePartial(_image, imagePointsZ[0], imagePointsZ[2], Scalar(0, 255, 0), 3);
linePartial(_image, imagePointsZ[0], imagePointsZ[3], Scalar(255, 0, 0), 3);
}
static CvMat _cvMat(const cv::Mat& m)
{
CvMat self;
CV_DbgAssert(m.dims <= 2);
self = cvMat(m.rows, m.dims == 1 ? 1 : m.cols, m.type(), m.data);
self.step = (int)m.step[0];
self.type = (self.type & ~cv::Mat::CONTINUOUS_FLAG) | (m.flags & cv::Mat::CONTINUOUS_FLAG);
return self;
CvMat self;
CV_DbgAssert(m.dims <= 2);
self = cvMat(m.rows, m.dims == 1 ? 1 : m.cols, m.type(), m.data);
self.step = (int)m.step[0];
self.type = (self.type & ~cv::Mat::CONTINUOUS_FLAG) | (m.flags & cv::Mat::CONTINUOUS_FLAG);
return self;
}
static void _projectPoints( InputArray _opoints,
@@ -179,47 +190,47 @@ static void _projectPoints( InputArray _opoints,
OutputArray _jacobian,
double aspectRatio )
{
Mat opoints = _opoints.getMat();
int npoints = opoints.checkVector(3), depth = opoints.depth();
CV_Assert(npoints >= 0 && (depth == CV_32F || depth == CV_64F));
Mat opoints = _opoints.getMat();
int npoints = opoints.checkVector(3), depth = opoints.depth();
CV_Assert(npoints >= 0 && (depth == CV_32F || depth == CV_64F));
CvMat dpdrot, dpdt, dpdf, dpdc, dpddist;
CvMat *pdpdrot=0, *pdpdt=0, *pdpdf=0, *pdpdc=0, *pdpddist=0;
CvMat dpdrot, dpdt, dpdf, dpdc, dpddist;
CvMat *pdpdrot = 0, *pdpdt = 0, *pdpdf = 0, *pdpdc = 0, *pdpddist = 0;
CV_Assert( _ipoints.needed() );
CV_Assert(_ipoints.needed());
_ipoints.create(npoints, 1, CV_MAKETYPE(depth, 3), -1, true);
Mat imagePoints = _ipoints.getMat();
CvMat c_imagePoints = _cvMat(imagePoints);
CvMat c_objectPoints = _cvMat(opoints);
Mat cameraMatrix = _cameraMatrix.getMat();
_ipoints.create(npoints, 1, CV_MAKETYPE(depth, 3), -1, true);
Mat imagePoints = _ipoints.getMat();
CvMat c_imagePoints = _cvMat(imagePoints);
CvMat c_objectPoints = _cvMat(opoints);
Mat cameraMatrix = _cameraMatrix.getMat();
Mat rvec = _rvec.getMat(), tvec = _tvec.getMat();
CvMat c_cameraMatrix = _cvMat(cameraMatrix);
CvMat c_rvec = _cvMat(rvec), c_tvec = _cvMat(tvec);
Mat rvec = _rvec.getMat(), tvec = _tvec.getMat();
CvMat c_cameraMatrix = _cvMat(cameraMatrix);
CvMat c_rvec = _cvMat(rvec), c_tvec = _cvMat(tvec);
double dc0buf[5]={0};
Mat dc0(5,1,CV_64F,dc0buf);
Mat distCoeffs = _distCoeffs.getMat();
if( distCoeffs.empty() )
distCoeffs = dc0;
CvMat c_distCoeffs = _cvMat(distCoeffs);
int ndistCoeffs = distCoeffs.rows + distCoeffs.cols - 1;
double dc0buf[5] = {0};
Mat dc0(5, 1, CV_64F, dc0buf);
Mat distCoeffs = _distCoeffs.getMat();
if (distCoeffs.empty())
distCoeffs = dc0;
CvMat c_distCoeffs = _cvMat(distCoeffs);
int ndistCoeffs = distCoeffs.rows + distCoeffs.cols - 1;
Mat jacobian;
if( _jacobian.needed() )
{
_jacobian.create(npoints*2, 3+3+2+2+ndistCoeffs, CV_64F);
jacobian = _jacobian.getMat();
pdpdrot = &(dpdrot = _cvMat(jacobian.colRange(0, 3)));
pdpdt = &(dpdt = _cvMat(jacobian.colRange(3, 6)));
pdpdf = &(dpdf = _cvMat(jacobian.colRange(6, 8)));
pdpdc = &(dpdc = _cvMat(jacobian.colRange(8, 10)));
pdpddist = &(dpddist = _cvMat(jacobian.colRange(10, 10+ndistCoeffs)));
}
Mat jacobian;
if (_jacobian.needed())
{
_jacobian.create(npoints * 2, 3 + 3 + 2 + 2 + ndistCoeffs, CV_64F);
jacobian = _jacobian.getMat();
pdpdrot = &(dpdrot = _cvMat(jacobian.colRange(0, 3)));
pdpdt = &(dpdt = _cvMat(jacobian.colRange(3, 6)));
pdpdf = &(dpdf = _cvMat(jacobian.colRange(6, 8)));
pdpdc = &(dpdc = _cvMat(jacobian.colRange(8, 10)));
pdpddist = &(dpddist = _cvMat(jacobian.colRange(10, 10 + ndistCoeffs)));
}
_cvProjectPoints2( &c_objectPoints, &c_rvec, &c_tvec, &c_cameraMatrix, &c_distCoeffs,
&c_imagePoints, pdpdrot, pdpdt, pdpdf, pdpdc, pdpddist, aspectRatio );
_cvProjectPoints2(&c_objectPoints, &c_rvec, &c_tvec, &c_cameraMatrix, &c_distCoeffs,
&c_imagePoints, pdpdrot, pdpdt, pdpdf, pdpdc, pdpddist, aspectRatio);
}
namespace _detail

View File

@@ -3,6 +3,7 @@
#include <opencv2/opencv.hpp>
#include <opencv2/aruco.hpp>
void _drawPlanarBoard(cv::aruco::Board *_board, cv::Size outSize, cv::OutputArray _img, int marginSize, int borderBits);
void _drawPlanarBoard(cv::aruco::Board *_board, cv::Size outSize, cv::OutputArray _img,
int marginSize, int borderBits, bool drawAxis); // editorconfig-checker-disable-line
void _drawAxis(cv::InputOutputArray image, cv::InputArray cameraMatrix, cv::InputArray distCoeffs,
cv::InputArray rvec, cv::InputArray tvec, float length);
cv::InputArray rvec, cv::InputArray tvec, float length); // editorconfig-checker-disable-line

View File

@@ -30,8 +30,7 @@ static void param(ros::NodeHandle nh, const std::string& param_name, T& param_va
}
}
static void parseCameraInfo(const sensor_msgs::CameraInfoConstPtr& cinfo,
cv::Mat& matrix, cv::Mat& dist)
static void parseCameraInfo(const sensor_msgs::CameraInfoConstPtr& cinfo, cv::Mat& matrix, cv::Mat& dist)
{
for (unsigned int i = 0; i < 3; ++i)
for (unsigned int j = 0; j < 3; ++j)

View File

@@ -1,101 +1,149 @@
#!/usr/bin/env python
import sys
import unittest
import json
import rospy
import rostest
import pytest
import tf2_ros
import tf2_geometry_msgs
from geometry_msgs.msg import PoseWithCovarianceStamped
from sensor_msgs.msg import Image
from aruco_pose.msg import MarkerArray
from visualization_msgs.msg import MarkerArray as VisMarkerArray
class TestArucoPose(unittest.TestCase):
def setUp(self):
rospy.init_node('test_aruco_detect', anonymous=True)
@pytest.fixture
def node():
return rospy.init_node('aruco_pose_test', anonymous=True)
def test_markers(self):
markers = rospy.wait_for_message('aruco_detect/markers', MarkerArray, timeout=5)
self.assertEqual(len(markers.markers), 4)
self.assertEqual(markers.header.frame_id, 'main_camera_optical')
@pytest.fixture
def tf_buffer():
buf = tf2_ros.Buffer()
tf2_ros.TransformListener(buf)
return buf
self.assertEqual(markers.markers[0].id, 2)
self.assertAlmostEqual(markers.markers[0].length, 0.33, places=4)
self.assertAlmostEqual(markers.markers[0].pose.position.x, 0.36706567854, places=4)
self.assertAlmostEqual(markers.markers[0].pose.position.y, 0.290484516644, places=4)
self.assertAlmostEqual(markers.markers[0].pose.position.z, 2.18787602301, places=4)
self.assertAlmostEqual(markers.markers[0].pose.orientation.x, 0.993997406299, places=4)
self.assertAlmostEqual(markers.markers[0].pose.orientation.y, -0.00532003481626, places=4)
self.assertAlmostEqual(markers.markers[0].pose.orientation.z, -0.107390951553, places=4)
self.assertAlmostEqual(markers.markers[0].pose.orientation.w, 0.0201999263402, places=4)
self.assertAlmostEqual(markers.markers[0].c1.x, 415.557739258, places=4)
self.assertAlmostEqual(markers.markers[0].c1.y, 335.557739258, places=4)
self.assertAlmostEqual(markers.markers[0].c2.x, 509.442260742, places=4)
self.assertAlmostEqual(markers.markers[0].c2.y, 335.557739258, places=4)
self.assertAlmostEqual(markers.markers[0].c3.x, 509.442260742, places=4)
self.assertAlmostEqual(markers.markers[0].c3.y, 429.442260742, places=4)
self.assertAlmostEqual(markers.markers[0].c4.x, 415.557739258, places=4)
self.assertAlmostEqual(markers.markers[0].c4.y, 429.442260742, places=4)
def approx(expected):
return pytest.approx(expected, abs=1e-4) # compare floats more roughly
self.assertEqual(markers.markers[3].id, 3)
self.assertAlmostEqual(markers.markers[3].length, 0.1, places=4)
self.assertAlmostEqual(markers.markers[3].pose.position.x, -0.1805169666, places=4)
self.assertAlmostEqual(markers.markers[3].pose.position.y, -0.200697302327, places=4)
self.assertAlmostEqual(markers.markers[3].pose.position.z, 0.585767514823, places=4)
self.assertAlmostEqual(markers.markers[3].pose.orientation.x, -0.961738074009, places=4)
self.assertAlmostEqual(markers.markers[3].pose.orientation.y, -0.0375180244707, places=4)
self.assertAlmostEqual(markers.markers[3].pose.orientation.z, -0.0115387773672, places=4)
self.assertAlmostEqual(markers.markers[3].pose.orientation.w, 0.271144115664, places=4)
self.assertAlmostEqual(markers.markers[3].c1.x, 129.557723999, places=4)
self.assertAlmostEqual(markers.markers[3].c1.y, 49.557723999, places=4)
self.assertAlmostEqual(markers.markers[3].c2.x, 223.442276001, places=4)
self.assertAlmostEqual(markers.markers[3].c2.y, 49.557723999, places=4)
self.assertAlmostEqual(markers.markers[3].c3.x, 223.442276001, places=4)
self.assertAlmostEqual(markers.markers[3].c3.y, 143.442276001, places=4)
self.assertAlmostEqual(markers.markers[3].c4.x, 129.557723999, places=4)
self.assertAlmostEqual(markers.markers[3].c4.y, 143.442276001, places=4)
def test_markers(node):
markers = rospy.wait_for_message('aruco_detect/markers', MarkerArray, timeout=5)
assert len(markers.markers) == 5
assert markers.header.frame_id == 'main_camera_optical'
self.assertEqual(markers.markers[1].id, 1)
self.assertAlmostEqual(markers.markers[1].length, 0.33, places=4)
self.assertEqual(markers.markers[2].id, 4)
self.assertAlmostEqual(markers.markers[2].length, 0.33, places=4)
assert markers.markers[0].id == 2
assert markers.markers[0].length == approx(0.33)
assert markers.markers[0].pose.position.x == approx(0.36706567854)
assert markers.markers[0].pose.position.y == approx(0.290484516644)
assert markers.markers[0].pose.position.z == approx(2.18787602301)
assert markers.markers[0].pose.orientation.x == approx(0.993997406299)
assert markers.markers[0].pose.orientation.y == approx(-0.00532003481626)
assert markers.markers[0].pose.orientation.z == approx(-0.107390951553)
assert markers.markers[0].pose.orientation.w == approx(0.0201999263402)
assert markers.markers[0].c1.x == approx(415.557739258)
assert markers.markers[0].c1.y == approx(335.557739258)
assert markers.markers[0].c2.x == approx(509.442260742)
assert markers.markers[0].c2.y == approx(335.557739258)
assert markers.markers[0].c3.x == approx(509.442260742)
assert markers.markers[0].c3.y == approx(429.442260742)
assert markers.markers[0].c4.x == approx(415.557739258)
assert markers.markers[0].c4.y == approx(429.442260742)
def test_visualization(self):
vis = rospy.wait_for_message('aruco_detect/visualization', VisMarkerArray, timeout=5)
self.assertEqual(len(vis.markers), 9)
assert markers.markers[4].id == 3
assert markers.markers[4].length == approx(0.1)
assert markers.markers[4].pose.position.x == approx(-0.1805169666)
assert markers.markers[4].pose.position.y == approx(-0.200697302327)
assert markers.markers[4].pose.position.z == approx(0.585767514823)
assert markers.markers[4].pose.orientation.x == approx(-0.961738074009)
assert markers.markers[4].pose.orientation.y == approx(-0.0375180244707)
assert markers.markers[4].pose.orientation.z == approx(-0.0115387773672)
assert markers.markers[4].pose.orientation.w == approx(0.271144115664)
assert markers.markers[4].c1.x == approx(129.557723999)
assert markers.markers[4].c1.y == approx(49.557723999)
assert markers.markers[4].c2.x == approx(223.442276001)
assert markers.markers[4].c2.y == approx(49.557723999)
assert markers.markers[4].c3.x == approx(223.442276001)
assert markers.markers[4].c3.y == approx(143.442276001)
assert markers.markers[4].c4.x == approx(129.557723999)
assert markers.markers[4].c4.y == approx(143.442276001)
def test_debug(self):
img = rospy.wait_for_message('aruco_detect/debug', Image, timeout=5)
self.assertEqual(img.width, 640)
self.assertEqual(img.height, 480)
self.assertEqual(img.header.frame_id, 'main_camera_optical')
assert markers.markers[1].id == 1
assert markers.markers[1].length == approx(0.33)
assert markers.markers[3].id == 4
assert markers.markers[3].length == approx(0.33)
def test_map(self):
pose = rospy.wait_for_message('aruco_map/pose', PoseWithCovarianceStamped, timeout=5)
self.assertEqual(pose.header.frame_id, 'main_camera_optical')
self.assertAlmostEqual(pose.pose.pose.position.x, -0.629167753342, places=4)
self.assertAlmostEqual(pose.pose.pose.position.y, 0.293822650809, places=4)
self.assertAlmostEqual(pose.pose.pose.position.z, 2.12641343155, places=4)
self.assertAlmostEqual(pose.pose.pose.orientation.x, -0.998383794799, places=4)
self.assertAlmostEqual(pose.pose.pose.orientation.y, -5.20919098575e-06, places=4)
self.assertAlmostEqual(pose.pose.pose.orientation.z, -0.0300861070302, places=4)
self.assertAlmostEqual(pose.pose.pose.orientation.w, 0.0482143590507, places=4)
assert markers.markers[2].id == 100
assert markers.markers[2].length == approx(0.33)
assert markers.markers[2].pose.position.x == approx(-1.37600105389)
assert markers.markers[2].pose.position.y == approx(-0.323028530991)
assert markers.markers[2].pose.position.z == approx(2.94611272668)
assert markers.markers[2].pose.orientation.x == approx(-0.955543925678)
assert markers.markers[2].pose.orientation.y == approx(0.0458801909197)
assert markers.markers[2].pose.orientation.z == approx(-0.249604946264)
assert markers.markers[2].pose.orientation.w == approx(-0.150093920537)
assert markers.markers[2].c1.x == approx(52.557723999)
assert markers.markers[2].c1.y == approx(205.557723999)
assert markers.markers[2].c2.x == approx(113.442276001)
assert markers.markers[2].c2.y == approx(205.557723999)
assert markers.markers[2].c3.x == approx(113.442276001)
assert markers.markers[2].c3.y == approx(265.442260742)
assert markers.markers[2].c4.x == approx(52.557723999)
assert markers.markers[2].c4.y == approx(265.442260742)
def test_map_image(self):
img = rospy.wait_for_message('aruco_map/image', Image, timeout=5)
self.assertEqual(img.width, 2000)
self.assertEqual(img.height, 2000)
self.assertEqual(img.encoding, 'mono8')
def test_markers_frames(node, tf_buffer):
marker_2 = tf_buffer.lookup_transform('main_camera_optical', 'aruco_2', rospy.Time(), rospy.Duration(5))
assert marker_2.transform.translation.x == approx(0.36706567854)
assert marker_2.transform.translation.y == approx(0.290484516644)
assert marker_2.transform.translation.z == approx(2.18787602301)
assert marker_2.transform.rotation.x == approx(0.993997406299)
assert marker_2.transform.rotation.y == approx(-0.00532003481626)
assert marker_2.transform.rotation.z == approx(-0.107390951553)
assert marker_2.transform.rotation.w == approx(0.0201999263402)
def test_map_visualization(self):
vis = rospy.wait_for_message('aruco_map/visualization', VisMarkerArray, timeout=5)
def test_map_markers_frames(node, tf_buffer):
stamp = rospy.get_rostime()
timeout = rospy.Duration(5)
def test_map_debug(self):
img = rospy.wait_for_message('aruco_map/debug', Image, timeout=5)
marker_1 = tf_buffer.lookup_transform('aruco_map', 'aruco_in_map_1', stamp, timeout)
assert marker_1.transform.translation.x == approx(0)
assert marker_1.transform.translation.y == approx(0)
assert marker_1.transform.translation.z == approx(0)
# def test_transforms(self):
# pass
marker_4 = tf_buffer.lookup_transform('aruco_map', 'aruco_in_map_4', stamp, timeout)
assert marker_4.transform.translation.x == approx(1)
assert marker_4.transform.translation.y == approx(1)
assert marker_4.transform.translation.z == approx(0)
marker_12 = tf_buffer.lookup_transform('aruco_map', 'aruco_in_map_12', stamp, timeout)
assert marker_12.transform.translation.x == approx(0.2)
assert marker_12.transform.translation.y == approx(0.5)
assert marker_12.transform.translation.z == approx(0)
rostest.rosrun('aruco_pose', 'test_aruco_detect', TestArucoPose, sys.argv)
def test_visualization(node):
vis = rospy.wait_for_message('aruco_detect/visualization', VisMarkerArray, timeout=5)
assert len(vis.markers) == 11
def test_debug(node):
img = rospy.wait_for_message('aruco_detect/debug', Image, timeout=5)
assert img.width == 640
assert img.height == 480
assert img.header.frame_id == 'main_camera_optical'
def test_map(node):
pose = rospy.wait_for_message('aruco_map/pose', PoseWithCovarianceStamped, timeout=5)
assert pose.header.frame_id == 'main_camera_optical'
assert pose.pose.pose.position.x == approx(-0.629167753342)
assert pose.pose.pose.position.y == approx(0.293822650809)
assert pose.pose.pose.position.z == approx(2.12641343155)
assert pose.pose.pose.orientation.x == approx(-0.998383794799)
assert pose.pose.pose.orientation.y == approx(-5.20919098575e-06)
assert pose.pose.pose.orientation.z == approx(-0.0300861070302)
assert pose.pose.pose.orientation.w == approx(0.0482143590507)
def test_map_image(node):
img = rospy.wait_for_message('aruco_map/image', Image, timeout=5)
assert img.width == 2000
assert img.height == 2000
assert img.encoding == 'mono8'
def test_map_visualization(node):
vis = rospy.wait_for_message('aruco_map/visualization', VisMarkerArray, timeout=5)
def test_map_debug(node):
img = rospy.wait_for_message('aruco_map/debug', Image, timeout=5)

View File

@@ -5,23 +5,27 @@
<param name="camera_info_url" value="file://$(find aruco_pose)/test/camera_info.yaml" />
</node>
<node pkg="nodelet" type="nodelet" name="nodelet_manager" args="manager"/>
<node pkg="nodelet" type="nodelet" name="nodelet_manager" args="manager" required="true"/>
<node pkg="nodelet" clear_params="true" type="nodelet" name="aruco_detect" args="load aruco_pose/aruco_detect nodelet_manager">
<node pkg="nodelet" clear_params="true" type="nodelet" name="aruco_detect" args="load aruco_pose/aruco_detect nodelet_manager" required="true">
<remap from="image_raw" to="main_camera/image_raw"/>
<remap from="camera_info" to="main_camera/camera_info"/>
<param name="length" value="0.33"/>
<param name="length_override/3" value="0.1"/>
<param name="estimate_poses" value="true"/>
<param name="send_tf" value="true"/>
</node>
<node name="aruco_map" pkg="nodelet" type="nodelet" args="load aruco_pose/aruco_map nodelet_manager" clear_params="true">
<node name="aruco_map" pkg="nodelet" type="nodelet" args="load aruco_pose/aruco_map nodelet_manager" clear_params="true" required="true">
<remap from="image_raw" to="main_camera/image_raw"/>
<remap from="camera_info" to="main_camera/camera_info"/>
<remap from="markers" to="aruco_detect/markers"/>
<param name="type" value="map"/>
<param name="map" value="$(find aruco_pose)/test/basic.txt"/>
<param name="markers/frame_id" value="aruco_map"/>
<param name="markers/child_frame_id_prefix" value="aruco_in_map_"/>
</node>
<test test-name="test_aruco_pose" pkg="aruco_pose" type="basic.py"/>
<param name="test_module" value="$(find aruco_pose)/test/basic.py"/>
<test test-name="aruco_pose_test" pkg="ros_pytest" type="ros_pytest_runner"/>
</launch>

26
aruco_pose/test/largemap.py Executable file
View File

@@ -0,0 +1,26 @@
#!/usr/bin/env python
import sys
import unittest
import json
import rospy
import rostest
from sensor_msgs.msg import Image
from visualization_msgs.msg import MarkerArray as VisMarkerArray
class TestArucoPose(unittest.TestCase):
def setUp(self):
rospy.init_node('test_aruco_largemap', anonymous=True)
def test_map_image(self):
img = rospy.wait_for_message('aruco_map/image', Image, timeout=5)
self.assertEqual(img.width, 2000)
self.assertEqual(img.height, 2000)
self.assertEqual(img.encoding, 'mono8')
def test_map_visualization(self):
vis = rospy.wait_for_message('aruco_map/visualization', VisMarkerArray, timeout=5)
rostest.rosrun('aruco_pose', 'test_aruco_largemap', TestArucoPose, sys.argv)

View File

@@ -0,0 +1,13 @@
<launch>
<node pkg="nodelet" type="nodelet" name="nodelet_manager" args="manager" required="true"/>
<node name="aruco_map" pkg="nodelet" type="nodelet" args="load aruco_pose/aruco_map nodelet_manager" clear_params="true" required="true">
<remap from="image_raw" to="main_camera/image_raw"/>
<remap from="camera_info" to="main_camera/camera_info"/>
<remap from="markers" to="aruco_detect/markers"/>
<param name="type" value="map"/>
<param name="map" value="$(find aruco_pose)/test/largemap.txt"/>
</node>
<test test-name="test_aruco_pose_largemap" pkg="aruco_pose" type="largemap.py"/>
</launch>

View File

@@ -0,0 +1,11 @@
0 0.2 0 0 0 0 0 0
1 0.2 10 0 0 0 0 0
2 0.2 20 0 0 0 0 0
3 0.2 30 0 0 0 0 0
4 0.2 40 0 0 0 0 0
5 0.2 50 0 0 0 0 0
6 0.2 60 0 0 0 0 0
7 0.2 70 0 0 0 0 0
8 0.2 80 0 0 0 0 0
9 0.2 90 0 0 0 0 0
10 0.2 100 0 0 0 0 0

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@@ -1,27 +1,13 @@
#!/usr/bin/env python
import sys
import unittest
import json
import rospy
import rostest
import pytest
from geometry_msgs.msg import PoseWithCovarianceStamped
from sensor_msgs.msg import Image
from aruco_pose.msg import MarkerArray
from visualization_msgs.msg import MarkerArray as VisMarkerArray
class TestArucoMapPass(unittest.TestCase):
def setUp(self):
rospy.init_node('test_parser_fail', anonymous=True)
@pytest.fixture
def node():
return rospy.init_node('aruco_pose_test', anonymous=True)
def test_node_failure(self):
try:
markers = rospy.wait_for_message('aruco_map/visualization', VisMarkerArray, timeout=5)
did_post_message = True
except rospy.exceptions.ROSException:
did_post_message = False
self.assertFalse(did_post_message)
rostest.rosrun('aruco_pose', 'test_aruco_map', TestArucoMapPass, sys.argv)
def test_node_failure(node):
with pytest.raises(rospy.exceptions.ROSException):
rospy.wait_for_message('aruco_map/visualization', VisMarkerArray, timeout=5)

View File

@@ -9,5 +9,6 @@
<param name="map" value="$(find aruco_pose)/test/test_node_failure.txt"/>
</node>
<test test-name="test_aruco_map_fail_dict" pkg="aruco_pose" type="test_node_failure.py"/>
<param name="test_module" value="$(find aruco_pose)/test/test_node_failure.py"/>
<test test-name="test_node_failure" pkg="ros_pytest" type="ros_pytest_runner"/>
</launch>

View File

@@ -1,24 +1,13 @@
#!/usr/bin/env python
import sys
import unittest
import json
import rospy
import rostest
import pytest
from geometry_msgs.msg import PoseWithCovarianceStamped
from sensor_msgs.msg import Image
from aruco_pose.msg import MarkerArray
from visualization_msgs.msg import MarkerArray as VisMarkerArray
class TestArucoMapPass(unittest.TestCase):
def setUp(self):
rospy.init_node('test_parser_fail', anonymous=True)
@pytest.fixture
def node():
return rospy.init_node('aruco_pose_test_empty_map', anonymous=True)
def test_node_failure(self):
markers = rospy.wait_for_message('aruco_map/visualization', VisMarkerArray, timeout=5)
self.assertEquals(len(markers.markers), 0)
rostest.rosrun('aruco_pose', 'test_aruco_map', TestArucoMapPass, sys.argv)
def test_empty_map(node):
markers = rospy.wait_for_message('aruco_map/visualization', VisMarkerArray, timeout=5)
assert len(markers.markers) == 0

View File

@@ -9,5 +9,6 @@
<param name="map" value="$(find aruco_pose)/test/test_parser_empty_map.txt"/>
</node>
<test test-name="test_aruco_map_incomplete" pkg="aruco_pose" type="test_parser_empty_map.py"/>
<param name="test_module" value="$(find aruco_pose)/test/test_parser_empty_map.py"/>
<test test-name="test_node_empty_map" pkg="ros_pytest" type="ros_pytest_runner"/>
</launch>

View File

@@ -1,75 +1,61 @@
#!/usr/bin/env python
import sys
import unittest
import json
import rospy
import rostest
import pytest
from geometry_msgs.msg import PoseWithCovarianceStamped
from sensor_msgs.msg import Image
from aruco_pose.msg import MarkerArray
from visualization_msgs.msg import MarkerArray as VisMarkerArray
class TestArucoMapPass(unittest.TestCase):
def setUp(self):
rospy.init_node('test_parser_pass', anonymous=True)
@pytest.fixture
def node():
return rospy.init_node('aruco_pose_test', anonymous=True)
def test_markers(self):
markers = rospy.wait_for_message('aruco_map/visualization', VisMarkerArray, timeout=5)
self.assertEqual(len(markers.markers), 6)
# FIXME: visual marker id is not ArUco marker id
# self.assertEqual(markers.markers[0].id, 1)
# self.assertEqual(markers.markers[1].id, 2)
# self.assertEqual(markers.markers[2].id, 3)
# self.assertEqual(markers.markers[3].id, 4)
def approx(expected):
return pytest.approx(expected, abs=1e-4) # compare floats more roughly
self.assertAlmostEqual(markers.markers[0].pose.position.x, 0, places=7)
self.assertAlmostEqual(markers.markers[0].pose.position.y, 0, places=7)
self.assertAlmostEqual(markers.markers[0].pose.position.z, 0, places=7)
self.assertAlmostEqual(markers.markers[1].pose.position.x, 1, places=7)
self.assertAlmostEqual(markers.markers[1].pose.position.y, 1, places=7)
self.assertAlmostEqual(markers.markers[1].pose.position.z, 1, places=7)
self.assertAlmostEqual(markers.markers[2].pose.position.x, 1, places=7)
self.assertAlmostEqual(markers.markers[2].pose.position.y, 0, places=7)
self.assertAlmostEqual(markers.markers[2].pose.position.z, 0.5, places=7)
self.assertAlmostEqual(markers.markers[3].pose.position.x, 0, places=7)
self.assertAlmostEqual(markers.markers[3].pose.position.y, 1, places=7)
self.assertAlmostEqual(markers.markers[3].pose.position.z, 0, places=7)
self.assertAlmostEqual(markers.markers[4].pose.position.x, 1, places=7)
self.assertAlmostEqual(markers.markers[4].pose.position.y, 0.5, places=7)
self.assertAlmostEqual(markers.markers[4].pose.position.z, 0, places=7)
self.assertAlmostEqual(markers.markers[5].pose.position.x, 2.2, places=7)
self.assertAlmostEqual(markers.markers[5].pose.position.y, 0.2, places=7)
self.assertAlmostEqual(markers.markers[5].pose.position.z, 0, places=7)
def test_markers(node):
markers = rospy.wait_for_message('aruco_map/visualization', VisMarkerArray, timeout=5)
assert len(markers.markers) == 6
self.assertAlmostEqual(markers.markers[0].scale.x, 0.33, places=7)
self.assertAlmostEqual(markers.markers[0].scale.y, 0.33, places=7)
self.assertAlmostEqual(markers.markers[1].scale.x, 0.225, places=7)
self.assertAlmostEqual(markers.markers[1].scale.y, 0.225, places=7)
self.assertAlmostEqual(markers.markers[2].scale.x, 0.45, places=7)
self.assertAlmostEqual(markers.markers[2].scale.y, 0.45, places=7)
self.assertAlmostEqual(markers.markers[3].scale.x, 0.15, places=7)
self.assertAlmostEqual(markers.markers[3].scale.y, 0.15, places=7)
self.assertAlmostEqual(markers.markers[4].scale.x, 0.25, places=7)
self.assertAlmostEqual(markers.markers[4].scale.y, 0.25, places=7)
self.assertAlmostEqual(markers.markers[5].scale.x, 0.35, places=7)
self.assertAlmostEqual(markers.markers[5].scale.y, 0.35, places=7)
assert markers.markers[0].pose.position.x == approx(0)
assert markers.markers[0].pose.position.y == approx(0)
assert markers.markers[0].pose.position.z == approx(0)
def test_map_image(self):
img = rospy.wait_for_message('aruco_map/image', Image, timeout=5)
self.assertEqual(img.width, 2000)
self.assertEqual(img.height, 2000)
self.assertEqual(img.encoding, 'mono8')
assert markers.markers[1].pose.position.x == approx(1)
assert markers.markers[1].pose.position.y == approx(1)
assert markers.markers[1].pose.position.z == approx(1)
# def test_transforms(self):
# pass
assert markers.markers[2].pose.position.x == approx(1)
assert markers.markers[2].pose.position.y == approx(0)
assert markers.markers[2].pose.position.z == approx(0.5)
assert markers.markers[3].pose.position.x == approx(0)
assert markers.markers[3].pose.position.y == approx(1)
assert markers.markers[3].pose.position.z == approx(0)
rostest.rosrun('aruco_pose', 'test_aruco_map', TestArucoMapPass, sys.argv)
assert markers.markers[4].pose.position.x == approx(1)
assert markers.markers[4].pose.position.y == approx(0.5)
assert markers.markers[4].pose.position.z == approx(0)
assert markers.markers[5].pose.position.x == approx(2.2)
assert markers.markers[5].pose.position.y == approx(0.2)
assert markers.markers[5].pose.position.z == approx(0)
assert markers.markers[0].scale.x == approx(0.33)
assert markers.markers[0].scale.y == approx(0.33)
assert markers.markers[1].scale.x == approx(0.225)
assert markers.markers[1].scale.y == approx(0.225)
assert markers.markers[2].scale.x == approx(0.45)
assert markers.markers[2].scale.y == approx(0.45)
assert markers.markers[3].scale.x == approx(0.15)
assert markers.markers[3].scale.y == approx(0.15)
assert markers.markers[4].scale.x == approx(0.25)
assert markers.markers[4].scale.y == approx(0.25)
assert markers.markers[5].scale.x == approx(0.35)
assert markers.markers[5].scale.y == approx(0.35)
def test_map_image(node):
img = rospy.wait_for_message('aruco_map/image', Image, timeout=5)
assert img.width == 2000
assert img.height == 2000
assert img.encoding == 'mono8'

View File

@@ -1,7 +1,7 @@
<launch>
<node pkg="nodelet" type="nodelet" name="nodelet_manager" args="manager"/>
<node pkg="nodelet" type="nodelet" name="nodelet_manager" args="manager" required="true"/>
<node name="aruco_map" pkg="nodelet" type="nodelet" args="load aruco_pose/aruco_map nodelet_manager" clear_params="true">
<node name="aruco_map" pkg="nodelet" type="nodelet" args="load aruco_pose/aruco_map nodelet_manager" clear_params="true" required="true">
<remap from="image_raw" to="main_camera/image_raw"/>
<remap from="camera_info" to="main_camera/camera_info"/>
<remap from="markers" to="aruco_detect/markers"/>
@@ -9,5 +9,6 @@
<param name="map" value="$(find aruco_pose)/test/test_parser_pass.txt"/>
</node>
<test test-name="test_aruco_map" pkg="aruco_pose" type="test_parser_pass.py"/>
<param name="test_module" value="$(find aruco_pose)/test/test_parser_pass.py"/>
<test test-name="test_node_pass" pkg="ros_pytest" type="ros_pytest_runner"/>
</launch>

View File

@@ -1,12 +1,12 @@
[Unit]
Description=Clever ROS package
Requires=roscore.service
After=roscore.service
After=network.target
[Service]
User=pi
EnvironmentFile=/lib/systemd/system/roscore.env
ExecStart=/opt/ros/kinetic/bin/roslaunch clever clever.launch --wait --screen
ExecStart=/bin/sh -c ". /home/pi/catkin_ws/devel/setup.sh; \
ROS_HOSTNAME=`hostname`.local exec roslaunch clever clever.launch --wait --screen --skip-log-check"
Restart=on-failure
RestartSec=3

View File

@@ -1,35 +0,0 @@
#!/usr/bin/env bash
# Set Clever hostname to the specified value
set -e
NEW_NAME_OPT=$1
if [[ -z ${NEW_NAME_OPT} ]]; then
echo "Please specify new name for this Clever"
exit 1
fi
NEW_NAME=$(echo ${NEW_NAME_OPT} | tr '[:upper:]' '[:lower:]')
echo "Setting name to ${NEW_NAME}"
echo "Backing up /etc/hostname"
cp /etc/hostname /etc/hostname.bak
echo "Writing new /etc/hostname"
echo ${NEW_NAME} > /etc/hostname
echo "Backing up /etc/hosts"
cp /etc/hosts /etc/hosts.bak
echo "Rewriting /etc/hosts with new values"
sed -i 's/127\.0\.1\.1.*/127.0.1.1\t'${NEW_NAME}'/g' /etc/hosts
echo "Changing hostname in /lib/systemd/system/roscore.env"
sed -i 's/ROS_HOSTNAME=.*/ROS_HOSTNAME='${NEW_NAME}'.local/g' /lib/systemd/system/roscore.env
echo "Changing hostname in .bashrc"
sed -i 's/export ROS_HOSTNAME=.*/export ROS_HOSTNAME='${NEW_NAME}'.local/g' /home/pi/.bashrc
echo "Done, reboot your Clever to see the results"

View File

@@ -38,7 +38,11 @@ echo_stamp() {
echo_stamp "Rename SSID"
NEW_SSID='CLEVER-'$(head -c 100 /dev/urandom | xxd -ps -c 100 | sed -e "s/[^0-9]//g" | cut -c 1-4)
sudo sed -i.OLD "s/CLEVER/${NEW_SSID}/" /etc/wpa_supplicant/wpa_supplicant.conf
clever_rename ${NEW_SSID}
echo_stamp "Rename hostname to $NEW_SSID"
hostnamectl set-hostname $NEW_SSID
sed -i 's/127\.0\.1\.1.*/127.0.1.1\t'${NEW_SSID}' '${NEW_SSID}'.local/g' /etc/hosts
# .local (mdns) hostname added to make it accesable when wlan and ethernet interfaces down
echo_stamp "Harware setup"
/root/hardware_setup.sh

View File

@@ -550,3 +550,6 @@ ddynamic_reconfigure:
realsense2_camera:
debian:
stretch: [ros-kinetic-realsense2-camera]
ros_pytest:
debian:
stretch: [ros-kinetic-ros-pytest]

View File

@@ -1,10 +0,0 @@
ROS_ROOT=/opt/ros/kinetic/share/ros
ROS_DISTRO=kinetic
ROS_PACKAGE_PATH=/home/pi/catkin_ws/src:/opt/ros/kinetic/share
ROS_PORT=11311
ROS_MASTER_URI=http://localhost:11311
CMAKE_PREFIX_PATH=/home/pi/catkin_ws/devel:/opt/ros/kinetic
PATH=/opt/ros/kinetic/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
LD_LIBRARY_PATH=/opt/ros/kinetic/lib
PYTHONPATH=/home/pi/catkin_ws/devel/lib/python2.7/dist-packages:/opt/ros/kinetic/lib/python2.7/dist-packages
ROS_HOSTNAME=raspberrypi.local

View File

@@ -4,8 +4,7 @@ After=network.target
[Service]
User=pi
EnvironmentFile=/lib/systemd/system/roscore.env
ExecStart=/opt/ros/kinetic/bin/roscore
ExecStart=/bin/sh -c ". /opt/ros/kinetic/setup.sh; ROS_HOSTNAME=`hostname`.local exec roscore"
Restart=on-failure
RestartSec=3

View File

@@ -109,16 +109,14 @@ ${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} exec ${SCRIPTS_DIR}'/image-network.
[[ $(arch) == 'armv7l' ]] && NUMBER_THREADS=1 || NUMBER_THREADS=$(nproc --all)
# Clever
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/clever.service' '/lib/systemd/system/'
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/roscore.env' '/lib/systemd/system/'
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/roscore.service' '/lib/systemd/system/'
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/kinetic-rosdep-clever.yaml' '/etc/ros/rosdep/'
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/ros_python_paths' '/etc/sudoers.d/'
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/pigpiod.service' '/lib/systemd/system/'
# ${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/kinetic-ros-clever.rosinstall' '/home/pi/ros_catkin_ws/'
# Add PX4 udev rules
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/99-px4fmu.rules' '/lib/udev/rules.d/'
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${REPO_DIR}'/clever/config/99-px4fmu.rules' '/lib/udev/rules.d/'
# Add rename script
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/clever_rename' '/usr/local/bin/clever_rename'
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} exec ${SCRIPTS_DIR}'/image-ros.sh' ${REPO_URL} ${IMAGE_VERSION} false false ${NUMBER_THREADS}
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} exec ${SCRIPTS_DIR}'/image-validate.sh'

View File

@@ -155,8 +155,6 @@ echo_stamp "Installing CLEVER" \
&& my_travis_retry pip install -r /home/pi/catkin_ws/src/clever/clever/requirements.txt \
&& source /opt/ros/kinetic/setup.bash \
&& catkin_make -j2 -DCMAKE_BUILD_TYPE=Release \
&& catkin_make run_tests \
&& catkin_test_results \
&& systemctl enable roscore \
&& systemctl enable clever \
&& echo_stamp "All CLEVER was installed!" "SUCCESS" \
@@ -171,7 +169,6 @@ gitbook build
echo_stamp "Installing additional ROS packages"
apt-get install -y --no-install-recommends \
ros-kinetic-dynamic-reconfigure \
ros-kinetic-tf2-web-republisher \
ros-kinetic-compressed-image-transport \
ros-kinetic-rosbridge-suite \
ros-kinetic-rosserial \
@@ -181,9 +178,12 @@ apt-get install -y --no-install-recommends \
ros-kinetic-rosshow
# TODO move GeographicLib datasets to Mavros debian package
echo_stamp "Install GeographicLib datasets (needs for mavros)" \
echo_stamp "Install GeographicLib datasets (needed for mavros)" \
&& wget -qO- https://raw.githubusercontent.com/mavlink/mavros/master/mavros/scripts/install_geographiclib_datasets.sh | bash
echo_stamp "Running tests"
catkin_make run_tests && catkin_test_results
echo_stamp "Change permissions for catkin_ws"
chown -Rf pi:pi /home/pi/catkin_ws
@@ -192,7 +192,7 @@ cat << EOF >> /home/pi/.bashrc
LANG='C.UTF-8'
LC_ALL='C.UTF-8'
ROS_DISTRO='kinetic'
export ROS_HOSTNAME='raspberrypi.local'
export ROS_HOSTNAME=\`hostname\`.local
source /opt/ros/kinetic/setup.bash
source /home/pi/catkin_ws/devel/setup.bash
EOF

View File

@@ -63,7 +63,7 @@ echo_stamp "Install apt keys & repos"
curl http://repo.coex.space/aptly_repo_signing.key 2> /dev/null | apt-key add -
apt-get update \
&& apt-get install --no-install-recommends -y -qq dirmngr=2.1.18-8~deb9u4 > /dev/null \
&& apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-key 421C365BD9FF1F717815A3895523BAEEB01FA116
&& apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-key C1CF6E31E6BADE8868B172B4F42ED6FBAB17C654
echo "deb http://packages.ros.org/ros/ubuntu stretch main" > /etc/apt/sources.list.d/ros-latest.list
echo "deb http://repo.coex.space/rpi-ros-kinetic stretch main" > /etc/apt/sources.list.d/rpi-ros-kinetic.list
@@ -88,7 +88,7 @@ lsof=4.89+dfsg-0.1 \
git \
dnsmasq=2.76-5+rpt1+deb9u1 \
tmux=2.3-4 \
vim=2:8.0.0197-4+deb9u1 \
vim \
cmake=3.7.2-1 \
libjpeg8=8d1-2 \
tcpdump \
@@ -108,16 +108,17 @@ ntpdate \
python-dev \
python3-dev \
python-systemd \
mjpg-streamer=2.0 \
&& echo_stamp "Everything was installed!" "SUCCESS" \
|| (echo_stamp "Some packages wasn't installed!" "ERROR"; exit 1)
echo_stamp "Updating kernel to fix camera bug"
apt-get install --no-install-recommends -y \
raspberrypi-kernel=1.20190517-1 \
raspberrypi-bootloader=1.20190517-1 \
libraspberrypi-bin=1.20190517-1 \
libraspberrypi-dev=1.20190517-1 \
libraspberrypi0=1.20190517-1 \
raspberrypi-kernel=1.20190709~stretch-1 \
raspberrypi-bootloader=1.20190709~stretch-1 \
libraspberrypi-bin=1.20190709~stretch-1 \
libraspberrypi-dev=1.20190709~stretch-1 \
libraspberrypi0=1.20190709~stretch-1 \
wireless-regdb=2018.05.09-0~rpt1 \
wpasupplicant=2:2.6-21~bpo9~rpt1

View File

@@ -29,6 +29,7 @@ pigpiod -v
i2cdetect -V
butterfly -h
espeak --version
mjpg_streamer --version
# ros stuff

31
check_files_size.py Executable file
View File

@@ -0,0 +1,31 @@
#!/usr/bin/env python3
import os
import sys
def human_size(num, suffix='B'):
for unit in ['', 'Ki', 'Mi', 'Gi', 'Ti', 'Pi', 'Ei', 'Zi']:
if abs(num) < 1024.0:
return "%3.1f %s%s" % (num, unit, suffix)
num /= 1024.0
return "%.1f %s%s" % (num, 'Yi', suffix)
SIZE_LIMIT = 600 * 1024
EXCLUDE = 'rviz.png', 'ssid.png', 'sitl_docker_demo.png', 'qgc-params.png', 'butterfly.png', \
'Clever main.png', 'fpv_3.png', '1_4.png', 'fpv_4.png', 'detal1.png', 'lockradio.png', \
'qground.png', 'allElements.png', 'download-log.png', 'explosion.png', 'rqt.png', \
'cl3_mountBEC.JPG', 'cl3_mountRpiCamera.JPG'
code = 0
for root, dirs, files in os.walk('docs/'):
for f in files:
if f not in EXCLUDE:
path = os.path.join(root, f)
size = os.path.getsize(path)
if size > SIZE_LIMIT:
print('\x1b[1;31mFile too large ({}): {}\x1b[0m'.format(human_size(size), path), \
file=sys.stderr)
code = 1
sys.exit(code)

View File

@@ -162,8 +162,6 @@ add_executable(rc src/rc.cpp)
add_executable(camera_markers src/camera_markers.cpp)
add_executable(frames src/frames.cpp)
add_executable(vpe_publisher src/vpe_publisher.cpp)
target_link_libraries(simple_offboard
@@ -175,8 +173,6 @@ target_link_libraries(rc ${catkin_LIBRARIES})
target_link_libraries(camera_markers ${catkin_LIBRARIES})
target_link_libraries(frames ${catkin_LIBRARIES})
target_link_libraries(vpe_publisher ${catkin_LIBRARIES})
add_dependencies(simple_offboard clever_generate_messages_cpp)
@@ -231,6 +227,19 @@ target_link_libraries(clever
# DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}
# )
# Only install udev rules when building a Debian package
# FIXME: Other operating systems may have other prefixes
string(FIND ${CMAKE_INSTALL_PREFIX} "/opt/ros" _PREFIX_INDEX)
if (${_PREFIX_INDEX} EQUAL 0)
message(STATUS "Building as a Debian package - adding udev rules as installable files")
install(FILES
config/99-px4fmu.rules
DESTINATION /lib/udev/rules.d
)
else()
message(STATUS "Building in a user workspace - not adding udev rules")
endif()
#############
## Testing ##
#############
@@ -243,3 +252,8 @@ target_link_libraries(clever
## Add folders to be run by python nosetests
# catkin_add_nosetests(test)
if (CATKIN_ENABLE_TESTING)
find_package(rostest REQUIRED)
add_rostest(test/basic.test)
endif()

View File

@@ -22,8 +22,11 @@
<remap from="markers" to="aruco_detect/markers"/>
<param name="map" value="$(find aruco_pose)/map/map.txt"/>
<param name="known_tilt" value="map"/>
<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" unless="$(arg aruco_vpe)"/>
<param name="markers/frame_id" value="aruco_map"/>
<param name="markers/child_frame_id_prefix" value="aruco_"/>
</node>
<!-- vpe publisher from aruco markers -->

View File

@@ -46,11 +46,6 @@
<param name="reference_frames/base_link" value="map"/>
</node>
<!-- Auxiliary frames -->
<node name="frames" pkg="clever" type="frames" output="screen">
<param name="body/frame_id" value="body"/>
</node>
<!-- main camera -->
<include file="$(find clever)/launch/main_camera.launch" if="$(arg main_camera)"/>

View File

@@ -24,6 +24,7 @@
<param name="rate" value="100"/> <!-- poll rate -->
<param name="cv_cap_prop_fps" value="40"/> <!-- camera FPS -->
<param name="capture_delay" value="0.02"/> <!-- approximate delay on frame retrieving -->
<param name="rescale_camera_info" value="true"/> <!-- automatically rescale camera calibration info -->
<!-- camera resolution, NOTE: camera_info file should match it -->
<param name="image_width" value="320"/>

View File

@@ -56,7 +56,7 @@
<node pkg="tf2_ros" type="static_transform_publisher" name="rangefinder_frame" args="0 0 -0.05 0 1.5707963268 0 base_link rangefinder"/>
<!-- Copter visualization -->
<node name="copter_visualization" pkg="mavros_extras" type="copter_visualization" if="$(arg viz)">
<node name="visualization" pkg="mavros_extras" type="visualization" if="$(arg viz)">
<remap to="mavros/local_position/pose" from="local_position"/>
<remap to="mavros/setpoint_position/local" from="local_setpoint"/>
<param name="fixed_frame_id" value="map"/>

View File

@@ -30,13 +30,13 @@
<depend>nodelet</depend>
<depend>mavros</depend>
<depend>mavros_extras</depend>
<depend>lxml</depend>
<depend>cv_camera</depend>
<depend>cv_bridge</depend>
<depend>opencv3</depend>
<depend>mjpg-streamer</depend>
<depend>rosbridge_server</depend>
<depend>web_video_server</depend>
<depend>tf2_web_republisher</depend>
<depend>python-lxml</depend>
<exec_depend>python-pymavlink</exec_depend>
<!-- Use test_depend for packages you need only for testing: -->
<!-- <test_depend>gtest</test_depend> -->

View File

@@ -1,4 +1,4 @@
flask==0.12.3
flask==1.1.1
docopt==0.6.2
geopy==1.11.0
smbus2==0.2.1

View File

@@ -1,63 +0,0 @@
/*
* Auxiliary TF frames for CLEVER drone kit:
* - Body frame (drone body with zero pitch and roll).
* - TODO: REP-0105 `odom` frame emulation: continuous frame without discrete jumps.
* - TODO: Terrain frame (base on ALTITUDE message).
* - TODO: map_upside_down frame
* - TODO: home frame?
*
* Copyright (C) 2018 Copter Express Technologies
*
* Author: Oleg Kalachev <okalachev@gmail.com>
*
* Distributed under MIT License (available at https://opensource.org/licenses/MIT).
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*/
// TODO: consider implementing as a mavros plugin
#include <string>
#include <memory>
#include <ros/ros.h>
#include <tf/transform_datatypes.h>
#include <tf2_ros/transform_broadcaster.h>
#include <geometry_msgs/TransformStamped.h>
#include <geometry_msgs/PoseStamped.h>
using std::string;
static std::shared_ptr<tf2_ros::TransformBroadcaster> br;
static geometry_msgs::TransformStamped body;
inline void publishBody(const geometry_msgs::PoseStamped& pose)
{
// Get only yaw from pose
tf::Quaternion q;
q.setRPY(0, 0, tf::getYaw(pose.pose.orientation));
tf::quaternionTFToMsg(q, body.transform.rotation);
body.transform.translation.x = pose.pose.position.x;
body.transform.translation.y = pose.pose.position.y;
body.transform.translation.z = pose.pose.position.z;
body.header.frame_id = pose.header.frame_id;
body.header.stamp = pose.header.stamp;
br->sendTransform(body);
}
void poseCallback(const geometry_msgs::PoseStamped& pose)
{
publishBody(pose);
}
int main(int argc, char **argv) {
ros::init(argc, argv, "frames");
ros::NodeHandle nh, nh_priv("~");
nh_priv.param<string>("body/frame_id", body.child_frame_id, "body");
br = std::make_shared<tf2_ros::TransformBroadcaster>();
ros::Subscriber pose_sub = nh.subscribe("mavros/local_position/pose", 1, &poseCallback);
ROS_INFO("frames: ready");
ros::spin();
}

View File

@@ -1,4 +1,5 @@
#!/usr/bin/env python
# coding=utf-8
# Copyright (C) 2018 Copter Express Technologies
#
@@ -24,6 +25,7 @@ from sensor_msgs.msg import Image, CameraInfo, NavSatFix, Imu, Range
from mavros_msgs.msg import State, OpticalFlowRad, Mavlink
from mavros_msgs.srv import ParamGet
from geometry_msgs.msg import PoseStamped, TwistStamped, PoseWithCovarianceStamped, Vector3Stamped
from visualization_msgs.msg import MarkerArray as VisualizationMarkerArray
import tf.transformations as t
from aruco_pose.msg import MarkerArray
from mavros import mavlink
@@ -143,6 +145,45 @@ def mavlink_exec(cmd, timeout=3.0):
return mavlink_recv
BOARD_ROTATIONS = {
0: 'no rotation',
1: 'yaw 45°',
2: 'yaw 90°',
3: 'yaw 135°',
4: 'yaw 180°',
5: 'yaw 225°',
6: 'yaw 270°',
7: 'yaw 315°',
8: 'roll 180°',
9: 'roll 180°, yaw 45°',
10: 'roll 180°, yaw 90°',
11: 'roll 180°, yaw 135°',
12: 'pitch 180°',
13: 'roll 180°, yaw 225°',
14: 'roll 180°, yaw 270°',
15: 'roll 180°, yaw 315°',
16: 'roll 90°',
17: 'roll 90°, yaw 45°',
18: 'roll 90°, yaw 90°',
19: 'roll 90°, yaw 135°',
20: 'roll 270°',
21: 'roll 270°, yaw 45°',
22: 'roll 270°, yaw 90°',
23: 'roll 270°, yaw 135°',
24: 'pitch 90°',
25: 'pitch 270°',
26: 'roll 270°, yaw 270°',
27: 'roll 180°, pitch 270°',
28: 'pitch 90°, yaw 180',
29: 'pitch 90°, roll 90°',
30: 'yaw 293°, pitch 68°, roll 90°',
31: 'pitch 90°, roll 270°',
32: 'pitch 9°, yaw 180°',
33: 'pitch 45°',
34: 'pitch 315°',
}
@check('FCU')
def check_fcu():
try:
@@ -188,6 +229,13 @@ def check_fcu():
else:
failure('unknown selected estimator: %s', est)
rot = get_param('SENS_BOARD_ROT')
if rot is not None:
try:
info('board rotation: %s', BOARD_ROTATIONS[rot])
except KeyError:
failure('unknown board rotation %s', rot)
except rospy.ROSException:
failure('no MAVROS state (check wiring)')
@@ -239,7 +287,7 @@ def check_camera(name):
if not optical or not cable:
info('%s: custom camera orientation detected', name)
else:
info('camera is oriented %s, camera cable goes %s', optical, cable)
info('camera is oriented %s, camera cable goes %s', optical, cable)
except tf2_ros.TransformException:
failure('cannot transform from base_link to camera frame')
@@ -290,6 +338,13 @@ def check_aruco():
elif known_tilt == 'map_flipped':
known_tilt += ' (marker\'s map is on the ceiling)'
info('aruco_map/known_tilt = %s', known_tilt)
try:
visualization = rospy.wait_for_message('aruco_map/visualization', VisualizationMarkerArray, timeout=1)
info('map has %s markers', len(visualization.markers))
except:
failure('cannot read aruco_map/visualization topic')
try:
rospy.wait_for_message('aruco_map/pose', PoseWithCovarianceStamped, timeout=1)
except rospy.ROSException:
@@ -342,8 +397,8 @@ def check_vpe():
if delay != 0:
failure('EKF2_EV_DELAY is %.2f, but it should be zero', delay)
info('EKF2_EVA_NOISE is %.3f, EKF2_EVP_NOISE is %.3f',
get_param('EKF2_EVA_NOISE'),
get_param('EKF2_EVP_NOISE'))
get_param('EKF2_EVA_NOISE'),
get_param('EKF2_EVP_NOISE'))
if not vis:
return
@@ -551,10 +606,16 @@ def check_cpu_usage():
@check('clever.service')
def check_clever_service():
output = subprocess.check_output('systemctl show -p ActiveState --value clever.service'.split())
try:
output = subprocess.check_output('systemctl show -p ActiveState --value clever.service'.split(),
stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as e:
failure('systemctl returned %s: %s', e.returncode, e.output)
return
if 'inactive' in output:
failure('clever.service is not running, try sudo systemctl restart clever')
return
j = journal.Reader()
j.this_boot()
j.add_match(_SYSTEMD_UNIT='clever.service')
@@ -577,7 +638,10 @@ def check_clever_service():
@check('Image')
def check_image():
info('version: %s', open('/etc/clever_version').read().strip())
try:
info('version: %s', open('/etc/clever_version').read().strip())
except IOError:
info('no /etc/clever_version file, not the Clever image?')
@check('Preflight status')
@@ -586,7 +650,7 @@ def check_preflight_status():
mavlink_exec('\n')
cmdr_output = mavlink_exec('commander check')
if cmdr_output == '':
failure('No data from FCU')
failure('no data from FCU')
return
cmdr_lines = cmdr_output.split('\n')
r = re.compile(r'^(.*)(Preflight|Prearm) check: (.*)')

View File

@@ -54,6 +54,7 @@ using mavros_msgs::Thrust;
// tf2
tf2_ros::Buffer tf_buffer;
std::shared_ptr<tf2_ros::TransformBroadcaster> transform_broadcaster;
// Parameters
string local_frame;
@@ -88,6 +89,7 @@ AttitudeTarget att_raw_msg;
Thrust thrust_msg;
TwistStamped rates_msg;
TransformStamped target;
geometry_msgs::TransformStamped body;
// State
PoseStamped nav_start;
@@ -121,18 +123,41 @@ TwistStamped velocity;
NavSatFix global_position;
BatteryState battery;
// Common subcriber callback template that stores message to the variable
// Common subscriber callback template that stores message to the variable
template<typename T, T& STORAGE>
void handleMessage(const T& msg)
{
STORAGE = msg;
}
inline void publishBodyFrame()
{
if (body.child_frame_id.empty()) return;
tf::Quaternion q;
q.setRPY(0, 0, tf::getYaw(local_position.pose.orientation));
tf::quaternionTFToMsg(q, body.transform.rotation);
body.transform.translation.x = local_position.pose.position.x;
body.transform.translation.y = local_position.pose.position.y;
body.transform.translation.z = local_position.pose.position.z;
body.header.frame_id = local_position.header.frame_id;
body.header.stamp = local_position.header.stamp;
transform_broadcaster->sendTransform(body);
}
void handleLocalPosition(const PoseStamped& pose)
{
local_position = pose;
publishBodyFrame();
// TODO: terrain?, home?
}
// wait for transform without interrupting publishing setpoints
inline bool waitTransform(const string& target, const string& source,
const ros::Time& stamp, const ros::Duration& timeout)
const ros::Time& stamp, const ros::Duration& timeout) // editorconfig-checker-disable-line
{
ros::Rate r(10);
ros::Rate r(100);
auto start = ros::Time::now();
while (ros::ok()) {
if (ros::Time::now() - start > timeout) return false;
@@ -176,31 +201,29 @@ bool getTelemetry(GetTelemetry::Request& req, GetTelemetry::Response& res)
res.mode = state.mode;
}
waitTransform(local_frame, req.frame_id, stamp, telemetry_transform_timeout);
try {
waitTransform(req.frame_id, fcu_frame, stamp, telemetry_transform_timeout);
auto transform = tf_buffer.lookupTransform(req.frame_id, fcu_frame, stamp);
res.x = transform.transform.translation.x;
res.y = transform.transform.translation.y;
res.z = transform.transform.translation.z;
if (!TIMEOUT(local_position, local_position_timeout)) {
try {
// transform pose
PoseStamped pose;
tf_buffer.transform(local_position, pose, req.frame_id);
res.x = pose.pose.position.x;
res.y = pose.pose.position.y;
res.z = pose.pose.position.z;
// Tait-Bryan angles, order z-y-x
double yaw, pitch, roll;
tf2::getEulerYPR(pose.pose.orientation, yaw, pitch, roll);
res.yaw = yaw;
res.pitch = pitch;
res.roll = roll;
} catch (const tf2::TransformException& e) {}
double yaw, pitch, roll;
tf2::getEulerYPR(transform.transform.rotation, yaw, pitch, roll);
res.yaw = yaw;
res.pitch = pitch;
res.roll = roll;
} catch (const tf2::TransformException& e) {
ROS_DEBUG("simple_offboard: %s", e.what());
}
if (!TIMEOUT(velocity, velocity_timeout)) {
try {
// transform velocity
waitTransform(req.frame_id, fcu_frame, velocity.header.stamp, telemetry_transform_timeout);
Vector3Stamped vec, vec_out;
vec.header = velocity.header;
vec.header.stamp = velocity.header.stamp;
vec.header.frame_id = velocity.header.frame_id;
vec.vector = velocity.twist.linear;
tf_buffer.transform(vec, vec_out, req.frame_id);
@@ -326,6 +349,10 @@ PoseStamped globalToLocal(double lat, double lon)
x_offset = distance * sin(azimuth_radians);
y_offset = distance * cos(azimuth_radians);
if (!waitTransform(local_frame, fcu_frame, global_position.header.stamp, ros::Duration(0.2))) {
throw std::runtime_error("No local position");
}
auto local = tf_buffer.lookupTransform(local_frame, fcu_frame, global_position.header.stamp);
PoseStamped pose;
@@ -364,13 +391,12 @@ void publish(const ros::Time stamp)
if (!target.child_frame_id.empty()) {
if (setpoint_type == NAVIGATE || setpoint_type == NAVIGATE_GLOBAL || setpoint_type == POSITION) {
static tf2_ros::TransformBroadcaster tf_broadcaster;
target.header = setpoint_position_transformed.header;
target.transform.translation.x = setpoint_position_transformed.pose.position.x;
target.transform.translation.y = setpoint_position_transformed.pose.position.y;
target.transform.translation.z = setpoint_position_transformed.pose.position.z;
target.transform.rotation = setpoint_position_transformed.pose.orientation;
tf_broadcaster.sendTransform(target);
transform_broadcaster->sendTransform(target);
}
}
@@ -455,10 +481,12 @@ inline void checkState()
throw std::runtime_error("No connection to FCU, https://clever.copterexpress.com/connection.html");
}
#define ENSURE_FINITE(var) { if (!std::isfinite(var)) throw std::runtime_error(#var " argument cannot be NaN or Inf"); }
bool serve(enum setpoint_type_t sp_type, float x, float y, float z, float vx, float vy, float vz,
float pitch, float roll, float yaw, float pitch_rate, float roll_rate, float yaw_rate,
float lat, float lon, float thrust, float speed, string frame_id, bool auto_arm,
uint8_t& success, string& message)
float pitch, float roll, float yaw, float pitch_rate, float roll_rate, float yaw_rate, // editorconfig-checker-disable-line
float lat, float lon, float thrust, float speed, string frame_id, bool auto_arm, // editorconfig-checker-disable-line
uint8_t& success, string& message) // editorconfig-checker-disable-line
{
auto stamp = ros::Time::now();
@@ -466,6 +494,20 @@ bool serve(enum setpoint_type_t sp_type, float x, float y, float z, float vx, fl
if (busy)
throw std::runtime_error("Busy");
ENSURE_FINITE(x);
ENSURE_FINITE(y);
ENSURE_FINITE(z);
ENSURE_FINITE(vx);
ENSURE_FINITE(vy);
ENSURE_FINITE(vz);
ENSURE_FINITE(pitch);
ENSURE_FINITE(roll);
ENSURE_FINITE(pitch_rate);
ENSURE_FINITE(roll_rate);
ENSURE_FINITE(lat);
ENSURE_FINITE(lon);
ENSURE_FINITE(thrust);
busy = true;
// Checks
@@ -515,7 +557,9 @@ bool serve(enum setpoint_type_t sp_type, float x, float y, float z, float vx, fl
if (sp_type == NAVIGATE_GLOBAL) {
// Calculate x and from lat and lot in request's frame
auto xy_in_req_frame = tf_buffer.transform(globalToLocal(lat, lon), frame_id);
auto pose_local = globalToLocal(lat, lon);
pose_local.header.stamp = stamp; // TODO: fix
auto xy_in_req_frame = tf_buffer.transform(pose_local, frame_id);
x = xy_in_req_frame.pose.position.x;
y = xy_in_req_frame.pose.position.y;
}
@@ -689,6 +733,7 @@ int main(int argc, char **argv)
ros::NodeHandle nh, nh_priv("~");
tf2_ros::TransformListener tf_listener(tf_buffer);
transform_broadcaster = std::make_shared<tf2_ros::TransformBroadcaster>();
// Params
nh.param<string>("mavros/local_position/tf/frame_id", local_frame, "map");
@@ -697,6 +742,7 @@ int main(int argc, char **argv)
nh_priv.param("auto_release", auto_release, true);
nh_priv.param("land_only_in_offboard", land_only_in_offboard, true);
nh_priv.param("default_speed", default_speed, 0.5f);
nh_priv.param<string>("body_frame", body.child_frame_id, "body");
nh_priv.getParam("reference_frames", reference_frames);
state_timeout = ros::Duration(nh_priv.param("state_timeout", 3.0));
@@ -717,11 +763,11 @@ int main(int argc, char **argv)
// Telemetry subscribers
auto state_sub = nh.subscribe("mavros/state", 1, &handleMessage<mavros_msgs::State, state>);
auto local_position_sub = nh.subscribe("mavros/local_position/pose", 1, &handleMessage<PoseStamped, local_position>);
auto velocity_sub = nh.subscribe("mavros/local_position/velocity", 1, &handleMessage<TwistStamped, velocity>);
auto velocity_sub = nh.subscribe("mavros/local_position/velocity_body", 1, &handleMessage<TwistStamped, velocity>);
auto global_position_sub = nh.subscribe("mavros/global_position/global", 1, &handleMessage<NavSatFix, global_position>);
auto battery_sub = nh.subscribe("mavros/battery", 1, &handleMessage<BatteryState, battery>);
auto statustext_sub = nh.subscribe("mavros/statustext/recv", 1, &handleMessage<mavros_msgs::StatusText, statustext>);
auto local_position_sub = nh.subscribe("mavros/local_position/pose", 1, &handleLocalPosition);
// Setpoint publishers
position_pub = nh.advertise<PoseStamped>("mavros/setpoint_position/local", 1);

29
clever/test/basic.py Executable file
View File

@@ -0,0 +1,29 @@
#!/usr/bin/env python
import rospy
import pytest
from mavros_msgs.msg import State
@pytest.fixture()
def node():
return rospy.init_node('clever_test', anonymous=True)
def test_state(node):
state = rospy.wait_for_message('mavros/state', State, timeout=10)
assert state.connected == False
assert state.armed == False
assert state.guided == False
assert state.mode == ''
def test_simple_offboard_services_available():
rospy.wait_for_service('get_telemetry', timeout=5)
rospy.wait_for_service('navigate', timeout=5)
rospy.wait_for_service('navigate_global', timeout=5)
rospy.wait_for_service('set_position', timeout=5)
rospy.wait_for_service('set_velocity', timeout=5)
rospy.wait_for_service('set_attitude', timeout=5)
rospy.wait_for_service('set_rates', timeout=5)
rospy.wait_for_service('land', timeout=5)
def test_web_video_server(node):
import urllib2
urllib2.urlopen("http://localhost:8080").read()

37
clever/test/basic.test Executable file
View File

@@ -0,0 +1,37 @@
<launch>
<!-- Verify all the required nodes basically work -->
<node pkg="mavros" type="mavros_node" name="mavros" required="true" output="screen">
<param name="fcu_url" value="udp://@127.0.1:14557"/>
<rosparam command="load" file="$(find clever)/launch/mavros_config.yaml"/>
</node>
<node name="visualization" pkg="mavros_extras" type="visualization" required="true">
<remap to="mavros/local_position/pose" from="local_position"/>
<remap to="mavros/setpoint_position/local" from="local_setpoint"/>
<param name="fixed_frame_id" value="map"/>
<param name="child_frame_id" value="base_link"/>
<param name="marker_scale" value="1"/>
<param name="max_track_size" value="20"/>
<param name="num_rotors" value="4"/>
</node>
<node name="web_video_server" pkg="web_video_server" type="web_video_server" required="true" output="screen">
<param name="default_stream_type" value="ros_compressed"/>
<param name="publish_rate" value="1.0"/>
</node>
<node pkg="tf2_ros" type="static_transform_publisher" name="map_flipped_frame" args="0 0 0 3.1415926 3.1415926 0 map map_flipped" required="true"/>
<node name="simple_offboard" pkg="clever" type="simple_offboard" required="true" output="screen">
<param name="reference_frames/body" value="map"/>
<param name="reference_frames/base_link" value="map"/>
</node>
<node name="tf2_web_republisher" pkg="tf2_web_republisher" type="tf2_web_republisher" required="true"/>
<node name="rc" pkg="clever" type="rc" required="true" output="screen"/>
<param name="test_module" value="$(find clever)/test/basic.py"/>
<test test-name="basic_test" pkg="ros_pytest" type="ros_pytest_runner"/>
</launch>

197
clever/test/sitl.py Normal file
View File

@@ -0,0 +1,197 @@
#!/usr/bin/env python
import math
import rospy
import pytest
from pytest import approx
from numpy import isfinite
from geometry_msgs.msg import PoseStamped
from mavros_msgs.msg import State
from sensor_msgs.msg import NavSatFix
from clever import srv
from std_srvs.srv import Trigger
import tf2_ros
import tf2_geometry_msgs
def roughly(expected):
return approx(expected, abs=1e-1)
def very_roughly(expected):
return approx(expected, abs=1)
@pytest.fixture()
def node():
return rospy.init_node('clever_test', anonymous=True)
def wait_service(name, service_class):
res = rospy.ServiceProxy(name, service_class)
res.wait_for_service(5)
return res
@pytest.fixture
def get_telemetry():
return wait_service('get_telemetry', srv.GetTelemetry)
@pytest.fixture
def navigate():
return wait_service('navigate', srv.Navigate)
@pytest.fixture
def navigate():
res = rospy.ServiceProxy('navigate', srv.Navigate)
res.wait_for_service(5)
return res
@pytest.fixture
def land():
return wait_service('land', Trigger)
@pytest.fixture
def tf_buffer():
buf = tf2_ros.Buffer()
tf2_ros.TransformListener(buf)
return buf
def wait_connection():
start = rospy.get_rostime()
while rospy.get_rostime() - start <= rospy.Duration(15):
state = rospy.wait_for_message('mavros/state', State, timeout=10)
if state.connected:
return True
assert False, "no connection to SITL"
def minimal_rate(func, rate):
start = rospy.get_rostime()
i = 0
while rospy.get_rostime() - start <= rospy.Duration(2):
func()
i += 1
result_rate = i / 2
assert result_rate >= rate, 'Rate too low: %s Hz' % result_rate
def test_state_initial(node):
wait_connection()
state = rospy.wait_for_message('mavros/state', State, timeout=10)
assert state.connected == True
assert state.armed == False
def test_telem_initial(node, get_telemetry):
# wait for local position
rospy.wait_for_message('mavros/local_position/pose', PoseStamped, timeout=15)
rospy.wait_for_message('mavros/global_position/global', NavSatFix, timeout=30)
telem = get_telemetry()
assert telem.frame_id == 'map'
assert telem.connected == True
assert telem.armed == False
assert telem.mode != ''
assert telem.x == roughly(0.0)
assert telem.y == roughly(0.0)
assert telem.z == roughly(0.0)
assert telem.lat == approx(47.397742)
assert telem.lon == approx(8.545594)
assert telem.vx == roughly(0.0)
assert telem.vy == roughly(0.0)
assert telem.vz == roughly(0.0)
assert telem.pitch == roughly(0.0)
assert telem.roll == roughly(0.0)
assert telem.yaw == roughly(1.56)
assert isfinite(telem.voltage)
assert isfinite(telem.cell_voltage)
def test_telem_rate(node, get_telemetry):
minimal_rate(lambda: get_telemetry(), 20)
minimal_rate(lambda: get_telemetry(frame_id='body'), 20)
minimal_rate(lambda: get_telemetry(frame_id='base_link'), 200)
def test_takeoff_without_auto_arm(node, navigate):
res = navigate(z=2, frame_id='body')
assert res.success == False
assert res.message == 'Copter is not in OFFBOARD mode, use auto_arm?'
def test_takeoff(node, navigate, get_telemetry, tf_buffer):
res = navigate(z=2, speed=1, frame_id='body', auto_arm=True)
assert res.success == True, res.message
rospy.sleep(5)
telem = get_telemetry()
assert telem.z == very_roughly(2.0)
assert telem.x == very_roughly(0.0)
assert telem.y == very_roughly(0.0)
assert telem.pitch == roughly(0.0)
assert telem.roll == roughly(0.0)
assert telem.yaw == roughly(1.56)
def test_navigate_nans(node, navigate):
res = navigate(x=float('nan'))
assert res.success == False
assert res.message == 'x argument cannot be NaN or Inf'
res = navigate(y=float('nan'))
assert res.success == False
assert res.message == 'y argument cannot be NaN or Inf'
res = navigate(z=float('nan'))
assert res.success == False
assert res.message == 'z argument cannot be NaN or Inf'
res = navigate(x=float('inf'))
assert res.success == False
assert res.message == 'x argument cannot be NaN or Inf'
res = navigate(y=float('inf'))
assert res.success == False
assert res.message == 'y argument cannot be NaN or Inf'
res = navigate(z=float('inf'))
assert res.success == False
assert res.message == 'z argument cannot be NaN or Inf'
res = navigate(x=float('-inf'))
assert res.success == False
assert res.message == 'x argument cannot be NaN or Inf'
res = navigate(y=float('-inf'))
assert res.success == False
assert res.message == 'y argument cannot be NaN or Inf'
res = navigate(z=float('-inf'))
assert res.success == False
assert res.message == 'z argument cannot be NaN or Inf'
def test_navigate(node, navigate, get_telemetry, tf_buffer):
res = navigate(x=1, y=2, z=3, speed=1)
assert res.success == True, res.message
nav_target = tf_buffer.lookup_transform('map', 'navigate_target', rospy.get_rostime(), rospy.Duration(0.2))
assert nav_target.transform.translation.x == approx(1)
assert nav_target.transform.translation.y == approx(2)
assert nav_target.transform.translation.z == approx(3)
rospy.sleep(10)
telem = get_telemetry()
assert telem.x == very_roughly(1.0)
assert telem.y == very_roughly(2.0)
assert telem.z == very_roughly(3.0)
assert telem.pitch == roughly(0.0)
assert telem.roll == roughly(0.0)
assert telem.yaw == roughly(0.0)
res = navigate(x=-1, y=-2, z=-1, frame_id='body', speed=1)
assert res.success == True, res.message
nav_target = tf_buffer.lookup_transform('map', 'navigate_target', rospy.get_rostime(), rospy.Duration(0.2))
assert nav_target.transform.translation.x == very_roughly(0)
assert nav_target.transform.translation.y == very_roughly(0)
assert nav_target.transform.translation.z == very_roughly(2)
rospy.sleep(10)
telem = get_telemetry()
assert telem.x == very_roughly(0)
assert telem.y == very_roughly(0)
assert telem.z == very_roughly(2)
assert telem.pitch == roughly(0)
assert telem.roll == roughly(0)
assert telem.yaw == roughly(0)
# TODO
# test navigate_global, set_velocity, set_attitude, set_rates
def test_land(node, get_telemetry, land):
res = land()
assert res.success, res.message
telem = get_telemetry()
assert telem.mode == 'AUTO.LAND'
assert telem.armed == True, 'Drone unexpectedly disarmed while landing'
rospy.sleep(12)
telem = get_telemetry()
assert telem.mode == 'AUTO.LAND'
assert telem.armed == False, 'Drone is not disarmed after landing'

20
clever/test/sitl.test Normal file
View File

@@ -0,0 +1,20 @@
<launch>
<arg name="ip" default="127.0.0.1"/>
<!-- mavros -->
<include file="$(find clever)/launch/mavros.launch">
<arg name="fcu_conn" value="udp"/>
<arg name="fcu_ip" value="$(arg ip)"/>
<arg name="gcs_bridge" value="false"/>
<arg name="viz" value="false"/>
<arg name="respawn" default="false"/>
</include>
<node name="simple_offboard" pkg="clever" type="simple_offboard" required="true" output="screen">
<param name="reference_frames/body" value="map"/>
<param name="reference_frames/base_link" value="map"/>
</node>
<param name="test_module" value="$(find clever)/test/sitl.py"/>
<test test-name="basic_test" pkg="ros_pytest" type="ros_pytest_runner" time-limit="120.0"/>
</launch>

View File

@@ -3,7 +3,7 @@ var titleEl = document.querySelector('title');
var modeEl = document.querySelector('.mode');
var batteryEl = document.querySelector('.battery');
var url = 'ws://' + location.host + ':9090';
var url = 'ws://' + location.hostname + ':9090';
var ros = new ROSLIB.Ros({ url: url });
function speak(txt) {

View File

@@ -1,5 +1,5 @@
var ros = new ROSLIB.Ros({
url : 'ws://' + location.host + ':9090'
url : 'ws://' + location.hostname + ':9090'
});
var titleEl = document.querySelector('title');

BIN
docs/assets/3d_drone_1.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 259 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.4 MiB

View File

@@ -1,735 +0,0 @@
# Onboard parameters for Vehicle 1
#
# Stack: PX4 Pro
# Vehicle: Multi-Rotor
# Version: 1.7.0
# Git Revision: cbdb08bb6109c1ea
#
# Vehicle-Id Component-Id Name Value Type
1 1 ATT_ACC_COMP 1 6
1 1 ATT_BIAS_MAX 0.050000000745058060 9
1 1 ATT_EXT_HDG_M 0 6
1 1 ATT_MAG_DECL 0.000000000000000000 9
1 1 ATT_MAG_DECL_A 1 6
1 1 ATT_W_ACC 0.200000002980232239 9
1 1 ATT_W_EXT_HDG 0.100000001490116119 9
1 1 ATT_W_GYRO_BIAS 0.100000001490116119 9
1 1 ATT_W_MAG 0.100000001490116119 9
1 1 BAT_A_PER_V 15.391030311584472656 9
1 1 BAT_CAPACITY -1.000000000000000000 9
1 1 BAT_CNT_V_CURR 0.000805664050858468 9
1 1 BAT_CNT_V_VOLT 0.000805664050858468 9
1 1 BAT_CRIT_THR 0.070000000298023224 9
1 1 BAT_EMERGEN_THR 0.050000000745058060 9
1 1 BAT_LOW_THR 0.150000005960464478 9
1 1 BAT_N_CELLS 4 6
1 1 BAT_R_INTERNAL -1.000000000000000000 9
1 1 BAT_SOURCE 0 6
1 1 BAT_V_CHARGED 4.199999809265136719 9
1 1 BAT_V_DIV 10.887768745422363281 9
1 1 BAT_V_EMPTY 3.400000095367431641 9
1 1 BAT_V_LOAD_DROP 0.300000011920928955 9
1 1 BAT_V_OFFS_CURR 0.000000000000000000 9
1 1 CAL_ACC0_EN 1 6
1 1 CAL_ACC0_ID 1246218 6
1 1 CAL_ACC0_XOFF 0.081070423126220703 9
1 1 CAL_ACC0_XSCALE 0.996176421642303467 9
1 1 CAL_ACC0_YOFF -0.182177066802978516 9
1 1 CAL_ACC0_YSCALE 1.000774979591369629 9
1 1 CAL_ACC0_ZOFF 0.506614208221435547 9
1 1 CAL_ACC0_ZSCALE 0.991645038127899170 9
1 1 CAL_ACC1_EN 1 6
1 1 CAL_ACC1_ID 1114634 6
1 1 CAL_ACC1_XOFF 0.864228248596191406 9
1 1 CAL_ACC1_XSCALE 1.004844427108764648 9
1 1 CAL_ACC1_YOFF 0.980560302734375000 9
1 1 CAL_ACC1_YSCALE 0.980703771114349365 9
1 1 CAL_ACC1_ZOFF 0.873121738433837891 9
1 1 CAL_ACC1_ZSCALE 0.989114820957183838 9
1 1 CAL_ACC2_XOFF 0.000000000000000000 9
1 1 CAL_ACC2_XSCALE 1.000000000000000000 9
1 1 CAL_ACC2_YOFF 0.000000000000000000 9
1 1 CAL_ACC2_YSCALE 1.000000000000000000 9
1 1 CAL_ACC2_ZOFF 0.000000000000000000 9
1 1 CAL_ACC2_ZSCALE 1.000000000000000000 9
1 1 CAL_AIR_CMODEL 0 6
1 1 CAL_AIR_TUBED_MM 1.500000000000000000 9
1 1 CAL_AIR_TUBELEN 0.200000002980232239 9
1 1 CAL_GYRO0_EN 1 6
1 1 CAL_GYRO0_ID 2163722 6
1 1 CAL_GYRO0_XOFF 0.013700588606297970 9
1 1 CAL_GYRO0_XSCALE 1.000000000000000000 9
1 1 CAL_GYRO0_YOFF 0.049494847655296326 9
1 1 CAL_GYRO0_YSCALE 1.000000000000000000 9
1 1 CAL_GYRO0_ZOFF -0.032555881887674332 9
1 1 CAL_GYRO0_ZSCALE 1.000000000000000000 9
1 1 CAL_GYRO1_EN 1 6
1 1 CAL_GYRO1_ID 2228490 6
1 1 CAL_GYRO1_XOFF -0.007464688271284103 9
1 1 CAL_GYRO1_XSCALE 1.000000000000000000 9
1 1 CAL_GYRO1_YOFF 0.008172192610800266 9
1 1 CAL_GYRO1_YSCALE 1.000000000000000000 9
1 1 CAL_GYRO1_ZOFF -0.010663406923413277 9
1 1 CAL_GYRO1_ZSCALE 1.000000000000000000 9
1 1 CAL_GYRO2_XOFF 0.000000000000000000 9
1 1 CAL_GYRO2_XSCALE 1.000000000000000000 9
1 1 CAL_GYRO2_YOFF 0.000000000000000000 9
1 1 CAL_GYRO2_YSCALE 1.000000000000000000 9
1 1 CAL_GYRO2_ZOFF 0.000000000000000000 9
1 1 CAL_GYRO2_ZSCALE 1.000000000000000000 9
1 1 CAL_MAG0_EN 1 6
1 1 CAL_MAG0_ID 131594 6
1 1 CAL_MAG0_ROT -1 6
1 1 CAL_MAG0_XOFF -0.020210459828376770 9
1 1 CAL_MAG0_XSCALE 1.033145427703857422 9
1 1 CAL_MAG0_YOFF 0.100117556750774384 9
1 1 CAL_MAG0_YSCALE 1.017574071884155273 9
1 1 CAL_MAG0_ZOFF -0.113783776760101318 9
1 1 CAL_MAG0_ZSCALE 0.933741152286529541 9
1 1 CAL_MAG1_ID 0 6
1 1 CAL_MAG1_ROT -1 6
1 1 CAL_MAG1_XOFF 0.000000000000000000 9
1 1 CAL_MAG1_XSCALE 1.000000000000000000 9
1 1 CAL_MAG1_YOFF 0.000000000000000000 9
1 1 CAL_MAG1_YSCALE 1.000000000000000000 9
1 1 CAL_MAG1_ZOFF 0.000000000000000000 9
1 1 CAL_MAG1_ZSCALE 1.000000000000000000 9
1 1 CAL_MAG2_ID 0 6
1 1 CAL_MAG2_ROT -1 6
1 1 CAL_MAG2_XOFF 0.000000000000000000 9
1 1 CAL_MAG2_XSCALE 1.000000000000000000 9
1 1 CAL_MAG2_YOFF 0.000000000000000000 9
1 1 CAL_MAG2_YSCALE 1.000000000000000000 9
1 1 CAL_MAG2_ZOFF 0.000000000000000000 9
1 1 CAL_MAG2_ZSCALE 1.000000000000000000 9
1 1 CAL_MAG3_ID 0 6
1 1 CAL_MAG3_ROT -1 6
1 1 CAL_MAG3_XOFF 0.000000000000000000 9
1 1 CAL_MAG3_XSCALE 1.000000000000000000 9
1 1 CAL_MAG3_YOFF 0.000000000000000000 9
1 1 CAL_MAG3_YSCALE 1.000000000000000000 9
1 1 CAL_MAG3_ZOFF 0.000000000000000000 9
1 1 CAL_MAG3_ZSCALE 1.000000000000000000 9
1 1 CAL_MAG_SIDES 63 6
1 1 CBRK_AIRSPD_CHK 0 6
1 1 CBRK_ENGINEFAIL 284953 6
1 1 CBRK_FLIGHTTERM 121212 6
1 1 CBRK_GPSFAIL 240024 6
1 1 CBRK_IO_SAFETY 22027 6
1 1 CBRK_RATE_CTRL 0 6
1 1 CBRK_SUPPLY_CHK 894281 6
1 1 CBRK_USB_CHK 197848 6
1 1 CBRK_VELPOSERR 0 6
1 1 COM_ARM_AUTH 256010 6
1 1 COM_ARM_EKF_AB 0.004999999888241291 9
1 1 COM_ARM_EKF_GB 0.000869999988935888 9
1 1 COM_ARM_EKF_HGT 1.000000000000000000 9
1 1 COM_ARM_EKF_POS 0.500000000000000000 9
1 1 COM_ARM_EKF_VEL 0.500000000000000000 9
1 1 COM_ARM_EKF_YAW 0.500000000000000000 9
1 1 COM_ARM_IMU_ACC 0.699999988079071045 9
1 1 COM_ARM_IMU_GYR 0.200000002980232239 9
1 1 COM_ARM_MIS_REQ 0 6
1 1 COM_ARM_SWISBTN 0 6
1 1 COM_ARM_WO_GPS 1 6
1 1 COM_DISARM_LAND 1 6
1 1 COM_DL_LOSS_T 10 6
1 1 COM_DL_REG_T 0 6
1 1 COM_EF_C2T 5.000000000000000000 9
1 1 COM_EF_THROT 0.500000000000000000 9
1 1 COM_EF_TIME 10.000000000000000000 9
1 1 COM_FLIGHT_UUID 34 6
1 1 COM_FLTMODE1 8 6
1 1 COM_FLTMODE2 -1 6
1 1 COM_FLTMODE3 -1 6
1 1 COM_FLTMODE4 1 6
1 1 COM_FLTMODE5 -1 6
1 1 COM_FLTMODE6 2 6
1 1 COM_HOME_H_T 5.000000000000000000 9
1 1 COM_HOME_V_T 10.000000000000000000 9
1 1 COM_LOW_BAT_ACT 2 6
1 1 COM_OBL_ACT 0 6
1 1 COM_OBL_RC_ACT 0 6
1 1 COM_OF_LOSS_T 0.000000000000000000 9
1 1 COM_POSCTL_NAVL 0 6
1 1 COM_POS_FS_DELAY 1 6
1 1 COM_POS_FS_GAIN 10 6
1 1 COM_POS_FS_PROB 30 6
1 1 COM_RC_ARM_HYST 1000 6
1 1 COM_RC_IN_MODE 0 6
1 1 COM_RC_LOSS_T 0.500000000000000000 9
1 1 COM_RC_OVERRIDE 0 6
1 1 COM_RC_STICK_OV 12.000000000000000000 9
1 1 FW_CLMBOUT_DIFF 10.000000000000000000 9
1 1 FW_MAN_P_SC 1.000000000000000000 9
1 1 FW_MAN_R_SC 1.000000000000000000 9
1 1 FW_MAN_Y_SC 1.000000000000000000 9
1 1 GF_ACTION 1 6
1 1 GF_ALTMODE 0 6
1 1 GF_COUNT -1 6
1 1 GF_MAX_HOR_DIST 0.000000000000000000 9
1 1 GF_MAX_VER_DIST 0.000000000000000000 9
1 1 GF_SOURCE 0 6
1 1 GPS_DUMP_COMM 0 6
1 1 GPS_UBX_DYNMODEL 7 6
1 1 IMU_ACCEL_CUTOFF 30.000000000000000000 9
1 1 IMU_GYRO_CUTOFF 30.000000000000000000 9
1 1 LED_RGB_MAXBRT 15 6
1 1 LNDMC_ALT_MAX 10000.000000000000000000 9
1 1 LNDMC_FFALL_THR 2.000000000000000000 9
1 1 LNDMC_FFALL_TTRI 0.300000011920928955 9
1 1 LNDMC_ROT_MAX 20.000000000000000000 9
1 1 LNDMC_THR_RANGE 0.100000001490116119 9
1 1 LNDMC_XY_VEL_MAX 1.500000000000000000 9
1 1 LNDMC_Z_VEL_MAX 0.500000000000000000 9
1 1 LND_FLIGHT_T_HI 0 6
1 1 LND_FLIGHT_T_LO 959960540 6
1 1 LPE_ACC_XY 0.012000000104308128 9
1 1 LPE_ACC_Z 0.019999999552965164 9
1 1 LPE_BAR_Z 3.000000000000000000 9
1 1 LPE_EPH_MAX 3.000000000000000000 9
1 1 LPE_EPV_MAX 5.000000000000000000 9
1 1 LPE_FAKE_ORIGIN 1 6
1 1 LPE_FGYRO_HP 0.001000000047497451 9
1 1 LPE_FLW_OFF_Z 0.000000000000000000 9
1 1 LPE_FLW_QMIN 150 6
1 1 LPE_FLW_R 7.000000000000000000 9
1 1 LPE_FLW_RR 7.000000000000000000 9
1 1 LPE_FLW_SCALE 1.299999952316284180 9
1 1 LPE_FUSION 28 6
1 1 LPE_GPS_DELAY 0.289999991655349731 9
1 1 LPE_GPS_VXY 0.250000000000000000 9
1 1 LPE_GPS_VZ 0.250000000000000000 9
1 1 LPE_GPS_XY 1.000000000000000000 9
1 1 LPE_GPS_Z 3.000000000000000000 9
1 1 LPE_LAND_VXY 0.050000000745058060 9
1 1 LPE_LAND_Z 0.029999999329447746 9
1 1 LPE_LAT 47.397743225097656250 9
1 1 LPE_LDR_OFF_Z 0.000000000000000000 9
1 1 LPE_LDR_Z 0.029999999329447746 9
1 1 LPE_LON 8.545594215393066406 9
1 1 LPE_PN_B 0.001000000047497451 9
1 1 LPE_PN_P 0.100000001490116119 9
1 1 LPE_PN_T 0.001000000047497451 9
1 1 LPE_PN_V 0.100000001490116119 9
1 1 LPE_SNR_OFF_Z 0.000000000000000000 9
1 1 LPE_SNR_Z 0.050000000745058060 9
1 1 LPE_T_MAX_GRADE 1.000000000000000000 9
1 1 LPE_VIC_P 0.001000000047497451 9
1 1 LPE_VIS_DELAY 0.000000000000000000 9
1 1 LPE_VIS_XY 0.100000001490116119 9
1 1 LPE_VIS_Z 0.100000001490116119 9
1 1 LPE_VXY_PUB 0.300000011920928955 9
1 1 LPE_X_LP 5.000000000000000000 9
1 1 LPE_Z_PUB 1.000000000000000000 9
1 1 MAV_BROADCAST 0 6
1 1 MAV_COMP_ID 1 6
1 1 MAV_FWDEXTSP 1 6
1 1 MAV_PROTO_VER 0 6
1 1 MAV_RADIO_ID 0 6
1 1 MAV_SYS_ID 1 6
1 1 MAV_TEST_PAR 1 6
1 1 MAV_TYPE 2 6
1 1 MAV_USEHILGPS 0 6
1 1 MC_ACRO_EXPO 0.689999997615814209 9
1 1 MC_ACRO_P_MAX 360.000000000000000000 9
1 1 MC_ACRO_R_MAX 360.000000000000000000 9
1 1 MC_ACRO_SUPEXPO 0.699999988079071045 9
1 1 MC_ACRO_Y_MAX 360.000000000000000000 9
1 1 MC_BAT_SCALE_EN 0 6
1 1 MC_PITCHRATE_D 0.003000000026077032 9
1 1 MC_PITCHRATE_FF 0.000000000000000000 9
1 1 MC_PITCHRATE_I 0.045000001788139343 9
1 1 MC_PITCHRATE_MAX 220.000000000000000000 9
1 1 MC_PITCHRATE_P 0.144999995827674866 9
1 1 MC_PITCH_P 6.500000000000000000 9
1 1 MC_PITCH_TC 0.200000002980232239 9
1 1 MC_PR_INT_LIM 0.300000011920928955 9
1 1 MC_RATT_TH 0.800000011920928955 9
1 1 MC_ROLLRATE_D 0.003000000026077032 9
1 1 MC_ROLLRATE_FF 0.000000000000000000 9
1 1 MC_ROLLRATE_I 0.045000001788139343 9
1 1 MC_ROLLRATE_MAX 220.000000000000000000 9
1 1 MC_ROLLRATE_P 0.144999995827674866 9
1 1 MC_ROLL_P 6.500000000000000000 9
1 1 MC_ROLL_TC 0.200000002980232239 9
1 1 MC_RR_INT_LIM 0.300000011920928955 9
1 1 MC_TPA_BREAK_D 1.000000000000000000 9
1 1 MC_TPA_BREAK_I 1.000000000000000000 9
1 1 MC_TPA_BREAK_P 1.000000000000000000 9
1 1 MC_TPA_RATE_D 0.000000000000000000 9
1 1 MC_TPA_RATE_I 0.000000000000000000 9
1 1 MC_TPA_RATE_P 0.000000000000000000 9
1 1 MC_YAWRATE_D 0.000000000000000000 9
1 1 MC_YAWRATE_FF 0.000000000000000000 9
1 1 MC_YAWRATE_I 0.100000001490116119 9
1 1 MC_YAWRATE_MAX 200.000000000000000000 9
1 1 MC_YAWRATE_P 0.200000002980232239 9
1 1 MC_YAWRAUTO_MAX 45.000000000000000000 9
1 1 MC_YAW_FF 0.500000000000000000 9
1 1 MC_YAW_P 2.799999952316284180 9
1 1 MC_YR_INT_LIM 0.300000011920928955 9
1 1 MIS_ALTMODE 1 6
1 1 MIS_DIST_1WP 900.000000000000000000 9
1 1 MIS_DIST_WPS 900.000000000000000000 9
1 1 MIS_LTRMIN_ALT -1.000000000000000000 9
1 1 MIS_ONBOARD_EN 1 6
1 1 MIS_TAKEOFF_ALT 2.500000000000000000 9
1 1 MIS_YAWMODE 1 6
1 1 MIS_YAW_ERR 12.000000000000000000 9
1 1 MIS_YAW_TMT -1.000000000000000000 9
1 1 MNT_MODE_IN -1 6
1 1 MOT_SLEW_MAX 0.000000000000000000 9
1 1 MPC_ACC_DOWN_MAX 5.000000000000000000 9
1 1 MPC_ACC_HOR 5.000000000000000000 9
1 1 MPC_ACC_HOR_MAX 5.000000000000000000 9
1 1 MPC_ACC_UP_MAX 5.000000000000000000 9
1 1 MPC_ALT_MODE 0 6
1 1 MPC_CRUISE_90 3.000000000000000000 9
1 1 MPC_DEC_HOR_SLOW 5.000000000000000000 9
1 1 MPC_HOLD_DZ 0.100000001490116119 9
1 1 MPC_HOLD_MAX_XY 0.800000011920928955 9
1 1 MPC_HOLD_MAX_Z 0.600000023841857910 9
1 1 MPC_JERK_MAX 0.000000000000000000 9
1 1 MPC_JERK_MIN 1.000000000000000000 9
1 1 MPC_LAND_ALT1 10.000000000000000000 9
1 1 MPC_LAND_ALT2 5.000000000000000000 9
1 1 MPC_LAND_SPEED 0.500000000000000000 9
1 1 MPC_MANTHR_MAX 0.899999976158142090 9
1 1 MPC_MANTHR_MIN 0.079999998211860657 9
1 1 MPC_MAN_TILT_MAX 35.000000000000000000 9
1 1 MPC_MAN_Y_MAX 200.000000000000000000 9
1 1 MPC_THR_HOVER 0.500000000000000000 9
1 1 MPC_THR_MAX 0.899999976158142090 9
1 1 MPC_THR_MIN 0.119999997317790985 9
1 1 MPC_TILTMAX_AIR 45.000000000000000000 9
1 1 MPC_TILTMAX_LND 12.000000000000000000 9
1 1 MPC_TKO_RAMP_T 0.400000005960464478 9
1 1 MPC_TKO_SPEED 1.500000000000000000 9
1 1 MPC_VELD_LP 5.000000000000000000 9
1 1 MPC_VEL_MANUAL 10.000000000000000000 9
1 1 MPC_XY_CRUISE 5.000000000000000000 9
1 1 MPC_XY_MAN_EXPO 0.000000000000000000 9
1 1 MPC_XY_P 0.949999988079071045 9
1 1 MPC_XY_VEL_D 0.009999999776482582 9
1 1 MPC_XY_VEL_I 0.019999999552965164 9
1 1 MPC_XY_VEL_MAX 8.000000000000000000 9
1 1 MPC_XY_VEL_P 0.090000003576278687 9
1 1 MPC_Z_MAN_EXPO 0.000000000000000000 9
1 1 MPC_Z_P 1.000000000000000000 9
1 1 MPC_Z_VEL_D 0.000000000000000000 9
1 1 MPC_Z_VEL_I 0.019999999552965164 9
1 1 MPC_Z_VEL_MAX_DN 1.000000000000000000 9
1 1 MPC_Z_VEL_MAX_UP 3.000000000000000000 9
1 1 MPC_Z_VEL_P 0.200000002980232239 9
1 1 NAV_ACC_RAD 2.000000000000000000 9
1 1 NAV_AH_ALT 600.000000000000000000 9
1 1 NAV_AH_LAT -265847810 6
1 1 NAV_AH_LON 1518423250 6
1 1 NAV_DLL_ACT 0 6
1 1 NAV_DLL_AH_T 120.000000000000000000 9
1 1 NAV_DLL_CHSK 0 6
1 1 NAV_DLL_CH_ALT 600.000000000000000000 9
1 1 NAV_DLL_CH_LAT -266072120 6
1 1 NAV_DLL_CH_LON 1518453890 6
1 1 NAV_DLL_CH_T 120.000000000000000000 9
1 1 NAV_DLL_N 2 6
1 1 NAV_FORCE_VT 1 6
1 1 NAV_FT_DST 8.000000000000000000 9
1 1 NAV_FT_FS 1 6
1 1 NAV_FT_RS 0.500000000000000000 9
1 1 NAV_FW_ALT_RAD 10.000000000000000000 9
1 1 NAV_GPSF_LT 30.000000000000000000 9
1 1 NAV_GPSF_P 0.000000000000000000 9
1 1 NAV_GPSF_R 15.000000000000000000 9
1 1 NAV_GPSF_TR 0.699999988079071045 9
1 1 NAV_LOITER_RAD 50.000000000000000000 9
1 1 NAV_MC_ALT_RAD 0.800000011920928955 9
1 1 NAV_MIN_FT_HT 8.000000000000000000 9
1 1 NAV_RCL_ACT 3 6
1 1 NAV_RCL_LT 120.000000000000000000 9
1 1 NAV_TRAFF_AVOID 1 6
1 1 PWM_AUX_DIS1 -1 6
1 1 PWM_AUX_DIS2 -1 6
1 1 PWM_AUX_DIS3 -1 6
1 1 PWM_AUX_DIS4 -1 6
1 1 PWM_AUX_DIS5 -1 6
1 1 PWM_AUX_DIS6 -1 6
1 1 PWM_AUX_DISARMED 1500 6
1 1 PWM_AUX_MAX 2000 6
1 1 PWM_AUX_MIN 1000 6
1 1 PWM_AUX_REV1 0 6
1 1 PWM_AUX_REV2 0 6
1 1 PWM_AUX_REV3 0 6
1 1 PWM_AUX_REV4 0 6
1 1 PWM_AUX_REV5 0 6
1 1 PWM_AUX_REV6 0 6
1 1 PWM_AUX_TRIM1 0.000000000000000000 9
1 1 PWM_AUX_TRIM2 0.000000000000000000 9
1 1 PWM_AUX_TRIM3 0.000000000000000000 9
1 1 PWM_AUX_TRIM4 0.000000000000000000 9
1 1 PWM_AUX_TRIM5 0.000000000000000000 9
1 1 PWM_AUX_TRIM6 0.000000000000000000 9
1 1 PWM_DISARMED 900 6
1 1 PWM_MAIN_DIS1 -1 6
1 1 PWM_MAIN_DIS2 -1 6
1 1 PWM_MAIN_DIS3 -1 6
1 1 PWM_MAIN_DIS4 -1 6
1 1 PWM_MAIN_DIS5 -1 6
1 1 PWM_MAIN_DIS6 -1 6
1 1 PWM_MAIN_DIS7 -1 6
1 1 PWM_MAIN_DIS8 -1 6
1 1 PWM_MAIN_REV1 0 6
1 1 PWM_MAIN_REV2 0 6
1 1 PWM_MAIN_REV3 0 6
1 1 PWM_MAIN_REV4 0 6
1 1 PWM_MAIN_REV5 0 6
1 1 PWM_MAIN_REV6 0 6
1 1 PWM_MAIN_REV7 0 6
1 1 PWM_MAIN_REV8 0 6
1 1 PWM_MAIN_TRIM1 0.000000000000000000 9
1 1 PWM_MAIN_TRIM2 0.000000000000000000 9
1 1 PWM_MAIN_TRIM3 0.000000000000000000 9
1 1 PWM_MAIN_TRIM4 0.000000000000000000 9
1 1 PWM_MAIN_TRIM5 0.000000000000000000 9
1 1 PWM_MAIN_TRIM6 0.000000000000000000 9
1 1 PWM_MAIN_TRIM7 0.000000000000000000 9
1 1 PWM_MAIN_TRIM8 0.000000000000000000 9
1 1 PWM_MAX 1950 6
1 1 PWM_MIN 1075 6
1 1 PWM_RATE 400 6
1 1 PWM_SBUS_MODE 0 6
1 1 RC10_DZ 0.000000000000000000 9
1 1 RC10_MAX 2000.000000000000000000 9
1 1 RC10_MIN 1000.000000000000000000 9
1 1 RC10_REV 1.000000000000000000 9
1 1 RC10_TRIM 1500.000000000000000000 9
1 1 RC11_DZ 0.000000000000000000 9
1 1 RC11_MAX 2000.000000000000000000 9
1 1 RC11_MIN 1000.000000000000000000 9
1 1 RC11_REV 1.000000000000000000 9
1 1 RC11_TRIM 1500.000000000000000000 9
1 1 RC12_DZ 0.000000000000000000 9
1 1 RC12_MAX 2000.000000000000000000 9
1 1 RC12_MIN 1000.000000000000000000 9
1 1 RC12_REV 1.000000000000000000 9
1 1 RC12_TRIM 1500.000000000000000000 9
1 1 RC13_DZ 0.000000000000000000 9
1 1 RC13_MAX 2000.000000000000000000 9
1 1 RC13_MIN 1000.000000000000000000 9
1 1 RC13_REV 1.000000000000000000 9
1 1 RC13_TRIM 1500.000000000000000000 9
1 1 RC14_DZ 0.000000000000000000 9
1 1 RC14_MAX 2000.000000000000000000 9
1 1 RC14_MIN 1000.000000000000000000 9
1 1 RC14_REV 1.000000000000000000 9
1 1 RC14_TRIM 1500.000000000000000000 9
1 1 RC15_DZ 0.000000000000000000 9
1 1 RC15_MAX 2000.000000000000000000 9
1 1 RC15_MIN 1000.000000000000000000 9
1 1 RC15_REV 1.000000000000000000 9
1 1 RC15_TRIM 1500.000000000000000000 9
1 1 RC16_DZ 0.000000000000000000 9
1 1 RC16_MAX 2000.000000000000000000 9
1 1 RC16_MIN 1000.000000000000000000 9
1 1 RC16_REV 1.000000000000000000 9
1 1 RC16_TRIM 1500.000000000000000000 9
1 1 RC17_DZ 0.000000000000000000 9
1 1 RC17_MAX 2000.000000000000000000 9
1 1 RC17_MIN 1000.000000000000000000 9
1 1 RC17_REV 1.000000000000000000 9
1 1 RC17_TRIM 1500.000000000000000000 9
1 1 RC18_DZ 0.000000000000000000 9
1 1 RC18_MAX 2000.000000000000000000 9
1 1 RC18_MIN 1000.000000000000000000 9
1 1 RC18_REV 1.000000000000000000 9
1 1 RC18_TRIM 1500.000000000000000000 9
1 1 RC1_DZ 10.000000000000000000 9
1 1 RC1_MAX 1999.000000000000000000 9
1 1 RC1_MIN 1000.000000000000000000 9
1 1 RC1_REV 1.000000000000000000 9
1 1 RC1_TRIM 1499.000000000000000000 9
1 1 RC2_DZ 10.000000000000000000 9
1 1 RC2_MAX 2000.000000000000000000 9
1 1 RC2_MIN 1001.000000000000000000 9
1 1 RC2_REV 1.000000000000000000 9
1 1 RC2_TRIM 1499.000000000000000000 9
1 1 RC3_DZ 10.000000000000000000 9
1 1 RC3_MAX 1989.000000000000000000 9
1 1 RC3_MIN 1000.000000000000000000 9
1 1 RC3_REV 1.000000000000000000 9
1 1 RC3_TRIM 1000.000000000000000000 9
1 1 RC4_DZ 10.000000000000000000 9
1 1 RC4_MAX 1999.000000000000000000 9
1 1 RC4_MIN 1018.000000000000000000 9
1 1 RC4_REV 1.000000000000000000 9
1 1 RC4_TRIM 1501.000000000000000000 9
1 1 RC5_DZ 10.000000000000000000 9
1 1 RC5_MAX 2000.000000000000000000 9
1 1 RC5_MIN 1000.000000000000000000 9
1 1 RC5_REV 1.000000000000000000 9
1 1 RC5_TRIM 1500.000000000000000000 9
1 1 RC6_DZ 10.000000000000000000 9
1 1 RC6_MAX 2000.000000000000000000 9
1 1 RC6_MIN 1000.000000000000000000 9
1 1 RC6_REV 1.000000000000000000 9
1 1 RC6_TRIM 1500.000000000000000000 9
1 1 RC7_DZ 10.000000000000000000 9
1 1 RC7_MAX 2000.000000000000000000 9
1 1 RC7_MIN 1000.000000000000000000 9
1 1 RC7_REV 1.000000000000000000 9
1 1 RC7_TRIM 1500.000000000000000000 9
1 1 RC8_DZ 10.000000000000000000 9
1 1 RC8_MAX 2000.000000000000000000 9
1 1 RC8_MIN 1000.000000000000000000 9
1 1 RC8_REV 1.000000000000000000 9
1 1 RC8_TRIM 1500.000000000000000000 9
1 1 RC9_DZ 0.000000000000000000 9
1 1 RC9_MAX 2000.000000000000000000 9
1 1 RC9_MIN 1000.000000000000000000 9
1 1 RC9_REV 1.000000000000000000 9
1 1 RC9_TRIM 1500.000000000000000000 9
1 1 RC_ACRO_TH 0.500000000000000000 9
1 1 RC_ARMSWITCH_TH 0.250000000000000000 9
1 1 RC_ASSIST_TH 0.250000000000000000 9
1 1 RC_AUTO_TH 0.750000000000000000 9
1 1 RC_CHAN_CNT 8 6
1 1 RC_FAILS_THR 0 6
1 1 RC_FLT_CUTOFF 10.000000000000000000 9
1 1 RC_FLT_SMP_RATE 50.000000000000000000 9
1 1 RC_GEAR_TH 0.250000000000000000 9
1 1 RC_KILLSWITCH_TH 0.250000000000000000 9
1 1 RC_LOITER_TH 0.500000000000000000 9
1 1 RC_MAN_TH 0.500000000000000000 9
1 1 RC_MAP_ACRO_SW 0 6
1 1 RC_MAP_ARM_SW 0 6
1 1 RC_MAP_AUX1 0 6
1 1 RC_MAP_AUX2 0 6
1 1 RC_MAP_AUX3 0 6
1 1 RC_MAP_AUX4 0 6
1 1 RC_MAP_AUX5 0 6
1 1 RC_MAP_FAILSAFE 0 6
1 1 RC_MAP_FLAPS 0 6
1 1 RC_MAP_FLTMODE 5 6
1 1 RC_MAP_GEAR_SW 0 6
1 1 RC_MAP_KILL_SW 6 6
1 1 RC_MAP_LOITER_SW 0 6
1 1 RC_MAP_MAN_SW 0 6
1 1 RC_MAP_MODE_SW 0 6
1 1 RC_MAP_OFFB_SW 0 6
1 1 RC_MAP_PARAM1 0 6
1 1 RC_MAP_PARAM2 0 6
1 1 RC_MAP_PARAM3 0 6
1 1 RC_MAP_PITCH 2 6
1 1 RC_MAP_POSCTL_SW 0 6
1 1 RC_MAP_RATT_SW 0 6
1 1 RC_MAP_RETURN_SW 0 6
1 1 RC_MAP_ROLL 1 6
1 1 RC_MAP_STAB_SW 0 6
1 1 RC_MAP_THROTTLE 3 6
1 1 RC_MAP_TRANS_SW 0 6
1 1 RC_MAP_YAW 4 6
1 1 RC_OFFB_TH 0.500000000000000000 9
1 1 RC_POSCTL_TH 0.500000000000000000 9
1 1 RC_RATT_TH 0.500000000000000000 9
1 1 RC_RETURN_TH 0.500000000000000000 9
1 1 RC_RSSI_PWM_CHAN 0 6
1 1 RC_RSSI_PWM_MAX 1000 6
1 1 RC_RSSI_PWM_MIN 2000 6
1 1 RC_STAB_TH 0.500000000000000000 9
1 1 RC_TRANS_TH 0.250000000000000000 9
1 1 RTL_DESCEND_ALT 10.000000000000000000 9
1 1 RTL_LAND_DELAY 0.000000000000000000 9
1 1 RTL_MIN_DIST 5.000000000000000000 9
1 1 RTL_RETURN_ALT 30.000000000000000000 9
1 1 SDLOG_DIRS_MAX 0 6
1 1 SDLOG_MODE 0 6
1 1 SDLOG_PROFILE 3 6
1 1 SDLOG_UTC_OFFSET 0 6
1 1 SENS_BARO_QNH 1013.250000000000000000 9
1 1 SENS_BOARD_ROT 0 6
1 1 SENS_BOARD_X_OFF 0.576516091823577881 9
1 1 SENS_BOARD_Y_OFF 2.084044218063354492 9
1 1 SENS_BOARD_Z_OFF 0.000000000000000000 9
1 1 SENS_DPRES_ANSC 0.000000000000000000 9
1 1 SENS_DPRES_OFF 0.000000000000000000 9
1 1 SENS_EN_LL40LS 0 6
1 1 SENS_EN_MB12XX 0 6
1 1 SENS_EN_SF0X 0 6
1 1 SENS_EN_SF1XX 0 6
1 1 SENS_EN_THERMAL -1 6
1 1 SENS_EN_TRANGER 0 6
1 1 SYS_AUTOCONFIG 0 6
1 1 SYS_AUTOSTART 4001 6
1 1 SYS_CAL_ACCEL 0 6
1 1 SYS_CAL_BARO 0 6
1 1 SYS_CAL_GYRO 0 6
1 1 SYS_CAL_TDEL 24 6
1 1 SYS_CAL_TMAX 10 6
1 1 SYS_CAL_TMIN 5 6
1 1 SYS_COMPANION 921600 6
1 1 SYS_FMU_TASK 0 6
1 1 SYS_HITL 0 6
1 1 SYS_LOGGER 1 6
1 1 SYS_MC_EST_GROUP 1 6
1 1 SYS_PARAM_VER 1 6
1 1 SYS_RESTART_TYPE 0 6
1 1 SYS_STCK_EN 1 6
1 1 SYS_USE_IO 1 6
1 1 TC_A0_ID 0 6
1 1 TC_A0_SCL_0 1.000000000000000000 9
1 1 TC_A0_SCL_1 1.000000000000000000 9
1 1 TC_A0_SCL_2 1.000000000000000000 9
1 1 TC_A0_TMAX 100.000000000000000000 9
1 1 TC_A0_TMIN 0.000000000000000000 9
1 1 TC_A0_TREF 25.000000000000000000 9
1 1 TC_A0_X0_0 0.000000000000000000 9
1 1 TC_A0_X0_1 0.000000000000000000 9
1 1 TC_A0_X0_2 0.000000000000000000 9
1 1 TC_A0_X1_0 0.000000000000000000 9
1 1 TC_A0_X1_1 0.000000000000000000 9
1 1 TC_A0_X1_2 0.000000000000000000 9
1 1 TC_A0_X2_0 0.000000000000000000 9
1 1 TC_A0_X2_1 0.000000000000000000 9
1 1 TC_A0_X2_2 0.000000000000000000 9
1 1 TC_A0_X3_0 0.000000000000000000 9
1 1 TC_A0_X3_1 0.000000000000000000 9
1 1 TC_A0_X3_2 0.000000000000000000 9
1 1 TC_A1_ID 0 6
1 1 TC_A1_SCL_0 1.000000000000000000 9
1 1 TC_A1_SCL_1 1.000000000000000000 9
1 1 TC_A1_SCL_2 1.000000000000000000 9
1 1 TC_A1_TMAX 100.000000000000000000 9
1 1 TC_A1_TMIN 0.000000000000000000 9
1 1 TC_A1_TREF 25.000000000000000000 9
1 1 TC_A1_X0_0 0.000000000000000000 9
1 1 TC_A1_X0_1 0.000000000000000000 9
1 1 TC_A1_X0_2 0.000000000000000000 9
1 1 TC_A1_X1_0 0.000000000000000000 9
1 1 TC_A1_X1_1 0.000000000000000000 9
1 1 TC_A1_X1_2 0.000000000000000000 9
1 1 TC_A1_X2_0 0.000000000000000000 9
1 1 TC_A1_X2_1 0.000000000000000000 9
1 1 TC_A1_X2_2 0.000000000000000000 9
1 1 TC_A1_X3_0 0.000000000000000000 9
1 1 TC_A1_X3_1 0.000000000000000000 9
1 1 TC_A1_X3_2 0.000000000000000000 9
1 1 TC_A2_ID 0 6
1 1 TC_A2_SCL_0 1.000000000000000000 9
1 1 TC_A2_SCL_1 1.000000000000000000 9
1 1 TC_A2_SCL_2 1.000000000000000000 9
1 1 TC_A2_TMAX 100.000000000000000000 9
1 1 TC_A2_TMIN 0.000000000000000000 9
1 1 TC_A2_TREF 25.000000000000000000 9
1 1 TC_A2_X0_0 0.000000000000000000 9
1 1 TC_A2_X0_1 0.000000000000000000 9
1 1 TC_A2_X0_2 0.000000000000000000 9
1 1 TC_A2_X1_0 0.000000000000000000 9
1 1 TC_A2_X1_1 0.000000000000000000 9
1 1 TC_A2_X1_2 0.000000000000000000 9
1 1 TC_A2_X2_0 0.000000000000000000 9
1 1 TC_A2_X2_1 0.000000000000000000 9
1 1 TC_A2_X2_2 0.000000000000000000 9
1 1 TC_A2_X3_0 0.000000000000000000 9
1 1 TC_A2_X3_1 0.000000000000000000 9
1 1 TC_A2_X3_2 0.000000000000000000 9
1 1 TC_A_ENABLE 0 6
1 1 TC_B0_ID 0 6
1 1 TC_B0_SCL 1.000000000000000000 9
1 1 TC_B0_TMAX 75.000000000000000000 9
1 1 TC_B0_TMIN 5.000000000000000000 9
1 1 TC_B0_TREF 40.000000000000000000 9
1 1 TC_B0_X0 0.000000000000000000 9
1 1 TC_B0_X1 0.000000000000000000 9
1 1 TC_B0_X2 0.000000000000000000 9
1 1 TC_B0_X3 0.000000000000000000 9
1 1 TC_B0_X4 0.000000000000000000 9
1 1 TC_B0_X5 0.000000000000000000 9
1 1 TC_B1_ID 0 6
1 1 TC_B1_SCL 1.000000000000000000 9
1 1 TC_B1_TMAX 75.000000000000000000 9
1 1 TC_B1_TMIN 5.000000000000000000 9
1 1 TC_B1_TREF 40.000000000000000000 9
1 1 TC_B1_X0 0.000000000000000000 9
1 1 TC_B1_X1 0.000000000000000000 9
1 1 TC_B1_X2 0.000000000000000000 9
1 1 TC_B1_X3 0.000000000000000000 9
1 1 TC_B1_X4 0.000000000000000000 9
1 1 TC_B1_X5 0.000000000000000000 9
1 1 TC_B2_ID 0 6
1 1 TC_B2_SCL 1.000000000000000000 9
1 1 TC_B2_TMAX 75.000000000000000000 9
1 1 TC_B2_TMIN 5.000000000000000000 9
1 1 TC_B2_TREF 40.000000000000000000 9
1 1 TC_B2_X0 0.000000000000000000 9
1 1 TC_B2_X1 0.000000000000000000 9
1 1 TC_B2_X2 0.000000000000000000 9
1 1 TC_B2_X3 0.000000000000000000 9
1 1 TC_B2_X4 0.000000000000000000 9
1 1 TC_B2_X5 0.000000000000000000 9
1 1 TC_B_ENABLE 0 6
1 1 TC_G0_ID 0 6
1 1 TC_G0_SCL_0 1.000000000000000000 9
1 1 TC_G0_SCL_1 1.000000000000000000 9
1 1 TC_G0_SCL_2 1.000000000000000000 9
1 1 TC_G0_TMAX 100.000000000000000000 9
1 1 TC_G0_TMIN 0.000000000000000000 9
1 1 TC_G0_TREF 25.000000000000000000 9
1 1 TC_G0_X0_0 0.000000000000000000 9
1 1 TC_G0_X0_1 0.000000000000000000 9
1 1 TC_G0_X0_2 0.000000000000000000 9
1 1 TC_G0_X1_0 0.000000000000000000 9
1 1 TC_G0_X1_1 0.000000000000000000 9
1 1 TC_G0_X1_2 0.000000000000000000 9
1 1 TC_G0_X2_0 0.000000000000000000 9
1 1 TC_G0_X2_1 0.000000000000000000 9
1 1 TC_G0_X2_2 0.000000000000000000 9
1 1 TC_G0_X3_0 0.000000000000000000 9
1 1 TC_G0_X3_1 0.000000000000000000 9
1 1 TC_G0_X3_2 0.000000000000000000 9
1 1 TC_G1_ID 0 6
1 1 TC_G1_SCL_0 1.000000000000000000 9
1 1 TC_G1_SCL_1 1.000000000000000000 9
1 1 TC_G1_SCL_2 1.000000000000000000 9
1 1 TC_G1_TMAX 100.000000000000000000 9
1 1 TC_G1_TMIN 0.000000000000000000 9
1 1 TC_G1_TREF 25.000000000000000000 9
1 1 TC_G1_X0_0 0.000000000000000000 9
1 1 TC_G1_X0_1 0.000000000000000000 9
1 1 TC_G1_X0_2 0.000000000000000000 9
1 1 TC_G1_X1_0 0.000000000000000000 9
1 1 TC_G1_X1_1 0.000000000000000000 9
1 1 TC_G1_X1_2 0.000000000000000000 9
1 1 TC_G1_X2_0 0.000000000000000000 9
1 1 TC_G1_X2_1 0.000000000000000000 9
1 1 TC_G1_X2_2 0.000000000000000000 9
1 1 TC_G1_X3_0 0.000000000000000000 9
1 1 TC_G1_X3_1 0.000000000000000000 9
1 1 TC_G1_X3_2 0.000000000000000000 9
1 1 TC_G2_ID 0 6
1 1 TC_G2_SCL_0 1.000000000000000000 9
1 1 TC_G2_SCL_1 1.000000000000000000 9
1 1 TC_G2_SCL_2 1.000000000000000000 9
1 1 TC_G2_TMAX 100.000000000000000000 9
1 1 TC_G2_TMIN 0.000000000000000000 9
1 1 TC_G2_TREF 25.000000000000000000 9
1 1 TC_G2_X0_0 0.000000000000000000 9
1 1 TC_G2_X0_1 0.000000000000000000 9
1 1 TC_G2_X0_2 0.000000000000000000 9
1 1 TC_G2_X1_0 0.000000000000000000 9
1 1 TC_G2_X1_1 0.000000000000000000 9
1 1 TC_G2_X1_2 0.000000000000000000 9
1 1 TC_G2_X2_0 0.000000000000000000 9
1 1 TC_G2_X2_1 0.000000000000000000 9
1 1 TC_G2_X2_2 0.000000000000000000 9
1 1 TC_G2_X3_0 0.000000000000000000 9
1 1 TC_G2_X3_1 0.000000000000000000 9
1 1 TC_G2_X3_2 0.000000000000000000 9
1 1 TC_G_ENABLE 0 6
1 1 THR_MDL_FAC 0.000000000000000000 9
1 1 TRIG_MODE 0 6
1 1 TRIM_PITCH 0.000000000000000000 9
1 1 TRIM_ROLL 0.000000000000000000 9
1 1 TRIM_YAW 0.000000000000000000 9
1 1 VT_B_DEC_MSS 2.000000000000000000 9
1 1 VT_B_REV_DEL 0.000000000000000000 9

BIN
docs/assets/arucogenmap.PNG Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 122 KiB

After

Width:  |  Height:  |  Size: 177 KiB

BIN
docs/assets/cam_calib1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

BIN
docs/assets/cam_calib2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 236 KiB

BIN
docs/assets/cam_calib3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 338 KiB

BIN
docs/assets/cam_calib4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

BIN
docs/assets/cup.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

BIN
docs/assets/expotivka.PNG Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

BIN
docs/assets/fieldsetup.PNG Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

BIN
docs/assets/github-fork.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 175 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 192 KiB

BIN
docs/assets/tvorec.PNG Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

BIN
docs/assets/viz.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 235 KiB

23
docs/en/3dscan.md Normal file
View File

@@ -0,0 +1,23 @@
# 3D-scanning drone
<img src="../assets/3d_drone_1.jpg" title="3D-scanning drone">
The project was created in collaboration with Texel inc. that develops 3D-scanners for scanning people.
Our fellows from Texel provided a module consisting of a Raspberry Pi and a PrimeSense 3D-sensor.
We provided a Clever 3 drone that's capable of autonomous flight and wrote a flight program.
To make it all work we conducted many tests, made changes in the drone's design and tuned the drone properly.
## Video
<iframe width="966" height="543" src="https://www.youtube.com/embed/aqBION3TVhg" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
## Team
The project was made by:
* Timofei Kondratiev [Copter Express] - drone assembly, writing and debugging the program, conducting tests;
* Anton Maltsev [Copter Express] - modeling of the protection of the propellers;
* Andrei Poskonin [Texel] - modifying the Texel's software to work on Raspberry Pi.

View File

@@ -3,7 +3,7 @@ Clever
<img src="../assets/clever3.png" align="right" width="300px" alt="Klever">
"Clever" is an educational constructor set of a programmable quadcopter that consists of popular open source components, and a set of necessary documentation and libraries for working with it.
CLEVER (Russian: *"Клевер"*, meaning *"Clover"*) is an educational kit of a programmable quadcopter that consists of popular open source components, and a set of necessary documentation and libraries for working with it.
The kit includes a Pixhawk/Pixracer flight controller with the PX4 flight stack, a Raspberry Pi 3 as a controlling onboard computer, and a camera module for performing flights with the use of computer vision, and a set of various sensors and other peripherals.

View File

@@ -4,14 +4,14 @@
* Assembly
* [Clever 2 assembly](assemble_2.md)
* [Clever 3 assembly](assemble_3.md)
* [Installation of FPV](fpv.md)
* [Safety instruction](safety.md)
* [FPV Setup](fpv.md)
* [Safety tips](safety.md)
* [Connecting 4 in 1 ESCs](4in1.md)
* [Types of power connectors](connectortypes.md)
* [Blanching](zap.md)
* [Soldering safety](tb.md)
* [Using multimeter](test_connection.md)
* [Possible radio failures](radioerrors.md)
* [Multimeter usage](test_connection.md)
* [RC Troubleshooting](radioerrors.md)
* [Connecting GPS](gps.md)
* Configuration
* [Initial setup](setup.md)
@@ -19,43 +19,50 @@
* [Pixhawk / Pixracer Firmware](firmware.md)
* [PX4 Parameters](px4_parameters.md)
* [PID Setup](calibratePID.md)
* Work with Raspberry Pi
* Working with Raspberry Pi
* [Raspberry Pi](raspberry.md)
* [RPi Image](microsd_images.md)
* [RPi Connection to the Pixhawk](connection.md)
* [Wi-Fi connection](wifi.md)
* [SSH access to Raspberry Pi](ssh.md)
* [Configuring Wi-Fi](network.md)
* [Using QGroundControl via Wi-Fi](gcs_bridge.md)
* [Controlling Clever from a smartphone](rc.md)
* [Remote shell](ssh.md)
* [Wi-Fi Configuration](network.md)
* [Using QGroundControl over Wi-Fi](gcs_bridge.md)
* [Remote control app](rc.md)
* [UART settings](uart.md)
* [Viewing images from cameras](web_video_server.md)
* [Coordinate systems (frames)](frames.md)
* Coding
* Programming
* [ROS](ros.md)
* [MAVROS](mavros.md)
* [Simple OFFBOARD](simple_offboard.md)
* [Navigation using ArUco markers](aruco.md)
* [Automatic check](selfcheck.md)
* [Code examples](snippets.md)
* Fiducial markers (ArUco)
* [Overview](aruco.md)
* [Marker detection](aruco_marker.md)
* [Map-based navigation](aruco_map.md)
* [Automated self-checks](selfcheck.md)
* [Code snippets](snippets.md)
* [Adjusting the position of the main camera](camera_frame.md)
* [Working with the camera](camera.md)
* [Working with a LED strip on Raspberry 3](leds.md)
* [Computer vision basics](camera.md)
* [LED strip](leds.md)
* [Using rviz and rqt](rviz.md)
* [Working with the ultrasonic distance gage](sonar.md)
* [Working with a laser rangefinder](laser.md)
* [Working with GPIO](gpio.md)
* [Interfacing with a sonar](sonar.md)
* [Interfacing with a laser rangefinder](laser.md)
* [PX4 Simulation](sitl.md)
* [Software autorun](autolaunch.md)
* [Hostname](hostname.md)
* [Controlling the copter from Arduino](arduino.md)
* [Using an external 3G modem](3g.md)
* Clever-based projects
* [Copter spheric guard](shield.md)
* [Face recognition system](face_recognition.md)
* [An Android transmitter](android.md)
* [Android RC app](android.md)
* [3D-scanning drone](3dscan.md)
* [Human pose estimation drone control](human_pose_estimation_drone_control.md)
* [Copter Hack 2018](copterhack2018.md)
* [Copter Hack 2017](copterhack2017.md)
* Supplementary materials
* [Contribution to Clever](contributing.md)
* [Contribution Guidelines](contributing.md)
* [Flashing ESCs using BLHeliSuite](esc_firmware.md)
* [MAVLink](mavlink.md)
* [PX4 Logs and Topics](flight_logs.md)

View File

@@ -1,9 +1,8 @@
# Navigation using ArUco markers
# ArUco markers
> **Note** Documentation for the versions [of image](microsd_images.md), starting with **0.15**. For earlier versions, see [documentation for version **0.14**](https://github.com/CopterExpress/clever/blob/v0.14/docs/ru/aruco.md).
> **Note** The following applies to [image versions](microsd_images.md) **0.16** and up. Older documentation is still available [for version **0.15.1**](https://github.com/CopterExpress/clever/blob/v0.15.1/docs/ru/aruco.md).
[ArUco-Markers](https://docs.opencv.org/3.2.0/d5/dae/tutorial_aruco_detection.html) is a popular technology for positioning
robotic systems using computer vision.
[ArUco markers](https://docs.opencv.org/3.2.0/d5/dae/tutorial_aruco_detection.html) are commonly used for vision-based position estimation.
Examples of ArUco markers:
@@ -13,159 +12,13 @@ Examples of ArUco markers:
For rapid generation of markers for printing, you may use an online tool: http://chev.me/arucogen/.
## aruco\_pose
[Clever Raspberry Pi image](microsd_images.md) contains a pre-installed `aruco_pose` ROS package, which can be used for marker detection.
The `aruco_pose` module allows restoring the position of the copter relative to the map of ArUco markers and communicating it to the flight controller using the [Vision Position Estimation](https://dev.px4.io/en/ros/external_position_estimation.html) mechanism.
## Modes of operation
If the source of the copter position by the markers is available, the option appears for precise autonomous indoor navigation by the positions using the [simple_offboard](simple_offboard.md) module.
There are several preconfigured modes of operation for ArUco markers on the Clever drone:
### Turning on
* [single marker detection and navigation](aruco_marker.md);
* [map-based navigation](aruco_map.md).
Make sure that in the clever launch file \(`~/catkin_ws/src/clever/clever/launch/clever.launch`\), the start of aruco\_pose and [computer vision cameras](camera.md) is turned on:
```xml
<arg name="main_camera" default="true"/>
```
```xml
<arg name="aruco" default="true"/>
```
After the launch-file is edited, restart package `clever`:
```(bash)
sudo systemctl restart clever
```
### Calibrating the ArUco marker map
An automatically generated [ArUco-board](https://docs.opencv.org/trunk/db/da9/tutorial_aruco_board_detection.html) may be used as a map of marks.
The map of marks is adjusted using file `~/catkin_ws/src/clever/clever/launch/aruco.launch`. To use ArUco-board, enter its parameters:
```xml
<node pkg="nodelet" type="nodelet" name="aruco_pose" args="load aruco_pose/aruco_pose nodelet_manager">
<param name="frame_id" value="aruco_map_raw"/>
<!-- the type of the marker field -->
<param name="type" value="gridboard"/>
<!-- the number of markets along x -->
<param name="markers_x" value="1"/>
<!-- the number of markers along y -->
<param name="markers_y" value="6"/>
<!-- ID of the first marker (left top) -->
<param name="first_marker" value="240"/>
<!-- the length of the marker side in meters -->
<param name="markers_side" value="0.3362"/>
<!-- distance between the murders -->
<param name="markers_sep" value="0.46"/>
</node>
```
The vertical and horizontal distance between the markers may be set separately:
```xml
<!-- the horizontal distance between the markers -->
<param name="markers_sep_x" value="0.97"/>
<!-- the vertical distance between the marker -->
<param name="markers_sep_y" value="1.435"/>
```
If a map with a custom order of marker IDs is used, parameter `marker_ids` may be used:
```xml
<rosparam param="marker_ids">[5, 7, 9, 11, 13, 15]</rosparam>
```
The markers are numbered from the top left corner of the field.
For monitoring the map that is currently used by the copter for navigation, one can watch the content of topic `aruco_pose/map_image`. In a browser, it may be viewed with [web\_video\_server](web_video_server.md) by following the link [http://192.168.11.1:8080/snapshot?topic=/aruco\_pose/map\_image](http://192.168.11.1:8080/snapshot?topic=/aruco_pose/map_image):
![](../../assets/Снимок экрана 2017-11-27 в 23.20.49.png)
When flying, make sure that the markers glued to the floor correspond to the map.
In topic `aruco_pose/debug` \([http://192.168.11.1:8080/snapshot?topic=/aruco\_pose/debug](http://192.168.11.1:8080/snapshot?topic=/aruco_pose/debug)\) the current result of markers recognitions is available:
TODO
### The system of coordinates
According to [agreement](http://www.ros.org/reps/rep-0103.html), the standard ENU system of coordinates is used in the marker field:
* x — rightward \(conditional East\);
* y — forward \(conditional North\);
* z — upward.
_Note_: the definition above is provided for a situation when the marker field is on the floor.
First, the zero is the bottom left point of the marker field. The yaw angle is considered zero when the copter is faced rightward\(along the x-axis\).
![The system of markers coordinates](../assets/aruco-frame.png)
### Configuring the flight controller
Correct Vision Position Estimation requires making sure \(via [QGroundControl](gcs_bridge.md)\) that:
* **For Pixhawk**: Firmware with LPE \(local position estimator\) is installed. For Pixhawk [download firmware `px4fmu-v2_lpe.px4`](https://github.com/PX4/Firmware/releases).
**For Pixracer**: parameter `SYS_MC_EST_GROUP` should be set to`local_position_estimator, attitude_estimator_q`.
> **Note** After changing the value of parameter `SYS_MC_EST_GROUP` restart the flight controller.
* In parameter `LPE_FUSION` **only** flags `vision position`, `land detector` are enabled. The final value _20_.
* Compass disabled: `ATT_W_MAG` = 0
* Complimentary filter external heading weight: `ATT_W_EXT_HDG` = 0.5
* Orientation by yaw by vision enabled: `ATT_EXT_HDG_M` = 2 `MOCAP`.
* VPE settings: `LPE_VIS_DELAY` = 0 sec, `LPE_VIS_XY` = 0.1 m, `LPE_VIS_Z` = 0.15 m.
* Recommended settings for land detector:
* `COM_DISARM_LAND` = 1 s
* `LNDMC_ROT_MAX` = 45 deg
* `LNDMC_THR_RANGE` = 0.5
* `LNDMC_Z_VEL_MAX` = 1 m/s
<!--
For the ease of configuring, you may use a ready settings file for [Clever 2](https://github.com/CopterExpress/clever/blob/master/docs/assets/Clever2LPE_160118.params) or for [Clever 3](https://github.com/CopterExpress/clever/blob/master/docs/assets/Clever3_LPE_020218.params) and upload it to the controller using menu Tools - Load from file in tab Parameters in QGroundControl.
![](../assets/Screenshot from 2018-02-27 22-30-50.png)
-->
### Flight
A properly configured copter starts holding position by VPE \(in [modes](modes.md) `POSCTL` or `OFFBOARD`\) automatically.
For [autonomous flights](simple_offboard.md) do you will be able to use functions `navigate`, `set_position`, `set_velocity`. For flying to specific coordinates of the marker field, use frame `aruco_map`:
```python
# First, the copter has to take off to see the map of marks
# and for frame aruco_map to appear:
navigate(0, 0, 2, frame_id='body', speed=0.5, auto_arm=True) # take off to the altitude of 2 meters
time.sleep(5)
# Flying to coordinate 2:2 of the marker field at the altitude of 2 meters
navigate(2, 2, 2, speed=1, frame_id='aruco_map') # flying to coordinate 2:2 at the altitude of 3 meters
```
See [other functions](simple_offboard.md) simple_offboard.
### Location of markers on the ceiling
![Markers on the ceiling](../assets/IMG_4175.JPG)
To navigate by the markers placed on the ceiling, you need to set the main camera facing up and [set the corresponding frame of the camera](camera_frame.md).
To set the map of markers in a "turned over" system of coordinates, change parameter `aruco_orientation` in file `~/catkin_ws/src/clever/clever/aruco.launch`:
```xml
<param name="aruco_orientation" value="map_upside_down"/>
```
When this parameter is set, frame aruco\_map will also be "turned over". Thus, to fly at the altitude of 2 meters below the ceiling, argument `z` should be set to 2:
```python
navigate(x=1, y=2, z=1.1, speed=0.5, frame_id='aruco_map')
> **Info** Additional documentation for the `aruco_pose` ROS package is available [on GitHub](https://github.com/CopterExpress/clever/blob/master/aruco_pose/README.md).

145
docs/en/aruco_map.md Normal file
View File

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

117
docs/en/aruco_marker.md Normal file
View File

@@ -0,0 +1,117 @@
# ArUco marker detection
> **Info** Marker detection requires the camera module to be correctly plugged in and [configured](camera.md).
`aruco_detect` module detects ArUco markers and publishes their positions in ROS topics and as [TF frames](frames.md).
This is useful in conjunction with other positioning systems, such as [GPS](gps.md), [Optical Flow](optical_flow.md), PX4Flow, visual odometry, ultrasonic ([Marvelmind](https://marvelmind.com)) or UWB-based ([Pozyx](https://www.pozyx.io)) localization.
Using this module along with [map-based navigation](aruco_map.md) is also possible.
## Setup
Set the `aruco` argument in `~/catkin_ws/src/clever/clever/launch/aruco.launch` to `true`:
```xml
<arg name="aruco" default="true"/>
```
For enabling detection set the `aruco_detect` argument in `~/catkin_ws/src/clever/clever/launch/aruco.launch` to `true`:
```xml
<arg name="aruco_detect" default="true"/>
```
For the module to work correctly the following parameters should be set:
```xml
<param name="length" value="0.32"/> <!-- length of a single marker, in meters (excluding the white border) -->
<param name="estimate_poses" value="true"/> <!-- position estimation for single markers -->
<param name="send_tf" value="true"/> <!-- TF frame creation for markers -->
<param name="known_tilt" value="map"/> <!-- Marker tilt, explained below -->
```
`known_tilt` should be set to:
* `map` if *all* markers are on the ground;
* `map_flipped` if *all* markers are on the ceiling;
* an empty string otherwise.
You may specify length for each marker individually by using the `length_override` parameter:
```xml
<param name="length_override/3" value="0.1"/> <!-- marker with id=3 has a side of 0.1m -->
<param name="length_override/17" value="0.25"/> <!-- marker with id=17 has a side of 0.25m -->
```
## Coordinate system
Each marker has its own coordinate systems. It is aligned as follows:
* the **<font color=red>x</font>** axis points to the right side of the marker;
* the **<font color=green>y</font>** axis points to the top side of the marker;
* the **<font color=blue>z</font>** axis points outwards from the plane of the marker
<img src="../assets/aruco-axis.png" width="300">
## Working with detected markers
Navigation within the marker-based TF frames is possible with `simple_offboard` node.
Sample code to fly to a point 1 metre above marker with id 5:
```python
navigate(frame_id='aruco_5', x=0, y=0, z=1)
```
Sample code to fly to a point 1 metre to the left and 2 metres above marker with id 7:
```python
navigate(frame_id='aruco_7', x=-1, y=0, z=2)
```
Sample code to rotate counterclockwise while hovering 1.5 metres above marker id 10:
```python
navigate(frame_id='aruco_10', x=0, y=0, z=1.5, yaw_rate=0.5)
```
Note that if the required marker isn't detected for 0.5 seconds after the `navigate` command, the command will be ignored.
These frames may also be used in other services that accept TF frames (like `get_telemetry`). The following code will get the drone's position relative to the marker with id 3:
```python
telem = get_telemetry(frame_id='aruco_3')
```
Note that if the required marker isn't detected for 0.5 seconds, the `telem.x`, `telem.y`, `telem.z`, `telem.yaw` fields will contain `NaN`.
## Handling marker detection in Python
The following snippet shows how to read the `aruco_detect/markers` topic in Python:
```python
import rospy
from aruco_pose.msg import MarkerArray
rospy.init_node('my_node')
# ...
def markers_callback(msg):
print 'Detected markers:':
for marker in msg.markers:
print 'Marker: %s' % marker
# Create a Subscription object. Each time a message is posted in aruco_detect/markers, the markers_callback function is called with this message as its argument.
rospy.Subscriber('aruco_detect/markers', MarkerArray, markers_callback)
# ...
rospy.spin()
```
Each message contains the marker ID, its corner points on the image and its position relative to the camera.
---
Suggested reading: [map-based navigation](aruco_map.md)

View File

@@ -9,30 +9,30 @@ Clever 2 construction kit assembly instruction
* Central frame ×2.
* Additional frame ×4.
* Beam ×8.
* Motor mount ×8.
* Legs x8.
* Beam guard ×8.
* Motor mount guard ×8.
* Propeller guard ×16.
* Side guard ×16.
* Dalprop 5045 plastic propeller ×4.
* Racerstar BR2205 2300kV brushless motor ×4.
* Speed adjusters ESC, DYS XSD20А ×4.
* Speed controllers ESC, DYS XSD20А ×4.
* Power controller XT60 pin ×1.
* Power connector XT60 socket ×1.
* Three-wire female-female flat cable ×2.
* Wire copper multicore silicone insulated cable 14AWG (red, black), 50 cm long
* Multicore silicone insulated copper wire 14AWG (red, black), 50 cm long
* Power distribution board PDB BeeRotor Power Distribution Board V2.0 ×1.
* Li-ion rechargeable battery (battery) 18650 ×8.
* EFEST Luc V4 Li-lon Charger ×1.
* Regulators protective case ×4.
* Protective case for regulators ×4.
* Legs attachment ×8.
* Pixhawk flight controller ×1.
* FlySky i6 radio receiver×1.
* FlySky i6 radio receiver ×1.
* FlySky i6 radio transmitter ×1.
* EFEST LUC V4 Charger ×1.
* Micro USB to USB Cable ×1
* Battery compartment 18650 li-Ion ×1
* Wire copper multicore silicone insulated cable 18AWG (red, black), 100 cm long
* Multicore silicone insulated copper wire 18AWG (red, black), 100 cm long
* AA battery ×4
* Jumper, Bind-plug
@@ -45,16 +45,16 @@ Clever 2 construction kit assembly instruction
* М3х16 screws ×40.
* Plastic nuts ×8.
* Metal nuts ×48.
* Stickers for the compartment battery ×8.
* Stickers for the battery compartment ×8.
* Thermal contraction tube ⌀15, .50 cm
* Thermal contraction tube ⌀5, 100 cm
* Double-sided 3M adhesive tape ×16.
* Screwdriver ×1 (visualization needed)
* Insulation tape ×1
* Stationery scissors ×1
* Scissors ×1
* Strap for the battery 250 mm ×1
## Functionality of the Flysky i6 transmitter
## Flysky i6 transmitter
1. Switch A (SwA).
2. Switch B (SwB).
@@ -97,7 +97,7 @@ Clever 2 construction kit assembly instruction
### Installation of motors
* Unpack the motors. Using pliers, shorten the wires on the motors by cutting half the length (leaving about 25 mm).
* Unpack the motors. Using pliers, shorten the wires on the motors by cutting half their length (leaving about 25 mm).
![brrc2205 motor](../assets/brrc2205.png)
@@ -107,44 +107,44 @@ Strip
Twist the wires.
Blanch
Tin wires
* Apply flux to the exposed part of the wire.
* Cover the solder using tweezers.
![Blanching](../assets/zap.jpg)
![Tinning](../assets/zap.jpg)
#### Fix the motor on the beam
#### Fix the motor on the mount
* Install the motor on the engraved side of the beam.
* Attach the motors to the beams with М3х8 screws using a screwdriver.
* Install the motor on the engraved side of the mount.
* Attach the motors to the mounts with М3х8 screws using a screwdriver.
![Fix the motor on the beam](../assets/brrc2205on.jpg)
![Fix the motor on the mount](../assets/brrc2205on.jpg)
* Beams with motors should be arranged according to the diagram. The arrows indicate the direction of motors rotation.
* Mounts with motors should be arranged according to the diagram. The arrows indicate the direction of motor rotation direction.
![Rotation of motors](../assets/brrc2205ondeck.jpg)
### Blanch three contact pads of the adjuster
### Tin three contact pads of the speed controller
* Apply flux
* Apply solder
To make solder neatly fill the entire pad, warm up the contact pad of the adjuster. For this purpose, hold the tip of the soldering gun to the contact pad for 2 seconds (or more if needed)
By warming up the contact pads of the controller, the tin will evenly fill the entire pad. To do so, apply heat by holding the soldering iron on the contact pads for 2 sencods (or more if needed).
![Blanching of the adjuster contact pads](../assets/escDYSzap.png)
![Tinning of the controller contact pads](../assets/escDYSzap.png)
* Repeat this operation for the remaining three ESC
### Solder the wires of the motors to the ESC
Solder the prepared wires of the motors to the pads of ESC.
Solder the prepared wires of the motors to the pads of the controllers.
![Solder wires of motors to ESC](../assets/solderingBrrc2205ondeckTOescDYSzap.png)
![Solder wires of motors to the controllers](../assets/solderingBrrc2205ondeckTOescDYSzap.png)
* Repeat this operation for the remaining three ESC
### Installation of power connectors
### Power connectors installation
#### Preparing wires for XT60 power connectors
@@ -162,21 +162,21 @@ Solder the prepared wires of the motors to the pads of ESC.
![XT60 high-power connector](../assets/xt60pinsocket.jpg)
1. Blanch two red and black 14AWG power wires 7 cm long for the XT60 pin connector .
2. Blanch contact pads of the XT60 pin connector.
1. Tin two red and black 14AWG 7 cm long power wires for the XT60 pin connector.
2. Tin contact pads of the XT60 pin connector.
3. Solder the black wire to the “-” contact of the connector.
4. Solder the red wire to the “+” contact of the connector .
5. Cut ⌀5 thermal contraction tube (2 sections × 10 mm).
6. Put the ⌀5 thermal contraction tube on the wires so that it covers the contact pads of the wires from XT60.
7. Shrink the thermal contraction tube with a hot air gun. ![Installation of XT60 connectors](../assets/mountxt60pinsocket.png)
4. Solder the red wire to the “+” contact of the connector.
5. Cut ⌀5 heat-shrink tubing (2 sections × 10 mm).
6. Slip the ⌀5 heat-shrink tubing tube on the wires so that they cover the contact pads of the wires from XT60.
7. Shrink the heat-shrink tubing with a hot air gun. ![Installation of XT60 connectors](../assets/mountxt60pinsocket.png)
8. Repeat the procedure for XT60 socket connector.
#### Preparation of the power connector for the 5V control circuit
#### Preparation of the 5V power connectors for the control circuit
1. Trim/pull all pins from one of the connectors. Disconnect it.
2. Using a utility knife, pry the retainer off on the remaining connector to release the 3rd wire.
1. Trim/pull out all pins from one of the connectors. Disconnect it.
2. Using an utility knife, pry the retainer off on the remaining connector to release the 3rd wire.
3. Remove the 3rd (orange) wire from the connector, since it is not needed.
4. The length of the remaining black and red wires should be 10 12 cm.
4. The length of the remaining black and red wires should be of 10 12 cm.
![Installation of the 5V connector](../assets/en/mount5vconnector.jpg)
@@ -188,29 +188,29 @@ Solder the prepared wires of the motors to the pads of ESC.
![ Pre-soldering check](../assets/startPDBtest.jpg)
Check OPEN CONDITION of the following circuits (absence of the multimeter sound signal):
Check OPEN CONDITION of the following circuits (the multimeter does not beep):
* “BAT+” and “BAT-”
* “12V” and “GND”
* “5V” and “GND”
Check CLOSED CONDITION of the following circuits (presence of the multimeter sound signal):
Check CLOSED CONDITION of the following circuits (the multimeter beeps):
* “BAT-” with every contact marked “-” and “GND”
* “BAT+”, with every contact marked “+”
### Blanch the contact pads of the power board
### Tin the contact pads of the power board
1. [Blanch*] (zap.md) the contact pads of the power board
2. Using a multimeter, check absence of contact closure on the PCB (check continuity)
1. [Tin*] (zap.md) the contact pads of the power board.
2. Using a multimeter, check absence of short-circuits on the PCB (check continuity).
![ After-soldering check](../assets/en/zapPDBtest.jpg)
To make solder neatly fill the entire pad, it should be warmed up. For this purpose, hold the tip of the soldering gun to the contact pad for 2 seconds (or more if needed)
By warming up the contact pads of the controller, the tin will evenly fill the entire pad. To do so, apply heat by holding the soldering iron on the contact pads for 2 sencods (or more if needed).
#### Soldering the XT60 high power connector
Solder the connector for battery, observing polarity on the contact pads.
Solder the connector for battery, taking into account the polarity on the contact pads.
![Soldering of XT60 to PDB](../assets/solderingxt60socketTOpdb.jpg)
@@ -221,7 +221,7 @@ IMPORTANT NOTE about polarity
#### Soldering of the power connector for the 5V control circuit
Solder the 5V connector, observing polarity on the contact pads.
Solder the 5V connector, taking into account the polarity on the contact pads.
(in the picture: the red wire is “+”)
![Soldering of 5V to PDB](../assets/solderingxt60socketTOpdb.jpg)
@@ -232,32 +232,32 @@ Solder the 5V connector, observing polarity on the contact pads.
![Jumper](../assets/jumper.png)
* Cut off 2 cm length of the high-power wire
* Cut off 2 cm of high-power wire.
* Strip on both ends.
* Blanch
* Make 3 jumpers
* Tin.
* Make 3 jumpers.
* Solder the jumpers according to the diagram.
* Check continuity with a multimeter. If necessary, clean with sand paper.
* Check for continuity with a multimeter. If necessary, clean with sand paper.
#### Preparation of the battery compartment
![Preparation of the battery compartment](../assets/casebattery.png)
* Glue a sticker with marking to the inside of the battery compartment in accordance with the polarity.
* Conforming to the polarity, glue the sticker with markings inside the battery compartment.
* Stick a strip of adhesive tape to the bottom of the compartment.
### Installation of the power distribution board
* Fix the power board to the frame with М3х8 screws and plastic nuts. ![Installation of the PDB board](../assets/mountPDB.png)
> **IMPORTANT** An arrow on the board points to the fore cutout
> **IMPORTANT** The white arrow on the BeeRotor board points towards the fore cutout.
![Installation of the PDB board](../assets/topviewmountPDB.png)
#### Installation of elements
1. Install the nuts into plastic holders. ![Installation of plastic holders](../assets/holderLegs.png)
2. Fix the beams to the frame with М3х16 screws
* The beams are installed on top of the frame
* Plastic holders are installed on the bottom of the frame. ![Installation of beams](../assets/mountBeams.png)
2. Fix the motor mounts to the frame with М3х16 screws.
* The mounts are installed above the frame.
* Plastic holders are installed beneath the frame. ![Installation of mounts](../assets/mountBeams.png)
3. Arrangement of motors. Check arrangement of the motors (the motors with black nuts should be in the top left and lower right corners). ![Arrangement of motors](../assets/motorsTopview.png)
4. Put the power wires of the ESC through the holes. ![high-power wires of motors](../assets/escWires.png)
@@ -274,7 +274,7 @@ IMPORTANT NOTE about polarity
### Pairing the receiver and transmitter
1. Connect the radio receiver to the 5V connector. In any connector, GND in the bottom. In the diagram, the power is labeled 5V ![Connecting the receiver power](../assets/receiver5V.png)
1. Connect the radio receiver to the 5V connector. In any connector the GND is in the bottom. In the diagram, the power is labeled 5V ![Connecting the receiver power](../assets/receiver5V.png)
2. Connect the battery. The LED on the radio receiver should be flashing. ![Connecting the battery]
#### SAFETY when working with the battery
@@ -283,10 +283,10 @@ IMPORTANT NOTE about polarity
#### Enabling the transmitter
1. Insert the jumper into B/VCC of the radio receiver (short-circuit "ground" and "signal")
1. Insert the jumper into B/VCC of the radio receiver (short "ground" and "signal")
2. On the transmitter, hold down the BIND KEY button.
3. Power up the transmitter (flip the POWER switch, do not release BIND KEY).
4. Connect the battery to the copter.
4. Connect the battery to the drone.
5. Wait for synchronization.
6. Disconnect the jumper.
7. The LED will remain ON continuously.
@@ -295,25 +295,25 @@ IMPORTANT NOTE about polarity
[Radio equipment troubleshooting manual](radioerrors.md)
### Checking the motors rotation direction
### Checking the motors direction of rotation
1. Apply stickers to the 18650 battery.
2. Install the 18650 battery into the compartment observing polarity. ![Battery compartment readiness](../assets/readyBatteryholder.png)
1. Apply stickers to the 18650 batteries.
2. Install the 18650 batteries into the compartment observing polarity. ![Battery compartment readiness](../assets/readyBatteryholder.png)
3. Check that the 5V power plug is connected to the receiver according to the circuit diagram.
4. Connect the motor ESC to channel 3 of the CH3 receiver according to the circuit diagram. ![Connecting the ESC to the receiver](../assets/connectionESCtoReceiver.png)
4. Connect the motor ESC to channel 3, marked as CH3 on the receiver as on the circuit diagram. ![Connecting the ESC to the receiver](../assets/connectionESCtoReceiver.png)
5. Connect external power (battery).
6. Turn the transmitter ON
6. Turn the transmitter ON.
7. Using the left stick, set throttle to 10 %.
8. Check the motor rotation direction according to the scheme. ![Checking motors rotation](../assets/testMotors.jpg)
9. If you have to change the rotation direction, toggle any two phase wires of the motor (needs resoldering). ![Resoldering phase wires](../assets/resolderingESC.png)
8. Check the motor direction of rotation according to the scheme. ![Checking motors rotation](../assets/testMotors.jpg)
9. If you have to change the rotation direction, swap any two phase wires of the motor (needs resoldering). ![Resoldering phase wires](../assets/resolderingESC.png)
### Installation of the radio receiver
1. Install the 30 mm plastic legs on the frame with М3х8 screws.
2. Pass the 5V power connector through the slot. ![Installation of legs and the slot](../assets/mountReceiverStud.png)
3. Attach the receiver to the bottom of the additional frame using double-sided adhesive tape and noting the engraving. The antennas are to be pointing forward. ![Installation of the radio receiver on the deck](../assets/mountReceiverDeck.png)
2. Pass the 5V power connector through the slit. ![Installation of legs and the slot](../assets/mountReceiverStud.png)
3. Attach the receiver to the bottom of the additional frame using double-sided adhesive tape and following the orientation of the engraved arrow. The antennas are to be pointing forward. ![Installation of the radio receiver on the deck](../assets/mountReceiverDeck.png)
4. Install the 3-wire flat cable into the PPM / CH1 channel. ![Connecting the radio receiver](../assets/receiverPPM.png)
5. Pass through the slot to the 5 V connector.
5. Pass them through the slit to the 5 V connector.
6. Screw the bottom an additional frame to the legs on the central frame with М3х8 screws. ![Bottom deck installation](../assets/mountBottomDeck.png)
> **IMPORTANT** The directions of the arrows on the power supply board and the additional frame should coincide
@@ -341,12 +341,12 @@ should be increased up to 4 5.
### ESC assembly
1. Stick the double-sided adhesive tape to the base of ESC protective case ![Adhesive tape on the ESC case](../assets/escCase.png)
2. Put the ESCs into protective cases. Fasten the assembly to the beams of the frame. ![ESC cases top view](../assets/topESCcaseview.png)
1. Stick the double-sided adhesive tape to the base of the ESC protective case ![Adhesive tape on the ESC case](../assets/escCase.png)
2. Put the ESCs into protective cases. Fasten the assembly to the motor mounts of the frame. ![ESC cases top view](../assets/topESCcaseview.png)
### Installation of guard
1. Attach the lower guard with М3х16 screw to the beams of the frame. ![Installation of beams guard](../assets/lowsafeDeck.png)
1. Attach the lower guard with М3х16 screw to the motor mounts of the frame. ![Installation of beams guard](../assets/lowsafeDeck.png)
2. Attach the feet to the plastic holders with М3х16 screws. ![Feet installation](../assets/safeLegs.png)
3. Attach the 30 mm long legs to the holes of the lower guard with М3х12 screw. ![Installation of the lower radial guard](../assets/safelowRadial.png)
4. Attach the top guard with М3х12 screws. ![Installation of the top radial guard](../assets/safehighRadial.png)
@@ -360,7 +360,7 @@ Requires the following components:
* Additional frame (1 pc)
* Battery compartment (1 pc)
1. Attach the battery compartment on the top additional frame with М3х12 screws and nuts. ![Installation of the battery compartment](../assets/mountHolder.png)
1. Attach the battery compartment on top of the additional frame with М3х12 screws and nuts. ![Installation of the battery compartment](../assets/mountHolder.png)
2. Attach the top additional frame to the legs with М3х8 screws. ![Installation of the battery compartment](../assets/isoViewmountHolder.png)
3. Install the battery into the battery compartment.
@@ -370,13 +370,13 @@ Requires the following components:
![Installation of the battery compartment](../assets/mountAntenna.png)
The copter is ready for configuration!
The drone is ready for configuration!
## Safety notes for assembly ans configuration
## Safety notes for assembly and configuration
1. Remove the propellers.“All ground operations are to be performed with propellers removed. Propellers are to be installed on the motors before the flight only.”
2. Disconnect the battery. Keep the power off. “Assembly, configuration, and maintenance should be performed with power disconnected. Connect power only for testing electronic components of the copter. After testing, power is to be disconnected before other works.”
3. Call for help. “If you experience problem when completing the task, contact the instructor or the teacher, do not try to solve the problem yourself.”
2. Disconnect the battery. Keep the power off. “Assembly, configuration, and maintenance should be performed with power disconnected. Connect power only for testing electronic components of the drone. After testing, power is to be disconnected before other works.”
3. Call for help. “If you experience problem when working with the drone, contact the instructor or the teacher, do not try to solve the problem yourself.”
![Safety during assembly](../assets/safetybyassem.png)
@@ -384,6 +384,6 @@ The copter is ready for configuration!
1. Handle batteries carefully. Avoid falls, bumps, and deformations.
2. When connecting (disconnecting) batteries, hold only the connectors, never pull or tug the wires.
3. If you see open connectors, violation of insulation or battery compartment integrity, do not touch it, and immediately inform the instructor.
3. If you see open connectors, violation of insulation or battery compartment integrity, do not touch it, and immediately inform the instructor or teacher.
See article [safety precautions when soldering and during copter flight operation](safety.md)
See article [safety precautions when soldering and during drone flight operation](safety.md)

View File

@@ -20,25 +20,25 @@ TODO
![Conventional symbols](../assets/en/conditional_refer.jpg)
## Installation of motors
## Motor installation
1. Unpack the motors.
2. Attach a motor to the beam with М3х6 hex screws (the shortest screws supplied with the motors).
2. Attach a motor to the motor mount with М3х6 hex screws (the shortest screws supplied with the motors).
A hex wrench included.
A hex wrench is included.
3. Insert M3 nuts (4 pcs) into the plastic holder.
For convenience, you can use a long screw or pliers
The choice is yours to use a long screw or pliers.
4. Secure the beam, the lower beam guard and the holder with М3х12 screws, using a Phillips screwdriver.
5. Using a clamp connect the beam and the bottom guard of the beam.
4. Secure the motor mount, the lower motor mount guard and the holder with М3х12 screws, using a Phillips screwdriver.
5. Using a clamp connect the motor mount and its bottom guard.
Cut the remaining part of the clamp (tie wrap) with scissors.
Cut the remaining part of the clamp (cable tie) with scissors.
![Preparation of motors](../assets/en/cl3_prepareMotors.JPG)
## Installation of frame elements
## Frame elements installation
1. Insert the M3 plastic nuts (4 pcs) for mounting the PDB on the frame with М3х8 screws.
2. Install 6 mm legs (4 pcs) for attaching the Raspberry Pi to the frame with М3х8 screws.
@@ -47,29 +47,29 @@ TODO
![Legs installation on the frame](../assets/en/cl3_mountElements.JPG)
## Installation of the BEC voltage converter (to be soldered and tested)
## BEC voltage converter installation(to be soldered and tested)
1. Unpack the power board and install the power flat cable.
1. Unpack the power board and install the power ribbon cable.
2. Switch the multimeter in the DC voltage measurement mode (20V or 200V range).
3. Check operation of the power board by connecting the battery
3. Check the correct functionning of the power board by connecting the battery.
* Voltage measurements are to be made between black and red wires.
* Output voltage at the XT30 connector should be equal to the battery voltage (10 V to 12.6 V).
* The output voltage at the power flat cable should be between 4.9 V to 5.3 V.
* To be measured between the black and the red wires.
* The output voltage at the power ribbon cable should be between 4.9 V to 5.3 V.
4. Unpack the voltage converter and remove the transparent insulation.
5. Solder two additional wires to the BEC
* Take 3 male-female wires from the kit (red, black, and any color)
* The red and black wires [are to be blanched](zap.md) on both ends using tweezers. The blue wire is to be blanched from the side of the MALE connector.
* The red and black wires [are to be tinned](zap.md) on both ends using tweezers. The blue wire is to be tinned from the side of the MALE connector.
To blanch means:
To tin means:
* Apply flux to the exposed part of the wire.
* Cover with solder.
* Cover with tin.
* Solder the red and the black wires to BEC:
BLACK -> OUT-
RED -> OUT+
6. Check BEC operation.
6. Check BEC functionning.
* Solder the BEC to the power board:
BLACK -> -
@@ -77,17 +77,17 @@ TODO
* Connect the battery and check the voltage at the wires soldered to BEC (from step 5).
5 V - everything is okay!
5 V - great, everything is working properly!
more than 10 V - disconnect the power and move the yellow jumper to the other tweezers.
0 V - poorly soldered.
* If the BEC outputs 5 V, isolate the soldered connection with a black thermal contraction tube.
7. Installation of the LED strip.
0 V - not soldered properly.
* If the BEC outputs 5 V, isolate the soldered connection with a black heat-shrink tubing.
7. LED strip installation
* Solder the wires from BEC (from step 5) to the LED strip.
* Remove the silicone layer on the strip (make an incision with a knife and tear).
* [Blanch](zap.md) the contacts of the LED strip.
* [Tin](zap.md) the contacts of the LED strip.
Red -> +5V
Black -> GND
@@ -95,7 +95,7 @@ TODO
![Installation of the BEC voltage Converter](../assets/en/cl3_mountBEC.JPG)
## Installation of the 4 in 1 ESC board and the PDB power-board
## 4 in 1 ESC board and the PDB power-board installation
1. Install the 4 in 1 ESC circuit-board as shown in the picture.
@@ -105,7 +105,7 @@ TODO
Screw M3 plastic nuts (4 PCs.) to the legs.
3. Install the PDB power distribution board as shown in the picture (the XT60 connector should point to the tail of the copter).
3. Install the PDB power distribution board as shown in the picture (the XT60 connector should point to the tail of the drone).
4. Connect the wires of the PCB power supply board and ESC XT30 board.
![Power board installation](../assets/cl3_mountESC.JPG)
@@ -130,7 +130,7 @@ TODO
> **Hint** If the remote cannot be powered on, or is blocked, see
article [remote faults](radioerrors.md).
## Checking the motors rotation direction
## Checking the directions of motors rotation
1. Turn the transmitter ON
@@ -143,14 +143,14 @@ article [remote faults](radioerrors.md).
2. Connect the S1 orange wire from the ESC board to CH3 on the receiver. Connect external power.
3. Using the left stick, set throttle to 10 %.
4. Check the motor rotation direction according to the scheme. Repeat for each motor. Thus, it will be clear which motor is controlled.
5. If you have to change the rotation direction, toggle any two phase wires of the motor (needs re-connection).
5. If you have to change the rotation direction, swap any two phase wires of the motor (needs re-connection).
![Checking the motors rotation direction](../assets/cl3_testMotorsFlysky.JPG)
## Installation and connection of the Pixracer flight controller
1. Install the Pixracer flight controller on double-sided 3M adhesive tape (2 3 layers).
The flight controller may also be removed from the housing and rigidly mounted on the М3х6 leg.
The flight controller may also be removed from the housing and firmly mounted on the М3х6 leg.
2. Install 40 mm legs using М3х87 screws.
@@ -160,23 +160,23 @@ article [remote faults](radioerrors.md).
More [about connecting 4 in 1 ESCs](4in1.md).
4. Connect the flat cable from the radio receiver to the RCIN connector in Pixracer.
4. Connect the ribbon cable from the radio receiver to the RCIN connector in Pixracer.
![Installation of the flight controller](../assets/cl3_mountPixracer.JPG)
## Installation of Raspberry
## Raspberry installation
1. Turn the copter upside down.
1. Turn the drone upside down.
Install Raspberry on the legs using Raspberry mounting holes.
Install the Raspberry on the legs using Raspberry mounting holes.
USB connectors should point to the tail of the copter.
USB connectors should point to the tail of the drone.
2. Installation of the flat cable for the camera:
2. Installation of the ribbon cable for the camera:
* lift the latch;
* connect the flat cable;
* connect the ribbon cable;
* close the latch.
3. Connecting Raspberry power:
3. Connecting Raspberry to power supply:
5V -> pin 04 (DC power 5 V)
GND -> pin 06 (Ground)
@@ -188,24 +188,24 @@ article [remote faults](radioerrors.md).
![Installation of Raspberry Pi Model B](../assets/cl3_mountRaspberryPi.JPG)
## Installation of Arduino and the FlySky radio receiver
## Arduino and FlySky radio receiver installation
1. Mount the pins of the Arduino Nano micro-controller using soldering.
1. Solder Arduino Nano micro-controller pins to its board.
2. Install the micro-controller into a special mount, and attach to the lower deck using М3х16 screws (4 pcs).
3. Using double-sided tape, attach the receiver as shown in the picture.
4. Connect the flat cable from the radio receiver to Pixracer as shown in the picture.
4. Connect the ribbon cable from the radio receiver to Pixracer as shown in the picture.
white -> PPM
red -> 5V
black -> GND
orange, green -> currently not used. They are set to the unused pins of the radio receiver
orange, green -> currently not used. They are set to the unused pins of the radio receiver.
![Installation of Arduino nano and radio receiver Flysky i6](../assets/cl3_mountArduinoandFlysky.JPG)
![Arduino and FlySky radio receiver installation](../assets/cl3_mountArduinoandFlysky.JPG)
## Installation of the RPi camera
## RPi camera installation
1. Attach the mount for the RPi camera assembly to the lower deck with М3х12 screws (2 pcs.)
2. Connect the flat cable to the RPi camera.
2. Connect the ribbon cable to the RPi camera.
3. Install the camera into the mount, secure it with M2 self-tappers.
4. Attach Raspberry with 30 mm legs (4 pcs.).
@@ -225,11 +225,11 @@ article [remote faults](radioerrors.md).
![Installation of the remaining structural elements](../assets/cl3_mountOtherElements.JPG)
## Installation of USB connectors
## USB connectors installation
1. Connect Pixracer to Raspberry using the micro USB - USB cable.
2. Connect Arduino to Raspberry using the micro USB - USB cable.
![Installation of USB connectors](../assets/cl3_mountUSBconnectors.JPG).
![USB connectors installation](../assets/cl3_mountUSBconnectors.JPG).
Read more about connection in [article](connection.md).

View File

@@ -3,34 +3,76 @@ Pixhawk / Pixracer firmware flashing
Pixhawk or Pixracer firmware may be flashed using QGroundControl or command line utilities.
Various releases of stable PX4 firmwares are available from [Releases at GitHub] (https://github.com/PX4/Firmware/releases).
Modified firmware for Clever
---
The name of the firmware file contains encoded information about the target circuit-board and the release. Examples:
It is advisable to use a specialized build of PX4 with the necessary fixes and better defaults for the Clever drone. Use the latest stable release in our [GitHub repository](https://github.com/CopterExpress/Firmware/releases) with the word `clever`, for example, `v1.8.2-clever.5`.
* `px4fmu-v2_default.px4` — firmware for Pixhawk with EKF2.
* `px4fmu-v2_lpe.px4` — firmware for Pixhawk with LPE.
* `px4fmu-v4_default.px4` — firmware for Pixhawk with EKF2 and LPE (*Clever 3*).
* `px4fmu-v3_default.px4` — firmware for newer Pixhawk versions (rev. 3 chip, see Fig. + Bootloader v5) with EKF2 and LPE.
<div id="release" style="display:none">
<p>Latest stable release: <strong><a id="download-latest-release"></a></strong>.</p>
![STM revision](../assets/stmrev.jpg)
<ul>
<li>Firmware for Pixracer (<strong>Clever 4 / Clever 3</strong>) <a id="firmware-pixracer" href=""><code>px4fmu-v4_default.px4</code></a>.</li>
<li>Firmware for Pixhawk (<strong>Clever 2</strong>) <a id="firmware-pixhawk" href=""><code>px4fmu-v2_lpe.px4</code></a>.</li>
</ul>
</div>
> **Note** To download the `px4fmu-v3_default.px4` file, you may need to use the `force_upload` command in the command prompt.
<script type="text/javascript">
// get latest release from GitHub
fetch('https://api.github.com/repos/CopterExpress/Firmware/releases').then(function(res) {
return res.json();
}).then(function(data) {
// look for stable release
let stable;
for (let release of data) {
let clever = release.name.indexOf('clever') != -1;
if (clever && !release.prerelease && !release.draft) {
stable = release;
break;
}
}
let el = document.querySelector('#download-latest-release');
el.innerHTML = stable.name;
el.href = stable.html_url;
document.querySelector('#release').style.display = 'block';
for (let asset of stable.assets) {
console.log(asset.name);
if (asset.name == 'px4fmu-v4_default.px4') {
document.querySelector('#firmware-pixracer').href = asset.browser_download_url;
} else if (asset.name == 'px4fmu-v2_lpe.px4') {
document.querySelector('#firmware-pixhawk').href = asset.browser_download_url;
}
}
});
</script>
QGroundControl
---
In QGroundControl, go to Firmware. **After** that, connect Pixhawk / Pixracer via USB.
Open the Firmware section in QGroundControl. Then, connect your Pixhawk or Pixracer via USB.
Select PX4 Flight Stack. To download and upload the standard firmware (the version with EKF2 for Pixhawk), select the "Standard Version" menu item, to load your own firmware file, select "Custom firmware file...", then click OK.
Choose PX4 Flight Stack. If you wish to install the official firmware (with EKF2 for Pixhawk), choose "Standard version". In order to flash custom firmware, choose "Custom firmware file..." and click OK.
> **Warning** Do not disconnect the USB cable until the flashing process is complete.
> **Warning** Do not unplug your flight controller from USB during flashing!
TODO: Figure.
Firmware variants
---
The name of the firmware file contains information about the target flight controller and build variant. For example:
* `px4fmu-v4_default.px4` — firmware for Pixhawk with EKF2 and LPE (**Clever 3** / **Clever 4**).
* `px4fmu-v2_lpe.px4` — firmware for Pixhawk with LPE (**Clever 2**).
* `px4fmu-v2_default.px4` — firmware for Pixhawk with EKF2.
* `px4fmu-v3_default.px4` — firmware for newer Pixhawk versions (rev. 3 chip, see Fig. + Bootloader v5) with EKF2 and LPE.
![STM revision](../assets/stmrev.jpg)
> **Note** In order to flash the `px4fmu-v3_default.px4` file, you may need to use the `force_upload` command in the command prompt.
Command prompt
---
PX4 may be compiled from the source and automatically loaded to the circuit-board from the command prompt.
PX4 may be compiled from the source and automatically flashed to the flight controller from the command prompt.
To do this, clone the PX4 repository:
@@ -44,9 +86,9 @@ Select the appropriate version (tag) using `git checkout`. Then compile and uplo
make px4fmu-v4_default upload
```
Where `px4fmu-v4_default` is the required firmware version.
Where `px4fmu-v4_default` is the required firmware variant.
To upload the `v3` firmware to Pixhawk, you may need the `force_upload` command:
In order to upload the `v3` firmware to Pixhawk, you may need to use the `force_upload` option:
```
make px4fmu-v3_default force-upload

View File

@@ -11,7 +11,7 @@ An unmanned aerial vehicle with an electronic stabilization system and the numbe
## Flight controller / autopilot
**1\.** A specialized circuit-board designed for controlling a multicopter, an aircraft or another apparatus. Examples:
Pixhawk, Ardupilot, Naze32, CC3D.
Pixhawk, ArduPilot, Naze32, CC3D.
**2\.** Software for the multicopter control circuit-board. Examples: PX4, APM, CleanFlight.
@@ -41,7 +41,7 @@ Clever may also be [controlled from a smartphone](rc.md).
## Arming
Armed is the state of copter readiness for the fligh. When the gas stick is lifted, or when an external command with the target point is sent, the copter will fly. Usually, a copter starts rotating its propellers when it is switched to the "armed" state, even if the gas stick is down.
Armed is the state of copter readiness for the flight. When the gas stick is lifted, or when an external command with the target point is sent, the copter will fly. Usually, a copter starts rotating its propellers when it is switched to the "armed" state, even if the gas stick is down.
The opposite state is Disarmed.

98
docs/en/gpio.md Normal file
View File

@@ -0,0 +1,98 @@
# Working with GPIO
A GPIO (General-Purpose Input/Output) pin is a programmable digital signal pin on a circuit board or a microcontroller that may act as an input or an output. Raspberry Pi has a set of easily accessible GPIO pins, some of which have hardware <abbr title="Pulse-width modulation">PWM</abbr>.
> **Info** Use the [pinout](https://pinout.xyz) for figuring out, which Raspberry Pi's pins support GPIO and PWM.
The [`pigpio`](http://abyz.me.uk/rpi/pigpio) library for interfacing with the GPIO pins is already preinstalled on [the RPi image](microsd_images.md). To interact with this library, run the appropriate daemon:
```bash
sudo systemctl start pigpiod.service
```
To enable automatic launch of the daemon, run:
```bash
sudo systemctl enable pigpiod.service
```
Example of working with the library:
```python
import time
import pigpio
# initializing connection to pigpiod
pi = pigpio.pi()
# set pin 11 mode for output
pi.set_mode(11, pigpio.OUTPUT)
# set signal of pin 11 to high
pi.write(11, 1)
time.sleep(2)
# set signal on pin 11 to low
pi.write(11, 0)
# ...
# setting pin 12 mode for input
pi.set_mode(12, pigpio.INPUT)
# read the state of pin 12
level = pi.read(12)
```
To find out the pins' numbers, consult the [Raspberry Pi pinout](https://pinout.xyz).
## Connecting servos
Servo motors are typically controlled with PWM signal. Extreme positions of servos are reached with signal widths approximately equal to 1000 and 2000 µs. Values for a specific servo can be determined experimentally.
Connect the signal wire of the servo to one of GPIO-pins of the Raspberry. To control a servo connected to the pin 13 use a code like this:
```python
import time
import pigpio
pi = pigpio.pi()
# set mode of pin 13 to output
pi.set_mode(13, pigpio.OUTPUT)
# set pin 13 to output PWM signal 1000 us
pi.set_servo_pulsewidth(13, 1000)
time.sleep(2)
# set pin 13 to output PWM signal 2000 us
pi.set_servo_pulsewidth(13, 2000)
```
## Connecting an electromagnet
![GPIO Mosfet Magnet Connection](../assets/gpio_mosfet_magnet.png)
To connect an electromagnet use a field-effect transistor (MOSFET). Connect the MOSFET to one of GPIO-pins of the Raspberry Pi. To control the magnet connected to the pin 15 use a code like this:
```python
import time
import pigpio
pi = pigpio.pi()
# set mode of pin 15 for output
pi.set_mode(15, pigpio.OUTPUT)
# enable the magnet
pi.write(15, 1)
time.sleep(2)
# disable the magnet
pi.write(15, 0)
```
> **Note** A more [comprehensive description](https://elinux.org/RPi_Low-level_peripherals) of the Raspberry Pi GPIO pins and [additional examples](https://elinux.org/RPi_GPIO_Interface_Circuits) of circuits are available at the [Embedded Linux wiki](https://elinux.org/RPi_Hub).

31
docs/en/hostname.md Normal file
View File

@@ -0,0 +1,31 @@
# Hostname
[By default](microsd_images.md) the hostname of the Clever drone is set to `clever-xxxx`, where `xxxx` are random numbers. These numbers are the same as in the [Wi-Fi SSID](wifi.md).
Thus, Clever is accessible on machines that support mDNS as `clever-xxxx.local`. You can use this name to access Clever over SSH:
```bash
ssh pi@clever-xxxx.local
```
Also, this name can be used in place of IP-address to open Clever web pages in browser, accessing ROS master, etc.
## Changing hostname
In some situations it is necessary to change Clever's hostname. You can use the `hostnamectl` utility for that:
```bash
sudo hostnamectl set-hostname newname
```
Where `newname` is the new name of the machine. `hostnamectl` utility will change the name in `/etc/hostname` file.
You should also put the new name to `/etc/hosts` file:
```txt
127.0.1.1 newname newname.local
```
Setting `newname.local` is necessary to allow ROS to resolve this name in situations where all the network interfaces are down (when Wi-Fi is turned off or disconnected).
> **Note** Changing the hostname does not affect the Wi-Fi SSID (and vice versa, changing the Wi-Fi SSID won't affect the hostname).

View File

@@ -0,0 +1,152 @@
# Human Pose Estimation drone control
## Introduction
Human pose estimation is one of the computer vision applications in order to estimate all the joints and the different poses of the human body through a special camera and a special hardware or process the images from a regular camera by machine learning and deep learning techniques. This project is about controlling the drone through the poses of a person in front of regular camera without needing to an external hardware or hard build dependencies on your computer.
## Demo
<iframe width="770" height="421" src="https://www.youtube.com/embed/ucPONeHg2lk" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
<iframe width="853" height="480" src="https://www.youtube.com/embed/EDcTtPLxzoU" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
In the demo video, we were using Ubuntu 18.04 and clever4 drone.
## Development
We used posenet from tensorflow.js as our human pose estimation module because it is easier to use, build, fast and compatible with different environments(Hardware and OS). You can find the work of posenet for this project [here](https://github.com/hany606/tfjs-posenet). Websockets were used as communication protocol between the browser and a running server on the drone.
## Architecture
The image below is a visualization of our architecture for the project.
![Architecture](../assets/human_pose_estimation_project_architecture.png)
This figure is made from [here](https://www.draw.io/)
## Dependencies
Before you test it you need to install on your laptop:
- Install Nodejs from [here](https://nodejs.org/en/download/). For [Ubuntu installation](https://tecadmin.net/install-latest-nodejs-npm-on-ubuntu/)
- Install Yarn package manager from [here](https://yarnpkg.com/lang/en/docs/install/). [Usual problem](https://github.com/yarnpkg/yarn/issues/3189) while installing and using yarn with Ubuntu.
- Have an experience in manual control on the drone in case of any weird behavior happen.
- Worked before with COEX drones, if this is your first time to work with COEX drones check [this](https://clever.copterexpress.com/en/).
and you are ready to build and use the required codes.
## Setup & installation
### In your main laptop
#### (It has been tested until now only on Ubuntu 18.04)
- Clone the repo of posenet in your computer or download it if you are using Windows without GitHub
```sh
git clone https://github.com/hany606/tfjs-posenet.git
```
- Do the steps of running and setup as it is described in the README [here](https://github.com/hany606/tfjs-posenet/tree/master/posenet)
### In the Raspberry Pi of the drone (Main controller)
- Access the Raspberry Pi
- [Switch to Client mode](https://clever.copterexpress.com/en/network.html) and ensure that the network has internet connection.
Notice: I have already made a bash script based on that tutorial, it is in COEX-Internship19/helpers/ called .to_client.bash
To run it:
```sh
chmod +x .to_client.bash
./.to_client <NAME_OF_NETWORK> <PASSWORD>
```
- Install the tornado library to make a WebSocket server
```sh
sudo pip install tornado
```
- Clone the main repo on the Raspberry Pi of the drone
```sh
git clone https://github.com/hany606/COEX-Internship19.git
```
- Go to the project directory
```sh
cd COEX-Internship19/projects/Human_pose_estimation_drone_control/
```
- Run the server to test that everything is correct and run the posenet, you should see a lot of data is printed in the terminal (if you are running the human pose estimation code on your main computer, just refresh the page in the browser after running the below command in Raspberry Pi)
```sh
python websocket_server_test.py
```
- Close the server using Ctrl+C
- To run the main file
```sh
python main_drone.py
```
## How to use it
- Run the server first from the Raspberry Pi from the correct directory
```sh
python main_drone.py
```
- Run Human pose estimation module on your laptop with WebSocket by
```sh
yarn websocket
```
Or refresh the page if you already run it.
- You should see the instructions on the screen of the terminal of the Raspberry Pi right now.
- Firstly, you should be visible for the camera and it is better to have a clear background without many details.
- Secondly, you should do initial pose as it is described in the images below.
- You can perform any pose and try to keep it until your drone finish doing this move that is corresponding to the pose.
- After you do the pose return to the initial pose in order to give the drone the command to listen to another pose.
- If you want to stop the program, land the drone and don't return to the initial pose and press Ctrl+C to stop the drone.
## Poses
![Poses](../assets/human_pose_estimation_project_poses.jpg)
Animation is created by [this](https://justsketchme.web.app/)
## Notes
- Websockets are used to communicate between the page on the browser that runs posenet and the drone.
- As the model of posenet is already pre-trained and using tensorflow.js. So, it is quite fast and can run on different computers without any problems thanks to yarn, parcel and tensorflow.js, and we have configured the code of posenet to the minimal configuration to not require a lot of computation power.
- This project has been built in 1 week of working, it took a lot of time trying to make openpose and google colab working but unfortunately I had many errors and one I decided to move to posenet everything was pretty easy.
- If you have any comments about the codes to try to improve it, I will be happy if you can contact me through telegram: @hany606 or email: h.hamed.elanwar@gmail.com or do pull requests.
- If you have implemented any of the applications below, or do some improvements, we will be very happy for that.
## Future application
- [Drone wars](https://web.facebook.com/COEXDrones/photos/pcb.1129309377266616/1129308437266710/?type=3&theater): Control the drone during the drones battle using human poses. It requires high speed interaction and more precise control.
- Control a drone that draw graffiti using human poses and draw in real-time.
- Playing with balls like ping pong game with the drones. It may require 3D Human Pose estimation Algorithms.
- Control two drones by your arms and do some task together.
## Acknowledgments
- This project was part of an internship in COEX in July 2019. if you found any bugs or problems, you can contact me through telegram: @hany606 or email: h.hamed.elanwar@gmail.com.
- The above applications were thought by me and my internship supervisor Timofey.
## References
- [Human pose estimation guide](https://blog.nanonets.com/human-pose-estimation-2d-guide/)
- [Clever drones tutorials](https://clever.copterexpress.com/en/)
- [Posenet GitHub repo](https://github.com/tensorflow/tfjs-models/tree/master/posenet)
- [Posenet meduim article](https://medium.com/tensorflow/real-time-human-pose-estimation-in-the-browser-with-tensorflow-js-7dd0bc881cd5)
- [Tensorflow.js demos](https://www.tensorflow.org/js/demos)
- [Posenet overview](https://www.tensorflow.org/lite/models/pose_estimation/overview)

View File

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

View File

@@ -1,46 +1,46 @@
# MAVROS
Main documentation: [http://wiki.ros.org/mavros](http://wiki.ros.org/mavros)
Main article is available in the official documentations: [http://wiki.ros.org/mavros](http://wiki.ros.org/mavros)
MAVROS \(MAVLink + ROS\) is a package for ROS that provides the possibility of controlling drones via the [MAVLink](mavlink.md) protocol. MAVROS supports flight stacks PX4 and APM. Communication is established via UART, USB, TCP or UDP.
MAVROS \(MAVLink + ROS\) is a ROS package that allows controlling drones via the [MAVLink](mavlink.md) protocol. MAVROS supports PX4 and APM flight stacks. Communication may be established via UART, USB, TCP or UDP.
MAVROS subscribes to certain ROS topics while waiting for commands, publishes telemetry to other topics, and provides services.
MAVROS subscribes to certain ROS topics that can be used to send commands, publishes telemetry to other topics, and provides services.
The MAVROS node is automatically started in the launch-file of Clever. For [setting the type of connection] (connection.md), see the `fcu_conn` argument.
The MAVROS node is automatically started in the Clever launch-file. In order to [set the type of connection](connection.md), change the `fcu_conn` argument.
> **Hint** Simplified interaction with the copter may be with the use of the [`simple_offboard`] package (simple_offboard.md).
> **Hint** Simplified interaction with the drone is possible with the use of [`simple_offboard`] package (simple_offboard.md).
<!-- -->
> **Note** In the `clever` package, some MAVROS plugins are disabled (to save resources). For more information, see the `plugin_blacklist` parameter in file `/home/pi/catkin_ws/src/clever/clever/launch/mavros.launch`.
> **Note** Some MAVROS plugins are disabled by default in the `clever` package in order to save resources. For more information, see the `plugin_blacklist` parameter in `/home/pi/catkin_ws/src/clever/clever/launch/mavros.launch`.
## Main services
`/mavros/set_mode` — set [flight mode](modes.md) of the controller. Usually, the OFFBOARD mode is set \(for control from Raspberry Pi\).
`/mavros/set_mode` — set [flight mode](modes.md) of the controller. Most often used to set the OFFBOARD mode to accept commands from Raspberry Pi.
`/mavros/cmd/arming`enable or disable drone motors \\ (change the armed status \\).
`/mavros/cmd/arming`arm or disarm drone motors \(change arming status\).
## Main published topics
`/mavros/state` — status of connection to the flight controller. The flight controller mode.
`/mavros/state` — status of connection to the flight controller and flight controller mode.
`/mavros/local_position/pose` — local position of the copter in the ENU system of coordinates, and its orientation.
`/mavros/local_position/pose` — local position and orientation of the copter in the ENU coordinate system.
`/mavros/local_position/velocity` — current speed in the local coordinates. Angular velocities.
`/mavros/local_position/velocity` — current speed in local coordinates and angular velocities.
`/mavros/global_position/global` the current global position \(latitude, longitude, altitude\).
`/mavros/global_position/global` — current global position \(latitude, longitude, altitude\).
`/mavros/global_position/local` — the global position in the [UTM] system of coordinates (https://ru.wikipedia.org/wiki/Системаоординат_UTM).
`/mavros/global_position/local` — the global position in the [UTM](https://en.wikipedia.org/wiki/Universal_Transverse_Mercator_coordinate_system) coordinate system.
`/mavros/global_position/rel_alt` — relative altitude \(relative to the engines ON altitude\).
`/mavros/global_position/rel_alt` — relative altitude \(relative to the arming altitude\).
Messages published in the topics may be viewed by using the `rostopic` utility, e.g., `rostopic echo /mavros/state`. See more in [working with ROS](ros.md).
Messages published in the topics may be viewed with the `rostopic` utility, e.g., `rostopic echo /mavros/state`. See more in [working with ROS](ros.md).
## Main topics for publication
`/mavros/setpoint_position/local` — set the target position and the yaw of the drone \(in the ENU system of coordinates\).
`/mavros/setpoint_position/local` — set target position and yaw of the drone \(in the ENU coordinate system\).
`/mavros/setpoint_position/cmd_vel` — set the target linear velocity of the drone.
`/mavros/setpoint_position/cmd_vel` — set target linear velocity of the drone.
`/mavros/setpoint_attitude/attitude` and `/mavros/setpoint_attitude/att_throttle` — set target attitude and throttle level.
@@ -48,8 +48,8 @@ Messages published in the topics may be viewed by using the `rostopic` utility,
### Topics for sending raw packets
`/mavros/setpoint_raw/local` — sending packet [SET\_POSITION\_TARGET\_LOCAL\_NED](https://pixhawk.ethz.ch/mavlink/#SET_POSITION_TARGET_LOCAL_NED). Allows setting target position/target speed and target yaw/angular yaw velocity. The values to be set are selected using the `type_mask` field.
`/mavros/setpoint_raw/local` — sends [SET\_POSITION\_TARGET\_LOCAL\_NED](https://mavlink.io/en/messages/common.html#SET_POSITION_TARGET_LOCAL_NED) message. Allows setting target position/target speed and target yaw/angular yaw velocity. The values to be set are selected using the `type_mask` field.
`/mavros/setpoint_raw/attitude` — sending packet [SET\_ATTITUDE\_TARGET](https://pixhawk.ethz.ch/mavlink/#SET_ATTITUDE_TARGET). Allows setting the target attitude /angular velocity and throttle level. The values to be set are selected using the `type_mask` field
`/mavros/setpoint_raw/attitude` — sends [SET\_ATTITUDE\_TARGET](https://mavlink.io/en/messages/common.html#SET_ATTITUDE_TARGET) message. Allows setting the target attitude /angular velocity and throttle level. The values to be set are selected using the `type_mask` field
`/mavros/setpoint_raw/global` — sending packet [SET\_POSITION\_TARGET\_GLOBAL\_INT](https://pixhawk.ethz.ch/mavlink/#SET_POSITION_TARGET_GLOBAL_INT). Allows setting the target attitude in global coordinates \(latitude, longitude, altitude\) and flight speed. **Not supported in PX4** \([issue](https://github.com/PX4/Firmware/issues/7552)\).
`/mavros/setpoint_raw/global` — sends [SET\_POSITION\_TARGET\_GLOBAL\_INT](https://mavlink.io/en/messages/common.html#SET_POSITION_TARGET_GLOBAL_INT). Allows setting the target attitude in global coordinates \(latitude, longitude, altitude\) and flight speed. **Not supported in PX4** \([issue](https://github.com/PX4/Firmware/issues/7552)\).

View File

@@ -1,22 +1,21 @@
# Configuring Wi-Fi
The Raspberry Pi Wi-Fi adapter of has two main operating modes:
The Raspberry Pi Wi-Fi adapter has two main operating modes:
1. **Client mode** RPi connects to an existing Wi-Fi network.
2. **Access point mode** RPi creates a Wi-Fi network that you can connect to.
When using [the RPi image](microsd_images.md), the Wi-Fi adapter works in the [access point mode] by default (Wi-Fi.md).
On our [RPi image](microsd_images.md) the Wi-Fi adapter is confuigured to use the [access point mode](Wi-Fi.md) by default.
## Changing the password or SSID (of the network name)
1. Edit file `/etc/wpa_supplicant/wpa_supplicant.conf` (using [SSH connection](ssh.md)):
1. Edit the `/etc/wpa_supplicant/wpa_supplicant.conf` file (using [SSH connection](ssh.md)):
```bash
sudo nano /etc/wpa_supplicant/wpa_supplicant.conf
```
To change the name of the Wi-Fi network, change the value of parameter `ssid`, to change the password, change parameter `psk`. For example:
In order to change the name of the Wi-Fi network, change the value of the `ssid` parameter; to change the password, change the `psk` parameter. For example:
```txt
network={
@@ -33,9 +32,9 @@ When using [the RPi image](microsd_images.md), the Wi-Fi adapter works in the [a
2. Restart Raspberry Pi.
> **Caution** The length of the password for the Wi-Fi network should be **at least** 8 characters.
> **Caution** The Wi-Fi network password should be **at least** 8 characters.
>
> In case of incorrect settings `wpa_supplicant.conf`, Raspberry Pi will stop distributing Wi-Fi!
> If your `wpa_supplicant.conf` is not valid, Raspberry Pi will not allow Wi-Fi connections!
## Switching adapter to the client mode
@@ -46,39 +45,27 @@ When using [the RPi image](microsd_images.md), the Wi-Fi adapter works in the [a
sudo systemctl disable dnsmasq
```
2. Enable obtaining IP address on the wireless interface by the DHCP client.
For this purpose, remove the following lines
2. Enable DHCP client on the wireless interface to obtain IP address. In order to do this, remove the following lines from the `etc/dhcpcd.conf` file:
```conf
interface wlan0
static ip_address=192.168.11.1/24
```
from file `/etc/dhcpcd.conf` manually, or run the following commands.
3. Configure `wpa_supplicant` to connect to an existing access point. Change your `/etc/wpa_supplicant/wpa_supplicant.conf` to contain the following:
```bash
sudo sed -i 's/interface wlan0//' /etc/dhcpcd.conf
sudo sed -i 's/static ip_address=192.168.11.1\/24//' /etc/dhcpcd.conf
```
3. Configure `wpa_supplicant` to connect to an existing access point.
```bash
cat << EOF | sudo tee /etc/wpa_supplicant/wpa_supplicant.conf
ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
update_config=1
country=GB
network={
ssid="CLEVER"
psk="cleverwifi"
ssid="SSID"
psk="password"
}
EOF
```
where `CLEVER` is the name of the network, and `cleverwifi` is the password.
where `SSID` is the name of the network, and `password` is its password.
4. Restart the `dhcpcd` service.
@@ -88,35 +75,22 @@ When using [the RPi image](microsd_images.md), the Wi-Fi adapter works in the [a
## Switching the adapter to the access point mode
1. Enable the static IP address in the wireless interface.
For this purpose, add the following lines
1. Enable the static IP address in the wireless interface. Add the following lines to your `/etc/dhcpcd.conf` file:
```conf
interface wlan0
static ip_address=192.168.11.1/24
```
to file `/etc/dhcpcd.conf` manually, or run the following command.
2. Configure wpa_supplicant to work in the access point mode. Change your `/etc/wpa_supplicant/wpa_supplicant.conf` file to contain the following:
```bash
cat << EOF | sudo tee -a /etc/dhcpcd.conf
interface wlan0
static ip_address=192.168.11.1/24
EOF
```
2. Configure wpa_supplicant to work in the access point mode.
```bash
cat << EOF | sudo tee /etc/wpa_supplicant/wpa_supplicant.conf
ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
update_config=1
country=GB
network={
ssid="CLEVER-$(head -c 100 /dev/urandom | xxd -ps -c 100 | sed -e 's/[^0-9]//g' | cut -c 1-4)"
ssid="CLEVER-1234"
psk="cleverwifi"
mode=2
proto=RSN
@@ -125,23 +99,23 @@ When using [the RPi image](microsd_images.md), the Wi-Fi adapter works in the [a
group=CCMP
auth_alg=OPEN
}
EOF
```
3. Restart the `dhcpcd` service.
where `CLEVER-1234` is the network name and `cleverwifi` is the password.
```bash
sudo systemctl restart dhcpcd
```
4. Enable the `dnsmasq` service.
3. Enable the `dnsmasq` service.
```bash
sudo systemctl enable dnsmasq
sudo systemctl start dnsmasq
```
4. Restart the `dhcpcd` service.
```bash
sudo systemctl restart dhcpcd
```
___
Below you can read more about how RPi networking is organized.
@@ -150,16 +124,16 @@ Below you can read more about how RPi networking is organized.
Network operation in the [image](microsd_images.md) is supported by two pre-installed services:
* **networking** — the service enables all network interfaces at the moment of start [5].
* **dhcpcd** — the service ensures configuration of addressing and routing on the interfaces obtained dynamically, or specified statically in the config file.
* **networking** — the service enables all network interfaces at startup [5].
* **dhcpcd** — the service ensures that configuration of addressing and routing on the interfaces is obtained dynamically or specified statically in the config file.
To work in the router (access point) mode, RPi requires a DHCP server. It is used to automatically send the settings of the current network to connected clients. `isc-dhcp-server` or `dnsmasq` may be used as such server.
To work in the router (access point) mode, RPi requires a DHCP server. It is used to automatically send the settings of the current network to connected clients. `isc-dhcp-server` or `dnsmasq` may be used for this.
### dhcpcd
Starting with Raspbian Jessy, network settings are no longer defined in the `/etc/network/interfaces` file. Now `dhcpcd` is used for sending addressing and routing settings[4].
Starting with Raspbian Jessie, network settings are no longer defined in the `/etc/network/interfaces` file. Now `dhcpcd` is used for sending addressing and routing settings[4].
By default, a dhcp client is enabled in all interfaces. Settings of the interfaces are changed in the `/etc/dhcpcd.conf` file. To start an access point, specify a static IP address. To do this, add the following lines to the end of the file:
By default, a dhcp client is enabled in all interfaces. Settings for network interfaces are changed in the `/etc/dhcpcd.conf` file. An access point should have a static IP address. To specify one, add the following lines to the end of the file:
```
interface wlan0
@@ -170,9 +144,9 @@ static ip_address=192.168.11.1/24
### wpa_supplicant
**wpa_supplicant** — the service configures the Wi-Fi adapter. The `wpa_supplicant` service runs not as standalone (although it exists as such), and runs as a `dhcpcd` child process.
**wpa_supplicant** — the service configures the Wi-Fi adapter. The `wpa_supplicant` service does not run as a standalone service (although it exists as such), but is instead launched as a `dhcpcd` child process.
By default, the config file should contain path `/etc/wpa_supplicant/wpa_supplicant.conf`.
By default the config file is `/etc/wpa_supplicant/wpa_supplicant.conf`.
An example of the configuration file:
```conf
@@ -192,7 +166,7 @@ network={
}
```
Inside the config file, general `wpa_supplicant` settings, and the settings for the adapter configuration are specified. The configuration file also contains `network` section with the basic settings of the Wi-Fi network, such as network SSID, password, adapter operating mode. There may be several such sections, but only the first one is the working one. For example, if the first section contains connection to an unavailable network, the adapter will be configured according to a next valid section, if there is one. Read more about the syntax of `wpa_supplicant.conf` [TODO WIKI].
Inside the config file, general `wpa_supplicant` settings, and the settings for the adapter configuration are specified. The configuration file also contains `network` section with the basic settings of the Wi-Fi network, such as network SSID, password, adapter operating mode. There may be several `network` sections, but only the first valid one is used. For example, if the first section contains a connection to an unavailable network, the adapter will be configured according to a next valid section, if there is one. Read more about the syntax of `wpa_supplicant.conf` [TODO WIKI].
#### wpa_passphrase

101
docs/en/optical_flow.md Normal file
View File

@@ -0,0 +1,101 @@
# Use of Optical Flow
Running the technology "Optical Flow" offers the possibility of POSCTL flight mode, and autonomous flight operating on a camera pointed downwards that detects changes of ground texture.
## Enabling
> **Hint** It is recommanded to use [special PX4 firmware for Clever](firmware.md#прошивка-для-клевера).
The use of a rangefinder is essential. [Connect and setup laser-ranging sensor VL53L1X](laser.md), according to the manual.
Enable Optical Flow in the file `~/catkin_ws/src/clever/clever/launch/clever.launch`:
```xml
<arg name="optical_flow" default="true"/>
```
Optical Flow publishes data in `mavros/px4flow/raw/send` topic. In the topic `optical_flow/debug` is also published a vizualization, that can be viewed with [web_video_server](web_video_server.md).
> **Info** Correct connexion and [setup](camera.md) of the camera module is needed for proper functioning.
## Setup of the flight controler
When using **EKF2** (parameter `SYS_MC_EST_GROUP` = `ekf2`):
* `EKF2_AID_MASK` flag 'use optical flow' is on.
* `EKF2_OF_DELAY`  0.
* `EKF2_OF_QMIN` 10.
* `EKF2_OF_N_MIN`  0.05.
* `EKF2_OF_N_MAX` - 0.2.
* `SENS_FLOW_ROT` No rotation.
* `SENS_FLOW_MAXHGT` 4.0 (for the rangefinder VL53L1X)
* `SENS_FLOW_MINHGT` 0.01 (for the rangefinder VL53L1X)
* Optional: `EKF2_HGT_MODE` range sensor (cf. [rangefinder setup](laser.md)).
When using **LPE** (parameter `SYS_MC_EST_GROUP` = `local_position_estimator, attitude_estimator_q`):
* `LPE_FUSION` flags 'fuse optical flow' and 'flow gyro compensation' are on.
* `LPE_FLW_QMIN` 10.
* `LPE_FLW_SCALE` 1.0.
* `LPE_FLW_R` 0.1.
* `LPE_FLW_RR` 0.0.
* `SENS_FLOW_ROT` No rotation.
* `SENS_FLOW_MAXHGT` 4.0 (for the rangefinder VL53L1X)
* `SENS_FLOW_MINHGT` 0.01 (for the rangefinder VL53L1X)
* Optional: `LPE_FUSION`  falg 'pub agl as lpos down' is on (сf. [rangefinder setup](laser.md).
[The `selfcheck.py` utility](selfcheck.md) will help you verify that all settings are corectly set.
## POSCTL flight
Setup POSCTL to be one of PX4 flight modes and then select POSCTL.
## Autonomous flight
The module [simple_offboard](simple_offboard.mb) enables autonomous flight.
Example of take off and leveling at 1.5m above the ground:
```python
navigate(z=1.5, frame_id='body', auto_arm=True)
```
Flying forward for 1m:
```python
navigate(x=1.5, frame_id='body')
```
[Navigation using ArUco-markers](aruco_marker) and [using VPE] are available when using Optical Flow.
## Additional settings
<!-- TODO: статья по пидам -->
If the copter has an unstable position using VPE, try to increase the *P* coefficient of speed PID controler - parameters are `MPC_XY_VEL_P` and `MPC_Z_VEL_P`.
If the copter has an unstable height, try increasing `MPC_Z_VEL_P` coefficient or getting better hover throttle - `MPC_THR_HOVER`.
If the copter is consistently yawing, try:
* recalibrate gyroscopes;
* recalibrate magnetometer;
* different values for `EKF2_MAG_TYPE` parameter, that indicates how data from the magnetometer is used in EKF2;
* changing values of `EKF2_MAG_NOISE`, `EKF2_GYR_NOISE`, `EKF2_GYR_B_NOISE` parameters.
If the copter's height is deviating, try:
* increasing the value of `MPC_Z_VEL_P` coefficient;
* change the value of `MPC_THR_HOVER` parameter;
* add `MPC_ALT_MODE` = 2 (Terrain following).
When using Optical Flow, the maximal horizontal speed is further limited. This is an indirect influence of the parameter `SENS_FLOW_MAXR` (maximal reliable "angular speed" of the optical flow). In normal flight mode, control loops will be adjusted so that Optical Flow values do not exceed 50% of this parameter.
## Errors
If errors of `EKF INTERNAL CHECKS` occur, try to restart EKF2. To do so, enter in the MAVLink-console : в MAVLink-консоли:
```nsh
ekf2 stop
ekf2 start
```

66
docs/en/ros-install.md Normal file
View File

@@ -0,0 +1,66 @@
# ROS Kinetic package installation and setup
In order to use tools such as rqt, rviz and others as well as running the simulator (SITL), you will need to install and setup ROS package
> **Hint** For more details on installation refer to [the main article](http://wiki.ros.org/kinetic/Installation/Ubuntu).
<!-- -->
> **Hint** If you are using Ubuntu 18.04, you will need to install ROS Melodic instead of ROS Kinetic. A complete guide of the installation is available [here](http://wiki.ros.org/melodic/Installation/Ubuntu).
## ROS Kinetic installation on Ubuntu
To find the correct package version, you will need to change the settings of your repositories. Go to "Software and updates" and enable `restricted`, `universe` and `multiverse`.
Set up your system so that software form `packages.ros.org` can be installed :
```bash
sudo sh -c 'echo "deb http://packages.ros.org/ros/ubuntu $(lsb_release -sc) main" > /etc/apt/sources.list.d/ros-latest.list'
```
Configure access keys in your system for correct download:
```bash
sudo apt-key adv --keyserver hkp://ha.pool.sks-keyservers.net:80 --recv-key 421C365BD9FF1F717815A3895523BAEEB01FA116
```
Make sure that your packages are up to date:
```bash
sudo apt-get update
```
Now you can install the ROS package itself.
+ If you plan to use ROS together with the simulator (also includes tools such as rqt, rviz and others):
```bash
sudo apt-get install ros-kinetic-desktop-full
```
+ If you plan to use ROS exclusively for tools rqt, rviz etc.:
```bash
sudo apt-get install ros-kinetic-desktop
```
After the package has installed, initialize `rosdep`.
Package `rosdep` will allow to easily install dependecies for the source files that you whish to compile. Running some essential components of ROS will as well require this package.
```bash
sudo rosdep init
rosdep update
```
If you are not confortable with entering environment variables manually each time, you may configure it in a way that it add itself in your bash session on every new shell startup:
```bash
echo "source /opt/ros/kinetic/setup.bash" >> ~/.bashrc
source ~/.bashrc
```
If you whish to install any additionnal packages for yout ROS Kinetic simply use:
```bash
sudo apt-get install ros-kinetic-PACKAGE
```

View File

@@ -14,6 +14,8 @@ ROS is already installed on [the RPi image](microsd_images.md).
To use ROS on a PC, we recommend using Ubuntu Linux (or a virtual machine such as Parallels Desktop Lite](https://itunes.apple.com/ru/app/parallels-desktop-lite/id1085114709?mt=12) or [VirtualBox](https://www.virtualbox.org)).
> **Note** For ROS Kinetic distribution, we recommend using Ubuntu 16.04.
Concepts
---
@@ -23,9 +25,9 @@ Main article: http://wiki.ros.org/Nodes
ROS node is a special program (usually written in Python or C++) that communicates with other nodes via ROS topics and ROS services. Dividing complex robotic systems into isolated nodes provides certain advantages: reduced coupling of the code, increases re-usability and reliability.
Many robotic libraries, and the driver are executed in the form of ROS-nodes.
Many robotic libraries and the drivers are executed in the form of ROS-nodes.
In order to turn an ordinary program into a ROS node, connect to it a `rospy` or `roscpp` library, and add the initialization code.
In order to turn an ordinary program into a ROS node, include a `rospy` or `roscpp` library, and insert the initialization code.
An example of a ROS node in Python:
@@ -43,7 +45,7 @@ Main article: http://wiki.ros.org/Topics
A topic is a named data bus used by the nodes for exchanging messages. Any node can *post* a message in a random topic, and *subscribe* to an arbitrary topic.
An example of posting a message of type [`std_msgs/String`](http://docs.ros.org/api/std_msgs/html/msg/String.html) (line) in topic `/foo` in Python:
An example of [`std_msgs/String`](http://docs.ros.org/api/std_msgs/html/msg/String.html) (line) message type posting in topic `/foo` in Python:
```python
from std_msgs.msg import String
@@ -77,9 +79,9 @@ rostopic echo /mavros/state
Main article: http://wiki.ros.org/Services
A service is an analogue to the function that can be called from one node, and processed in another one. The service has a name that is similar to the name of the topic, and 2 message types: request type and response type.
A service can be assimilated to the a function that can be called from one node, and processed in another one. The service has a name that is similar to the name of the topic, and 2 message types: request type and response type.
An example of invoking a ROS service from Python:
An example ROS service invoking from Python:
```python
from clever.srv import GetTelemetry

View File

@@ -23,7 +23,7 @@ ROS_MASTER_URI=http://192.168.11.1:11311 rviz
If connection is not established, make sure the `.bashrc` of Clever contains line:
```(bash)
export ROS_IP=192.168.11.1
export ROS_HOSTNAME=`hostname`.local
```
Using rviz

View File

@@ -1,12 +1,12 @@
# Automatic check
Before flying (especially autonomous), you can use several methods of automatic self-testing of the quadcopter subsystems.
It is generally a good idea to perform some checks before flight, especially before an autonomous one. There are several methods of automated self-checks of the drone subsystems.
## <span>selfcheck</span>.py
Utility `selfcheck.py` is part of `clever` package, and automatically tests the main aspects of the ROS platform and the PX4. The utility is pre-installed on [the Raspberry Pi image](microsd_images.md).
The `selfcheck.py` utility script is part of the `clever` package; it performs automated tests of the main aspects of the ROS platform and PX4. The utility is pre-installed on [the Raspberry Pi image](microsd_images.md).
To initiate it, type in [the Raspberry Pi console](ssh.md):
In order to run it, enter the following command in [the Raspberry Pi console](ssh.md):
```(bash)
rosrun clever selfcheck.py
@@ -16,17 +16,20 @@ rosrun clever selfcheck.py
Description of some checks:
* FCU checking proper connection with the flight controller;
* IMU checking correctness of the data from IMU;
* Local position presence of the local position of the drone;
* Velocity estimation drone velocity estimation (**if this check fails, never take off offline!**);
* Global position (GPS) — presence of the global position (GPS required);
* Camera — proper operation of the Raspberry camera.
* FCU checks for proper connection with the flight controller;
* IMU checks whether the data from from IMU is sane;
* Local position checks presence of local position data;
* Velocity estimation checks whether drone velocity estimation is sane(**autonomous flight is not to be performed if this check fails!**);
* Global position (GPS) — checks for presence of global position data (GPS module is required for this check);
* Camera — checks for proper operation of the Raspberry camera.
* ArUco — checks whether [ArUco](aruco.md) detection is working
* VPE — checks whether VPE data is published
* Rangefinder — checks whether [rangefinder](laser.md) data is published
## commander check
To check the main sub systems of PX4 and the possibility of arming at the moment, you can perform command `commander check` in the MAVLink console.
In order check the main subsystems of PX4 and the possibility of arming at the moment you can run the `commander check` command in the MAVLink console.
<img src="../assets/commander-check.png">
When using SITL instead of the MAVLink console, use a terminal with SITL running.
If you're running SITL, you should use the terminal where PX4 simulation is running instead of the MAVLink console.

View File

@@ -180,7 +180,7 @@ To unlock the copter, disable the safety button
1. Go to the Power menu.
2. Set the Number of cells — 4S.
3. Set parameter Full Voltage (per cell) - 4.20 V.
3. Set parameter Full Voltage (per cell) - 4.20 V; Empty Voltage (per cell) - 3.50V.
To save the changes, restart Pihxawk:

View File

@@ -1,9 +1,13 @@
Simple OFFBOARD
===
> **Note** Documentation for the [image](microsd_images.md), versions, starting with **0.15**. For older versions refer to [documentation for version **0.14**](https://github.com/CopterExpress/clever/blob/v0.14/docs/ru/simple_offboard.md).
> **Note** Documentation for the [image](microsd_images.md), versions, starting with **0.15**. For older versions refer to [documentation for version **0.14**](https://github.com/droneExpress/clever/blob/v0.14/docs/ru/simple_offboard.md).
The `simple_offboard` module of the `clever` package is intended for simplified programming of an autonomous drone ([mode](modes.md) `OFFBOARD`). It allows setting the desired flight tasks, and automatically transforms [the system of coordinates](frames.md).
<!-- -->
> **Hint** For autonomous flights it is recommanded to use [special firmware PX4 for Clever](firmware.md#прошивка-для-клевера).
The `simple_offboard` module of the `clever` package is intended for simplified programming of the autonomous drone ([mode](modes.md) `OFFBOARD`). It allows setting the desired flight tasks, and automatically transforms [the system of coordinates](frames.md).
`simple_offboard` is a high level way of interacting with the flight controller. For a more low level work, see [mavros](mavros.md).
@@ -12,7 +16,7 @@ Main services are `get_telemetry` (receiving all telemetry), `navigate` (flying
The use of Python language
---
To use the services, create proxies to them. An example of the program the declared proxies to all `simple_offboard` services:
To use the services, create proxies to them. Following is an example of the program that declares proxies to all `simple_offboard` services:
```python
import rospy
@@ -42,7 +46,7 @@ API description
### get_telemetry
Obtaining complete telemetry of the copter.
Obtaining complete telemetry of the drone.
Parameters:
@@ -52,12 +56,12 @@ Response format:
* `frame_id` — frame;
* `connected` whether there is a connection to <abbr title="Flight Control Unit flight controller">FCU</abbr>;
* `armed` `armed` state of propellers (the propellers are armed, if true);
* `armed` - state of propellers (the propellers are armed, if true);
* `mode` current [flight mode](modes.md);
* `x, y, z` — local position of the copter *(m)*;
* `x, y, z` — local position of the drone *(m)*;
* `lat, lon` latitude, longitude *(degrees)*, [GPS](gps.md) is to be available;
* `alt` altitude in the global system of coordinates (standard [WGS-84](https://ru.wikipedia.org/wiki/WGS_84), not <abbr title="Above Mean Sea Level">AMSL</abbr>!), [GPS](gps.md) is to be available ;
* `vx, vy, vz` copter velocity *(m/s)*;
* `vx, vy, vz` drone velocity *(m/s)*;
* `pitch` pitch angle *(radians)*;
* `roll` roll angle *(radians)*;
* `yaw` — yaw angle *(radians)*;
@@ -67,16 +71,16 @@ Response format:
* `voltage` total battery voltage *(V)*;
* `cell_voltage` battery cell voltage *(V)*.
> **Note** Fields that are available for some reason will contain the `NaN` value.
> **Note** Fields that are unavailabe for any reason will contain the `NaN` value.
Displaying copter coordinates `x`, `y` and `z` in the local system of coordinates:
Displaying drone coordinates `x`, `y` and `z` in the local system of coordinates:
```python
telemetry = get_telemetry()
print telemetry.x, telemetry.y, telemetry.z
```
Displaying copter altitude relative to [the card of ArUco tags](aruco.md):
Displaying drone altitude relative to [the card of ArUco tags](aruco.md):
```python
telemetry = get_telemetry(frame_id='aruco_map')
@@ -109,7 +113,7 @@ Parameters:
* `yaw` — yaw angle *(radians)*;
* `yaw_rate` angular yaw velocity (used for setting the yaw to `NaN`) *(rad/s)*;
* `speed` flight speed (setpoint speed) *(m/s)*;
* `auto_arm` switch the copter to `OFFBOARD` and arm automatically (**the copter will take off**);
* `auto_arm` switch the drone to `OFFBOARD` and arm automatically (**the drone will take off**);
* `frame_id` [system of coordinates](frames.md) for values `x`, `y`, `z`, `vx`, `vy`, `vz`. Example: `map`, `body`, `aruco_map`. Default value: `map`.
> **Note** To fly without changing the yaw angle, it is sufficient to set the `yaw` to `NaN` (angular velocity by default is 0).
@@ -132,7 +136,7 @@ Flying to point 5:0 without changing the yaw angle (`yaw` = `NaN`, `yaw_rate` =
navigate(x=5, y=0, z=3, speed=0.8, yaw=float('nan'))
```
Flying 3 m to the right from the copter:
Flying 3 m to the right from the drone:
```python
navigate(x=0, y=-3, z=0, speed=1, frame_id='body')
@@ -179,12 +183,12 @@ Parameters:
* `yaw` — yaw angle *(radians)*;
* `yaw_rate` angular yaw velocity (used for setting the yaw to `NaN`) *(rad/s)*;
* `speed` flight speed (setpoint speed) *(m/s)*;
* `auto_arm` switch the copter to `OFFBOARD` and arm automatically (**the copter will take off**);
* `auto_arm` switch the drone to `OFFBOARD` and arm automatically (**the drone will take off**);
* `frame_id` [system of coordinates](frames.md), given `z` и `yaw` (Default value: `map`).
> **Note** To fly without changing the yaw angle, it is sufficient to set the `yaw` to `NaN` (angular velocity by default is 0).
Flying to a global point at the speed of 5 m/s, while remaining at current altitude (`yaw` will be set to 0, the copter will face East):
Flying to a global point at the speed of 5 m/s, while remaining at current altitude (`yaw` will be set to 0, the drone will face East):
```python
navigate_global(lat=55.707033, lon=37.725010, z=0, speed=5, frame_id='body')
@@ -204,7 +208,7 @@ rosservice call /navigate_global "{lat: 55.707033, lon: 37.725010, z: 0.0, yaw:
### set_position
Set the target by position and yaw. This service may be used to specify the continuous flow of target points, for example, for flying along complex trajectories (circular, arcuate, etc.).
Set the target for position and yaw. This service may be used to specify the continuous flow of target points, for example, for flying along complex trajectories (circular, arcuate, etc.).
> **Hint** For flying to a point in a straight line or takeoff, use the [`navigate`] higher-level service (#navigate).
@@ -213,7 +217,7 @@ Parameters:
* `x`, `y`, `z` — point coordinates *(m)*;
* `yaw` — yaw angle *(radians)*;
* `yaw_rate` angular yaw velocity (used for setting the yaw to NaN) *(rad/s)*;
* `auto_arm` switch the copter to `OFFBOARD` and arm automatically (**the copter will take off**);
* `auto_arm` switch the drone to `OFFBOARD` and arm automatically (**the drone will take off**);
* `frame_id` [system of coordinates](frames.md), given `x`, `y`, `z` и `yaw` (Default value: `map`).
Hovering on the spot:
@@ -247,12 +251,12 @@ Setting speed and yaw.
* `vx`, `vy`, `vz` required flight speed *(m/s)*;
* `yaw` — yaw angle *(radians)*;
* `yaw_rate` angular yaw velocity (used for setting the yaw to NaN) *(rad/s)*;
* `auto_arm` switch the copter to `OFFBOARD` and arm automatically (**the copter will take off**);
* `auto_arm` switch the drone to `OFFBOARD` and arm automatically (**the drone will take off**);
* `frame_id` [system of coordinates](frames.md), given `vx`, `vy`, `vz` и `yaw` (Default value: `map`).
> **Note** Parameter `frame_id` specifies only the orientation of the resulting velocity vector, but not its length.
Flying forward (relative to the copter) at the speed of 1 m/s:
Flying forward (relative to the drone) at the speed of 1 m/s:
```python
set_velocity(vx=1, vy=0.0, vz=0, frame_id='body')
@@ -266,41 +270,41 @@ set_velocity(vx=0.4, vy=0.0, vz=0, yaw=float('nan'), yaw_rate=0.4, frame_id='bod
### set_attitude
Setting pitch, roll, yaw and throttle level (approximate analogue to control in [the `STABILIZED` mode](modes.md)). This service may be used for lower level monitoring of the copter behavior or controlling the copter, if no reliable data about its position are available.
Setting pitch, roll, yaw and throttle level (approximate analogue to control in [the `STABILIZED` mode](modes.md)). This service may be used for lower level monitoring of the drone behavior or controlling the drone, if no reliable data on its position are available.
Parameters:
* `pitch`, `roll`, `yaw` required pitch, roll, and yaw angle *(radians)*;
* `thrust` — throttle level from 0 (no throttle, propellers are stopped) to 1 (full throttle).
* `auto_arm` switch the copter to `OFFBOARD` and arm automatically (**the copter will take off**);
* `auto_arm` switch the drone to `OFFBOARD` and arm automatically (**the drone will take off**);
* `frame_id` [system of coordinates](frames.md), given `yaw` (Default value: `map`).
### set_rates
Setting pitch, roll, and yaw angular velocity and the throttle level (approximate analogue to control in [the `ACRO` mode](modes.md)). This is the lowest copter control level (excluding direct control of motor rotation speed). This service may be used to automatically perform acrobatic tricks (e.g., flips).
Setting pitch, roll, and yaw angular velocity and the throttle level (approximate analogue to control in [the `ACRO` mode](modes.md)). This is the lowest drone control level (excluding direct control of motor rotation speed). This service may be used to automatically perform acrobatic tricks (e.g., flips).
Parameters:
* `pitch_rate`, `roll_rate`, `yaw_rate` angular pitch, roll, and yaw velocity *(rad/s)*;
* `thrust` — throttle level from 0 (no throttle, propellers are stopped) to 1 (full throttle).
* `auto_arm` switch the copter to `OFFBOARD` and arm automatically (**the copter will take off**);
* `auto_arm` switch the drone to `OFFBOARD` and arm automatically (**the drone will take off**);
### land
Transfer the copter to the landing [mode](modes.md) (`AUTO.LAND` or similar).
Transfer the drone to the landing [mode](modes.md) (`AUTO.LAND` or similar).
> **Note** For automated disabling the propellers after landing [parameter PX4](px4_parameters.md) `COM_DISARM_LAND` is to be set to a value > 0.
> **Note** For automatic propeller disabling after landing, [parameter PX4](px4_parameters.md) `COM_DISARM_LAND` is to be set to a value > 0.
Landing the copter:
Landing the drone:
```python
res = land()
if res.success:
print 'Copter is landing'
print 'drone is landing'
```
Landing the copter (command line):
Landing the drone (command line):
```(bash)
rosservice call /land "{}"
@@ -309,7 +313,7 @@ rosservice call /land "{}"
<!--
### release
Stop publishing setpoints to the copter (release control). Required to continue monitoring by means of [MAVROS](mavros.md).
Stop publishing setpoints to the drone (release control). Required to continue monitoring by means of [MAVROS](mavros.md).
-->
Additional materials

View File

@@ -3,9 +3,9 @@ PX4 Simulation
Main article: https://dev.px4.io/en/simulation/
PX4 simulation is possible in Linux and macOS with the use of physical environment simulation systems [jMavSim](https://pixhawk.org/dev/hil/jmavsim) and [the Gazebo](http://gazebosim.org).
PX4 simulation is possible in Linux and macOS with the use of physical environment simulation systems [jMAVSim](https://pixhawk.org/dev/hil/jmavsim) and [the Gazebo](http://gazebosim.org).
jMavSim is a lightweight environment intended only for testing multi-rotor aircraft systems; Gazebo is a versatile environment for all types of robots.
jMAVSim is a lightweight environment intended only for testing multi-rotor aircraft systems; Gazebo is a versatile environment for all types of robots.
Launching PX4 SITL
--
@@ -17,12 +17,12 @@ git clone https://github.com/PX4/Firmware.git
cd Firmware
```
jMavSim
jMAVSim
--
Main article: https://dev.px4.io/en/simulation/jmavsim.html
For simulation using the jMavSim lightweight environment, use the following command:
For simulation using the jMAVSim lightweight environment, use the following command:
```(bash)
make posix_sitl_default jmavsim

View File

@@ -6,7 +6,7 @@ Python
### # {#distance}
The function of determining the distance between two points (**important**: the points are to be in the same [system of coordinates](frames.md)):
Calculating the distance between two points (**important**: the points are to be in the same [system of coordinates](frames.md)):
```python
def get_distance(x1, y1, z1, x2, y2, z2):
@@ -15,7 +15,7 @@ def get_distance(x1, y1, z1, x2, y2, z2):
### # {#distance-global}
A function for approximate determination of the distance (in meters) between two global coordinates (latitude/longitude):
Approximation of distance (in meters) between two global coordinates (latitude/longitude):
```python
def get_distance_global(lat1, lon1, lat2, lon2):
@@ -24,20 +24,20 @@ def get_distance_global(lat1, lon1, lat2, lon2):
### # {#block-takeoff}
Takeoff and waiting for the end of takeoff:
Takeoff and waiting for it to finish:
```python
z = 2 # altitude
tolerance = 0.2 # precision of altitude check (m)
# Remembering the initial point
# Saving the initial point
start = get_telemetry()
# Take off to the altitude of 2 m
# Take off and leveling at 2 m above the ground
print navigate(z=z, speed=0.5, frame_id='body', auto_arm=True)
# Waiting for takeoff
while True:
while not rospy.is_shutdown():
# Checking current altitude
if get_telemetry().z - start.z + z < tolerance:
# Takeoff complete
@@ -45,9 +45,23 @@ while True:
rospy.sleep(0.2)
```
This code can be wrapped in a function:
```python
def takeoff_wait(alt, speed=0.5, tolerance=0.2):
start = get_telemetry()
print navigate(z=alt, speed=speed, frame_id='body', auto_arm=True)
while not rospy.is_shutdown():
if get_telemetry().z - start.z + z < tolerance:
break
rospy.sleep(0.2)
```
### # {#block-nav}
Flying to a point and waiting for the copter to arrive at it:
Flying towards a point and waiting for copter's arrival:
```python
tolerance = 0.2 # precision of arrival check (m)
@@ -57,7 +71,7 @@ frame_id='aruco_map'
print navigate(frame_id=frame_id, x=1, y=2, z=3, speed=0.5)
# Wait for the copter to arrive at the requested point
while True:
while not rospy.is_shutdown():
telem = get_telemetry(frame_id=frame_id)
# Calculating the distance to the requested point
if get_distance(1, 2, 3, telem.x, telem.y, telem.z) < tolerance:
@@ -66,6 +80,25 @@ while True:
rospy.sleep(0.2)
```
### # {#block-land}
Landing and waiting until the copter lands:
```python
land()
while get_telemetry().armed:
rospy.sleep(0.2)
```
This code can be wrapped in a function:
```python
def land_wait():
land()
while get_telemetry().armed:
rospy.sleep(0.2)
```
### # {#disarm}
Quadcopter disarm (disabling propellers **the copter will fall down**):
@@ -93,10 +126,10 @@ tf_listener = tf2_ros.TransformListener(tf_buffer)
# ...
# Creating as PoseStamped object (or getting it from the topic):
# PoseStamped object creation (or getting it from a topic):
pose = PoseStamped()
pose.header.frame_id = 'map' # frame, which is the position is specified
pose.header.stamp = rospy.get_rostime() # the moment for which the position is specified (current time)
pose.header.stamp = rospy.get_rostime() # the instant for which the position is specified (current time)
pose.pose.position.x = 1
pose.pose.position.y = 2
pose.pose.position.z = 3
@@ -111,7 +144,7 @@ new_pose = tf_buffer.transform(pose, frame_id, transform_timeout)
### # {#upside-down}
Determining whether the copter is turned over:
Determining whether the copter is turned upside-down:
```python
PI_2 = math.pi / 2
@@ -122,7 +155,7 @@ flipped = not -PI_2 <= telem.pitch <= PI_2 or not -PI_2 <= telem.roll <= PI_2
### # {#angle-hor}
Calculating the copter total angle to the horizon:
Calculating the copter total horizontal angle:
```python
PI_2 = math.pi / 2
@@ -158,7 +191,7 @@ while not rospy.is_shutdown():
### # {#rate}
repeating an action with the frequency of 10 Hz:
repeating an action at a frequency of 10 Hz:
```python
r = rospy.Rate(10)
@@ -179,7 +212,7 @@ from mavros_msgs.msg import RCIn
# ...
def pose_update(pose):
# Processing new data about the copter position
# Processing new data of copter's position
pass
# Other handler functions
@@ -191,7 +224,7 @@ rospy.Subscriber('/mavros/battery', BatteryState, battery_update)
rospy.Subscriber('mavros/rc/in', RCIn, rc_callback)
```
Information about MAVROS topics us available at [the link](mavros.md).
Information about MAVROS topics is available at [the link](mavros.md).
<!-- markdownlint-disable MD044 -->
@@ -199,7 +232,7 @@ Information about MAVROS topics us available at [the link](mavros.md).
<!-- markdownlint-enable MD044 -->
An example of sending an arbitrary [MAVLink message](mavlink.md) to the copter:
Sending an arbitrary [MAVLink message](mavlink.md) to the copter:
```python
# ...
@@ -223,14 +256,14 @@ mavlink_pub.publish(ros_msg)
### # {#rc-sub}
Reaction to switching the mode on the transmitter (may be used for starting an offline flight, see [example](https://gist.github.com/okalachev/b709f04522d2f9af97e835baedeb806b)):
Return on mode switching with the transmitter (may be used for starting an autonomous flight, see [example](https://gist.github.com/okalachev/b709f04522d2f9af97e835baedeb806b)):
```python
from mavros_msgs.msg import RCIn
# Called when new data are received from the transmitter
# Called when new data is received from the transmitter
def rc_callback(data):
# Arbitrary reaction to switching the toggle switch on the transmitter
# Return on switch toggling of the transmitter
if data.channels[5] < 1100:
# ...
pass
@@ -247,8 +280,53 @@ rospy.Subscriber('mavros/rc/in', RCIn, rc_callback)
rospy.spin()
```
### # {#set_mode}
Change the [flight mode](modes.md) to arbitrary one:
```python
from mavros_msgs.srv import SetMode
# ...
set_mode = rospy.ServiceProxy('mavros/set_mode', SetMode)
# ...
set_mode(custom_mode='STABILIZED')
```
### # {#flip}
Flip:
Flip on roll:
TODO
```python
import math
# ...
def flip():
start = get_telemetry() # saving starting position
set_rates(thrust=1) # bump up
rospy.sleep(0.2)
set_rates(roll_rate=30, thrust=0.2) # maximum roll rate
while True:
telem = get_telemetry()
if -math.pi + 0.1 < telem.roll < -0.2:
break
rospy.loginfo('finish flip')
set_position(x=start.x, y=start.y, z=start.z, yaw=start.yaw) # finish flip
print navigate(z=2, speed=1, frame_id='body', auto_arm=True) # take off
rospy.sleep(10)
rospy.loginfo('flip')
flip()
```
Requires the [special PX4 firmware for Clever](firmware.md#modified-firmware-for-clever). Before running a flip, take all necessary safty precautions.

23
docs/ru/3dscan.md Normal file
View File

@@ -0,0 +1,23 @@
# Дрон для 3D-сканирования человека
<img src="../assets/3d_drone_1.jpg" title="Сканирующий дрон">
Проект был создан совместно с компанией Texel, которая разрабатывает стационарные 3D-сканеры для создания модели человека.
Коллеги из Texel предоставили модуль, состоящий из Raspberry Pi с установленным ПО для сканирования и камеры для захвата 3D-изображения PrimeSense.
С нашей стороны был предоставлен квадрокоптер Клевер 3 с установленным оборудованием для автономного полета, а также написана полетная программа.
Мы провели множество тестов и настроек, внесли много изменений в конструкции дрона, чтобы в итоге добиться результата.
## Видео
<iframe width="966" height="543" src="https://www.youtube.com/embed/aqBION3TVhg" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
## Команда
Над проектом работали:
* Тимофей Кондратьев [Copter Express] - сборка дрона, написание и отладка программы, проведение тестов;
* Антон Мальцев [Copter Express] - моделирование защиты пропеллеров;
* Андрей Посконин [Texel] - импорт ПО Texel на Raspberr Pi, совместные тесты;

View File

@@ -17,6 +17,7 @@
* Настройка
* [Первоначальная настройка](setup.md)
* [Полетные режимы](modes.md)
* [Настройка режима тренера](trainer_mode.md)
* [Прошивка полетного контролера](firmware.md)
* [Параметры PX4](px4_parameters.md)
* [Настройка PID](calibratePID.md)
@@ -53,17 +54,22 @@
* [Работа с SITL](sitl.md)
* [Контейнер с симулятором](sitl_docker.md)
* [Автозапуск ПО](autolaunch.md)
* [Имя хоста](hostname.md)
* [Взаимодействие с Arduino](arduino.md)
* [3G-модем](3g.md)
* [Установка ROS Kinetic](ros-install.md)
* Проекты на базе Клевера
* [Генератор ArUco карт](arucogenmap.md)
* [Модель аэротакси в городе](bigchallenges.md)
* [Шаровая защита коптера](shield.md)
* [Распознавание лиц](face_recognition.md)
* [Подсчет количества объектов c камеры](object_counting.md)
* [Пульт на Андроид](android.md)
* [Блочный конструктор полета](clever_blocks.md)
* [Дрон для 3D-сканирования человека](3dscan.md)
* [CopterHack-2018](copterhack2018.md)
* [CopterHack-2017](copterhack2017.md)
* [Робокросс-2019](robocross2019.md)
* Дополнительные материалы
* [Олимпиада НТИ 2019](nti2019.md)
* [Вклад в Клевер](contributing.md)

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