Compare commits

..

448 Commits

Author SHA1 Message Date
Oleg Kalachev
d34f98f8c6 Merge branch 'master' into roswww-static-target 2022-11-08 02:28:16 +06:00
Oleg Kalachev
96ea78f141 image: check rosserial version only on rpi image 2022-11-07 20:47:18 +06:00
Oleg Kalachev
5e3b07ff5e Add a basic example script on working with the camera 2022-11-07 20:09:15 +06:00
Oleg Kalachev
92748a760b simulation: remove redundant warning on creating a new LedController 2022-11-07 19:00:02 +06:00
Oleg Kalachev
8512e8a045 image: check rosshow version only on rpi image 2022-11-07 18:30:54 +06:00
Oleg Kalachev
8b1b365e67 image: check compressed_image_transport on rpi image only 2022-11-07 18:21:55 +06:00
Oleg Kalachev
2cd77662df image: check version of rosbridge_server which clover package depends on instead of rosbridge_suite 2022-11-07 18:21:32 +06:00
Oleg Kalachev
64f939d7ed image: updates to tests for VM image 2022-11-06 04:15:32 +06:00
Oleg Kalachev
9a8aa00bc7 Run map_flipped frame only when markers placement is ceiling 2022-11-06 02:02:43 +06:00
Oleg Kalachev
3f3d1cd220 image: don’t test mjpg_streamer on vm image 2022-11-06 01:43:03 +06:00
Oleg Kalachev
9c34d7722c image: don’t test butterfly on vm image 2022-11-06 00:25:23 +06:00
Oleg Kalachev
19e0d725b0 image: test ptvsd on the RPi image only 2022-11-05 22:47:39 +06:00
Oleg Kalachev
a42ee2ab1e Minor update 2022-11-05 22:15:19 +06:00
Oleg Kalachev
6fafaf3184 Fix Python tests for VM 2022-11-05 20:48:38 +06:00
Oleg Kalachev
8f09df6b34 Optimize shell tests for vm image 2022-11-05 17:45:10 +06:00
Oleg Kalachev
c5d01c678a image and vm image: validate python is python2 (for now) 2022-11-05 17:26:19 +06:00
Oleg Kalachev
209d5dde2f Another fix 2022-11-05 04:09:12 +06:00
Oleg Kalachev
27ee253234 Fix 2022-11-05 04:08:19 +06:00
Oleg Kalachev
a2639204e4 Use sh for sudo 2022-11-05 01:41:09 +06:00
Oleg Kalachev
7a8b5585c1 Updates 2022-11-05 01:29:22 +06:00
Oleg Kalachev
06a4478b5e Fix 2022-11-05 00:52:18 +06:00
Oleg Kalachev
71f2d69139 Preserve environment 2022-11-05 00:14:10 +06:00
Oleg Kalachev
b2c98ba502 Move update to src 2022-11-04 22:10:36 +06:00
Oleg Kalachev
49338e6f58 Run catkin_make from pi 2022-11-04 22:08:07 +06:00
Oleg Kalachev
d5baa0b1e1 Revert image-ros.sh 2022-11-04 21:06:35 +06:00
Oleg Kalachev
ee81586fa5 Debug 2022-11-04 19:31:17 +06:00
Oleg Kalachev
5da20d4ac5 Make update not a node 2022-11-04 16:41:44 +06:00
Oleg Kalachev
d2e886d952 Fix 2022-11-04 05:50:13 +06:00
Oleg Kalachev
0a1c98d5f0 Try to fix 2022-11-04 05:22:28 +06:00
Oleg Kalachev
ee7da701e6 Merge remote-tracking branch 'origin/roswww-static-target' into roswww-static-target 2022-11-04 03:54:06 +06:00
Oleg Kalachev
873a08865e Merge branch 'master' into roswww-static-target 2022-11-04 03:51:51 +06:00
Oleg Kalachev
2b13aa02eb docs: make /camera redirect to English version of the article 2022-11-04 03:10:56 +06:00
Oleg Kalachev
ec9ddf5fd2 selfcheck.py: read VM image version from /etc/clover_vm_version 2022-11-03 18:36:18 +06:00
Oleg Kalachev
c5399868cb selfcheck.py: remove obsolete todos 2022-11-03 18:28:22 +06:00
Oleg Kalachev
a6cee773ab selfcheck.py: fix reading diagnostics considering there might be multiple nodes publishing them 2022-11-03 18:23:54 +06:00
Oleg Kalachev
d03cfe00ca selfcheck.py: handle inability to read time sync offset 2022-11-03 17:53:59 +06:00
Oleg Kalachev
0fb101cc59 selfcheck.py: add time sync offset report 2022-11-03 17:47:53 +06:00
Oleg Kalachev
0d44ff3993 selfcheck.py: handle nicely when a PX4 parameter cannot be retrieved 2022-11-03 17:47:34 +06:00
Oleg Kalachev
dc5da00abd selfcheck.py: print reports when there was exception in check 2022-11-03 06:56:07 +06:00
Oleg Kalachev
9461e2120f Trigger build 2022-11-03 05:44:24 +06:00
Oleg Kalachev
4f00960db3 Minor fix to long_callback decorator example 2022-11-02 16:09:56 +06:00
Oleg Kalachev
ce0b4eb428 Implement long_callback Python decorator addressing #218
Clover package Python library added.
2022-11-02 06:38:36 +06:00
Oleg Kalachev
ccbd1cbad9 selfcheck.py: make output colored 2022-10-31 04:24:13 +06:00
Oleg Kalachev
4b397670a1 selfcheck.py: make report more compact by removing severity levels
The severity level is visible from the color
2022-10-31 03:08:30 +06:00
Oleg Kalachev
89bfc150f3 selfcheck.py: split up estimator's and flow sensor's parameters reports 2022-10-31 02:41:21 +06:00
Oleg Kalachev
6b05cb34e5 optical_flow: add disable_on_vpe parameter (#461) 2022-10-31 02:31:47 +06:00
Oleg Kalachev
22293c2220 aruco.launch: set use_map_markers parameter to true 2022-10-31 02:30:19 +06:00
Oleg Kalachev
38a3f656ab selfcheck.py: add parameter to print the time spent on each check
Usage:

rosrun clover selfcheck.py _time:=1
2022-10-31 02:28:02 +06:00
Oleg Kalachev
2e79979411 selfcheck.py: implement experimental parameter for parallel checks run
Run:

rosrun clover selfcheck.py _parallel:=1
2022-10-31 02:25:53 +06:00
Oleg Kalachev
b165e154f5 selfcheck.py: decrease some timeouts to speed up check 2022-10-31 02:14:02 +06:00
Oleg Kalachev
99fad312c5 aruco_detect: wait util map is published to avoid race condition 2022-10-31 01:37:06 +06:00
Elena Seliverstova
ee17a3bada docs: update course contest deadline 2022-10-30 21:38:40 +03:00
Elena Seliverstova
1461dd22f4 Update deadline 2022-10-30 21:22:04 +03:00
Oleg Kalachev
2c07bbffe3 aruco_map: fix visualization markers' orientation
Was by mistake uninitialized
2022-10-30 23:16:03 +06:00
Oleg Kalachev
0b62f677af aruco_detect: consider markers size from markers map
`use_map_markers` parameter added
2022-10-30 22:28:33 +06:00
Oleg Kalachev
070c23be53 selfcheck.py: shorten some reports replacing 'is' with '=' 2022-10-30 21:00:45 +06:00
Oleg Kalachev
c907e6041a selfcheck.py: skip battery check in simulation 2022-10-30 20:44:13 +06:00
Oleg Kalachev
69d5d1e521 selfcheck.py: don't fail when there is no vpe and vpe_publisher is not running 2022-10-30 19:48:42 +06:00
Oleg Kalachev
1700ad24df selfcheck.py: don't failure when no vpe and drone is on the floor 2022-10-30 19:21:42 +06:00
Oleg Kalachev
6361984794 selfcheck.py: don't fail when marker's map not detected 2022-10-30 19:21:10 +06:00
Oleg Kalachev
7f31fdd320 docs: minor cleanup in snippets 2022-10-30 18:39:51 +06:00
Oleg Kalachev
f9450fe03d selfcheck.py: skip px4 version check in SITL 2022-10-30 05:34:57 +06:00
Oleg Kalachev
b41cb6b581 selfcheck.py: minor fix 2022-10-30 05:10:54 +06:00
Oleg Kalachev
b855c4586a led: allow to use namespaced mavros (from #439)
rosout_agg cannot be namespaced:
f5fa3a1687/tools/rosout/rosout.cpp (L127)

Co-Authored-By: Playergeek181 <93044104+Playergeek181@users.noreply.github.com>
2022-10-30 04:05:53 +06:00
Oleg Kalachev
26245dfb42 docs: add snippet on how to check if code is running inside simulation 2022-10-26 03:31:00 +06:00
Oleg Kalachev
d6f9327ede simulation: set distance sensor's fov to 27 deg
As vl53l1x rangefinder specification stances
2022-10-25 05:10:25 +06:00
Oleg Kalachev
0f5f111f46 docs: minor fix 2022-10-13 02:05:56 +06:00
Oleg Kalachev
4e9d8a64d0 simple_offboard: test for simple_offboard/release service presence 2022-10-13 00:08:35 +06:00
Oleg Kalachev
94171d51ac simple_offboard: implement simple_offboard/release service 2022-10-13 00:07:27 +06:00
Oleg Kalachev
eb448ae0e7 main_camera.launch: run image_raw_throttled topic by default (#248) 2022-10-12 00:25:12 +06:00
Oleg Kalachev
c0707e066a actions: build Debian packages and upload to artifacts (#458) 2022-10-05 16:10:17 +06:00
Oleg Kalachev
91c6998633 docs: add snippet to subscribe and decode incoming mavlink messages 2022-09-29 13:58:02 +05:00
Sergey Stetsky
7b431fa021 docs: add command for updating markers map in the sim (#456)
Co-authored-by: Oleg Kalachev <okalachev@gmail.com>
2022-09-23 20:15:57 +03:00
Oleg Kalachev
1e12498cb2 Install GeographicLib datasets in build workflow 2022-09-14 14:19:06 +03:00
Oleg Kalachev
43037f515d aruco_detect: check for duplicates in marker transforms, send all transforms in one message 2022-09-14 12:55:27 +03:00
Oleg Kalachev
2ea848721c aruco_pose: add duplicate test to CMakeLists.txt 2022-09-14 12:46:04 +03:00
Oleg Kalachev
d06b0a0cd2 aruco_pose: implement test for TF_REPEATED_DATA when multiple markers with the same ID 2022-09-14 12:35:16 +03:00
Oleg Kalachev
1efe10c9dd Simplify script for testing native Noetic build 2022-09-10 15:26:34 +03:00
Oleg Kalachev
24cd1f6fac Show number of messages received in topic viewer 2022-09-10 08:08:09 +03:00
Oleg Kalachev
5223bef5e7 Fix error when viewing messages without header in topic viewer 2022-09-10 01:31:38 +03:00
Oleg Kalachev
105eac7e1d clover.launch: make force_init argument overridable externally 2022-09-08 14:42:45 +03:00
Oleg Kalachev
c1d6ed27aa mavros.launch: fix fcu_url for hitl connection 2022-09-08 01:32:51 +03:00
Oleg Kalachev
614784e949 mavros.launch: add hitl option for fcu_conn argument 2022-09-07 00:55:13 +03:00
Oleg Kalachev
9376c017b4 selfcheck.py: skip boot duration check on not Clover image 2022-09-03 22:53:38 +03:00
Oleg Kalachev
b5d300e218 optical_flow: timeout for previous frame
For cases when optical flow is dynamically disabled and enabled back
2022-09-03 07:26:25 +03:00
Oleg Kalachev
efb44484b0 Remove obsolete note from readme 2022-08-26 22:51:08 +03:00
Oleg Kalachev
0a2ad3d64f docs: remove some obsolete notes about renaming 2022-08-26 22:50:55 +03:00
oponfil
ffe2d3d5e4 docs: update connection article (en) (#363)
* Add instructions on how to connect FMU to Raspberry Pi by UART

* Remove sitl connection section

Co-authored-by: Oleg Kalachev <okalachev@gmail.com>
2022-08-26 22:36:11 +03:00
oponfil
81f4795aec docs: update connection article (#362)
* Add instructions on how to connect FMU to Raspberry Pi by UART

* Remove inactual sitl connection

Co-authored-by: Oleg Kalachev <okalachev@gmail.com>
2022-08-26 22:35:40 +03:00
oponfil
596ed3dcf2 docs: docs: correction in mavros article on global setpoints (en) (#358)
Co-authored-by: Oleg Kalachev <okalachev@gmail.com>
2022-08-26 22:09:56 +03:00
oponfil
63c71fc331 docs: correction in mavros article on global setpoints (#357)
Co-authored-by: Oleg Kalachev <okalachev@gmail.com>
2022-08-26 22:08:22 +03:00
Oleg Kalachev
0efb249d9b docs: update PX4 to v1.12.3 in native simulation installation article 2022-08-26 16:05:28 +03:00
Oleg Kalachev
47c6e5aa9b docs: comment out link to install_software.sh in simulation installation manual
The link confuses users so they often try to run it
2022-08-25 18:28:52 +03:00
Oleg Kalachev
687a4f50fd Fix python-pymavlink installation adding some lxml dependencies
Otherwise installation falls with:
Error: Please make sure the libxml2 and libxslt development packages are installed
2022-08-24 22:42:18 +03:00
Oleg Kalachev
2372cdd7db vpe_publisher: fix reading map and base link frame_id from mavros 2022-08-19 00:17:34 +03:00
Oleg Kalachev
596a7276ac Update docs job runner to ubuntu 22.04
https://github.com/actions/runner-images/issues/6002
2022-08-19 00:02:22 +03:00
Oleg Kalachev
a2d984272b Deploy docs using build artifacts instead of gh-pages branch (#452)
https://github.blog/changelog/2021-12-16-github-pages-using-github-actions-for-builds-and-deployments-for-public-repositories/
2022-08-18 18:17:30 +03:00
Elena Seliverstova
e0f200f069 Update the contact link in Telegram 2022-08-09 20:01:38 +03:00
Elena Seliverstova
bb68b56c25 Update the contact link in Telegram 2022-08-09 19:59:55 +03:00
Elena Seliverstova
54e685a9d6 docs: add new articles about CopterHack2023 (#450) 2022-08-09 10:15:56 +03:00
Oleg Kalachev
c64a80312c Make PX4 node required in the sim 2022-07-29 06:05:48 +03:00
Oleg Kalachev
840f2c220c simulator: change COM_RCL_EXCEPT param to enable offboard flights without RC on PX4 v1.13 2022-07-26 21:25:01 +03:00
Oleg Kalachev
5325017a77 Implement dynamic reconfiguration for aruco_map (#448) 2022-07-19 03:30:43 +03:00
Niels Hoppe
98d21d1760 Add missing dependency 2022-07-18 19:12:55 +03:00
Oleg Kalachev
a13806ef14 Minor whitespace fix 2022-07-15 00:24:34 +03:00
Oleg Kalachev
e8de04a1dd Add shortcut launch-file to run the simulation 2022-07-14 19:42:54 +03:00
Oleg Kalachev
1dd098ba6b docs: redirect for blocks programming article 2022-06-21 02:02:13 +03:00
Oleg Kalachev
48de99a942 ws281x was updated to 0.0.13 2022-06-16 18:19:29 +03:00
Oleg Kalachev
ac8caea2b1 docs: update some outdated articles 2022-06-16 00:55:32 +03:00
Oleg Kalachev
fd22a3b19f selfcheck.py: don't check clover.service on not Clover image 2022-06-10 18:29:24 +03:00
Oleg Kalachev
e74df44a27 selfcheck.py: don't check preflight status in SITL
SITL doesn't have MAVLink interface to command line
2022-06-10 17:38:33 +03:00
Oleg Kalachev
4cdf073c1d selfcheck.py: don't check ROS_HOSTNAME not on Clover image 2022-06-10 17:33:21 +03:00
Oleg Kalachev
4179beca6d selfcheck.py: failure if GPS fusion is enabled but there is no GPS data 2022-06-10 17:09:23 +03:00
Oleg Kalachev
494a116cd3 simulator: replace set with set-default in airframe file
To allow user to change parameters and save them
2022-06-10 16:51:59 +03:00
Oleg Kalachev
6c7f8637f4 docs: add sourcing gazebo/setup.sh to simulator installation manual 2022-06-08 00:31:27 +03:00
Oleg Kalachev
9955599a0a led: remove delay before blink effect start 2022-06-08 00:18:56 +03:00
Oleg Kalachev
a360dc19c0 Add json-to-pretty-yaml lib to gitattributes vendored 2022-06-07 15:23:13 +03:00
Oleg Kalachev
d9a547a3e5 Document aruco_map/image_axis parameter 2022-06-07 15:15:29 +03:00
Oleg Kalachev
762613f659 Minor changes 2022-06-07 01:39:26 +03:00
Oleg Kalachev
51112651d4 vpe_publisher: minor typo 2022-06-02 18:13:37 +03:00
Oleg Kalachev
db0393a6f0 simple_offboard: avoid TF_REPEATED_DATA when publishing body frame 2022-06-02 17:37:39 +03:00
Oleg Kalachev
8d9dc1d122 Add autotest scripts (#443) 2022-06-02 12:08:20 +03:00
Oleg Kalachev
f567ba689c aruco.launch: increase default transform timeout 2022-06-02 09:48:44 +03:00
Oleg Kalachev
cbdc93d1c3 simple_offboard: fix handling set_attitude service 2022-06-02 09:48:29 +03:00
Oleg Kalachev
c4cd157f7c docs: fix markdownlint 2022-05-31 15:14:05 +03:00
Oleg Kalachev
9692c030f1 Disable GPS in EKF2_AID_MASK by default
As this flag breaks vision position aiding
2022-05-31 14:28:23 +03:00
Oleg Kalachev
dd01353533 vpe_publisher: fix force init 2022-05-31 12:20:07 +03:00
Oleg Kalachev
afa81e8ee2 docs: add 'optical_flow/enabled' parameter usage snippets + minor fixes 2022-05-27 06:25:56 +03:00
Oleg Kalachev
8cef6be840 optical_flow: implement optical_flow/enabled dynamic parameter 2022-05-27 06:15:22 +03:00
Oleg Kalachev
07cac29937 aruco_pose: make aruco_detect/length parameter dynamic 2022-05-26 12:47:32 +03:00
Elena Seliverstova
7df4cb2589 Update copterhack2022.md 2022-05-24 17:03:22 +03:00
Elena Seliverstova
f1d2f45a9e Update copterhack2022.md 2022-05-24 16:57:52 +03:00
Elena Seliverstova
addc600f48 Update copterhack2022.md 2022-05-24 16:07:39 +03:00
Elena Seliverstova
608c09f3a5 Update copterhack2022.md 2022-05-24 16:02:12 +03:00
Oleg Kalachev
1e68369053 docs: minor fix 2022-05-24 08:14:01 +03:00
Oleg Kalachev
80730fd7b3 aruco_pose: include SetMarkers service file to CMakeLists 2022-05-24 06:08:06 +03:00
Oleg Kalachev
031c8b5305 aruco_detect: implement ~/set_length_override service
For changing individual markers length dynamically
2022-05-24 05:15:53 +03:00
Oleg Kalachev
d0ab69df7f docs: add CopterHack-2022 final video 2022-05-24 01:34:45 +03:00
Oleg Kalachev
4562bf3b57 aruco_detect: document ~map_markers topic 2022-05-17 20:16:44 +03:00
Oleg Kalachev
00aef350ea aruco_map: rename published map topic from '~markers' to '~map'
The previous name markers was overlapped with the subscribed recognized
markers topic
2022-05-17 20:00:02 +03:00
Oleg Kalachev
2796917bd0 Fix 2022-05-17 02:33:45 +03:00
Oleg Kalachev
da3f570225 Fix the name of ZeroTier VPN 2022-05-16 18:34:24 +03:00
Oleg Kalachev
1cb257b6a1 Typo 2022-05-13 00:31:58 +03:00
Oleg Kalachev
16d29fed80 aruco_detect: add transform_timeout parameter 2022-05-13 00:29:28 +03:00
Oleg Kalachev
2418c259a8 docs: add link to full results table of CopterHack-2022 2022-04-26 21:35:37 +04:00
matveylapin
38b9b7215d docs: add English version of CopterCat article (#440)
* Create info.md

* Add files via upload

* Create capterCat.md

* Rename capterCat.md to copterCat.md

* Create copterCat.md

* Remove already present article

* Use lowercase

* Remove unused assets

* Editing

* Add to summary

Co-authored-by: Oleg Kalachev <okalachev@gmail.com>
2022-04-25 12:53:37 +04:00
Oleg Kalachev
f1215347f6 ci: fix 2022-04-25 12:45:16 +04:00
Oleg Kalachev
b3f46e47ec docs: publish results of CopterHack-2022 2022-04-25 10:40:29 +04:00
Oleg Kalachev
a053d0a3fc docs: fix headers anchors 2022-04-18 21:24:26 +04:00
guisoares9
8838c0b8bf docs: CopterHack 2022: Swarm in Blocks (Atena - Grupo SEMEAR) (#398)
* final article added but without assets

* Assets added

* Added information about the Clover Platform and Swarm in Blocks repository to the final article

* Minor changes

* Minor changes

* Minor changes

* Update swarm_in_blocks.md

* Add video preview image to the repo

* Some editing

* Reduce image size

* List article

* Remove unused image

* Replace huge animations with external links to save space

Co-authored-by: Rafael-Saud <79988012+Rafael-Saud@users.noreply.github.com>
Co-authored-by: Oleg Kalachev <okalachev@gmail.com>
2022-04-18 21:18:48 +04:00
Oleg Kalachev
2a0f4155ef docs: fix images urls 2022-04-18 10:44:10 +04:00
Oleg Kalachev
620f10118d docs: list ftl copterhack-2022 article 2022-04-18 10:30:17 +04:00
Max
6762b251c9 docs: CopterHack-2022: FTL advanced clover2 (#382)
* Create advancedClover2.md

* Fix markdownlint

* Fix markdownlint 2

* Add t.me links to command description

* Rename advancedClover2.md to advancedclover2.md

* Add development roadmap

* Fix markdownlint

* Rename and write article

* January 2022 update

* Finish article

* Add banner image

* Reduce advanced_clover_simulation.png size to fit limit

* Move images to subfolder

* Edit

* Reduce image size

Co-authored-by: Oleg Kalachev <okalachev@gmail.com>
2022-04-18 10:28:30 +04:00
Oleg Kalachev
59d9274c9b docs: add Stereo team article to the summary and copterhack lists 2022-04-18 10:04:19 +04:00
den250400
c145789be1 docs: CopterHack-2022: neural obstacle avoidance (Stereo) (#393)
* Create neural-obstacle-avoidance.md

* Update docs/en/neural-obstacle-avoidance.md

Co-authored-by: Oleg Kalachev <okalachev@gmail.com>

* Update docs/en/neural-obstacle-avoidance.md

Co-authored-by: Oleg Kalachev <okalachev@gmail.com>

* Update docs/en/neural-obstacle-avoidance.md

Co-authored-by: Oleg Kalachev <okalachev@gmail.com>

* Update neural-obstacle-avoidance.md

* Update docs/en/neural-obstacle-avoidance.md

* Update neural-obstacle-avoidance.md

* Update neural-obstacle-avoidance.md

* Update neural-obstacle-avoidance.md

* Update neural-obstacle-avoidance.md

* Rename docs/en/neural-obstacle-avoidance.md to docs/obstacle-avoidance-potential-fields.md

* Update obstacle-avoidance-potential-fields.md

* Create obstacle-avoidance-potential-fields.md

* Delete obstacle-avoidance-potential-fields.md

* Update obstacle-avoidance-potential-fields.md

* Some editing

* Fix animation address

* Move smaller image to the repo

* More editing

Co-authored-by: Oleg Kalachev <okalachev@gmail.com>
2022-04-18 10:01:55 +04:00
slavikyd
180c892eaa docs: CopterHack-2022: C4S (Space clowns) (#394)
* Create c4s.md

* Edit article

* Update docs/ru/c4s.md

* Update c4s.md

* Add files via upload

Pictures for c4s project

* Add files via upload

One more picture for c4s

* Update c4s.md

* Update c4s.md

* Update c4s.md

* Update c4s.md

* Edit

* Update c4s.md

* Final article

Финальный вариант статьи

* Update c4s.md

* Update c4s.md

* Some editing

* Move the assets to subfolder

* Fix

* Redice images sizes

* List article

Co-authored-by: Oleg Kalachev <okalachev@gmail.com>
2022-04-18 09:49:13 +04:00
Mikhail Kuznetsov
da065a79f5 docs: CopterHack-2022: Clover Rescue Team (#400)
* Create CloverRescueTeam.md

* Update and rename CloverRescueTeam.md to clover-rescue-team.md

* Update project description

* Update project description

* Update project description

* update project description

* readme update

* readme update

* Update clover-rescue-team.md

* Update clover-rescue-team.md

* Update clover-rescue-team.md

* Move English article to en folder

* Edit article

* Move all images to the repo

* List article

Co-authored-by: Oleg Kalachev <okalachev@gmail.com>
2022-04-18 09:26:25 +04:00
matveylapin
d1f0fe5aa9 docs: CopterHack-2022 - CopterCat (#403)
* Create FMUv6U с поддержкой распределённой сети.md

* Update docs/ru/FMUv6U с поддержкой распределённой сети.md

Co-authored-by: Oleg Kalachev <okalachev@gmail.com>

* Update docs/ru/FMUv6U с поддержкой распределённой сети.md

Co-authored-by: Oleg Kalachev <okalachev@gmail.com>

* Update docs/ru/FMUv6U с поддержкой распределённой сети.md

Co-authored-by: Oleg Kalachev <okalachev@gmail.com>

* Update and rename FMUv6U с поддержкой распределённой сети.md to CopterCat.md

* Update CopterCat.md

* Update CopterCat.md

* Rename CopterCat.md to сopter_сat.md

* Create coptreCat.md

* Create info.md

* Add files via upload

* Update сopter_сat.md

* Delete coptreCat.md

* Remove Cyrillic letter

* Remove another Cyrillic letter

* Edit article

* Remove capital letter from image paths, remove unused images

* Add forgotten (?) logo

* List article

* Reduce images size

Co-authored-by: Oleg Kalachev <okalachev@gmail.com>
2022-04-18 09:07:26 +04:00
DJS Phoenix
d3eed2cba9 docs: CopterHack-2022: DJS_Phoenix-Ikshana (#414)
* Create DJS_Phoenix_Ikshana.md

* Update DJS_Phoenix_Ikshana.md

* Update DJS_Phoenix_Ikshana.md

* Update DJS_Phoenix_Ikshana.md

* Update DJS_Phoenix_Ikshana.md

* Update DJS_Phoenix_Ikshana.md

* Rename DJS_Phoenix_Ikshana.md to djs_phoenix_ikshana.md

* Edit

* GitBook: No commit message

* Update djs_phoenix_ikshana.md

* Update djs_phoenix_ikshana.md

* Update djs_phoenix_ikshana.md

* Remove unneeded files

* Add all the images to repo

* Edit article

* Add article to summary and copterhack-22 list

Co-authored-by: Oleg Kalachev <okalachev@gmail.com>
Co-authored-by: DJS Phoenix <djsphoenixteam@gmail.com>
2022-04-18 08:50:20 +04:00
Ruslan Mambetov
6356292c6f docs: Copterhack2022 - Аir analysis system (С305 team) (#422)
* Create new-article.md

* Renamed new_article to air_monitor

* Added gases table

* Removed an unnecessary space after the table

* Reformed extra information

* Added blank lines to lists

* Removed trailing spaces

* Modified article

* Fixed logo

* Renamed repository title

* Added github links

* Added github links

* Added resource links

* Added github links

* Move logo to repo

* Edit article

* Add to summary and to copterhack list

* Minor fix

Co-authored-by: sfilimonov <sfilimonov@enter-vr.com>
Co-authored-by: Oleg Kalachev <okalachev@gmail.com>
2022-04-18 08:34:55 +04:00
Oleg Kalachev
4cf91dd73d docs: update copter hack teams list 2022-04-14 02:53:59 +04:00
Oleg Kalachev
88c1b85608 Put column names to all marker map files 2022-04-08 20:41:16 +04:00
Oleg Kalachev
169680129b docs: update PX4 building instructions 2022-04-06 02:51:20 +04:00
Oleg Kalachev
6541d60d08 docs: minor typo 2022-04-05 23:37:11 +04:00
Oleg Kalachev
e3addb9eb0 docs: add an example to get the configured number of LEDs 2022-04-05 08:24:33 +04:00
Oleg Kalachev
b7d74ef6c9 docs: simplify wait_for_message example for rangefinder range 2022-04-05 08:24:12 +04:00
Oleg Kalachev
da92aea727 actions: try to build pdfs even if GITBOOK_SKIP_PDF is set 2022-04-05 05:22:11 +04:00
Oleg Kalachev
0b78c84ac0 selfcheck.py: consider locale settings while converting top output
top uses locale to format numbers, so using simple float() doesn't work
in case of locales using comma as decimal separator
2022-04-05 00:22:21 +04:00
Oleg Kalachev
de2467acb1 actions: secret for skipping building docs pdf 2022-03-30 03:04:10 +04:00
Oleg Kalachev
3d6b8b6a10 docs: update magnetic grip article, add arduino code 2022-03-30 01:30:01 +04:00
Oleg Kalachev
b6f1ca5d20 docs: enhancements to m1 installation article 2022-03-24 16:38:00 +04:00
Oleg Kalachev
850b49b2b6 Fix #435 (#438)
* Split Gazebo LED plugin to LED visual plugin and LED model plugin

* Get rid of unneeded 'Failed to convert' warning

* Minor
2022-03-23 22:33:19 +04:00
londrwus
f21ba3feb4 docs: add lane control article by @londrwus (#437) 2022-03-16 21:40:20 +03:00
Oleg Kalachev
9c3a97f945 simulator: #435 fix working LED layer if there is no camera in the world 2022-03-15 14:45:29 +03:00
Oleg Kalachev
293448028a docs: update copter hack teams list 2022-03-15 00:03:14 +03:00
Oleg Kalachev
b5cd9512ef Change advised SENS_FLOW_MINHGT value to 0 2022-03-02 07:29:44 +03:00
Oleg Kalachev
dd74ceb383 docs: fix link to Ubuntu installation iso for arm64 2022-02-22 19:56:27 +03:00
Oleg Kalachev
e217678f7d Changes for experimental support for official PX4 version (#434)
* docs: minor fix

* docs: update PX4 docs links

* docs: info on no mags found error

* docs: some updates in setup section

* docs: use enumerated list for consistency

* docs: update firmware flashing section

* docs: update

* selfcheck.py: remove timestamps from selfcheck reports

* selfcheck.py: add gzclient and gzserver to cpu eaters whitelist

* selfcheck.py: make not finding vcgencmd not a failure

* selfcheck.py: fix and simplify firmware version parsing, remove Clover firmware warning

* docs: some updates to optical flow article

* ci: cancel previous docs builds to avoid publishing old site

* vpe_publisher: rename parameter publish_zero to force_init

* genmap.py: use -p flag in example

* selfcheck.py: add checking map=>body transform

* selfcheck.py: bring back info about non-Clover firmware

* docs: reduce qgc-params.png file size

* docs: reduce size of some images

* docs: rephrase firmware flashing section to continue recommending COEX firmware

* docs: update PX4 docs links

* docs: rename px4_parameters.md article to parameters.md

* docs: add note about possible unintended switching out of LAND mode

* docs: remove obsolete notes and simplify titles in autonomous flight article

* clover.launch: add force_init argument
PX4 1.12.3 doesn’t init by flow without mag
force_init runs vpe_publisher to force init using vpe

* docs: rework parameters article, make summary parameters table

* docs: remove unused asset
2022-02-22 19:20:23 +03:00
Oleg Kalachev
dc06ba1bd2 docs: add article on installation the simulator on M1 computers 2022-02-22 19:17:28 +03:00
Oleg Kalachev
21bbc8a86c docs: minor fix 2022-02-20 21:39:39 +03:00
Oleg Kalachev
76ef764143 docs: consider architecture in Monkey installation 2022-02-19 19:34:04 +03:00
Oleg Kalachev
d282098134 docs: fix Monkey installation commands 2022-02-19 19:22:15 +03:00
Oleg Kalachev
0f37f19b40 Basic tests for Blocks 2022-02-18 22:52:59 +03:00
Oleg Kalachev
e9c3c6ff72 simple_offboard: match tests and clover.launch parameters 2022-02-18 22:52:29 +03:00
Oleg Kalachev
7909756046 Fix mavros rangefinder subscriber config 2022-02-18 21:24:14 +03:00
Oleg Kalachev
1e8a4841af clover_descrition: remove usage of undeclared argument 2022-02-18 15:40:35 +03:00
Oleg Kalachev
6ec574e193 selfcheck.py: change low space threshold from 100 MB to 1 GB 2022-02-17 15:14:19 +03:00
Oleg Kalachev
8381aecd50 simple_offboard: param for changing mavros name if using multiple (#432) 2022-02-12 12:23:09 +03:00
Oleg Kalachev
f5eb475660 selfcheck.py: check free disk space 2022-02-11 15:03:37 +03:00
Oleg Kalachev
928f4f135e docs: fix for markdownlint 2022-02-11 11:06:58 +03:00
Oleg Kalachev
8d15de0849 docs: article with testing list 2022-02-11 11:00:48 +03:00
Oleg Kalachev
826f631b97 Fix version in package.xml files 2022-02-10 13:49:14 +03:00
Oleg Kalachev
52b5d7b04e CI: disable Melodic build 2022-02-10 13:33:32 +03:00
Oleg Kalachev
455d52007e Update version in package.xml files 2022-02-10 13:31:12 +03:00
Oleg Kalachev
e9e6cabbb9 builder: use cv-camera@0.5.1 with init fix 2022-02-10 13:30:42 +03:00
Oleg Kalachev
8fcd6e9b9e builder: validate version of some ros packages 2022-02-10 13:30:14 +03:00
Oleg Kalachev
24d3a1df8d docs: minor fix of links rendering 2022-02-09 16:49:15 +03:00
Oleg Kalachev
9784e7bfa1 docs: change python to python3 in autolaunch article 2022-02-09 16:41:19 +03:00
Oleg Kalachev
fbad85d87f docs: add main_camera_optical to frames article 2022-02-07 09:44:53 +03:00
Oleg Kalachev
c1ca40187e www: add date and offset param to topics viewer 2022-02-03 05:05:08 +03:00
Oleg Kalachev
c1179869cd www: remove annoying hover title in topics viewer 2022-02-03 04:37:22 +03:00
Oleg Kalachev
2096be5080 docs: rename px4_parameters to parameters.md 2022-02-01 11:40:20 +03:00
Oleg Kalachev
0c879f2aad docs: rename px4_parameters.md article to parameters.md 2022-02-01 11:37:41 +03:00
Oleg Kalachev
f34e8b4774 docs: updates (en) 2022-02-01 11:19:40 +03:00
Oleg Kalachev
be76ea82d7 docs: some updates to optical flow article 2022-02-01 11:04:10 +03:00
Oleg Kalachev
6a8806c476 docs: some updates in setup section 2022-02-01 11:03:27 +03:00
Oleg Kalachev
00a76a306e docs: update PX4 docs links 2022-02-01 11:02:26 +03:00
Oleg Kalachev
f66b53f9cb docs: update PX4 docs links 2022-02-01 11:01:56 +03:00
Oleg Kalachev
28927246db docs: minor fix 2022-02-01 10:57:12 +03:00
Oleg Kalachev
ca5817c3d2 builder: fix Butterfly installation
Fix the `can't find Rust compiler` error using the older PyOpenSSL
to not update `cryptography` because newer `cryptography` requires Rust to install.
2022-02-01 10:53:28 +03:00
Oleg Kalachev
7717461631 genmap.py: use -o flag in example 2022-02-01 08:30:59 +03:00
Oleg Kalachev
3f352ebc06 docs: reduce size of some images 2022-02-01 08:29:29 +03:00
Oleg Kalachev
8c8fe5c40c docs: reduce qgc-params.png file size 2022-02-01 08:29:23 +03:00
Oleg Kalachev
d89e5eada7 selfcheck.py: add checking map=>body transform 2022-02-01 08:28:43 +03:00
Oleg Kalachev
2ee90e62fc Minor typo in mavros_config 2022-02-01 07:04:43 +03:00
Elena Seliverstova
848d9dcbe4 docs: contests article (#430)
Co-authored-by: Oleg Kalachev <okalachev@gmail.com>
2022-02-01 06:04:35 +03:00
Oleg Kalachev
6d68d06787 simple_offboard: default reference frames
To simplify running with rosrun
2022-01-30 00:37:24 +03:00
Oleg Kalachev
d18ca32688 www: add console page to show logs 2022-01-28 08:08:41 +03:00
Oleg Kalachev
bf9f7d035f docs: edit programming intro text 2022-01-28 06:21:53 +03:00
Oleg Kalachev
1aec5063d6 docs: simplify and fix some snippets 2022-01-28 06:20:57 +03:00
Oleg Kalachev
e7eae1c02d ci: cancel previous docs builds to avoid publishing old site 2022-01-25 19:12:47 +03:00
Oleg Kalachev
e3958d7fef selfcheck.py: increase long boot duration value to 20 2022-01-21 23:22:47 +03:00
Oleg Kalachev
fb47858010 selfcheck.py: add checking map->base_link tf transform 2022-01-21 23:22:33 +03:00
Oleg Kalachev
a525714e3a mavros: disable startup_px4_usb_quirk 2022-01-20 19:56:44 +03:00
Oleg Kalachev
29fdbf23af docs: update copterhack-2022 teams list 2022-01-11 16:39:31 +03:00
Oleg Kalachev
6eacb8966a docs: fix broken links 2022-01-10 05:39:53 +03:00
Oleg Kalachev
d8afb711f0 docs: fix links copterhack-2022 articles 2022-01-10 04:56:06 +03:00
Oleg Kalachev
9cae4c9064 Debug 2021-12-17 10:48:34 +03:00
Oleg Kalachev
87361c3499 builder: initialize catkin workspace berfore building everything 2021-12-17 09:18:57 +03:00
Oleg Kalachev
9aa5a7e447 Merge branch 'master' into roswww-static-target 2021-12-17 09:18:19 +03:00
Oleg Kalachev
cd08dba827 Debug 2021-12-16 15:19:18 +03:00
Oleg Kalachev
cba12e115e builder: remove unneeded catkin_blacklist_packages 2021-12-16 13:57:13 +03:00
Oleg Kalachev
bb6a6c81f3 selfcheck.py: don’t show 'different index' warnings 2021-12-16 13:41:14 +03:00
Elena Seliverstova
d27bbf31bd docs: video contest page (#427)
Co-authored-by: Oleg Kalachev <okalachev@gmail.com>
2021-12-16 00:21:48 +03:00
Oleg Kalachev
8668295cfe docs: fix 2021-12-14 19:24:59 +03:00
Oleg Kalachev
535b366bab docs: update copterhack article 2021-12-14 18:52:32 +03:00
Oleg Kalachev
9f6aa7dabd docs: add get-param and set-param snippets 2021-12-11 09:54:02 +03:00
Oleg Kalachev
f960e5e662 Fix 2021-12-11 00:25:49 +03:00
Oleg Kalachev
a82736f041 Rename roswww_static main.py to update 2021-12-10 21:27:43 +03:00
Oleg Kalachev
278aa7b58b Minor fix 2021-12-10 21:23:52 +03:00
Oleg Kalachev
08f6d82fd2 Info on updating roswww in index.html 2021-12-10 21:23:46 +03:00
Oleg Kalachev
8a8dc8b78f Update www directory only on catkin_make 2021-12-10 21:23:30 +03:00
Oleg Kalachev
f4d00a47af docs: fix 2021-12-10 16:19:07 +03:00
Oleg Kalachev
0f438235c2 docs: minor fix 2021-12-10 09:43:09 +03:00
Oleg Kalachev
e4ad687e28 docs: fix to native simulator setup article 2021-12-10 09:07:27 +03:00
Oleg Kalachev
5d58ffd1db docs: rework native simulator installation article 2021-12-10 07:53:55 +03:00
Oleg Kalachev
b2ed1fccc6 simple_offboard: remove warnings 2021-12-10 05:00:08 +03:00
Oleg Kalachev
aa136e7f15 docs: minor fix 2021-12-10 02:28:28 +03:00
Oleg Kalachev
9743bcbaaf Disable publish_sim_time in mavros as it breaks the simulation 2021-12-10 02:28:18 +03:00
Oleg Kalachev
75aed624db docs: remove unneeded 'coding' from program template 2021-11-30 16:40:35 +03:00
Oleg Kalachev
36a4962bc0 www: add link to topics list page in topic viewer 2021-11-25 23:41:24 +03:00
Oleg Kalachev
2cd3be1139 optical_flow: ability to change default flow gyro values 2021-11-25 23:36:20 +03:00
Oleg Kalachev
6909ba5819 www: change background when connection in closed in topics view 2021-11-25 23:27:09 +03:00
Oleg Kalachev
f1783bdd0b selfcheck.py: ignore some records of error log in report 2021-11-25 22:54:45 +03:00
Oleg Kalachev
528be179e6 selfcheck.py: parse warnings from error log correctly 2021-11-25 22:49:31 +03:00
Oleg Kalachev
fe588e7af9 blocks: raise exception when cannot connect to pigpiod 2021-11-22 20:16:10 +03:00
Oleg Kalachev
15551db840 docs: add redirect from /gpio 2021-11-22 20:10:39 +03:00
Oleg Kalachev
9dc4407afc selfcheck.py: make not finding vcgencmd not a failure 2021-11-19 09:50:56 +03:00
Oleg Kalachev
365bd4146a selfcheck.py: add gzclient and gzserver to cpu eaters whitelist 2021-11-19 09:50:52 +03:00
Oleg Kalachev
fc99269404 selfcheck.py: remove timestamps from selfcheck reports 2021-11-19 09:50:43 +03:00
Oleg Kalachev
9231679353 copterhack-2022: update Moopt title 2021-11-09 16:31:43 +03:00
Oleg Kalachev
4defe2c7ef copterhack-2022: fix 2021-11-08 22:57:51 +03:00
Bartosz Ptak
9f3410847f copterhack-2022: fix (#423) 2021-11-08 22:56:46 +03:00
Oleg Kalachev
fa8da1cb33 copterhack-2022: add participants list 2021-11-08 17:38:43 +03:00
Oleg Kalachev
3bb285fd35 docs: fix 2021-11-03 18:59:48 +03:00
Oleg Kalachev
ec1829e60c docs: remove external image 2021-11-03 00:34:17 +03:00
Oleg Kalachev
c32a412f42 Builder: echo commands in image-ros.sh 2021-11-02 23:19:18 +03:00
Oleg Kalachev
810ddb4157 docs: update ros article 2021-11-02 20:58:27 +03:00
Oleg Kalachev
3656c1714a docs: update copterhack article 2021-11-01 20:11:30 +03:00
Oleg Kalachev
937b68aa43 docs: redirect /ros to English version 2021-11-01 19:12:46 +03:00
Oleg Kalachev
bdd1b06541 docs: fix building pdf 2021-10-30 21:57:49 +03:00
Oleg Kalachev
dd96c91b55 docs: minor fix 2021-10-30 21:36:51 +03:00
Oleg Kalachev
8f3d64e9aa docs: minor fix 2021-10-22 16:46:18 +03:00
Oleg Kalachev
cfd413ffc1 simulation: tune external camera model fov 2021-10-20 10:09:33 +03:00
Oleg Kalachev
ca054c88ba clover_simulation: add script for running gzweb 2021-10-12 05:55:16 +03:00
murata,katsutoshi
d55576bf4f udev: add CUAV X7 Pro (#392) 2021-10-12 01:46:52 +03:00
Oleg Kalachev
470e6ff0e9 Fix for editorconfig 2021-10-08 16:40:18 +03:00
Oleg Kalachev
441cf7fcf7 editoconfig-lint: don’t check .material files 2021-10-08 16:38:30 +03:00
Oleg Kalachev
fc5960586b simulation: add several separate aruco markers models 2021-10-08 16:35:12 +03:00
Oleg Kalachev
4aef1e616c docs: minor fix 2021-10-07 01:59:58 +03:00
Oleg Kalachev
463c08da96 docs: update simulation installation instructions 2021-10-07 01:32:16 +03:00
Oleg Kalachev
ebaaa14a7e docs: update header in sitl article (ru) 2021-10-07 01:17:33 +03:00
Oleg Kalachev
c0d33abff6 docs: add info on rostopic info and rostopic hz 2021-10-07 00:51:55 +03:00
Oleg Kalachev
3c4ef56b4e Fix can't find Rust compiler while installing cryptography
Using an older cryptography version that didn’t need Rust
See https://stackoverflow.com/a/68472128/6850197
2021-10-07 00:18:45 +03:00
Oleg Kalachev
17e806601d Tune parameters of external camera 2021-10-07 00:18:19 +03:00
Oleg Kalachev
3e3c5aa453 Add maintain_camera_rate argument to simulator.launch 2021-10-06 23:54:19 +03:00
Oleg Kalachev
7fd463d1cb docs: add note on bridge mode for using rviz etc 2021-10-06 22:12:47 +03:00
Oleg Kalachev
64b083b242 Add external camera model 2021-10-06 21:17:21 +03:00
Oleg Kalachev
b2ec48f0f3 blocks: minor fix 2021-10-06 17:53:25 +03:00
Oleg Kalachev
b249524828 Use markdownlint-cli@0.28.1 as of https://github.com/DavidAnson/markdownlint/issues/435 2021-10-05 16:33:38 +03:00
Oleg Kalachev
d0dcc0e72a docs: some fixes to rviz article 2021-10-05 15:34:59 +03:00
Oleg Kalachev
4c6e7029e8 docs: fix 2021-09-26 18:23:52 +03:00
Oleg Kalachev
613f70fd25 Add subscriber example 2021-09-24 17:34:54 +03:00
Oleg Kalachev
77832e65fa blocks: implement global navigation 2021-09-16 19:41:11 +03:00
Oleg Kalachev
01edd129ab blocks: show global_position block 2021-09-16 18:05:19 +03:00
Oleg Kalachev
d03acff31b simulator.launch: make gui argument work on jmavsim 2021-09-13 13:23:14 +03:00
oponfil
22e74febd6 docs: added the gsreamer's section in duocam_setup.md (#387)
* Added the gsreamer's section in duocam_setup.md

Added the section on how to modify video using GStreamer

* Edit

Co-authored-by: Oleg Kalachev <okalachev@gmail.com>
2021-09-13 12:07:16 +03:00
Oleg Kalachev
989d9b66f1 simulation: disable optical flow for jmavsim simulation 2021-09-04 04:06:57 +03:00
oponfil
f8eb8e1e67 docs: fix in VEYE-MIPI-327E cam driver installation instruction (#386)
* Some fix in VEYE-MIPI-327E cam driver install

Don't update Linux Kernel before install VEYE-MIPI-327E driver

* Edit

Co-authored-by: Oleg Kalachev <okalachev@gmail.com>
2021-09-02 21:30:58 +03:00
Oleg Kalachev
b92ebe7d60 www: determine rosdistro in topics view 2021-09-02 05:19:13 +03:00
Oleg Kalachev
af51e88179 Fix building docs
Use forked validate-links plugin
2021-09-02 05:18:22 +03:00
oponfil
59c8debcab docs: add duocam_setup.md (#385)
Co-authored-by: Oleg Kalachev <okalachev@gmail.com>
2021-08-25 00:29:31 +03:00
Oleg Kalachev
ec6f3089e3 docs: add snippet to wait for global position 2021-08-24 20:43:26 +03:00
Oleg Kalachev
2b88d21792 blocks: tooltips updates 2021-08-24 18:00:59 +03:00
Oleg Kalachev
274b81c50f blocks: fix global_position block 2021-08-24 17:49:21 +03:00
Oleg Kalachev
33a6dffb1f Make examples more verbose 2021-08-23 22:42:47 +03:00
Oleg Kalachev
5f9b3e82db clover: add GPS usage example 2021-08-23 22:39:29 +03:00
Oleg Kalachev
5f43367d82 simple_offboard: consider message timed out if time stamp is zero 2021-08-23 22:39:10 +03:00
Oleg Kalachev
7809e7ed2d docs: more notes on configuring the simulator 2021-08-23 22:34:54 +03:00
Oleg Kalachev
1688b97091 Enable GPS aiding in EKF2 by default in Clover airframe 2021-08-23 22:23:48 +03:00
Oleg Kalachev
1c6129fde8 clover_description: fix gps sensor 2021-08-23 22:23:24 +03:00
Oleg Kalachev
dae9599d64 web: confirm using Butterfly Terminal on a local machine 2021-08-18 21:15:31 +03:00
Oleg Kalachev
c0677f6aa3 docs: typo 2021-08-18 21:05:27 +03:00
Oleg Kalachev
e7bbf21700 docs: minor update to rangefinder article 2021-08-18 21:05:20 +03:00
Oleg Kalachev
58c10d7cb8 clover_blocks: make default yaw_tolerance 1 2021-08-18 20:35:27 +03:00
Oleg Kalachev
b6bd6bdde8 clover_description: simplify URDF and copy Gazebo plugins from current PX4’s Iris 2021-08-16 19:12:18 +03:00
Oleg Kalachev
3374c7756c docs: minor clarification to setup article 2021-08-16 18:40:04 +03:00
Oleg Kalachev
0dffeca55f clover_description: make xacro include relative 2021-08-14 21:39:57 +03:00
Oleg Kalachev
8cb911854d Minor changes to examples 2021-08-12 21:09:43 +03:00
Oleg Kalachev
a1b3efe67d simple_offboard: avoid TF_REPEATED_DATA warnings of setpoint frame 2021-08-12 13:00:23 +03:00
Oleg Kalachev
6700d8728f Add .vscode to .gitignore 2021-08-12 10:38:34 +03:00
Oleg Kalachev
be2e6ae198 clover_simulation: add Clover PX4 startup file 2021-08-11 19:17:36 +03:00
Oleg Kalachev
b9eed0f3ad optical_flow: publish flow when local position is not available
With integrated_*gyro = NAN
2021-08-10 07:06:15 +03:00
Oleg Kalachev
853a7fcf67 Minor code style fix 2021-08-10 06:56:47 +03:00
Oleg Kalachev
e342796f07 docs: add note on disabling optical flow if using ceiling markers placement 2021-08-04 08:00:36 +03:00
Oleg Kalachev
4fa70aa73a docs: fix link 2021-08-03 11:28:42 +03:00
Oleg Kalachev
226c91c3d8 docs: fix 2021-08-01 08:48:25 +03:00
SeliverstovaE
e33c9e78ea docs: add copterhack2021 video (ru) (#377)
Co-authored-by: Oleg Kalachev <okalachev@gmail.com>
2021-07-30 08:43:35 +03:00
SeliverstovaE
18c927469e docs: add copterhack2021 video (#378)
Co-authored-by: Oleg Kalachev <okalachev@gmail.com>
2021-07-30 08:43:10 +03:00
Oleg Kalachev
a465afd65c Don’t put .img file into images subdirectory in zip file 2021-07-30 08:20:11 +03:00
Oleg Kalachev
a2c65d2466 ci: print built image contents 2021-07-30 08:07:03 +03:00
Oleg Kalachev
ef7faa126a Fix aruco_gen utility for Python 3 2021-07-29 05:58:56 +03:00
Oleg Kalachev
d0666ca9d7 simulator.launch: add gui argument 2021-07-27 19:06:22 +03:00
Oleg Kalachev
b48f22cd35 simulator.launch: remove rc=false as it false by default 2021-07-23 14:32:22 +03:00
Oleg Kalachev
731f908053 clover_description: fix view_drone.launch to view clover4 urdf 2021-07-22 19:58:46 +03:00
timkondratiev
505a1efebd docs: add info how to determine Clover version (#376)
* Add info how to determine Clover version

* Optimize image

* Edit

Co-authored-by: Oleg Kalachev <okalachev@gmail.com>
2021-07-22 16:47:18 +03:00
Oleg Kalachev
f1b5484e65 docs: minor fix 2021-07-20 13:39:18 +03:00
Oleg Kalachev
3343477a02 docs: remove trailing spaces 2021-07-20 13:23:18 +03:00
oponfil
60da608191 docs: add how to use Quectel EP06 4G modem (#375)
Co-authored-by: Oleg Kalachev <okalachev@gmail.com>
2021-07-20 13:19:01 +03:00
Oleg Kalachev
7e383d713d docs: typos 2021-07-17 20:34:25 +03:00
Oleg Kalachev
c2a60380b7 docs: edit copterhack-2022 template 2021-07-17 19:26:54 +03:00
oponfil
7f82f8683f docs: change some fasteners (#373)
* Changed installing Raspberry Pi with M2.5 bolts

Changed installing Raspberry Pi with M2.5 bolts

* Changed installing Raspberry Pi with M2.5 bolts

Changed installing Raspberry Pi with M2.5 bolts

* Changed some fasteners

Changed some bolts from M3x8 to M3x10

* Update assemble_4_2.md

* Update assemble_4_2_pro.md

* Update assemble_4_2.md

* Update assemble_4_2_ws.md

* Update assemble_4_2.md

* Update assemble_4_2_ws.md
2021-07-16 22:32:10 +03:00
Oleg Kalachev
e28cbea8d9 docs: add a note on creating a new branch into copterhack2022 2021-07-16 22:31:01 +03:00
Oleg Kalachev
27528c20dc docs: simplify 2021-07-16 21:55:18 +03:00
Oleg Kalachev
e3503fca35 docs: fix 2021-07-16 21:54:04 +03:00
Oleg Kalachev
40b8941cab docs: edit copterhack-2021 template 2021-07-16 21:27:19 +03:00
Oleg Kalachev
0a9b6fff95 docs: fix 2021-07-16 11:03:28 +03:00
Oleg Kalachev
4a4e539edd docs: fixes 2021-07-16 11:03:00 +03:00
Oleg Kalachev
8d24a737ac docs: typos 2021-07-16 10:33:46 +03:00
oponfil
20af13947d docs: changed installing Raspberry Pi with M2.5 bolts (#372)
* Changed installing Raspberry Pi with M2.5 bolts

Changed installing Raspberry Pi with M2.5 bolts

* Changed installing Raspberry Pi with M2.5 bolts

Changed installing Raspberry Pi with M2.5 bolts
2021-07-15 17:56:38 +03:00
Oleg Kalachev
e91609ff61 docs: typos 2021-07-14 21:48:14 +03:00
Oleg Kalachev
0024372c45 docs: remove unnecessary text 2021-07-14 21:23:23 +03:00
Oleg Kalachev
b62d202a29 docs: add not on ATT_W_MAG when using GPS 2021-07-14 20:34:44 +03:00
Oleg Kalachev
84d685469a aruco_pose: minor fix in readme 2021-07-14 20:33:49 +03:00
Oleg Kalachev
3f6bb0cd68 docs: comment out link to arucogenmap as it’s not up
Also link to author is added.
2021-07-14 12:00:50 +03:00
oponfil
c01a145a16 docs: specified how to install a COEX Pix (#371)
* Specify how to install COEX Pix

With ROLL_180_YAW_90 orientation

* Specify how to install COEX Pix

* Update assemble_4_2_pro.md

* Update assemble_4_2.md

* Update assemble_4_2_ws.md

* Update calibration.md

* Typos

* Edit

Co-authored-by: Oleg Kalachev <okalachev@gmail.com>
2021-07-14 08:20:05 +03:00
oponfil
6d28bf4ef9 docs: added 4.2 to Clover Pro in summary (#370)
Fixed the title
2021-07-13 10:01:32 +03:00
oponfil
f6ea7209db docs: fix 'ZeroTire' to 'ZeroTier' (en) (#368)
Fixed spelling error
2021-07-12 19:27:12 +03:00
oponfil
21727ef76d docs: fix 'ZeroTire' to 'ZeroTier' (ru) (#369)
Fixed spelling error
2021-07-12 19:26:55 +03:00
oponfil
9847b7a71c docs: highlight GPIO21 (#359)
Highlighted GPIO21
2021-07-12 18:55:03 +03:00
oponfil
a5d44ff63a docs: update leds_old.md (#360)
Highlighted GPIO21
2021-07-12 18:54:39 +03:00
oponfil
391a2f9af9 docs: put the right images of how to connect a LED stripe (#361)
Put the right images of how to connect a LED stripe
2021-07-12 16:37:58 +03:00
oponfil
5918702fbd docs: update 4g.md (#355)
* Update 4g.md

Added how to stream from USB cam

* Edit

* Update 4g.md

Co-authored-by: Oleg Kalachev <okalachev@gmail.com>
2021-07-12 10:45:01 +03:00
oponfil
69fe288a41 docs: specified the aluminum 15 mm stands (#366)
Specified the aluminum 15 mm stands
2021-07-12 10:31:02 +03:00
oponfil
5154720348 docs: specified the aluminum 15 mm stands (#367)
Specified the aluminum 15 mm stands
2021-07-12 10:30:47 +03:00
oponfil
cc1694661d docs: update copterhack2022.md (#365)
Delete PX4 v.1.12 case. Modified Pixhawk FMUv6U
2021-07-12 09:54:37 +03:00
oponfil
d5efa962d8 docs: update copterhack2022.md (#364)
Delete PX4 v.1.12 case. Modified Pixhawk FMUv6U
2021-07-12 09:54:26 +03:00
Oleg Kalachev
1195336cbc Exclude json-to-pretty-yaml.js from linting 2021-07-08 10:06:43 +03:00
Oleg Kalachev
5fc67b8e65 web: show messages in yaml format in topic viewer 2021-07-08 09:57:24 +03:00
Oleg Kalachev
c1409d4467 web: group 3d visualization and markers map visualization 2021-07-08 09:39:19 +03:00
Oleg Kalachev
7a1f09da98 image: log all clover service output to /var/log/clover.log 2021-07-08 09:25:29 +03:00
oponfil
6934cc7a1a docs: update 4g.md (#356)
* Update 4g.md

Add info to stream from usb camera video1

* Update 4g.md

Co-authored-by: Oleg Kalachev <okalachev@gmail.com>
2021-07-07 06:50:31 +03:00
Oleg Kalachev
2550ffe532 Minor typo 2021-07-07 06:43:34 +03:00
Oleg Kalachev
9def866a30 web: add simple topics viewer 2021-07-07 06:34:52 +03:00
oponfil
802f8eeaa4 docs: update 4g.md (#354)
Shorted the title
2021-07-07 01:18:29 +03:00
Oleg Kalachev
504aa53b1a image: make udev rules symlinks
Rename package’s udev directory from config to more standard udev
2021-07-06 06:33:51 +03:00
Oleg Kalachev
015cf730c2 image: make clover.service and roscore.server symlinks 2021-07-06 06:25:45 +03:00
Oleg Kalachev
8e6ef727ce builder: show systemd version 2021-07-06 06:22:50 +03:00
1Den4ik1
973e1f1181 docs: update magnetic_grip_load.md (#352)
* Update magnetic_grip_load.md

* Update magnetic_grip_load.md

Co-authored-by: Oleg Kalachev <okalachev@gmail.com>
2021-07-06 01:33:42 +03:00
Oleg Kalachev
275faa78a4 image: validate documented packages can be installed 2021-07-06 00:52:06 +03:00
Oleg Kalachev
fc7d010881 Exclude unused asset for readme from check 2021-06-30 16:32:22 +03:00
Oleg Kalachev
242e35f84a docs: make links to the latest release of vm 2021-06-30 16:10:22 +03:00
Oleg Kalachev
3f07d28e0f docs: simplify titles of simple offboard api articles 2021-06-30 16:06:33 +03:00
Oleg Kalachev
613d378e66 readme: add clover-4.2 image with margin 2021-06-30 16:03:11 +03:00
stinger000
769c421898 docs: add articles for Clover Pro (ru) (#350)
Co-authored-by: Oleg Kalachev <okalachev@gmail.com>
2021-06-29 23:03:02 +03:00
Oleg Kalachev
3830ceea04 aruco.launch: change default not-in-map marker size to 0.22 2021-06-29 21:50:05 +03:00
Oleg Kalachev
df3a11b035 docs: add link to @devclover chat 2021-06-29 01:21:33 +03:00
stinger000
ef5700845f docs: fix incorrect table (#351) 2021-06-28 20:00:51 +03:00
Oleg Kalachev
921084504e Make gitbook installation script
commit f83207848fafa302d6385f4814386c433aeb9686
Author: Oleg Kalachev <okalachev@gmail.com>
Date:   Wed Jun 23 17:35:01 2021 +0300

    Make gitbook installation script
2021-06-23 19:00:32 +03:00
Oleg Kalachev
6550780afb aruco.launch: remove unnecessary condition 2021-06-23 08:32:39 +03:00
1Den4ik1
2448915300 docs: add magnetic grip load model (ru) (#349)
* Add new article for Clover

* Update Gruz_dlya_magnitnogo_zahvata.md

* Gruz_dlya_magnitnogo_zahvata.md -> magnetic_grip_load.md

* Rename image files

* Optimize images

* Edit article

* Make images widths 300

* Files for 3D Print

* Update models.md

Add files for 3D print #338

* Update models.md

* Edit

Co-authored-by: Oleg Kalachev <okalachev@gmail.com>
2021-06-22 18:19:53 +03:00
Oleg Kalachev
247a7917d9 docs: download ala-too logo to the repo 2021-06-19 05:16:52 +03:00
Oleg Kalachev
37f2c78b36 CI: shorten names 2021-06-18 00:56:16 +03:00
Oleg Kalachev
e76618bd3b cmake: install www directories 2021-06-17 23:42:33 +03:00
Oleg Kalachev
9fbfcfbd2e docs: remove unused assets 2021-06-16 15:21:30 +03:00
Oleg Kalachev
2003b4516a docs: fixes in models page 2021-06-16 15:02:57 +03:00
Oleg Kalachev
03985ae1b8 docs: add links to carbon parts 2021-06-15 20:40:55 +03:00
Oleg Kalachev
a47d5d1bfe docs: remove unused assets 2021-06-15 18:26:37 +03:00
Oleg Kalachev
20075dd40f docs: add links to hardware parts 2021-06-15 18:13:07 +03:00
Oleg Kalachev
c247c75d17 aruco_pose, clover, clover_blocks: fix install sections 2021-06-09 15:40:24 +03:00
Oleg Kalachev
c36279e536 image: move examples to clover package 2021-06-09 15:40:22 +03:00
SeliverstovaE
1471a53b3a docs: update copterhack2022.md (#348) 2021-06-09 15:37:26 +03:00
SeliverstovaE
931f50a458 docs: update copterhack 2022 (#347)
* Update copterhack2022.md

* Update copterhack2022.md

Co-authored-by: Oleg Kalachev <okalachev@gmail.com>
2021-06-08 23:38:09 +03:00
Oleg Kalachev
118b4573fe docs: add link to packages repo 2021-06-08 20:28:38 +03:00
Oleg Kalachev
f77843f4a5 Move ROS Noetic (#327)
* builder: Use 64-bit Raspberry Pi OS

* travis: Use 64-bit builder

* builder: Don't try to install Melodic packages on Noetic

* clover: Use package version 3, update dependencies

* travis: Enable Noetic build

* standalone_install: Auto-select Python, ROS distro

* builder: Use variable substitution for ROS_DISTRO

* builder: Add Noetic package definitions

* builder: Use variable substitution for validation

* aruco_pose, clover: Allow compiling against OpenCV 3 and 4

* builder: Add proper Noetic repository

* builder: Don't force Tornado version

Assume rosbridge_suite depends on the right one.

* builder: Install packages for Python 3

* builder/test: Use Python3 interpreter for ROS tests

TODO (?): add tests for Python2?

* builder: Use Python 3 syntax for Python 3 tests

* builder: Install rpi_ws281x for Python3

* standalone_install: Use proper Python for pytest

* builder: Install espeak for python3

* builder: Use proper path for roscore

* builder: Install rosdep, etc. for python3

* builder: Run Clever/Clover test with Python3

* builder: Use Python3 for Clever compat layer

* builder: Enable OpenCV 4.2 repository

* builder: Force versions for ROS packages that use OpenCV

Also, hold their versions so that they don't get updated for no reason.

* aruco_pose/draw: Replace OpenCV projection code with a rewrite

* builder: Don't try to install compressed_transport twice

* clover: Fix importing urllib for Python3

* aruco_pose, clover: Expose Python scripts through CMake

* clover/selfcheck: Be more python3-compatible

This is basically commit a01d199890 from buster-python3, not sure if it aged well.

* roswww_static: Add python script installation

* clover_blocks: Use Python3 syntax for exec

* aruco_pose: Remove unused code

* Melodic => Noetic in some docs

* docs: add 0.22 migration article

* docs: remove unneeded comment

* docs: python 3 updates

* docs: python 3 update in auto_setup article

* docs: add ROS Noetic transition note

* aruco.launch: add placement, length and map arguments

* genmap.py: add -o argument for output file name

* docs: use -o argument of genmap.py

* simple_offboard: correctly check manual control timeout, separate it from kill switch check

* blocks: force led_leds index to int

* docs: update and fix 0.22 migration articles

* blocks: fix set_leds with color-typed argument

* aruco_gen: Open file in binary mode for Python3 compatibility

* clover: Use proper variable in aruco.launch

* led: change default number of leds to 72

* aruco_pose: Make sure there are no undefined symbols

Also, compile in apriltag_quad_thresh.cpp - it contains some of the functions referenced
in aruco.cpp, which would otherwise be undefined.

* aruco_pose: Make vendored library compatible with older OpenCVs

* aruco_pose, clover: Reduce the amount of OpenCV libs requested

* aruco_pose, clover: Move subscriptions to the end of init

* aruco_pose: Don't expose vendored library symbols

* aruco_pose: Simplify dynamic parameter callback setting

* builder: Build with debug symbols

* clover: Attempt to respawn dying nodelets

* Change Raspberry Pi OS to latest armhf, use packages.coex.tech as a source

* Add CRYPTOGRAPHY_DONT_BUILD_RUST=1

* Fix Node.js installation

* image: use older CMake (3.13.4-1)
Fixing https://travis-ci.org/github/CopterExpress/clover/jobs/764367665#L6984

* image: update Raspberry Pi OS to 2021-03-04

* image: bring back moving ld.so.preload out of the way while building

* Fix pthreads ld error

* Try to fix pthreads ld error

* Another attempt to fix pthreads ld error

* Yet another attempt to fix pthreads ld error

* Try to fix

* Be verbose

* Temporarily disable rc and camera_markers building

* Fix standalone-install

* Revert "Temporarily disable rc and camera_markers building"

This reverts commit e119220e91.

* Try to fix

* Try to fix

* Revert "image: use older CMake (3.13.4-1)"

This reverts commit df28da0060.

* Revert "Revert "image: use older CMake (3.13.4-1)""

This reverts commit a28c774e8f.

* Verbosity

* Debugging

* More debugging

* Display all CMake variables

* Try to fix

* Another try to fix

* Revert "Another try to fix"

This reverts commit 5a4c3a0da7.

* Another try to fix

* And another

* And yet another

* Continue...

* Cleanup

* Sources lists cleanup

* More cleanup

* Restore .git directory in clover repo

* Fix building documentation

* Fix documentation building in image

* Trigger build to update ws281x package

* Test

* Disable unneeded hack

* Disable hack

* image: add cmake-modules package

* www: add viewing clover.err file from web interface

* Remove hacks

* Show nodelet version

* docs: add packages article

* image: add image-view package for recording video from topics

* Minor fix

* CI: add Docker authentication on image build

* CI: fix Bash syntax

* CI: fix authentication in Docker

* CI: move Melodic build and editorconfig-lint to GitHub Actions (#331)

* Create main.yml

* Update main.yml

* Disable native Melodic build in Travis

* Run editorconfig-lint in Actions

* Let wget be less verbose

* Test

* Test ok

* Disable editorconfig-lint in Travis

* docs: add links to hardware sources

* CI: move image building to GitHub actions (#335)

* Start working on building image in GitHub actions

* Trigger GitHub on push to any branch

* Fix TRAVIS_TAG

* Add compress image step

* Disable image build in Travis

* Add upload image step

* Fix compress image

* Fix

* Fix

* Minor fix

* Trigger build on tag

* Show images sizes not in human format

* Upload only built image

* Make prerelease

* Upload assets on release not on tags

* readme: change build badge to GitHub Actions

* readme: add support chat badge

* CI: move documentation building to GitHub Actions (#337)

* CI: change docs target branch to actions

* CI: change docs target branch to master

* CI: use gh-pages target branch for docs

* CI: split up to several workflows

* CI: remove .travis.yml

* CI: change apt to apt-get

* CI: push documentation site to the main repo

* builder: less verbosity

* CI: add new key for apt
Fixing https://github.com/CopterExpress/clover/runs/2700356960#step:3:74

* Add Noetic building to CI

* Add test for QR recognition

* Fix

* Move QR recognition test to a separate file

* Fix QR recognition code for Python 3

* Import SetLEDs, LEDStateArray, LEDState in tests

* Add more imports to tests
(from documentation)

* Fix permissions

* Fix standalone-install for Python 2

* Fix QR recognition test

* Don’t use ROS for QR recognition test

* docs: remove non-working example

* Make v4l2 device file an argument in main_camera.launch

* Wait for v4l2 device before launching the camera driver

* Use exec in waitfile

* Transfer main camera nodelet manager to main_camera.launch

* Update cv_camera version to 0.5.1

* docs: minor fix

* Revert cv_camera to 0.5.0

* Update Raspberry Pi OS to 2021-05-07

* docs: add link to the last ROS Melodic version.

Co-authored-by: Alexey Rogachevskiy <sfalexrog@gmail.com>
2021-06-08 20:13:46 +03:00
Oleg Kalachev
5f62a8639a docs: use -o argument of genmap.py 2021-06-08 19:28:17 +03:00
Oleg Kalachev
fa1db1d90b genmap.py: add -o argument for output file name 2021-06-08 19:28:09 +03:00
Alexey Rogachevskiy
1a2e87bb6a aruco_pose, clover: Move subscriptions to the end of init 2021-06-08 19:23:39 +03:00
Oleg Kalachev
7dbd983ec5 Update Raspberry Pi OS to 2021-05-07 2021-06-08 19:10:30 +03:00
Oleg Kalachev
d2d395f1fc docs: add copterhack-2022 logo 2021-06-08 14:27:37 +03:00
oponfil
ff93f79c0a docs: some changes to copterhack2022 (#345)
Have made some changes in text
2021-06-07 19:39:18 +03:00
SeliverstovaE
5deb09eb45 docs: changed the dates for copterhack2022 (#344) 2021-06-07 19:38:39 +03:00
SeliverstovaE
70b8be5c5d docs: copterhack 2022 (en) (#343)
* Create copterhack2022.md

* Fixes

Co-authored-by: Oleg Kalachev <okalachev@gmail.com>
2021-06-07 12:32:32 +03:00
Oleg Kalachev
2a08e20b47 docs: minor fix 2021-06-07 12:17:24 +03:00
SeliverstovaE
3328d8f4ac docs: update copterhack 2022 (#341) 2021-06-05 16:56:00 +03:00
oponfil
f7fb814894 docs: update copterhack 2022(#340)
Fixed some text phrases
2021-06-05 16:54:54 +03:00
SeliverstovaE
3a3b0bbd80 docs: Copterhack2022 (ru) (#336)
* Add new article for Clover

* Edit article

* Optimize images

* Remove unused asset

Co-authored-by: Oleg Kalachev <okalachev@gmail.com>
2021-06-05 12:06:42 +03:00
Oleg Kalachev
ca095f3f16 docs: update wi-fi screenshots 2021-06-04 09:14:56 +03:00
Oleg Kalachev
baf2467939 docs: minor fix 2021-06-03 17:53:55 +03:00
Oleg Kalachev
abba3bf876 docs: change some redirects to English version
Since GitHub pages considers /<page> as /<page>.html now
2021-06-03 11:38:19 +03:00
Oleg Kalachev
346373ed23 Use exec in waitfile 2021-06-03 09:58:41 +03:00
Oleg Kalachev
bb996056c9 docs: remove non-working example 2021-06-02 08:03:51 +03:00
Oleg Kalachev
0e0b1cdc31 docs: minor fix 2021-06-01 06:00:12 +03:00
1Den4ik1
eceaa0ec91 docs: add magnetic grip load article (ru) (#338)
Co-authored-by: Oleg Kalachev <okalachev@gmail.com>
2021-06-01 05:56:25 +03:00
Oleg Kalachev
f29686b9f4 CI: add new key for apt
Fixing https://github.com/CopterExpress/clover/runs/2700356960#step:3:74
2021-06-01 03:44:20 +03:00
Oleg Kalachev
b7f1f2b391 builder: less verbosity 2021-05-29 13:22:57 +03:00
Oleg Kalachev
6b0bb41564 CI: push documentation site to the main repo 2021-05-29 06:11:29 +03:00
Oleg Kalachev
563e5acad6 CI: change apt to apt-get 2021-05-29 05:31:29 +03:00
Oleg Kalachev
5932faa29c CI: remove .travis.yml 2021-05-29 05:29:32 +03:00
Oleg Kalachev
bcc2e86e6f CI: split up to several workflows 2021-05-29 05:27:20 +03:00
Oleg Kalachev
e80a1cc7d6 CI: use gh-pages target branch for docs 2021-05-28 22:43:58 +03:00
Oleg Kalachev
5fd3a92c7b CI: change docs target branch to master 2021-05-28 22:21:07 +03:00
Oleg Kalachev
84b87055df CI: change docs target branch to actions 2021-05-28 21:20:43 +03:00
Oleg Kalachev
7cc0f066c7 CI: move documentation building to GitHub Actions (#337) 2021-05-28 21:09:09 +03:00
Oleg Kalachev
868fc728dd readme: add support chat badge 2021-05-28 01:47:33 +03:00
Oleg Kalachev
faa90b89f6 readme: change build badge to GitHub Actions 2021-05-28 01:31:32 +03:00
Oleg Kalachev
f4d07e2c2c CI: move image building to GitHub actions (#335)
* Start working on building image in GitHub actions

* Trigger GitHub on push to any branch

* Fix TRAVIS_TAG

* Add compress image step

* Disable image build in Travis

* Add upload image step

* Fix compress image

* Fix

* Fix

* Minor fix

* Trigger build on tag

* Show images sizes not in human format

* Upload only built image

* Make prerelease

* Upload assets on release not on tags
2021-05-28 01:25:29 +03:00
Oleg Kalachev
fad7886012 docs: add links to hardware sources 2021-05-27 20:33:47 +03:00
Oleg Kalachev
7eb139fd22 CI: fix authentication in Docker 2021-05-26 23:56:41 +03:00
Oleg Kalachev
855d13e210 CI: fix Bash syntax 2021-05-26 23:46:09 +03:00
Oleg Kalachev
781b8962be CI: add Docker authentication on image build 2021-05-26 23:43:52 +03:00
Oleg Kalachev
047a965f9f CI: move Melodic build and editorconfig-lint to GitHub Actions (#331)
* Create main.yml

* Update main.yml

* Disable native Melodic build in Travis

* Run editorconfig-lint in Actions

* Let wget be less verbose

* Test

* Test ok

* Disable editorconfig-lint in Travis
2021-05-24 16:31:29 +03:00
Oleg Kalachev
47060db84b travis: disable native kinetic build 2021-05-20 20:59:50 +03:00
Oleg Kalachev
2693fd4ace docs: typos 2021-05-20 20:46:48 +03:00
Oleg Kalachev
faa702cab0 docs: typos 2021-05-20 20:27:46 +03:00
Oleg Kalachev
150ecbe29d Remove all unneeded static’s 2021-05-20 18:32:49 +03:00
Oleg Kalachev
df5e83e416 led: add error/ignore parameter to ignore some errors 2021-05-20 18:30:47 +03:00
Oleg Kalachev
1f2ba65669 Minor fix 2021-05-20 18:00:22 +03:00
452 changed files with 8258 additions and 132689 deletions

1
.gitattributes vendored
View File

@@ -3,6 +3,7 @@ roslib.js linguist-vendored
eventemitter2.js linguist-vendored
ros3d.js linguist-vendored
three.min.js linguist-vendored
json-to-pretty-yaml.js linguist-vendored
aruco_pose/vendor/* linguist-vendored
blockly/* linguist-vendored
highlight/* linguist-vendored

29
.github/workflows/build-image.yaml vendored Normal file
View File

@@ -0,0 +1,29 @@
name: RPi image
on:
push:
branches: [ '*' ]
pull_request:
branches: [ master ]
release:
types: [ created ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Build image
run: |
docker run --privileged --rm -v /dev:/dev -v $(pwd):/builder/repo -e TRAVIS_TAG="${{ github.event.release.tag_name }}" sfalexrog/img-tool:qemu-update
- name: Compress image
run: |
cd images && sudo chmod -R 777 . && zip -9 $(echo clover_*).zip clover_* && ls -l . && unzip -l clover_*.zip
- name: Upload image
uses: softprops/action-gh-release@v1
if: ${{ github.event_name == 'release' }}
with:
files: images/clover_*.zip
prerelease: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

51
.github/workflows/build.yml vendored Normal file
View File

@@ -0,0 +1,51 @@
name: Build
on:
push:
branches: [ '*' ]
pull_request:
branches: [ master ]
jobs:
# melodic:
# runs-on: ubuntu-latest
# steps:
# - uses: actions/checkout@v2
# - name: Native Melodic build
# run: |
# docker run --rm -v $(pwd):/root/catkin_ws/src/clover ros:melodic-ros-base /root/catkin_ws/src/clover/builder/standalone-install.sh
noetic:
runs-on: ubuntu-latest
container: ros:noetic-ros-base
defaults:
run:
working-directory: catkin_ws
shell: bash
steps:
- uses: actions/checkout@v2
with:
path: catkin_ws/src/clover
- name: Install requirements
run: apt-get update && apt-get -y install python3-pip fakeroot python3-bloom debhelper dpkg-dev
- name: Install dependencies
run: rosdep update && rosdep install --from-paths src --ignore-src -y
- name: Install GeographicLib datasets
run: wget -qO- https://raw.githubusercontent.com/mavlink/mavros/master/mavros/scripts/install_geographiclib_datasets.sh | bash
- name: catkin_make
run: source /opt/ros/$ROS_DISTRO/setup.bash && catkin_make
- name: Run tests
run: source devel/setup.bash && catkin_make run_tests && catkin_test_results
- name: Build Debian packages
run: |
source devel/setup.bash
for file in `find . -name "package.xml"`; do
cd $(dirname ${file})
bloom-generate rosdebian --os-name ubuntu --os-version $(lsb_release -cs) --ros-distro $ROS_DISTRO
fakeroot debian/rules binary
cd -
done
- uses: actions/upload-artifact@v3
with:
name: debian-packages
path: catkin_ws/src/clover/*.deb
retention-days: 1

86
.github/workflows/docs.yml vendored Normal file
View File

@@ -0,0 +1,86 @@
name: Documentation
on:
push:
branches: [ '*' ]
pull_request:
branches: [ '*' ]
permissions:
contents: read
pages: write
id-token: write
concurrency:
group: "pages"
cancel-in-progress: true
defaults:
run:
shell: bash
jobs:
docs:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v2
- name: Use Node.js
uses: actions/setup-node@v1
with: { node-version: '10' }
- name: Setup tools
run: |
sudo sh -c "echo ttf-mscorefonts-installer msttcorefonts/accepted-mscorefonts-eula select true | debconf-set-selections"
sudo apt-get update && sudo apt-get install -y calibre msttcorefonts
builder/assets/install_gitbook.sh
npm install markdownlint-cli@0.28.1 -g # FIXME: https://github.com/DavidAnson/markdownlint/issues/435
npm install svgexport -g
gitbook -V
markdownlint -V
- name: Run markdownlint
run: markdownlint docs
- name: Check Assets
run: |
./check_assets_size.py
./check_unused_assets.py
- name: Build GitBook
run: |
gitbook install
gitbook build
- name: Generate PDF
id: generate-pdf
env:
GITBOOK_SKIP_PDF: ${{ secrets.GITBOOK_SKIP_PDF }}
continue-on-error: ${{ env.GITBOOK_SKIP_PDF != '' }}
if: ${{ github.event_name == 'push' }}
run: |
for i in 1 2 3 4; do gitbook pdf ./ _book/clover.pdf && break || sleep 1; done
sudo apt-get -q install ghostscript
gs -sDEVICE=pdfwrite -dCompatibilityLevel=1.4 -dPDFSETTINGS=/default -dNOPAUSE -dQUIET -dBATCH -dDetectDuplicateImages -dCompressFonts=true -r150 -sOutputFile=_book/clover_ru_compressed.pdf _book/clover_ru.pdf
gs -sDEVICE=pdfwrite -dCompatibilityLevel=1.4 -dPDFSETTINGS=/default -dNOPAUSE -dQUIET -dBATCH -dDetectDuplicateImages -dCompressFonts=true -r150 -sOutputFile=_book/clover_en_compressed.pdf _book/clover_en.pdf
rm _book/clover_ru.pdf && mv _book/clover_ru_compressed.pdf _book/clover_ru.pdf
rm _book/clover_en.pdf && mv _book/clover_en_compressed.pdf _book/clover_en.pdf
ls -lah _book/clover*.pdf
echo '::set-output name=GITBOOK_PDF_OK::1'
- name: Download older PDFs
if: ${{ !steps.generate-pdf.outputs.GITBOOK_PDF_OK }}
run: |
rm -f _book/clover*.pdf
wget --no-verbose https://clover.coex.tech/clover_ru.pdf -P _book/
wget --no-verbose https://clover.coex.tech/clover_en.pdf -P _book/
- name: Upload artifact
# if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }}
uses: actions/upload-pages-artifact@v1
with:
path: _book
deploy-docs:
if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }}
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
needs: docs
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v1

18
.github/workflows/editorconfig.yaml vendored Normal file
View File

@@ -0,0 +1,18 @@
name: Editorconfig
on:
push:
branches: [ '*' ]
pull_request:
branches: [ master ]
jobs:
editorconfig:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: .editorconfig Linter
run: |
wget --no-verbose https://github.com/okalachev/editorconfig-checker/releases/download/1.2.1-disable-spaces-amount/ec-linux-amd64
chmod +x ec-linux-amd64
./ec-linux-amd64 -spaces-after-tabs -e "roslib.js|ros3d.js|eventemitter2.js|json-to-pretty-yaml.js|draw.cpp|BinUtils.swift|\.idea|apps/android/app|blockly/|clover_blocks/programs/|highlight/|python.js|Assets.xcassets|test_parser_pass.txt|test_node_failure.txt|aruco_pose/vendor|\.stl|\.dxf|\.dae|\.material"

1
.gitignore vendored
View File

@@ -6,3 +6,4 @@ _book/
package-lock.json
clover_blocks/programs/*.*
!clover_blocks/programs/examples/*
/.vscode/

View File

@@ -110,7 +110,8 @@
"Li-ion",
"Nvidia",
"VirtualBox",
"VMware"
"VMware",
"DuoCam"
],
"code_blocks": false
},

View File

@@ -1,132 +0,0 @@
os: linux
dist: xenial
language: generic
services:
- docker
env:
global:
- DOCKER="sfalexrog/img-tool:qemu-update"
- TARGET_REPO="https://github.com/${TRAVIS_REPO_SLUG}.git"
- IMAGE_VERSION=${TRAVIS_TAG:-${TRAVIS_COMMIT:0:7}}
- IMAGE_NAME="$(basename -s '.git' ${TARGET_REPO})_${IMAGE_VERSION}.img"
git:
depth: 1
jobs:
fast_finish: true
include:
- stage: Build
name: "Raspberry Pi Image Build"
cache:
directories:
- imgcache
before_script:
- docker pull ${DOCKER}
# 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}" != "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 ===" &&
export SKIP_BUILD=true;
fi;
fi
- if [ -z ${SKIP_BUILD} ]; then
docker run --privileged --rm -v /dev:/dev -v $(pwd):/builder/repo -e TRAVIS_TAG="${TRAVIS_TAG}" ${DOCKER};
fi
before_cache:
- cp images/*.zip imgcache
after_success:
- sudo chmod -R 777 *
- cd images && zip -9 ${IMAGE_NAME}.zip ${IMAGE_NAME} && stat --printf="Compressed image size:%s\n" ${IMAGE_NAME}.zip
before_deploy:
# Set up git user name and tag this commit
- git config --local user.name "goldarte"
- git config --local user.email "goldartt@gmail.com"
deploy:
provider: releases
token: ${GITHUB_OAUTH_TOKEN}
file: ${IMAGE_NAME}.zip
skip_cleanup: true
on:
tags: true
draft: true
name: ${TRAVIS_TAG}
- stage: Build
name: "Native Kinetic build"
env:
- NATIVE_DOCKER=ros:kinetic-ros-base
before_script:
- docker pull ${NATIVE_DOCKER}
script:
- docker run --rm -v $(pwd):/root/catkin_ws/src/clover ${NATIVE_DOCKER} /root/catkin_ws/src/clover/builder/standalone-install.sh
- stage: Build
name: "Native Melodic build"
env:
- NATIVE_DOCKER=ros:melodic-ros-base
before_script:
- docker pull ${NATIVE_DOCKER}
script:
- docker run --rm -v $(pwd):/root/catkin_ws/src/clover ${NATIVE_DOCKER} /root/catkin_ws/src/clover/builder/standalone-install.sh
- stage: Build
name: "Native Noetic build"
env:
- NATIVE_DOCKER=ros:noetic-ros-base
before_script:
- docker pull ${NATIVE_DOCKER}
script:
- docker run --rm -v $(pwd):/root/catkin_ws/src/clover ${NATIVE_DOCKER} /root/catkin_ws/src/clover/builder/standalone-install.sh
- stage: Build
name: "Documentation"
language: node_js
node_js:
- "10"
before_script:
- sudo sh -c "echo ttf-mscorefonts-installer msttcorefonts/accepted-mscorefonts-eula select true | debconf-set-selections"
- sudo apt update && sudo apt install -y calibre msttcorefonts
- npm install gitbook-cli -g
- gitbook fetch 3.2.3 && npm i npm@3.10.10 --prefix=~/.gitbook/versions/3.2.3/ # fixing https://travis-ci.org/github/CopterExpress/clover/jobs/766541125#L932
- npm install markdownlint-cli -g
- npm install svgexport -g
- gitbook -V
- markdownlint -V
script:
- markdownlint docs
- ./check_assets_size.py
- ./check_unused_assets.py
- gitbook install
- gitbook build
- for i in 1 2 3 4; do gitbook pdf ./ _book/clover.pdf && break || sleep 1; done
- sudo apt-get install ghostscript
- gs -sDEVICE=pdfwrite -dCompatibilityLevel=1.4 -dPDFSETTINGS=/default -dNOPAUSE -dQUIET -dBATCH -dDetectDuplicateImages -dCompressFonts=true -r150 -sOutputFile=_book/clover_ru_compressed.pdf _book/clover_ru.pdf
- gs -sDEVICE=pdfwrite -dCompatibilityLevel=1.4 -dPDFSETTINGS=/default -dNOPAUSE -dQUIET -dBATCH -dDetectDuplicateImages -dCompressFonts=true -r150 -sOutputFile=_book/clover_en_compressed.pdf _book/clover_en.pdf
- rm _book/clover_ru.pdf && mv _book/clover_ru_compressed.pdf _book/clover_ru.pdf
- rm _book/clover_en.pdf && mv _book/clover_en_compressed.pdf _book/clover_en.pdf
- ls -lah _book/clover*.pdf
deploy:
provider: pages
local_dir: _book
skip_cleanup: true
token: ${GITHUB_OAUTH_TOKEN}
keep_history: true
target_branch: master
repo: CopterExpress/clover.coex.tech
fqdn: clover.coex.tech
verbose: true
on:
branch: master
- 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|blockly/|clover_blocks/programs/|highlight/|python.js|Assets.xcassets|test_parser_pass.txt|test_node_failure.txt|aruco_pose/vendor|\.stl|\.dxf|\.dae"
stages:
- Build
# More info there
# https://github.com/travis-ci/travis-ci/issues/6893
# https://docs.travis-ci.com/user/customizing-the-build/
# https://docs.travis-ci.com/user/deployment/releases
# https://docs.travis-ci.com/user/environment-variables/

View File

@@ -1,6 +1,6 @@
# clover🍀: create autonomous drones easily
<img src="docs/assets/clover42-main.png" align="right" width="400px" alt="COEX Clover Drone">
<img src="docs/assets/clover42-main-margin.png" align="right" width="400px" alt="COEX Clover Drone">
Clover is an open source [ROS](https://www.ros.org)-based framework, providing user-friendly tools to control [PX4](https://px4.io)-powered drones. Clover is available as a ROS package, but is shipped mainly as a preconfigured image for Raspberry Pi. Once you've installed Raspberry Pi on your drone and flashed the image to its microSD card, taking the drone up in the air is a matter of minutes.
@@ -20,7 +20,7 @@ Clover drone is used on a wide range of educational events, including [Copter Ha
Preconfigured image for Raspberry Pi with installed and configured software, ready to fly, is available [in the Releases section](https://github.com/CopterExpress/clover/releases).
[![Build Status](https://travis-ci.org/CopterExpress/clover.svg?branch=master)](https://travis-ci.org/CopterExpress/clover)
![GitHub Workflow Status](https://img.shields.io/github/workflow/status/CopterExpress/clover/CI)
![GitHub all releases](https://img.shields.io/github/downloads/CopterExpress/clover/total)
Image features:
@@ -38,6 +38,10 @@ API description for autonomous flights is available [on GitBook](https://clover.
For manual package installation and running see [`clover` package documentation](clover/README.md).
## Support
[![Telegram Support Chat](https://img.shields.io/endpoint?label=Support%20Chat&url=https%3A%2F%2Ftelegram-badge-4mbpu8e0fit4.runkit.sh%2F%3Furl%3Dhttps%3A%2F%2Ft.me%2FCOEXHelpDesk)](https://t.me/COEXHelpdesk)
## License
While the Clover platform source code is available under the MIT License, note, that the [documentation](docs/) is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.

View File

@@ -1,5 +1,4 @@
iOS-приложение для управления Клевером
--------------------------------------
# iOS-приложение для управления Клевером
Для установки зависимостей необходим [CocoaPods](https://cocoapods.org):

View File

@@ -83,11 +83,10 @@ add_message_files(
)
## Generate services in the 'srv' folder
# add_service_files(
# FILES
# Service1.srv
# Service2.srv
# )
add_service_files(
FILES
SetMarkers.srv
)
## Generate actions in the 'action' folder
# add_action_files(
@@ -120,6 +119,7 @@ generate_messages(
## Generate dynamic reconfigure parameters in the 'cfg' folder
generate_dynamic_reconfigure_options(
cfg/Detector.cfg
cfg/Map.cfg
)
###################################
@@ -202,11 +202,11 @@ set_property(TARGET aruco_pose
# )
## Mark executables and/or libraries for installation
# install(TARGETS ${PROJECT_NAME} ${PROJECT_NAME}_node
# ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
# LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
# RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
# )
install(TARGETS ${PROJECT_NAME}
ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
)
## Mark cpp header files for installation
# install(DIRECTORY include/${PROJECT_NAME}/
@@ -226,6 +226,10 @@ catkin_install_python(PROGRAMS src/genmap.py
DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
)
install(DIRECTORY launch DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION})
install(DIRECTORY map DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION})
#############
## Testing ##
#############
@@ -247,4 +251,5 @@ if (CATKIN_ENABLE_TESTING)
add_rostest(test/test_node_failure.test)
add_rostest(test/largemap.test)
add_rostest(test/crash_opencv.test)
add_rostest(test/duplicate.test)
endif()

View File

@@ -51,6 +51,7 @@ It's recommended to run it within the same nodelet manager with the camera nodel
* `image_raw` (*sensor_msgs/Image*) camera image
* `camera_info` (*sensor_msgs/CameraInfo*) camera calibration info
* `map_markers` (*aruco_pose/MarkerArray*) list of markers to disable TF transform publishing
#### Published
@@ -70,10 +71,11 @@ It's recommended to run it within the same nodelet manager with the camera nodel
* `~map` path to text file with markers list
* `~frame_id` published frame id (default: `aruco_map`)
* `~known_tilt` debug image width
* `~known_tilt` known tilt (pitch and roll) of markers map as a frame
* `~image_width` debug image width (default: 2000)
* `~image_height` debug image height (default: 2000)
* `~image_margin`  debug image margin (default: 200)
* `~image_axis` whether debug image should contain axis (default: true)
* `~dictionary` (*int*)  ArUco dictionary (default: 2) - should be the same as `dictionary` parameter of `aruco_detect` nodelet
Map file has one marker per line with the following line format:
@@ -97,6 +99,7 @@ See examples in [`map`](map/) directory.
#### Published
* `~pose` (*geometry_msgs/PoseWithCovarianceStamped*) estimated map pose
* `~map` (*aruco_pose/MarkerArray*) list of markers in the loaded map
* `~image` (*sensor_msgs/Image*) planarized map image
* `~visualization` (*visualization_msgs/MarkerArray*) markers map visualization for rviz
* `~debug` (*sensor_msgs/Image*) debug image with detected markers and map axis

View File

@@ -10,6 +10,8 @@ gen = ParameterGenerator()
gen.add("enabled", bool_t, 0, "if detection enabled", True)
gen.add("length", double_t, 0, "markers' side length", min=0, max=10)
gen.add("adaptiveThreshConstant", double_t, 0,
"Constant for adaptive thresholding before finding contours",
p.adaptiveThreshConstant, 0, 100)

14
aruco_pose/cfg/Map.cfg Normal file
View File

@@ -0,0 +1,14 @@
#!/usr/bin/env python
PACKAGE = "aruco_pose"
from dynamic_reconfigure.parameter_generator_catkin import *
gen = ParameterGenerator()
gen.add("enabled", bool_t, 0, "if map detection enabled", True)
gen.add("map", str_t, 0, "full path for the map file")
gen.add("image_axis", bool_t, 0, "debug image axis", default=True)
exit(gen.generate(PACKAGE, "aruco_pose", "Map"))

View File

@@ -1,3 +1,4 @@
# id length x y z rot_z rot_y rot_x
0 0.33 0.0 9.0 0 0 0 0
1 0.33 1.0 9.0 0 0 0 0
2 0.33 2.0 9.0 0 0 0 0

View File

@@ -1,3 +1,4 @@
# id length x y z rot_z rot_y rot_x
107 0.33 0 0 0 0 0 0
106 0.33 0.77 0 0 0 0 0
105 0.33 0 0.77 0 0 0 0

View File

@@ -1,3 +1,4 @@
# id length x y z rot_z rot_y rot_x
14 0.365 0.000 0.0 0 0 0 0
15 0.365 1.335 0.0 0 0 0 0
30 0.365 2.865 0.0 0 0 0 0

View File

@@ -1,7 +1,7 @@
<?xml version="1.0"?>
<package format="2">
<name>aruco_pose</name>
<version>0.21.1</version>
<version>0.23.0</version>
<description>Positioning with ArUco markers</description>
<maintainer email="okalachev@gmail.com">Oleg Kalachev</maintainer>

View File

@@ -48,6 +48,7 @@
#include <aruco_pose/Marker.h>
#include <aruco_pose/MarkerArray.h>
#include <aruco_pose/DetectorConfig.h>
#include <aruco_pose/SetMarkers.h>
#include "utils.h"
#include <memory>
@@ -69,8 +70,11 @@ private:
image_transport::CameraSubscriber img_sub_;
ros::Publisher markers_pub_, vis_markers_pub_;
ros::Subscriber map_markers_sub_;
bool estimate_poses_, send_tf_, auto_flip_;
ros::ServiceServer set_markers_srv_;
bool estimate_poses_, send_tf_, auto_flip_, use_map_markers_;
bool waiting_for_map_;
double length_;
ros::Duration transform_timeout_;
std::unordered_map<int, double> length_override_;
std::string frame_id_prefix_, known_tilt_;
Mat camera_matrix_, dist_coeffs_;
@@ -92,11 +96,14 @@ public:
dictionary = nh_priv_.param("dictionary", 2);
estimate_poses_ = nh_priv_.param("estimate_poses", true);
send_tf_ = nh_priv_.param("send_tf", true);
use_map_markers_ = nh_priv_.param("use_map_markers", false);
waiting_for_map_ = use_map_markers_;
if (estimate_poses_ && !nh_priv_.getParam("length", length_)) {
NODELET_FATAL("can't estimate marker's poses as ~length parameter is not defined");
ros::shutdown();
}
readLengthOverride(nh_priv_);
transform_timeout_ = ros::Duration(nh_priv_.param("transform_timeout", 0.02));
known_tilt_ = nh_priv_.param<std::string>("known_tilt", "");
auto_flip_ = nh_priv_.param("auto_flip", false);
@@ -114,6 +121,8 @@ public:
dyn_srv_ = std::make_shared<dynamic_reconfigure::Server<aruco_pose::DetectorConfig>>(nh_priv_);
dyn_srv_->setCallback(std::bind(&ArucoDetect::paramCallback, this, std::placeholders::_1, std::placeholders::_2));
set_markers_srv_ = nh_priv_.advertiseService("set_length_override", &ArucoDetect::setMarkers, this);
debug_pub_ = it_priv.advertise("debug", 1);
markers_pub_ = nh_priv_.advertise<aruco_pose::MarkerArray>("markers", 1);
vis_markers_pub_ = nh_priv_.advertise<visualization_msgs::MarkerArray>("visualization", 1);
@@ -127,6 +136,7 @@ private:
void imageCallback(const sensor_msgs::ImageConstPtr& msg, const sensor_msgs::CameraInfoConstPtr &cinfo)
{
if (!enabled_) return;
if (waiting_for_map_) return;
Mat image = cv_bridge::toCvShare(msg, "bgr8")->image;
@@ -172,7 +182,7 @@ private:
if (!known_tilt_.empty()) {
try {
snap_to = tf_buffer_->lookupTransform(msg->header.frame_id, known_tilt_,
msg->header.stamp, ros::Duration(0.02));
msg->header.stamp, transform_timeout_);
} catch (const tf2::TransformException& e) {
NODELET_WARN_THROTTLE(5, "can't snap: %s", e.what());
}
@@ -181,6 +191,8 @@ private:
array_.markers.reserve(ids.size());
aruco_pose::Marker marker;
vector<geometry_msgs::TransformStamped> transforms;
transforms.reserve(ids.size());
geometry_msgs::TransformStamped transform;
transform.header.stamp = msg->header.stamp;
transform.header.frame_id = msg->header.frame_id;
@@ -198,20 +210,33 @@ private:
snapOrientation(marker.pose.orientation, snap_to.transform.rotation, auto_flip_);
}
// TODO: check IDs are unique
if (send_tf_) {
transform.child_frame_id = getChildFrameId(ids[i]);
// check if such static transform is in the map
if (map_markers_ids_.find(ids[i]) == map_markers_ids_.end()) {
transform.transform.rotation = marker.pose.orientation;
fillTranslation(transform.transform.translation, tvecs[i]);
br_->sendTransform(transform);
// check if a markers with that id is already added
bool send = true;
for (auto &t : transforms) {
if (t.child_frame_id == transform.child_frame_id) {
send = false;
break;
}
}
if (send) {
transform.transform.rotation = marker.pose.orientation;
fillTranslation(transform.transform.translation, tvecs[i]);
transforms.push_back(transform);
}
}
}
}
array_.markers.push_back(marker);
}
if (send_tf_) {
br_->sendTransform(transforms);
}
}
markers_pub_.publish(array_);
@@ -346,17 +371,47 @@ private:
}
}
bool setMarkers(aruco_pose::SetMarkers::Request& req, aruco_pose::SetMarkers::Response& res)
{
for (auto const& marker : req.markers) {
if (marker.id > 999) {
res.message = "Invalid marker id: " + std::to_string(marker.id);
ROS_ERROR("%s", res.message.c_str());
return true;
}
if (!std::isfinite(marker.length) || marker.length <= 0) {
res.message = "Invalid marker " + std::to_string(marker.id) + " length: " + std::to_string(marker.length);
ROS_ERROR("%s", res.message.c_str());
return true;
}
}
for (auto const& marker : req.markers) {
length_override_[marker.id] = marker.length;
}
res.success = true;
return true;
}
void mapMarkersCallback(const aruco_pose::MarkerArray& msg)
{
map_markers_ids_.clear();
for (auto const& marker : msg.markers) {
map_markers_ids_.insert(marker.id);
if (use_map_markers_) {
if (length_override_.find(marker.id) == length_override_.end()) {
length_override_[marker.id] = marker.length;
}
}
}
waiting_for_map_ = false;
}
void paramCallback(aruco_pose::DetectorConfig &config, uint32_t level)
{
enabled_ = config.enabled;
enabled_ = config.enabled && config.length > 0;
length_ = config.length;
parameters_->adaptiveThreshConstant = config.adaptiveThreshConstant;
parameters_->adaptiveThreshWinSizeMin = config.adaptiveThreshWinSizeMin;
parameters_->adaptiveThreshWinSizeMax = config.adaptiveThreshWinSizeMax;

View File

@@ -19,11 +19,13 @@
#include <vector>
#include <fstream>
#include <algorithm>
#include <memory>
#include <ros/ros.h>
#include <nodelet/nodelet.h>
#include <pluginlib/class_list_macros.h>
#include <image_transport/image_transport.h>
#include <cv_bridge/cv_bridge.h>
#include <dynamic_reconfigure/server.h>
#include <tf/transform_datatypes.h>
#include <tf2_ros/buffer.h>
#include <tf2_ros/transform_listener.h>
@@ -41,6 +43,7 @@
#include <aruco_pose/MarkerArray.h>
#include <aruco_pose/Marker.h>
#include <aruco_pose/MapConfig.h>
#include <opencv2/opencv.hpp>
#include <opencv2/aruco.hpp>
@@ -74,6 +77,9 @@ private:
tf2_ros::StaticTransformBroadcaster static_br_;
tf2_ros::Buffer tf_buffer_;
tf2_ros::TransformListener tf_listener_{tf_buffer_};
std::shared_ptr<dynamic_reconfigure::Server<aruco_pose::MapConfig>> dyn_srv_;
bool enabled_ = true;
std::string type_;
visualization_msgs::MarkerArray vis_array_;
std::string known_tilt_, map_, markers_frame_, markers_parent_frame_;
int image_width_, image_height_, image_margin_;
@@ -89,15 +95,14 @@ public:
// TODO: why image_transport doesn't work here?
img_pub_ = nh_priv_.advertise<sensor_msgs::Image>("image", 1, true);
markers_pub_ = nh_priv_.advertise<aruco_pose::MarkerArray>("markers", 1, true);
markers_pub_ = nh_priv_.advertise<aruco_pose::MarkerArray>("map", 1, true);
board_ = cv::makePtr<cv::aruco::Board>();
board_->dictionary = cv::aruco::getPredefinedDictionary(
static_cast<cv::aruco::PREDEFINED_DICTIONARY_NAME>(nh_priv_.param("dictionary", 2)));
camera_matrix_ = cv::Mat::zeros(3, 3, CV_64F);
std::string type, map;
type = nh_priv_.param<std::string>("type", "map");
type_ = nh_priv_.param<std::string>("type", "map");
transform_.child_frame_id = nh_priv_.param<std::string>("frame_id", "aruco_map");
known_tilt_ = nh_priv_.param<std::string>("known_tilt", "");
auto_flip_ = nh_priv_.param("auto_flip", false);
@@ -110,13 +115,13 @@ public:
// createStripLine();
if (type == "map") {
param(nh_priv_, "map", map);
loadMap(map);
} else if (type == "gridboard") {
if (type_ == "map") {
map_ = nh_priv_.param<std::string>("map" , "");
loadMap(map_);
} else if (type_ == "gridboard") {
createGridBoard(nh_priv_);
} else {
NODELET_FATAL("unknown type: %s", type.c_str());
NODELET_FATAL("unknown type: %s", type_.c_str());
ros::shutdown();
}
@@ -124,10 +129,7 @@ public:
vis_markers_pub_ = nh_priv_.advertise<visualization_msgs::MarkerArray>("visualization", 1, true);
debug_pub_ = it_priv.advertise("debug", 1);
publishMarkersFrames();
publishMarkers();
publishMapImage();
vis_markers_pub_.publish(vis_array_);
publishMap();
image_sub_.subscribe(nh_, "image_raw", 1);
info_sub_.subscribe(nh_, "camera_info", 1);
@@ -136,6 +138,12 @@ 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));
dyn_srv_ = std::make_shared<dynamic_reconfigure::Server<aruco_pose::MapConfig>>(nh_priv_);
dynamic_reconfigure::Server<aruco_pose::MapConfig>::CallbackType cb;
cb = std::bind(&ArucoMap::paramCallback, this, std::placeholders::_1, std::placeholders::_2);
dyn_srv_->setCallback(cb);
NODELET_INFO("ready");
}
@@ -143,6 +151,9 @@ public:
const sensor_msgs::CameraInfoConstPtr& cinfo,
const aruco_pose::MarkerArrayConstPtr& markers)
{
if (!enabled_) return;
if (markers->markers.empty()) return; // map not loaded
int valid = 0;
int count = markers->markers.size();
std::vector<int> ids;
@@ -268,9 +279,17 @@ publish_debug:
std::ifstream f(filename);
std::string line;
clearMarkers();
if (map_ == "") {
NODELET_INFO("No map loaded");
return;
}
if (!f.good()) {
NODELET_FATAL("%s - %s", strerror(errno), filename.c_str());
ros::shutdown();
NODELET_ERROR("%s - %s", strerror(errno), filename.c_str());
map_ = "";
return;
}
while (std::getline(f, line)) {
@@ -296,9 +315,10 @@ publish_debug:
s.putback(first);
} else {
// Probably garbage data; inform user and throw an exception, possibly killing nodelet
NODELET_FATAL("Malformed input: %s", line.c_str());
ros::shutdown();
throw std::runtime_error("Malformed input");
NODELET_ERROR("Malformed input: %s", line.c_str());
map_ = "";
clearMarkers();
return;
}
if (!(s >> id >> length >> x >> y)) {
@@ -329,6 +349,14 @@ publish_debug:
NODELET_INFO("loading %s complete (%d markers)", filename.c_str(), static_cast<int>(board_->ids.size()));
}
void publishMap()
{
publishMarkersFrames();
publishMarkers();
publishMapImage();
vis_markers_pub_.publish(vis_array_);
}
void createGridBoard(ros::NodeHandle& nh)
{
NODELET_INFO("generate gridboard");
@@ -370,6 +398,15 @@ publish_debug:
}
}
void clearMarkers()
{
board_->ids.clear();
board_->objPoints.clear();
markers_.markers.clear();
vis_array_.markers.clear();
markers_transforms_.clear();
}
// void createStripLine()
// {
// visualization_msgs::Marker marker;
@@ -466,7 +503,7 @@ publish_debug:
vis_marker.pose.position.x = x;
vis_marker.pose.position.y = y;
vis_marker.pose.position.z = z;
tf::quaternionTFToMsg(q, marker.pose.orientation);
tf::quaternionTFToMsg(q, vis_marker.pose.orientation);
vis_marker.frame_locked = true;
vis_array_.markers.push_back(vis_marker);
@@ -509,6 +546,22 @@ publish_debug:
msg.image = image;
img_pub_.publish(msg.toImageMsg());
}
void paramCallback(aruco_pose::MapConfig &config, uint32_t level)
{
// https://github.com/CopterExpress/clover/commit/2cd334c474e3ed04ef65ca1ba7f08ab535a3dc6d#diff-942723f9452c398ae93f1a91427f9a7b614be5e5871f8a3e590f324d804f0d58R356
enabled_ = config.enabled;
if (type_ == "map" && config.map != map_) {
map_ = config.map;
loadMap(map_);
publishMap();
}
if (config.image_axis != image_axis_) {
image_axis_ = config.image_axis;
publishMapImage();
}
}
};
PLUGINLIB_EXPORT_CLASS(ArucoMap, nodelet::Nodelet)

View File

@@ -30,7 +30,7 @@ Options:
-o <filename> Output map file name in the 'map' subdirectory of aruco_pose package
Example:
rosrun aruco_pose genmap.py 0.33 2 4 1 1 0 > $(catkin_find aruco_pose map)/test_map.txt
rosrun aruco_pose genmap.py 0.33 2 4 1 1 0 -o test_map.txt
"""
from __future__ import print_function

View File

@@ -0,0 +1,7 @@
# * Add or change markers in the map
# * Change markers' properties, e. g. lengths
Marker[] markers # if length or pose is nan - remove from map
---
bool success
string message

View File

@@ -6,7 +6,7 @@ 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
from visualization_msgs.msg import MarkerArray as VisMarkerArray, Marker as VisMarker
@pytest.fixture
@@ -143,7 +143,7 @@ def test_map_image(node):
assert img.encoding in ('mono8', 'rgb8')
def test_map_markers(node):
markers = rospy.wait_for_message('aruco_map/markers', MarkerArray, timeout=5)
markers = rospy.wait_for_message('aruco_map/map', MarkerArray, timeout=5)
assert markers.markers[0].id == 1
assert markers.markers[1].id == 2
assert markers.markers[2].id == 3
@@ -199,6 +199,36 @@ def test_map_markers(node):
def test_map_visualization(node):
vis = rospy.wait_for_message('aruco_map/visualization', VisMarkerArray, timeout=5)
assert len(vis.markers) == 7
assert vis.markers[0].header.frame_id == 'aruco_map'
assert vis.markers[0].type == VisMarker.CUBE
assert vis.markers[0].action == VisMarker.ADD
assert vis.markers[0].pose.position.x == 0
assert vis.markers[0].pose.position.y == 0
assert vis.markers[0].pose.position.z == 0
assert vis.markers[0].pose.orientation.x == 0
assert vis.markers[0].pose.orientation.y == 0
assert vis.markers[0].pose.orientation.z == 0
assert vis.markers[0].pose.orientation.w == 1
assert vis.markers[0].scale.x == approx(0.33)
assert vis.markers[0].scale.y == approx(0.33)
assert vis.markers[0].scale.z == approx(0.001)
assert vis.markers[1].pose.position.x == 1
assert vis.markers[1].pose.position.y == 0
assert vis.markers[1].pose.position.z == 0
assert vis.markers[1].pose.orientation.x == 0
assert vis.markers[1].pose.orientation.y == 0
assert vis.markers[1].pose.orientation.z == 0
assert vis.markers[1].pose.orientation.w == 1
# non-zero yaw marker:
assert vis.markers[4].scale.x == approx(0.5)
assert vis.markers[4].pose.position.x == approx(0.5)
assert vis.markers[4].pose.position.y == 2
assert vis.markers[4].pose.position.z == 0
assert vis.markers[4].pose.orientation.x == 0
assert vis.markers[4].pose.orientation.y == 0
assert vis.markers[4].pose.orientation.z == approx(0.5646424733950354)
assert vis.markers[4].pose.orientation.w == approx(0.8253356149096783)
def test_map_debug(node):
img = rospy.wait_for_message('aruco_map/debug', Image, timeout=5)

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

View File

@@ -0,0 +1,8 @@
import pytest
import subprocess
def test_no_tf_repeated_data():
# `/rosout` acts weirdly inside rostest, so using a subprocess
cmd = """python -c 'import rospy, tf; rospy.init_node("foo"); listener = tf.TransformListener(); rospy.sleep(2)'"""
output = str(subprocess.check_output(cmd, shell=True, stderr=subprocess.STDOUT))
assert 'TF_REPEATED_DATA' not in output, 'TF_REPEATED_DATA was logged on duplicate markers'

View File

@@ -0,0 +1,21 @@
<launch>
<node pkg="image_publisher" type="image_publisher" name="main_camera" args="$(find aruco_pose)/test/duplicate.png">
<param name="frame_id" value="main_camera_optical"/>
<param name="publish_rate" value="10"/>
<param name="camera_info_url" value="file://$(find aruco_pose)/test/camera_info.yaml" />
</node>
<node pkg="nodelet" type="nodelet" name="nodelet_manager" args="manager" required="true"/>
<node pkg="nodelet" clear_params="true" type="nodelet" name="aruco_detect" args="load aruco_pose/aruco_detect nodelet_manager" required="true">
<remap from="image_raw" to="main_camera/image_raw"/>
<remap from="camera_info" to="main_camera/camera_info"/>
<param name="length" value="0.33"/>
<param name="estimate_poses" value="true"/>
<param name="send_tf" value="true"/>
<param name="cornerRefinementMethod" value="1"/>
</node>
<param name="test_module" value="$(find aruco_pose)/test/duplicate.py"/>
<test test-name="aruco_pose_test" pkg="ros_pytest" type="ros_pytest_runner"/>
</launch>

View File

@@ -2,6 +2,7 @@ import rospy
import pytest
from visualization_msgs.msg import MarkerArray as VisMarkerArray
from aruco_pose.msg import MarkerArray
@pytest.fixture
@@ -9,5 +10,5 @@ def node():
return rospy.init_node('aruco_pose_test', anonymous=True)
def test_node_failure(node):
with pytest.raises(rospy.exceptions.ROSException):
rospy.wait_for_message('aruco_map/visualization', VisMarkerArray, timeout=5)
assert rospy.wait_for_message('aruco_map/visualization', VisMarkerArray, timeout=5).markers == []
assert rospy.wait_for_message('aruco_map/map', MarkerArray, timeout=5).markers == []

View File

@@ -10,7 +10,7 @@
"yametrika",
"anchors",
"collapsible-menu",
"validate-links",
"validate-links@https://github.com/okalachev/gitbook-plugin-validate-links.git",
"bulk-redirect@https://github.com/okalachev/gitbook-plugin-bulk-redirect.git",
"sitemap@https://github.com/okalachev/plugin-sitemap.git",
"toolbar@https://github.com/hamishwillee/gitbook-plugin-toolbar.git",

View File

@@ -8,5 +8,9 @@ ExecStart=/bin/bash -c ". /home/pi/catkin_ws/devel/setup.sh; \
ROS_HOSTNAME=`hostname`.local exec stdbuf -o L roslaunch clover clover.launch --wait --screen --skip-log-check \
2> >(tee /tmp/clover.err)"
ExecStartPre=+rm /var/log/clover.log
StandardOutput=file:/var/log/clover.log
StandardError=file:/var/log/clover.log
[Install]
WantedBy=multi-user.target

View File

@@ -0,0 +1,9 @@
#!/usr/bin/env bash
# GitBook CLI is deprecated, its installation is broken.
# This script fixes it until we stop using GitBook.
export NPM_CONFIG_UNSAFE_PERM=1
npm install gitbook-cli -g
gitbook fetch 3.2.3 && npm i npm@3.10.10 --prefix=~/.gitbook/versions/3.2.3/ # fixing https://travis-ci.org/github/CopterExpress/clover/jobs/766541125#L932

View File

@@ -16,7 +16,7 @@
set -e # Exit immidiately on non-zero result
# https://www.raspberrypi.org/software/operating-systems/#raspberry-pi-os-32-bit
SOURCE_IMAGE="https://downloads.raspberrypi.org/raspios_lite_armhf/images/raspios_lite_armhf-2021-03-25/2021-03-04-raspios-buster-armhf-lite.zip"
SOURCE_IMAGE="https://downloads.raspberrypi.org/raspios_lite_armhf/images/raspios_lite_armhf-2021-05-28/2021-05-07-raspios-buster-armhf-lite.zip"
export DEBIAN_FRONTEND=${DEBIAN_FRONTEND:='noninteractive'}
export LANG=${LANG:='C.UTF-8'}
@@ -105,8 +105,6 @@ ${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/butterf
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/monkey.service' '/lib/systemd/system/'
# software install
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} exec ${SCRIPTS_DIR}'/image-software.sh'
# examples
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/examples' '/home/pi/' # TODO: symlink?
# network setup
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} exec ${SCRIPTS_DIR}'/image-network.sh'
# avahi setup
@@ -115,15 +113,11 @@ ${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/avahi-s
# If RPi then use a one thread to build a ROS package on RPi, else use all
[[ $(arch) == 'armv7l' ]] && NUMBER_THREADS=1 || NUMBER_THREADS=$(nproc --all)
# Clover
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/clover.service' '/lib/systemd/system/'
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/roscore.service' '/lib/systemd/system/'
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/noetic-rosdep-clover.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/launch.nanorc' '/usr/share/nano/'
# ${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/kinetic-ros-clover.rosinstall' '/home/pi/ros_catkin_ws/'
# Add PX4 udev rules
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${REPO_DIR}'/clover/config/99-px4fmu.rules' '/lib/udev/rules.d/'
# Add rename script
${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

@@ -13,7 +13,7 @@
# copies or substantial portions of the Software.
#
set -e # Exit immidiately on non-zero result
set -ex # exit on error, echo commands
REPO=$1
REF=$2
@@ -90,7 +90,7 @@ echo_stamp "Installing OpenCV 4.2-compatible ROS packages"
apt install -y --no-install-recommends \
ros-${ROS_DISTRO}-compressed-image-transport=1.14.0-0buster \
ros-${ROS_DISTRO}-cv-bridge=1.15.0-0buster \
ros-${ROS_DISTRO}-cv-camera=0.5.0-0buster \
ros-${ROS_DISTRO}-cv-camera=0.5.1-0buster \
ros-${ROS_DISTRO}-image-publisher=1.15.3-0buster \
ros-${ROS_DISTRO}-web-video-server=0.2.1-0buster
apt-mark hold \
@@ -112,7 +112,8 @@ my_travis_retry pip3 install wheel
my_travis_retry pip3 install -r /home/pi/catkin_ws/src/clover/clover/requirements.txt
source /opt/ros/${ROS_DISTRO}/setup.bash
# Don't build simulation plugins for actual drone
catkin_make -j2 -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCATKIN_BLACKLIST_PACKAGES=clover_gazebo_plugins
sudo -E -u pi sh -c '. /opt/ros/${ROS_DISTRO}/setup.sh && catkin_make -j2 -DCMAKE_BUILD_TYPE=RelWithDebInfo'
source devel/setup.bash
echo_stamp "Install clever package (for backwards compatibility)"
cd /home/pi/catkin_ws/src/clover/builder/assets/clever
@@ -121,9 +122,8 @@ rm -rf build # remove build artifacts
echo_stamp "Build Clover documentation"
cd /home/pi/catkin_ws/src/clover
NPM_CONFIG_UNSAFE_PERM=true npm install gitbook-cli -g
NPM_CONFIG_UNSAFE_PERM=true gitbook fetch 3.2.3 && npm i npm@3.10.10 --prefix=~/.gitbook/versions/3.2.3/ # fixing https://travis-ci.org/github/CopterExpress/clover/jobs/766541125#L932
NPM_CONFIG_UNSAFE_PERM=true gitbook install
builder/assets/install_gitbook.sh
gitbook install
gitbook build
touch node_modules/CATKIN_IGNORE docs/CATKIN_IGNORE _book/CATKIN_IGNORE clover/www/CATKIN_IGNORE apps/CATKIN_IGNORE # ignore documentation files by catkin
@@ -151,9 +151,20 @@ catkin_make run_tests #&& catkin_test_results
echo_stamp "Change permissions for catkin_ws"
chown -Rf pi:pi /home/pi/catkin_ws
echo_stamp "Change permissions for examples"
echo_stamp "Make \$HOME/examples symlink"
ln -s "$(catkin_find clover examples --first-only)" /home/pi
chown -Rf pi:pi /home/pi/examples
echo_stamp "Make systemd services symlinks"
ln -s /home/pi/catkin_ws/src/clover/builder/assets/clover.service /lib/systemd/system/
ln -s /home/pi/catkin_ws/src/clover/builder/assets/roscore.service /lib/systemd/system/
# validate
[ -f /lib/systemd/system/clover.service ]
[ -f /lib/systemd/system/roscore.service ]
echo_stamp "Make udev rules symlink"
ln -s "$(catkin_find clover udev --first-only)"/* /lib/udev/rules.d/
echo_stamp "Setup ROS environment"
cat << EOF >> /home/pi/.bashrc
LANG='C.UTF-8'

View File

@@ -137,6 +137,8 @@ pip3 --version
echo_stamp "Install and enable Butterfly (web terminal)"
echo_stamp "Workaround for tornado >= 6.0 breaking butterfly"
export CRYPTOGRAPHY_DONT_BUILD_RUST=1
my_travis_retry pip3 install cryptography==3.4.6 # https://stackoverflow.com/a/68472128/6850197
my_travis_retry pip3 install pyOpenSSL==20.0.1
my_travis_retry pip3 install tornado==5.1.1
my_travis_retry pip3 install butterfly
my_travis_retry pip3 install butterfly[systemd]
@@ -153,7 +155,7 @@ systemctl enable monkey.service
echo_stamp "Install Node.js"
cd /home/pi
wget https://nodejs.org/dist/v10.15.0/node-v10.15.0-linux-armv6l.tar.gz
wget --no-verbose https://nodejs.org/dist/v10.15.0/node-v10.15.0-linux-armv6l.tar.gz
tar -xzf node-v10.15.0-linux-armv6l.tar.gz
cp -R node-v10.15.0-linux-armv6l/* /usr/local/
rm -rf node-v10.15.0-linux-armv6l/

View File

@@ -20,12 +20,20 @@ export ROS_DISTRO='noetic'
export ROS_IP='127.0.0.1'
source /opt/ros/${ROS_DISTRO}/setup.bash
source /home/pi/catkin_ws/devel/setup.bash
systemctl start roscore
cd /home/pi/catkin_ws/src/clover/builder/test/
./tests.sh
./tests.py
./tests_py3.py
[[ $(./test_qr.py) == "Found QRCODE with data Проверка Unicode with center at x=66.0, y=66.0" ]]
[[ $(./tests_clever.py) == "Warning: clever package is renamed to clover" ]] # test backwards compatibility
systemctl stop roscore
# check documented packages available
apt-cache show gst-rtsp-launch
apt-cache show openvpn
echo "Move /etc/ld.so.preload back to its original position"
mv /etc/ld.so.preload.disabled-for-build /etc/ld.so.preload

View File

@@ -3,10 +3,17 @@
# Perform a "standalone install" in a Docker container
set -e
# Step 1: Install pip
apt update
apt install -y curl
curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py
python3 ./get-pip.py
apt-key adv --keyserver 'hkp://keyserver.ubuntu.com:80' --recv-key C1CF6E31E6BADE8868B172B4F42ED6FBAB17C654 # https://github.com/osrf/docker_images/issues/535
apt-get update
apt-get install -y curl
if [ "x${ROS_PYTHON_VERSION}" = "x3" ]; then
PYTHON=python3
curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py
else
PYTHON=python
curl https://bootstrap.pypa.io/pip/2.7/get-pip.py -o get-pip.py
fi
${PYTHON} ./get-pip.py
# Step 1.5: Add deb.coex.tech to apt
curl http://deb.coex.tech/aptly_repo_signing.key 2> /dev/null | apt-key add -
@@ -34,7 +41,7 @@ ws281x:
ubuntu:
${CODENAME}: [ros-${ROS_DISTRO}-ws281x]
EOF
apt update
apt-get update
rosdep update
# Step 2: Run rosdep to install all dependencies
@@ -50,7 +57,7 @@ cd /root/catkin_ws
catkin_make
# Step 4: Run tests
python3 -m pip install --upgrade pytest # TODO: https://github.com/CopterExpress/clover/commit/5b970d51970cfa6f46e5c0b34acb7889d36b89c8
${PYTHON} -m pip install --upgrade pytest
cd /root/catkin_ws
source devel/setup.bash
catkin_make run_tests && catkin_test_results

BIN
builder/test/qr.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

42
builder/test/test_qr.py Executable file
View File

@@ -0,0 +1,42 @@
#!/usr/bin/env python3
# Test QG recognition example
# Should be synced with the documentation: /docs/en/camera.md, /docs/ru/camera.md
# TODO: use real ROS topics
import rospy
from pyzbar import pyzbar
from cv_bridge import CvBridge
from sensor_msgs.msg import Image
bridge = CvBridge()
# rospy.init_node('barcode_test')
# Image subscriber callback function
def image_callback(data):
cv_image = bridge.imgmsg_to_cv2(data, 'bgr8') # OpenCV image
barcodes = pyzbar.decode(cv_image)
for barcode in barcodes:
b_data = barcode.data.decode("utf-8")
b_type = barcode.type
(x, y, w, h) = barcode.rect
xc = x + w/2
yc = y + h/2
print("Found {} with data {} with center at x={}, y={}".format(b_type, b_data, xc, yc))
# rospy.signal_shutdown('done')
# image_sub = rospy.Subscriber('main_camera/image_raw', Image, image_callback, queue_size=1)
# ==============================================================================
# Publish test image
# rospy.sleep(2)
import cv2
img = cv2.imread('qr.png')
image_callback(bridge.cv2_to_imgmsg(img, 'bgr8'))
# image_pub = rospy.Publisher('/main_camera/image_raw', Image, queue_size=1, latch=True)
# image_pub.publish(bridge.cv2_to_imgmsg(img, 'bgr8'))
# rospy.spin()

View File

@@ -2,20 +2,30 @@
# validate all required modules installed
import os
import rospy
from geometry_msgs.msg import PoseStamped
from sensor_msgs.msg import Range, BatteryState
import cv2
import cv2.aruco
from sensor_msgs.msg import Image
from cv_bridge import CvBridge
import numpy
import mavros
from mavros_msgs.msg import State, StatusText, ExtendedState
from mavros_msgs.msg import State, StatusText, ExtendedState, RCIn, Mavlink
from mavros_msgs.srv import CommandBool, CommandLong, SetMode
from std_srvs.srv import Trigger
from clover.srv import GetTelemetry, Navigate, NavigateGlobal, SetPosition, SetVelocity, \
SetAttitude, SetRates, SetLEDEffect
from led_msgs.srv import SetLEDs
from led_msgs.msg import LEDStateArray, LEDState
from aruco_pose.msg import Marker, MarkerArray, Point2D
from clover import long_callback
import dynamic_reconfigure.client
import tf2_ros
import tf2_geometry_msgs
@@ -23,9 +33,11 @@ import tf2_geometry_msgs
import VL53L1X
import pymavlink
from pymavlink import mavutil
import rpi_ws281x
import pigpio
# from espeak import espeak
from pyzbar import pyzbar
print(cv2.getBuildInformation())
if not os.environ.get('VM'):
import rpi_ws281x
import pigpio

View File

@@ -6,16 +6,10 @@ set -ex
# validate required software is installed
python --version
python2 --version
python3 --version
ipython --version
ipython3 --version
# ptvsd does not have a stand-alone binary
python -m ptvsd --version
python3 -m ptvsd --version
node -v
npm -v
@@ -25,37 +19,64 @@ lsof -v
git --version
vim --version
pip --version
pip2 --version
pip3 --version
tcpdump --version
monkey --version
pigpiod -v
i2cdetect -V
butterfly -h
# espeak --version
mjpg_streamer --version
systemctl --version
if [ -z $VM ]; then
# rpi only software
python --version
ipython --version
pip2 --version
# `python` is python2 for now
[[ $(python -c 'import sys;print(sys.version_info.major)') == "2" ]]
# ptvsd does not have a stand-alone binary
python -m ptvsd --version
python3 -m ptvsd --version
pigpiod -v
i2cdetect -V
butterfly -h
mjpg_streamer --version
fi
# ros stuff
roscore -h
rosversion clover
rosversion aruco_pose
rosversion vl53l1x
rosversion mavros
rosversion mavros_extras
rosversion ws281x
rosversion led_msgs
rosversion dynamic_reconfigure
rosversion tf2_web_republisher
rosversion compressed_image_transport
rosversion rosbridge_suite
rosversion rosserial
rosversion rosbridge_server
rosversion usb_cam
rosversion cv_camera
rosversion web_video_server
rosversion rosshow
rosversion nodelet
rosversion image_view
[[ $(rosversion ws281x) == "0.0.13" ]]
if [ -z $VM ]; then
rosversion compressed_image_transport
rosversion rosshow
rosversion vl53l1x
rosversion rosserial
[[ $(rosversion cv_camera) == "0.5.1" ]] # patched version with init fix
fi
[ $VM ] && H="/home/clover" || H="/home/pi"
# validate examples are present
[[ $(ls /home/pi/examples/*) ]]
[[ $(ls $H/examples/*) ]]
# validate web tools present
[ -d $H/.ros/www ]
[ "$(readlink $H/.ros/www/clover)" = "$H/catkin_ws/src/clover/clover/www" ]
[ "$(readlink $H/.ros/www/clover_blocks)" = "$H/catkin_ws/src/clover/clover_blocks/www" ]

View File

@@ -5,7 +5,7 @@ import sys
import subprocess
EXCLUDE = ('clever4-front-white.png', 'clever4-front-white-large.png', '.DS_Store',
'clever4-front-black-large.png','clover42-black.png')
'clever4-front-black-large.png','clover42-black.png', 'clover42-main-margin.png')
code = 0

View File

@@ -17,6 +17,7 @@ find_package(catkin REQUIRED COMPONENTS
message_generation
geometry_msgs
sensor_msgs
led_msgs
geographic_msgs
tf
tf2
@@ -24,6 +25,7 @@ find_package(catkin REQUIRED COMPONENTS
tf2_ros
image_transport
cv_bridge
dynamic_reconfigure
)
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake")
@@ -51,7 +53,7 @@ find_package(OpenCV ${_opencv_version} REQUIRED
## Uncomment this if the package has a setup.py. This macro ensures
## modules and global scripts declared therein get installed
## See http://ros.org/doc/api/catkin/html/user_guide/setup_dot_py.html
# catkin_python_setup()
catkin_python_setup()
################################################
## Declare ROS messages, services and actions ##
@@ -126,10 +128,9 @@ generate_messages(
## and list every .cfg file to be processed
## Generate dynamic reconfigure parameters in the 'cfg' folder
# generate_dynamic_reconfigure_options(
# cfg/DynReconf1.cfg
# cfg/DynReconf2.cfg
# )
generate_dynamic_reconfigure_options(
cfg/Flow.cfg
)
###################################
## catkin specific configuration ##
@@ -211,6 +212,8 @@ add_dependencies(clover_led ${PROJECT_NAME}_generate_messages_cpp)
add_dependencies(shell ${PROJECT_NAME}_generate_messages_cpp)
add_dependencies(${PROJECT_NAME} ${PROJECT_NAME}_gencfg)
## Rename C++ executable without prefix
## The above recommended prefix causes long target names, the following renames the
## target back to the shorter version for ease of user use
@@ -227,6 +230,9 @@ target_link_libraries(${PROJECT_NAME}
${OpenCV_LIBRARIES}
)
# Set Clover to default www page
set(ROSWWW_STATIC_DEFAULT ${PROJECT_NAME})
#############
## Install ##
#############
@@ -241,12 +247,12 @@ target_link_libraries(${PROJECT_NAME}
# DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
# )
## Mark executables and/or libraries for installation
# install(TARGETS ${PROJECT_NAME} ${PROJECT_NAME}_node
# ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
# LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
# RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
# )
# Mark executables and/or libraries for installation
install(TARGETS simple_offboard clover_rc camera_markers vpe_publisher clover_led shell clover
ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
)
## Mark cpp header files for installation
# install(DIRECTORY include/${PROJECT_NAME}/
@@ -266,13 +272,21 @@ catkin_install_python(PROGRAMS src/selfcheck.py
DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
)
install(DIRECTORY launch DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION})
install(DIRECTORY examples DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION})
install(DIRECTORY www DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION})
# TODO: install www
# 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
udev/99-px4fmu.rules
DESTINATION /lib/udev/rules.d
)
else()

View File

@@ -36,7 +36,7 @@ curl https://raw.githubusercontent.com/mavlink/mavros/master/mavros/scripts/inst
You may optionally install udev rules to provide `/dev/px4fmu` symlink to your PX4-based flight controller connected over USB. Copy `99-px4fmu.rules` to your `/lib/udev/rules.d` folder:
```bash
cd ~/catkin_ws/src/clover/clover/config
cd ~/catkin_ws/src/clover/clover/udev
sudo cp 99-px4fmu.rules /lib/udev/rules.d
```

10
clover/cfg/Flow.cfg Normal file
View File

@@ -0,0 +1,10 @@
#!/usr/bin/env python
PACKAGE = "clover"
from dynamic_reconfigure.parameter_generator_catkin import *
gen = ParameterGenerator()
gen.add("enabled", bool_t, 0, "if optical flow enabled", True)
exit(gen.generate(PACKAGE, "clover", "Flow"))

64
clover/examples/camera.py Normal file
View File

@@ -0,0 +1,64 @@
# Information: https://clover.coex.tech/camera
# Example on basic working with the camera and image processing:
# - cuts out a central square from the camera image;
# - publishes this cropped image to the topic `/cv/center`;
# - computes the average color of it;
# - prints its name to the console.
import rospy
import cv2
from sensor_msgs.msg import Image
from cv_bridge import CvBridge
from clover import long_callback
rospy.init_node('cv')
bridge = CvBridge()
printed_color = None
center_pub = rospy.Publisher('~center', Image, queue_size=1)
def get_color_name(h):
if h < 15: return 'red'
if h < 30: return 'orange'
elif h < 60: return 'yellow'
elif h < 90: return 'green'
elif h < 120: return 'cyan'
elif h < 150: return 'blue'
elif h < 170: return 'magenta'
else: return 'red'
@long_callback
def image_callback(msg):
img = bridge.imgmsg_to_cv2(msg, 'bgr8')
# convert to HSV to work with color hue
img_hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
# cut out a central square
w = img.shape[1]
h = img.shape[0]
r = 20
center = img_hsv[h // 2 - r:h // 2 + r, w // 2 - r:w // 2 + r]
# compute and print the average hue
mean_hue = center[:, :, 0].mean()
color = get_color_name(mean_hue)
global printed_color
if color != printed_color:
print(color)
printed_color = color
# publish the cropped image
center = cv2.cvtColor(center, cv2.COLOR_HSV2BGR)
center_pub.publish(bridge.cv2_to_imgmsg(center, 'bgr8'))
# process every frame:
image_sub = rospy.Subscriber('main_camera/image_raw', Image, image_callback, queue_size=1)
# process 5 frames per second:
# image_sub = rospy.Subscriber('main_camera/image_raw_throttled', Image, image_callback, queue_size=1)
rospy.spin()

View File

@@ -15,17 +15,17 @@ set_attitude = rospy.ServiceProxy('set_attitude', srv.SetAttitude)
set_rates = rospy.ServiceProxy('set_rates', srv.SetRates)
land = rospy.ServiceProxy('land', Trigger)
# Take off and hover 1 m above the ground
print('Take off and hover 1 m above the ground')
navigate(x=0, y=0, z=1, frame_id='body', auto_arm=True)
# Wait for 3 seconds
rospy.sleep(3)
# Wait for 5 seconds
rospy.sleep(5)
# Fly forward 1 m
print('Fly forward 1 m')
navigate(x=1, y=0, z=0, frame_id='body')
# Wait for 3 seconds
rospy.sleep(3)
# Wait for 5 seconds
rospy.sleep(5)
# Perform landing
print('Perform landing')
land()

View File

@@ -15,23 +15,23 @@ set_attitude = rospy.ServiceProxy('set_attitude', srv.SetAttitude)
set_rates = rospy.ServiceProxy('set_rates', srv.SetRates)
land = rospy.ServiceProxy('land', Trigger)
# Take off and hover 1 m above the ground
print('Take off and hover 1 m above the ground')
navigate(x=0, y=0, z=1, frame_id='body', auto_arm=True)
# Wait for 3 seconds
rospy.sleep(3)
# Wait for 5 seconds
rospy.sleep(5)
# Fly 1 meter above ArUco marker 0
print('Fly 1 meter above ArUco marker 0')
navigate(x=0, y=0, z=1, frame_id='aruco_0')
# Wait for 3 seconds
rospy.sleep(3)
# Wait for 5 seconds
rospy.sleep(5)
# Fly to x=1 y=1 z=1 relative to ArUco markers map
print('Fly to x=1 y=1 z=1 relative to ArUco markers map')
navigate(x=1, y=1, z=1, frame_id='aruco_map')
# Wait for 3 seconds
rospy.sleep(3)
# Wait for 5 seconds
rospy.sleep(5)
# Perform landing
print('Perform landing')
land()

47
clover/examples/gps.py Normal file
View File

@@ -0,0 +1,47 @@
# Information: https://clover.coex.tech/en/simple_offboard.html#navigateglobal
import rospy
from clover import srv
from std_srvs.srv import Trigger
import math
rospy.init_node('flight')
get_telemetry = rospy.ServiceProxy('get_telemetry', srv.GetTelemetry)
navigate = rospy.ServiceProxy('navigate', srv.Navigate)
navigate_global = rospy.ServiceProxy('navigate_global', srv.NavigateGlobal)
set_position = rospy.ServiceProxy('set_position', srv.SetPosition)
set_velocity = rospy.ServiceProxy('set_velocity', srv.SetVelocity)
set_attitude = rospy.ServiceProxy('set_attitude', srv.SetAttitude)
set_rates = rospy.ServiceProxy('set_rates', srv.SetRates)
land = rospy.ServiceProxy('land', Trigger)
# https://clover.coex.tech/en/snippets.html#wait_arrival
def wait_arrival(tolerance=0.2):
while not rospy.is_shutdown():
telem = get_telemetry(frame_id='navigate_target')
if math.sqrt(telem.x ** 2 + telem.y ** 2 + telem.z ** 2) < tolerance:
break
rospy.sleep(0.2)
start = get_telemetry()
if math.isnan(start.lat):
raise Exception('No global position, install and configure GPS sensor: https://clover.coex.tech/gps')
print('Start point global position: lat={}, lon={}'.format(start.lat, start.lon))
print('Take off 3 meters')
navigate(x=0, y=0, z=3, frame_id='body', auto_arm=True)
wait_arrival()
print('Fly 1 arcsecond to the North (approx. 30 meters)')
navigate_global(lat=start.lat+1.0/60/60, lon=start.lon, z=start.z+3, yaw=math.inf, speed=5)
wait_arrival()
print('Fly to home position')
navigate_global(lat=start.lat, lon=start.lon, z=start.z+3, yaw=math.inf, speed=5)
wait_arrival()
print('Land')
land()

View File

@@ -31,11 +31,11 @@ def navigate_wait(x=0, y=0, z=0, yaw=float('nan'), yaw_rate=0, speed=0.5, \
return res
rospy.sleep(0.2)
# Take off 1 meter
print('Take off 1 meter')
navigate_wait(z=1, frame_id='body', auto_arm=True)
# Fly forward 1 m
print('Fly forward 1 m')
navigate_wait(x=1, frame_id='body')
# Land
print('Land')
land()

View File

@@ -0,0 +1,15 @@
# Information: https://clover.coex.tech/en/laser.html
import rospy
from sensor_msgs.msg import Range
rospy.init_node('process_rangefinder')
def range_callback(msg):
# Process data from the rangefinder
print('Rangefinder distance:', msg.range)
# Subscribe to laser rangefinder data
rospy.Subscriber('rangefinder/range', Range, range_callback)
rospy.spin()

View File

@@ -3,21 +3,26 @@
<arg name="aruco_map" default="false"/>
<arg name="aruco_vpe" default="false"/>
<arg name="placement" default="floor"/> <!-- markers placement: floor, ceiling, unknown -->
<arg name="length" default="0.33"/> <!-- not-in-map markers length, m -->
<arg name="length" default="0.22"/> <!-- not-in-map markers length, m -->
<arg name="map" default="map.txt"/> <!-- markers map file name -->
<!-- For additional help go to https://clover.coex.tech/aruco -->
<arg name="force_init" default="false"/>
<arg name="disable" default="false"/> <!-- only force init -->
<!-- aruco_detect: detect aruco markers, estimate poses -->
<node name="aruco_detect" pkg="nodelet" if="$(arg aruco_detect)" type="nodelet" args="load aruco_pose/aruco_detect nodelet_manager" output="screen" clear_params="true" respawn="true">
<node name="aruco_detect" pkg="nodelet" if="$(eval aruco_detect and not disable)" type="nodelet" args="load aruco_pose/aruco_detect main_camera_nodelet_manager" output="screen" clear_params="true" respawn="true">
<remap from="image_raw" to="main_camera/image_raw"/>
<remap from="camera_info" to="main_camera/camera_info"/>
<remap from="map_markers" to="aruco_map/markers" if="$(arg aruco_map)"/>
<remap from="map_markers" to="aruco_map/map"/>
<param name="estimate_poses" value="true"/>
<param name="send_tf" value="true"/>
<param name="use_map_markers" value="true"/>
<param name="known_tilt" value="map" if="$(eval placement == 'floor')"/>
<param name="known_tilt" value="map_flipped" if="$(eval placement == 'ceiling')"/>
<param name="length" value="$(arg length)"/>
<param name="transform_timeout" value="0.1"/>
<!-- aruco detector parameters -->
<param name="cornerRefinementMethod" value="2"/> <!-- contour refinement -->
<param name="minMarkerPerimeterRate" value="0.075"/> <!-- 0.075 for 320x240, 0.0375 for 640x480 -->
@@ -26,7 +31,7 @@
</node>
<!-- aruco_map: estimate aruco map pose -->
<node name="aruco_map" pkg="nodelet" type="nodelet" if="$(arg aruco_map)" args="load aruco_pose/aruco_map nodelet_manager" output="screen" clear_params="true" respawn="true">
<node name="aruco_map" pkg="nodelet" type="nodelet" if="$(eval aruco_map and not disable)" args="load aruco_pose/aruco_map main_camera_nodelet_manager" output="screen" clear_params="true" respawn="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"/>
@@ -41,11 +46,15 @@
</node>
<!-- vpe publisher from aruco markers -->
<node name="vpe_publisher" pkg="clover" type="vpe_publisher" if="$(arg aruco_vpe)" output="screen" clear_params="true">
<remap from="~pose_cov" to="aruco_map/pose"/>
<node name="vpe_publisher" pkg="clover" type="vpe_publisher" if="$(eval aruco_vpe or force_init)" output="screen" clear_params="true">
<remap from="~pose_cov" to="aruco_map/pose" if="$(arg aruco_vpe)"/>
<remap from="~vpe" to="mavros/vision_pose/pose"/>
<param name="frame_id" value="aruco_map_detected"/>
<param name="publish_zero" value="true"/>
<param name="frame_id" value="aruco_map_detected" if="$(arg aruco_vpe)"/>
<param name="force_init" value="$(arg force_init)"/>
<param name="offset_frame_id" value="aruco_map"/>
</node>
<!-- run map_flipped frame if placement is ceiling -->
<node pkg="tf2_ros" type="static_transform_publisher" name="map_flipped_frame"
args="0 0 0 3.1415926 3.1415926 0 map map_flipped" if="$(eval placement == 'ceiling')"/>
</launch>

View File

@@ -12,6 +12,7 @@
<arg name="led" default="true"/>
<arg name="blocks" default="false"/>
<arg name="rc" default="false"/>
<arg name="force_init" default="true"/> <!-- force estimator to init by publishing zero pose -->
<arg name="simulator" default="false"/> <!-- flag that we are operating on a simulated drone -->
@@ -33,28 +34,22 @@
</node>
<!-- aruco markers -->
<include file="$(find clover)/launch/aruco.launch" if="$(arg aruco)"/>
<include file="$(find clover)/launch/aruco.launch" if="$(eval aruco or force_init)">
<arg name="force_init" value="$(arg force_init)"/>
<arg name="disable" value="$(eval not aruco)"/>
</include>
<!-- optical flow -->
<node pkg="nodelet" type="nodelet" name="optical_flow" args="load clover/optical_flow nodelet_manager" if="$(arg optical_flow)" clear_params="true" output="screen" respawn="true">
<node pkg="nodelet" type="nodelet" name="optical_flow" args="load clover/optical_flow main_camera_nodelet_manager" if="$(arg optical_flow)" clear_params="true" output="screen" respawn="true">
<remap from="image_raw" to="main_camera/image_raw"/>
<remap from="camera_info" to="main_camera/camera_info"/>
<param name="calc_flow_gyro" value="true"/>
<param name="roi_rad" value="0.8"/>
<param name="disable_on_vpe" value="false"/>
</node>
<!-- main nodelet manager -->
<node pkg="nodelet" type="nodelet" name="nodelet_manager" args="manager" output="screen" clear_params="true" respawn="true">
<param name="num_worker_threads" value="2"/>
</node>
<node pkg="tf2_ros" type="static_transform_publisher" name="map_flipped_frame" args="0 0 0 3.1415926 3.1415926 0 map map_flipped"/>
<!-- simplified offboard control -->
<node name="simple_offboard" pkg="clover" type="simple_offboard" output="screen" clear_params="true">
<param name="reference_frames/body" value="map"/>
<param name="reference_frames/base_link" value="map"/>
<param name="reference_frames/navigate_target" value="map"/>
<param name="reference_frames/main_camera_optical" value="map"/>
</node>
@@ -89,9 +84,4 @@
<!-- Send fake GCS heartbeats. Set to "true" for upstream PX4 -->
<param name="use_fake_gcs" value="false"/>
</node>
<!-- Update static directory -->
<node pkg="roswww_static" name="roswww_static" type="main.py" clear_params="true">
<param name="default_package" value="clover"/>
</node>
</launch>

View File

@@ -36,7 +36,7 @@
posctl: { r: 50, g: 100, b: 220 }
offboard: { r: 220, g: 20, b: 250 }
low_battery: { threshold: 3.6, effect: blink_fast, r: 255, g: 0, b: 0 }
error: { effect: flash, r: 255, g: 0, b: 0 }
error: { effect: flash, r: 255, g: 0, b: 0, ignore: [ "[lpe] vision position timeout" ]}
</rosparam>
</node>
</launch>

View File

@@ -3,6 +3,9 @@
<arg name="direction_z" default="down"/> <!-- direction the camera points: down, up -->
<arg name="direction_y" default="backward"/> <!-- direction the camera cable points: backward, forward -->
<arg name="device" default="/dev/video0"/> <!-- v4l2 device -->
<arg name="throttled_topic" default="true"/> <!-- enable throttled image topic -->
<arg name="throttled_topic_rate" default="5.0"/> <!-- throttled image topic rate -->
<arg name="simulator" default="false"/>
<node if="$(eval direction_z == 'down' and direction_y == 'backward')" pkg="tf2_ros" type="static_transform_publisher" name="main_camera_frame" args="0.05 0 -0.07 -1.5707963 0 3.1415926 base_link main_camera_optical"/>
@@ -17,9 +20,14 @@
<!-- static_transform_publisher arguments: x y z yaw pitch roll frame_id child_frame_id -->
<!-- <node pkg="tf2_ros" type="static_transform_publisher" name="main_camera_frame" args="0.05 0 -0.07 -1.5707963 0 3.1415926 base_link main_camera_optical"/> -->
<!-- camera nodelet manager -->
<node pkg="nodelet" type="nodelet" name="main_camera_nodelet_manager" args="manager" output="screen" clear_params="true" respawn="true">
<param name="num_worker_threads" value="2"/>
</node>
<!-- camera node -->
<node pkg="nodelet" type="nodelet" name="main_camera" args="load cv_camera/CvCameraNodelet nodelet_manager" clear_params="true" unless="$(arg simulator)" respawn="true">
<param name="device_path" value="/dev/video0"/> <!-- v4l2 device -->
<node pkg="nodelet" type="nodelet" name="main_camera" args="load cv_camera/CvCameraNodelet main_camera_nodelet_manager" launch-prefix="rosrun clover waitfile $(arg device)" clear_params="true" unless="$(arg simulator)" respawn="true">
<param name="device_path" value="$(arg device)"/>
<param name="frame_id" value="main_camera_optical"/>
<param name="camera_info_url" value="file://$(find clover)/camera_info/fisheye_cam.yaml"/>
@@ -37,4 +45,8 @@
<node pkg="clover" type="camera_markers" ns="main_camera" name="main_camera_markers">
<param name="scale" value="3.0"/>
</node>
<!-- image topic throttled -->
<node pkg="topic_tools" name="main_camera_throttle" type="throttle" ns="main_camera"
args="messages image_raw $(arg throttled_topic_rate) image_raw_throttled" if="$(arg throttled_topic)"/>
</launch>

View File

@@ -1,5 +1,5 @@
<launch>
<arg name="fcu_conn" default="usb"/> <!-- options: usb, uart, tcp, udp, sitl -->
<arg name="fcu_conn" default="usb"/> <!-- options: usb, uart, tcp, udp, sitl, hitl -->
<arg name="fcu_ip" default="127.0.0.1"/>
<arg name="fcu_sys_id" default="1"/>
<arg name="gcs_bridge" default="tcp"/>
@@ -23,6 +23,9 @@
<!-- sitl since PX4 1.9.0 -->
<param name="fcu_url" value="udp://@$(arg fcu_ip):14580" if="$(eval fcu_conn == 'sitl')"/>
<!-- hitl connection (to gazebo_mavlink_interface plugin) -->
<param name="fcu_url" value="udp://$(arg fcu_ip):14540@" if="$(eval fcu_conn == 'hitl')"/>
<!-- set target_system_id -->
<param name="target_system_id" value="$(arg fcu_sys_id)" />
@@ -39,7 +42,7 @@
<rosparam command="load" file="$(find clover)/launch/mavros_config.yaml"/>
<!-- remap rangefinder -->
<remap from="mavros/distance_sensor/rangefinder_sub" to="rangefinder/range"/>
<remap from="mavros/distance_sensor/rangefinder_sub" to="$(arg distance_sensor_remap)" if="$(eval bool(distance_sensor_remap))"/>
<rosparam param="plugin_whitelist">
- altitude

View File

@@ -1,11 +1,11 @@
# Config file for mavros
# Based on https://raw.githubusercontent.com/mavlink/mavros/master/mavros/launch/px4_config.yaml
startup_px4_usb_quirk: true
startup_px4_usb_quirk: false
conn:
heartbeat_rate: 1.0 # send hertbeat rate in Hertz
timeout: 10.0 # hertbeat timeout in seconds
heartbeat_rate: 1.0 # send heartbeat rate in Hertz
timeout: 10.0 # heartbeat timeout in seconds
timesync_rate: 10.0 # TIMESYNC rate in Hertz (feature disabled if 0.0)
system_time_rate: 1.0 # send system time to FCU rate in Hertz (disabled if 0.0)
@@ -13,6 +13,7 @@ time:
time_ref_source: "fcu" # time_reference source
timesync_mode: MAVLINK
timesync_avg_alpha: 0.6 # timesync averaging factor
publish_sim_time: false # don't publish /clock
global_position:
frame_id: "map" # origin frame
@@ -77,6 +78,9 @@ distance_sensor:
field_of_view: 0.5
rangefinder_sub:
subscriber: true
id: 1
orientation: PITCH_270
covariance: 1 # cm
# fake_gps
fake_gps:

View File

@@ -0,0 +1,4 @@
<launch>
<!-- shurtcut for running the simulation (`roslaunch clover simulator.launch`) -->
<include file="$(find clover_simulation)/launch/simulator.launch"/>
</launch>

View File

@@ -1,7 +1,7 @@
<?xml version="1.0"?>
<package format="3">
<name>clover</name>
<version>0.21.1</version>
<version>0.23.0</version>
<description>The Clover package</description>
<maintainer email="okalachev@gmail.com">Oleg Kalachev</maintainer>
@@ -37,8 +37,11 @@
<depend>rosbridge_server</depend>
<depend>web_video_server</depend>
<depend>tf2_web_republisher</depend>
<depend>libxml2</depend>
<depend>libxslt</depend>
<depend condition="$ROS_PYTHON_VERSION == 2">python-lxml</depend>
<depend condition="$ROS_PYTHON_VERSION == 3">python3-lxml</depend>
<depend>dynamic_reconfigure</depend>
<exec_depend>python-pymavlink</exec_depend>
<!-- Use test_depend for packages you need only for testing: -->
<!-- <test_depend>gtest</test_depend> -->

11
clover/setup.py Normal file
View File

@@ -0,0 +1,11 @@
## ! DO NOT MANUALLY INVOKE THIS setup.py, USE CATKIN INSTEAD
from distutils.core import setup
from catkin_pkg.python_setup import generate_distutils_setup
# fetch values from package.xml
setup_args = generate_distutils_setup(
packages=['clover'],
package_dir={'': 'src'})
setup(**setup_args)

View File

@@ -0,0 +1,87 @@
#!/usr/bin/env python3
import rospy
import math
import signal
import sys
import dynamic_reconfigure.client
from clover import srv
from std_srvs.srv import Trigger
from sensor_msgs.msg import Range
from aruco_pose.msg import MarkerArray
from util import handle_response
rospy.init_node('autotest_aruco', disable_signals=True) # disable signals to allow interrupting with ctrl+c
flow_client = dynamic_reconfigure.client.Client('optical_flow')
get_telemetry = rospy.ServiceProxy('get_telemetry', srv.GetTelemetry)
navigate = handle_response(rospy.ServiceProxy('navigate', srv.Navigate))
land = handle_response(rospy.ServiceProxy('land', Trigger))
def interrupt(sig, frame):
print('\nInterrupted, landing...')
land()
sys.exit(0)
signal.signal(signal.SIGINT, interrupt)
def print_current_map_position():
telem = get_telemetry()
dist = rospy.wait_for_message('rangefinder/range', Range).range
print('Map position:\tx={:.1f}\ty={:.1f}\tz={:.1f}\tyaw={:.1f}\tdist={:.2f}'.format(telem.x, telem.y, telem.z, telem.yaw, dist))
def navigate_wait(x=0, y=0, z=0, yaw=float('nan'), yaw_rate=0, speed=0.5, \
frame_id='body', tolerance=0.2, auto_arm=False):
res = navigate(x=x, y=y, z=z, yaw=yaw, yaw_rate=yaw_rate, speed=speed, \
frame_id=frame_id, auto_arm=auto_arm)
if not res.success:
return res
while not rospy.is_shutdown():
telem = get_telemetry(frame_id='navigate_target')
if math.sqrt(telem.x ** 2 + telem.y ** 2 + telem.z ** 2) < tolerance:
return res
rospy.sleep(0.2)
markers = rospy.wait_for_message('aruco_map/map', MarkerArray, timeout=3)
left = min(marker.pose.position.x for marker in markers.markers)
bottom = min(marker.pose.position.y for marker in markers.markers)
width = max(marker.pose.position.x for marker in markers.markers)
height = max(marker.pose.position.y for marker in markers.markers)
center_x = left + width / 2
center_y = bottom + height / 2
print('Map rect: %g %g - %g %g' % (left, bottom, width, height))
input('Take off and hover 1 m [enter] ')
navigate_wait(x=0, y=0, z=1, frame_id='body', auto_arm=True)
print_current_map_position()
input('Go to corner %g %g 1.5 speed 1 [enter] ' % (width, height))
navigate_wait(x=width, y=height, z=1.5, speed=1, frame_id='aruco_map')
print_current_map_position()
input('Go to center %g %g 1.5 speed 5 [enter] ' % (center_x, center_y))
navigate_wait(x=center_x, y=center_y, z=1.5, speed=5, frame_id='aruco_map')
print_current_map_position()
input('Disable optical flow and keep hovering [enter] ')
flow_client.update_configuration({'enabled': False})
rospy.sleep(5)
input('Enable optical flow back [enter] ')
flow_client.update_configuration({'enabled': True})
input('Go to side 1 %g 2 heading top [enter] ' % (center_y))
navigate_wait(x=1, y=center_y, z=2, yaw=1.57, frame_id='aruco_map')
print_current_map_position()
marker_id = markers.markers[0].id
input('Go to marker %d z=1.5 [enter] ' % marker_id)
navigate_wait(x=0, y=0, z=1.5, yaw=0, frame_id='aruco_%d' % marker_id)
print_current_map_position()
input('Perform landing [enter] ')
land()

View File

@@ -0,0 +1,100 @@
#!/usr/bin/env python3
import rospy
import math
from math import nan
import signal
import sys
from clover import srv
from std_srvs.srv import Trigger
from sensor_msgs.msg import Range
from util import handle_response
rospy.init_node('autotest_flight', disable_signals=True) # disable signals to allow interrupting with ctrl+c
get_telemetry = rospy.ServiceProxy('get_telemetry', srv.GetTelemetry)
navigate = handle_response(rospy.ServiceProxy('navigate', srv.Navigate))
navigate_global = handle_response(rospy.ServiceProxy('navigate_global', srv.NavigateGlobal))
set_position = handle_response(rospy.ServiceProxy('set_position', srv.SetPosition))
set_velocity = handle_response(rospy.ServiceProxy('set_velocity', srv.SetVelocity))
set_attitude = handle_response(rospy.ServiceProxy('set_attitude', srv.SetAttitude))
set_rates = handle_response(rospy.ServiceProxy('set_rates', srv.SetRates))
land = handle_response(rospy.ServiceProxy('land', Trigger))
def interrupt(sig, frame):
print('\nInterrupted, landing...')
land()
sys.exit(0)
signal.signal(signal.SIGINT, interrupt)
def navigate_wait(x=0, y=0, z=0, yaw=nan, yaw_rate=0, speed=0.5, \
frame_id='body', tolerance=0.2, auto_arm=False):
res = navigate(x=x, y=y, z=z, yaw=yaw, yaw_rate=yaw_rate, speed=speed, \
frame_id=frame_id, auto_arm=auto_arm)
if not res.success:
return res
while not rospy.is_shutdown():
telem = get_telemetry(frame_id='navigate_target')
if math.sqrt(telem.x ** 2 + telem.y ** 2 + telem.z ** 2) < tolerance:
return res
rospy.sleep(0.2)
def print_distance():
dist = rospy.wait_for_message('rangefinder/range', Range).range
print('Distance: {:.2f}'.format(dist))
input('Take off and hover 1 m [enter] ')
navigate_wait(z=1, frame_id='body', auto_arm=True)
print_distance()
start = get_telemetry()
input('Fly forward 2 m [enter] ')
navigate_wait(x=2, frame_id='navigate_target')
print_distance()
input('Climb 0.5 m [enter] ')
navigate_wait(z=0.5, frame_id='navigate_target')
print_distance()
input('Rotate left 90° [enter] ')
navigate(yaw=math.pi / 2, frame_id='navigate_target')
rospy.sleep(3)
input('Use set_velocity to fly forward 2 m speed 1 [enter]')
set_velocity(vx=1, vy=0.0, vz=0, frame_id='body')
rospy.sleep(2)
set_position(frame_id='body')
input('Rotate right 90° [enter] ')
navigate(yaw=-math.pi / 2, frame_id='navigate_target')
rospy.sleep(3)
input('Use set_attitude to fly backwards [enter]')
set_attitude(pitch=-0.3, roll=0, yaw=0, thrust=0.5, frame_id='body')
rospy.sleep(0.3)
set_position(frame_id='body')
input('Use set_attitude to fly right [enter]')
set_attitude(pitch=0, roll=0.3, yaw=0, thrust=0.5, frame_id='body')
rospy.sleep(0.5)
set_position(frame_id='body')
input('Use set_rates to fly right [enter]')
set_rates(roll_rate=1.2, thrust=0.5)
rospy.sleep(0.4)
set_position(frame_id='body')
input('Rotate 360° to the right using yaw_rate [enter]')
set_position(x=nan, y=nan, z=nan, frame_id='body', yaw=nan, yaw_rate=-1)
rospy.sleep(2 * math.pi)
set_position(frame_id='body')
input('Return to start point [enter]')
navigate_wait(x=start.x, y=start.y, z=start.z, yaw=start.yaw, speed=1, frame_id='map')
input('Land [enter]')
land()

View File

@@ -0,0 +1,72 @@
#!/usr/bin/env python3
import rospy
import functools
from clover.srv import SetLEDEffect
from led_msgs.srv import SetLEDs
from led_msgs.msg import LEDStateArray, LEDState
from util import handle_response
rospy.init_node('autotest_led', disable_signals=True)
set_leds = handle_response(rospy.ServiceProxy('led/set_leds', SetLEDs))
set_effect = handle_response(rospy.ServiceProxy('led/set_effect', SetLEDEffect))
led_count = len(rospy.wait_for_message('led/state', LEDStateArray, timeout=10).leds)
print('LED count =', led_count)
print('== Testing effects ==')
input('Fill red [enter] ')
set_effect(r=255, g=0, b=0)
input('Fill green [enter] ')
set_effect(r=0, g=100, b=0)
input('Blink white [enter] ')
set_effect(effect='blink', r=255, g=255, b=255)
rospy.sleep(3)
input('Blink fast violet [enter] ')
set_effect(effect='blink_fast', r=220, g=20, b=250)
rospy.sleep(3)
input('Fade to blue [enter] ')
set_effect(effect='fade', r=0, g=0, b=255)
input('Wipe to yellow [enter] ')
set_effect(effect='wipe', r=255, g=255, b=40)
input('Flash red [enter] ')
set_effect(effect='flash', r=255, g=0, b=0)
rospy.sleep(1)
input('Rainbow [enter] ')
set_effect(effect='rainbow')
rospy.sleep(4)
input('Rainbow fill [enter] ')
set_effect(effect='rainbow_fill')
rospy.sleep(4)
input('Turn off [enter] ')
set_effect()
print('== Testing low-level control ==')
input('Fill orange [enter] ')
set_leds(leds=[LEDState(index=i, r=245, g=155, b=0) for i in range(led_count)])
input('Fill blue gradient [enter] ')
set_leds(leds=[LEDState(index=i, r=0, g=20, b=int(255 * i / led_count)) for i in range(led_count)])
input('Animate green dot [enter] ')
set_effect()
for i in range(led_count):
if i > 0:
set_leds(leds=[LEDState(index=i - 1, r=0, g=0, b=0)])
set_leds(leds=[LEDState(index=i, r=0, g=255, b=0)])
rospy.sleep(0.05)
input('Turn off [enter] ')
set_effect()

View File

@@ -0,0 +1,11 @@
import functools
# decorator to handle response and print error message
def handle_response(fn):
@functools.wraps(fn)
def wrapper(*args, **kwargs):
res = fn(*args, **kwargs)
if not res.success:
print('\033[91mError:\033[0m {}'.format(res.message))
return res
return wrapper

View File

@@ -0,0 +1,35 @@
import rospy
from threading import Thread, Event
def long_callback(fn):
"""
Decorator fixing a rospy issue for long-running topic callbacks, primarily
for image processing.
See: https://github.com/ros/ros_comm/issues/1901.
Usage example:
@long_callback
def image_callback(msg):
# perform image processing
# ...
rospy.Subscriber('main_camera/image_raw', Image, image_callback, queue_size=1)
"""
e = Event()
def thread():
while not rospy.is_shutdown():
e.wait()
e.clear()
fn(thread.current_msg)
thread.current_msg = None
Thread(target=thread, daemon=True).start()
def wrapper(msg):
thread.current_msg = msg
e.set()
return wrapper

View File

@@ -12,6 +12,7 @@
#include <ros/ros.h>
#include <string>
#include <vector>
#include <boost/algorithm/string.hpp>
#include <clover/SetLEDEffect.h>
@@ -29,7 +30,7 @@ ros::Timer timer;
ros::Time start_time;
double blink_rate, blink_fast_rate, flash_delay, fade_period, wipe_period, rainbow_period;
double low_battery_threshold;
bool blink_state;
std::vector<std::string> error_ignore;
led_msgs::SetLEDs set_leds;
led_msgs::LEDStateArray state, start_state;
ros::ServiceClient set_leds_srv;
@@ -85,9 +86,8 @@ void proceed(const ros::TimerEvent& event)
set_leds.request.leds.resize(led_count);
if (current_effect.effect == "blink" || current_effect.effect == "blink_fast") {
blink_state = !blink_state;
// toggle all leds
if (blink_state) {
// enable on odd counter
if (counter % 2 != 0) {
fill(current_effect.r, current_effect.g, current_effect.b);
} else {
fill(0, 0, 0);
@@ -220,6 +220,7 @@ bool setEffect(clover::SetLEDEffect::Request& req, clover::SetLEDEffect::Respons
counter = 0;
start_state = state;
start_time = ros::Time::now();
proceed({ .current_real = start_time });
return true;
}
@@ -274,6 +275,10 @@ void handleMavrosState(const mavros_msgs::State& msg)
void handleLog(const rosgraph_msgs::Log& log)
{
if (log.level >= rosgraph_msgs::Log::ERROR) {
// check if ignored
for (auto const& str : error_ignore) {
if (log.msg.find(str) != std::string::npos) return;
}
notify("error");
}
}
@@ -302,6 +307,7 @@ int main(int argc, char **argv)
nh_priv.param("rainbow_period", rainbow_period, 5.0);
nh_priv.param("notify/low_battery/threshold", low_battery_threshold, 3.7);
nh_priv.param("notify/error/ignore", error_ignore, {});
ros::service::waitForService("set_leds"); // cannot work without set_leds service
set_leds_srv = nh.serviceClient<led_msgs::SetLEDs>("set_leds", true);
@@ -313,8 +319,8 @@ int main(int argc, char **argv)
auto set_effect = nh.advertiseService("set_effect", &setEffect);
auto mavros_state_sub = nh.subscribe("/mavros/state", 1, &handleMavrosState);
auto battery_sub = nh.subscribe("/mavros/battery", 1, &handleBattery);
auto mavros_state_sub = nh.subscribe("mavros/state", 1, &handleMavrosState);
auto battery_sub = nh.subscribe("mavros/battery", 1, &handleBattery);
auto rosout_sub = nh.subscribe("/rosout_agg", 1, &handleLog);
timer = nh.createTimer(ros::Duration(0), &proceed, false, false);

View File

@@ -22,11 +22,14 @@
#include <tf2/utils.h>
#include <tf2_ros/transform_listener.h>
#include <tf2_geometry_msgs/tf2_geometry_msgs.h>
#include <dynamic_reconfigure/server.h>
#include <mavros_msgs/OpticalFlowRad.h>
#include <sensor_msgs/Imu.h>
#include <geometry_msgs/PoseStamped.h>
#include <geometry_msgs/Vector3Stamped.h>
#include <geometry_msgs/PointStamped.h>
#include <geometry_msgs/TwistStamped.h>
#include <clover/FlowConfig.h>
using cv::Mat;
@@ -38,6 +41,7 @@ public:
{}
private:
bool enabled_;
ros::Publisher flow_pub_, velo_pub_, shift_pub_;
ros::Time prev_stamp_;
std::string fcu_frame_id_, local_frame_id_;
@@ -53,6 +57,11 @@ private:
std::unique_ptr<tf2_ros::Buffer> tf_buffer_;
std::unique_ptr<tf2_ros::TransformListener> tf_listener_;
bool calc_flow_gyro_;
float flow_gyro_default_;
bool disable_on_vpe_;
ros::Subscriber vpe_sub_;
ros::Time last_vpe_time_;
std::shared_ptr<dynamic_reconfigure::Server<clover::FlowConfig>> dyn_srv_;
void onInit()
{
@@ -69,21 +78,30 @@ private:
roi_px_ = nh_priv.param("roi", 128);
roi_rad_ = nh_priv.param("roi_rad", 0.0);
calc_flow_gyro_ = nh_priv.param("calc_flow_gyro", false);
flow_gyro_default_ = nh_priv.param("flow_gyro_default", NAN);
img_pub_ = it_priv.advertise("debug", 1);
flow_pub_ = nh.advertise<mavros_msgs::OpticalFlowRad>("mavros/px4flow/raw/send", 1);
velo_pub_ = nh_priv.advertise<geometry_msgs::TwistStamped>("angular_velocity", 1);
shift_pub_ = nh_priv.advertise<geometry_msgs::Vector3Stamped>("shift", 1);
flow_.integrated_xgyro = NAN; // no IMU available
flow_.integrated_ygyro = NAN;
flow_.integrated_zgyro = NAN;
flow_.time_delta_distance_us = 0;
flow_.distance = -1; // no distance sensor available
flow_.temperature = 0;
img_sub_ = it.subscribeCamera("image_raw", 1, &OpticalFlow::flow, this);
disable_on_vpe_ = nh_priv.param("disable_on_vpe", false);
if (disable_on_vpe_) {
vpe_sub_ = nh.subscribe("mavros/vision_pose/pose", 1, &OpticalFlow::vpeCallback, this);
}
dyn_srv_ = std::make_shared<dynamic_reconfigure::Server<clover::FlowConfig>>(nh_priv);
dynamic_reconfigure::Server<clover::FlowConfig>::CallbackType cb;
cb = std::bind(&OpticalFlow::paramCallback, this, std::placeholders::_1, std::placeholders::_2);
dyn_srv_->setCallback(cb);
NODELET_INFO("Optical Flow initialized");
}
@@ -110,6 +128,14 @@ private:
void flow(const sensor_msgs::ImageConstPtr& msg, const sensor_msgs::CameraInfoConstPtr& cinfo)
{
if (!enabled_) return;
if (disable_on_vpe_ &&
!last_vpe_time_.isZero() &&
(msg->header.stamp - last_vpe_time_).toSec() < 0.1) {
return;
}
parseCameraInfo(cinfo);
auto img = cv_bridge::toCvShare(msg, "mono8")->image;
@@ -143,7 +169,7 @@ private:
img.convertTo(curr_, CV_32F);
if (prev_.empty()) {
if (prev_.empty() || (msg->header.stamp - prev_stamp_).toSec() > 0.1) { // outdated previous frame
prev_ = curr_.clone();
prev_stamp_ = msg->header.stamp;
cv::createHanningWindow(hann_, curr_.size(), CV_32F);
@@ -153,7 +179,7 @@ private:
cv::Point2d shift = cv::phaseCorrelate(prev_, curr_, hann_, &response);
// Publish raw shift in pixels
static geometry_msgs::Vector3Stamped shift_vec;
geometry_msgs::Vector3Stamped shift_vec;
shift_vec.header.stamp = msg->header.stamp;
shift_vec.header.frame_id = msg->header.frame_id;
shift_vec.vector.x = shift.x;
@@ -179,8 +205,8 @@ private:
double flow_x = atan2(points_undist[0].x, focal_length_x);
double flow_y = atan2(points_undist[0].y, focal_length_y);
// // Convert to FCU frame
static geometry_msgs::Vector3Stamped flow_camera, flow_fcu;
// Convert to FCU frame
geometry_msgs::Vector3Stamped flow_camera, flow_fcu;
flow_camera.header.frame_id = msg->header.frame_id;
flow_camera.header.stamp = msg->header.stamp;
flow_camera.vector.x = flow_y; // +y means counter-clockwise rotation around Y axis
@@ -196,18 +222,21 @@ private:
ros::Duration integration_time = msg->header.stamp - prev_stamp_;
uint32_t integration_time_us = integration_time.toSec() * 1.0e6;
// Calculate flow gyro
flow_.integrated_xgyro = flow_gyro_default_;
flow_.integrated_ygyro = flow_gyro_default_;
flow_.integrated_zgyro = flow_gyro_default_;
if (calc_flow_gyro_) {
try {
auto flow_gyro_camera = calcFlowGyro(msg->header.frame_id, prev_stamp_, msg->header.stamp);
static geometry_msgs::Vector3Stamped flow_gyro_fcu;
geometry_msgs::Vector3Stamped flow_gyro_fcu;
tf_buffer_->transform(flow_gyro_camera, flow_gyro_fcu, fcu_frame_id_);
flow_.integrated_xgyro = flow_gyro_fcu.vector.x;
flow_.integrated_ygyro = flow_gyro_fcu.vector.y;
flow_.integrated_zgyro = flow_gyro_fcu.vector.z;
} catch (const tf2::TransformException& e) {
// Invalidate previous frame
prev_.release();
goto publish_debug;
// Transform not available, keep NANs in flow gyro
}
}
@@ -222,6 +251,14 @@ private:
prev_ = curr_.clone();
prev_stamp_ = msg->header.stamp;
// Publish estimated angular velocity
geometry_msgs::TwistStamped velo;
velo.header.stamp = msg->header.stamp;
velo.header.frame_id = fcu_frame_id_;
velo.twist.angular.x = flow_fcu.vector.x / integration_time.toSec();
velo.twist.angular.y = flow_fcu.vector.y / integration_time.toSec();
velo_pub_.publish(velo);
publish_debug:
// Publish debug image
if (img_pub_.getNumSubscribers() > 0) {
@@ -234,14 +271,6 @@ publish_debug:
out_msg.image = img;
img_pub_.publish(out_msg.toImageMsg());
}
// Publish estimated angular velocity
static geometry_msgs::TwistStamped velo;
velo.header.stamp = msg->header.stamp;
velo.header.frame_id = fcu_frame_id_;
velo.twist.angular.x = flow_fcu.vector.x / integration_time.toSec();
velo.twist.angular.y = flow_fcu.vector.y / integration_time.toSec();
velo_pub_.publish(velo);
}
}
@@ -262,6 +291,18 @@ publish_debug:
return flow;
}
void paramCallback(clover::FlowConfig &config, uint32_t level)
{
enabled_ = config.enabled;
if (!enabled_) {
prev_ = Mat(); // clear previous frame
}
}
void vpeCallback(const geometry_msgs::PoseStamped& vpe) {
last_vpe_time_ = vpe.header.stamp;
}
};
PLUGINLIB_EXPORT_CLASS(OpticalFlow, nodelet::Nodelet)

View File

@@ -91,7 +91,7 @@ private:
void fakeGCSThread()
{
// Awful workaround for fixing PX4 not sending STATUSTEXTs
// if there is no GCS hearbeats.
// if there is no GCS heartbeats.
// TODO: use timer
// TODO: remove, when PX4 get this fixed.
ros::Publisher mavlink_pub = nh.advertise<mavros_msgs::Mavlink>("mavlink/to", 1);

View File

@@ -15,7 +15,8 @@ import subprocess
import re
from collections import OrderedDict
import traceback
from threading import Event
import threading
from threading import Event, Thread, Lock
import numpy
import rospy
import tf2_ros
@@ -27,67 +28,76 @@ 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
from diagnostic_msgs.msg import DiagnosticArray
import tf.transformations as t
from aruco_pose.msg import MarkerArray
from mavros import mavlink
# TODO: check attitude is present
# TODO: disk free space
# TODO: map, base_link, body
# TODO: rc service
# TODO: perform commander check, ekf2 status on PX4
# TODO: check if FCU params setter succeed
# TODO: selfcheck ROS service (with blacklists for checks)
import locale
rospy.init_node('selfcheck')
os.environ['ROSCONSOLE_FORMAT']='${message}'
# use user's locale to convert numbers, etc
locale.setlocale(locale.LC_ALL, '')
tf_buffer = tf2_ros.Buffer()
tf_listener = tf2_ros.TransformListener(tf_buffer)
failures = []
infos = []
current_check = None
thread_local = threading.local()
reports_lock = Lock()
def failure(text, *args):
msg = text % args
rospy.logwarn('%s: %s', current_check, msg)
failures.append(msg)
thread_local.reports += [{'failure': msg}]
def info(text, *args):
msg = text % args
rospy.loginfo('%s: %s', current_check, msg)
infos.append(msg)
thread_local.reports += [{'info': msg}]
def check(name):
def inner(fn):
def wrapper(*args, **kwargs):
failures[:] = []
infos[:] = []
global current_check
current_check = name
start = rospy.get_time()
thread_local.reports = []
try:
fn(*args, **kwargs)
except Exception as e:
traceback.print_exc()
rospy.logerr('%s: exception occurred', name)
return
if not failures and not infos:
rospy.loginfo('%s: OK', name)
with reports_lock:
for report in thread_local.reports:
if 'failure' in report:
rospy.logerr('%s: %s', name, report['failure'])
elif 'info' in report:
rospy.loginfo('\033[90m%s\033[0m: %s', name, report['info'])
if not thread_local.reports:
rospy.loginfo('\033[90m%s\033[0m: \033[92mOK\033[0m', name)
if rospy.get_param('~time', False):
rospy.loginfo('%s: %.1f sec', name, rospy.get_time() - start)
return wrapper
return inner
def ff(value, precision=2):
# safely format float or int
if value is None:
return '\033[31m???\033[0m'
if isinstance(value, float):
return ('{:.' + str(precision + 1) + '}').format(value)
elif isinstance(value, int):
return str(value)
param_get = rospy.ServiceProxy('mavros/param/get', ParamGet)
def get_param(name):
def get_param(name, default=None):
try:
res = param_get(param_id=name)
except rospy.ServiceException as e:
@@ -96,12 +106,17 @@ def get_param(name):
if not res.success:
failure('unable to retrieve PX4 parameter %s', name)
return default
else:
if res.value.integer != 0:
return res.value.integer
return res.value.real
def get_paramf(name, precision=2):
return ff(get_param(name), precision)
recv_event = Event()
link = mavutil.mavlink.MAVLink('', 255, 1)
mavlink_pub = rospy.Publisher('mavlink/to', Mavlink, queue_size=1)
@@ -146,6 +161,24 @@ def mavlink_exec(cmd, timeout=3.0):
return mavlink_recv
def read_diagnostics(name, key):
e = Event()
def cb(msg):
for status in msg.status:
if status.name.lower() == name.lower():
for value in status.values:
if value.key.lower() == key.lower():
cb.value = value.value
e.set()
return
cb.value = None
sub = rospy.Subscriber('/diagnostics', DiagnosticArray, cb)
e.wait(1.0) # wait to read all the diagnostics from nodes publishing them
sub.unregister()
return cb.value
BOARD_ROTATIONS = {
0: 'no rotation',
1: 'yaw 45°',
@@ -193,24 +226,28 @@ def check_fcu():
failure('no connection to the FCU (check wiring)')
return
# Make sure the console is available to us
mavlink_exec('\n')
version_str = mavlink_exec('ver all')
if version_str == '':
info('no version data available from SITL')
if not is_process_running('px4', exact=True): # can't use px4 console in SITL
clover_tag = re.compile(r'-cl[oe]ver\.\d+$')
clover_fw = False
r = re.compile(r'^FW (git tag|version): (v?\d\.\d\.\d.*)$')
is_clover_firmware = False
for ver_line in version_str.split('\n'):
match = r.search(ver_line)
if match is not None:
field, version = match.groups()
info('firmware %s: %s' % (field, version))
if 'clover' in version or 'clever' in version:
is_clover_firmware = True
# Make sure the console is available to us
mavlink_exec('\n')
version_str = mavlink_exec('ver all')
if version_str == '':
info('no version data available from SITL')
if not is_clover_firmware:
failure('not running Clover PX4 firmware, https://clover.coex.tech/firmware')
for line in version_str.split('\n'):
if line.startswith('FW version: '):
info(line[len('FW version: '):])
elif line.startswith('FW git tag: '): # only Clover's firmware
tag = line[len('FW git tag: '):]
clover_fw = clover_tag.search(tag)
info(tag)
elif line.startswith('HW arch: '):
info(line[len('HW arch: '):])
if not clover_fw:
info('not Clover PX4 firmware, check https://clover.coex.tech/firmware')
est = get_param('SYS_MC_EST_GROUP')
if est == 1:
@@ -247,18 +284,25 @@ def check_fcu():
if cbrk_usb_chk != 197848:
failure('set parameter CBRK_USB_CHK to 197848 for flying with USB connected')
if not is_process_running('px4', exact=True): # skip battery check in SITL
try:
battery = rospy.wait_for_message('mavros/battery', BatteryState, timeout=3)
if not battery.cell_voltage:
failure('cell voltage is not available, https://clover.coex.tech/power')
else:
cell = battery.cell_voltage[0]
if cell > 4.3 or cell < 3.0:
failure('incorrect cell voltage: %.2f V, https://clover.coex.tech/power', cell)
elif cell < 3.7:
failure('critically low cell voltage: %.2f V, recharge battery', cell)
except rospy.ROSException:
failure('no battery state')
# time sync check
try:
battery = rospy.wait_for_message('mavros/battery', BatteryState, timeout=3)
if not battery.cell_voltage:
failure('cell voltage is not available, https://clover.coex.tech/power')
else:
cell = battery.cell_voltage[0]
if cell > 4.3 or cell < 3.0:
failure('incorrect cell voltage: %.2f V, https://clover.coex.tech/power', cell)
elif cell < 3.7:
failure('critically low cell voltage: %.2f V, recharge battery', cell)
except rospy.ROSException:
failure('no battery state')
info('time sync offset: %.2f s', float(read_diagnostics('mavros: Time Sync', 'Estimated time offset (s)')))
except:
failure('cannot read time sync offset')
except rospy.ROSException:
failure('no MAVROS state (check wiring)')
@@ -328,7 +372,7 @@ def is_process_running(binary, exact=False, full=False):
if exact:
args.append('-x') # match exactly with the command name
if full:
args.append('-f') # use full process name to match
args.append('-f') # use full command line (including arguments) to match
args.append(binary)
subprocess.check_output(args)
return True
@@ -338,6 +382,8 @@ def is_process_running(binary, exact=False, full=False):
@check('ArUco markers')
def check_aruco():
markers = None
if is_process_running('aruco_detect', full=True):
try:
info('aruco_detect/length = %g m', rospy.get_param('aruco_detect/length'))
@@ -348,9 +394,9 @@ def check_aruco():
known_tilt += ' (ALL markers are on the floor)'
elif known_tilt == 'map_flipped':
known_tilt += ' (ALL markers are on the ceiling)'
info('aruco_detector/known_tilt = %s', known_tilt)
info('aruco_detect/known_tilt = %s', known_tilt)
try:
rospy.wait_for_message('aruco_detect/markers', MarkerArray, timeout=1)
markers = rospy.wait_for_message('aruco_detect/markers', MarkerArray, timeout=0.8)
except rospy.ROSException:
failure('no markers detection')
return
@@ -367,34 +413,49 @@ def check_aruco():
info('aruco_map/known_tilt = %s', known_tilt)
try:
visualization = rospy.wait_for_message('aruco_map/visualization', VisualizationMarkerArray, timeout=1)
visualization = rospy.wait_for_message('aruco_map/visualization', VisualizationMarkerArray, timeout=0.8)
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)
rospy.wait_for_message('aruco_map/pose', PoseWithCovarianceStamped, timeout=0.8)
except rospy.ROSException:
failure('no map detection')
if not markers:
info('no map detection as no markers detection')
elif not markers.markers:
info('no map detection as no markers detected')
else:
failure('no map detection')
else:
info('aruco_map is not running')
def is_on_the_floor():
try:
dist = rospy.wait_for_message('rangefinder/range', Range, timeout=1)
return dist.range < 0.3
except rospy.ROSException:
return False
@check('Vision position estimate')
def check_vpe():
vis = None
try:
vis = rospy.wait_for_message('mavros/vision_pose/pose', PoseStamped, timeout=1)
vis = rospy.wait_for_message('mavros/vision_pose/pose', PoseStamped, timeout=0.8)
except rospy.ROSException:
try:
vis = rospy.wait_for_message('mavros/mocap/pose', PoseStamped, timeout=1)
vis = rospy.wait_for_message('mavros/mocap/pose', PoseStamped, timeout=0.8)
except rospy.ROSException:
failure('no VPE or MoCap messages')
# check if vpe_publisher is running
try:
subprocess.check_output(['pgrep', '-x', 'vpe_publisher'])
except subprocess.CalledProcessError:
return # it's not running, skip following checks
if not is_process_running('vpe_publisher', full=True):
info('no vision position estimate, vpe_publisher is not running')
elif rospy.get_param('aruco_map/known_tilt') == 'map_flipped':
failure('no vision position estimate, markers are on the ceiling')
elif is_on_the_floor():
info('no vision position estimate, the drone is on the floor')
else:
failure('no vision position estimate')
# check PX4 settings
est = get_param('SYS_MC_EST_GROUP')
@@ -406,14 +467,14 @@ def check_vpe():
if vision_yaw_w == 0:
failure('vision yaw weight is zero, change ATT_W_EXT_HDG parameter')
else:
info('Vision yaw weight: %.2f', vision_yaw_w)
info('vision yaw weight: %s', ff(vision_yaw_w))
fuse = get_param('LPE_FUSION')
if not fuse & (1 << 2):
failure('vision position fusion is disabled, change LPE_FUSION parameter')
delay = get_param('LPE_VIS_DELAY')
if delay != 0:
failure('LPE_VIS_DELAY parameter is %s, but it should be zero', delay)
info('LPE_VIS_XY is %.2f m, LPE_VIS_Z is %.2f m', get_param('LPE_VIS_XY'), get_param('LPE_VIS_Z'))
failure('LPE_VIS_DELAY = %s, but it should be zero', delay)
info('LPE_VIS_XY = %s m, LPE_VIS_Z = %s m', get_paramf('LPE_VIS_XY'), get_paramf('LPE_VIS_Z'))
elif est == 2:
fuse = get_param('EKF2_AID_MASK')
if not fuse & (1 << 3):
@@ -422,10 +483,10 @@ def check_vpe():
failure('vision yaw fusion is disabled, change EKF2_AID_MASK parameter')
delay = get_param('EKF2_EV_DELAY')
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'))
failure('EKF2_EV_DELAY = %.2f, but it should be zero', delay)
info('EKF2_EVA_NOISE = %s, EKF2_EVP_NOISE = %s',
get_paramf('EKF2_EVA_NOISE', 3),
get_paramf('EKF2_EVP_NOISE', 3))
if not vis:
return
@@ -483,6 +544,12 @@ def check_local_position():
failure('roll is %.2f deg; place copter horizontally or redo level horizon calib',
math.degrees(roll))
if not tf_buffer.can_transform('base_link', pose.header.frame_id, rospy.get_rostime(), rospy.Duration(0.5)):
failure('can\'t transform from %s to base_link (timeout 0.5 s): is TF enabled?', pose.header.frame_id)
if not tf_buffer.can_transform('body', pose.header.frame_id, rospy.get_rostime(), rospy.Duration(0.5)):
failure('can\'t transform from %s to body (timeout 0.5 s)', pose.header.frame_id)
except rospy.ROSException:
failure('no local position')
@@ -517,9 +584,11 @@ def check_velocity():
@check('Global position (GPS)')
def check_global_position():
try:
rospy.wait_for_message('mavros/global_position/global', NavSatFix, timeout=1)
rospy.wait_for_message('mavros/global_position/global', NavSatFix, timeout=0.8)
except rospy.ROSException:
info('no global position')
if get_param('SYS_MC_EST_GROUP') == 2 and (get_param('EKF2_AID_MASK', 0) & (1 << 0)):
failure('enabled GPS fusion may suppress vision position aiding')
@check('Optical flow')
@@ -531,7 +600,7 @@ def check_optical_flow():
# check PX4 settings
rot = get_param('SENS_FLOW_ROT')
if rot != 0:
failure('SENS_FLOW_ROT parameter is %s, but it should be zero', rot)
failure('SENS_FLOW_ROT = %s, but it should be zero', rot)
est = get_param('SYS_MC_EST_GROUP')
if est == 1:
fuse = get_param('LPE_FUSION')
@@ -539,32 +608,36 @@ def check_optical_flow():
failure('optical flow fusion is disabled, change LPE_FUSION parameter')
if not fuse & (1 << 1):
failure('flow gyro compensation is disabled, change LPE_FUSION parameter')
scale = get_param('LPE_FLW_SCALE')
scale = get_param('LPE_FLW_SCALE', 1)
if not numpy.isclose(scale, 1.0):
failure('LPE_FLW_SCALE parameter is %.2f, but it should be 1.0', scale)
failure('LPE_FLW_SCALE = %.2f, but it should be 1.0', scale)
info('LPE_FLW_QMIN is %s, LPE_FLW_R is %.4f, LPE_FLW_RR is %.4f, SENS_FLOW_MINHGT is %.3f, SENS_FLOW_MAXHGT is %.3f',
get_param('LPE_FLW_QMIN'),
get_param('LPE_FLW_R'),
get_param('LPE_FLW_RR'),
get_param('SENS_FLOW_MINHGT'),
get_param('SENS_FLOW_MAXHGT'))
info('LPE_FLW_QMIN = %s, LPE_FLW_R = %s, LPE_FLW_RR = %s',
get_paramf('LPE_FLW_QMIN'),
get_paramf('LPE_FLW_R', 4),
get_paramf('LPE_FLW_RR', 4))
elif est == 2:
fuse = get_param('EKF2_AID_MASK')
fuse = get_param('EKF2_AID_MASK', 0)
if not fuse & (1 << 1):
failure('optical flow fusion is disabled, change EKF2_AID_MASK parameter')
delay = get_param('EKF2_OF_DELAY')
delay = get_param('EKF2_OF_DELAY', 0)
if delay != 0:
failure('EKF2_OF_DELAY is %.2f, but it should be zero', delay)
info('EKF2_OF_QMIN is %s, EKF2_OF_N_MIN is %.4f, EKF2_OF_N_MAX is %.4f, SENS_FLOW_MINHGT is %.3f, SENS_FLOW_MAXHGT is %.3f',
get_param('EKF2_OF_QMIN'),
get_param('EKF2_OF_N_MIN'),
get_param('EKF2_OF_N_MAX'),
get_param('SENS_FLOW_MINHGT'),
get_param('SENS_FLOW_MAXHGT'))
failure('EKF2_OF_DELAY = %.2f, but it should be zero', delay)
info('EKF2_OF_QMIN = %s, EKF2_OF_N_MIN = %s, EKF2_OF_N_MAX = %s',
get_paramf('EKF2_OF_QMIN'),
get_paramf('EKF2_OF_N_MIN', 4),
get_paramf('EKF2_OF_N_MAX', 4))
info('SENS_FLOW_MINHGT = %s, SENS_FLOW_MAXHGT = %s', get_paramf('SENS_FLOW_MINHGT', 3), get_paramf('SENS_FLOW_MAXHGT', 3))
except rospy.ROSException:
failure('no optical flow data (from Raspberry)')
if rospy.get_param('optical_flow/disable_on_vpe', False):
try:
rospy.wait_for_message('mavros/vision_pose/pose', PoseStamped, timeout=1)
info('no optical flow as disable_on_vpe is true')
except:
failure('no optical flow on RPi, disable_on_vpe is true, but no vision pose also')
else:
failure('no optical flow on RPi')
@check('Rangefinder')
@@ -588,7 +661,7 @@ def check_rangefinder():
est = get_param('SYS_MC_EST_GROUP')
if est == 1:
fuse = get_param('LPE_FUSION')
fuse = get_param('LPE_FUSION', 0)
if not fuse & (1 << 5):
info('"pub agl as lpos down" in LPE_FUSION is disabled, NOT operating over flat surface')
else:
@@ -609,16 +682,20 @@ def check_rangefinder():
@check('Boot duration')
def check_boot_duration():
if not os.path.exists('/etc/clover_version'):
info('skip check')
return # Don't check not on Clover's image
output = subprocess.check_output('systemd-analyze').decode()
r = re.compile(r'([\d\.]+)s\s*$', flags=re.MULTILINE)
duration = float(r.search(output).groups()[0])
if duration > 15:
if duration > 20:
failure('long Raspbian boot duration: %ss (systemd-analyze for analyzing)', duration)
@check('CPU usage')
def check_cpu_usage():
WHITELIST = 'nodelet',
WHITELIST = 'nodelet', 'gzclient', 'gzserver', 'selfcheck.py'
CMD = "top -n 1 -b -i | tail -n +8 | awk '{ printf(\"%-8s\\t%-8s\\t%-8s\\n\", $1, $9, $12); }'"
output = subprocess.check_output(CMD, shell=True).decode()
processes = output.split('\n')
@@ -627,13 +704,16 @@ def check_cpu_usage():
continue
pid, cpu, cmd = process.split('\t')
if cmd.strip() not in WHITELIST and float(cpu) > 30:
if cmd.strip() not in WHITELIST and locale.atof(cpu) > 30:
failure('high CPU usage (%s%%) detected: %s (PID %s)',
cpu.strip(), cmd.strip(), pid.strip())
@check('clover.service')
def check_clover_service():
if not os.path.exists('/etc/clover_version'):
return # Don't check not on Clover's image
try:
output = subprocess.check_output('systemctl show -p ActiveState --value clover.service'.split(),
stderr=subprocess.STDOUT).decode()
@@ -646,13 +726,22 @@ def check_clover_service():
elif 'failed' in output:
failure('service failed to run, check your launch-files')
r = re.compile(r'^(.*)\[(FATAL|ERROR)\] \[\d+.\d+\]: (.*?)(\x1b(.*))?$')
BLACKLIST = 'Unexpected command 520', 'Time jump detected', 'different index:'
r = re.compile(r'^(.*)\[(FATAL|ERROR| WARN)\] \[\d+.\d+\]: (.*?)(\x1b(.*))?$')
error_count = OrderedDict()
try:
for line in open('/tmp/clover.err', 'r'):
skip = False
for substr in BLACKLIST:
if substr in line:
skip = True
if skip:
continue
node_error = r.search(line)
if node_error:
msg = node_error.groups()[1] + ': ' + node_error.groups()[2]
msg = node_error.groups()[1].strip() + ': ' + node_error.groups()[2]
if msg in error_count:
error_count[msg] += 1
else:
@@ -675,11 +764,18 @@ def check_image():
try:
info('version: %s', open('/etc/clover_version').read().strip())
except IOError:
info('no /etc/clover_version file, not the Clover image?')
try:
info('VM version: %s', open('/etc/clover_vm_version').read().strip())
except IOError:
info('no /etc/clover_version file, not the Clover image?')
@check('Preflight status')
def check_preflight_status():
if is_process_running('px4', exact=True):
info('can\'t check in SITL')
return
# Make sure the console is available to us
mavlink_exec('\n')
cmdr_output = mavlink_exec('commander check')
@@ -701,6 +797,10 @@ def check_preflight_status():
@check('Network')
def check_network():
if not os.path.exists('/etc/clover_version'):
# TODO:
return # Don't check not on Clover's image
ros_hostname = os.environ.get('ROS_HOSTNAME', '').strip()
if not ros_hostname:
@@ -723,6 +823,14 @@ def check_network():
@check('RPi health')
def check_rpi_health():
try:
import shutil
total, used, free = shutil.disk_usage('/')
if free < 1024 * 1024 * 1024:
failure('disk space is less than 1 GB; consider removing logs (~/.ros/log/)')
except Exception as e:
info('could not check the disk free space: %s', str(e))
# `vcgencmd get_throttled` output codes taken from
# https://github.com/raspberrypi/documentation/blob/JamesH65-patch-vcgencmd-vcdbg-docs/raspbian/applications/vcgencmd.md#get_throttled
# TODO: support more base platforms?
@@ -753,7 +861,7 @@ def check_rpi_health():
# with some of the FLAGs OR'ed together
output = subprocess.check_output(['vcgencmd', 'get_throttled']).decode()
except OSError:
failure('could not call vcgencmd binary; not a Raspberry Pi?')
info('could not call vcgencmd binary; not a Raspberry Pi?')
return
throttle_mask = int(output.split('=')[1], base=16)
@@ -770,26 +878,47 @@ def check_board():
info('could not open /proc/device-tree/model, not a Raspberry Pi?')
def parallel_for(fns):
threads = []
for fn in fns:
thread = Thread(target=fn)
thread.start()
threads.append(thread)
for thread in threads:
thread.join()
def consequentially_for(fns):
for fn in fns:
fn()
def selfcheck():
check_image()
check_board()
check_clover_service()
check_network()
check_fcu()
check_imu()
check_local_position()
check_velocity()
check_global_position()
check_preflight_status()
check_main_camera()
check_aruco()
check_simpleoffboard()
check_optical_flow()
check_vpe()
check_rangefinder()
check_rpi_health()
check_cpu_usage()
check_boot_duration()
checks = [
check_image,
check_board,
check_clover_service,
check_network,
check_fcu,
check_imu,
check_local_position,
check_velocity,
check_global_position,
check_preflight_status,
check_main_camera,
check_aruco,
check_simpleoffboard,
check_optical_flow,
check_vpe,
check_rangefinder,
check_rpi_health,
check_cpu_usage,
check_boot_duration,
]
if rospy.get_param('~parallel', False):
parallel_for(checks)
else:
consequentially_for(checks)
if __name__ == '__main__':

View File

@@ -61,6 +61,7 @@ std::shared_ptr<tf2_ros::TransformBroadcaster> transform_broadcaster;
std::shared_ptr<tf2_ros::StaticTransformBroadcaster> static_transform_broadcaster;
// Parameters
string mavros;
string local_frame;
string fcu_frame;
ros::Duration transform_timeout;
@@ -149,6 +150,9 @@ void handleState(const mavros_msgs::State& s)
inline void publishBodyFrame()
{
if (body.child_frame_id.empty()) return;
if (!body.header.stamp.isZero() && body.header.stamp == local_position.header.stamp) {
return; // avoid TF_REPEATED_DATA warnings
}
tf::Quaternion q;
q.setRPY(0, 0, tf::getYaw(local_position.pose.orientation));
@@ -181,9 +185,10 @@ inline bool waitTransform(const string& target, const string& source,
ros::spinOnce();
r.sleep();
}
return false;
}
#define TIMEOUT(msg, timeout) (ros::Time::now() - msg.header.stamp > timeout)
#define TIMEOUT(msg, timeout) (msg.header.stamp.isZero() || (ros::Time::now() - msg.header.stamp > timeout))
bool getTelemetry(GetTelemetry::Request& req, GetTelemetry::Response& res)
{
@@ -441,6 +446,10 @@ void publish(const ros::Time stamp)
// publish setpoint frame
if (!setpoint.child_frame_id.empty()) {
if (setpoint.header.stamp == position_msg.header.stamp) {
return; // avoid TF_REPEATED_DATA warnings
}
setpoint.transform.translation.x = position_msg.pose.position.x;
setpoint.transform.translation.y = position_msg.pose.position.y;
setpoint.transform.translation.z = position_msg.pose.position.z;
@@ -685,7 +694,7 @@ bool serve(enum setpoint_type_t sp_type, float x, float y, float z, float vx, fl
// }
if (sp_type == POSITION || sp_type == NAVIGATE || sp_type == NAVIGATE_GLOBAL || sp_type == VELOCITY || sp_type == ATTITUDE) {
// destination point and/or yaw
// destination point and/or attitude
PoseStamped ps;
ps.header.frame_id = frame_id;
ps.header.stamp = stamp;
@@ -694,7 +703,12 @@ bool serve(enum setpoint_type_t sp_type, float x, float y, float z, float vx, fl
ps.pose.position.z = z;
ps.pose.orientation.w = 1.0; // Ensure quaternion is always valid
if (std::isnan(yaw)) {
if (sp_type == ATTITUDE) {
ps.pose.position.x = 0;
ps.pose.position.y = 0;
ps.pose.position.z = 0;
ps.pose.orientation = tf::createQuaternionMsgFromRollPitchYaw(roll, pitch, yaw);
} else if (std::isnan(yaw)) {
setpoint_yaw_type = YAW_RATE;
setpoint_yaw_rate = yaw_rate;
} else if (std::isinf(yaw) && yaw > 0) {
@@ -712,7 +726,7 @@ bool serve(enum setpoint_type_t sp_type, float x, float y, float z, float vx, fl
}
if (sp_type == VELOCITY) {
static Vector3Stamped vel;
Vector3Stamped vel;
vel.header.frame_id = frame_id;
vel.header.stamp = stamp;
vel.vector.x = vx;
@@ -843,6 +857,14 @@ bool land(std_srvs::Trigger::Request& req, std_srvs::Trigger::Response& res)
busy = false;
return true;
}
return false;
}
bool release(std_srvs::Trigger::Request& req, std_srvs::Trigger::Response& res)
{
setpoint_timer.stop();
res.success = true;
return true;
}
int main(int argc, char **argv)
@@ -855,8 +877,9 @@ int main(int argc, char **argv)
static_transform_broadcaster = std::make_shared<tf2_ros::StaticTransformBroadcaster>();
// Params
nh.param<string>("mavros/local_position/tf/frame_id", local_frame, "map");
nh.param<string>("mavros/local_position/tf/child_frame_id", fcu_frame, "base_link");
nh_priv.param("mavros", mavros, string("mavros")); // for case of using multiple connections
nh.param<string>(mavros + "/local_position/tf/frame_id", local_frame, "map");
nh.param<string>(mavros + "/local_position/tf/child_frame_id", fcu_frame, "base_link");
nh_priv.param("target_frame", target.child_frame_id, string("navigate_target"));
nh_priv.param("setpoint", setpoint.child_frame_id, string("setpoint"));
nh_priv.param("auto_release", auto_release, true);
@@ -867,6 +890,13 @@ int main(int argc, char **argv)
nh_priv.param<string>("body_frame", body.child_frame_id, "body");
nh_priv.getParam("reference_frames", reference_frames);
// Default reference frames
std::map<string, string> default_reference_frames;
default_reference_frames[body.child_frame_id] = local_frame;
default_reference_frames[fcu_frame] = local_frame;
if (!target.child_frame_id.empty()) default_reference_frames[target.child_frame_id] = local_frame;
reference_frames.insert(default_reference_frames.begin(), default_reference_frames.end()); // merge defaults
state_timeout = ros::Duration(nh_priv.param("state_timeout", 3.0));
local_position_timeout = ros::Duration(nh_priv.param("local_position_timeout", 2.0));
velocity_timeout = ros::Duration(nh_priv.param("velocity_timeout", 2.0));
@@ -881,25 +911,25 @@ int main(int argc, char **argv)
arming_timeout = ros::Duration(nh_priv.param("arming_timeout", 4.0));
// Service clients
arming = nh.serviceClient<mavros_msgs::CommandBool>("mavros/cmd/arming");
set_mode = nh.serviceClient<mavros_msgs::SetMode>("mavros/set_mode");
arming = nh.serviceClient<mavros_msgs::CommandBool>(mavros + "/cmd/arming");
set_mode = nh.serviceClient<mavros_msgs::SetMode>(mavros + "/set_mode");
// Telemetry subscribers
auto state_sub = nh.subscribe("mavros/state", 1, &handleState);
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 manual_control_sub = nh.subscribe("mavros/manual_control/control", 1, &handleMessage<mavros_msgs::ManualControl, manual_control>);
auto local_position_sub = nh.subscribe("mavros/local_position/pose", 1, &handleLocalPosition);
auto state_sub = nh.subscribe(mavros + "/state", 1, &handleState);
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 manual_control_sub = nh.subscribe(mavros + "/manual_control/control", 1, &handleMessage<mavros_msgs::ManualControl, manual_control>);
auto local_position_sub = nh.subscribe(mavros + "/local_position/pose", 1, &handleLocalPosition);
// Setpoint publishers
position_pub = nh.advertise<PoseStamped>("mavros/setpoint_position/local", 1);
position_raw_pub = nh.advertise<PositionTarget>("mavros/setpoint_raw/local", 1);
attitude_pub = nh.advertise<PoseStamped>("mavros/setpoint_attitude/attitude", 1);
attitude_raw_pub = nh.advertise<AttitudeTarget>("mavros/setpoint_raw/attitude", 1);
rates_pub = nh.advertise<TwistStamped>("mavros/setpoint_attitude/cmd_vel", 1);
thrust_pub = nh.advertise<Thrust>("mavros/setpoint_attitude/thrust", 1);
position_pub = nh.advertise<PoseStamped>(mavros + "/setpoint_position/local", 1);
position_raw_pub = nh.advertise<PositionTarget>(mavros + "/setpoint_raw/local", 1);
attitude_pub = nh.advertise<PoseStamped>(mavros + "/setpoint_attitude/attitude", 1);
attitude_raw_pub = nh.advertise<AttitudeTarget>(mavros + "/setpoint_raw/attitude", 1);
rates_pub = nh.advertise<TwistStamped>(mavros + "/setpoint_attitude/cmd_vel", 1);
thrust_pub = nh.advertise<Thrust>(mavros + "/setpoint_attitude/thrust", 1);
// Service servers
auto gt_serv = nh.advertiseService("get_telemetry", &getTelemetry);
@@ -910,6 +940,7 @@ int main(int argc, char **argv)
auto sa_serv = nh.advertiseService("set_attitude", &setAttitude);
auto sr_serv = nh.advertiseService("set_rates", &setRates);
auto ld_serv = nh.advertiseService("land", &land);
auto rl_serv = nh_priv.advertiseService("release", &release);
// Setpoint timer
setpoint_timer = nh.createTimer(ros::Duration(1 / nh_priv.param("setpoint_rate", 30.0)), &publishSetpoint, false, false);

View File

@@ -33,14 +33,14 @@ ros::Subscriber local_position_sub;
ros::Timer zero_timer;
PoseStamped vpe, pose;
ros::Time got_local_pos(0);
ros::Duration publish_zero_timout, publish_zero_duration, offset_timeout;
ros::Duration publish_zero_timeout, publish_zero_duration, offset_timeout;
TransformStamped offset;
void publishZero(const ros::TimerEvent& e)
{
if (e.current_real - vpe.header.stamp < publish_zero_timout) return; // have vpe
if (!vpe.header.stamp.isZero() && e.current_real - vpe.header.stamp < publish_zero_timeout) return; // have vpe
if (e.current_real - pose.header.stamp < publish_zero_timout) { // have local position
if (!pose.header.stamp.isZero() && e.current_real - pose.header.stamp < publish_zero_timeout) { // have local position
if (got_local_pos.isZero()) {
ROS_INFO("got local position");
got_local_pos = e.current_real;
@@ -53,7 +53,7 @@ void publishZero(const ros::TimerEvent& e)
}
ROS_INFO_THROTTLE(10, "publish zero");
static geometry_msgs::PoseStamped zero;
geometry_msgs::PoseStamped zero;
zero.header.frame_id = local_frame_id;
zero.header.stamp = e.current_real;
zero.pose.orientation.w = 1;
@@ -124,8 +124,8 @@ int main(int argc, char **argv) {
nh_priv.param<string>("frame_id", frame_id, "");
nh_priv.param<string>("offset_frame_id", offset_frame_id, "");
nh_priv.param<string>("mavros/local_position/frame_id", local_frame_id, "map");
nh_priv.param<string>("mavros/local_position/tf/child_frame_id", child_frame_id, "base_link");
nh.param<string>("mavros/local_position/frame_id", local_frame_id, "map");
nh.param<string>("mavros/local_position/tf/child_frame_id", child_frame_id, "base_link");
offset_timeout = ros::Duration(nh_priv.param("offset_timeout", 3.0));
if (!frame_id.empty()) {
@@ -141,11 +141,11 @@ int main(int argc, char **argv) {
vpe_pub = nh_priv.advertise<PoseStamped>("vpe", 1);
//vpe_cov_pub = nh_priv_.advertise<PoseStamped>("pose_cov_pub", 1);
if (nh_priv.param("publish_zero", false)) {
if (nh_priv.param("force_init", false) || nh_priv.param("publish_zero", false)) { // publish_zero is old name
// publish zero to initialize the local position
zero_timer = nh.createTimer(ros::Duration(0.1), &publishZero);
publish_zero_timout = ros::Duration(nh_priv.param("publish_zero_timout", 5.0));
publish_zero_duration = ros::Duration(nh_priv.param("publish_zero_duration", 5.0));
publish_zero_timeout = ros::Duration(nh_priv.param("force_init_timeout", 5.0));
publish_zero_duration = ros::Duration(nh_priv.param("force_init_duration", 5.0));
local_position_sub = nh.subscribe("mavros/local_position/pose", 1, &localPositionCallback);
}

View File

@@ -6,4 +6,4 @@
echo "wait for file $1"
while [ ! -e "$1" ]; do sleep 1; done;
echo "file $1 appeared"
"${@:2}"
exec "${@:2}"

View File

@@ -3,6 +3,7 @@ import rospy
import pytest
from mavros_msgs.msg import State
from clover import srv
import time
@pytest.fixture()
def node():
@@ -24,6 +25,7 @@ def test_simple_offboard_services_available():
rospy.wait_for_service('set_attitude', timeout=5)
rospy.wait_for_service('set_rates', timeout=5)
rospy.wait_for_service('land', timeout=5)
rospy.wait_for_service('simple_offboard/release', timeout=5)
def test_web_video_server(node):
try:
@@ -33,3 +35,44 @@ def test_web_video_server(node):
# Python 3
import urllib.request as urllib
urllib.urlopen("http://localhost:8080").read()
def test_blocks(node):
rospy.wait_for_service('clover_blocks/run', timeout=5)
rospy.wait_for_service('clover_blocks/stop', timeout=5)
rospy.wait_for_service('clover_blocks/load', timeout=5)
rospy.wait_for_service('clover_blocks/store', timeout=5)
from std_msgs.msg import String
from clover_blocks.srv import Run
def wait_print():
try:
wait_print.result = rospy.wait_for_message('clover_blocks/print', String, timeout=5).data
except Exception as e:
wait_print.result = str(e)
import threading
t = threading.Thread(target=wait_print)
t.start()
rospy.sleep(0.1)
run = rospy.ServiceProxy('clover_blocks/run', Run)
assert run(code='print("test")').success == True
t.join()
assert wait_print.result == 'test'
def test_long_callback():
from clover import long_callback
from time import sleep
# very basic test for long_callback
@long_callback
def cb(i):
cb.counter += i
cb.counter = 0
cb(2)
sleep(0.1)
cb(3)
sleep(1)
assert cb.counter == 5

View File

@@ -23,10 +23,7 @@
<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="clover" 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="simple_offboard" pkg="clover" type="simple_offboard" required="true" output="screen"/>
<node name="tf2_web_republisher" pkg="tf2_web_republisher" type="tf2_web_republisher" required="true"/>
@@ -38,6 +35,11 @@
<rosparam param="notify">startup: { r: 255, g: 255, b: 255 }</rosparam>
</node>
<node name="clover_blocks" pkg="clover_blocks" type="clover_blocks" output="screen" required="true"/>
<node pkg="topic_tools" name="main_camera_throttle" type="throttle" ns="main_camera"
args="messages image_raw 5.0 image_raw_throttled" required="true"/>
<param name="test_module" value="$(find clover)/test/basic.py"/>
<test test-name="basic_test" pkg="ros_pytest" type="ros_pytest_runner"/>
</launch>

View File

@@ -12,4 +12,6 @@ SUBSYSTEM=="tty", ATTRS{idVendor}=="26ac", ATTRS{idProduct}=="0016", ATTRS{produ
SUBSYSTEM=="tty", ATTRS{idVendor}=="26ac", ATTRS{idProduct}=="0013", ATTRS{product}=="PX4 FMU v4.x PRO", SYMLINK+="px4fmu"
# Omnibus
SUBSYSTEM=="tty", ATTRS{idVendor}=="26ac", ATTRS{idProduct}=="0001", ATTRS{product}=="PX4 OmnibusF4SD", SYMLINK+="px4fmu"
# CUAV X7 Pro
SUBSYSTEM=="tty", ATTRS{idVendor}=="3163", ATTRS{idProduct}=="004c", ATTRS{product}=="PX4 CUAV X7Pro", SYMLINK+="px4fmu"

View File

@@ -1 +0,0 @@
/tmp/clover.err

1
clover/www/clover.log Symbolic link
View File

@@ -0,0 +1 @@
/var/log/clover.log

23
clover/www/console.html Normal file
View File

@@ -0,0 +1,23 @@
<h1>
/var/log/clover.log
<a style="font-size: 0.5em; vertical-align: super; font-weight: normal" href="clover.log" download>download</a>
</h1>
<pre></pre>
<script type="module">
var pre = document.querySelector('pre');
fetch('clover.log?' + Math.random()).then(function(response) { // random to forbid caching
if (response.status == 404) {
pre.innerHTML = '/var/log/clover.log does not exist';
return;
} else if (response.status !== 200) {
pre.innerHTML('Error ' + response.status);
return;
}
response.text().then(function(content) {
pre.innerHTML = content;
});
});
</script>

View File

@@ -4,20 +4,32 @@
<ul>
<li><a href="docs">View documentation</a> (snapshot of <a href="https://clover.coex.tech">clover.coex.tech</a>)</li>
<li><a href="topics.html">View topics</a></li>
<li><a href="" id="wvs">View image topics</a> (<code>web_video_server</code>)</li>
<li><a href="" id="butterfly">Open web terminal</a> (<code>Butterfly</code>)</li>
<li><a href="viz.html">View 3D visualization</a> (<code>ros3djs</code>)</li>
<li><a href="aruco_map.html">3D visualization for markers map</a> (<code>ros3djs</code>)</li>
<li>View <a href="viz.html">View 3D visualization</a>, <a href="aruco_map.html">3D visualization for markers map</a> (<code>ros3djs</code>)</li>
<li><a href="../clover_blocks/">Blocks programming</a> (<code>Blockly</code>)</li>
<li><a href="clover.err">Clover console</a> (<code>tmp/clover.err</code>)</li>
<li><a href="console.html">Clover console</a> (<code>/var/log/clover.log</code>)</li>
</ul>
<p>
Update www using <code>rosrun roswww_static update</code>.
</p>
<div class="version"></div>
<script type="text/javascript">
document.querySelector("#wvs").href = location.protocol + '//' + location.hostname + ':8080';
document.querySelector("#butterfly").href = location.protocol + '//' + location.hostname + ':57575';
document.querySelector("#butterfly").addEventListener('click', function(e) {
if (location.hostname == 'localhost' || location.hostname == '127.0.0.1') {
if (!confirm('Please use regular Terminal app on a local machine.\nClick OK to proceed to Butterfly anyway.')) {
e.preventDefault();
}
}
});
// Determine image version
fetch('clover_version').then(function(response) {
if (response.status !== 200) return;

236
clover/www/js/json-to-pretty-yaml.js vendored Normal file
View File

@@ -0,0 +1,236 @@
// Browserified https://www.npmjs.com/package/json-to-pretty-yaml module
(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
(function() {
"use strict";
var typeOf = require('remedial').typeOf;
var trimWhitespace = require('remove-trailing-spaces');
function stringify(data) {
var handlers, indentLevel = '';
handlers = {
"undefined": function() {
// objects will not have `undefined` converted to `null`
// as this may have unintended consequences
// For arrays, however, this behavior seems appropriate
return 'null';
},
"null": function() {
return 'null';
},
"number": function(x) {
return x;
},
"boolean": function(x) {
return x ? 'true' : 'false';
},
"string": function(x) {
// to avoid the string "true" being confused with the
// the literal `true`, we always wrap strings in quotes
return JSON.stringify(x);
},
"array": function(x) {
var output = '';
if (0 === x.length) {
output += '[]';
return output;
}
indentLevel = indentLevel.replace(/$/, ' ');
x.forEach(function(y, i) {
// TODO how should `undefined` be handled?
var handler = handlers[typeOf(y)];
if (!handler) {
throw new Error('what the crap: ' + typeOf(y));
}
output += '\n' + indentLevel + '- ' + handler(y, true);
});
indentLevel = indentLevel.replace(/ /, '');
return output;
},
"object": function(x, inArray, rootNode) {
var output = '';
if (0 === Object.keys(x).length) {
output += '{}';
return output;
}
if (!rootNode) {
indentLevel = indentLevel.replace(/$/, ' ');
}
Object.keys(x).forEach(function(k, i) {
var val = x[k],
handler = handlers[typeOf(val)];
if ('undefined' === typeof val) {
// the user should do
// delete obj.key
// and not
// obj.key = undefined
// but we'll error on the side of caution
return;
}
if (!handler) {
throw new Error('what the crap: ' + typeOf(val));
}
if (!(inArray && i === 0)) {
output += '\n' + indentLevel;
}
output += k + ': ' + handler(val);
});
indentLevel = indentLevel.replace(/ /, '');
return output;
},
"function": function() {
// TODO this should throw or otherwise be ignored
return '[object Function]';
}
};
return trimWhitespace(handlers[typeOf(data)](data, true, true) + '\n');
}
window.yamlStringify = stringify;
module.exports.stringify = stringify;
}());
},{"remedial":2,"remove-trailing-spaces":3}],2:[function(require,module,exports){
/*jslint onevar: true, undef: true, nomen: true, eqeqeq: true, plusplus: true, bitwise: true, regexp: true, newcap: true, immed: true */
(function () {
"use strict";
var global = Function('return this')()
, classes = "Boolean Number String Function Array Date RegExp Object".split(" ")
, i
, name
, class2type = {}
;
for (i in classes) {
if (classes.hasOwnProperty(i)) {
name = classes[i];
class2type["[object " + name + "]"] = name.toLowerCase();
}
}
function typeOf(obj) {
return (null === obj || undefined === obj) ? String(obj) : class2type[Object.prototype.toString.call(obj)] || "object";
}
function isEmpty(o) {
var i, v;
if (typeOf(o) === 'object') {
for (i in o) { // fails jslint
v = o[i];
if (v !== undefined && typeOf(v) !== 'function') {
return false;
}
}
}
return true;
}
if (!String.prototype.entityify) {
String.prototype.entityify = function () {
return this.replace(/&/g, "&amp;").replace(/</g,
"&lt;").replace(/>/g, "&gt;");
};
}
if (!String.prototype.quote) {
String.prototype.quote = function () {
var c, i, l = this.length, o = '"';
for (i = 0; i < l; i += 1) {
c = this.charAt(i);
if (c >= ' ') {
if (c === '\\' || c === '"') {
o += '\\';
}
o += c;
} else {
switch (c) {
case '\b':
o += '\\b';
break;
case '\f':
o += '\\f';
break;
case '\n':
o += '\\n';
break;
case '\r':
o += '\\r';
break;
case '\t':
o += '\\t';
break;
default:
c = c.charCodeAt();
o += '\\u00' + Math.floor(c / 16).toString(16) +
(c % 16).toString(16);
}
}
}
return o + '"';
};
}
if (!String.prototype.supplant) {
String.prototype.supplant = function (o) {
return this.replace(/{([^{}]*)}/g,
function (a, b) {
var r = o[b];
return typeof r === 'string' || typeof r === 'number' ? r : a;
}
);
};
}
if (!String.prototype.trim) {
String.prototype.trim = function () {
return this.replace(/^\s*(\S*(?:\s+\S+)*)\s*$/, "$1");
};
}
// CommonJS / npm / Ender.JS
module.exports = {
typeOf: typeOf,
isEmpty: isEmpty
};
global.typeOf = global.typeOf || typeOf;
global.isEmpty = global.isEmpty || isEmpty;
}());
},{}],3:[function(require,module,exports){
"use strict";
/**
* removeTrailingSpaces
* Remove the trailing spaces from a string.
*
* @name removeTrailingSpaces
* @function
* @param {String} input The input string.
* @returns {String} The output string.
*/
module.exports = function removeTrailingSpaces(input) {
// TODO If possible, use a regex
return input.split("\n").map(function (x) {
return x.trimRight();
}).join("\n");
};
},{}]},{},[1]);

86
clover/www/js/topics.js Normal file
View File

@@ -0,0 +1,86 @@
const url = 'ws://' + location.hostname + ':9090';
const ros = new ROSLIB.Ros({ url: url });
const params = Object.fromEntries(new URLSearchParams(window.location.search).entries());
ros.on('connection', function () {
document.body.classList.add('connected');
document.body.classList.remove('closed');
init();
});
ros.on('close', function () {
document.body.classList.remove('connected');
document.body.classList.add('closed');
setTimeout(function() {
// reconnect
ros.connect(url);
}, 2000);
});
const title = document.querySelector('h1');
const topicsList = document.querySelector('#topics');
const topicMessage = document.querySelector('#topic-message');
function viewTopicsList() {
title.innerHTML = 'Topics:';
ros.getTopics(function(topics) {
topicsList.innerHTML = topics.topics.map(function(topic, i) {
const type = topics.types[i];
if (type == 'sensor_msgs/Image') {
let url = `${location.protocol}//${location.hostname}:8080/stream_viewer?topic=${topic}`;
return `<li><a href="${url}" class=topic title=${type}>${topic}</a> &#x1F5BC;</li>`;
} else {
return `<li><a href="?topic=${topic}" class=topic title=${type}>${topic}</a></li>`;
}
}).join('');
});
}
let rosdistro;
function viewTopic(topic) {
let counter = 0;
let index = '<a href=topics.html>Topics</a>';
title.innerHTML = `${index}: ${topic}`;
topicMessage.style.display = 'block';
ros.getTopicType(topic, function(typeStr) {
const [pack, type] = typeStr.split('/');
let href = `https://docs.ros.org/en/${rosdistro}/api/${pack}/html/msg/${type}.html`;
title.innerHTML = `${index}: ${topic} <a id="topic-type" href=${href} target="_blank">${typeStr}</a>`;
});
new ROSLIB.Topic({ ros: ros, name: topic }).subscribe(function(msg) {
counter++;
document.title = topic;
if (mouseDown) return;
if (msg.header && msg.header.stamp) {
if (params.date || params.offset) {
let date = new Date(msg.header.stamp.secs * 1e3 + msg.header.stamp.nsecs * 1e-6);
if (params.date) msg.header.date = date.toISOString();
if (params.offset) msg.header.offset = (new Date() - date) * 1e-3;
}
}
let txt = `<div class=counter>${counter} received</div>${yamlStringify(msg)}`; // JSON.stringify(msg, null, 4);
topicMessage.innerHTML = txt;
});
}
let mouseDown;
topicMessage.addEventListener('mousedown', function() { mouseDown = true; });
topicMessage.addEventListener('mouseup', function() { mouseDown = false; });
function init() {
if (!params.topic) {
viewTopicsList();
} else {
new ROSLIB.Param({ ros: ros, name: '/rosdistro'}).get(function(value) {
rosdistro = value.trim();
viewTopic(params.topic);
});
}
}

29
clover/www/topics.html Normal file
View File

@@ -0,0 +1,29 @@
<html lang="en">
<head>
<title>ROS topics</title>
<script src="js/roslib.js"></script>
<link rel="icon" href="data:,"> <!-- make chrome don't request icon -->
<script type="module" src="js/topics.js"></script>
<script src="js/json-to-pretty-yaml.js"></script>
<style>
#topics { line-height: 1.2em; }
#topic-view {
display: none;
}
#topic-message {
display: none;
white-space: pre;
font-family: monospace;
}
.counter { color: #b9b9b9; margin-bottom: 1em; }
#topic-type { font-family: monospace; font-size: 0.5em; vertical-align: super; font-weight: normal; }
.topic { font-family: monospace; }
body.closed { background-color: rgb(207, 207, 207); }
</style>
</head>
<body>
<h1>&nbsp;</h1>
<ul id="topics"></ul>
<code id="topic-message">No messages received</code>
</body>
</html>

View File

@@ -73,6 +73,13 @@ catkin_install_python(PROGRAMS src/clover_blocks
DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
)
# TODO: store programs in home directory?
install(DIRECTORY programs
DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}
)
install(DIRECTORY www DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION})
#############
## Testing ##
#############

View File

@@ -10,7 +10,7 @@ Internal package documentation is given below.
## Frontend
The frontend files are located in [`www`](./www/) subdirectory. The frontend application uses [`roblib.js`](http://wiki.ros.org/roslibjs) library for communicating with backend node and other ROS resources.
The frontend files are located in [`www`](./www/) subdirectory. The frontend application uses [`roslib.js`](http://wiki.ros.org/roslibjs) library for communicating with backend node and other ROS resources.
## `clover_blocks` node
@@ -19,7 +19,7 @@ The frontend files are located in [`www`](./www/) subdirectory. The frontend app
### Services
* `~run` ([*clover_blocks/Run*](srv/Run.srv)) run Blockly-generated program (in Python).
* `~stop` ([*std_srvs/Trigger*](http://docs.ros.org/melodic/api/std_srvs/html/srv/Trigger.html)) terminate the running program.
* `~stop` ([*std_srvs/Trigger*](http://docs.ros.org/noetic/api/std_srvs/html/srv/Trigger.html)) terminate the running program.
* `~store` ([*clover_blocks/load*](srv/Store.srv)) store a user program (to `<package_path>/programs` by default).
* `~load` ([*clover_blocks/load*](srv/Load.srv)) load all the stored programs.
@@ -30,7 +30,8 @@ The frontend files are located in [`www`](./www/) subdirectory. The frontend app
Parameters read by frontend:
* `~navigate_tolerance` (*float*) distance tolerance in meters, used for navigate-like blocks (default: 0.2).
* `~yaw_tolerance` (*float*) yaw angle tolerance in degrees, used in set_yaw block (default: 20).
* `~navigate_global_tolerance` (*float*) distance tolerance for global coordinates navigation (default: 1).
* `~yaw_tolerance` (*float*) yaw angle tolerance in degrees, used in set_yaw block (default: 1).
* `~sleep_time` (*float*) duration of sleep in loop cycles, used for navigate-like blocks (default: 0.2).
* `~confirm_run` (*bool*) enable confirmation to run the program (default: true).
@@ -44,11 +45,11 @@ http://<hostname>/clover_blocks/?navigate_tolerance=0.5&sleep_time=0.1
#### Published
* `~running` ([*std_msgs/Bool*](http://docs.ros.org/melodic/api/std_msgs/html/msg/Bool.html)) indicates if the program is currently running.
* `~block` ([*std_msgs/String*](http://docs.ros.org/melodic/api/std_msgs/html/msg/String.html)) current executing block (maximum topic rate is limited).
* `~error` ([*std_msgs/String*](http://docs.ros.org/melodic/api/std_msgs/html/msg/String.html))  user program errors and exceptions.
* `~running` ([*std_msgs/Bool*](http://docs.ros.org/noetic/api/std_msgs/html/msg/Bool.html)) indicates if the program is currently running.
* `~block` ([*std_msgs/String*](http://docs.ros.org/noetic/api/std_msgs/html/msg/String.html)) current executing block (maximum topic rate is limited).
* `~error` ([*std_msgs/String*](http://docs.ros.org/noetic/api/std_msgs/html/msg/String.html))  user program errors and exceptions.
* `~prompt` ([*clover_blocks/Prompt*](msg/Prompt.msg)) user input request (includes random request ID string).
This topic is published from the frontend side:
* `~prompt/<request_id>` ([*std_msgs/String*](http://docs.ros.org/melodic/api/std_msgs/html/msg/String.html))  user input response.
* `~prompt/<request_id>` ([*std_msgs/String*](http://docs.ros.org/noetic/api/std_msgs/html/msg/String.html))  user input response.

View File

@@ -1,7 +1,7 @@
<?xml version="1.0"?>
<package format="2">
<name>clover_blocks</name>
<version>0.21.1</version>
<version>0.23.0</version>
<description>Blockly programming support for Clover</description>
<maintainer email="okalachev@gmail.com">Oleg Kalachev</maintainer>
<license>MIT</license>

View File

@@ -146,6 +146,7 @@ def stop(req):
return {'success': True}
# TODO: find dir in installed package
programs_path = rospy.get_param('~programs_dir', os.path.dirname(os.path.abspath(__file__)) + '/../programs')

View File

@@ -31,6 +31,14 @@ function considerFrameId(e) {
this.getInput('Y').fieldRow[0].setValue('y');
this.getInput('Z').fieldRow[0].setValue('z');
}
if (this.getInput('LAT')) { // block has global coordinates
let global = frameId.startsWith('GLOBAL');
this.getInput('LAT').setVisible(global);
this.getInput('LON').setVisible(global);
this.getInput('X').setVisible(!global);
this.getInput('Y').setVisible(!global);
this.getInput('Z').fieldRow[0].setValue(frameId == 'GLOBAL' ? 'altitude' : 'z');
}
}
if (this.getInput('ID')) { // block has marker id field
this.getInput('ID').setVisible(frameId == 'ARUCO'); // toggle id field
@@ -65,6 +73,9 @@ function updateSetpointBlock(e) {
Blockly.Blocks['navigate'] = {
init: function () {
let navFrameId = frameIds.slice();
navFrameId.push(['global', 'GLOBAL_LOCAL'])
navFrameId.push(['global, WGS 84 alt.', 'GLOBAL'])
this.appendDummyInput()
.appendField("navigate to point");
this.appendValueInput("X")
@@ -73,12 +84,20 @@ Blockly.Blocks['navigate'] = {
this.appendValueInput("Y")
.setCheck("Number")
.appendField("left");
this.appendValueInput("LAT")
.setCheck("Number")
.appendField("latitude")
.setVisible(false);
this.appendValueInput("LON")
.setCheck("Number")
.appendField("longitude")
.setVisible(false)
this.appendValueInput("Z")
.setCheck("Number")
.appendField("up");
this.appendDummyInput()
.appendField("relative to")
.appendField(new Blockly.FieldDropdown(frameIds), "FRAME_ID");
.appendField(new Blockly.FieldDropdown(navFrameId), "FRAME_ID");
this.appendValueInput("ID")
.setCheck("Number")
.appendField("with ID")
@@ -268,7 +287,7 @@ Blockly.Blocks['mode'] = {
.appendField("current flight mode");
this.setOutput(true, "String");
this.setColour(COLOR_STATE);
this.setTooltip("");
this.setTooltip("Returns current flight mode (POSCTL, OFFBOARD, etc).");
this.setHelpUrl(DOCS_URL + '#' + this.type);
}
};
@@ -375,7 +394,7 @@ Blockly.Blocks['take_off'] = {
this.setPreviousStatement(true, null);
this.setNextStatement(true, null);
this.setColour(COLOR_FLIGHT);
this.setTooltip("Take off on desired altitude in meters");
this.setTooltip("Take off on desired altitude in meters.");
this.setHelpUrl(DOCS_URL + '#' + this.type);
}
};
@@ -391,7 +410,7 @@ Blockly.Blocks['land'] = {
this.setPreviousStatement(true, null);
this.setNextStatement(true, null);
this.setColour(COLOR_FLIGHT);
this.setTooltip("");
this.setTooltip("Land the drone.");
this.setHelpUrl(DOCS_URL + '#' + this.type);
}
};
@@ -400,10 +419,10 @@ Blockly.Blocks['global_position'] = {
init: function () {
this.appendDummyInput()
.appendField("current")
.appendField(new Blockly.FieldDropdown([["latitude", "LATITUDE"], ["longitude", "LONGITUDE"], ["altitude", "ALTITUDE"]]), "NAME");
.appendField(new Blockly.FieldDropdown([["latitude", "LAT"], ["longitude", "LON"], ["altitude", "ALT"]]), "FIELD");
this.setOutput(true, "Number");
this.setColour(230);
this.setTooltip("");
this.setColour(COLOR_STATE);
this.setTooltip("Returns current global position (latitude, longitude, altitude above the WGS 84 ellipsoid).");
this.setHelpUrl(DOCS_URL + '#' + this.type);
}
};

View File

@@ -52,6 +52,8 @@
<value name="X"><shadow type="math_number"><field name="NUM">0</field></shadow></value>
<value name="Y"><shadow type="math_number"><field name="NUM">0</field></shadow></value>
<value name="Z"><shadow type="math_number"><field name="NUM">0</field></shadow></value>
<value name="LAT"><shadow type="math_number"><field name="NUM">47.397503</field></shadow></value>
<value name="LON"><shadow type="math_number"><field name="NUM">8.544945</field></shadow></value>
<value name="SPEED"><shadow type="math_number"><field name="NUM">0.5</field></shadow></value>
<value name="ID"><shadow type="math_number"><field name="NUM">0</field></shadow></value>
</block>
@@ -85,6 +87,7 @@
<value name="ID"><shadow type="math_number"><field name="NUM">0</field></shadow></value>
</block>
<block type="get_attitude"></block>
<block type="global_position"></block>
<block type="distance">
<value name="X"><shadow type="math_number"><field name="NUM">0</field></shadow></value>
<value name="Y"><shadow type="math_number"><field name="NUM">0</field></shadow></value>

View File

@@ -39,7 +39,8 @@ var workspace = Blockly.inject('blockly', {
function readParams() {
return Promise.all([
ros.readParam('navigate_tolerance', true, 0.2),
ros.readParam('yaw_tolerance', true, 20),
ros.readParam('navigate_global_tolerance', true, 1),
ros.readParam('yaw_tolerance', true, 1),
ros.readParam('sleep_time', true, 0.2),
ros.readParam('confirm_run', true, true),
]);
@@ -210,7 +211,7 @@ function loadPrograms() {
updateChanged();
}, function(err) {
document.querySelector('.backend-fail').style.display = 'inline';
alert(`Error loading programs list.\n\nHave you enabled clover_blocks in clover.launch?`);
alert(`Error loading programs list.\n\nHave you enabled 'blocks' in clover.launch?`);
runButton.disabled = true;
})
}

View File

@@ -33,6 +33,18 @@ const NAVIGATE_WAIT = () => `\ndef navigate_wait(x=0, y=0, z=0, speed=0.5, frame
return
rospy.sleep(${params.sleep_time})\n`;
const NAVIGATE_GLOBAL_WAIT = () => `\ndef navigate_global_wait(lat, lon, z, speed=0.5):
res = navigate_global(lat=lat, lon=lon, z=z, yaw=float('inf'), speed=speed)
if not res.success:
raise Exception(res.message)
while not rospy.is_shutdown():
telem = get_telemetry(frame_id='navigate_target')
if math.sqrt(telem.x ** 2 + telem.y ** 2 + telem.z ** 2) < ${params.navigate_global_tolerance}:
return
rospy.sleep(${params.sleep_time})\n`;
const LAND_WAIT = () => `\ndef land_wait():
land()
while get_telemetry().armed:
@@ -68,6 +80,9 @@ function generateROSDefinitions() {
if (rosDefinitions.offboard) {
code += `get_telemetry = rospy.ServiceProxy('get_telemetry', srv.GetTelemetry)\n`;
code += `navigate = rospy.ServiceProxy('navigate', srv.Navigate)\n`;
if (rosDefinitions.navigateGlobal) {
code += `navigate_global = rospy.ServiceProxy('navigate_global', srv.NavigateGlobal)\n`;
}
if (rosDefinitions.setVelocity) {
code += `set_velocity = rospy.ServiceProxy('set_velocity', srv.SetVelocity)\n`;
}
@@ -94,6 +109,10 @@ function generateROSDefinitions() {
Blockly.Python.definitions_['import_math'] = 'import math';
code += NAVIGATE_WAIT();
}
if (rosDefinitions.navigateGlobalWait) {
Blockly.Python.definitions_['import_math'] = 'import math';
code += NAVIGATE_GLOBAL_WAIT();
}
if (rosDefinitions.landWait) {
code += LAND_WAIT();
}
@@ -161,24 +180,48 @@ Blockly.Python.navigate = function(block) {
let x = Blockly.Python.valueToCode(block, 'X', Blockly.Python.ORDER_NONE);
let y = Blockly.Python.valueToCode(block, 'Y', Blockly.Python.ORDER_NONE);
let z = Blockly.Python.valueToCode(block, 'Z', Blockly.Python.ORDER_NONE);
let frameId = buildFrameId(block);
let lat = Blockly.Python.valueToCode(block, 'LAT', Blockly.Python.ORDER_NONE);
let lon = Blockly.Python.valueToCode(block, 'LON', Blockly.Python.ORDER_NONE);
let wait = block.getFieldValue('WAIT') == 'TRUE';
let frameId = block.getFieldValue('FRAME_ID');
let speed = Blockly.Python.valueToCode(block, 'SPEED', Blockly.Python.ORDER_NONE);
let params = [`x=${x}`, `y=${y}`, `z=${z}`, `frame_id=${frameId}`, `speed=${speed}`];
simpleOffboard();
if (block.getFieldValue('WAIT') == 'TRUE') {
rosDefinitions.navigateWait = true;
// global coordinates
if (frameId.startsWith('GLOBAL')) {
rosDefinitions.navigateGlobal = true;
simpleOffboard();
return `navigate_wait(${params.join(', ')})\n`;
if (frameId == 'GLOBAL') {
z = `${z} + get_telemetry().alt - get_telemetry().z`;
}
if (wait) {
rosDefinitions.navigateGlobalWait = true;
simpleOffboard();
return `navigate_global_wait(lat=${lat}, lon=${lon}, z=${z}, speed=${speed})\n`;
} else {
return `navigate_global(lat=${lat}, lon=${lon}, z=${z}, yaw=float('inf'), speed=${speed})\n`;
}
} else {
if (frameId != 'body') {
params.push(`yaw=float('nan')`);
frameId = buildFrameId(block);
let params = [`x=${x}`, `y=${y}`, `z=${z}`, `frame_id=${frameId}`, `speed=${speed}`];
if (wait) {
rosDefinitions.navigateWait = true;
simpleOffboard();
return `navigate_wait(${params.join(', ')})\n`;
} else {
if (frameId != 'body') {
params.push(`yaw=float('nan')`);
}
return `navigate(${params.join(', ')})\n`;
}
return `navigate(${params.join(', ')})\n`;
}
}
@@ -315,6 +358,12 @@ Blockly.Python.get_attitude = function(block) {
return [code, Blockly.Python.ORDER_FUNCTION_CALL];
}
Blockly.Python.global_position = function(block) {
simpleOffboard();
var code = `get_telemetry().${block.getFieldValue('FIELD').toLowerCase()}`;
return [code, Blockly.Python.ORDER_FUNCTION_CALL];
}
Blockly.Python.distance = function(block) {
rosDefinitions.distance = true;
simpleOffboard();
@@ -415,7 +464,7 @@ Blockly.Python.led_count = function(block) {
function pigpio() {
Blockly.Python.definitions_['import_pigpio'] = 'import pigpio';
Blockly.Python.definitions_['init_pigpio'] = 'pi = pigpio.pi()';
Blockly.Python.definitions_['init_pigpio'] = 'pi = pigpio.pi()\nif not pi.connected: raise Exception(\'Cannot connect to pigpiod\')';
}
const GPIO_READ = `\ndef gpio_read(pin):

View File

@@ -14,7 +14,7 @@ You may provide additional parameters for `spawn_drone.launch` as well:
* `rangefinder` (*boolean*, default: *true*) - controls whether the drone will have a downward-facing laser rangefinder attached;
* `led` (*boolean*, default: *true*) - controls whether the drone will have a programmable LED strip (requires plugins from `clover_simulation`);
* `gps` (*boolean*, default: *true*) - controls whether the drone will have a simulated GPS attached (requires plugins from `sitl_gazebo`);
* `maintain_camera_rate` (*boolean*, default: *false*) - slow down the simultion to maintain camera publishing rate (internally changes the camera plugin from `libgazebo_ros_camera.so` to `libthrottling_camera.so` from [`clover_simulation`](../clover_simulation/README.md#throttling-camera-plugin-throttling_camera)).
* `maintain_camera_rate` (*boolean*, default: *false*) - slow down the simulation to maintain camera publishing rate (internally changes the camera plugin from `libgazebo_ros_camera.so` to `libthrottling_camera.so` from [`clover_simulation`](../clover_simulation/README.md#throttling-camera-plugin-throttling_camera)).
For example, in order to spawn a drone without a GPS module, you may use the following command:

View File

@@ -1,7 +1,7 @@
<launch>
<arg name="model" default="$(find clover_description)/urdf/drones/clover4.xacro"/>
<arg name="model" default="$(find clover_description)/urdf/clover/clover4.xacro"/>
<param name="robot_description" command="$(find xacro)/xacro.py $(arg model)"/>
<param name="robot_description" command="xacro $(arg model)"/>
<node name="rviz" pkg="rviz" type="rviz" required="true"/>
</launch>

View File

@@ -1,6 +1,6 @@
<package format="2">
<name>clover_description</name>
<version>0.21.1</version>
<version>0.23.0</version>
<description>The clover_description package provides URDF models of the Clover series of quadcopters.</description>
<maintainer email="sfalexrog@gmail.com">Alexey Rogachevskiy</maintainer>

View File

@@ -1,5 +1,5 @@
<?xml version="1.0"?>
<robot name="clover_camera" xmlns:xacro="http://ros.org/wiki/xacro">
<robot name="clover" xmlns:xacro="http://ros.org/wiki/xacro">
<xacro:arg name="main_camera" default="true"/>
<xacro:arg name="rangefinder" default="true"/>
@@ -8,10 +8,10 @@
<xacro:arg name="maintain_camera_rate" default="false"/>
<xacro:arg name="use_clover_physics" default="false"/>
<xacro:include filename="$(find clover_description)/urdf/clover/clover4_base.xacro" />
<xacro:include filename="$(find clover_description)/urdf/sensors/rpi_cam.urdf.xacro"/>
<xacro:include filename="$(find clover_description)/urdf/sensors/distance_sensor.urdf.xacro"/>
<xacro:include filename="$(find clover_description)/urdf/leds/led_strip.xacro"/>
<xacro:include filename="clover4_base.xacro" />
<xacro:include filename="../sensors/rpi_cam.urdf.xacro"/>
<xacro:include filename="../sensors/distance_sensor.urdf.xacro"/>
<xacro:include filename="../leds/led_strip.xacro"/>
<!-- Create camera plugin -->
<xacro:if value="$(arg main_camera)">
@@ -36,11 +36,17 @@
</xacro:if>
<xacro:if value="$(arg gps)">
<!-- Instantiate gps plugin. -->
<xacro:gps_plugin_macro
namespace="${namespace}"
gps_noise="true"
/>
<gazebo>
<include>
<uri>model://gps</uri>
<pose>0.1 0 0 0 0 0</pose>
<name>gps0</name>
</include>
<joint name='gps0_joint' type='fixed'>
<child>gps0::link</child>
<parent>base_link</parent>
</joint>
</gazebo>
</xacro:if>
</robot>

View File

@@ -1,40 +1,15 @@
<?xml version="1.0"?>
<robot name="clover" xmlns:xacro="http://ros.org/wiki/xacro">
<!-- Properties that can be assigned at build time as arguments.
Is there a reason not to make all properties arguments?
-->
<xacro:arg name='name' default='iris' />
<xacro:arg name='mavlink_addr' default='INADDR_ANY' />
<xacro:arg name='mavlink_udp_port' default='14560' />
<xacro:arg name='mavlink_tcp_port' default='4560' />
<xacro:arg name='serial_enabled' default='false' />
<xacro:arg name='serial_device' default='/dev/ttyACM0' />
<xacro:arg name='baudrate' default='921600' />
<xacro:arg name='qgc_addr' default='INADDR_ANY' />
<xacro:arg name='qgc_udp_port' default='14550' />
<xacro:arg name='sdk_addr' default='INADDR_ANY' />
<xacro:arg name='sdk_udp_port' default='14540' />
<xacro:arg name='hil_mode' default='false' />
<xacro:arg name='hil_state_level' default='false' />
<xacro:arg name='send_vision_estimation' default='false' />
<xacro:arg name='send_odometry' default='false' />
<xacro:arg name='enable_lockstep' default='true' />
<xacro:arg name='use_tcp' default='true' />
<xacro:arg name='vehicle_is_tailsitter' default='false' />
<xacro:arg name='visual_material' default='DarkGrey' />
<xacro:arg name='enable_mavlink_interface' default='true' />
<xacro:arg name='enable_wind' default='false' />
<!-- The following causes segfault with multiple vehicles if defaults to true!!! -->
<xacro:arg name='enable_ground_truth' default='false' />
<xacro:arg name='enable_logging' default='false' />
<xacro:arg name='log_file' default='iris' />
<xacro:arg name='log_file' default='clover' />
<!-- macros for gazebo plugins, sensors -->
<xacro:include filename="$(find clover_description)/urdf/component_snippets.urdf.xacro" />
<xacro:include filename="../component_snippets.urdf.xacro" />
<!-- Instantiate iris "mechanics" -->
<xacro:include filename="$(find clover_description)/urdf/clover/clover4_physics.xacro" />
<xacro:include filename="clover4_physics.xacro" />
<xacro:if value="$(arg enable_wind)">
<xacro:wind_plugin_macro
@@ -49,126 +24,8 @@
/>
</xacro:if>
<!-- Instantiate magnetometer plugin. -->
<xacro:magnetometer_plugin_macro
namespace="${namespace}"
pub_rate="20"
noise_density="0.0004"
random_walk="0.0000064"
bias_correlation_time="600"
mag_topic="/mag"
>
</xacro:magnetometer_plugin_macro>
<!-- Instantiate barometer plugin. -->
<xacro:barometer_plugin_macro
namespace="${namespace}"
pub_rate="10"
baro_topic="/baro"
>
</xacro:barometer_plugin_macro>
<xacro:if value="$(arg enable_mavlink_interface)">
<!-- Instantiate mavlink telemetry interface. -->
<xacro:mavlink_interface_macro
namespace="${namespace}"
imu_sub_topic="/imu"
gps_sub_topic="/gps"
mag_sub_topic="/mag"
baro_sub_topic="/baro"
mavlink_addr="$(arg mavlink_addr)"
mavlink_udp_port="$(arg mavlink_udp_port)"
mavlink_tcp_port="$(arg mavlink_tcp_port)"
serial_enabled="$(arg serial_enabled)"
serial_device="$(arg serial_device)"
baudrate="$(arg baudrate)"
qgc_addr="$(arg qgc_addr)"
qgc_udp_port="$(arg qgc_udp_port)"
sdk_addr="$(arg sdk_addr)"
sdk_udp_port="$(arg sdk_udp_port)"
hil_mode="$(arg hil_mode)"
hil_state_level="$(arg hil_state_level)"
vehicle_is_tailsitter="$(arg vehicle_is_tailsitter)"
send_vision_estimation="$(arg send_vision_estimation)"
send_odometry="$(arg send_odometry)"
enable_lockstep="$(arg enable_lockstep)"
use_tcp="$(arg use_tcp)"
>
</xacro:mavlink_interface_macro>
</xacro:if>
<!-- Mount an ADIS16448 IMU. -->
<xacro:imu_plugin_macro
namespace="${namespace}"
imu_suffix=""
parent_link="base_link"
imu_topic="/imu"
mass_imu_sensor="0.015"
gyroscope_noise_density="0.0003394"
gyroscopoe_random_walk="0.000038785"
gyroscope_bias_correlation_time="1000.0"
gyroscope_turn_on_bias_sigma="0.0087"
accelerometer_noise_density="0.004"
accelerometer_random_walk="0.006"
accelerometer_bias_correlation_time="300.0"
accelerometer_turn_on_bias_sigma="0.1960"
>
<inertia ixx="0.00001" ixy="0.0" ixz="0.0" iyy="0.00001" iyz="0.0" izz="0.00001" />
<origin xyz="0 0 0" rpy="0 0 0" />
</xacro:imu_plugin_macro>
<xacro:if value="$(arg enable_ground_truth)">
<!-- Mount an IMU providing ground truth. -->
<xacro:imu_plugin_macro
namespace="${namespace}"
imu_suffix="gt"
parent_link="base_link"
imu_topic="ground_truth/imu"
mass_imu_sensor="0.00001"
gyroscope_noise_density="0.0"
gyroscopoe_random_walk="0.0"
gyroscope_bias_correlation_time="1000.0"
gyroscope_turn_on_bias_sigma="0.0"
accelerometer_noise_density="0.0"
accelerometer_random_walk="0.0"
accelerometer_bias_correlation_time="300.0"
accelerometer_turn_on_bias_sigma="0.0"
>
<inertia ixx="0.00001" ixy="0.0" ixz="0.0" iyy="0.00001" iyz="0.0" izz="0.00001" />
<origin xyz="0 0 0" rpy="0 0 0" />
</xacro:imu_plugin_macro>
<!-- Mount a generic odometry sensor providing ground truth. -->
<xacro:odometry_plugin_macro
namespace="${namespace}/ground_truth"
odometry_sensor_suffix="gt"
parent_link="base_link"
pose_topic="pose"
pose_with_covariance_topic="pose_with_covariance"
position_topic="position"
transform_topic="transform"
odometry_topic="odometry"
parent_frame_id="world"
mass_odometry_sensor="0.00001"
measurement_divisor="1"
measurement_delay="0"
unknown_delay="0.0"
noise_normal_position="0 0 0"
noise_normal_quaternion="0 0 0"
noise_normal_linear_velocity="0 0 0"
noise_normal_angular_velocity="0 0 0"
noise_uniform_position="0 0 0"
noise_uniform_quaternion="0 0 0"
noise_uniform_linear_velocity="0 0 0"
noise_uniform_angular_velocity="0 0 0"
enable_odometry_map="false"
odometry_map=""
image_scale=""
>
<inertia ixx="0.00001" ixy="0.0" ixz="0.0" iyy="0.00001" iyz="0.0" izz="0.00001" /> <!-- [kg.m^2] [kg.m^2] [kg.m^2] [kg.m^2] [kg.m^2] [kg.m^2] -->
<origin xyz="0.0 0.0 0.0" rpy="0.0 0.0 0.0" />
</xacro:odometry_plugin_macro>
</xacro:if>
<!-- Gazebo plugins -->
<xacro:include filename="$(find clover_description)/urdf/clover/clover4_gazebo.xacro" />
<xacro:if value="$(arg enable_logging)">
<!-- Instantiate a logger -->

View File

@@ -0,0 +1,183 @@
<?xml version="1.0"?>
<!-- Reference: https://github.com/PX4/PX4-SITL_gazebo/blob/7505aee97d2d3112fb2bd95198946345c6fa0b07/models/iris/iris.sdf.jinja#L430 -->
<robot xmlns:xacro="http://ros.org/wiki/xacro">
<!-- IMU link -->
<link name="/imu_link">
<inertial>
<inertia ixx="0.00001" ixy="0.0" ixz="0.0" iyy="0.00001" iyz="0.0" izz="0.00001"/>
<mass value="0.015"/>
<!-- [kg] -->
<origin rpy="0 0 0" xyz="0 0 0"/>
</inertial>
</link>
<!-- IMU joint -->
<joint name="/imu_joint" type="fixed">
<origin rpy="0 0 0" xyz="0 0 0"/>
<parent link="base_link"/>
<child link="/imu_link"/>
<!-- <limit upper="0" lower="0" effort="0" velocity="0" /> -->
</joint>
<gazebo reference="/imu_joint">
<disableFixedJointLumping>true</disableFixedJointLumping>
<preserveFixedJoint>true</preserveFixedJoint>
</gazebo>
<gazebo>
<plugin name='groundtruth_plugin' filename='libgazebo_groundtruth_plugin.so'>
<robotNamespace/>
</plugin>
<plugin name='magnetometer_plugin' filename='libgazebo_magnetometer_plugin.so'>
<robotNamespace/>
<pubRate>100</pubRate>
<noiseDensity>0.0004</noiseDensity>
<randomWalk>6.4e-06</randomWalk>
<biasCorrelationTime>600</biasCorrelationTime>
<magTopic>/mag</magTopic>
</plugin>
<plugin name='barometer_plugin' filename='libgazebo_barometer_plugin.so'>
<robotNamespace/>
<pubRate>50</pubRate>
<baroTopic>/baro</baroTopic>
<baroDriftPaPerSec>0</baroDriftPaPerSec>
</plugin>
<plugin name='mavlink_interface' filename='libgazebo_mavlink_interface.so'>
<robotNamespace/>
<imuSubTopic>/imu</imuSubTopic>
<magSubTopic>/mag</magSubTopic>
<baroSubTopic>/baro</baroSubTopic>
<mavlink_addr>INADDR_ANY</mavlink_addr>
<mavlink_tcp_port>4560</mavlink_tcp_port>
<mavlink_udp_port>14560</mavlink_udp_port>
<serialEnabled>false</serialEnabled>
<serialDevice>/dev/ttyACM0</serialDevice>
<baudRate>921600</baudRate>
<qgc_addr>INADDR_ANY</qgc_addr>
<qgc_udp_port>14550</qgc_udp_port>
<sdk_addr>INADDR_ANY</sdk_addr>
<sdk_udp_port>14540</sdk_udp_port>
<hil_mode>false</hil_mode>
<hil_state_level>0</hil_state_level>
<send_vision_estimation>0</send_vision_estimation>
<send_odometry>1</send_odometry>
<enable_lockstep>1</enable_lockstep>
<use_tcp>1</use_tcp>
<motorSpeedCommandPubTopic>/gazebo/command/motor_speed</motorSpeedCommandPubTopic>
<control_channels>
<channel name='rotor1'>
<input_index>0</input_index>
<input_offset>0</input_offset>
<input_scaling>1000</input_scaling>
<zero_position_disarmed>0</zero_position_disarmed>
<zero_position_armed>100</zero_position_armed>
<joint_control_type>velocity</joint_control_type>
</channel>
<channel name='rotor2'>
<input_index>1</input_index>
<input_offset>0</input_offset>
<input_scaling>1000</input_scaling>
<zero_position_disarmed>0</zero_position_disarmed>
<zero_position_armed>100</zero_position_armed>
<joint_control_type>velocity</joint_control_type>
</channel>
<channel name='rotor3'>
<input_index>2</input_index>
<input_offset>0</input_offset>
<input_scaling>1000</input_scaling>
<zero_position_disarmed>0</zero_position_disarmed>
<zero_position_armed>100</zero_position_armed>
<joint_control_type>velocity</joint_control_type>
</channel>
<channel name='rotor4'>
<input_index>3</input_index>
<input_offset>0</input_offset>
<input_scaling>1000</input_scaling>
<zero_position_disarmed>0</zero_position_disarmed>
<zero_position_armed>100</zero_position_armed>
<joint_control_type>velocity</joint_control_type>
</channel>
<channel name='rotor5'>
<input_index>4</input_index>
<input_offset>1</input_offset>
<input_scaling>324.6</input_scaling>
<zero_position_disarmed>0</zero_position_disarmed>
<zero_position_armed>0</zero_position_armed>
<joint_control_type>velocity</joint_control_type>
<joint_control_pid>
<p>0.1</p>
<i>0</i>
<d>0</d>
<iMax>0.0</iMax>
<iMin>0.0</iMin>
<cmdMax>2</cmdMax>
<cmdMin>-2</cmdMin>
</joint_control_pid>
<joint_name>zephyr_delta_wing::propeller_joint</joint_name>
</channel>
<channel name='rotor6'>
<input_index>5</input_index>
<input_offset>0</input_offset>
<input_scaling>0.524</input_scaling>
<zero_position_disarmed>0</zero_position_disarmed>
<zero_position_armed>0</zero_position_armed>
<joint_control_type>position</joint_control_type>
<joint_name>zephyr_delta_wing::flap_left_joint</joint_name>
<joint_control_pid>
<p>10.0</p>
<i>0</i>
<d>0</d>
<iMax>0</iMax>
<iMin>0</iMin>
<cmdMax>20</cmdMax>
<cmdMin>-20</cmdMin>
</joint_control_pid>
</channel>
<channel name='rotor7'>
<input_index>6</input_index>
<input_offset>0</input_offset>
<input_scaling>0.524</input_scaling>
<zero_position_disarmed>0</zero_position_disarmed>
<zero_position_armed>0</zero_position_armed>
<joint_control_type>position</joint_control_type>
<joint_name>zephyr_delta_wing::flap_right_joint</joint_name>
<joint_control_pid>
<p>10.0</p>
<i>0</i>
<d>0</d>
<iMax>0</iMax>
<iMin>0</iMin>
<cmdMax>20</cmdMax>
<cmdMin>-20</cmdMin>
</joint_control_pid>
</channel>
<channel name='rotor8'>
<input_index>7</input_index>
<input_offset>0</input_offset>
<input_scaling>0.524</input_scaling>
<zero_position_disarmed>0</zero_position_disarmed>
<zero_position_armed>0</zero_position_armed>
<joint_control_type>position</joint_control_type>
</channel>
</control_channels>
</plugin>
<static>0</static>
<plugin name='rotors_gazebo_imu_plugin' filename='libgazebo_imu_plugin.so'>
<robotNamespace/>
<linkName>/imu_link</linkName>
<imuTopic>/imu</imuTopic>
<gyroscopeNoiseDensity>0.00018665</gyroscopeNoiseDensity>
<gyroscopeRandomWalk>3.8785e-05</gyroscopeRandomWalk>
<gyroscopeBiasCorrelationTime>1000.0</gyroscopeBiasCorrelationTime>
<gyroscopeTurnOnBiasSigma>0.0087</gyroscopeTurnOnBiasSigma>
<accelerometerNoiseDensity>0.00186</accelerometerNoiseDensity>
<accelerometerRandomWalk>0.006</accelerometerRandomWalk>
<accelerometerBiasCorrelationTime>300.0</accelerometerBiasCorrelationTime>
<accelerometerTurnOnBiasSigma>0.196</accelerometerTurnOnBiasSigma>
</plugin>
</gazebo>
</robot>

View File

@@ -6,7 +6,7 @@
-->
<robot name="iris" xmlns:xacro="http://ros.org/wiki/xacro">
<robot name="clover" xmlns:xacro="http://ros.org/wiki/xacro">
<!-- Properties -->
<xacro:property name="namespace" value="" />
<xacro:property name="rotor_velocity_slowdown_sim" value="10" />
@@ -35,7 +35,7 @@
<xacro:property name="sqrt2" value="1.4142135623730951" />
<xacro:property name="rotor_drag_coefficient" value="1.75e-04" />
<xacro:property name="rolling_moment_coefficient" value="0.000001" />
<xacro:property name="color" value="$(arg visual_material)" />
<xacro:property name="color" value="DarkGrey" />
<!-- Property Blocks -->
<!-- Clover body inertia -->
@@ -84,7 +84,7 @@
</xacro:unless>
<!-- Included URDF Files -->
<xacro:include filename="$(find clover_description)/urdf/clover/clover4_macros.xacro" />
<xacro:include filename="clover4_macros.xacro" />
<!-- Instantiate multirotor_base_macro once -->
<xacro:clover4_base_macro

View File

@@ -64,6 +64,12 @@
<!-- <gazebo>
<static>true</static>
</gazebo> -->
<gazebo>
<plugin name="${name}_ros_controller" filename="libsim_leds_controller.so">
<robotNamespace></robotNamespace>
<ledCount>${led_count}</ledCount>
</plugin>
</gazebo>
</xacro:macro>
</robot>

View File

@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<robot name="rpi_camera" xmlns:xacro="http://ros.org/wiki/xacro">
<xacro:include filename="$(find clover_description)/urdf/camera_sensor.urdf.xacro"/>
<xacro:include filename="../camera_sensor.urdf.xacro"/>
<xacro:macro name="distance_sensor" params="name:=lidar_vl53l1x parent x:=0 y:=0 z:=0 roll:=0 pitch:=0 yaw:=0 range_min:=0.01 range_max:=4.0 resolution:=0.01 rate:=10">
<xacro:macro name="distance_sensor" params="name:=lidar_vl53l1x parent x:=0 y:=0 z:=0 roll:=0 pitch:=0 yaw:=0 range_min:=0.01 range_max:=4.0 resolution:=0.01 rate:=10 fov:=0.471239">
<joint name="${name}_joint" type="fixed">
<origin xyz="${x} ${y} ${z}"
rpy="${roll} ${pitch} ${yaw}"/>
@@ -58,7 +58,7 @@
<topicName>/rangefinder/range</topicName>
<frameName>rangefinder</frameName>
<radiation>infrared</radiation>
<fov>0.01</fov>
<fov>${fov}</fov>
<gaussianNoise>0.001</gaussianNoise>
<updateRate>${rate}</updateRate>
<min_distance>${range_min}</min_distance>

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<robot name="pinhole_camera" xmlns:xacro="http://ros.org/wiki/xacro">
<xacro:include filename="$(find clover_description)/urdf/camera_sensor.urdf.xacro"/>
<xacro:include filename="../camera_sensor.urdf.xacro"/>
<xacro:camera_sensor name="main_camera" width="640" height="480" rate="30" horizontal_fov="${120.0*pi/180.0}"/>
</robot>

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