Compare commits

...

611 Commits

Author SHA1 Message Date
Oleg Kalachev
621b70711d Typo 2024-06-18 00:52:59 +03:00
Oleg Kalachev
2a2fbe3bed Add vision_msgs package 2024-06-18 00:52:30 +03:00
Oleg Kalachev
add15ac2fe Remove libcamera_ros from dependencies as this package is not commonly available 2024-06-16 13:15:49 +03:00
Oleg Kalachev
abe6f5cf0f Add rosdep key for libcamera_ros package 2024-06-16 12:22:27 +03:00
Oleg Kalachev
b04d8311bd Add tool for rescaling camera info file
Generate fisheye_cam camera info for all the
different resolutions
2024-06-16 08:33:25 +03:00
Oleg Kalachev
b7e8acefb4 Use libcamera_ros camera driver
v4l2 doesn't work on Bookworm with RPi camera
2024-06-16 08:32:44 +03:00
Oleg Kalachev
caa974b71c Fix running rosbridge_suite adding ROS_OV_OVERRIDE env variable
It gives error 'Could not detect OS' (from roslib) on Bookworm without that
2024-05-22 14:17:59 +03:00
Oleg Kalachev
3f00001d9a Merge branch 'master' into bookworm 2024-05-09 20:40:34 +03:00
Oleg Kalachev
1a9e689b8c Disable exiting on error in init scripts 2024-05-08 20:09:09 +03:00
Oleg Kalachev
1e2f92f165 Fix Wi-Fi creation 2024-05-08 20:07:10 +03:00
Oleg Kalachev
d087d1c953 Try to fix 2024-05-07 22:26:25 +03:00
Oleg Kalachev
01c495597b Upload image to artifacts if this is not master 2024-05-07 21:39:42 +03:00
Oleg Kalachev
8c8fdcda54 Try to fix Butterfly installation 2024-05-07 21:33:06 +03:00
Oleg Kalachev
fbe353b767 Bring back CRYPTOGRAPHY_DONT_BUILD_RUST while installing Butterfly 2024-05-07 02:24:48 +03:00
Oleg Kalachev
4748d34fa2 Fix pi home directory permissions to make Monkey work 2024-05-07 01:53:01 +03:00
Oleg Kalachev
afe46ab7e5 Clean initial scripts, fix UART setup 2024-05-07 01:01:40 +03:00
Oleg Kalachev
d7e8d22bf6 Fix ws281x version checks as it was updated 2024-04-28 22:15:09 +03:00
Oleg Kalachev
ee4165ea26 Use 7z image archive to reduce size 2024-04-28 00:06:36 +03:00
Oleg Kalachev
0a265db50e Update upload-artifact action version 2024-04-28 00:06:21 +03:00
Oleg Kalachev
04d4dcaa1c Compress image to zip using 7z with ultra level to reduce archive size 2024-04-27 22:14:45 +03:00
Oleg Kalachev
6adfc038a2 Print size of image archive in human style 2024-04-27 22:08:09 +03:00
Oleg Kalachev
5877235e9a Fix adding pi user 2024-04-27 16:19:15 +03:00
Oleg Kalachev
b70c4387f7 Fix 2024-04-27 11:26:51 +03:00
Oleg Kalachev
00fb98c0c2 Create Wi-Fi AP using NetworkManager 2024-04-27 00:55:11 +03:00
Oleg Kalachev
249800bc87 Fix config.txt path to the new 2024-04-27 00:54:43 +03:00
Oleg Kalachev
465bdd743b Enable ssh using raspi-config 2024-04-27 00:54:18 +03:00
Oleg Kalachev
028d1ecd82 Create pi user which is not created by default now 2024-04-27 00:54:06 +03:00
Oleg Kalachev
70439f172d selfcheck.py: address situation when individual cells voltage is unknown
When length of cell_voltage array is 1, it means that cell voltage is
unknown and overall voltage is reported instead. Consider this situation
gathering the number of cells from PX4 parameters.
2024-04-25 22:20:01 +03:00
Oleg Kalachev
dacaa8ebde docs: minor fixes 2024-04-25 11:12:29 +03:00
Oleg Kalachev
a5309765f1 Some changes to markdownlint config 2024-04-25 11:04:55 +03:00
Oleg Kalachev
2e86ed199a Implement possibility to upload image from repo not named clover 2024-04-25 10:50:02 +03:00
Oleg Kalachev
ad14822684 Fix running aruco_detect without aruco_map enabled
use_map_markers parameter blocked aruco_detect from running
2024-04-25 10:48:20 +03:00
Oleg Kalachev
4517cdf595 Merge branch 'cleanup-build' into bookworm 2024-04-18 07:46:34 +03:00
Oleg Kalachev
dc2293e960 More build scripts cleanup 2024-04-18 06:51:11 +03:00
Oleg Kalachev
1b191d9cf0 Major build scripts cleanup
Including removing echo_stamp, which is not needed
2024-04-18 06:49:46 +03:00
Oleg Kalachev
41b6311d9e Enable echo in image-init, move disabling ld preload to the top 2024-04-18 06:17:31 +03:00
Oleg Kalachev
b39fa10ba9 One more fix to working with fstab 2024-04-18 06:13:12 +03:00
Oleg Kalachev
2c62fa3583 Fix work with fstab 2024-04-18 06:08:50 +03:00
Oleg Kalachev
8dcdae1259 Remove editing /boot/firmware/cmdline.txt 2024-04-18 05:36:19 +03:00
Oleg Kalachev
be27a6d3a3 Correct cmdline.txt and fstab editing for the new OS version 2024-04-18 05:04:43 +03:00
Oleg Kalachev
d8c26c114e Add installation of xxd which is needed for generating SSID 2024-04-18 05:04:12 +03:00
Oleg Kalachev
ef43b04a8b Update Raspberry Pi OS to 2024-03-15 2024-04-17 08:27:27 +03:00
Oleg Kalachev
960a105cba Disable full remove of apt cache 2024-04-17 06:48:59 +03:00
Oleg Kalachev
4fcd0eaf2f Cleanup apt including all caches in the end of the build 2024-04-16 04:06:30 +03:00
Oleg Kalachev
f9026fa0e3 Cleanup /tmp in image build 2024-04-15 07:04:41 +03:00
Oleg Kalachev
cd5a9d1176 Cleanup pip cache in image build 2024-04-15 07:03:59 +03:00
Oleg Kalachev
cfe430676b Clean npm cache after building docs 2024-04-15 07:02:36 +03:00
Oleg Kalachev
3bccfd8c06 Do apt-get clean after autoremove 2024-04-15 06:45:59 +03:00
Oleg Kalachev
fef8a3299e Try to use autoremove to reduce image size 2024-04-14 04:12:14 +03:00
Oleg Kalachev
63deabe01d Code cleanup 2024-04-14 04:11:21 +03:00
Oleg Kalachev
b021afff7e Make apt-get clean verbose 2024-04-14 04:10:45 +03:00
Oleg Kalachev
b3d28723d7 Upload image artifact on on manual workflow run 2024-04-14 04:08:03 +03:00
Oleg Kalachev
22c9d238d4 Don't install boost explicitly 2024-04-14 01:42:28 +03:00
Oleg Kalachev
eb1a6f4349 Upload image to artifacts 2024-04-14 01:42:06 +03:00
Oleg Kalachev
a41004d294 Merge remote-tracking branch 'origin/master' into bookworm 2024-04-13 23:56:50 +03:00
Oleg Kalachev
e788bf3d4d Merge noetic-bookworm into noetic-rosdep-clover, remove noetic-bullseye 2024-04-13 23:54:40 +03:00
Oleg Kalachev
6e8a0d14b3 Disable back catkin_test_results, cleanup 2024-04-13 23:29:33 +03:00
Oleg Kalachev
78465fc93f Replace ptvsd with debugpy
ptvsd is deprecated
2024-04-13 22:43:57 +03:00
Oleg Kalachev
bf8482a6ce Enable catkin_test_results call 2024-04-13 22:39:27 +03:00
Oleg Kalachev
4659772d40 Cleanup 2024-04-13 22:38:52 +03:00
Oleg Kalachev
62cade7404 Add correct librocos resolve to rosdep 2024-04-13 22:38:35 +03:00
Oleg Kalachev
fa7a05bdb1 Bring back printing largest installed packages 2024-04-13 22:36:48 +03:00
Oleg Kalachev
936efa985d Make clover rosdep file more priority to fix build 2024-04-13 21:32:55 +03:00
Oleg Kalachev
5580bd754c Don't run roscore in image validate script 2024-04-13 21:31:38 +03:00
Oleg Kalachev
50c295ed3e Wait until roscore is terminated before script end 2024-04-13 20:10:12 +03:00
Oleg Kalachev
0f76999214 Stop roscore using killall before ending the validate script 2024-04-13 20:08:55 +03:00
Oleg Kalachev
f7ae2c21a4 Add some sleep to try to fix busy error 2024-04-13 19:16:36 +03:00
Oleg Kalachev
c55e0cb7e1 Fix geographiclib dependency 2024-04-13 19:06:44 +03:00
Oleg Kalachev
b8344dbb84 Add dictionary parameter to aruco.launch 2024-04-13 16:57:29 +03:00
Oleg Kalachev
fae431c890 Disable printing largest packages 2024-04-13 03:55:41 +03:00
Oleg Kalachev
fdbd989d0e Move recovering ld.so.preload file at the end of the build 2024-04-13 03:55:09 +03:00
Oleg Kalachev
649e596479 Disable compressed_image_transport 2024-04-12 23:50:38 +03:00
Oleg Kalachev
d9d85f5979 Skip python3-catkin-pkg-modules and python3-rosdep-modules via rosdep file 2024-04-12 20:54:18 +03:00
Oleg Kalachev
bcf355acf9 Ignore python3-rosdep-modules dependency 2024-04-12 20:23:36 +03:00
Oleg Kalachev
89912c67b8 Ignore python3-catkin-pkg-modules dependency 2024-04-12 20:20:53 +03:00
Oleg Kalachev
05c773b297 Bring back rosdep init 2024-04-12 19:39:28 +03:00
Oleg Kalachev
51d6d78b3d Fix 2024-04-12 17:29:54 +03:00
Oleg Kalachev
dd5dff7922 Bring back installing rosdep with pip 2024-04-12 16:44:04 +03:00
Oleg Kalachev
94c102b5e0 Install catkin before sourcing setup.bash 2024-04-12 01:33:32 +03:00
Oleg Kalachev
70026fef74 Disable rosdep init 2024-04-12 01:16:43 +03:00
Oleg Kalachev
41f81f35e6 Don't install rosdep using pip 2024-04-12 00:25:52 +03:00
Oleg Kalachev
e7ae321b54 Add ros-base installation 2024-04-11 22:56:50 +03:00
Oleg Kalachev
5b35df41b1 Print out environment variables right after setup.bash call 2024-04-11 22:21:53 +03:00
Oleg Kalachev
51af09bde8 Print out environment variables for debug 2024-04-11 21:06:06 +03:00
Oleg Kalachev
3d17433ba8 Print catkin_ws contents for debug 2024-04-11 19:42:48 +03:00
Oleg Kalachev
2e174b2bc0 Print out caktin path 2024-04-11 16:42:57 +03:00
Oleg Kalachev
9bff8b47dc Start roscore directly instead of using systemd 2024-04-11 16:41:52 +03:00
Oleg Kalachev
cceb62cfc4 Remove butterfly installation workarounds 2024-04-11 05:45:14 +03:00
Oleg Kalachev
4e111fa299 Check butterfly installation via butterfly.server.py 2024-04-11 04:03:45 +03:00
Oleg Kalachev
ba7f15b1d7 Allow pip usage globally 2024-04-11 03:03:34 +03:00
Oleg Kalachev
30e9d111ec Fix pip installation check 2024-04-11 03:01:14 +03:00
Oleg Kalachev
8087bb26c9 Re-arrange installed software list 2024-04-11 03:00:27 +03:00
Oleg Kalachev
3ef0065ab2 Bring back Monkey setup 2024-04-11 02:57:27 +03:00
Oleg Kalachev
d0fa541d13 Validate espeak is installed 2024-04-11 02:50:34 +03:00
Oleg Kalachev
31dde130b4 Update cv_camera version for check 2024-04-11 02:49:27 +03:00
Oleg Kalachev
bced762f77 Bring back ipython 2024-04-11 02:49:15 +03:00
Oleg Kalachev
2d8ac74f8d Remove Python 2 tests 2024-04-11 01:55:30 +03:00
Oleg Kalachev
0e2f1b9ac1 Bring back rosbridge-suite 2024-04-11 01:02:55 +03:00
Oleg Kalachev
f4733e7209 Remove not existing wno rosbridge-suite package installation 2024-04-11 00:58:13 +03:00
Oleg Kalachev
2cfdf36768 Bring mjpeg-streamer installation back 2024-04-11 00:29:06 +03:00
Oleg Kalachev
24961393a5 Add missing pluginlib dependency to aruco_pose 2024-04-10 21:21:33 +03:00
Oleg Kalachev
f16273c1ae Add rosserial_server to noetic-bookworm.yaml 2024-04-10 21:21:22 +03:00
Oleg Kalachev
9f20448831 Fix shellcheck errors 2024-04-10 21:21:09 +03:00
Oleg Kalachev
343a09a242 Remove printing out /opt/ros 2024-04-10 01:11:05 +03:00
Oleg Kalachev
9355790333 Print out /opt/ros contents 2024-04-05 19:42:25 +03:00
Oleg Kalachev
d07a55334f Change a bit the way of enabling pip packages installation 2024-04-05 19:42:17 +03:00
Oleg Kalachev
72197392fa Enable installing packages with pip 2024-04-05 19:14:55 +03:00
Oleg Kalachev
c91da69552 Add some correct dependencies resolves for bookworm 2024-04-05 18:47:22 +03:00
Oleg Kalachev
8e09a1243a Bring back monkey 2024-04-05 18:47:01 +03:00
Oleg Kalachev
f6594dee62 Try to fix rosdep update another way 2024-04-05 16:00:19 +03:00
Oleg Kalachev
a3fad60aae Try to fix rosdep update from pi: enable preserve-env 2024-04-05 15:31:47 +03:00
Oleg Kalachev
e548cade90 Try to fix rosdep update from pi 2024-04-05 14:54:54 +03:00
Oleg Kalachev
407ab40614 Bring back installation python's pigpio, systemd, espeak, and python3-dev 2024-04-05 14:37:45 +03:00
Oleg Kalachev
90d447734d Set ROS_OS_OVERRIDE 2024-04-05 14:33:37 +03:00
Oleg Kalachev
f5a7dc2b94 Disable packages.ros.org and install rosdep using pip3 2024-04-05 14:21:24 +03:00
Oleg Kalachev
2085b68cec Bring back ws281x 2024-04-05 02:39:21 +03:00
Oleg Kalachev
c5bf88d858 Don't install ws281 for now 2024-04-05 00:39:35 +03:00
Oleg Kalachev
5cf42ee650 Disable monkey 2024-04-04 23:35:48 +03:00
Oleg Kalachev
a5a87f8590 Disable mjpeg-streamer installation 2024-04-04 23:21:28 +03:00
Oleg Kalachev
8c119b8bc9 Use packages.ros.org/ros/ubuntu buster 2024-04-04 23:11:39 +03:00
Oleg Kalachev
b1f36bf966 Disable package.ros.org 2024-04-04 23:06:48 +03:00
Oleg Kalachev
100eca7210 Correctly add coex package source considering Debian release 2024-04-04 23:02:57 +03:00
Oleg Kalachev
3a53b86707 Install liboost-all-dev 2024-04-04 21:51:37 +03:00
Oleg Kalachev
ed623c2e97 Correct debian version codename 2024-04-04 21:16:27 +03:00
Oleg Kalachev
956490b9bc Disable holding opencv 4.2 compatible packages 2024-04-04 21:01:09 +03:00
Oleg Kalachev
5790d4de01 Fix used rosdep file 2024-04-04 20:44:47 +03:00
Oleg Kalachev
bea3149ac5 Fix 2024-04-04 20:29:39 +03:00
Oleg Kalachev
8c56da44fd --break-system-packages 2024-04-04 20:17:17 +03:00
Oleg Kalachev
26bff21253 Don't install Python 2's pip 2024-04-04 19:54:31 +03:00
Oleg Kalachev
eedb028232 Install pip using apt 2024-04-04 19:36:52 +03:00
Oleg Kalachev
c1a7fc7765 Echo commands while installing software 2024-04-04 19:18:01 +03:00
Oleg Kalachev
4ccd5003ad Disable python-pigpio installation 2024-04-03 00:48:39 +03:00
Oleg Kalachev
9dde4d0c51 Disable libjpeg8 install 2024-04-03 00:43:00 +03:00
Oleg Kalachev
23c795f21f Disable ipython installation 2024-04-02 23:52:40 +03:00
Oleg Kalachev
05fc8ff626 Disable some missing packages installation 2024-04-02 23:46:00 +03:00
Oleg Kalachev
380b395c72 Remove cmake=3.13.4-1 installation 2024-04-02 23:08:21 +03:00
Oleg Kalachev
b01f090453 Switch to use prebuilt packages 2024-04-02 23:00:40 +03:00
Qandra Si
3b7242f3d6 docs: Add advanced SSH article (SSH keys) (#503)
---------

Co-authored-by: Oleg Kalachev <okalachev@gmail.com>
2024-03-12 19:33:55 +03:00
Oleg Kalachev
f1e9e50617 Install future python module 2024-03-08 05:03:37 +03:00
Oleg Kalachev
f0048f804a Set ROS_PYTHON_VERSION to 3 2024-03-08 04:35:42 +03:00
Oleg Kalachev
857d134919 Install cmake 2024-03-08 04:07:20 +03:00
Oleg Kalachev
8f47a26a1b Add Poco library 2024-03-08 03:23:53 +03:00
Oleg Kalachev
77b195c203 Merge branch 'master' into bookworm 2024-03-08 03:22:53 +03:00
Oleg Kalachev
cfeff0c74d Use installed GeographicLib CMake file
As it's done in mavros https://github.com/mavlink/mavros/pull/1775
2024-03-08 02:45:28 +03:00
Oleg Kalachev
bd99923f6b Install python3-empy 2024-03-04 22:13:10 +03:00
Oleg Kalachev
521c248c90 Fix 2024-03-04 21:27:33 +03:00
Oleg Kalachev
7a4f611e47 Fix running install_geographiclib_datasets script 2024-03-04 21:10:27 +03:00
Oleg Kalachev
39cd8d5827 Fix ROS build 2024-03-04 20:47:00 +03:00
Qandra Si
7d022a5af1 docs: improve UART connection to FC section (#502)
* добавил схему подключения fc-uart-rpi, дополнил и уточнил настройки для старой версии px4 (в списке рекомендуемых всё ещё 1.8.2), поправил мелкие опечатки и формулировки в eng-версии, вставил (для общности) изображение не только для fc-uart-rpi, но и для fc-usb-rpi, проверил работоспособность инструкции на своём комплекте с PX4 и прошивкой v1.8.2 (работает с rpi v2.22, и v2.23, и последней v2.24)

---------

Co-authored-by: Oleg Kalachev <okalachev@gmail.com>
2024-03-04 19:42:04 +03:00
Oleg Kalachev
6fa9442963 Replace libgeographic-dev dependency as suggested 2024-03-01 03:25:27 +03:00
Oleg Kalachev
ce4d84d8c1 Build most of the Clover dependency packages 2024-03-01 01:25:49 +03:00
Oleg Kalachev
ebd9c03251 docs: fix broken image in Flysky RC article 2024-02-23 23:17:10 +03:00
Oleg Kalachev
0356133ab7 Merge branch 'master' into bookworm 2024-02-21 15:07:29 +03:00
Oleg Kalachev
5755300d3a Install image_geometry and dynamic_reconfigure as clover dependencies 2024-02-21 15:04:56 +03:00
Oleg Kalachev
147d6238d5 Fix patching 2024-02-21 00:19:26 +03:00
Oleg Kalachev
9576141376 Apply needed patches 2024-02-20 23:22:27 +03:00
Oleg Kalachev
39f0be296e Set OS to bullseye when doing rosdep update and install
Disable building packages
2024-02-20 21:04:05 +03:00
Oleg Kalachev
8c5551b00b docs: fix anchor link in snippets 2024-02-13 19:22:02 +03:00
Oleg Kalachev
42c26aa645 docs: add a snippet for moving objects in Gazebo 2024-02-13 15:45:27 +03:00
Qandra Si
f91dc4df71 docs: warnings about usage of v1.10- firmwares and QGC (#500)
* сведения о совместимости QGC v4.2.0 / v4.3.0 и прошивок до /после v1.8, v1.0 и готовящейся к выпуску v1.15

* Remove unintended change

* Edit and shorten warnings

---------

Co-authored-by: Oleg Kalachev <okalachev@gmail.com>
2024-02-08 19:15:03 +03:00
Oleg Kalachev
cf6f8f3488 Install Catkin using pip 2024-02-06 23:09:38 +03:00
Oleg Kalachev
4b059f3145 Skip log4cxx in rosdep 2024-02-06 21:55:01 +03:00
Oleg Kalachev
ce3b6c0cc7 Fix 2024-02-06 21:44:28 +03:00
Oleg Kalachev
9d0499159a Add --break-system-packages flag to pip 2024-02-06 21:34:31 +03:00
Oleg Kalachev
3906bc0318 Install pip using apt 2024-02-06 21:05:14 +03:00
Oleg Kalachev
ee9b3d8fe7 Use --break-system-packages flag for pip 2024-02-06 20:47:07 +03:00
Oleg Kalachev
aac5b25c60 Install rosdep and rosinstall-generator using pip 2024-02-06 20:26:09 +03:00
Oleg Kalachev
5211630542 Uncomment add ROS apt sources 2024-02-06 16:49:43 +03:00
Oleg Kalachev
bb2afd5e9d Fix 2024-02-06 16:38:19 +03:00
Oleg Kalachev
88668897f9 Fix 2024-02-06 16:26:08 +03:00
Oleg Kalachev
36daaddfb2 Correct RPI_IMAGE_NAME 2024-02-06 16:16:51 +03:00
Oleg Kalachev
b5e2073007 Add --allow-releaseinfo-change flag 2024-02-06 16:11:21 +03:00
Oleg Kalachev
1154969af2 Merge branch 'master' into bookworm 2024-02-06 16:09:52 +03:00
Oleg Kalachev
225d8e85ff Foo 2024-02-06 16:08:21 +03:00
Oleg Kalachev
bf29e87cea Use unxz to unarchive image 2024-02-06 16:06:51 +03:00
Oleg Kalachev
24c15f6e65 Decompress image using xz 2024-02-06 16:03:54 +03:00
Oleg Kalachev
d6acb0a202 Base Debian version is update to bookworm 2024-02-06 15:48:25 +03:00
Oleg Kalachev
6defbea453 Update Raspberry Pi OS to 2023-12-11 2024-02-06 15:46:51 +03:00
Oleg Kalachev
e31b69a790 Add possibility to trigger workflows manually 2024-01-22 01:35:52 +03:00
Oleg Kalachev
7251a76315 image: symlink assets instead of copying in documentation to save space 2023-11-04 01:28:00 +03:00
Oleg Kalachev
921e09c392 docs: minor fixes 2023-11-02 17:27:06 +03:00
Oleg Kalachev
9e69bdb01b docs: fix building by new assets size excludes 2023-11-02 06:57:02 +03:00
Oleg Kalachev
50495a9de9 docs: publish mechanical gripper model 2023-11-02 06:52:18 +03:00
Oleg Kalachev
12ccd919a2 docs: fix camera orientation setup example 2023-10-29 14:51:39 +03:00
Oleg Kalachev
f0eacfc0f7 aruco_pose: make dynamic reconfigure generator work with newer versions of OpenCV (#495) 2023-10-14 17:28:37 +03:00
Oleg Kalachev
742d0535c3 docs: add information about EKF2 parameter in PX4 1.14+ 2023-10-11 10:27:05 +03:00
Oleg Kalachev
af1b993e64 led_effect: add led parameter to specify led namespace
When using ROS namespace, subscription to mavros topics is broken
2023-10-11 10:06:18 +03:00
Oleg Kalachev
d3bda9df48 docs: add some additional tests to testing list 2023-10-11 08:30:00 +03:00
Oleg Kalachev
939086362a Run rectify nodelet in tests 2023-10-11 06:40:50 +03:00
Oleg Kalachev
7cf14373b0 main_camera.launch: argument for running image rectification nodelet 2023-10-11 00:51:02 +03:00
Oleg Kalachev
f428dfdb50 image: install stereo-msgs package 2023-10-10 08:46:03 +03:00
Oleg Kalachev
76982dc198 image: install nodelet-topic-tools package 2023-10-10 08:43:20 +03:00
Oleg Kalachev
29f01c25e0 selfcheck.py: support PX4 v1.14 EKF2 aiding parameters change
EKF2_AID_MASK has been split (EKF2_EV_CTRL, EKF2_GPS_CTRL, EKF2_OF_CTRL)
EKF2_HGT_MODE renamed to EKF2_HGT_REF
EKF2_RNG_AID is removed
2023-10-10 08:25:27 +03:00
Oleg Kalachev
7ca0ede1d7 selfcheck.py: cast parameter to int when performing bitwise operations 2023-10-10 07:47:52 +03:00
Oleg Kalachev
c3d87b1608 Update udev rules using data from PX4 sources
Adding Cube Orange, Holybro 6X and many more popular boards
2023-10-10 07:35:42 +03:00
Oleg Kalachev
47901dcff2 selfcheck.py: check udev rules presence and give more useful hint if no mavros state 2023-10-10 06:45:47 +03:00
Oleg Kalachev
9404d4be6d Use image_geometry library in red circle following example 2023-09-20 02:44:26 +03:00
Oleg Kalachev
ad51d86464 docs: add terrain frame to frames list 2023-09-19 15:54:20 +03:00
Oleg Kalachev
9a713057b6 image: add image_geometry package 2023-08-15 17:49:55 +07:00
Oleg Kalachev
7b591d350c aruco_map: fix publishing detected markers count 2023-08-01 17:09:10 +07:00
Oleg Kalachev
2f8915ce31 aruco_map: add ability to pass number of detected markers to Pose covariance field 2023-07-30 14:04:59 +07:00
Oleg Kalachev
6fb84ae584 Remove unneeded line 2023-07-22 13:36:14 +03:00
Oleg Kalachev
bf4f680164 aruco_detect: don't convert image to bgr8 as this is not needed 2023-07-18 17:47:13 +03:00
Oleg Kalachev
c0baf30c96 Move rangefinder frame node out of mavros.launch 2023-07-15 22:05:24 +03:00
Oleg Kalachev
8f2c3b2e55 vpe_publisher: fix code aligning 2023-07-15 20:04:27 +03:00
Oleg Kalachev
6423eb91a2 vpe_publisher: fix calculating the offset in topic mode 2023-07-15 19:15:31 +03:00
Oleg Kalachev
22d7236a47 docs: publish CopterHack-2023 results 2023-05-28 10:05:04 +03:00
Oleg Kalachev
91d33a5961 docs: minor fixes 2023-05-28 09:59:27 +03:00
Oleg Kalachev
2997951371 docs: fix Atena article links to gitbook 2023-05-27 06:26:27 +03:00
Oleg Kalachev
a2ffcf381c docs: workaround for inter-lingual links inarticle 2023-05-27 05:46:49 +03:00
Oleg Kalachev
9aab324f60 docs: enable markdownlint for Atena CopterHack-2023 article 2023-05-23 00:28:54 +03:00
José Carlos Andrade do Nascimento
984fb39b85 docs: Swarm in Blocks 2 (Atena) (CopterHack 2023) (#471)
* Create swarm_in_blocks2.md

* Delete swarm_in_blocks2.md

* Create swarm_in_blocks_2.md

* markdown fixed

* Update swarm_in_blocks_2.md

* markdown first version

* Update swarm_in_blocks_2.md

* markdown fixed

* changing images

* Update swarm_in_blocks_2.md

* Lowercase asset file extension

* Some editing

* linking asstes

* docs: team image link fixed

* removing raw assets from pr

* docs: removing all unused assets

* docs: doc checking unused files

* Reduce logo image size

* Lowercase logo image file

* Rename logo directory

* Fix external images urls, some fix to whitespace

* Add link to CopterHack page in the header

* Add article to summary and CopterHack page

---------

Co-authored-by: Oleg Kalachev <okalachev@gmail.com>
2023-05-22 23:55:28 +03:00
Oleg Kalachev
3a1a9d486c docs: fix some issues with CopterHack-2023 articles 2023-05-20 08:26:40 +03:00
Oleg Kalachev
55297696d6 docs: add CopterHack articles to summary 2023-05-20 07:49:46 +03:00
Oleg Kalachev
371f244228 docs: update CopterHack teams table 2023-05-20 07:16:49 +03:00
Juli-Shvetsova
ab3e7ac951 docs: CH2023 - Liceu128 (CopterHack-2023) (#473)
* Create liceu128.md

* Update liceu128.md

* Update liceu128.md

* Update liceu128.md

* Edit article

* final liceu128.md

* Edit article

---------

Co-authored-by: микемка <mikemka@vk.com>
Co-authored-by: Oleg Kalachev <okalachev@gmail.com>
2023-05-20 07:05:52 +03:00
Max
cdd6195f0b docs: Advanced clover simulation platform (CopterHack-2023) (#472)
* Create advanced_clover_simulator_platform.md

* Write better description

* AdvancedClover article finished

* Some editing

* Reduce images size

---------

Co-authored-by: Oleg Kalachev <okalachev@gmail.com>
2023-05-20 07:04:55 +03:00
ssmith-81
c9b015148f docs: MoCap-Clover (CopterHack-2023) (#470)
* Create MoCap-Clover

* Rename MoCap-Clover to mocap_clover.md

* Create mocap_logo

* Add files via upload

* Update mocap_clover.md

* Update mocap_clover.md

update

* Update mocap_clover.md

* Update mocap_clover.md

* Add files via upload

* Update mocap_clover.md

* Update mocap_clover.md

* Add files via upload

* Update mocap_clover.md

* Update mocap_clover.md

* Update mocap_clover.md

* Update mocap_clover.md

* Add files via upload

* Update mocap_clover.md

* Add files via upload

* Update mocap_clover.md

* Add files via upload

* Update mocap_clover.md

* Update mocap_clover.md

* Update mocap_clover.md

* Edit article

* Remove unneeded asset

* Reduce sizes of some assets

* Update mocap_clover.md

* Update mocap_clover.md

* Delete docs/assets/mocap_clover directory

* Fix again headers anchors

* Create test

* Add files via upload

* Update mocap_clover.md

* Add files via upload

* Delete test

* Update mocap_clover.md

---------

Co-authored-by: Oleg Kalachev <okalachev@gmail.com>
2023-05-20 07:00:39 +03:00
Lukerrr
2054472c23 docs: C305: Radio-Navigation System (CopterHack-2023) (#468)
* Create nav-beacon

* Update and rename article

* Fixed article issues

* Update nav-beacon.md

* Update nav-beacon.md

* Update nav-beacon.md

* Update nav-beacon.md

* Update nav-beacon.md

* Update nav-beacon.md

* Edit article

---------

Co-authored-by: Oleg Kalachev <okalachev@gmail.com>
2023-05-20 06:58:51 +03:00
DJS Phoenix
b1084f99b9 docs: DJS PHOENIX (CopterHack-2023) (#462)
* Create djs_phoenix_chetak.md

* Update djs_phoenix_chetak.md

* Update djs_phoenix_chetak.md

* Update djs_phoenix_chetak.md

* Edit article

* Move English article to en/ subfolder

---------

Co-authored-by: Oleg Kalachev <okalachev@gmail.com>
2023-05-20 06:58:11 +03:00
Mikhail Kuznetsov
c5f405c4d9 docs: Clover Cloud Platform CopterHack 2023 (#455)
* Create clover-cloud-platform.md

* md fix

* fix link to repositories

* Update clover-cloud-platform.md

* Editing

---------

Co-authored-by: Oleg Kalachev <okalachev@gmail.com>
2023-05-20 06:57:23 +03:00
Oleg Kalachev
099d39d42d docs: some updates to version warnings 2023-05-20 04:43:32 +03:00
Oleg Kalachev
c9035790f2 image: loose required Python libraries versions, add missing validation 2023-04-17 22:54:18 +03:00
Oleg Kalachev
95da57fea1 docs: fix obsolete link to Ubuntu Focal desktop image 2023-04-12 21:32:38 +03:00
Oleg Kalachev
ad0138cd26 Merge pull request #488 from CopterExpress/v0.24-release
V0.24 release changes
2023-04-12 02:04:59 +03:00
Oleg Kalachev
d6101dc0a3 ci: add secret variable to temporarily freeze updating docs website 2023-04-12 01:34:34 +03:00
Oleg Kalachev
cbba62d165 blocks: add block for reading RC values 2023-04-11 19:35:00 +03:00
Oleg Kalachev
28ddbbcdf9 docs: add version warnings to camera articles 2023-04-11 19:21:53 +03:00
Oleg Kalachev
cac6b59a56 Set the version to 0.24.0 in ROS packages 2023-04-11 15:28:38 +03:00
Oleg Kalachev
c82490a0c1 Set terrain_frame_mode to range by default addressing CopterExpress/clover_vm#14 2023-04-11 15:27:39 +03:00
Oleg Kalachev
808726b4b7 Try to tix the build 2023-04-11 13:33:35 +03:00
Oleg Kalachev
19fde7095f simple_offboard: add test for land service 2023-04-11 01:40:58 +03:00
Oleg Kalachev
5e9f442996 simple_offboard: add terrain_frame_mode parameter, CopterExpress/clover_vm#14
`altitude` mode takes the current altitude from the estimator
`range` mode takes the current altitude from a simple range topic
2023-04-11 01:28:38 +03:00
Oleg Kalachev
68903373b0 simple_offboard: reset stored setpoint on auto_arm only if needed to be armed
CopterExpress/clover_vm#13
2023-04-11 00:57:43 +03:00
Oleg Kalachev
ae05710a37 blocks: fix set_yaw block implementation (#487) 2023-03-27 19:18:21 +03:00
Oleg Kalachev
4c576ba5d4 builder: print largest installed packages 2023-02-22 00:42:06 +03:00
Oleg Kalachev
ffd8b98e53 Merge branch 'master' into v0.24-release 2023-02-07 10:06:27 +03:00
Oleg Kalachev
69deeae32f blocks: document ~print topic of the main node 2023-02-07 10:06:09 +03:00
Oleg Kalachev
df66deb32c docs: add running flight autotests to testing plan 2023-02-07 10:03:41 +03:00
Oleg Kalachev
87a51221bc docs: update documentation for autonomous flights 2023-02-07 10:02:02 +03:00
Oleg Kalachev
08bda736e9 aruco_detect: fix drawing markers axis 2023-01-26 18:16:24 +03:00
Oleg Kalachev
56a2be8170 docs: add redirect from /red_circle to camera article 2023-01-13 12:59:43 +03:00
Oleg Kalachev
59518fddd1 examples: add program to recognize and follow the red circle 2023-01-13 12:59:26 +03:00
Oleg Kalachev
25ae694d1f simulator: add red circle model for recognizing 2023-01-13 12:58:51 +03:00
Oleg Kalachev
f78a03ec89 Change default EKF2_HGT_MODE to 3 (vision) 2023-01-13 12:06:55 +03:00
Oleg Kalachev
0cfdac43ec Significant update to simple_offboard node
* Allow using nans for most of services parameters
* Add terrain frame
* Remove yaw_rate parameter from most services
* Add set_yaw and set_yaw_rate services
* Correct order for pitch and roll everywhere to match XYZ convention
* Add simple_offboard/state topic
* Add essential tests
* Stop publishing setpoints when land called
2023-01-12 11:00:05 +03:00
Oleg Kalachev
cb2850b1d4 docs: update CopterHack-2023 project link 2023-01-12 00:45:27 +03:00
Oleg Kalachev
460c3fdbe1 Whitespaces fixes 2022-12-29 05:54:32 +03:00
Oleg Kalachev
e3fb7cf28e Merge branch 'master' into v0.24-release 2022-12-29 05:53:12 +03:00
Oleg Kalachev
3b930d48d2 Update build passing badge in readme 2022-12-21 11:34:04 +03:00
murata,katsutoshi
f3aadd11ec docs: change the item name in summary (#480) 2022-12-06 00:01:28 +03:00
Oleg Kalachev
976c7114e5 docs: update motion capture project link 2022-11-26 21:58:44 +01:00
Oleg Kalachev
d8662007fe docs: add teams list for CopterHack-2023 2022-11-23 23:21:11 +01:00
Oleg Kalachev
ac1ac33a1a Merge branch 'master' into v0.24-release 2022-11-12 01:35:11 +06:00
Oleg Kalachev
95db8ba1b1 aruco_pose: known_tilt => known_vertical, add flip_vertical parameter (#476)
* aruco_pose: rename parameter known_tilt to known_vertical

* More clean variable names

* aruco_pose: add flip_vertical parameter and get rid of map_flipped

* selfcheck.py: support flip_vertical parameter

* aruco_pose: document flip_vertical parameter

* selfcheck.py: fix known_vertical description

* Fix editorconfig
2022-11-12 01:33:15 +06:00
Oleg Kalachev
94a95b28b3 Minor typo 2022-11-11 06:07:55 +06:00
Oleg Kalachev
d4a83bdf58 autotest: run aruco test without optical flow 2022-11-11 05:58:49 +06:00
Oleg Kalachev
cb1773b708 selfcheck.py: skip optical_flow check if it's not running 2022-11-11 05:46:58 +06:00
Oleg Kalachev
5afbcff949 vpe_publisher: fix a bug when the first pose arrives at the start of clock (simulation) 2022-11-11 05:43:16 +06:00
Oleg Kalachev
3870e62be7 Merge branch 'master' into v0.24-release 2022-11-10 22:26:59 +06:00
Oleg Kalachev
f719406c8b Remove www directory update from clover.launch and update it on demand only (#475) 2022-11-10 22:25:44 +06:00
Oleg Kalachev
72f8d901d5 ci: set cancel-in-progress only for deploy job not the whole docs wf 2022-11-10 20:04:05 +06:00
Oleg Kalachev
393801b023 Fix ROS tools tests considering some of them exit with 64 on usage 2022-11-10 19:57:02 +06:00
Oleg Kalachev
a0322c55f2 Fix ROS tools tests 2022-11-10 17:56:23 +06:00
Oleg Kalachev
3662f512a7 docs: update in wall aruco article 2022-11-10 05:06:46 +06:00
Oleg Kalachev
277eb7297f image: test basic ros tools work 2022-11-10 04:37:30 +06:00
Oleg Kalachev
e719b0f1e2 selfcheck.py: print fcu_url value if no connection to fcu 2022-11-09 23:46:05 +06:00
Oleg Kalachev
e65d380b4b Minor camera example fix 2022-11-08 22:43:08 +06:00
Oleg Kalachev
8fe34e90e6 Depend on docopt in package.xml instead of requirements.txt 2022-11-08 16:07:36 +06:00
Oleg Kalachev
54ab5ab4b5 selfcheck.py: make output colored only in a tty 2022-11-08 06:47:01 +06:00
Oleg Kalachev
2cda68ae4a selfcheck.py: don't fall if aruco_detect/length is not set 2022-11-08 06:46:29 +06:00
Oleg Kalachev
d24b6617a4 selfcheck.py: don't fall when aruco_map/known_tilt is not set 2022-11-08 06:41:21 +06:00
Oleg Kalachev
640ec1ee1a Add clover package dependency on pytest 2022-11-08 03:37:12 +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
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
2b13aa02eb docs: make /camera redirect to English version of the article 2022-11-04 03:10:56 +06:00
Oleg Kalachev
45042cd6f5 docs: updates to camera and computer vision article 2022-11-04 03:05:59 +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
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
2dda726d3e clover.launch: turn on disable_on_vpe by default 2022-10-31 02:32:45 +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
9e8470a3cb Install TinyXML 2022-10-11 16:05:47 +06:00
Oleg Kalachev
e9cd3d917c Try to fix --install-layout not recognized issue 2022-10-08 19:54:19 +06:00
Oleg Kalachev
373cefb01c Remove fakeroot as we are root 2022-10-07 22:51:34 +06:00
Oleg Kalachev
d0039ea23f Typo 2022-10-07 20:44:20 +06:00
Oleg Kalachev
bd25818aa7 Add rosdep file to help bloom-generate resolve Bullseye dependencies 2022-10-07 14:34:30 +06:00
Oleg Kalachev
a73457c5c5 Debug 2022-10-07 14:08:10 +06:00
Oleg Kalachev
c8a4d49577 Try to build from catkin workdspace 2022-10-06 23:24:12 +06:00
Oleg Kalachev
123379b60e Fix 2022-10-06 23:23:21 +06:00
Oleg Kalachev
71d3144691 fix 2022-10-06 15:18:14 +06:00
Oleg Kalachev
d306a9d96d Try 2022-10-05 17:45:49 +06:00
Oleg Kalachev
1d514cf5ca Merge branch 'master' into bullseye 2022-10-05 17:42:06 +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
Oleg Kalachev
96adadeae9 Put Debian build log to file 2022-09-24 18:14:44 +03:00
Oleg Kalachev
a1061ff32c Add ls 2022-09-24 13:46:34 +03:00
Oleg Kalachev
e9d13b865c Build Debian packages 2022-09-24 12:58:56 +03:00
Oleg Kalachev
b655a4274e List built ROS packages 2022-09-23 23:10:20 +03:00
Oleg Kalachev
d58a59afab Fix 2022-09-23 22:48:19 +03: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
d0fabd8b3e Fix 2022-09-23 19:40:06 +03:00
Oleg Kalachev
b50207e5e9 Install ROS to /opt/ros/ 2022-09-23 18:44:18 +03:00
Oleg Kalachev
97797a9178 Fix 2022-09-23 15:53:20 +03:00
Oleg Kalachev
1e8569b664 Fix 2022-09-23 15:35:23 +03:00
Oleg Kalachev
21cc47eecb Fix 2022-09-23 15:20:09 +03:00
Oleg Kalachev
4c56adafb2 Fix 2022-09-23 03:17:30 +03:00
Oleg Kalachev
8a524a8aa5 Fix 2022-09-23 03:11:47 +03:00
Oleg Kalachev
5493445747 Manually install catkin_pkg 2022-09-23 03:04:30 +03:00
Oleg Kalachev
0ef26334dc Fix 2022-09-23 02:45:52 +03:00
Oleg Kalachev
8d83e8faa3 Add ROS sources for Ubuntu 2022-09-23 02:36:01 +03:00
Oleg Kalachev
9b90214d9d Try to fix 2022-09-23 01:53:56 +03:00
Oleg Kalachev
224f6cee27 Fix 2022-09-23 01:38:59 +03:00
Oleg Kalachev
ff04b2fb75 Fix 2022-09-23 01:21:12 +03:00
Oleg Kalachev
473444ae33 Fix 2022-09-23 01:13:41 +03:00
Oleg Kalachev
168734ad8c Remove sources list before rosdep init 2022-09-23 00:56:49 +03:00
Oleg Kalachev
4e7b2e379a Restore rosdep init 2022-09-23 00:50:29 +03:00
Oleg Kalachev
ad1d51fd9e Fix 2022-09-23 00:42:20 +03:00
Oleg Kalachev
57c415db22 Add os flag to rosdep update 2022-09-23 00:32:35 +03:00
Oleg Kalachev
568386a758 Install GeographicLib datasets in build workflow 2022-09-23 00:31:21 +03:00
Oleg Kalachev
55f8f4fa1a aruco_detect: check for duplicates in marker transforms, send all transforms in one message 2022-09-23 00:31:19 +03:00
Oleg Kalachev
dd0dd6b5c1 aruco_pose: add duplicate test to CMakeLists.txt 2022-09-23 00:31:19 +03:00
Oleg Kalachev
4c40bea226 aruco_pose: implement test for TF_REPEATED_DATA when multiple markers with the same ID 2022-09-23 00:31:18 +03:00
Oleg Kalachev
6b3f5c3690 Simplify script for testing native Noetic build 2022-09-23 00:31:18 +03:00
Oleg Kalachev
63067823ee Show number of messages received in topic viewer 2022-09-23 00:31:17 +03:00
Oleg Kalachev
880f67c3bc Fix error when viewing messages without header in topic viewer 2022-09-23 00:31:17 +03:00
Oleg Kalachev
839aeb6a26 clover.launch: make force_init argument overridable externally 2022-09-23 00:31:16 +03:00
Oleg Kalachev
b123585756 mavros.launch: fix fcu_url for hitl connection 2022-09-23 00:31:16 +03:00
Oleg Kalachev
fa4757a4c8 mavros.launch: add hitl option for fcu_conn argument 2022-09-23 00:31:15 +03:00
Oleg Kalachev
28d77aea33 selfcheck.py: skip boot duration check on not Clover image 2022-09-23 00:31:15 +03:00
Oleg Kalachev
0425e1da24 optical_flow: timeout for previous frame
For cases when optical flow is dynamically disabled and enabled back
2022-09-23 00:31:14 +03:00
Oleg Kalachev
692d424a0b Remove obsolete note from readme 2022-09-23 00:31:14 +03:00
Oleg Kalachev
1c9fd7b126 docs: remove some obsolete notes about renaming 2022-09-23 00:31:13 +03:00
oponfil
a1752c1642 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-09-23 00:31:13 +03:00
oponfil
0dbd2d1048 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-09-23 00:31:12 +03:00
oponfil
79408861a2 docs: docs: correction in mavros article on global setpoints (en) (#358)
Co-authored-by: Oleg Kalachev <okalachev@gmail.com>
2022-09-23 00:31:12 +03:00
oponfil
063f2c3c13 docs: correction in mavros article on global setpoints (#357)
Co-authored-by: Oleg Kalachev <okalachev@gmail.com>
2022-09-23 00:31:10 +03:00
Oleg Kalachev
68819bbd34 docs: update PX4 to v1.12.3 in native simulation installation article 2022-09-23 00:31:10 +03:00
Oleg Kalachev
3a5c3d5bb4 docs: comment out link to install_software.sh in simulation installation manual
The link confuses users so they often try to run it
2022-09-23 00:31:09 +03:00
Oleg Kalachev
032eb52a88 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-09-23 00:31:09 +03:00
Oleg Kalachev
7dc24437cf vpe_publisher: fix reading map and base link frame_id from mavros 2022-09-23 00:31:08 +03:00
Oleg Kalachev
1ccb756931 Update docs job runner to ubuntu 22.04
https://github.com/actions/runner-images/issues/6002
2022-09-23 00:31:08 +03:00
Oleg Kalachev
b5268a7b62 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-09-23 00:31:07 +03:00
Elena Seliverstova
a0663d6b36 Update the contact link in Telegram 2022-09-23 00:31:07 +03:00
Elena Seliverstova
26f2c966ff Update the contact link in Telegram 2022-09-23 00:31:06 +03:00
Elena Seliverstova
4517186862 docs: add new articles about CopterHack2023 (#450) 2022-09-23 00:31:06 +03:00
Oleg Kalachev
5236afe035 Make PX4 node required in the sim 2022-09-23 00:31:05 +03:00
Oleg Kalachev
fe1707d0c3 simulator: change COM_RCL_EXCEPT param to enable offboard flights without RC on PX4 v1.13 2022-09-23 00:31:05 +03:00
Oleg Kalachev
03854d2589 Implement dynamic reconfiguration for aruco_map (#448) 2022-09-23 00:31:04 +03:00
Niels Hoppe
28339e65af Add missing dependency 2022-09-23 00:31:04 +03:00
Oleg Kalachev
e11f0cf054 Minor whitespace fix 2022-09-23 00:31:03 +03:00
Oleg Kalachev
c1045cd11e Add shortcut launch-file to run the simulation 2022-09-23 00:31:03 +03:00
Oleg Kalachev
aa3373da58 docs: redirect for blocks programming article 2022-09-23 00:31:01 +03:00
Oleg Kalachev
a4ad408228 Restore rosdep update 2022-09-23 00:09:09 +03:00
Oleg Kalachev
0d436637cd Trigger build 2022-09-22 23:59:23 +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
18daff1044 Fix 2022-06-18 00:11:51 +03:00
Oleg Kalachev
b96d17a746 Fix 2022-06-18 00:00:47 +03:00
Oleg Kalachev
1506d3fd96 Fix 2022-06-17 23:55:15 +03:00
Oleg Kalachev
eec0833707 Merge branch 'master' into bullseye 2022-06-17 22:14:41 +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
f9883baa87 builder: script to build ROS from scratch 2022-02-09 19:42:18 +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
3bebecf91e Update Raspberry Pi OS to 2022-01-28 (bullseye) 2022-02-05 19:40:06 +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
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
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
365 changed files with 10538 additions and 1996 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

View File

@@ -7,6 +7,7 @@ on:
branches: [ master ]
release:
types: [ created ]
workflow_dispatch:
jobs:
build:
@@ -16,14 +17,24 @@ jobs:
- 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
# - name: Compress image
# run: |
# cd images && sudo chmod -R 777 . && zip -9 $(echo clover_*).zip clover_* && ls -lh . && unzip -l clover_*.zip
- name: Compress image using 7-Zip
run: |
cd images && sudo chmod -R 777 . && zip -9 $(echo clover_*).zip clover_* && ls -l . && unzip -l clover_*.zip
cd images && sudo chmod -R 777 . && 7z a -mx=9 $(echo *_*).7z *_* && ls -lh . && 7z l *_*.7z
- name: Upload image
uses: softprops/action-gh-release@v1
if: ${{ github.event_name == 'release' }}
with:
files: images/clover_*.zip
files: images/*_*.zip
prerelease: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Upload image to artifacts
if: ${{ github.event_name == 'workflow_dispatch' || github.ref != 'refs/heads/master' }}
uses: actions/upload-artifact@v4
with:
name: image
path: images/*_*.7z
retention-days: 1

View File

@@ -5,19 +5,48 @@ on:
branches: [ '*' ]
pull_request:
branches: [ master ]
workflow_dispatch:
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
# 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
- name: Native Noetic build
run: |
docker run --rm -v $(pwd):/root/catkin_ws/src/clover ros:noetic-ros-base /root/catkin_ws/src/clover/builder/standalone-install.sh
- 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

View File

@@ -4,11 +4,21 @@ on:
push:
branches: [ '*' ]
pull_request:
branches: [ master ]
branches: [ '*' ]
workflow_dispatch:
permissions:
contents: read
pages: write
id-token: write
defaults:
run:
shell: bash
jobs:
docs:
runs-on: ubuntu-18.04
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v2
- name: Use Node.js
@@ -34,7 +44,11 @@ jobs:
gitbook install
gitbook build
- name: Generate PDF
if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }}
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
@@ -43,11 +57,33 @@ jobs:
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
- name: Deploy
uses: JamesIves/github-pages-deploy-action@4.1.3
if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }}
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:
branch: gh-pages
folder: _book
clean: true
single-commit: true # to avoid multiple copies of large pdf files
path: _book
deploy-docs:
if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }}
concurrency:
group: "pages"
cancel-in-progress: true
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
needs: docs
steps:
- name: Deploy to GitHub Pages
env:
FREEZE_DOCS: ${{ secrets.FREEZE_DOCS }}
if: ${{ !env.FREEZE_DOCS }}
id: deployment
uses: actions/deploy-pages@v1

View File

@@ -5,6 +5,7 @@ on:
branches: [ '*' ]
pull_request:
branches: [ master ]
workflow_dispatch:
jobs:
editorconfig:
@@ -15,4 +16,4 @@ jobs:
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"
./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"

View File

@@ -113,7 +113,9 @@
"VMware",
"DuoCam"
],
"code_blocks": false
"code_blocks": false,
"html_elements": false
},
"MD045": false
"MD045": false,
"MD051": false
}

View File

@@ -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).
![GitHub Workflow Status](https://img.shields.io/github/workflow/status/CopterExpress/clover/CI)
![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/CopterExpress/clover/build-image.yaml?branch=master)
![GitHub all releases](https://img.shields.io/github/downloads/CopterExpress/clover/total)
Image features:

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
)
###################################
@@ -251,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

@@ -43,7 +43,8 @@ It's recommended to run it within the same nodelet manager with the camera nodel
* `~frame_id_prefix` (*string*) prefix for TF transforms names, marker's ID is appended (default: `aruco_`)
* `~length` (*double*) markers' sides length
* `~length_override` (*map*) lengths of markers with specified ids
* `~known_tilt` (*string*) known tilt (pitch and roll) of all the markers as a frame
* `~known_vertical` (*string*) known vertical (Z axis) of all the markers as a frame
* `~flip_vertical` flip vertical vector
### Topics
@@ -51,6 +52,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 +72,12 @@ 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` known tilt (pitch and roll) of markers map as a frame
* `~known_vertical` known vertical (Z axis) of markers map as a frame
* `~flip_vertical` flip vertical vector
* `~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 +101,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

@@ -4,12 +4,17 @@ PACKAGE = "aruco_pose"
from dynamic_reconfigure.parameter_generator_catkin import *
import cv2.aruco
p = cv2.aruco.DetectorParameters_create()
try:
p = cv2.aruco.DetectorParameters_create()
except AttributeError:
p = cv2.aruco.DetectorParameters()
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">
<package format="3">
<name>aruco_pose</name>
<version>0.21.1</version>
<version>0.24.0</version>
<description>Positioning with ArUco markers</description>
<maintainer email="okalachev@gmail.com">Oleg Kalachev</maintainer>
@@ -28,6 +28,9 @@
<depend>sensor_msgs</depend>
<depend>rostest</depend>
<depend>dynamic_reconfigure</depend>
<depend>pluginlib</depend>
<depend condition="$ROS_PYTHON_VERSION == 2">python-docopt</depend>
<depend condition="$ROS_PYTHON_VERSION == 3">python3-docopt</depend>
<test_depend>image_publisher</test_depend>
<test_depend>ros_pytest</test_depend>

View File

@@ -48,7 +48,9 @@
#include <aruco_pose/Marker.h>
#include <aruco_pose/MarkerArray.h>
#include <aruco_pose/DetectorConfig.h>
#include <aruco_pose/SetMarkers.h>
#include "draw.h"
#include "utils.h"
#include <memory>
#include <functional>
@@ -69,10 +71,13 @@ 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_, flip_vertical_, 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_;
std::string frame_id_prefix_, known_vertical_;
Mat camera_matrix_, dist_coeffs_;
aruco_pose::MarkerArray array_;
std::unordered_set<int> map_markers_ids_;
@@ -92,13 +97,17 @@ 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", "");
known_vertical_ = nh_priv_.param("known_vertical", nh_priv_.param("known_tilt", std::string(""))); // known_tilt is an old name
flip_vertical_ = nh_priv_.param<bool>("flip_vertical", false);
auto_flip_ = nh_priv_.param("auto_flip", false);
frame_id_prefix_ = nh_priv_.param<std::string>("frame_id_prefix", "aruco_");
@@ -114,6 +123,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,14 +138,15 @@ 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;
Mat image = cv_bridge::toCvShare(msg)->image;
vector<int> ids;
vector<vector<cv::Point2f>> corners, rejected;
vector<cv::Vec3d> rvecs, tvecs;
vector<cv::Point3f> obj_points;
geometry_msgs::TransformStamped snap_to;
geometry_msgs::TransformStamped vertical;
// Detect markers
cv::aruco::detectMarkers(image, dictionary_, corners, ids, parameters_, rejected);
@@ -169,18 +181,20 @@ private:
}
}
if (!known_tilt_.empty()) {
if (!known_vertical_.empty()) {
try {
snap_to = tf_buffer_->lookupTransform(msg->header.frame_id, known_tilt_,
msg->header.stamp, ros::Duration(0.02));
vertical = tf_buffer_->lookupTransform(msg->header.frame_id, known_vertical_,
msg->header.stamp, transform_timeout_);
} catch (const tf2::TransformException& e) {
NODELET_WARN_THROTTLE(5, "can't snap: %s", e.what());
NODELET_WARN_THROTTLE(5, "can't retrieve known vertical: %s", e.what());
}
}
}
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;
@@ -193,25 +207,38 @@ private:
if (estimate_poses_) {
fillPose(marker.pose, rvecs[i], tvecs[i]);
// snap orientation (if enabled and snap frame available)
if (!known_tilt_.empty() && !snap_to.header.frame_id.empty()) {
snapOrientation(marker.pose.orientation, snap_to.transform.rotation, auto_flip_);
// apply known vertical (if enabled and vertical frame available)
if (!known_vertical_.empty() && !vertical.header.frame_id.empty()) {
applyVertical(marker.pose.orientation, vertical.transform.rotation, false, 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_);
@@ -238,8 +265,7 @@ private:
cv::aruco::drawDetectedMarkers(debug, corners, ids); // draw markers
if (estimate_poses_)
for (unsigned int i = 0; i < ids.size(); i++)
cv::aruco::drawAxis(debug, camera_matrix_, dist_coeffs_,
rvecs[i], tvecs[i], getMarkerLength(ids[i]));
_drawAxis(debug, camera_matrix_, dist_coeffs_, rvecs[i], tvecs[i], getMarkerLength(ids[i]));
cv_bridge::CvImage out_msg;
out_msg.header.frame_id = msg->header.frame_id;
@@ -346,17 +372,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,10 +77,13 @@ 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_;
std::string known_vertical_, map_, markers_frame_, markers_parent_frame_;
int image_width_, image_height_, image_margin_;
bool auto_flip_, image_axis_;
bool flip_vertical_, auto_flip_, image_axis_, put_markers_count_to_covariance_;
public:
virtual void onInit()
@@ -89,34 +95,35 @@ 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", "");
known_vertical_ = nh_priv_.param("known_vertical", nh_priv_.param("known_tilt", std::string(""))); // known_tilt is an old name
flip_vertical_ = nh_priv_.param<bool>("flip_vertical", false);
auto_flip_ = nh_priv_.param("auto_flip", false);
image_width_ = nh_priv_.param("image_width" , 2000);
image_height_ = nh_priv_.param("image_height", 2000);
image_margin_ = nh_priv_.param("image_margin", 200);
image_axis_ = nh_priv_.param("image_axis", true);
put_markers_count_to_covariance_ = nh_priv_.param("put_markers_count_to_covariance", false);
markers_parent_frame_ = nh_priv_.param<std::string>("markers/frame_id", transform_.child_frame_id);
markers_frame_ = nh_priv_.param<std::string>("markers/child_frame_id_prefix", "");
// 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 +131,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 +140,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 +153,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;
@@ -166,7 +179,21 @@ public:
corners.push_back(marker_corners);
}
if (known_tilt_.empty()) {
if (put_markers_count_to_covariance_) {
// HACK: pass markers count using covariance field
int valid_markers = 0;
for (auto const &marker : markers->markers) {
for (auto const &board_marker : board_->ids) {
if (board_marker == marker.id) {
valid_markers++;
break;
}
}
}
pose_.pose.covariance[0] = valid_markers;
}
if (known_vertical_.empty()) {
// simple estimation
valid = cv::aruco::estimatePoseBoard(corners, ids, board_, camera_matrix_, dist_coeffs_,
rvec, tvec, false);
@@ -180,7 +207,7 @@ public:
} else {
Mat obj_points, img_points;
// estimation with "snapping"
// estimation with known vertical
cv::aruco::getBoardObjectAndImagePoints(board_, corners, ids, obj_points, img_points);
if (obj_points.empty()) goto publish_debug;
@@ -192,11 +219,11 @@ public:
fillTransform(transform_.transform, rvec, tvec);
try {
geometry_msgs::TransformStamped snap_to = tf_buffer_.lookupTransform(markers->header.frame_id,
known_tilt_, markers->header.stamp, ros::Duration(0.02));
snapOrientation(transform_.transform.rotation, snap_to.transform.rotation, auto_flip_);
geometry_msgs::TransformStamped vertical = tf_buffer_.lookupTransform(markers->header.frame_id,
known_vertical_, markers->header.stamp, ros::Duration(0.02));
applyVertical(transform_.transform.rotation, vertical.transform.rotation, flip_vertical_, auto_flip_);
} catch (const tf2::TransformException& e) {
NODELET_WARN_THROTTLE(1, "can't snap: %s", e.what());
NODELET_WARN_THROTTLE(1, "can't retrieve known vertical: %s", e.what());
}
geometry_msgs::TransformStamped shift;
@@ -268,9 +295,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 +331,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 +365,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 +414,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 +519,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 +562,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

@@ -106,26 +106,25 @@ inline bool isFlipped(tf::Quaternion& q)
return (abs(pitch) > M_PI / 2) || (abs(roll) > M_PI / 2);
}
/* Set roll and pitch from "from" to "to", keeping yaw */
inline void snapOrientation(geometry_msgs::Quaternion& to, const geometry_msgs::Quaternion& from, bool auto_flip = false)
/* Apply a vertical to an orientation */
inline void applyVertical(geometry_msgs::Quaternion& orientation, const geometry_msgs::Quaternion& vertical,
bool flip_vertical = false, bool auto_flip = false) // editorconfig-checker-disable-line
{
tf::Quaternion _from, _to;
tf::quaternionMsgToTF(from, _from);
tf::quaternionMsgToTF(to, _to);
tf::Quaternion _vertical, _orientation;
tf::quaternionMsgToTF(vertical, _vertical);
tf::quaternionMsgToTF(orientation, _orientation);
if (auto_flip) {
if (!isFlipped(_from)) {
static const tf::Quaternion flip = tf::createQuaternionFromRPY(M_PI, 0, 0);
_from *= flip; // flip "from"
}
if (flip_vertical || (auto_flip && !isFlipped(_orientation))) {
static const tf::Quaternion flip = tf::createQuaternionFromRPY(M_PI, 0, 0);
_vertical *= flip; // flip vertical
}
auto diff = tf::Matrix3x3(_to).transposeTimes(tf::Matrix3x3(_from));
auto diff = tf::Matrix3x3(_orientation).transposeTimes(tf::Matrix3x3(_vertical));
double _, yaw;
diff.getRPY(_, _, yaw);
auto q = tf::createQuaternionFromRPY(0, 0, -yaw);
_from = _from * q; // set yaw from "to" to "from"
tf::quaternionTFToMsg(_from, to); // set "from" to "to"
_vertical = _vertical * q; // set yaw from orientation to vertical
tf::quaternionTFToMsg(_vertical, orientation); // set vertical to orientation
}
inline void transformToPose(const geometry_msgs::Transform& transform, geometry_msgs::Pose& pose)

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

@@ -5,7 +5,7 @@ Requires=roscore.service
[Service]
User=pi
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 \
ROS_HOSTNAME=`hostname`.local ROS_OS_OVERRIDE=debian:bookworm exec stdbuf -o L roslaunch clover clover.launch --wait --screen --skip-log-check \
2> >(tee /tmp/clover.err)"
ExecStartPre=+rm /var/log/clover.log

View File

@@ -13,70 +13,38 @@
# copies or substantial portions of the Software.
#
set -e # Exit immidiately on non-zero result
echo_stamp() {
# TEMPLATE: echo_stamp <TEXT> <TYPE>
# TYPE: SUCCESS, ERROR, INFO
# More info there https://www.shellhacks.com/ru/bash-colors/
TEXT="$(date '+[%Y-%m-%d %H:%M:%S]') $1"
TEXT="\e[1m$TEXT\e[0m" # BOLD
case "$2" in
SUCCESS)
TEXT="\e[32m${TEXT}\e[0m";; # GREEN
ERROR)
TEXT="\e[31m${TEXT}\e[0m";; # RED
*)
TEXT="\e[34m${TEXT}\e[0m";; # BLUE
esac
echo -e ${TEXT}
}
# https://www.raspberrypi.com/documentation/computers/configuration.html
##################################################
# Configure hardware interfaces
##################################################
# 1. Enable sshd
echo_stamp "#1 Turn on sshd"
touch /boot/ssh
# /usr/bin/raspi-config nonint do_ssh 0
echo "--- Enable sshd"
/usr/bin/raspi-config nonint do_ssh 0
# 2. Enable GPIO
echo_stamp "#2 GPIO enabled by default"
echo "--- GPIO enabled by default"
# 3. Enable I2C
echo_stamp "#3 Turn on I2C"
echo "--- Enable I2C"
/usr/bin/raspi-config nonint do_i2c 0
# 4. Enable SPI
echo_stamp "#4 Turn on SPI"
echo "--- Enable SPI"
/usr/bin/raspi-config nonint do_spi 0
# 5. Enable raspicam
echo_stamp "#5 Turn on raspicam"
echo "--- Enable raspicam"
/usr/bin/raspi-config nonint do_camera 0
# 6. Enable hardware UART
echo_stamp "#6 Turn on UART"
echo "--- Setup UART"
# Temporary solution
# https://github.com/RPi-Distro/raspi-config/pull/75
/usr/bin/raspi-config nonint do_serial 1
/usr/bin/raspi-config nonint set_config_var enable_uart 1 /boot/config.txt
echo dtoverlay=pi3-disable-bt >> /boot/config.txt
/usr/bin/raspi-config nonint do_serial_hw 0
/usr/bin/raspi-config nonint do_serial_cons 1
echo dtoverlay=pi3-disable-bt >> /boot/firmware/config.txt
systemctl disable hciuart.service
# After adding to Raspbian OS
# https://github.com/RPi-Distro/raspi-config/commit/d6d9ecc0d9cbe4aaa9744ae733b9cb239e79c116
#/usr/bin/raspi-config nonint do_serial 2
# 7. Enable V4L driver http://robocraft.ru/blog/electronics/3158.html
echo "--- Enable v4l2 driver"
# http://robocraft.ru/blog/electronics/3158.html
#echo "bcm2835-v4l2" >> /etc/modules
echo_stamp "#7 Turn on v4l2 driver"
if ! grep -q "^bcm2835-v4l2" /etc/modules;
then printf "bcm2835-v4l2\n" >> /etc/modules
fi
echo_stamp "#8 End of configure hardware interfaces"

View File

@@ -13,63 +13,31 @@
# copies or substantial portions of the Software.
#
set -e # Exit immidiately on non-zero result
echo_stamp() {
# TEMPLATE: echo_stamp <TEXT> <TYPE>
# TYPE: SUCCESS, ERROR, INFO
# More info there https://www.shellhacks.com/ru/bash-colors/
TEXT="$(date '+[%Y-%m-%d %H:%M:%S]') $1"
TEXT="\e[1m$TEXT\e[0m" # BOLD
case "$2" in
SUCCESS)
TEXT="\e[32m${TEXT}\e[0m";; # GREEN
ERROR)
TEXT="\e[31m${TEXT}\e[0m";; # RED
*)
TEXT="\e[34m${TEXT}\e[0m";; # BLUE
esac
echo -e ${TEXT}
}
echo "--- Fix home directory permissions"
chmod +rx /home/pi
NEW_SSID='clover-'$(head -c 100 /dev/urandom | xxd -ps -c 100 | sed -e "s/[^0-9]//g" | cut -c 1-4)
echo_stamp "Setting SSID to ${NEW_SSID}"
# TODO: Use wpa_cli insted direct file edit
# FIXME: We rely on raspberrypi-net-mods to copy our file to /etc/wpa_supplicant.
# This is not very reliable, but seems to fix our rfkill problem.
cat << EOF >> /boot/wpa_supplicant.conf
ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
update_config=1
country=GB
network={
ssid="${NEW_SSID}"
psk="cloverwifi"
mode=2
proto=WPA RSN
key_mgmt=WPA-PSK
pairwise=CCMP
group=CCMP
auth_alg=OPEN
}
EOF
echo "--- Creating Wi-Fi AP with SSID=${NEW_SSID}"
nmcli con add type wifi ifname wlan0 mode ap con-name clover ssid $NEW_SSID autoconnect true \
&& nmcli con modify clover 802-11-wireless.band bg \
&& nmcli con modify clover ipv4.method shared ipv4.address 192.168.11.1/24 \
&& nmcli con modify clover ipv6.method disabled \
&& nmcli con modify clover wifi-sec.key-mgmt wpa-psk \
&& nmcli con modify clover wifi-sec.psk "cloverwifi" \
&& systemctl disable dnsmasq # disable dnsmasq to avoid conflicts with NetworkManager's dnsmasq
NEW_HOSTNAME=$(echo ${NEW_SSID} | tr '[:upper:]' '[:lower:]')
echo_stamp "Setting hostname to $NEW_HOSTNAME"
hostnamectl set-hostname $NEW_HOSTNAME
sed -i 's/127\.0\.1\.1.*/127.0.1.1\t'${NEW_HOSTNAME}' '${NEW_HOSTNAME}'.local/g' /etc/hosts
echo "--- Setting hostname to $NEW_HOSTNAME"
hostnamectl set-hostname $NEW_HOSTNAME \
&& sed -i 's/127\.0\.1\.1.*/127.0.1.1\t'${NEW_HOSTNAME}' '${NEW_HOSTNAME}'.local/g' /etc/hosts
# .local (mdns) hostname added to make it accesable when wlan and ethernet interfaces are down
echo_stamp "Enable ROS services"
echo "--- Enable ROS services"
systemctl enable roscore
systemctl enable clover
echo_stamp "Harware setup"
echo "--- Harware setup"
/root/hardware_setup.sh
echo_stamp "Remove init scripts"
echo "--- Remove init scripts"
rm /root/init_rpi.sh /root/hardware_setup.sh
echo_stamp "End of initialization of the image"

File diff suppressed because it is too large Load Diff

122
builder/image-build-ros.sh Executable file
View File

@@ -0,0 +1,122 @@
#! /usr/bin/env bash
#
# Script for building ROS packages from scratch
#
# Copyright (C) 2022 Copter Express Technologies
#
# Author: Oleg Kalachev <okalachev@gmail.com>
#
# Distributed under MIT License (available at https://opensource.org/licenses/MIT).
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
set -ex # exit on error, echo commands
# http://wiki.ros.org/Installation/Source
export ROS_DISTRO=noetic
. /etc/os-release # set $VERSION_CODENAME to Debian release code name
export ROS_OS_OVERRIDE=debian:$VERSION_CODENAME
export ROS_PYTHON_VERSION=3
export CLOVER_DEPS="tf tf2 tf2_ros tf2_geometry_msgs geometry_msgs sensor_msgs visualization_msgs libgeographiclib-dev mavros mavros_extras cv_camera cv_bridge rosbridge_server web_video_server tf2_web_republisher libxml2 libxslt python3-lxml dynamic_reconfigure image_transport image_proc image_geometry python-pymavlink ros_pytest"
export CLOVER_DEPS="$CLOVER_DEPS rostest python3-docopt image_publisher"
echo "=== Building ROS from scratch"
#echo "--- Adding sources"
# echo "deb http://packages.ros.org/ros/ubuntu $(lsb_release -sc) main" > /etc/apt/sources.list.d/ros-latest.list
# curl -s https://raw.githubusercontent.com/ros/rosdistro/master/ros.asc | sudo apt-key add -
#cp /etc/apt/trusted.gpg /etc/apt/trusted.gpg.d # https://askubuntu.com/a/1408456
apt-get update
apt-get install -y python3-distutils build-essential cmake git python3-pip python3-rosinstall-generator python3-vcstools python3-empy libpoco-dev
# install vcstool using pip
# curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py && python3 get-pip.py && rm get-pip.py
pip3 install -U --break-system-packages vcstool rosdep rosinstall-generator catkin-pkg future
# sudo rosdep init
# rm /etc/ros/rosdep/sources.list.d/20-default.list
rosdep init
rosdep update --os=debian:bullseye
# rm /etc/ros/rosdep/sources.list.d/20-default.list && rosdep init
# rosdep --os=debian:$VERSION_CODENAME update
echo "--- Create Catkin workspace to build ROS package"
mkdir ~/ros_catkin_ws
cd ~/ros_catkin_ws
echo "--- Download ROS sources"
rosinstall_generator ros_base $CLOVER_DEPS --rosdistro $ROS_DISTRO --deps --tar > noetic.rosinstall
mkdir ./src
vcs import --input noetic.rosinstall ./src
# https://answers.ros.org/question/343367/catkin-package-dependencies-issue-when-installing-ros-melodic-on-raspberry-pi-4/
#sudo apt remove python-rospkg
#sudo apt remove python-catkin-pkg
##sudo apt --fix-broken install
#sudo apt-get autoremove
#echo "--- Install catkin_pkg"
#cd
#git clone https://github.com/ros-infrastructure/catkin_pkg.git
#cd catkin_pkg
#python3 setup.py install
#cd ~/ros_catkin_ws
echo "--- Resolve dependencies"
rosdep install --from-paths ./src --ignore-packages-from-source --rosdistro $ROS_DISTRO -y --os=debian:bullseye --skip-keys="python3-catkin-pkg-modules libboost-thread python3-rosdep-modules" || true
echo "--- Install missing dependencies"
apt-get install -y liborocos-kdl1.5 geographiclib-tools libgeographiclib-dev
echo "-- Install geographiclib datasets"
wget https://raw.githubusercontent.com/mavlink/mavros/master/mavros/scripts/install_geographiclib_datasets.sh
chmod +x install_geographiclib_datasets.sh
./install_geographiclib_datasets.sh
echo "--- Apply patches"
wget https://github.com/ros/rosconsole/pull/58.patch
patch -p1 -d src/rosconsole < 58.patch
wget https://github.com/ros/ros_comm/pull/2353.patch
patch -p2 -d src/ros_comm < 2353.patch
wget https://github.com/AJahueyM/web_video_server/commit/5b722eb0822bcc3fe45fefe7b393b87bfe004417.patch
patch -p1 -d src/web_video_server < 5b722eb0822bcc3fe45fefe7b393b87bfe004417.patch
echo "--- Build ROS"
# https://github.com/ros/catkin/issues/863#issuecomment-290392074
./src/catkin/bin/catkin_make_isolated --install -DCMAKE_BUILD_TYPE=Release
# -DSETUPTOOLS_DEB_LAYOUT=OFF \
# --install-space=/opt/ros/$ROS_DISTRO
# source ~/ros_catkin_ws/install_isolated/setup.bash
#source /opt/ros/$ROS_DISTRO/setup.bash
#
#echo "--- List built ROS packages"
#set +x
#rospack list-names | while read line; do echo $line `rosversion $line`; done
#set -x
#
#echo "--- Build Debian packages"
#apt-get install -y python3-bloom debhelper dpkg-dev libtinyxml-dev
#
## add rosdep file to help bloom-generate resolve missing bookworm dependencies
#echo "yaml file:///etc/ros/rosdep/noetic-rosdep-clover.yaml" >> /etc/ros/rosdep/sources.list.d/20-default.list
#rosdep update
#
#pip3 install setuptools==45.2.0 # https://github.com/ros/catkin/issues/863#issuecomment-1000446018
#
#for file in `find . -name "package.xml" -not -path "*/debian/*"`; do
# cd $(dirname ${file})
# rm -rf debian
# bloom-generate rosdebian --os-name debian --os-version $VERSION_CODENAME --ros-distro $ROS_DISTRO --debug
# debian/rules binary # fakeroot is not needed as we are root
# cd -
#done
ls

View File

@@ -13,42 +13,22 @@
# copies or substantial portions of the Software.
#
set -e # Exit immidiately on non-zero result
set -ex # exit on error, echo commands
# 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-05-28/2021-05-07-raspios-buster-armhf-lite.zip"
SOURCE_IMAGE="https://downloads.raspberrypi.com/raspios_lite_armhf/images/raspios_lite_armhf-2024-03-15/2024-03-15-raspios-bookworm-armhf-lite.img.xz"
export DEBIAN_FRONTEND=${DEBIAN_FRONTEND:='noninteractive'}
export LANG=${LANG:='C.UTF-8'}
export LC_ALL=${LC_ALL:='C.UTF-8'}
echo_stamp() {
# TEMPLATE: echo_stamp <TEXT> <TYPE>
# TYPE: SUCCESS, ERROR, INFO
# More info there https://www.shellhacks.com/ru/bash-colors/
TEXT="$(date '+[%Y-%m-%d %H:%M:%S]') $1"
TEXT="\e[1m$TEXT\e[0m" # BOLD
case "$2" in
SUCCESS)
TEXT="\e[32m${TEXT}\e[0m";; # GREEN
ERROR)
TEXT="\e[31m${TEXT}\e[0m";; # RED
*)
TEXT="\e[34m${TEXT}\e[0m";; # BLUE
esac
echo -e ${TEXT}
}
BUILDER_DIR="/builder"
REPO_DIR="${BUILDER_DIR}/repo"
SCRIPTS_DIR="${REPO_DIR}/builder"
IMAGES_DIR="${REPO_DIR}/images"
[[ ! -d ${SCRIPTS_DIR} ]] && (echo_stamp "Directory ${SCRIPTS_DIR} doesn't exist" "ERROR"; exit 1)
[[ ! -d ${IMAGES_DIR} ]] && mkdir ${IMAGES_DIR} && echo_stamp "Directory ${IMAGES_DIR} was created successful" "SUCCESS"
[[ ! -d ${SCRIPTS_DIR} ]] && (echo "Error: directory ${SCRIPTS_DIR} doesn't exist"; exit 1)
[[ ! -d ${IMAGES_DIR} ]] && mkdir ${IMAGES_DIR} && echo "Directory ${IMAGES_DIR} was created successful"
if [[ -z ${TRAVIS_TAG} ]]; then IMAGE_VERSION="$(cd ${REPO_DIR}; git log --format=%h -1)"; else IMAGE_VERSION="${TRAVIS_TAG}"; fi
# IMAGE_VERSION="${TRAVIS_TAG:=$(cd ${REPO_DIR}; git log --format=%h -1)}"
@@ -64,15 +44,17 @@ get_image() {
local RPI_IMAGE_NAME=$(echo ${RPI_ZIP_NAME} | sed 's/zip/img/')
if [ ! -e "${BUILD_DIR}/${RPI_ZIP_NAME}" ]; then
echo_stamp "Downloading original Linux distribution"
echo "--- Downloading original Linux distribution"
wget --progress=dot:giga -O ${BUILD_DIR}/${RPI_ZIP_NAME} $2
echo_stamp "Downloading complete" "SUCCESS" \
else echo_stamp "Linux distribution already donwloaded"; fi
echo "--- Downloading complete" "SUCCESS"
else
echo "Linux distribution already downloaded"
fi
echo_stamp "Unzipping Linux distribution image" \
&& unzip -p ${BUILD_DIR}/${RPI_ZIP_NAME} ${RPI_IMAGE_NAME} > $1 \
&& echo_stamp "Unzipping complete" "SUCCESS" \
|| (echo_stamp "Unzipping was failed!" "ERROR"; exit 1)
echo "--- Unzipping Linux distribution image"
apt-get update --allow-releaseinfo-change
apt-get install -y xz-utils
unxz --stdout ${BUILD_DIR}/${RPI_ZIP_NAME} > $1
}
get_image ${IMAGE_PATH} ${SOURCE_IMAGE}
@@ -120,6 +102,7 @@ ${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/launch.
# ${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/kinetic-ros-clover.rosinstall' '/home/pi/ros_catkin_ws/'
# 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-build-ros.sh'
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} exec ${SCRIPTS_DIR}'/image-validate.sh'
${BUILDER_DIR}/image-resize.sh ${IMAGE_PATH}

View File

@@ -12,50 +12,33 @@
# copies or substantial portions of the Software.
#
set -e # Exit immidiately on non-zero result
set -ex # exit on error, echo commands
echo_stamp() {
# TEMPLATE: echo_stamp <TEXT> <TYPE>
# TYPE: SUCCESS, ERROR, INFO
echo "--- Move /etc/ld.so.preload out of the way"
mv /etc/ld.so.preload /etc/ld.so.preload.disabled-for-build
# More info there https://www.shellhacks.com/ru/bash-colors/
TEXT="$(date '+[%Y-%m-%d %H:%M:%S]') $1"
TEXT="\e[1m$TEXT\e[0m" # BOLD
case "$2" in
SUCCESS)
TEXT="\e[32m${TEXT}\e[0m";; # GREEN
ERROR)
TEXT="\e[31m${TEXT}\e[0m";; # RED
*)
TEXT="\e[34m${TEXT}\e[0m";; # BLUE
esac
echo -e ${TEXT}
}
echo_stamp "Write Clover information"
echo "--- Create pi user"
echo 'pi:$6$c70VpvPsVNCG0YR5$l5vWWLsLko9Kj65gcQ8qvMkuOoRkEagI90qi3F/Y7rm8eNYZHW8CY6BOIKwMH7a3YYzZYL90zf304cAHLFaZE0' > /boot/userconf.txt
echo "--- Write Clover information"
# Clover image version
echo "$1" >> /etc/clover_version
# Origin image file name
echo "${2%.*}" >> /etc/clover_origin
echo_stamp "Write magic script to /etc/rc.local"
echo "--- Write magic script to /etc/rc.local"
MAGIC_SCRIPT="sudo /root/init_rpi.sh; sudo sed -i '/sudo \\\/root\\\/init_rpi.sh/d' /etc/rc.local && sudo reboot"
sed -i "19a${MAGIC_SCRIPT}" /etc/rc.local
# TODO: remake to oneshot systemd service
# It needs for autosizer.sh & maybe that is correct
echo_stamp "Change boot partition"
echo "--- Change boot partition"
sed -i 's/root=[^ ]*/root=\/dev\/mmcblk0p2/' /boot/cmdline.txt
sed -i 's/.* \/boot vfat defaults 0 2$/\/dev\/mmcblk0p1 \/boot vfat defaults 0 2/' /etc/fstab
sed -i 's/.* \/boot\/firmware vfat defaults 0 2$/\/dev\/mmcblk0p1 \/boot\/firmware vfat defaults 0 2/' /etc/fstab
sed -i 's/.* \/ ext4 defaults,noatime 0 1$/\/dev\/mmcblk0p2 \/ ext4 defaults,noatime 0 1/' /etc/fstab
cat /boot/cmdline.txt
cat /etc/fstab
echo_stamp "Set max space for syslogs"
echo "--- Set max space for syslogs"
# https://unix.stackexchange.com/questions/139513/how-to-clear-journalctl
sed -i 's/#SystemMaxUse=/SystemMaxUse=200M/' /etc/systemd/journald.conf
echo_stamp "Move /etc/ld.so.preload out of the way"
mv /etc/ld.so.preload /etc/ld.so.preload.disabled-for-build
echo_stamp "End of init image"

View File

@@ -12,43 +12,20 @@
# copies or substantial portions of the Software.
#
set -e # Exit immidiately on non-zero result
echo_stamp() {
# TEMPLATE: echo_stamp <TEXT> <TYPE>
# TYPE: SUCCESS, ERROR, INFO
# More info there https://www.shellhacks.com/ru/bash-colors/
TEXT="$(date '+[%Y-%m-%d %H:%M:%S]') $1"
TEXT="\e[1m$TEXT\e[0m" # BOLD
case "$2" in
SUCCESS)
TEXT="\e[32m${TEXT}\e[0m";; # GREEN
ERROR)
TEXT="\e[31m${TEXT}\e[0m";; # RED
*)
TEXT="\e[34m${TEXT}\e[0m";; # BLUE
esac
echo -e ${TEXT}
}
echo_stamp "#1 Write STATIC to /etc/dhcpcd.conf"
set -ex # exit on error, echo commands
echo "--- Write static to /etc/dhcpcd.conf"
cat << EOF >> /etc/dhcpcd.conf
interface wlan0
static ip_address=192.168.11.1/24
EOF
echo_stamp "#2 Set wpa_supplicant country"
echo "--- Set wpa_supplicant country"
cat << EOF >> /etc/wpa_supplicant/wpa_supplicant.conf
country=GB
EOF
echo_stamp "#3 Write dhcp-config to /etc/dnsmasq.conf"
echo "--- Write dhcp-config to /etc/dnsmasq.conf"
cat << EOF >> /etc/dnsmasq.conf
interface=wlan0
address=/clover/coex/192.168.11.1
@@ -59,5 +36,3 @@ bogus-priv
domain-needed
quiet-dhcp6
EOF
echo_stamp "#4 End of network installation"

View File

@@ -13,46 +13,22 @@
# copies or substantial portions of the Software.
#
set -e # Exit immidiately on non-zero result
REPO=$1
REF=$2
INSTALL_ROS_PACK_SOURCES=$3
DISCOVER_ROS_PACK=$4
NUMBER_THREADS=$5
set -ex # exit on error, echo commands
# Current ROS distribution
ROS_DISTRO=noetic
echo_stamp() {
# TEMPLATE: echo_stamp <TEXT> <TYPE>
# TYPE: SUCCESS, ERROR, INFO
# More info there https://www.shellhacks.com/ru/bash-colors/
TEXT="$(date '+[%Y-%m-%d %H:%M:%S]') $1"
TEXT="\e[1m$TEXT\e[0m" # BOLD
case "$2" in
SUCCESS)
TEXT="\e[32m${TEXT}\e[0m";; # GREEN
ERROR)
TEXT="\e[31m${TEXT}\e[0m";; # RED
*)
TEXT="\e[34m${TEXT}\e[0m";; # BLUE
esac
echo -e ${TEXT}
}
. /etc/os-release # set $VERSION_CODENAME to Debian release code name
export ROS_OS_OVERRIDE=debian:$VERSION_CODENAME
# https://gist.github.com/letmaik/caa0f6cc4375cbfcc1ff26bd4530c2a3
# https://github.com/travis-ci/travis-build/blob/master/lib/travis/build/templates/header.sh
my_travis_retry() {
local result=0
local count=1
local max_count=50
local max_count=5
while [ $count -le $max_count ]; do
[ $result -ne 0 ] && {
echo -e "\nThe command \"$@\" failed. Retrying, $count of $max_count.\n" >&2
echo -e "\nThe command \"$*\" failed. Retrying, $count of $max_count.\n" >&2
}
# ! { } ignores set -e, see https://stackoverflow.com/a/4073372
! { "$@"; result=$?; }
@@ -62,124 +38,136 @@ my_travis_retry() {
done
[ $count -gt $max_count ] && {
echo -e "\nThe command \"$@\" failed $max_count times.\n" >&2
echo -e "\nThe command \"$*\" failed $max_count times.\n" >&2
}
return $result
}
echo "--- Install rosdep"
my_travis_retry pip3 install -U rosdep
# TODO: 'kinetic-rosdep-clover.yaml' should add only if we use our repo?
echo_stamp "Init rosdep"
echo "--- Init rosdep"
my_travis_retry rosdep init
# FIXME: Re-add this after missing packages are built
echo "yaml file:///etc/ros/rosdep/${ROS_DISTRO}-rosdep-clover.yaml" >> /etc/ros/rosdep/sources.list.d/20-default.list
echo "--- Update rosdep"
echo "yaml file:///etc/ros/rosdep/${ROS_DISTRO}-rosdep-clover.yaml" >> /etc/ros/rosdep/sources.list.d/10-clover.list
my_travis_retry rosdep update
echo_stamp "Populate rosdep for ROS user"
my_travis_retry sudo -u pi rosdep update
echo "--- Populate rosdep for ROS user"
my_travis_retry sudo -u pi ROS_OS_OVERRIDE=debian:$VERSION_CODENAME rosdep update
export ROS_IP='127.0.0.1' # needed for running tests
# echo_stamp "Reconfiguring Clover repository for simplier unshallowing"
# echo "Reconfiguring Clover repository for simplier unshallowing"
cd /home/pi/catkin_ws/src/clover
git config remote.origin.fetch "+refs/heads/*:refs/remotes/origin/*"
# This is sort of a hack to force "custom" packages to be installed - the ones built by COEX, linked against OpenCV 4.2
# I **wish** OpenCV would not be such a mess, but, well, here we are.
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}-image-publisher=1.15.3-0buster \
ros-${ROS_DISTRO}-web-video-server=0.2.1-0buster
apt-mark hold \
ros-${ROS_DISTRO}-compressed-image-transport \
ros-${ROS_DISTRO}-cv-bridge \
ros-${ROS_DISTRO}-cv-camera \
ros-${ROS_DISTRO}-image-publisher \
ros-${ROS_DISTRO}-web-video-server
# echo "--- 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.1-0buster \
# ros-${ROS_DISTRO}-image-publisher=1.15.3-0buster \
# ros-${ROS_DISTRO}-web-video-server=0.2.1-0buster
# apt-mark hold \
# ros-${ROS_DISTRO}-compressed-image-transport \
# ros-${ROS_DISTRO}-cv-bridge \
# ros-${ROS_DISTRO}-cv-camera \
# ros-${ROS_DISTRO}-image-publisher \
# ros-${ROS_DISTRO}-web-video-server
echo_stamp "Installing libboost-dev" # https://travis-ci.org/github/CopterExpress/clover/jobs/766318908#L6536
my_travis_retry apt-get install -y --no-install-recommends libboost-dev libboost-all-dev
#echo "--- Installing libboost-dev" # https://travis-ci.org/github/CopterExpress/clover/jobs/766318908#L6536
#my_travis_retry apt-get install -y --no-install-recommends libboost-dev libboost-all-dev
echo_stamp "Build and install Clover"
echo "--- Build and install Clover"
cd /home/pi/catkin_ws
# Don't try to install gazebo_ros
my_travis_retry rosdep install -y --from-paths src --ignore-src --rosdistro ${ROS_DISTRO} --os=debian:buster \
--skip-keys=gazebo_ros --skip-keys=gazebo_plugins
my_travis_retry rosdep install -y --from-paths src --ignore-src --rosdistro ${ROS_DISTRO} --os=debian:$VERSION_CODENAME \
--skip-keys="gazebo_ros gazebo_plugins"
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
catkin_make -j2 -DCMAKE_BUILD_TYPE=RelWithDebInfo
source devel/setup.bash
echo_stamp "Install clever package (for backwards compatibility)"
echo "--- Install clever package (for backwards compatibility)"
cd /home/pi/catkin_ws/src/clover/builder/assets/clever
./setup.py install
rm -rf build # remove build artifacts
echo_stamp "Build Clover documentation"
echo "--- Build Clover documentation"
cd /home/pi/catkin_ws/src/clover
builder/assets/install_gitbook.sh
gitbook install
gitbook build
# replace assets copy to assets symlink to save space
rm -rf _book/assets && ln -s ../docs/assets _book/assets
touch node_modules/CATKIN_IGNORE docs/CATKIN_IGNORE _book/CATKIN_IGNORE clover/www/CATKIN_IGNORE apps/CATKIN_IGNORE # ignore documentation files by catkin
npm cache clean --force
echo_stamp "Installing additional ROS packages"
echo "--- Installing additional ROS packages"
my_travis_retry apt-get install -y --no-install-recommends \
ros-${ROS_DISTRO}-dynamic-reconfigure \
ros-${ROS_DISTRO}-rosbridge-suite \
ros-${ROS_DISTRO}-rosserial \
ros-${ROS_DISTRO}-usb-cam \
ros-${ROS_DISTRO}-vl53l1x \
ros-${ROS_DISTRO}-ws281x \
ros-${ROS_DISTRO}-libcamera-ros \
ros-${ROS_DISTRO}-rosshow \
ros-${ROS_DISTRO}-cmake-modules \
ros-${ROS_DISTRO}-image-view
ros-${ROS_DISTRO}-image-view \
ros-${ROS_DISTRO}-nodelet-topic-tools \
ros-${ROS_DISTRO}-stereo-msgs \
ros-${ROS_DISTRO}-vision-msgs
# TODO move GeographicLib datasets to Mavros debian package
echo_stamp "Install GeographicLib datasets (needed for mavros)" \
echo "--- Install GeographicLib datasets (needed for mavros)" \
&& wget -qO- https://raw.githubusercontent.com/mavlink/mavros/master/mavros/scripts/install_geographiclib_datasets.sh | bash
echo_stamp "Running tests"
echo "--- Running tests"
export ROS_IP='127.0.0.1' # needed for running tests
cd /home/pi/catkin_ws
# FIXME: Investigate failing tests
catkin_make run_tests #&& catkin_test_results
echo_stamp "Change permissions for catkin_ws"
echo "--- Change permissions for catkin_ws"
chown -Rf pi:pi /home/pi/catkin_ws
echo_stamp "Make \$HOME/examples symlink"
echo "--- Update www"
sudo -u pi sh -c ". devel/setup.sh && rosrun clover www"
echo "--- 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"
echo "--- 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"
echo "--- Make udev rules symlink"
ln -s "$(catkin_find clover udev --first-only)"/* /lib/udev/rules.d/
echo_stamp "Setup ROS environment"
echo "--- Setup ROS environment"
cat << EOF >> /home/pi/.bashrc
LANG='C.UTF-8'
LC_ALL='C.UTF-8'
export ROS_HOSTNAME=\`hostname\`.local
export ROS_OS_OVERRIDE=debian:bookworm
source /opt/ros/${ROS_DISTRO}/setup.bash
source /home/pi/catkin_ws/devel/setup.bash
EOF
#echo_stamp "Removing local apt mirror"
# Restore original sources.list
#mv /var/sources.list.bak /etc/apt/sources.list
# Clean apt cache
apt-get clean -qq > /dev/null
# Remove local mirror repository key
#apt-key del COEX-MIRROR
echo "--- Cleanup apt"
apt-get autoremove --purge -y
apt-get clean
echo_stamp "END of ROS INSTALLATION"
echo "--- Cleanup pip"
pip3 cache purge
echo "--- Cleanup /tmp"
rm -rf /tmp/*

View File

@@ -12,27 +12,9 @@
# copies or substantial portions of the Software.
#
set -e # Exit immidiately on non-zero result
set -ex # exit on error, echo commands
echo_stamp() {
# TEMPLATE: echo_stamp <TEXT> <TYPE>
# TYPE: SUCCESS, ERROR, INFO
# More info there https://www.shellhacks.com/ru/bash-colors/
TEXT="$(date '+[%Y-%m-%d %H:%M:%S]') $1"
TEXT="\e[1m${TEXT}\e[0m" # BOLD
case "$2" in
SUCCESS)
TEXT="\e[32m${TEXT}\e[0m";; # GREEN
ERROR)
TEXT="\e[31m${TEXT}\e[0m";; # RED
*)
TEXT="\e[34m${TEXT}\e[0m";; # BLUE
esac
echo -e ${TEXT}
}
. /etc/os-release # set $VERSION_CODENAME to Debian release code name
# https://gist.github.com/letmaik/caa0f6cc4375cbfcc1ff26bd4530c2a3
# https://github.com/travis-ci/travis-build/blob/master/lib/travis/build/templates/header.sh
@@ -41,7 +23,7 @@ my_travis_retry() {
local count=1
while [ $count -le 3 ]; do
[ $result -ne 0 ] && {
echo -e "\n${ANSI_RED}The command \"$@\" failed. Retrying, $count of 3.${ANSI_RESET}\n" >&2
echo -e "\n${ANSI_RED}The command \"$*\" failed. Retrying, $count of 3.${ANSI_RESET}\n" >&2
}
# ! { } ignores set -e, see https://stackoverflow.com/a/4073372
! { "$@"; result=$?; }
@@ -51,41 +33,39 @@ my_travis_retry() {
done
[ $count -gt 3 ] && {
echo -e "\n${ANSI_RED}The command \"$@\" failed 3 times.${ANSI_RESET}\n" >&2
echo -e "\n${ANSI_RED}The command \"$*\" failed 3 times.${ANSI_RESET}\n" >&2
}
return $result
}
echo_stamp "Increase apt retries"
echo "--- Increase apt retries"
echo "APT::Acquire::Retries \"3\";" > /etc/apt/apt.conf.d/80-retries
echo_stamp "Install apt keys & repos"
echo "--- Install apt keys & repos"
# TODO: This STDOUT consist 'OK'
apt-get update \
&& apt-get install --no-install-recommends -y dirmngr > /dev/null \
&& apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-key C1CF6E31E6BADE8868B172B4F42ED6FBAB17C654
echo "deb http://packages.ros.org/ros/ubuntu buster main" > /etc/apt/sources.list.d/ros-latest.list
# echo "deb http://packages.ros.org/ros/ubuntu buster main" > /etc/apt/sources.list.d/ros-latest.list
wget -O - 'http://packages.coex.tech/key.asc' | apt-key add -
echo 'deb http://packages.coex.tech buster main' >> /etc/apt/sources.list
echo "deb http://packages.coex.tech $VERSION_CODENAME main" >> /etc/apt/sources.list
echo_stamp "Update apt cache"
echo "--- Update apt cache"
# TODO: FIX ERROR: /usr/bin/apt-key: 596: /usr/bin/apt-key: cannot create /dev/null: Permission denied
apt-get update
# && apt upgrade -y
# Let's retry fetching those packages several times, just in case
echo_stamp "Software installing"
my_travis_retry apt-get install --no-install-recommends -y cmake-data=3.13.4-1 cmake=3.13.4-1 # FIXME: using older CMake due to https://travis-ci.org/github/CopterExpress/clover/jobs/764367665#L6984
echo "--- Install software"
my_travis_retry apt-get install --no-install-recommends -y cmake-data cmake
my_travis_retry apt-get install --no-install-recommends -y \
unzip \
zip \
ipython \
ipython3 \
screen \
byobu \
@@ -96,62 +76,57 @@ dnsmasq \
tmux \
tree \
vim \
libjpeg8 \
tcpdump \
libpoco-dev \
libzbar0 \
python3-rosdep \
python3-rosinstall-generator \
python3-wstool \
python3-rosinstall \
build-essential \
libffi-dev \
monkey \
pigpio python-pigpio python3-pigpio \
pigpio python3-pigpio \
i2c-tools \
espeak espeak-data python-espeak python3-espeak \
espeak espeak-data python3-espeak \
ntpdate \
python-dev \
python3-dev \
python-systemd \
mjpg-streamer \
python3-opencv
xxd \
python3-dev \
python3-systemd \
python3-opencv \
python3-pip
#libjpeg8 \
# Deny byobu to check available updates
sed -i "s/updates_available//" /usr/share/byobu/status/status
# sed -i "s/updates_available//" /home/pi/.byobu/status
echo_stamp "Installing pip"
curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py
curl https://bootstrap.pypa.io/pip/2.7/get-pip.py -o get-pip2.py
python3 get-pip.py
python get-pip2.py
rm get-pip.py get-pip2.py
#my_travis_retry pip install --upgrade pip
#my_travis_retry pip3 install --upgrade pip
echo_stamp "Make sure both pip and pip3 are installed"
echo "--- Make sure pip is installed"
pip --version
pip3 --version
echo_stamp "Install and enable Butterfly (web terminal)"
echo_stamp "Workaround for tornado >= 6.0 breaking butterfly"
echo "--- Enable installing packages with pip"
mv /usr/lib/python3.11/EXTERNALLY-MANAGED /usr/lib/python3.11/EXTERNALLY-MANAGED.old
echo "--- Install and enable Butterfly (web terminal)"
export CRYPTOGRAPHY_DONT_BUILD_RUST=1
my_travis_retry pip3 install tornado==5.1.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]
systemctl enable butterfly.socket
echo_stamp "Install ws281x library"
echo "--- Install ws281x library"
my_travis_retry pip3 install --prefer-binary rpi_ws281x
echo_stamp "Setup Monkey"
echo "--- Setup Monkey"
mv /etc/monkey/sites/default /etc/monkey/sites/default.orig
mv /root/monkey /etc/monkey/sites/default
sed -i 's/SymLink Off/SymLink On/' /etc/monkey/monkey.conf
systemctl enable monkey.service
echo_stamp "Install Node.js"
echo "--- Install Node.js"
cd /home/pi
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
@@ -159,28 +134,24 @@ cp -R node-v10.15.0-linux-armv6l/* /usr/local/
rm -rf node-v10.15.0-linux-armv6l/
rm node-v10.15.0-linux-armv6l.tar.gz
echo_stamp "Installing ptvsd"
my_travis_retry pip install ptvsd
my_travis_retry pip3 install ptvsd
echo "--- Installing debugpy"
my_travis_retry pip3 install debugpy
echo_stamp "Installing pyzbar"
my_travis_retry pip install pyzbar
echo "--- Installing pyzbar"
my_travis_retry pip3 install pyzbar
echo_stamp "Add .vimrc"
echo "--- Add .vimrc"
cat << EOF > /home/pi/.vimrc
set mouse-=a
syntax on
autocmd BufNewFile,BufRead *.launch set syntax=xml
EOF
echo_stamp "Change default keyboard layout to US"
echo "--- Change default keyboard layout to US"
sed -i 's/XKBLAYOUT="gb"/XKBLAYOUT="us"/g' /etc/default/keyboard
echo_stamp "Attempting to kill dirmngr"
echo "--- Attempting to kill dirmngr"
gpgconf --kill dirmngr
# dirmngr is only used by apt-key, so we can safely kill it.
# We ignore pkill's exit value as well.
pkill -9 -f dirmngr || true
echo_stamp "End of software installation"

View File

@@ -12,28 +12,33 @@
# copies or substantial portions of the Software.
#
set -ex
set -ex # exit on error, echo commands
echo "Run image tests"
echo "--- Run image tests"
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 "Largest packages installed"
sudo -E sh -c 'apt-get install -y debian-goodies'
dpigs -H -n 100
echo "Cleanup apt"
apt-get autoremove --purge -y
apt-get clean
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

@@ -2,6 +2,7 @@
# validate all required modules installed
import os
import rospy
from geometry_msgs.msg import PoseStamped
from sensor_msgs.msg import Range, BatteryState
@@ -22,6 +23,11 @@ from clover.srv import GetTelemetry, Navigate, NavigateGlobal, SetPosition, SetV
from led_msgs.srv import SetLEDs
from led_msgs.msg import LEDStateArray, LEDState
from aruco_pose.msg import Marker, MarkerArray, Point2D
from vision_msgs.msg import BoundingBox2D, BoundingBox2DArray, BoundingBox3D, BoundingBox3DArray, \
Classification2D, Classification3D, Detection2D, Detection2DArray, Detection3D, Detection3DArray, \
ObjectHypothesis, ObjectHypothesisWithPose, VisionInfo
from clover import long_callback
import dynamic_reconfigure.client
@@ -31,9 +37,15 @@ import tf2_geometry_msgs
import VL53L1X
import pymavlink
from pymavlink import mavutil
import rpi_ws281x
import pigpio
from image_geometry import PinholeCameraModel, StereoCameraModel
# from espeak import espeak
from pyzbar import pyzbar
import docopt
import geopy
import flask
print(cv2.getBuildInformation())
if not os.environ.get('VM'):
import rpi_ws281x
import pigpio

View File

@@ -1,21 +1,14 @@
#!/usr/bin/env bash
set -ex
set -ex # exit on error, echo commands
# TODO: validate versions
# 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,38 +18,75 @@ 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
espeak --version
xxd --version
systemctl --version
if [ -z $VM ]; then
# rpi only software
python --version
[[ $(python -c 'import sys;print(sys.version_info.major)') == "3" ]]
python -m debugpy --version
python3 -m debugpy --version
pigpiod -v
i2cdetect -V
/usr/local/bin/butterfly.server.py --help
mjpg_streamer --version
fi
# ros stuff
roscore -h
catkin_find
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.15" ]]
if [ -z $VM ]; then
# rosversion compressed_image_transport
rosversion rosshow
rosversion vl53l1x
rosversion rosserial
[[ $(rosversion cv_camera) == "0.6.1" ]] # patched version with init fix
fi
# determine user home directory
[ $VM ] && H="/home/clover" || H="/home/pi"
# test basic ros tool work
source $H/catkin_ws/devel/setup.bash
roscd
rosrun
rosmsg
rossrv
rosnode || [ $? -eq 64 ] # usage output code is 64
rostopic || [ $? -eq 64 ]
rosservice || [ $? -eq 64 ]
rosparam
roslaunch -h
# 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

@@ -1,8 +0,0 @@
#!/usr/bin/env python3
# Make sure our Python 3 software is installed
import cv2
from pyzbar import pyzbar
print(cv2.getBuildInformation())

View File

@@ -18,7 +18,7 @@ EXCLUDE = 'rviz.png', 'ssid.png', 'sitl_docker_demo.png', 'qgc-params.png', 'but
'qgc-battery.png', 'qgc-radio.png', 'qgc-cal-acc.png', 'qgc-esc.png', 'qgc-cal-compass.png', \
'qgc.png', 'qgc-parameters.png', 'clever4-front-white-large.png', 'qgc-modes.png', \
'qgc-requires-setup.png', 'clever4-front-white.png', 'clever4-kit-white.png', '26_1.png', 'battery_holder.stl', \
'camera_case.stl', 'camera_mount.stl'
'camera_case.stl', 'camera_mount.stl', 'grip_right.stl', 'grip_left.stl'
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,10 +25,13 @@ find_package(catkin REQUIRED COMPONENTS
tf2_ros
image_transport
cv_bridge
dynamic_reconfigure
)
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake")
# https://github.com/mavlink/mavros/blob/7f1a8/mavros/CMakeLists.txt#L42
set(CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH};/usr/share/cmake/geographiclib")
find_package(GeographicLib REQUIRED)
# Workaround for OpenCV 3/4 support
@@ -51,7 +55,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 ##
@@ -78,11 +82,10 @@ find_package(OpenCV ${_opencv_version} REQUIRED
## * add every package in MSG_DEP_SET to generate_messages(DEPENDENCIES ...)
## Generate messages in the 'msg' folder
# add_message_files(
# FILES
# Message1.msg
# Message2.msg
# )
add_message_files(
FILES
State.msg
)
## Generate services in the 'srv' folder
add_service_files(
@@ -90,6 +93,9 @@ add_service_files(
GetTelemetry.srv
Navigate.srv
NavigateGlobal.srv
SetAltitude.srv
SetYaw.srv
SetYawRate.srv
SetPosition.srv
SetVelocity.srv
SetAttitude.srv
@@ -126,10 +132,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 +216,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
@@ -303,4 +310,5 @@ endif()
if (CATKIN_ENABLE_TESTING)
find_package(rostest REQUIRED)
add_rostest(test/basic.test)
add_rostest(test/offboard.test)
endif()

View File

@@ -0,0 +1,65 @@
# Generated from fisheye_cam.yaml by rescale_camera_info.py
camera_matrix:
cols: 3
data:
- 415.5985593268293
- 0.0
- 400.0
- 0.0
- 312.35267324512984
- 225.0
- 0.0
- 0.0
- 1.0
rows: 3
camera_name: main_camera_optical
distortion_coefficients:
cols: 8
data:
- 0.215356885
- -0.117472846
- -0.000306197672
- -0.000109444025
- -0.00453657258
- 0.573090623
- -0.127574577
- -0.0286125589
- 0.0
- 0.0
- 0.0
- 0.0
- 0.0
- 0.0
rows: 1
distortion_model: plumb_bob
image_height: 720
image_width: 1280
projection_matrix:
cols: 4
data:
- 415.5985593268293
- 0.0
- 405.4752811707317
- 0.0
- 0.0
- 312.35267324512984
- 205.91677004464282
- 0.0
- 0.0
- 0.0
- 1.0
- 0.0
rows: 3
rectification_matrix:
cols: 3
data:
- 1
- 0
- 0
- 0
- 1
- 0
- 0
- 0
- 1
rows: 3

View File

@@ -0,0 +1,65 @@
# Generated from fisheye_cam.yaml by rescale_camera_info.py
camera_matrix:
cols: 3
data:
- 1246.7956779804879
- 0.0
- 1200.0
- 0.0
- 702.7935148015422
- 506.25
- 0.0
- 0.0
- 1.0
rows: 3
camera_name: main_camera_optical
distortion_coefficients:
cols: 8
data:
- 0.215356885
- -0.117472846
- -0.000306197672
- -0.000109444025
- -0.00453657258
- 0.573090623
- -0.127574577
- -0.0286125589
- 0.0
- 0.0
- 0.0
- 0.0
- 0.0
- 0.0
rows: 1
distortion_model: plumb_bob
image_height: 1080
image_width: 1920
projection_matrix:
cols: 4
data:
- 1246.7956779804879
- 0.0
- 1216.4258435121951
- 0.0
- 0.0
- 702.7935148015422
- 463.31273260044634
- 0.0
- 0.0
- 0.0
- 1.0
- 0.0
rows: 3
rectification_matrix:
cols: 3
data:
- 1
- 0
- 0
- 0
- 1
- 0
- 0
- 0
- 1
rows: 3

View File

@@ -0,0 +1,65 @@
# Generated from fisheye_cam.yaml by rescale_camera_info.py
camera_matrix:
cols: 3
data:
- 5049.522495820976
- 0.0
- 4860.0
- 0.0
- 2846.313734946246
- 2050.3125
- 0.0
- 0.0
- 1.0
rows: 3
camera_name: main_camera_optical
distortion_coefficients:
cols: 8
data:
- 0.215356885
- -0.117472846
- -0.000306197672
- -0.000109444025
- -0.00453657258
- 0.573090623
- -0.127574577
- -0.0286125589
- 0.0
- 0.0
- 0.0
- 0.0
- 0.0
- 0.0
rows: 1
distortion_model: plumb_bob
image_height: 1944
image_width: 2592
projection_matrix:
cols: 4
data:
- 5049.522495820976
- 0.0
- 4926.52466622439
- 0.0
- 0.0
- 2846.313734946246
- 1876.4165670318075
- 0.0
- 0.0
- 0.0
- 1.0
- 0.0
rows: 3
rectification_matrix:
cols: 3
data:
- 1
- 0
- 0
- 0
- 1
- 0
- 0
- 0
- 1
rows: 3

View File

@@ -0,0 +1,65 @@
# Generated from fisheye_cam.yaml by rescale_camera_info.py
camera_matrix:
cols: 3
data:
- 166.23942373073172
- 0.0
- 160.0
- 0.0
- 166.5880923974026
- 120.0
- 0.0
- 0.0
- 1.0
rows: 3
camera_name: main_camera_optical
distortion_coefficients:
cols: 8
data:
- 0.215356885
- -0.117472846
- -0.000306197672
- -0.000109444025
- -0.00453657258
- 0.573090623
- -0.127574577
- -0.0286125589
- 0.0
- 0.0
- 0.0
- 0.0
- 0.0
- 0.0
rows: 1
distortion_model: plumb_bob
image_height: 240
image_width: 320
projection_matrix:
cols: 4
data:
- 166.23942373073172
- 0.0
- 162.19011246829268
- 0.0
- 0.0
- 166.5880923974026
- 109.82227735714285
- 0.0
- 0.0
- 0.0
- 1.0
- 0.0
rows: 3
rectification_matrix:
cols: 3
data:
- 1
- 0
- 0
- 0
- 1
- 0
- 0
- 0
- 1
rows: 3

View File

@@ -0,0 +1,65 @@
# Generated from fisheye_cam.yaml by rescale_camera_info.py
camera_matrix:
cols: 3
data:
- 30297.13497492585
- 0.0
- 29160.0
- 0.0
- 12808.411807258106
- 9226.40625
- 0.0
- 0.0
- 1.0
rows: 3
camera_name: main_camera_optical
distortion_coefficients:
cols: 8
data:
- 0.215356885
- -0.117472846
- -0.000306197672
- -0.000109444025
- -0.00453657258
- 0.573090623
- -0.127574577
- -0.0286125589
- 0.0
- 0.0
- 0.0
- 0.0
- 0.0
- 0.0
rows: 1
distortion_model: plumb_bob
image_height: 2160
image_width: 3840
projection_matrix:
cols: 4
data:
- 30297.13497492585
- 0.0
- 29559.14799734634
- 0.0
- 0.0
- 12808.411807258106
- 8443.874551643134
- 0.0
- 0.0
- 0.0
- 1.0
- 0.0
rows: 3
rectification_matrix:
cols: 3
data:
- 1
- 0
- 0
- 0
- 1
- 0
- 0
- 0
- 1
rows: 3

View File

@@ -0,0 +1,65 @@
# Generated from fisheye_cam.yaml by rescale_camera_info.py
camera_matrix:
cols: 3
data:
- 192008.0929035926
- 0.0
- 184801.5
- 0.0
- 81119.941445968
- 58433.90625
- 0.0
- 0.0
- 1.0
rows: 3
camera_name: main_camera_optical
distortion_coefficients:
cols: 8
data:
- 0.215356885
- -0.117472846
- -0.000306197672
- -0.000109444025
- -0.00453657258
- 0.573090623
- -0.127574577
- -0.0286125589
- 0.0
- 0.0
- 0.0
- 0.0
- 0.0
- 0.0
rows: 1
distortion_model: plumb_bob
image_height: 3040
image_width: 4056
projection_matrix:
cols: 4
data:
- 192008.0929035926
- 0.0
- 187331.10043318244
- 0.0
- 0.0
- 81119.941445968
- 53477.87216040651
- 0.0
- 0.0
- 0.0
- 1.0
- 0.0
rows: 3
rectification_matrix:
cols: 3
data:
- 1
- 0
- 0
- 0
- 1
- 0
- 0
- 0
- 1
rows: 3

View File

@@ -0,0 +1,65 @@
# Generated from fisheye_cam.yaml by rescale_camera_info.py
camera_matrix:
cols: 3
data:
- 166.23942373073172
- 0.0
- 160.0
- 0.0
- 166.5880923974026
- 120.0
- 0.0
- 0.0
- 1.0
rows: 3
camera_name: main_camera_optical
distortion_coefficients:
cols: 8
data:
- 0.215356885
- -0.117472846
- -0.000306197672
- -0.000109444025
- -0.00453657258
- 0.573090623
- -0.127574577
- -0.0286125589
- 0.0
- 0.0
- 0.0
- 0.0
- 0.0
- 0.0
rows: 1
distortion_model: plumb_bob
image_height: 480
image_width: 640
projection_matrix:
cols: 4
data:
- 166.23942373073172
- 0.0
- 162.19011246829268
- 0.0
- 0.0
- 166.5880923974026
- 109.82227735714285
- 0.0
- 0.0
- 0.0
- 1.0
- 0.0
rows: 3
rectification_matrix:
cols: 3
data:
- 1
- 0
- 0
- 0
- 1
- 0
- 0
- 0
- 1
rows: 3

View File

@@ -0,0 +1,65 @@
# Generated from fisheye_cam.yaml by rescale_camera_info.py
camera_matrix:
cols: 3
data:
- 207.79927966341464
- 0.0
- 200.0
- 0.0
- 208.23511549675322
- 150.0
- 0.0
- 0.0
- 1.0
rows: 3
camera_name: main_camera_optical
distortion_coefficients:
cols: 8
data:
- 0.215356885
- -0.117472846
- -0.000306197672
- -0.000109444025
- -0.00453657258
- 0.573090623
- -0.127574577
- -0.0286125589
- 0.0
- 0.0
- 0.0
- 0.0
- 0.0
- 0.0
rows: 1
distortion_model: plumb_bob
image_height: 600
image_width: 800
projection_matrix:
cols: 4
data:
- 207.79927966341464
- 0.0
- 202.73764058536585
- 0.0
- 0.0
- 208.23511549675322
- 137.27784669642855
- 0.0
- 0.0
- 0.0
- 1.0
- 0.0
rows: 3
rectification_matrix:
cols: 3
data:
- 1
- 0
- 0
- 0
- 1
- 0
- 0
- 0
- 1
rows: 3

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

View File

@@ -1,18 +0,0 @@
# taken from: https://github.com/mavlink/mavros/blob/master/libmavconn/cmake/Modules/FindGeographicLib.cmake
# Look for GeographicLib
#
# Set
# GEOGRAPHICLIB_FOUND = TRUE
# GeographicLib_INCLUDE_DIRS = /usr/local/include
# GeographicLib_LIBRARIES = /usr/local/lib/libGeographic.so
# GeographicLib_LIBRARY_DIRS = /usr/local/lib
find_path (GeographicLib_INCLUDE_DIRS NAMES GeographicLib/Config.h)
find_library (GeographicLib_LIBRARIES NAMES Geographic)
include (FindPackageHandleStandardArgs)
find_package_handle_standard_args (GeographicLib DEFAULT_MSG
GeographicLib_LIBRARIES GeographicLib_INCLUDE_DIRS)
mark_as_advanced (GeographicLib_LIBRARIES GeographicLib_INCLUDE_DIRS)

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'
elif 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

@@ -16,11 +16,8 @@ set_attitude = rospy.ServiceProxy('set_attitude', srv.SetAttitude)
set_rates = rospy.ServiceProxy('set_rates', srv.SetRates)
land = rospy.ServiceProxy('land', Trigger)
def navigate_wait(x=0, y=0, z=0, yaw=float('nan'), yaw_rate=0, speed=0.5, \
frame_id='body', tolerance=0.2, auto_arm=False):
res = navigate(x=x, y=y, z=z, yaw=yaw, yaw_rate=yaw_rate, speed=speed, \
frame_id=frame_id, auto_arm=auto_arm)
def navigate_wait(x=0, y=0, z=0, yaw=math.nan, speed=0.5, frame_id='body', tolerance=0.2, auto_arm=False):
res = navigate(x=x, y=y, z=z, yaw=yaw, speed=speed, frame_id=frame_id, auto_arm=auto_arm)
if not res.success:
return res

View File

@@ -0,0 +1,91 @@
# This example makes the drone find and follow the red circle.
# To test in the simulator, place 'Red Circle' model on the floor.
# More information: https://clover.coex.tech/red_circle
# Input topic: main_camera/image_raw (camera image)
# Output topics:
# cv/mask (red color mask)
# cv/red_circle (position of the center of the red circle in 3D space)
import rospy
import cv2
import numpy as np
from math import nan
from sensor_msgs.msg import Image, CameraInfo
from geometry_msgs.msg import PointStamped, Point
from cv_bridge import CvBridge
from clover import long_callback, srv
import tf2_ros
import tf2_geometry_msgs
import image_geometry
rospy.init_node('cv', disable_signals=True) # disable signals to allow interrupting with ctrl+c
get_telemetry = rospy.ServiceProxy('get_telemetry', srv.GetTelemetry)
set_position = rospy.ServiceProxy('set_position', srv.SetPosition)
bridge = CvBridge()
tf_buffer = tf2_ros.Buffer()
tf_listener = tf2_ros.TransformListener(tf_buffer)
mask_pub = rospy.Publisher('~mask', Image, queue_size=1)
point_pub = rospy.Publisher('~red_circle', PointStamped, queue_size=1)
# read camera info
camera_model = image_geometry.PinholeCameraModel()
camera_model.fromCameraInfo(rospy.wait_for_message('main_camera/camera_info', CameraInfo))
def img_xy_to_point(xy, dist):
xy_rect = camera_model.rectifyPoint(xy)
ray = camera_model.projectPixelTo3dRay(xy_rect)
return Point(x=ray[0] * dist, y=ray[1] * dist, z=dist)
def get_center_of_mass(mask):
M = cv2.moments(mask)
if M['m00'] == 0:
return None
return M['m10'] // M['m00'], M['m01'] // M['m00']
follow_red_circle = False
@long_callback
def image_callback(msg):
img = bridge.imgmsg_to_cv2(msg, 'bgr8')
img_hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
# we need to use two ranges for red color
mask1 = cv2.inRange(img_hsv, (0, 150, 150), (15, 255, 255))
mask2 = cv2.inRange(img_hsv, (160, 150, 150), (180, 255, 255))
# combine two masks using bitwise OR
mask = cv2.bitwise_or(mask1, mask2)
# publish the mask
if mask_pub.get_num_connections() > 0:
mask_pub.publish(bridge.cv2_to_imgmsg(mask, 'mono8'))
# calculate x and y of the circle
xy = get_center_of_mass(mask)
if xy is None:
return
# calculate and publish the position of the circle in 3D space
altitude = get_telemetry('terrain').z
xy3d = img_xy_to_point(xy, altitude)
target = PointStamped(header=msg.header, point=xy3d)
point_pub.publish(target)
if follow_red_circle:
# follow the target
setpoint = tf_buffer.transform(target, 'map', timeout=rospy.Duration(0.2))
set_position(x=setpoint.point.x, y=setpoint.point.y, z=nan, yaw=nan, frame_id=setpoint.header.frame_id)
# process each camera frame:
image_sub = rospy.Subscriber('main_camera/image_raw', Image, image_callback, queue_size=1)
rospy.loginfo('Hit enter to follow the red circle')
input()
follow_red_circle = True
rospy.spin()

View File

@@ -8,16 +8,22 @@
<!-- 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 main_camera_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"/>
<remap from="map_markers" to="aruco_map/map"/>
<param name="dictionary" value="2"/> <!-- DICT_4X4_250 -->
<param name="estimate_poses" value="true"/>
<param name="send_tf" 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="use_map_markers" value="$(arg aruco_map)"/>
<param name="known_vertical" value="map" if="$(eval placement == 'floor' or placement == 'ceiling')"/>
<param name="flip_vertical" value="true" 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,13 +32,13 @@
</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 main_camera_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"/>
<param name="map" value="$(find aruco_pose)/map/$(arg map)"/>
<param name="known_tilt" value="map" if="$(eval placement == 'floor')"/>
<param name="known_tilt" value="map_flipped" if="$(eval placement == 'ceiling')"/>
<param name="known_vertical" value="map" if="$(eval placement == 'floor' or placement == 'ceiling')"/>
<param name="flip_vertical" value="true" if="$(eval placement == 'ceiling')"/>
<param name="image_axis" value="true"/>
<param name="frame_id" value="aruco_map_detected" if="$(arg aruco_vpe)"/>
<param name="frame_id" value="aruco_map" unless="$(arg aruco_vpe)"/>
@@ -41,11 +47,11 @@
</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>
</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,7 +34,10 @@
</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 main_camera_nodelet_manager" if="$(arg optical_flow)" clear_params="true" output="screen" respawn="true">
@@ -41,16 +45,13 @@
<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="true"/>
</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"/>
<param name="terrain_frame_mode" value="range"/>
</node>
<!-- main camera -->
@@ -71,6 +72,9 @@
<param name="pass_statuses" type="yaml" value="[0, 6, 7, 11]"/>
</node>
<!-- rangefinder's frame -->
<node pkg="tf2_ros" type="static_transform_publisher" name="rangefinder_frame" args="0 0 -0.05 0 1.5707963268 0 base_link rangefinder" if="$(arg rangefinder_vl53l1x)"/>
<!-- led strip -->
<include file="$(find clover)/launch/led.launch" if="$(arg led)">
<arg name="simulator" value="$(arg simulator)"/>
@@ -85,8 +89,4 @@
<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

@@ -21,7 +21,8 @@
</node>
<!-- high level led effects control, events notification with leds -->
<node pkg="clover" name="led_effect" type="led" ns="led" clear_params="true" output="screen" if="$(arg led_effect)">
<node pkg="clover" name="led_effect" type="led" clear_params="true" output="screen" if="$(arg led_effect)">
<param name="led" value="led"/>
<param name="blink_rate" value="2"/>
<param name="fade_period" value="0.5"/>
<param name="rainbow_period" value="5"/>

View File

@@ -3,7 +3,15 @@
<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="type" default="libcamera"/> <!-- camera interface: libcamera, v4l2 -->
<arg name="camera_id" default="0"/> <!-- libcamera camera id -->
<arg name="device" default="/dev/video0"/> <!-- v4l2 device path -->
<arg name="width" default="320"/>
<arg name="height" default="240"/>
<arg name="fps" default="40"/>
<arg name="throttled_topic" default="true"/> <!-- enable throttled image topic -->
<arg name="throttled_topic_rate" default="5.0"/> <!-- throttled image topic rate -->
<arg name="rectify" default="false"/> <!-- enable rectification -->
<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"/>
@@ -23,24 +31,49 @@
<param name="num_worker_threads" value="2"/>
</node>
<!-- camera node -->
<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">
<!-- camera node using libcamera -->
<node pkg="nodelet" type="nodelet" name="main_camera" args="load libcamera_ros/LibcameraRos main_camera_nodelet_manager" output="screen" clear_params="true" if="$(eval not simulator and type == 'libcamera')" respawn="true">
<param name="camera_name" value=""/>
<param name="camera_id" value="$(arg camera_id)"/>
<param name="frame_id" value="main_camera_optical"/>
<param name="calib_url" type="string" value="file://$(find clover)/camera_info/fisheye_cam_$(arg width)x$(arg height).yaml"/>
<param name="stream_role" value="still"/>
<param name="pixel_format" value="RGB888"/>
<param name="use_ros_time" value="true"/>
<param name="resolution/width" value="$(arg width)"/>
<param name="resolution/height" value="$(arg height)"/>
<!-- see: https://github.com/ctu-mrs/libcamera_ros/blob/b3645/config/param.yaml#L19 -->
<param name="control/fps" value="$(arg fps)"/>
</node>
<!-- old camera node for v4l2 (cv_camera) -->
<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" if="$(eval not simulator and type == 'v4l2')" 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"/>
<param name="rate" value="100"/> <!-- poll rate -->
<param name="cv_cap_prop_fps" value="40"/> <!-- camera FPS -->
<param name="cv_cap_prop_fps" value="$(arg fps)"/> <!-- camera FPS -->
<param name="capture_delay" value="0.02"/> <!-- approximate delay on frame retrieving -->
<param name="rescale_camera_info" value="true"/> <!-- automatically rescale camera calibration info -->
<!-- camera resolution -->
<param name="image_width" value="320"/>
<param name="image_height" value="240"/>
<param name="image_width" value="$(arg width)"/>
<param name="image_height" value="$(arg height)"/>
</node>
<!-- camera visualization markers -->
<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)"/>
<!-- rectified image topic -->
<node pkg="nodelet" type="nodelet" name="rectify" args="load image_proc/rectify main_camera_nodelet_manager" if="$(arg rectify)">
<remap from="image_mono" to="main_camera/image_raw"/>
<remap from="camera_info" to="main_camera/camera_info"/>
<remap from="image_rect" to="main_camera/image_rect"/>
</node>
</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
@@ -74,9 +77,6 @@
covariance: 1 # cm
</rosparam>
<!-- Rangefinders frame -->
<node pkg="tf2_ros" type="static_transform_publisher" name="rangefinder_frame" args="0 0 -0.05 0 1.5707963268 0 base_link rangefinder"/>
<!-- Copter visualization -->
<node name="visualization" pkg="mavros_extras" type="visualization" if="$(arg viz)">
<remap to="mavros/local_position/pose" from="local_position"/>

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>
<!-- shortcut for running the simulation (`roslaunch clover simulator.launch`) -->
<include file="$(find clover_simulation)/launch/simulator.launch"/>
</launch>

38
clover/msg/State.msg Normal file
View File

@@ -0,0 +1,38 @@
uint8 MODE_NONE = 0
uint8 MODE_NAVIGATE = 1
uint8 MODE_NAVIGATE_GLOBAL = 2
uint8 MODE_POSITION = 3
uint8 MODE_VELOCITY = 4
uint8 MODE_ATTITUDE = 5
uint8 MODE_RATES = 6
uint8 YAW_MODE_YAW = 0
uint8 YAW_MODE_YAW_RATE = 1
uint8 YAW_MODE_YAW_TOWARDS = 2
# type of offboard control
uint8 mode
uint8 yaw_mode
# targets
float32 x
float32 y
float32 z
float32 speed
float32 lat
float32 lon
float32 vx
float32 vy
float32 vz
float32 roll
float32 pitch
float32 yaw
float32 roll_rate
float32 pitch_rate
float32 yaw_rate
float32 thrust
# frames of reference
string xy_frame_id
string z_frame_id
string yaw_frame_id

View File

@@ -1,7 +1,7 @@
<?xml version="1.0"?>
<package format="3">
<name>clover</name>
<version>0.21.1</version>
<version>0.24.0</version>
<description>The Clover package</description>
<maintainer email="okalachev@gmail.com">Oleg Kalachev</maintainer>
@@ -37,11 +37,15 @@
<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>
<depend>image_proc</depend>
<depend>image_geometry</depend>
<exec_depend>python-pymavlink</exec_depend>
<!-- Use test_depend for packages you need only for testing: -->
<!-- <test_depend>gtest</test_depend> -->
<test_depend>ros_pytest</test_depend>
<!-- The export tag contains other, unspecified, tags -->
<export>

View File

@@ -1,5 +1,4 @@
flask==1.1.1
docopt==0.6.2
geopy==1.11.0
smbus2==0.3.0
VL53L1X==0.0.5
flask
geopy
smbus2
VL53L1X

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,90 @@
#!/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
try:
flow_client = dynamic_reconfigure.client.Client('optical_flow', timeout=2)
except rospy.ROSException:
flow_client = None
print('Cannot configure optical flow, skip')
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=math.nan, speed=0.5, frame_id='body', tolerance=0.2, auto_arm=False):
res = navigate(x=x, y=y, z=z, yaw=yaw, 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()
if flow_client:
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,99 @@
#!/usr/bin/env python3
import rospy
import math
from math import nan, inf
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_yaw = handle_response(rospy.ServiceProxy('set_yaw', srv.SetYaw))
set_yaw_rate = handle_response(rospy.ServiceProxy('set_yaw_rate', srv.SetYawRate))
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, speed=0.5, frame_id='body', tolerance=0.2, auto_arm=False):
res = navigate(x=x, y=y, z=z, yaw=yaw, 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° using set_yaw [enter] ')
set_yaw(yaw=-math.pi / 2, frame_id='navigate_target')
rospy.sleep(3)
input('Use set_attitude to fly backwards [enter]')
set_attitude(roll=0, pitch=-0.3, 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(roll=0.3, pitch=0, 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 set_yaw_rate [enter]')
set_yaw_rate(yaw_rate=-1)
rospy.sleep(2 * math.pi)
set_position(frame_id='body')
input('Return to start point heading forward [enter]')
navigate_wait(x=start.x, y=start.y, z=start.z, yaw=inf, 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

@@ -31,7 +31,6 @@ ros::Time start_time;
double blink_rate, blink_fast_rate, flash_delay, fade_period, wipe_period, rainbow_period;
double low_battery_threshold;
std::vector<std::string> error_ignore;
bool blink_state;
led_msgs::SetLEDs set_leds;
led_msgs::LEDStateArray state, start_state;
ros::ServiceClient set_leds_srv;
@@ -87,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);
@@ -222,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;
}
@@ -310,18 +309,22 @@ int main(int argc, char **argv)
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);
std::string led; // led namespace
nh_priv.param("led", led, std::string("led"));
if (!led.empty()) led += "/";
ros::service::waitForService(led + "set_leds"); // cannot work without set_leds service
set_leds_srv = nh.serviceClient<led_msgs::SetLEDs>(led + "set_leds", true);
// wait for leds count info
handleState(*ros::topic::waitForMessage<led_msgs::LEDStateArray>("state", nh));
handleState(*ros::topic::waitForMessage<led_msgs::LEDStateArray>(led + "state", nh));
auto state_sub = nh.subscribe("state", 1, &handleState);
auto state_sub = nh.subscribe(led + "state", 1, &handleState);
auto set_effect = nh.advertiseService("set_effect", &setEffect);
auto set_effect = nh.advertiseService(led + "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,6 +78,7 @@ 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);
@@ -81,6 +91,17 @@ private:
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");
}
@@ -107,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;
@@ -140,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);
@@ -194,9 +223,9 @@ private:
uint32_t integration_time_us = integration_time.toSec() * 1.0e6;
// Calculate flow gyro
flow_.integrated_xgyro = NAN;
flow_.integrated_ygyro = NAN;
flow_.integrated_zgyro = NAN;
flow_.integrated_xgyro = flow_gyro_default_;
flow_.integrated_ygyro = flow_gyro_default_;
flow_.integrated_zgyro = flow_gyro_default_;
if (calc_flow_gyro_) {
try {
@@ -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
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

@@ -9,13 +9,14 @@
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
import os
import os, sys
import math
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,86 @@ 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()
# formatting colors
if sys.stdout.isatty():
GREY = '\033[90m'
GREEN = '\033[92m'
RED = '\033[31m'
END = '\033[0m'
else:
GREY = GREEN = RED = END = ''
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(GREY + name + END + ': ' + report['info'])
if not thread_local.reports:
rospy.loginfo(GREY + name + END + ': ' + GREEN + 'OK' + END)
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 RED + '???' + END
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, strict=True):
try:
res = param_get(param_id=name)
except rospy.ServiceException as e:
@@ -95,13 +115,19 @@ def get_param(name):
return None
if not res.success:
failure('unable to retrieve PX4 parameter %s', name)
if strict:
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 +172,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°',
@@ -191,31 +235,36 @@ def check_fcu():
state = rospy.wait_for_message('mavros/state', State, timeout=3)
if not state.connected:
failure('no connection to the FCU (check wiring)')
info('fcu_url = %s', rospy.get_param('mavros/fcu_url', '?'))
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:
info('selected estimator: LPE')
fuse = get_param('LPE_FUSION')
fuse = int(get_param('LPE_FUSION'))
if fuse & (1 << 4):
info('LPE_FUSION: land detector fusion is enabled')
else:
@@ -247,21 +296,43 @@ 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]
# number of cells 1 means this is overall voltage
if len(battery.cell_voltage) == 1:
n_cells = get_param('BAT1_N_CELLS', strict=False)
if n_cells is None:
# older PX4
n_cells = get_param('BAT_N_CELLS', strict=True)
cell /= n_cells
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)')
failure('no MAVROS state')
fcu_url = rospy.get_param('mavros/fcu_url', '?')
if fcu_url == '/dev/px4fmu':
if not os.path.exists('/lib/udev/rules.d/99-px4fmu.rules'):
info('udev rules are not installed, install udev rules or change usb_device to /dev/ttyACM0 in mavros.launch')
else:
info('udev did\'t recognize px4fmu device, check wiring or change usb_device to /dev/ttyACM0 in mavros.launch')
info('fcu_url = %s', rospy.get_param('mavros/fcu_url', '?'))
def describe_direction(v):
@@ -328,7 +399,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,19 +409,24 @@ 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'))
info('aruco_detect/length = %g m', rospy.get_param('aruco_detect/length', '?'))
except KeyError:
failure('aruco_detect/length parameter is not set')
known_tilt = rospy.get_param('aruco_detect/known_tilt', '')
if known_tilt == 'map':
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)
known_vertical = rospy.get_param('aruco_detect/known_vertical', '')
flip_vertical = rospy.get_param('aruco_detect/flip_vertical', False)
description = ''
if known_vertical == 'map' and not flip_vertical:
description = ' (all markers are on the floor)'
elif known_vertical == 'map' and flip_vertical:
description = ' (all markers are on the ceiling)'
info('aruco_detect/known_vertical = %s', known_vertical)
info('aruco_detect/flip_vertical = %s%s', flip_vertical, description)
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
@@ -359,42 +435,61 @@ def check_aruco():
return
if is_process_running('aruco_map', full=True):
known_tilt = rospy.get_param('aruco_map/known_tilt', '')
if known_tilt == 'map':
known_tilt += ' (marker\'s map is on the floor)'
elif known_tilt == 'map_flipped':
known_tilt += ' (marker\'s map is on the ceiling)'
info('aruco_map/known_tilt = %s', known_tilt)
known_vertical = rospy.get_param('aruco_map/known_vertical', '')
flip_vertical = rospy.get_param('aruco_map/flip_vertical', False)
description = ''
if known_vertical == 'map' and not flip_vertical:
description += ' (markers map is on the floor)'
elif known_vertical == 'map' and flip_vertical:
description += ' (markers map is on the ceiling)'
info('aruco_map/known_vertical = %s', known_vertical)
info('aruco_map/flip_vertical = %s%s', flip_vertical, description)
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_vertical', '') == 'map' \
and rospy.get_param('aruco_map/flip_vertical', False):
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,26 +501,37 @@ 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)
fuse = get_param('LPE_FUSION')
info('vision yaw weight: %s', ff(vision_yaw_w))
fuse = int(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):
failure('vision position fusion is disabled, change EKF2_AID_MASK parameter')
if not fuse & (1 << 4):
failure('vision yaw fusion is disabled, change EKF2_AID_MASK parameter')
ev_ctrl = get_param('EKF2_EV_CTRL', strict=False)
if ev_ctrl is not None: # PX4 after v1.14
ev_ctrl = int(ev_ctrl)
if not ev_ctrl & (1 << 0):
failure('vision horizontal position fusion is disabled, change EKF2_EV_CTRL parameter')
if not ev_ctrl & (1 << 1):
failure('vision vertical position fusion is disabled, change EKF2_EV_CTRL parameter')
if not ev_ctrl & (1 << 3):
failure('vision yaw fusion is disabled, change EKF2_EV_CTRL parameter')
else: # PX4 before v1.14
fuse = int(get_param('EKF2_AID_MASK'))
if not fuse & (1 << 3):
failure('vision position fusion is disabled, change EKF2_AID_MASK parameter')
if not fuse & (1 << 4):
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 +589,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,13 +629,25 @@ 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:
gps_ctrl = get_param('EKF2_GPS_CTRL', strict=False)
if gps_ctrl is not None: # PX4 after v1.14
if int(gps_ctrl) & (1 << 0):
failure('GPS fusion enabled may suppress vision position aiding, change EKF2_GPS_CTRL')
else: # PX4 before v1.14
if int(get_param('EKF2_AID_MASK', 0)) & (1 << 0):
failure('GPS fusion enabled may suppress vision position aiding, change EKF2_AID_MASK')
@check('Optical flow')
def check_optical_flow():
if not is_process_running('optical_flow', full=True):
info('optical_flow is not running')
return
# TODO:check FPS!
try:
rospy.wait_for_message('mavros/px4flow/raw/send', OpticalFlowRad, timeout=0.5)
@@ -531,40 +655,49 @@ 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')
fuse = int(get_param('LPE_FUSION'))
if not fuse & (1 << 1):
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')
if not fuse & (1 << 1):
failure('optical flow fusion is disabled, change EKF2_AID_MASK parameter')
delay = get_param('EKF2_OF_DELAY')
of_ctrl = get_param('EKF2_OF_CTRL', strict=False)
if of_ctrl is not None: # PX4 after v1.14
if of_ctrl == 0:
failure('optical flow fusion is disabled, change EKF2_OF_CTRL')
else: # PX4 before v1.14
fuse = int(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', 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,37 +721,44 @@ def check_rangefinder():
est = get_param('SYS_MC_EST_GROUP')
if est == 1:
fuse = get_param('LPE_FUSION')
fuse = int(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:
info('"pub agl as lpos down" in LPE_FUSION is enabled, operating over flat surface')
elif est == 2:
hgt = get_param('EKF2_HGT_MODE')
hgt = get_param('EKF2_HGT_REF', strict=False)
if hgt is None: # PX4 before v1.14
hgt = get_param('EKF2_HGT_MODE')
if hgt != 2:
info('EKF2_HGT_MODE != Range sensor, NOT operating over flat surface')
else:
info('EKF2_HGT_MODE = Range sensor, operating over flat surface')
aid = get_param('EKF2_RNG_AID')
if aid != 1:
info('EKF2_RNG_AID != 1, range sensor aiding disabled')
else:
info('EKF2_RNG_AID = 1, range sensor aiding enabled')
aid = get_param('EKF2_RNG_AID', strict=False)
if aid is not None: # PX4 before v1.14
if aid != 1:
info('EKF2_RNG_AID != 1, range sensor aiding disabled')
else:
info('EKF2_RNG_AID = 1, range sensor aiding enabled')
@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 +767,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 +789,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 +827,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 +860,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 +886,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 +924,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 +941,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

@@ -23,12 +23,14 @@
#include <tf2_ros/static_transform_broadcaster.h>
#include <tf2_geometry_msgs/tf2_geometry_msgs.h>
#include <std_srvs/Trigger.h>
#include <geometry_msgs/PointStamped.h>
#include <geometry_msgs/PoseStamped.h>
#include <geometry_msgs/TwistStamped.h>
#include <geometry_msgs/Vector3Stamped.h>
#include <geometry_msgs/QuaternionStamped.h>
#include <sensor_msgs/NavSatFix.h>
#include <sensor_msgs/BatteryState.h>
#include <sensor_msgs/Range.h>
#include <mavros_msgs/CommandBool.h>
#include <mavros_msgs/SetMode.h>
#include <mavros_msgs/PositionTarget.h>
@@ -37,14 +39,19 @@
#include <mavros_msgs/State.h>
#include <mavros_msgs/StatusText.h>
#include <mavros_msgs/ManualControl.h>
#include <mavros_msgs/Altitude.h>
#include <clover/GetTelemetry.h>
#include <clover/Navigate.h>
#include <clover/NavigateGlobal.h>
#include <clover/SetAltitude.h>
#include <clover/SetYaw.h>
#include <clover/SetYawRate.h>
#include <clover/SetPosition.h>
#include <clover/SetVelocity.h>
#include <clover/SetAttitude.h>
#include <clover/SetRates.h>
#include <clover/State.h>
using std::string;
using std::isnan;
@@ -54,6 +61,7 @@ using namespace clover;
using mavros_msgs::PositionTarget;
using mavros_msgs::AttitudeTarget;
using mavros_msgs::Thrust;
using mavros_msgs::Altitude;
// tf2
tf2_ros::Buffer tf_buffer;
@@ -61,6 +69,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;
@@ -78,35 +87,43 @@ float default_speed;
bool auto_release;
bool land_only_in_offboard, nav_from_sp, check_kill_switch;
std::map<string, string> reference_frames;
string terrain_frame_mode;
// Publishers
ros::Publisher attitude_pub, attitude_raw_pub, position_pub, position_raw_pub, rates_pub, thrust_pub;
ros::Publisher attitude_pub, attitude_raw_pub, position_pub, position_raw_pub, rates_pub, thrust_pub, state_pub;
// Service clients
ros::ServiceClient arming, set_mode;
// Containers
ros::Timer setpoint_timer;
tf::Quaternion tq;
PoseStamped position_msg;
PositionTarget position_raw_msg;
AttitudeTarget att_raw_msg;
Thrust thrust_msg;
TwistStamped rates_msg;
//TwistStamped rates_msg;
TransformStamped target, setpoint;
geometry_msgs::TransformStamped body;
geometry_msgs::TransformStamped terrain;
// State
PoseStamped nav_start;
PoseStamped setpoint_position, setpoint_position_transformed;
Vector3Stamped setpoint_velocity, setpoint_velocity_transformed;
QuaternionStamped setpoint_attitude, setpoint_attitude_transformed;
float setpoint_yaw_rate;
PointStamped setpoint_position;
PointStamped setpoint_altitude;
Vector3Stamped setpoint_velocity;
float setpoint_yaw, setpoint_roll, setpoint_pitch;
Vector3 setpoint_rates;
string yaw_frame_id;
float setpoint_thrust;
float nav_speed;
float setpoint_lat = NAN, setpoint_lon = NAN;
bool busy = false;
bool wait_armed = false;
bool nav_from_sp_flag = false;
// Last published
PoseStamped setpoint_pose_local;
Vector3Stamped setpoint_velocity_local;
float yaw_local;
enum setpoint_type_t {
NONE,
NAVIGATE,
@@ -114,7 +131,10 @@ enum setpoint_type_t {
POSITION,
VELOCITY,
ATTITUDE,
RATES
RATES,
_ALTITUDE,
_YAW,
_YAW_RATE,
};
enum setpoint_type_t setpoint_type = NONE;
@@ -149,6 +169,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));
@@ -166,7 +189,7 @@ void handleLocalPosition(const PoseStamped& pose)
{
local_position = pose;
publishBodyFrame();
// TODO: terrain?, home?
// TODO: home?
}
// wait for transform without interrupting publishing setpoints
@@ -181,6 +204,30 @@ inline bool waitTransform(const string& target, const string& source,
ros::spinOnce();
r.sleep();
}
return false;
}
void publishTerrain(const double distance, const ros::Time& stamp)
{
if (!waitTransform(local_frame, body.child_frame_id, stamp, ros::Duration(0.1))) return;
auto t = tf_buffer.lookupTransform(local_frame, body.child_frame_id, stamp);
t.child_frame_id = terrain.child_frame_id;
t.transform.translation.z -= distance;
static_transform_broadcaster->sendTransform(t);
}
void handleAltitude(const Altitude& alt)
{
if (!std::isfinite(alt.bottom_clearance)) return;
publishTerrain(alt.bottom_clearance, alt.header.stamp);
}
void handleRange(const Range& range)
{
if (!std::isfinite(range.range)) return;
// TODO: check it's facing down
publishTerrain(range.range, range.header.stamp);
}
#define TIMEOUT(msg, timeout) (msg.header.stamp.isZero() || (ros::Time::now() - msg.header.stamp > timeout))
@@ -202,11 +249,11 @@ bool getTelemetry(GetTelemetry::Request& req, GetTelemetry::Response& res)
res.vx = NAN;
res.vy = NAN;
res.vz = NAN;
res.pitch = NAN;
res.roll = NAN;
res.pitch = NAN;
res.yaw = NAN;
res.pitch_rate = NAN;
res.roll_rate = NAN;
res.pitch_rate = NAN;
res.yaw_rate = NAN;
res.voltage = NAN;
res.cell_voltage = NAN;
@@ -336,20 +383,20 @@ inline float getDistance(const Point& from, const Point& to)
return hypot(from.x - to.x, from.y - to.y, from.z - to.z);
}
void getNavigateSetpoint(const ros::Time& stamp, float speed, Point& nav_setpoint)
void getNavigateSetpoint(const ros::Time& stamp, const float speed, Point& nav_setpoint)
{
if (wait_armed) {
// don't start navigating if we're waiting arming
nav_start.header.stamp = stamp;
}
float distance = getDistance(nav_start.pose.position, setpoint_position_transformed.pose.position);
float distance = getDistance(nav_start.pose.position, setpoint_pose_local.pose.position);
float time = distance / speed;
float passed = std::min((stamp - nav_start.header.stamp).toSec() / time, 1.0);
nav_setpoint.x = nav_start.pose.position.x + (setpoint_position_transformed.pose.position.x - nav_start.pose.position.x) * passed;
nav_setpoint.y = nav_start.pose.position.y + (setpoint_position_transformed.pose.position.y - nav_start.pose.position.y) * passed;
nav_setpoint.z = nav_start.pose.position.z + (setpoint_position_transformed.pose.position.z - nav_start.pose.position.z) * passed;
nav_setpoint.x = nav_start.pose.position.x + (setpoint_pose_local.pose.position.x - nav_start.pose.position.x) * passed;
nav_setpoint.y = nav_start.pose.position.y + (setpoint_pose_local.pose.position.y - nav_start.pose.position.y) * passed;
nav_setpoint.z = nav_start.pose.position.z + (setpoint_pose_local.pose.position.z - nav_start.pose.position.z) * passed;
}
PoseStamped globalToLocal(double lat, double lon)
@@ -380,44 +427,101 @@ PoseStamped globalToLocal(double lat, double lon)
return pose;
}
// publish navigate_target frame
void publishTarget(ros::Time stamp, bool _static = false)
{
bool single_frame = (setpoint_position.header.frame_id == setpoint_altitude.header.frame_id);
// handle yaw for target frame
if (setpoint_yaw_type == YAW || setpoint_yaw_type == YAW_RATE) { // use last set yaw for yaw_rate
if (setpoint_altitude.header.frame_id == yaw_frame_id) {
target.transform.rotation = tf::createQuaternionMsgFromYaw(setpoint_yaw);
} else {
single_frame = false;
target.transform.rotation = tf::createQuaternionMsgFromYaw(yaw_local);
}
} else if (setpoint_yaw_type == TOWARDS) {
single_frame = false;
target.transform.rotation = tf::createQuaternionMsgFromYaw(yaw_local);
}
if (_static && single_frame) {
// publish at user's command, if all frames are the same
target.header.frame_id = setpoint_position.header.frame_id;
target.header.stamp = stamp;
target.transform.translation.x = setpoint_position.point.x;
target.transform.translation.y = setpoint_position.point.y;
target.transform.translation.z = setpoint_position.point.z;
} else if (!_static) {
// publish at each iteration, if frames are different
target.header = setpoint_pose_local.header;
target.transform.translation.x = setpoint_pose_local.pose.position.x;
target.transform.translation.y = setpoint_pose_local.pose.position.y;
target.transform.translation.z = setpoint_pose_local.pose.position.z;
}
static_transform_broadcaster->sendTransform(target);
}
void publish(const ros::Time stamp)
{
if (setpoint_type == NONE) return;
position_raw_msg.header.stamp = stamp;
thrust_msg.header.stamp = stamp;
rates_msg.header.stamp = stamp;
try {
// transform position and/or yaw
if (setpoint_type == NAVIGATE || setpoint_type == NAVIGATE_GLOBAL || setpoint_type == POSITION || setpoint_type == VELOCITY || setpoint_type == ATTITUDE) {
setpoint_position.header.stamp = stamp;
tf_buffer.transform(setpoint_position, setpoint_position_transformed, local_frame, ros::Duration(0.05));
// transform position
if (setpoint_type == NAVIGATE || setpoint_type == NAVIGATE_GLOBAL || setpoint_type == POSITION) {
setpoint_position.header.stamp = stamp;
setpoint_altitude.header.stamp = stamp;
// transform xy
try {
auto xy = tf_buffer.transform(setpoint_position, local_frame, ros::Duration(0.05));
setpoint_pose_local.header = xy.header;
setpoint_pose_local.pose.position.x = xy.point.x;
setpoint_pose_local.pose.position.y = xy.point.y;
} catch (tf2::TransformException& ex) {
// can't transform xy, use last known
ROS_WARN_THROTTLE(10, "can't transform: %s", ex.what());
}
// transform velocity
if (setpoint_type == VELOCITY) {
setpoint_velocity.header.stamp = stamp;
tf_buffer.transform(setpoint_velocity, setpoint_velocity_transformed, local_frame, ros::Duration(0.05));
// transform altitude
try {
setpoint_pose_local.pose.position.z = tf_buffer.transform(setpoint_altitude, local_frame, ros::Duration(0.05)).point.z;
} catch (tf2::TransformException& ex) {
// can't transform altitude, use last known
ROS_WARN_THROTTLE(10, "can't transform: %s", ex.what());
}
} catch (const tf2::TransformException& e) {
ROS_WARN_THROTTLE(10, "can't transform");
}
// transform yaw
if (setpoint_yaw_type == YAW) {
try {
QuaternionStamped q;
q.header.stamp = stamp;
q.header.frame_id = yaw_frame_id;
q.quaternion = tf::createQuaternionMsgFromYaw(setpoint_yaw);
yaw_local = tf2::getYaw(tf_buffer.transform(q, local_frame, ros::Duration(0.05)).quaternion);
} catch (tf2::TransformException& ex) {
// can't transform yaw, use last known
ROS_WARN_THROTTLE(10, "can't transform: %s", ex.what());
}
}
// compute navigate setpoint
if (setpoint_type == NAVIGATE || setpoint_type == NAVIGATE_GLOBAL) {
position_msg.pose.orientation = setpoint_position_transformed.pose.orientation; // copy yaw
getNavigateSetpoint(stamp, nav_speed, position_msg.pose.position);
if (setpoint_yaw_type == TOWARDS) {
double yaw_towards = atan2(position_msg.pose.position.y - nav_start.pose.position.y,
position_msg.pose.position.x - nav_start.pose.position.x);
position_msg.pose.orientation = tf::createQuaternionMsgFromRollPitchYaw(0, 0, yaw_towards);
yaw_local = atan2(position_msg.pose.position.y - nav_start.pose.position.y,
position_msg.pose.position.x - nav_start.pose.position.x);
}
position_msg.pose.orientation = tf::createQuaternionMsgFromYaw(yaw_local);
}
if (setpoint_type == POSITION) {
position_msg = setpoint_position_transformed;
position_msg = setpoint_pose_local;
position_msg.pose.orientation = tf::createQuaternionMsgFromYaw(yaw_local);
}
if (setpoint_type == POSITION || setpoint_type == NAVIGATE || setpoint_type == NAVIGATE_GLOBAL) {
@@ -434,14 +538,14 @@ void publish(const ros::Time stamp)
PositionTarget::IGNORE_AFY +
PositionTarget::IGNORE_AFZ +
PositionTarget::IGNORE_YAW;
position_raw_msg.yaw_rate = setpoint_yaw_rate;
position_raw_msg.yaw_rate = setpoint_rates.z;
position_raw_msg.position = position_msg.pose.position;
position_raw_pub.publish(position_raw_msg);
}
// publish setpoint frame
if (!setpoint.child_frame_id.empty()) {
if (setpoint.header.stamp == position_msg.header.stamp) {
if (setpoint.header.stamp >= position_msg.header.stamp) {
return; // avoid TF_REPEATED_DATA warnings
}
@@ -453,9 +557,22 @@ void publish(const ros::Time stamp)
setpoint.header.stamp = position_msg.header.stamp;
transform_broadcaster->sendTransform(setpoint);
}
// publish dynamic target frame
publishTarget(stamp);
}
if (setpoint_type == VELOCITY) {
// transform velocity to local frame
setpoint_velocity.header.stamp = stamp;
try {
setpoint_velocity_local = tf_buffer.transform(setpoint_velocity, local_frame, ros::Duration(0.05));
} catch (tf2::TransformException& ex) {
// can't transform velocity, use last known
ROS_WARN_THROTTLE(10, "can't transform: %s", ex.what());
}
// publish velocity
position_raw_msg.type_mask = PositionTarget::IGNORE_PX +
PositionTarget::IGNORE_PY +
PositionTarget::IGNORE_PZ +
@@ -463,14 +580,22 @@ void publish(const ros::Time stamp)
PositionTarget::IGNORE_AFY +
PositionTarget::IGNORE_AFZ;
position_raw_msg.type_mask += setpoint_yaw_type == YAW ? PositionTarget::IGNORE_YAW_RATE : PositionTarget::IGNORE_YAW;
position_raw_msg.velocity = setpoint_velocity_transformed.vector;
position_raw_msg.yaw = tf2::getYaw(setpoint_position_transformed.pose.orientation);
position_raw_msg.yaw_rate = setpoint_yaw_rate;
position_raw_msg.velocity = setpoint_velocity_local.vector;
position_raw_msg.yaw = yaw_local;
position_raw_msg.yaw_rate = setpoint_rates.z;
position_raw_pub.publish(position_raw_msg);
}
if (setpoint_type == ATTITUDE) {
attitude_pub.publish(setpoint_position_transformed);
PoseStamped msg;
msg.header.stamp = stamp;
msg.header.frame_id = local_frame;
msg.pose.orientation = tf::createQuaternionMsgFromRollPitchYaw(setpoint_roll, setpoint_pitch, yaw_local);
attitude_pub.publish(msg);
Thrust thrust_msg;
thrust_msg.header.stamp = stamp;
thrust_msg.thrust = setpoint_thrust;
thrust_pub.publish(thrust_msg);
}
@@ -479,11 +604,12 @@ void publish(const ros::Time stamp)
// thrust_pub.publish(thrust_msg);
// mavros rates topics waits for rates in local frame
// use rates in body frame for simplicity
AttitudeTarget att_raw_msg;
att_raw_msg.header.stamp = stamp;
att_raw_msg.header.frame_id = fcu_frame;
att_raw_msg.type_mask = AttitudeTarget::IGNORE_ATTITUDE;
att_raw_msg.body_rate = rates_msg.twist.angular;
att_raw_msg.thrust = thrust_msg.thrust;
att_raw_msg.body_rate = setpoint_rates;
att_raw_msg.thrust = setpoint_thrust;
attitude_raw_pub.publish(att_raw_msg);
}
}
@@ -523,10 +649,59 @@ inline void checkState()
throw std::runtime_error("No connection to FCU, https://clover.coex.tech/connection");
}
void publishState()
{
clover::State msg;
msg.mode = setpoint_type;
msg.yaw_mode = setpoint_yaw_type;
if (setpoint_position.header.frame_id.empty()) {
msg.x = NAN;
msg.y = NAN;
msg.z = NAN;
} else {
msg.x = setpoint_position.point.x;
msg.y = setpoint_position.point.y;
msg.z = setpoint_altitude.point.z;
}
msg.speed = nav_speed;
msg.lat = setpoint_lat;
msg.lon = setpoint_lon;
msg.vx = setpoint_velocity.vector.x;
msg.vy = setpoint_velocity.vector.y;
msg.vz = setpoint_velocity.vector.z;
msg.roll = setpoint_roll;
msg.pitch = setpoint_pitch;
msg.yaw = !yaw_frame_id.empty() ? setpoint_yaw : NAN;
msg.roll_rate = setpoint_rates.x;
msg.pitch_rate = setpoint_rates.y;
msg.yaw_rate = setpoint_rates.z;
msg.thrust = setpoint_thrust;
if (setpoint_type == VELOCITY) {
msg.xy_frame_id = setpoint_velocity.header.frame_id;
msg.z_frame_id = setpoint_velocity.header.frame_id;
} else {
msg.xy_frame_id = setpoint_position.header.frame_id;
msg.z_frame_id = setpoint_altitude.header.frame_id;
}
msg.yaw_frame_id = yaw_frame_id;
state_pub.publish(msg);
}
inline float safe(float value) {
return std::isfinite(value) ? value : 0;
}
#define ENSURE_FINITE(var) { if (!std::isfinite(var)) throw std::runtime_error(#var " argument cannot be NaN or Inf"); }
#define ENSURE_NON_INF(var) { if (std::isinf(var)) throw std::runtime_error(#var " argument cannot be Inf"); }
bool serve(enum setpoint_type_t sp_type, float x, float y, float z, float vx, float vy, float vz,
float pitch, float roll, float yaw, float pitch_rate, float roll_rate, float yaw_rate, // editorconfig-checker-disable-line
float roll, float pitch, float yaw, float roll_rate, float pitch_rate, float yaw_rate, // editorconfig-checker-disable-line
float lat, float lon, float thrust, float speed, string frame_id, bool auto_arm, // editorconfig-checker-disable-line
uint8_t& success, string& message) // editorconfig-checker-disable-line
{
@@ -553,69 +728,40 @@ bool serve(enum setpoint_type_t sp_type, float x, float y, float z, float vx, fl
auto search = reference_frames.find(frame_id);
const string& reference_frame = search == reference_frames.end() ? frame_id : search->second;
// Serve "partial" commands
ENSURE_NON_INF(x);
ENSURE_NON_INF(y);
ENSURE_NON_INF(z);
ENSURE_NON_INF(speed); // TODO: allow inf
ENSURE_NON_INF(vx);
ENSURE_NON_INF(vy);
ENSURE_NON_INF(vz);
ENSURE_NON_INF(roll);
ENSURE_NON_INF(pitch);
ENSURE_NON_INF(roll_rate);
ENSURE_NON_INF(pitch_rate);
ENSURE_NON_INF(yaw_rate);
ENSURE_NON_INF(thrust);
if (!auto_arm && std::isfinite(yaw) &&
isnan(x) && isnan(y) && isnan(z) && isnan(vx) && isnan(vy) && isnan(vz) &&
isnan(pitch) && isnan(roll) && isnan(thrust) &&
isnan(lat) && isnan(lon)) {
// change only the yaw
if (setpoint_type == POSITION || setpoint_type == NAVIGATE || setpoint_type == NAVIGATE_GLOBAL || setpoint_type == VELOCITY) {
if (!waitTransform(setpoint_position.header.frame_id, frame_id, stamp, transform_timeout))
throw std::runtime_error("Can't transform from " + frame_id + " to " + setpoint_position.header.frame_id);
message = "Changing yaw only";
QuaternionStamped q;
q.header.frame_id = frame_id;
q.header.stamp = stamp;
q.quaternion = tf::createQuaternionMsgFromYaw(yaw); // TODO: pitch=0, roll=0 is not totally correct
setpoint_position.pose.orientation = tf_buffer.transform(q, setpoint_position.header.frame_id).quaternion;
setpoint_yaw_type = YAW;
goto publish_setpoint;
} else {
throw std::runtime_error("Setting yaw is possible only when position or velocity setpoints active");
}
}
if (!auto_arm && std::isfinite(yaw_rate) &&
isnan(x) && isnan(y) && isnan(z) && isnan(vx) && isnan(vy) && isnan(vz) &&
isnan(pitch) && isnan(roll) && isnan(yaw) && isnan(thrust) &&
isnan(lat) && isnan(lon)) {
// change only the yaw rate
if (setpoint_type == POSITION || setpoint_type == NAVIGATE || setpoint_type == NAVIGATE_GLOBAL || setpoint_type == VELOCITY) {
message = "Changing yaw rate only";
setpoint_yaw_type = YAW_RATE;
setpoint_yaw_rate = yaw_rate;
goto publish_setpoint;
} else {
throw std::runtime_error("Setting yaw rate is possible only when position or velocity setpoints active");
}
}
// Serve normal commands
if (sp_type == NAVIGATE || sp_type == POSITION) {
ENSURE_FINITE(x);
ENSURE_FINITE(y);
ENSURE_FINITE(z);
} else if (sp_type == NAVIGATE_GLOBAL) {
if (sp_type == NAVIGATE_GLOBAL) {
ENSURE_FINITE(lat);
ENSURE_FINITE(lon);
ENSURE_FINITE(z);
} else if (sp_type == VELOCITY) {
ENSURE_FINITE(vx);
ENSURE_FINITE(vy);
ENSURE_FINITE(vz);
} else if (sp_type == ATTITUDE) {
ENSURE_FINITE(pitch);
ENSURE_FINITE(roll);
ENSURE_FINITE(thrust);
} else if (sp_type == RATES) {
ENSURE_FINITE(pitch_rate);
ENSURE_FINITE(roll_rate);
ENSURE_FINITE(thrust);
}
if (isfinite(x) != isfinite(y)) {
throw std::runtime_error("x and y can be set only together");
}
if (isfinite(yaw_rate)) {
if (sp_type > RATES && setpoint_type == ATTITUDE) {
throw std::runtime_error("Yaw rate cannot be set in attitude mode.");
}
}
// set_altitude
if (sp_type == _ALTITUDE) {
if (setpoint_type == VELOCITY || setpoint_type == ATTITUDE || setpoint_type == RATES) {
throw std::runtime_error("Altitude cannot be set in velocity, attitude or rates mode.");
}
}
if (sp_type == NAVIGATE || sp_type == NAVIGATE_GLOBAL) {
@@ -629,20 +775,13 @@ bool serve(enum setpoint_type_t sp_type, float x, float y, float z, float vx, fl
speed = default_speed;
}
if (sp_type == NAVIGATE || sp_type == NAVIGATE_GLOBAL || sp_type == POSITION || sp_type == VELOCITY) {
if (yaw_rate != 0 && !std::isnan(yaw))
throw std::runtime_error("Yaw value should be NaN for setting yaw rate");
if (std::isnan(yaw_rate) && std::isnan(yaw))
throw std::runtime_error("Both yaw and yaw_rate cannot be NaN");
}
if (sp_type == NAVIGATE_GLOBAL) {
if (TIMEOUT(global_position, global_position_timeout))
throw std::runtime_error("No global position");
}
if (sp_type == NAVIGATE || sp_type == NAVIGATE_GLOBAL || sp_type == POSITION || sp_type == VELOCITY || sp_type == ATTITUDE) {
// if any value need to be transformed to reference frame
if (isfinite(x) || isfinite(y) || isfinite(z) || isfinite(vx) || isfinite(vy) || isfinite(vz) || isfinite(yaw)) {
// make sure transform from frame_id to reference frame available
if (!waitTransform(reference_frame, frame_id, stamp, transform_timeout))
throw std::runtime_error("Can't transform from " + frame_id + " to " + reference_frame);
@@ -659,15 +798,27 @@ bool serve(enum setpoint_type_t sp_type, float x, float y, float z, float vx, fl
auto xy_in_req_frame = tf_buffer.transform(pose_local, frame_id);
x = xy_in_req_frame.pose.position.x;
y = xy_in_req_frame.pose.position.y;
setpoint_lat = lat;
setpoint_lon = lon;
}
// Everything fine - switch setpoint type
setpoint_type = sp_type;
if (sp_type <= RATES) {
setpoint_type = sp_type;
}
if (sp_type != NAVIGATE && sp_type != NAVIGATE_GLOBAL) {
if (setpoint_type != NAVIGATE && setpoint_type != NAVIGATE_GLOBAL) {
nav_from_sp_flag = false;
}
bool to_auto_arm = auto_arm && (state.mode != "OFFBOARD" || !state.armed);
if (to_auto_arm || setpoint_type == VELOCITY || setpoint_type == ATTITUDE || setpoint_type == RATES) {
// invalidate position setpoint
setpoint_position.header.frame_id = "";
setpoint_altitude.header.frame_id = "";
yaw_frame_id = "";
}
if (sp_type == NAVIGATE || sp_type == NAVIGATE_GLOBAL) {
// starting point
if (nav_from_sp && nav_from_sp_flag) {
@@ -676,84 +827,139 @@ bool serve(enum setpoint_type_t sp_type, float x, float y, float z, float vx, fl
} else {
nav_start = local_position;
}
nav_speed = speed;
if (!isnan(speed)) {
nav_speed = speed;
}
nav_from_sp_flag = true;
}
// if (sp_type == NAVIGATE || sp_type == NAVIGATE_GLOBAL || sp_type == POSITION || sp_type == VELOCITY) {
// if (std::isnan(yaw) && yaw_rate == 0) {
// // keep yaw unchanged
// // TODO: this is incorrect, because we need yaw in desired frame
// yaw = tf2::getYaw(local_position.pose.orientation);
// }
// }
// handle position
if (setpoint_type == NAVIGATE || setpoint_type == NAVIGATE_GLOBAL || setpoint_type == POSITION) {
if (sp_type == POSITION || sp_type == NAVIGATE || sp_type == NAVIGATE_GLOBAL || sp_type == VELOCITY || sp_type == ATTITUDE) {
// destination point and/or yaw
PoseStamped ps;
ps.header.frame_id = frame_id;
ps.header.stamp = stamp;
ps.pose.position.x = x;
ps.pose.position.y = y;
ps.pose.position.z = z;
ps.pose.orientation.w = 1.0; // Ensure quaternion is always valid
PointStamped desired;
desired.header.frame_id = frame_id;
desired.header.stamp = stamp;
desired.point.x = safe(x);
desired.point.y = safe(y);
desired.point.z = safe(z);
if (std::isnan(yaw)) {
setpoint_yaw_type = YAW_RATE;
setpoint_yaw_rate = yaw_rate;
} else if (std::isinf(yaw) && yaw > 0) {
// yaw towards
setpoint_yaw_type = TOWARDS;
yaw = 0;
setpoint_yaw_rate = 0;
} else {
setpoint_yaw_type = YAW;
setpoint_yaw_rate = 0;
ps.pose.orientation = tf::createQuaternionMsgFromYaw(yaw);
// transform to reference frame
desired = tf_buffer.transform(desired, reference_frame);
// set horizontal position
if (isfinite(x) && isfinite(y)) {
setpoint_position = desired;
} else if (setpoint_position.header.frame_id.empty()) {
// TODO: use transform for current stamp
setpoint_position.header = local_position.header;
setpoint_position.point = local_position.pose.position;
}
tf_buffer.transform(ps, setpoint_position, reference_frame);
// set altitude
if (isfinite(z)) {
setpoint_altitude = desired;
} else if (setpoint_altitude.header.frame_id.empty()) {
setpoint_altitude.header = local_position.header;
setpoint_altitude.point = local_position.pose.position;
}
}
// handle velocity
if (sp_type == VELOCITY) {
Vector3Stamped vel;
vel.header.frame_id = frame_id;
vel.header.stamp = stamp;
vel.vector.x = vx;
vel.vector.y = vy;
vel.vector.z = vz;
tf_buffer.transform(vel, setpoint_velocity, reference_frame);
// TODO: allow setting different modes by altitude and xy
Vector3Stamped desired;
desired.header.frame_id = frame_id;
desired.header.stamp = stamp;
desired.vector.x = safe(vx);
desired.vector.y = safe(vy);
desired.vector.z = safe(vz);
// transform to reference frame
desired = tf_buffer.transform(desired, reference_frame);
setpoint_velocity.header = desired.header;
// set horizontal velocity
if (isfinite(vx) && isfinite(vy)) {
setpoint_velocity.vector.x = desired.vector.x;
setpoint_velocity.vector.y = desired.vector.y;
}
// set vertical velocity
if (isfinite(vz)) {
setpoint_velocity.vector.z = desired.vector.z;
}
}
if (sp_type == ATTITUDE || sp_type == RATES) {
thrust_msg.thrust = thrust;
// handle yaw
if (sp_type == NAVIGATE || sp_type == NAVIGATE_GLOBAL || sp_type == POSITION || sp_type == VELOCITY || sp_type == ATTITUDE || sp_type == _YAW) {
if (isfinite(yaw)) {
setpoint_yaw_type = YAW;
QuaternionStamped desired;
desired.header.frame_id = frame_id;
desired.header.stamp = stamp;
desired.quaternion = tf::createQuaternionMsgFromYaw(yaw);
// transform to reference frame
desired = tf_buffer.transform(desired, reference_frame);
setpoint_yaw = tf2::getYaw(desired.quaternion);
yaw_frame_id = reference_frame;
} else if (isinf(yaw) && yaw > 0) {
// yaw towards
setpoint_yaw_type = TOWARDS;
} else if (yaw_frame_id.empty() || sp_type == _YAW) {
// yaw is nan and not set previously OR set_yaw(yaw=nan) was called
setpoint_yaw_type = YAW;
setpoint_yaw = tf2::getYaw(local_position.pose.orientation); // set yaw to current yaw
yaw_frame_id = local_position.header.frame_id;
}
}
if (sp_type == RATES) {
rates_msg.twist.angular.x = roll_rate;
rates_msg.twist.angular.y = pitch_rate;
rates_msg.twist.angular.z = yaw_rate;
// handle roll
if (isfinite(roll)) {
setpoint_roll = roll;
}
// handle pitch
if (isfinite(pitch)) {
setpoint_pitch = pitch;
}
// handle yaw rate
if (isfinite(yaw_rate)) {
setpoint_yaw_type = YAW_RATE;
setpoint_rates.z = yaw_rate;
}
// handle pitch rate
if (isfinite(roll_rate)) {
setpoint_rates.x = roll_rate;
}
// handle roll rate
if (isfinite(pitch_rate)) {
setpoint_rates.y = pitch_rate;
}
// handle thrust
if (isfinite(thrust)) {
setpoint_thrust = thrust;
}
wait_armed = auto_arm;
publish_setpoint:
publish(stamp); // calculate initial transformed messages first
setpoint_timer.start();
// publish target frame
if (!target.child_frame_id.empty()) {
if (setpoint_type == NAVIGATE || setpoint_type == NAVIGATE_GLOBAL || setpoint_type == POSITION) {
target.header.frame_id = setpoint_position.header.frame_id;
target.header.stamp = stamp;
target.transform.translation.x = setpoint_position.pose.position.x;
target.transform.translation.y = setpoint_position.pose.position.y;
target.transform.translation.z = setpoint_position.pose.position.z;
target.transform.rotation = setpoint_position.pose.orientation;
static_transform_broadcaster->sendTransform(target);
}
if (setpoint_type == NAVIGATE || setpoint_type == NAVIGATE_GLOBAL || setpoint_type == POSITION) {
publishTarget(stamp, true);
}
publishState();
if (auto_arm) {
offboardAndArm();
wait_armed = false;
@@ -778,27 +984,39 @@ publish_setpoint:
}
bool navigate(Navigate::Request& req, Navigate::Response& res) {
return serve(NAVIGATE, req.x, req.y, req.z, NAN, NAN, NAN, NAN, NAN, req.yaw, NAN, NAN, req.yaw_rate, NAN, NAN, NAN, req.speed, req.frame_id, req.auto_arm, res.success, res.message);
return serve(NAVIGATE, req.x, req.y, req.z, NAN, NAN, NAN, NAN, NAN, req.yaw, NAN, NAN, NAN, NAN, NAN, NAN, req.speed, req.frame_id, req.auto_arm, res.success, res.message);
}
bool navigateGlobal(NavigateGlobal::Request& req, NavigateGlobal::Response& res) {
return serve(NAVIGATE_GLOBAL, NAN, NAN, req.z, NAN, NAN, NAN, NAN, NAN, req.yaw, NAN, NAN, req.yaw_rate, req.lat, req.lon, NAN, req.speed, req.frame_id, req.auto_arm, res.success, res.message);
return serve(NAVIGATE_GLOBAL, NAN, NAN, req.z, NAN, NAN, NAN, NAN, NAN, req.yaw, NAN, NAN, NAN, req.lat, req.lon, NAN, req.speed, req.frame_id, req.auto_arm, res.success, res.message);
}
bool setAltitude(SetAltitude::Request& req, SetAltitude::Response& res) {
return serve(_ALTITUDE, NAN, NAN, req.z, NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN, req.frame_id, false, res.success, res.message);
}
bool setYaw(SetYaw::Request& req, SetYaw::Response& res) {
return serve(_YAW, NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN, req.yaw, NAN, NAN, NAN, NAN, NAN, NAN, NAN, req.frame_id, false, res.success, res.message);
}
bool setYawRate(SetYawRate::Request& req, SetYawRate::Response& res) {
return serve(_YAW_RATE, NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN, req.yaw_rate, NAN, NAN, NAN, NAN, "", false, res.success, res.message);
}
bool setPosition(SetPosition::Request& req, SetPosition::Response& res) {
return serve(POSITION, req.x, req.y, req.z, NAN, NAN, NAN, NAN, NAN, req.yaw, NAN, NAN, req.yaw_rate, NAN, NAN, NAN, NAN, req.frame_id, req.auto_arm, res.success, res.message);
return serve(POSITION, req.x, req.y, req.z, NAN, NAN, NAN, NAN, NAN, req.yaw, NAN, NAN, NAN, NAN, NAN, NAN, NAN, req.frame_id, req.auto_arm, res.success, res.message);
}
bool setVelocity(SetVelocity::Request& req, SetVelocity::Response& res) {
return serve(VELOCITY, NAN, NAN, NAN, req.vx, req.vy, req.vz, NAN, NAN, req.yaw, NAN, NAN, req.yaw_rate, NAN, NAN, NAN, NAN, req.frame_id, req.auto_arm, res.success, res.message);
return serve(VELOCITY, NAN, NAN, NAN, req.vx, req.vy, req.vz, NAN, NAN, req.yaw, NAN, NAN, NAN, NAN, NAN, NAN, NAN, req.frame_id, req.auto_arm, res.success, res.message);
}
bool setAttitude(SetAttitude::Request& req, SetAttitude::Response& res) {
return serve(ATTITUDE, NAN, NAN, NAN, NAN, NAN, NAN, req.pitch, req.roll, req.yaw, NAN, NAN, NAN, NAN, NAN, req.thrust, NAN, req.frame_id, req.auto_arm, res.success, res.message);
return serve(ATTITUDE, NAN, NAN, NAN, NAN, NAN, NAN, req.roll, req.pitch, req.yaw, NAN, NAN, NAN, NAN, NAN, req.thrust, NAN, req.frame_id, req.auto_arm, res.success, res.message);
}
bool setRates(SetRates::Request& req, SetRates::Response& res) {
return serve(RATES, NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN, req.pitch_rate, req.roll_rate, req.yaw_rate, NAN, NAN, req.thrust, NAN, "", req.auto_arm, res.success, res.message);
return serve(RATES, NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN, req.roll_rate, req.pitch_rate, req.yaw_rate, NAN, NAN, req.thrust, NAN, "", req.auto_arm, res.success, res.message);
}
bool land(std_srvs::Trigger::Request& req, std_srvs::Trigger::Response& res)
@@ -830,9 +1048,7 @@ bool land(std_srvs::Trigger::Request& req, std_srvs::Trigger::Response& res)
auto start = ros::Time::now();
while (ros::ok()) {
if (state.mode == "AUTO.LAND") {
res.success = true;
busy = false;
return true;
break;
}
if (ros::Time::now() - start > land_timeout)
throw std::runtime_error("Land request timed out");
@@ -841,12 +1057,37 @@ bool land(std_srvs::Trigger::Request& req, std_srvs::Trigger::Response& res)
r.sleep();
}
// stop setpoints and invalidate position setpoint
setpoint_timer.stop();
setpoint_type = NONE;
setpoint_position.header.frame_id = "";
setpoint_altitude.header.frame_id = "";
yaw_frame_id = "";
publishState();
res.success = true;
busy = false;
return true;
} catch (const std::exception& e) {
res.message = e.what();
ROS_INFO("%s", e.what());
busy = false;
return true;
}
return false;
}
bool release(std_srvs::Trigger::Request& req, std_srvs::Trigger::Response& res)
{
setpoint_timer.stop();
setpoint_type = NONE;
setpoint_position.header.frame_id = "";
setpoint_altitude.header.frame_id = "";
yaw_frame_id = "";
publishState();
res.success = true;
return true;
}
int main(int argc, char **argv)
@@ -859,8 +1100,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);
@@ -869,8 +1111,17 @@ int main(int argc, char **argv)
nh_priv.param("check_kill_switch", check_kill_switch, true);
nh_priv.param("default_speed", default_speed, 0.5f);
nh_priv.param<string>("body_frame", body.child_frame_id, "body");
nh_priv.param<string>("terrain_frame", terrain.child_frame_id, "terrain");
nh_priv.param<string>("terrain_frame_mode", terrain_frame_mode, "altitude");
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));
@@ -885,35 +1136,56 @@ 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);
ros::Subscriber altitude_sub;
if (!body.child_frame_id.empty() && !terrain.child_frame_id.empty()) {
terrain.header.frame_id = local_frame;
if (terrain_frame_mode == "altitude") {
altitude_sub = nh.subscribe(mavros + "/altitude", 1, &handleAltitude);
} else if (terrain_frame_mode == "range") {
string range_topic = nh_priv.param("range_topic", string("rangefinder/range"));
altitude_sub = nh.subscribe(range_topic, 1, &handleRange);
} else {
ROS_FATAL("Unknown terrain_frame_mode: %s, valid values: altitude, range", terrain_frame_mode.c_str());
ros::shutdown();
}
}
// 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);
// State publisher
state_pub = nh_priv.advertise<clover::State>("state", 1, true);
// Service servers
auto gt_serv = nh.advertiseService("get_telemetry", &getTelemetry);
auto na_serv = nh.advertiseService("navigate", &navigate);
auto ng_serv = nh.advertiseService("navigate_global", &navigateGlobal);
auto sl_serv = nh.advertiseService("set_altitude", &setAltitude);
auto ya_serv = nh.advertiseService("set_yaw", &setYaw);
auto yr_serv = nh.advertiseService("set_yaw_rate", &setYawRate);
auto sp_serv = nh.advertiseService("set_position", &setPosition);
auto sv_serv = nh.advertiseService("set_velocity", &setVelocity);
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);
@@ -921,7 +1193,7 @@ int main(int argc, char **argv)
position_msg.header.frame_id = local_frame;
position_raw_msg.header.frame_id = local_frame;
position_raw_msg.coordinate_frame = PositionTarget::FRAME_LOCAL_NED;
rates_msg.header.frame_id = fcu_frame;
//rates_msg.header.frame_id = fcu_frame;
ROS_INFO("ready");
ros::spin();

View File

@@ -11,12 +11,14 @@
#include <string>
#include <ros/ros.h>
#include <tf/transform_datatypes.h>
#include <tf2/transform_datatypes.h>
#include <tf2_ros/buffer.h>
#include <tf2_ros/transform_listener.h>
#include <tf2_ros/static_transform_broadcaster.h>
#include <tf2_geometry_msgs/tf2_geometry_msgs.h>
#include <geometry_msgs/TransformStamped.h>
#include <geometry_msgs/Quaternion.h>
#include <geometry_msgs/PoseStamped.h>
#include <geometry_msgs/PoseWithCovarianceStamped.h>
#include <std_srvs/Trigger.h>
@@ -25,7 +27,7 @@
using std::string;
using namespace geometry_msgs;
bool reset_flag = false;
bool reset_flag = true; // offset should be reset on the start
string local_frame_id, frame_id, child_frame_id, offset_frame_id;
tf2_ros::Buffer tf_buffer;
ros::Publisher vpe_pub;
@@ -33,14 +35,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;
@@ -66,6 +68,13 @@ inline Pose getPose(const PoseStampedConstPtr& pose) { return pose->pose; }
inline Pose getPose(const PoseWithCovarianceStampedConstPtr& pose) { return pose->pose.pose; }
inline void keepYaw(Quaternion& quaternion)
{
tf::Quaternion q;
q.setRPY(0, 0, tf::getYaw(quaternion));
tf::quaternionTFToMsg(q, quaternion);
}
template <typename T>
void callback(const T& msg)
{
@@ -88,10 +97,29 @@ void callback(const T& msg)
if (!offset_frame_id.empty()) {
if (reset_flag || msg->header.stamp - vpe.header.stamp > offset_timeout) {
// calculate the offset
offset = tf_buffer.lookupTransform(local_frame_id, frame_id,
msg->header.stamp, ros::Duration(0.02));
// offset.header.frame_id = vpe.header.frame_id;
offset.child_frame_id = offset_frame_id;
if (!frame_id.empty()) {
// calculate from TF
offset = tf_buffer.lookupTransform(local_frame_id, frame_id,
msg->header.stamp, ros::Duration(0.02));
// offset.header.frame_id = vpe.header.frame_id;
offset.child_frame_id = offset_frame_id;
} else {
// calculate transform between pose in vpe frame and pose in local frame
TransformStamped local_pose = tf_buffer.lookupTransform(local_frame_id, child_frame_id,
msg->header.stamp, ros::Duration(0.02));
keepYaw(local_pose.transform.rotation);
tf::Transform vpeTransform, poseTransform;
tf::poseMsgToTF(vpe.pose, vpeTransform);
tf::transformMsgToTF(local_pose.transform, poseTransform);
tf::Transform offset_tf = vpeTransform.inverseTimes(poseTransform);
tf::transformTFToMsg(offset_tf, offset.transform);
offset.header.frame_id = local_frame_id;
offset.header.stamp = msg->header.stamp;
offset.child_frame_id = offset_frame_id;
}
br.sendTransform(offset);
reset_flag = false;
ROS_INFO("offset reset");
@@ -122,10 +150,11 @@ int main(int argc, char **argv) {
tf2_ros::TransformListener tf_listener(tf_buffer);
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_priv.param<string>("frame_id", frame_id, ""); // name for used visual pose frame
nh_priv.param<string>("offset_frame_id", offset_frame_id, ""); // name for published offset frame
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 +170,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);
}

4
clover/src/www Executable file
View File

@@ -0,0 +1,4 @@
#!/usr/bin/env bash
export ROSWWW_DEFAULT=clover
rosrun roswww_static update

View File

@@ -13,11 +13,11 @@ float32 alt
float32 vx
float32 vy
float32 vz
float32 pitch
float32 roll
float32 pitch
float32 yaw
float32 pitch_rate
float32 roll_rate
float32 pitch_rate
float32 yaw_rate
float32 voltage
float32 cell_voltage

View File

@@ -2,7 +2,6 @@ float32 x
float32 y
float32 z
float32 yaw
float32 yaw_rate
float32 speed
string frame_id
bool auto_arm

View File

@@ -2,7 +2,6 @@ float64 lat
float64 lon
float32 z
float32 yaw
float32 yaw_rate
float32 speed
string frame_id
bool auto_arm

View File

@@ -0,0 +1,5 @@
float32 z
string frame_id
---
bool success
string message

View File

@@ -1,5 +1,5 @@
float32 pitch
float32 roll
float32 pitch
float32 yaw
float32 thrust
string frame_id

View File

@@ -2,7 +2,6 @@ float32 x
float32 y
float32 z
float32 yaw
float32 yaw_rate
string frame_id
bool auto_arm
---

View File

@@ -1,5 +1,5 @@
float32 pitch_rate
float32 roll_rate
float32 pitch_rate
float32 yaw_rate
float32 thrust
bool auto_arm

View File

@@ -2,7 +2,6 @@ float32 vx
float32 vy
float32 vz
float32 yaw
float32 yaw_rate
string frame_id
bool auto_arm
---

5
clover/srv/SetYaw.srv Normal file
View File

@@ -0,0 +1,5 @@
float32 yaw
string frame_id
---
bool success
string message

View File

@@ -0,0 +1,4 @@
float32 yaw_rate
---
bool success
string message

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,21 @@
<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"/>
<node pkg="nodelet" type="nodelet" name="main_camera_nodelet_manager" args="manager" output="screen" required="true">
<param name="num_worker_threads" value="2"/>
</node>
<node pkg="nodelet" type="nodelet" name="rectify" args="load image_proc/rectify main_camera_nodelet_manager" required="true">
<remap from="image_mono" to="main_camera/image_raw"/>
<remap from="camera_info" to="main_camera/camera_info"/>
<remap from="image_rect" to="main_camera/image_rect"/>
</node>
<param name="test_module" value="$(find clover)/test/basic.py"/>
<test test-name="basic_test" pkg="ros_pytest" type="ros_pytest_runner"/>
</launch>

437
clover/test/offboard.py Executable file
View File

@@ -0,0 +1,437 @@
import rospy
import pytest
from pytest import approx
import threading
import mavros_msgs.msg
from mavros_msgs.srv import SetMode
from geometry_msgs.msg import PoseStamped
from clover import srv
from clover.msg import State
from std_srvs.srv import Trigger
from math import nan, inf
import tf2_ros
import tf2_geometry_msgs
@pytest.fixture()
def node():
return rospy.init_node('offboard_test', anonymous=True)
@pytest.fixture
def tf_buffer():
buf = tf2_ros.Buffer()
tf2_ros.TransformListener(buf)
return buf
def get_state():
return rospy.wait_for_message('/simple_offboard/state', State, timeout=1)
def get_navigate_target(tf_buffer):
target = tf_buffer.lookup_transform('map', 'navigate_target', rospy.get_rostime(), rospy.Duration(1))
assert target.child_frame_id == 'navigate_target'
return target
def test_offboard(node, tf_buffer):
navigate = rospy.ServiceProxy('navigate', srv.Navigate)
set_position = rospy.ServiceProxy('set_position', srv.SetPosition)
set_altitude = rospy.ServiceProxy('set_altitude', srv.SetAltitude)
set_yaw = rospy.ServiceProxy('set_yaw', srv.SetYaw)
set_yaw_rate = rospy.ServiceProxy('set_yaw_rate', srv.SetYawRate)
set_velocity = rospy.ServiceProxy('set_velocity', srv.SetVelocity)
set_attitude = rospy.ServiceProxy('set_attitude', srv.SetAttitude)
set_rates = rospy.ServiceProxy('set_rates', srv.SetRates)
get_telemetry = rospy.ServiceProxy('get_telemetry', srv.GetTelemetry)
land = rospy.ServiceProxy('land', Trigger)
res = navigate()
assert res.success == False
assert res.message.startswith('State timeout')
telem = get_telemetry()
assert telem.connected == False
# mocked state publisher
state_pub = rospy.Publisher('/mavros/state', mavros_msgs.msg.State, latch=True, queue_size=1)
state_msg = mavros_msgs.msg.State(mode='OFFBOARD', armed=True)
def publish_state():
r = rospy.Rate(2)
while not rospy.is_shutdown():
state_msg.header.stamp = rospy.Time.now()
state_pub.publish(state_msg)
r.sleep()
# start publishing state
threading.Thread(target=publish_state, daemon=True).start()
rospy.sleep(0.5)
# set_mode service mock
def set_mode(req):
state_msg.mode = req.custom_mode # set mocked mode to requested
return True,
rospy.Service('/mavros/set_mode', SetMode, set_mode)
telem = get_telemetry()
assert telem.connected == False
res = navigate()
assert res.success == False
assert res.message.startswith('No connection to FCU')
state_msg.connected = True
rospy.sleep(1)
telem = get_telemetry()
assert telem.connected == True
res = navigate()
assert res.success == False
assert res.message.startswith('No local position')
local_position_pub = rospy.Publisher('/mavros/local_position/pose', PoseStamped, latch=True, queue_size=1)
local_position_msg = PoseStamped()
local_position_msg.header.frame_id = 'map'
local_position_msg.pose.position.x = 1
local_position_msg.pose.position.y = 2
local_position_msg.pose.position.z = 3
local_position_msg.pose.orientation.w = 1
def publish_local_position():
r = rospy.Rate(30)
while not rospy.is_shutdown():
local_position_msg.header.stamp = rospy.Time.now()
local_position_pub.publish(local_position_msg)
r.sleep()
# start publishing local position
threading.Thread(target=publish_local_position, daemon=True).start()
rospy.sleep(0.5)
# check body frame
body = tf_buffer.lookup_transform('map', 'body', rospy.get_rostime(), rospy.Duration(1))
assert body.child_frame_id == 'body'
assert body.transform.translation.x == approx(1)
assert body.transform.translation.y == approx(2)
assert body.transform.translation.z == approx(3)
res = navigate(x=3, y=2, z=1, frame_id='map')
assert res.success == True
state = get_state()
assert state.mode == State.MODE_NAVIGATE
assert state.yaw_mode == State.YAW_MODE_YAW
assert state.x == 3
assert state.y == 2
assert state.z == 1
assert state.yaw == 0
assert state.xy_frame_id == 'map'
assert state.z_frame_id == 'map'
assert state.yaw_frame_id == 'map'
target = get_navigate_target(tf_buffer)
assert target.header.frame_id == 'map'
assert target.transform.translation.x == approx(3)
assert target.transform.translation.y == approx(2)
assert target.transform.translation.z == approx(1)
assert target.transform.rotation.x == 0
assert target.transform.rotation.y == 0
assert target.transform.rotation.z == 0
assert target.transform.rotation.w == 1
# try to set only the y
res = navigate(x=nan, y=1, z=nan)
assert res.success == False
assert res.message.startswith('x and y can be set only together')
# set z in body frame
res = navigate(x=nan, y=nan, z=1, frame_id='body')
assert res.success == True
state = get_state()
assert state.mode == State.MODE_NAVIGATE
assert state.yaw_mode == State.YAW_MODE_YAW
assert state.x == 3
assert state.y == 2
assert state.z == 4
assert state.yaw == 0
assert state.xy_frame_id == 'map'
assert state.z_frame_id == 'map'
assert state.yaw_frame_id == 'map'
# set xy in test frame
res = navigate(x=1, y=2, z=nan, frame_id='test')
assert res.success == True
state = get_state()
assert state.mode == State.MODE_NAVIGATE
assert state.yaw_mode == State.YAW_MODE_YAW
assert state.x == 1
assert state.y == 2
assert state.z == 4
assert state.yaw == 0
assert state.xy_frame_id == 'test'
assert state.z_frame_id == 'map'
assert state.yaw_frame_id == 'test'
# auto_arm should not invalidate the setpoint if not effective
res = navigate(x=nan, y=nan, z=1, frame_id='map', auto_arm=True)
assert res.success == True
state = get_state()
assert state.mode == State.MODE_NAVIGATE
assert state.yaw_mode == State.YAW_MODE_YAW
assert state.x == 1
assert state.y == 2
assert state.z == 1
assert state.yaw == 0
assert state.xy_frame_id == 'test'
assert state.z_frame_id == 'map'
assert state.yaw_frame_id == 'map'
# auto_arm should invalidate the setpoint if effective
state_msg.mode = 'STABILIZED' # pretend we are not in OFFBOARD mode
rospy.sleep(1)
res = navigate(x=nan, y=nan, z=1, frame_id='map', auto_arm=True)
assert res.success == True
state = get_state()
assert state.mode == State.MODE_NAVIGATE
assert state.yaw_mode == State.YAW_MODE_YAW
assert state.x == 1
assert state.y == 2
assert state.z == 1
assert state.yaw == 0
assert state.xy_frame_id == 'map'
assert state.z_frame_id == 'map'
assert state.yaw_frame_id == 'map'
state_msg.mode = 'OFFBOARD'
rospy.sleep(1)
# set_attitude should invalidate the setpoint
res = set_attitude()
assert res.success == True
res = navigate(x=5, y=6, z=nan, yaw=nan, frame_id='map')
assert res.success == True
state = get_state()
assert state.mode == State.MODE_NAVIGATE
assert state.yaw_mode == State.YAW_MODE_YAW
assert state.x == 5
assert state.y == 6
assert state.z == 3
assert state.yaw == 0
assert state.xy_frame_id == 'map'
assert state.z_frame_id == 'map'
assert state.yaw_frame_id == 'map'
# test set_altitude
res = set_altitude(z=7, frame_id='test')
assert res.success == True
state = get_state()
assert state.mode == State.MODE_NAVIGATE
assert state.yaw_mode == State.YAW_MODE_YAW
assert state.x == 5
assert state.y == 6
assert state.z == 7
assert state.yaw == 0
assert state.xy_frame_id == 'map'
assert state.z_frame_id == 'test'
assert state.yaw_frame_id == 'map'
# test set_yaw
res = set_yaw(yaw=0.5, frame_id='test2')
assert res.success == True
state = get_state()
assert state.mode == State.MODE_NAVIGATE
assert state.yaw_mode == State.YAW_MODE_YAW
assert state.x == 5
assert state.y == 6
assert state.z == 7
assert state.yaw == 0.5
assert state.xy_frame_id == 'map'
assert state.z_frame_id == 'test'
assert state.yaw_frame_id == 'test2'
# test set_yaw_rate
res = set_yaw_rate(yaw_rate=2)
assert res.success == True
state = get_state()
assert state.mode == State.MODE_NAVIGATE
assert state.yaw_mode == State.YAW_MODE_YAW_RATE
assert state.x == 5
assert state.y == 6
assert state.z == 7
assert state.yaw_rate == 2
assert state.xy_frame_id == 'map'
assert state.z_frame_id == 'test'
# navigate(yaw=nan) should keep yaw rate mode
res = navigate(x=nan, y=nan, z=nan, yaw=nan)
assert res.success == True
state = get_state()
assert state.mode == State.MODE_NAVIGATE
assert state.yaw_mode == State.YAW_MODE_YAW_RATE
assert state.x == 5
assert state.y == 6
assert state.z == 7
assert state.yaw_rate == 2
assert state.xy_frame_id == 'map'
assert state.z_frame_id == 'test'
# set_yaw(nan) should change back to yaw mode
res = set_yaw(yaw=nan)
assert res.success == True
state = get_state()
assert state.mode == State.MODE_NAVIGATE
assert state.yaw_mode == State.YAW_MODE_YAW
assert state.yaw == 0
assert state.yaw_frame_id == 'map'
# test set_position
res = set_position(x=nan, y=nan, z=13, yaw=nan, frame_id='test2')
assert res.success == True
state = get_state()
assert state.mode == State.MODE_POSITION
assert state.yaw_mode == State.YAW_MODE_YAW
assert state.x == 5
assert state.y == 6
assert state.z == 13
assert state.yaw == 0
assert state.xy_frame_id == 'map'
assert state.z_frame_id == 'test2'
assert state.yaw_frame_id == 'map'
# set_altitude should not change the mode
res = set_altitude(z=3, frame_id='test')
assert res.success == True
state = get_state()
assert state.mode == State.MODE_POSITION
assert state.yaw_mode == State.YAW_MODE_YAW
assert state.x == 5
assert state.y == 6
assert state.z == 3
assert state.yaw == 0
assert state.xy_frame_id == 'map'
assert state.z_frame_id == 'test'
assert state.yaw_frame_id == 'map'
# set_yaw should not change the main mode
res = set_yaw(yaw=1, frame_id='test2')
assert res.success == True
state = get_state()
assert state.mode == State.MODE_POSITION
assert state.yaw_mode == State.YAW_MODE_YAW
assert state.x == 5
assert state.y == 6
assert state.z == 3
assert state.yaw == 1
assert state.xy_frame_id == 'map'
assert state.z_frame_id == 'test'
assert state.yaw_frame_id == 'test2'
# test set_velocity
res = set_velocity(vx=1, frame_id='body')
state = get_state()
assert state.mode == State.MODE_VELOCITY
assert state.yaw_mode == State.YAW_MODE_YAW
assert state.vx == 1
assert state.vy == 0
assert state.vz == 0
assert state.yaw == 0
assert state.xy_frame_id == 'map'
assert state.z_frame_id == 'map'
assert state.yaw_frame_id == 'map'
# set_altitude should not work in velocity mode
res = set_altitude(z=3, frame_id='test')
assert res.success == False
assert res.message.startswith('Altitude cannot be set in')
# test set_attitude
res = set_attitude(roll=0.1, pitch=0.2, yaw=0.3, thrust=0.5)
assert res.success == True
state = get_state()
assert state.mode == State.MODE_ATTITUDE
assert state.yaw_mode == State.YAW_MODE_YAW
assert state.roll == approx(0.1)
assert state.pitch == approx(0.2)
assert state.yaw == approx(0.3)
assert state.thrust == approx(0.5)
assert state.yaw_frame_id == 'map'
msg = rospy.wait_for_message('/mavros/setpoint_attitude/attitude', PoseStamped, timeout=3)
# Tait-Bryan ZYX angle (rzyx) converted to quaternion
assert msg.pose.orientation.x == approx(0.0342708)
assert msg.pose.orientation.y == approx(0.10602051)
assert msg.pose.orientation.z == approx(0.14357218)
assert msg.pose.orientation.w == approx(0.98334744)
msg = rospy.wait_for_message('/mavros/setpoint_attitude/thrust', mavros_msgs.msg.Thrust, timeout=3)
assert msg.thrust == approx(0.5)
# set_yaw should work in attitude mode
res = set_yaw(yaw=0.7, frame_id='test2')
assert res.success == True
state = get_state()
assert state.mode == State.MODE_ATTITUDE
assert state.yaw_mode == State.YAW_MODE_YAW
assert state.roll == approx(0.1)
assert state.pitch == approx(0.2)
assert state.yaw == approx(0.7)
assert state.thrust == approx(0.5)
assert state.yaw_frame_id == 'test2'
# set_yaw_rate should not work in attitude mode
res = set_yaw_rate(yaw_rate=0.3)
assert res.success == False
assert res.message.startswith('Yaw rate cannot be set in')
# test set_rates
res = set_rates(roll_rate=nan, pitch_rate=nan, yaw_rate=0.3, thrust=0.6)
assert res.success == True
state = get_state()
assert state.mode == State.MODE_RATES
assert state.yaw_mode == State.YAW_MODE_YAW_RATE
assert state.roll_rate == approx(0)
assert state.pitch_rate == approx(0)
assert state.yaw_rate == approx(0.3)
assert state.thrust == approx(0.6)
msg = rospy.wait_for_message('/mavros/setpoint_raw/attitude', mavros_msgs.msg.AttitudeTarget, timeout=3)
assert msg.thrust == approx(0.6)
res = set_rates(roll_rate=0.3, pitch_rate=0.2, yaw_rate=0.1, thrust=0.4)
assert res.success == True
state = get_state()
assert state.mode == State.MODE_RATES
assert state.yaw_mode == State.YAW_MODE_YAW_RATE
assert state.roll_rate == approx(0.3)
assert state.pitch_rate == approx(0.2)
assert state.yaw_rate == approx(0.1)
assert state.thrust == approx(0.4)
res = set_rates(roll_rate=nan, pitch_rate=nan, yaw_rate=nan, thrust=0.3)
assert res.success == True
state = get_state()
assert state.mode == State.MODE_RATES
assert state.yaw_mode == State.YAW_MODE_YAW_RATE
assert state.roll_rate == approx(0.3)
assert state.pitch_rate == approx(0.2)
assert state.yaw_rate == approx(0.1)
assert state.thrust == approx(0.3)
msg = rospy.wait_for_message('/mavros/setpoint_raw/attitude', mavros_msgs.msg.AttitudeTarget, timeout=3)
assert msg.type_mask == mavros_msgs.msg.AttitudeTarget.IGNORE_ATTITUDE
assert msg.body_rate.x == approx(0.3)
assert msg.body_rate.y == approx(0.2)
assert msg.body_rate.z == approx(0.1)
# set_yaw_rate should work in rates mode
res = set_yaw_rate(yaw_rate=0.4)
assert res.success == True
state = get_state()
assert state.mode == State.MODE_RATES
assert state.yaw_mode == State.YAW_MODE_YAW_RATE
assert state.roll_rate == approx(0.3)
assert state.pitch_rate == approx(0.2)
assert state.yaw_rate == approx(0.4)
assert state.thrust == approx(0.3)
res = set_rates(roll_rate=inf)
assert res.success == False
assert res.message == 'roll_rate argument cannot be Inf'
# test land service
res = land()
assert res.success == True
assert state_msg.mode == 'AUTO.LAND' # check that the mode was set correctly

10
clover/test/offboard.test Normal file
View File

@@ -0,0 +1,10 @@
<launch>
<node name="simple_offboard" pkg="clover" type="simple_offboard" required="true" output="screen"/>
<node pkg="tf2_ros" type="static_transform_publisher" name="test_frame" args="10 20 30 0 0 0 map test"/>
<node pkg="tf2_ros" type="static_transform_publisher" name="test2_frame" args="100 200 300 0 0 0 map test2"/>
<param name="test_module" value="$(find clover)/test/offboard.py"/>
<test test-name="offboard_test" pkg="ros_pytest" type="ros_pytest_runner"/>
</launch>

View File

@@ -0,0 +1,68 @@
#!/usr/bin/env python3
# Copyright (C) 2024 Copter Express Technologies
#
# Author: Oleg Kalachev <okalachev@gmail.com>
#
# Distributed under MIT License (available at https://opensource.org/licenses/MIT).
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
"""Rescale camera info
Rescale camera info files for different resolutions.
Usage:
rescale_camera_info.py <camera_info_file>
rescale_camera_info.py (-h | --help)
Options:
<camera_info_file> Path to the source camera info file
Example:
rescale_camera_info.py camera_info.yaml
"""
from docopt import docopt
import yaml
arguments = docopt(__doc__)
camera_info = yaml.safe_load(open(arguments['<camera_info_file>']))
RESOLUTIONS = (
(320, 240), # QVGA
(640, 480), # VGA
(800, 600), # SVGA
(1280, 720), # HD
(1920, 1080), # FullHD
(2592, 1944), # 5MP
(3840, 2160), # 4K
(4056, 3040),
)
# TODO: retrieve resolutions list (v4l2-ctl --list-formats-ext)
for resolution in RESOLUTIONS:
width_k = resolution[0] / camera_info['image_width']
height_k = resolution[1] / camera_info['image_height']
camera_info_rescaled = camera_info.copy()
camera_info_rescaled['image_width'] = resolution[0]
camera_info_rescaled['image_height'] = resolution[1]
# See http://docs.ros.org/api/sensor_msgs/html/msg/CameraInfo.html for clarification
camera_info_rescaled['camera_matrix']['data'][0] *= width_k
camera_info_rescaled['camera_matrix']['data'][2] *= width_k
camera_info_rescaled['camera_matrix']['data'][4] *= height_k
camera_info_rescaled['camera_matrix']['data'][5] *= height_k
camera_info_rescaled['projection_matrix']['data'][0] *= width_k
camera_info_rescaled['projection_matrix']['data'][2] *= width_k
camera_info_rescaled['projection_matrix']['data'][5] *= height_k
camera_info_rescaled['projection_matrix']['data'][6] *= height_k
output_file = arguments['<camera_info_file>'].replace('.yaml', '_{}x{}.yaml'.format(resolution[0], resolution[1]))
with open(output_file, 'w') as f:
f.write('# Generated from {} by rescale_camera_info.py\n'.format(arguments['<camera_info_file>']))
yaml.dump(camera_info_rescaled, f)
print('Saved {}'.format(output_file))

View File

@@ -1,15 +1,54 @@
# PixHawk (px4fmu-v2), px4fmu-v3
SUBSYSTEM=="tty", ATTRS{idVendor}=="26ac", ATTRS{idProduct}=="0011", ATTRS{product}=="PX4 FMU v2.x", SYMLINK+="px4fmu"
# PixRacer (px4fmu-v4)
SUBSYSTEM=="tty", ATTRS{idVendor}=="26ac", ATTRS{idProduct}=="0012", ATTRS{product}=="PX4 FMU v4.x", SYMLINK+="px4fmu"
# px4fmu-v5
SUBSYSTEM=="tty", ATTRS{idVendor}=="26ac", ATTRS{idProduct}=="0032", ATTRS{product}=="PX4 FMU v5.x", SYMLINK+="px4fmu"
# auav
SUBSYSTEM=="tty", ATTRS{idVendor}=="26ac", ATTRS{idProduct}=="0021", ATTRS{product}=="PX4 AUAV x2.1", SYMLINK+="px4fmu"
# crazyflie
SUBSYSTEM=="tty", ATTRS{idVendor}=="26ac", ATTRS{idProduct}=="0016", ATTRS{product}=="PX4 Crazyflie v2.0", SYMLINK+="px4fmu"
# px4fmu-v4pro
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"
# Source files: PX4-Autopilot/boards/**/nuttx-config/nsh/defconfig
# Additional info:
# https://docs.px4.io/main/en/flight_controller/
# https://github.com/mavlink/qgroundcontrol/blob/master/src/comm/USBBoardInfo.json
SUBSYSTEM=="tty", ATTRS{idVendor}=="26ac", ATTRS{idProduct}=="0001", ATTRS{product}=="PX4 GNF405", SYMLINK+="px4fmu"
SUBSYSTEM=="tty", ATTRS{idVendor}=="26ac", ATTRS{idProduct}=="0001", ATTRS{product}=="PX4 OmnibusF4SD", SYMLINK+="px4fmu"
SUBSYSTEM=="tty", ATTRS{idVendor}=="26ac", ATTRS{idProduct}=="0016", ATTRS{product}=="PX4 Crazyflie v2.0", SYMLINK+="px4fmu"
SUBSYSTEM=="tty", ATTRS{idVendor}=="1FC9", ATTRS{idProduct}=="001c", ATTRS{product}=="PX4 FMUK66 v3.x", SYMLINK+="px4fmu"
SUBSYSTEM=="tty", ATTRS{idVendor}=="1FC9", ATTRS{idProduct}=="001c", ATTRS{product}=="PX4 FMUK66 E", SYMLINK+="px4fmu"
SUBSYSTEM=="tty", ATTRS{idVendor}=="1FC9", ATTRS{idProduct}=="001d", ATTRS{product}=="PX4 FMURT1062 v1.x", SYMLINK+="px4fmu"
SUBSYSTEM=="tty", ATTRS{idVendor}=="26ac", ATTRS{idProduct}=="0001", ATTRS{product}=="DiatoneMambaF405 MK2", SYMLINK+="px4fmu"
SUBSYSTEM=="tty", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="a32f", ATTRS{product}=="PX4 FMU ModalAI FCv1", SYMLINK+="px4fmu"
SUBSYSTEM=="tty", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="a330", ATTRS{product}=="PX4 FMU ModalAI FCv2", SYMLINK+="px4fmu"
SUBSYSTEM=="tty", ATTRS{idVendor}=="26ac", ATTRS{idProduct}=="0012", ATTRS{product}=="PX4 FMU UVify Core", SYMLINK+="px4fmu"
SUBSYSTEM=="tty", ATTRS{idVendor}=="3162", ATTRS{idProduct}=="0050", ATTRS{product}=="PX4 KakuteH7", SYMLINK+="px4fmu"
SUBSYSTEM=="tty", ATTRS{idVendor}=="3162", ATTRS{idProduct}=="0050", ATTRS{product}=="PX4 KakuteH7v2", SYMLINK+="px4fmu"
SUBSYSTEM=="tty", ATTRS{idVendor}=="3162", ATTRS{idProduct}=="004b", ATTRS{product}=="PX4 DurandalV1", SYMLINK+="px4fmu"
SUBSYSTEM=="tty", ATTRS{idVendor}=="26ac", ATTRS{idProduct}=="0050", ATTRS{product}=="PX4 KakuteF7", SYMLINK+="px4fmu"
SUBSYSTEM=="tty", ATTRS{idVendor}=="3162", ATTRS{idProduct}=="0050", ATTRS{product}=="PX4 KakuteH7Mini-nand", SYMLINK+="px4fmu"
SUBSYSTEM=="tty", ATTRS{idVendor}=="3162", ATTRS{idProduct}=="004E", ATTRS{product}=="PX4 PIX32V5", SYMLINK+="px4fmu"
SUBSYSTEM=="tty", ATTRS{idVendor}=="26ac", ATTRS{idProduct}=="0061", ATTRS{product}=="PX4 ATL Mantis-EDU", SYMLINK+="px4fmu"
SUBSYSTEM=="tty", ATTRS{idVendor}=="3163", ATTRS{idProduct}=="004c", ATTRS{product}=="PX4 CUAV Nora", SYMLINK+="px4fmu"
SUBSYSTEM=="tty", ATTRS{idVendor}=="3163", ATTRS{idProduct}=="004c", ATTRS{product}=="PX4 CUAV X7Pro", SYMLINK+="px4fmu"
SUBSYSTEM=="tty", ATTRS{idVendor}=="1B8C", ATTRS{idProduct}=="0036", ATTRS{product}=="MatekH743-mini", SYMLINK+="px4fmu"
SUBSYSTEM=="tty", ATTRS{idVendor}=="1B8C", ATTRS{idProduct}=="0036", ATTRS{product}=="MatekH743", SYMLINK+="px4fmu"
SUBSYSTEM=="tty", ATTRS{idVendor}=="120A", ATTRS{idProduct}=="1004", ATTRS{product}=="Matekgnssm9nf4", SYMLINK+="px4fmu"
SUBSYSTEM=="tty", ATTRS{idVendor}=="1209", ATTRS{idProduct}=="1013", ATTRS{product}=="MatekH743", SYMLINK+="px4fmu"
SUBSYSTEM=="tty", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="0037", ATTRS{product}=="PX4 FMU SmartAP AIRLink", SYMLINK+="px4fmu"
SUBSYSTEM=="tty", ATTRS{idVendor}=="2DAE", ATTRS{idProduct}=="1058", ATTRS{product}=="CubeOrange+", SYMLINK+="px4fmu"
SUBSYSTEM=="tty", ATTRS{idVendor}=="2DAE", ATTRS{idProduct}=="1012", ATTRS{product}=="CubeYellow", SYMLINK+="px4fmu"
SUBSYSTEM=="tty", ATTRS{idVendor}=="2DAE", ATTRS{idProduct}=="1016", ATTRS{product}=="CubeOrange", SYMLINK+="px4fmu"
SUBSYSTEM=="tty", ATTRS{idVendor}=="3185", ATTRS{idProduct}=="0035", ATTRS{product}=="PX4 FMU v6X.x", SYMLINK+="px4fmu"
SUBSYSTEM=="tty", ATTRS{idVendor}=="3185", ATTRS{idProduct}=="0038", ATTRS{product}=="PX4 FMU v6C.x", SYMLINK+="px4fmu"
SUBSYSTEM=="tty", ATTRS{idVendor}=="3185", ATTRS{idProduct}=="0033", ATTRS{product}=="PX4 FMU v5X.x", SYMLINK+="px4fmu"
SUBSYSTEM=="tty", ATTRS{idVendor}=="1B8C", ATTRS{idProduct}=="0036", ATTRS{product}=="PX4 FMU v6U.x", SYMLINK+="px4fmu"
SUBSYSTEM=="tty", ATTRS{idVendor}=="26ac", ATTRS{idProduct}=="0013", ATTRS{product}=="PX4 FMU v4.x PRO", SYMLINK+="px4fmu"
SUBSYSTEM=="tty", ATTRS{idVendor}=="26ac", ATTRS{idProduct}=="0011", ATTRS{product}=="PX4 FMU v2.x", SYMLINK+="px4fmu"
SUBSYSTEM=="tty", ATTRS{idVendor}=="26ac", ATTRS{idProduct}=="0012", ATTRS{product}=="PX4 FMU v4.x", SYMLINK+="px4fmu"
SUBSYSTEM=="tty", ATTRS{idVendor}=="26ac", ATTRS{idProduct}=="0032", ATTRS{product}=="PX4 FMU v5.x", SYMLINK+="px4fmu"
SUBSYSTEM=="tty", ATTRS{idVendor}=="3162", ATTRS{idProduct}=="004b", ATTRS{product}=="PX4 SP RACING H7 EXTREME", SYMLINK+="px4fmu"
SUBSYSTEM=="tty", ATTRS{idVendor}=="26ac", ATTRS{idProduct}=="0030", ATTRS{product}=="MindPX FMU v2.x", SYMLINK+="px4fmu"
SUBSYSTEM=="tty", ATTRS{idVendor}=="3185", ATTRS{idProduct}=="0039", ATTRS{product}=="ARK FMU v6X.x", SYMLINK+="px4fmu"
SUBSYSTEM=="tty", ATTRS{idVendor}=="26ac", ATTRS{idProduct}=="0016", ATTRS{product}=="PX4 FreeFly RTK GPS", SYMLINK+="px4fmu"
SUBSYSTEM=="tty", ATTRS{idVendor}=="26ac", ATTRS{idProduct}=="1024", ATTRS{product}=="mRoControlZeroH7 OEM", SYMLINK+="px4fmu"
SUBSYSTEM=="tty", ATTRS{idVendor}=="26ac", ATTRS{idProduct}=="1017", ATTRS{product}=="mRoPixracerPro", SYMLINK+="px4fmu"
SUBSYSTEM=="tty", ATTRS{idVendor}=="26ac", ATTRS{idProduct}=="1023", ATTRS{product}=="mRoControlZeroH7", SYMLINK+="px4fmu"
SUBSYSTEM=="tty", ATTRS{idVendor}=="26ac", ATTRS{idProduct}=="008D", ATTRS{product}=="mRoControlZeroF7", SYMLINK+="px4fmu"
SUBSYSTEM=="tty", ATTRS{idVendor}=="26ac", ATTRS{idProduct}=="0021", ATTRS{product}=="PX4 AUAV X2.1", SYMLINK+="px4fmu"
SUBSYSTEM=="tty", ATTRS{idVendor}=="26ac", ATTRS{idProduct}=="1022", ATTRS{product}=="mRoControlZero Classic", SYMLINK+="px4fmu"
SUBSYSTEM=="tty", ATTRS{idVendor}=="26ac", ATTRS{idProduct}=="0088", ATTRS{product}=="mRo x2.1-777", SYMLINK+="px4fmu"
SUBSYSTEM=="tty", ATTRS{idVendor}=="35a7", ATTRS{idProduct}=="0002", ATTRS{product}=="FCC-R1", SYMLINK+="px4fmu"
SUBSYSTEM=="tty", ATTRS{idVendor}=="35a7", ATTRS{idProduct}=="0001", ATTRS{product}=="FCC-K1", SYMLINK+="px4fmu"

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

@@ -9,7 +9,7 @@
<li><a href="" id="butterfly">Open web terminal</a> (<code>Butterfly</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.log">Clover console</a> (<code>/var/log/clover.log</code>)</li>
<li><a href="console.html">Clover console</a> (<code>/var/log/clover.log</code>)</li>
</ul>
<div class="version"></div>

View File

@@ -1,13 +1,16 @@
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);
@@ -37,19 +40,32 @@ function viewTopicsList() {
let rosdistro;
function viewTopic(topic) {
title.innerHTML = 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 = `${topic} <a id="topic-type" href=${href} target="_blank">${typeStr}</a>`;
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;
topicMessage.innerHTML = yamlStringify(msg); // JSON.stringify(msg, null, 4);
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;
});
}
@@ -59,8 +75,6 @@ topicMessage.addEventListener('mousedown', function() { mouseDown = true; });
topicMessage.addEventListener('mouseup', function() { mouseDown = false; });
function init() {
const params = Object.fromEntries(new URLSearchParams(window.location.search).entries());
if (!params.topic) {
viewTopicsList();
} else {

View File

@@ -15,13 +15,15 @@
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" title="Hold mouse button to pause">No messages received</code>
<code id="topic-message">No messages received</code>
</body>
</html>

View File

@@ -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.
@@ -45,11 +45,12 @@ 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).
* `~print` ([*std_msgs/String*](http://docs.ros.org/noetic/api/std_msgs/html/msg/String.html)) user program output messages (published in *print* blocks).
* `~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.24.0</version>
<description>Blockly programming support for Clover</description>
<maintainer email="okalachev@gmail.com">Oleg Kalachev</maintainer>
<license>MIT</license>

View File

@@ -15,6 +15,7 @@ const COLOR_GPIO = 200;
const DOCS_URL = 'https://clover.coex.tech/en/blocks.html';
var frameIds = [["body", "BODY"], ["markers map", "ARUCO_MAP"], ["marker", "ARUCO"], ["last navigate target", "NAVIGATE_TARGET"], ["map", "MAP"]];
var frameIdsWithTerrain = frameIds.concat([["terrain", "TERRAIN"]]);
function considerFrameId(e) {
if (!(e instanceof Blockly.Events.Change || e instanceof Blockly.Events.Create)) return;
@@ -22,7 +23,7 @@ function considerFrameId(e) {
var frameId = this.getFieldValue('FRAME_ID');
// set appropriate coordinates labels
if (this.getInput('X')) { // block has x-y-z fields
if (frameId == 'BODY' || frameId == 'NAVIGATE_TARGET' || frameId == 'BASE_LINK') {
if (frameId == 'BODY' || frameId == 'NAVIGATE_TARGET' || frameId == 'BASE_LINK' || frameId == 'TERRAIN') {
this.getInput('X').fieldRow[0].setValue('forward');
this.getInput('Y').fieldRow[0].setValue('left');
this.getInput('Z').fieldRow[0].setValue('up');
@@ -59,8 +60,8 @@ function updateSetpointBlock(e) {
this.getInput('VY').setVisible(velocity);
this.getInput('VZ').setVisible(velocity);
this.getInput('YAW').setVisible(attitude);
this.getInput('PITCH').setVisible(attitude);
this.getInput('ROLL').setVisible(attitude);
this.getInput('PITCH').setVisible(attitude);
this.getInput('THRUST').setVisible(attitude);
this.getInput('RELATIVE_TO').setVisible(type != 'RATES');
@@ -73,7 +74,7 @@ function updateSetpointBlock(e) {
Blockly.Blocks['navigate'] = {
init: function () {
let navFrameId = frameIds.slice();
let navFrameId = frameIdsWithTerrain.slice();
navFrameId.push(['global', 'GLOBAL_LOCAL'])
navFrameId.push(['global, WGS 84 alt.', 'GLOBAL'])
this.appendDummyInput()
@@ -163,14 +164,14 @@ Blockly.Blocks['setpoint'] = {
this.appendValueInput("VZ")
.setCheck("Number")
.appendField("vz");
this.appendValueInput("PITCH")
.setCheck("Number")
.appendField("pitch")
.setVisible(false);
this.appendValueInput("ROLL")
.setCheck("Number")
.appendField("roll")
.setVisible(false);
this.appendValueInput("PITCH")
.setCheck("Number")
.appendField("pitch")
.setVisible(false);
this.appendValueInput("YAW")
.setCheck("Number")
.appendField("yaw")
@@ -213,7 +214,7 @@ Blockly.Blocks['get_position'] = {
.appendField("current")
.appendField(new Blockly.FieldDropdown([["x", "X"], ["y", "Y"], ["z", "Z"], ["vx", "VX"], ["vy", "VY"], ["vz", "VZ"]]), "FIELD")
.appendField("relative to")
.appendField(new Blockly.FieldDropdown(frameIds), "FRAME_ID");
.appendField(new Blockly.FieldDropdown(frameIdsWithTerrain), "FRAME_ID");
this.appendValueInput("ID")
.setCheck("Number")
.appendField("with ID")
@@ -247,7 +248,7 @@ Blockly.Blocks['get_attitude'] = {
init: function () {
this.appendDummyInput()
.appendField("current")
.appendField(new Blockly.FieldDropdown([["pitch", "PITCH"], ["roll", "ROLL"], ["pitch rate", "PITCH_RATE"], ["roll rate", "ROLL_RATE"], ["yaw rate", "YAW_RATE"]]), "FIELD");
.appendField(new Blockly.FieldDropdown([["roll", "ROLL"], ["pitch", "PITCH"], ["roll rate", "ROLL_RATE"], ["pitch rate", "PITCH_RATE"], ["yaw rate", "YAW_RATE"]]), "FIELD");
this.setOutput(true, "Number");
this.setColour(COLOR_STATE);
this.setTooltip("Returns current orientation or angle rates in degree or degree per second (not radian).");
@@ -268,6 +269,19 @@ Blockly.Blocks['voltage'] = {
}
};
Blockly.Blocks['get_rc'] = {
init: function () {
this.appendDummyInput()
.appendField("RC channel")
this.appendValueInput("CHANNEL")
.setCheck("Number");
this.setInputsInline(true);
this.setOutput(true, "Number");
this.setColour(COLOR_STATE);
this.setTooltip("Returns current RC channel value.");
this.setHelpUrl(DOCS_URL + '#' + this.type);
}
}
Blockly.Blocks['armed'] = {
init: function () {
@@ -509,7 +523,7 @@ Blockly.Blocks['distance'] = {
.appendField("z");
this.appendDummyInput()
.appendField("relative to")
.appendField(new Blockly.FieldDropdown([["markers map", "ARUCO_MAP"], ["marker", "ARUCO"], ["last navigate target", "NAVIGATE_TARGET"]]), "FRAME_ID");
.appendField(new Blockly.FieldDropdown([["markers map", "ARUCO_MAP"], ["marker", "ARUCO"], ["last navigate target", "NAVIGATE_TARGET"], ["terrain", "TERRAIN"]]), "FRAME_ID");
this.appendValueInput("ID")
.setCheck("Number")
.appendField("with ID")

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