Compare commits
117 Commits
install
...
web-params
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2930eb9c22 | ||
|
|
64b083b242 | ||
|
|
b2ec48f0f3 | ||
|
|
b249524828 | ||
|
|
d0dcc0e72a | ||
|
|
4c6e7029e8 | ||
|
|
613f70fd25 | ||
|
|
77832e65fa | ||
|
|
01edd129ab | ||
|
|
d03acff31b | ||
|
|
22e74febd6 | ||
|
|
989d9b66f1 | ||
|
|
f8eb8e1e67 | ||
|
|
b92ebe7d60 | ||
|
|
af51e88179 | ||
|
|
59c8debcab | ||
|
|
ec6f3089e3 | ||
|
|
2b88d21792 | ||
|
|
274b81c50f | ||
|
|
33a6dffb1f | ||
|
|
5f9b3e82db | ||
|
|
5f43367d82 | ||
|
|
7809e7ed2d | ||
|
|
1688b97091 | ||
|
|
1c6129fde8 | ||
|
|
dae9599d64 | ||
|
|
c0677f6aa3 | ||
|
|
e7bbf21700 | ||
|
|
58c10d7cb8 | ||
|
|
b6bd6bdde8 | ||
|
|
3374c7756c | ||
|
|
0dffeca55f | ||
|
|
8cb911854d | ||
|
|
a1b3efe67d | ||
|
|
6700d8728f | ||
|
|
be2e6ae198 | ||
|
|
b9eed0f3ad | ||
|
|
853a7fcf67 | ||
|
|
e342796f07 | ||
|
|
4fa70aa73a | ||
|
|
226c91c3d8 | ||
|
|
e33c9e78ea | ||
|
|
18c927469e | ||
|
|
a465afd65c | ||
|
|
a2c65d2466 | ||
|
|
ef7faa126a | ||
|
|
d0666ca9d7 | ||
|
|
b48f22cd35 | ||
|
|
731f908053 | ||
|
|
505a1efebd | ||
|
|
f1b5484e65 | ||
|
|
3343477a02 | ||
|
|
60da608191 | ||
|
|
7e383d713d | ||
|
|
c2a60380b7 | ||
|
|
7f82f8683f | ||
|
|
e28cbea8d9 | ||
|
|
27528c20dc | ||
|
|
e3503fca35 | ||
|
|
40b8941cab | ||
|
|
0a9b6fff95 | ||
|
|
4a4e539edd | ||
|
|
8d24a737ac | ||
|
|
20af13947d | ||
|
|
e91609ff61 | ||
|
|
0024372c45 | ||
|
|
b62d202a29 | ||
|
|
84d685469a | ||
|
|
3f6bb0cd68 | ||
|
|
c01a145a16 | ||
|
|
6d28bf4ef9 | ||
|
|
f6ea7209db | ||
|
|
21727ef76d | ||
|
|
9847b7a71c | ||
|
|
a5d44ff63a | ||
|
|
391a2f9af9 | ||
|
|
5918702fbd | ||
|
|
69fe288a41 | ||
|
|
5154720348 | ||
|
|
cc1694661d | ||
|
|
d5efa962d8 | ||
|
|
1195336cbc | ||
|
|
5fc67b8e65 | ||
|
|
c1409d4467 | ||
|
|
7a1f09da98 | ||
|
|
6934cc7a1a | ||
|
|
2550ffe532 | ||
|
|
9def866a30 | ||
|
|
802f8eeaa4 | ||
|
|
504aa53b1a | ||
|
|
015cf730c2 | ||
|
|
8e6ef727ce | ||
|
|
973e1f1181 | ||
|
|
275faa78a4 | ||
|
|
fc7d010881 | ||
|
|
242e35f84a | ||
|
|
3f07d28e0f | ||
|
|
613d378e66 | ||
|
|
769c421898 | ||
|
|
3830ceea04 | ||
|
|
df3a11b035 | ||
|
|
ef5700845f | ||
|
|
921084504e | ||
|
|
6550780afb | ||
|
|
2448915300 | ||
|
|
247a7917d9 | ||
|
|
37f2c78b36 | ||
|
|
e76618bd3b | ||
|
|
9fbfcfbd2e | ||
|
|
2003b4516a | ||
|
|
03985ae1b8 | ||
|
|
a47d5d1bfe | ||
|
|
20075dd40f | ||
|
|
c247c75d17 | ||
|
|
c36279e536 | ||
|
|
1471a53b3a | ||
|
|
931f50a458 |
6
.github/workflows/build-image.yaml
vendored
@@ -1,4 +1,4 @@
|
||||
name: Build RPi image
|
||||
name: RPi image
|
||||
|
||||
on:
|
||||
push:
|
||||
@@ -9,7 +9,7 @@ on:
|
||||
types: [ created ]
|
||||
|
||||
jobs:
|
||||
build-image:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
@@ -18,7 +18,7 @@ jobs:
|
||||
docker run --privileged --rm -v /dev:/dev -v $(pwd):/builder/repo -e TRAVIS_TAG="${{ github.event.release.tag_name }}" sfalexrog/img-tool:qemu-update
|
||||
- name: Compress image
|
||||
run: |
|
||||
sudo chmod -R 777 images && zip -9 $(echo images/clover_*).zip images/clover_* && ls -l images
|
||||
cd images && sudo chmod -R 777 . && zip -9 $(echo clover_*).zip clover_* && ls -l . && unzip -l clover_*.zip
|
||||
- name: Upload image
|
||||
uses: softprops/action-gh-release@v1
|
||||
if: ${{ github.event_name == 'release' }}
|
||||
|
||||
4
.github/workflows/build.yml
vendored
@@ -7,14 +7,14 @@ on:
|
||||
branches: [ master ]
|
||||
|
||||
jobs:
|
||||
build-melodic:
|
||||
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
|
||||
build-noetic:
|
||||
noetic:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
7
.github/workflows/docs.yml
vendored
@@ -7,7 +7,7 @@ on:
|
||||
branches: [ master ]
|
||||
|
||||
jobs:
|
||||
documentation:
|
||||
docs:
|
||||
runs-on: ubuntu-18.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
@@ -18,9 +18,8 @@ jobs:
|
||||
run: |
|
||||
sudo sh -c "echo ttf-mscorefonts-installer msttcorefonts/accepted-mscorefonts-eula select true | debconf-set-selections"
|
||||
sudo apt-get update && sudo apt-get install -y calibre msttcorefonts
|
||||
npm install gitbook-cli -g
|
||||
gitbook fetch 3.2.3 && npm i npm@3.10.10 --prefix=~/.gitbook/versions/3.2.3/ # fixing https://travis-ci.org/github/CopterExpress/clover/jobs/766541125#L932
|
||||
npm install markdownlint-cli -g
|
||||
builder/assets/install_gitbook.sh
|
||||
npm install markdownlint-cli@0.28.1 -g # FIXME: https://github.com/DavidAnson/markdownlint/issues/435
|
||||
npm install svgexport -g
|
||||
gitbook -V
|
||||
markdownlint -V
|
||||
|
||||
4
.github/workflows/editorconfig.yaml
vendored
@@ -1,4 +1,4 @@
|
||||
name: Editorconfig lint
|
||||
name: Editorconfig
|
||||
|
||||
on:
|
||||
push:
|
||||
@@ -15,4 +15,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|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"
|
||||
|
||||
1
.gitignore
vendored
@@ -6,3 +6,4 @@ _book/
|
||||
package-lock.json
|
||||
clover_blocks/programs/*.*
|
||||
!clover_blocks/programs/examples/*
|
||||
/.vscode/
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# clover🍀: create autonomous drones easily
|
||||
|
||||
<img src="docs/assets/clover42-main.png" align="right" width="400px" alt="COEX Clover Drone">
|
||||
<img src="docs/assets/clover42-main-margin.png" align="right" width="400px" alt="COEX Clover Drone">
|
||||
|
||||
Clover is an open source [ROS](https://www.ros.org)-based framework, providing user-friendly tools to control [PX4](https://px4.io)-powered drones. Clover is available as a ROS package, but is shipped mainly as a preconfigured image for Raspberry Pi. Once you've installed Raspberry Pi on your drone and flashed the image to its microSD card, taking the drone up in the air is a matter of minutes.
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
iOS-приложение для управления Клевером
|
||||
--------------------------------------
|
||||
# iOS-приложение для управления Клевером
|
||||
|
||||
Для установки зависимостей необходим [CocoaPods](https://cocoapods.org):
|
||||
|
||||
|
||||
@@ -202,11 +202,11 @@ set_property(TARGET aruco_pose
|
||||
# )
|
||||
|
||||
## Mark executables and/or libraries for installation
|
||||
# install(TARGETS ${PROJECT_NAME} ${PROJECT_NAME}_node
|
||||
# ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
|
||||
# LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
|
||||
# RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
|
||||
# )
|
||||
install(TARGETS ${PROJECT_NAME}
|
||||
ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
|
||||
LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
|
||||
RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
|
||||
)
|
||||
|
||||
## Mark cpp header files for installation
|
||||
# install(DIRECTORY include/${PROJECT_NAME}/
|
||||
@@ -226,6 +226,10 @@ catkin_install_python(PROGRAMS src/genmap.py
|
||||
DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
|
||||
)
|
||||
|
||||
install(DIRECTORY launch DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION})
|
||||
|
||||
install(DIRECTORY map DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION})
|
||||
|
||||
#############
|
||||
## Testing ##
|
||||
#############
|
||||
|
||||
@@ -70,7 +70,7 @@ It's recommended to run it within the same nodelet manager with the camera nodel
|
||||
|
||||
* `~map` – path to text file with markers list
|
||||
* `~frame_id` – published frame id (default: `aruco_map`)
|
||||
* `~known_tilt` – debug image width
|
||||
* `~known_tilt` – known tilt (pitch and roll) of markers map as a frame
|
||||
* `~image_width` – debug image width (default: 2000)
|
||||
* `~image_height` – debug image height (default: 2000)
|
||||
* `~image_margin` – debug image margin (default: 200)
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
"yametrika",
|
||||
"anchors",
|
||||
"collapsible-menu",
|
||||
"validate-links",
|
||||
"validate-links@https://github.com/okalachev/gitbook-plugin-validate-links.git",
|
||||
"bulk-redirect@https://github.com/okalachev/gitbook-plugin-bulk-redirect.git",
|
||||
"sitemap@https://github.com/okalachev/plugin-sitemap.git",
|
||||
"toolbar@https://github.com/hamishwillee/gitbook-plugin-toolbar.git",
|
||||
|
||||
@@ -8,5 +8,9 @@ ExecStart=/bin/bash -c ". /home/pi/catkin_ws/devel/setup.sh; \
|
||||
ROS_HOSTNAME=`hostname`.local exec stdbuf -o L roslaunch clover clover.launch --wait --screen --skip-log-check \
|
||||
2> >(tee /tmp/clover.err)"
|
||||
|
||||
ExecStartPre=+rm /var/log/clover.log
|
||||
StandardOutput=file:/var/log/clover.log
|
||||
StandardError=file:/var/log/clover.log
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
||||
9
builder/assets/install_gitbook.sh
Executable file
@@ -0,0 +1,9 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# GitBook CLI is deprecated, its installation is broken.
|
||||
# This script fixes it until we stop using GitBook.
|
||||
|
||||
export NPM_CONFIG_UNSAFE_PERM=1
|
||||
|
||||
npm install gitbook-cli -g
|
||||
gitbook fetch 3.2.3 && npm i npm@3.10.10 --prefix=~/.gitbook/versions/3.2.3/ # fixing https://travis-ci.org/github/CopterExpress/clover/jobs/766541125#L932
|
||||
@@ -105,8 +105,6 @@ ${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/butterf
|
||||
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/monkey.service' '/lib/systemd/system/'
|
||||
# software install
|
||||
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} exec ${SCRIPTS_DIR}'/image-software.sh'
|
||||
# examples
|
||||
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/examples' '/home/pi/' # TODO: symlink?
|
||||
# network setup
|
||||
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} exec ${SCRIPTS_DIR}'/image-network.sh'
|
||||
# avahi setup
|
||||
@@ -115,15 +113,11 @@ ${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/avahi-s
|
||||
# If RPi then use a one thread to build a ROS package on RPi, else use all
|
||||
[[ $(arch) == 'armv7l' ]] && NUMBER_THREADS=1 || NUMBER_THREADS=$(nproc --all)
|
||||
# Clover
|
||||
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/clover.service' '/lib/systemd/system/'
|
||||
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/roscore.service' '/lib/systemd/system/'
|
||||
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/noetic-rosdep-clover.yaml' '/etc/ros/rosdep/'
|
||||
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/ros_python_paths' '/etc/sudoers.d/'
|
||||
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/pigpiod.service' '/lib/systemd/system/'
|
||||
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/launch.nanorc' '/usr/share/nano/'
|
||||
# ${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/kinetic-ros-clover.rosinstall' '/home/pi/ros_catkin_ws/'
|
||||
# Add PX4 udev rules
|
||||
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${REPO_DIR}'/clover/config/99-px4fmu.rules' '/lib/udev/rules.d/'
|
||||
# Add rename script
|
||||
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} exec ${SCRIPTS_DIR}'/image-ros.sh' ${REPO_URL} ${IMAGE_VERSION} false false ${NUMBER_THREADS}
|
||||
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} exec ${SCRIPTS_DIR}'/image-validate.sh'
|
||||
|
||||
@@ -113,6 +113,7 @@ my_travis_retry pip3 install -r /home/pi/catkin_ws/src/clover/clover/requirement
|
||||
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
|
||||
source devel/setup.bash
|
||||
|
||||
echo_stamp "Install clever package (for backwards compatibility)"
|
||||
cd /home/pi/catkin_ws/src/clover/builder/assets/clever
|
||||
@@ -121,9 +122,8 @@ rm -rf build # remove build artifacts
|
||||
|
||||
echo_stamp "Build Clover documentation"
|
||||
cd /home/pi/catkin_ws/src/clover
|
||||
NPM_CONFIG_UNSAFE_PERM=true npm install gitbook-cli -g
|
||||
NPM_CONFIG_UNSAFE_PERM=true gitbook fetch 3.2.3 && npm i npm@3.10.10 --prefix=~/.gitbook/versions/3.2.3/ # fixing https://travis-ci.org/github/CopterExpress/clover/jobs/766541125#L932
|
||||
NPM_CONFIG_UNSAFE_PERM=true gitbook install
|
||||
builder/assets/install_gitbook.sh
|
||||
gitbook install
|
||||
gitbook build
|
||||
touch node_modules/CATKIN_IGNORE docs/CATKIN_IGNORE _book/CATKIN_IGNORE clover/www/CATKIN_IGNORE apps/CATKIN_IGNORE # ignore documentation files by catkin
|
||||
|
||||
@@ -151,9 +151,20 @@ catkin_make run_tests #&& catkin_test_results
|
||||
echo_stamp "Change permissions for catkin_ws"
|
||||
chown -Rf pi:pi /home/pi/catkin_ws
|
||||
|
||||
echo_stamp "Change permissions for examples"
|
||||
echo_stamp "Make \$HOME/examples symlink"
|
||||
ln -s "$(catkin_find clover examples --first-only)" /home/pi
|
||||
chown -Rf pi:pi /home/pi/examples
|
||||
|
||||
echo_stamp "Make systemd services symlinks"
|
||||
ln -s /home/pi/catkin_ws/src/clover/builder/assets/clover.service /lib/systemd/system/
|
||||
ln -s /home/pi/catkin_ws/src/clover/builder/assets/roscore.service /lib/systemd/system/
|
||||
# validate
|
||||
[ -f /lib/systemd/system/clover.service ]
|
||||
[ -f /lib/systemd/system/roscore.service ]
|
||||
|
||||
echo_stamp "Make udev rules symlink"
|
||||
ln -s "$(catkin_find clover udev --first-only)"/* /lib/udev/rules.d/
|
||||
|
||||
echo_stamp "Setup ROS environment"
|
||||
cat << EOF >> /home/pi/.bashrc
|
||||
LANG='C.UTF-8'
|
||||
|
||||
@@ -31,5 +31,9 @@ cd /home/pi/catkin_ws/src/clover/builder/test/
|
||||
|
||||
systemctl stop roscore
|
||||
|
||||
# check documented packages available
|
||||
apt-cache show gst-rtsp-launch
|
||||
apt-cache show openvpn
|
||||
|
||||
echo "Move /etc/ld.so.preload back to its original position"
|
||||
mv /etc/ld.so.preload.disabled-for-build /etc/ld.so.preload
|
||||
|
||||
@@ -34,6 +34,7 @@ i2cdetect -V
|
||||
butterfly -h
|
||||
# espeak --version
|
||||
mjpg_streamer --version
|
||||
systemctl --version
|
||||
|
||||
# ros stuff
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import sys
|
||||
import subprocess
|
||||
|
||||
EXCLUDE = ('clever4-front-white.png', 'clever4-front-white-large.png', '.DS_Store',
|
||||
'clever4-front-black-large.png','clover42-black.png')
|
||||
'clever4-front-black-large.png','clover42-black.png', 'clover42-main-margin.png')
|
||||
|
||||
code = 0
|
||||
|
||||
|
||||
@@ -241,12 +241,12 @@ target_link_libraries(${PROJECT_NAME}
|
||||
# DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
|
||||
# )
|
||||
|
||||
## Mark executables and/or libraries for installation
|
||||
# install(TARGETS ${PROJECT_NAME} ${PROJECT_NAME}_node
|
||||
# ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
|
||||
# LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
|
||||
# RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
|
||||
# )
|
||||
# Mark executables and/or libraries for installation
|
||||
install(TARGETS simple_offboard clover_rc camera_markers vpe_publisher clover_led shell clover
|
||||
ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
|
||||
LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
|
||||
RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
|
||||
)
|
||||
|
||||
## Mark cpp header files for installation
|
||||
# install(DIRECTORY include/${PROJECT_NAME}/
|
||||
@@ -266,13 +266,21 @@ catkin_install_python(PROGRAMS src/selfcheck.py
|
||||
DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
|
||||
)
|
||||
|
||||
install(DIRECTORY launch DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION})
|
||||
|
||||
install(DIRECTORY examples DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION})
|
||||
|
||||
install(DIRECTORY www DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION})
|
||||
|
||||
# TODO: install www
|
||||
|
||||
# Only install udev rules when building a Debian package
|
||||
# FIXME: Other operating systems may have other prefixes
|
||||
string(FIND ${CMAKE_INSTALL_PREFIX} "/opt/ros" _PREFIX_INDEX)
|
||||
if (${_PREFIX_INDEX} EQUAL 0)
|
||||
message(STATUS "Building as a Debian package - adding udev rules as installable files")
|
||||
install(FILES
|
||||
config/99-px4fmu.rules
|
||||
udev/99-px4fmu.rules
|
||||
DESTINATION /lib/udev/rules.d
|
||||
)
|
||||
else()
|
||||
|
||||
@@ -36,7 +36,7 @@ curl https://raw.githubusercontent.com/mavlink/mavros/master/mavros/scripts/inst
|
||||
You may optionally install udev rules to provide `/dev/px4fmu` symlink to your PX4-based flight controller connected over USB. Copy `99-px4fmu.rules` to your `/lib/udev/rules.d` folder:
|
||||
|
||||
```bash
|
||||
cd ~/catkin_ws/src/clover/clover/config
|
||||
cd ~/catkin_ws/src/clover/clover/udev
|
||||
sudo cp 99-px4fmu.rules /lib/udev/rules.d
|
||||
```
|
||||
|
||||
|
||||
@@ -15,17 +15,17 @@ set_attitude = rospy.ServiceProxy('set_attitude', srv.SetAttitude)
|
||||
set_rates = rospy.ServiceProxy('set_rates', srv.SetRates)
|
||||
land = rospy.ServiceProxy('land', Trigger)
|
||||
|
||||
# Take off and hover 1 m above the ground
|
||||
print('Take off and hover 1 m above the ground')
|
||||
navigate(x=0, y=0, z=1, frame_id='body', auto_arm=True)
|
||||
|
||||
# Wait for 3 seconds
|
||||
rospy.sleep(3)
|
||||
# Wait for 5 seconds
|
||||
rospy.sleep(5)
|
||||
|
||||
# Fly forward 1 m
|
||||
print('Fly forward 1 m')
|
||||
navigate(x=1, y=0, z=0, frame_id='body')
|
||||
|
||||
# Wait for 3 seconds
|
||||
rospy.sleep(3)
|
||||
# Wait for 5 seconds
|
||||
rospy.sleep(5)
|
||||
|
||||
# Perform landing
|
||||
print('Perform landing')
|
||||
land()
|
||||
@@ -15,23 +15,23 @@ set_attitude = rospy.ServiceProxy('set_attitude', srv.SetAttitude)
|
||||
set_rates = rospy.ServiceProxy('set_rates', srv.SetRates)
|
||||
land = rospy.ServiceProxy('land', Trigger)
|
||||
|
||||
# Take off and hover 1 m above the ground
|
||||
print('Take off and hover 1 m above the ground')
|
||||
navigate(x=0, y=0, z=1, frame_id='body', auto_arm=True)
|
||||
|
||||
# Wait for 3 seconds
|
||||
rospy.sleep(3)
|
||||
# Wait for 5 seconds
|
||||
rospy.sleep(5)
|
||||
|
||||
# Fly 1 meter above ArUco marker 0
|
||||
print('Fly 1 meter above ArUco marker 0')
|
||||
navigate(x=0, y=0, z=1, frame_id='aruco_0')
|
||||
|
||||
# Wait for 3 seconds
|
||||
rospy.sleep(3)
|
||||
# Wait for 5 seconds
|
||||
rospy.sleep(5)
|
||||
|
||||
# Fly to x=1 y=1 z=1 relative to ArUco markers map
|
||||
print('Fly to x=1 y=1 z=1 relative to ArUco markers map')
|
||||
navigate(x=1, y=1, z=1, frame_id='aruco_map')
|
||||
|
||||
# Wait for 3 seconds
|
||||
rospy.sleep(3)
|
||||
# Wait for 5 seconds
|
||||
rospy.sleep(5)
|
||||
|
||||
# Perform landing
|
||||
print('Perform landing')
|
||||
land()
|
||||
47
clover/examples/gps.py
Normal file
@@ -0,0 +1,47 @@
|
||||
# Information: https://clover.coex.tech/en/simple_offboard.html#navigateglobal
|
||||
|
||||
import rospy
|
||||
from clover import srv
|
||||
from std_srvs.srv import Trigger
|
||||
import math
|
||||
|
||||
rospy.init_node('flight')
|
||||
|
||||
get_telemetry = rospy.ServiceProxy('get_telemetry', srv.GetTelemetry)
|
||||
navigate = rospy.ServiceProxy('navigate', srv.Navigate)
|
||||
navigate_global = rospy.ServiceProxy('navigate_global', srv.NavigateGlobal)
|
||||
set_position = rospy.ServiceProxy('set_position', srv.SetPosition)
|
||||
set_velocity = rospy.ServiceProxy('set_velocity', srv.SetVelocity)
|
||||
set_attitude = rospy.ServiceProxy('set_attitude', srv.SetAttitude)
|
||||
set_rates = rospy.ServiceProxy('set_rates', srv.SetRates)
|
||||
land = rospy.ServiceProxy('land', Trigger)
|
||||
|
||||
# https://clover.coex.tech/en/snippets.html#wait_arrival
|
||||
def wait_arrival(tolerance=0.2):
|
||||
while not rospy.is_shutdown():
|
||||
telem = get_telemetry(frame_id='navigate_target')
|
||||
if math.sqrt(telem.x ** 2 + telem.y ** 2 + telem.z ** 2) < tolerance:
|
||||
break
|
||||
rospy.sleep(0.2)
|
||||
|
||||
start = get_telemetry()
|
||||
|
||||
if math.isnan(start.lat):
|
||||
raise Exception('No global position, install and configure GPS sensor: https://clover.coex.tech/gps')
|
||||
|
||||
print('Start point global position: lat={}, lon={}'.format(start.lat, start.lon))
|
||||
|
||||
print('Take off 3 meters')
|
||||
navigate(x=0, y=0, z=3, frame_id='body', auto_arm=True)
|
||||
wait_arrival()
|
||||
|
||||
print('Fly 1 arcsecond to the North (approx. 30 meters)')
|
||||
navigate_global(lat=start.lat+1.0/60/60, lon=start.lon, z=start.z+3, yaw=math.inf, speed=5)
|
||||
wait_arrival()
|
||||
|
||||
print('Fly to home position')
|
||||
navigate_global(lat=start.lat, lon=start.lon, z=start.z+3, yaw=math.inf, speed=5)
|
||||
wait_arrival()
|
||||
|
||||
print('Land')
|
||||
land()
|
||||
@@ -31,11 +31,11 @@ def navigate_wait(x=0, y=0, z=0, yaw=float('nan'), yaw_rate=0, speed=0.5, \
|
||||
return res
|
||||
rospy.sleep(0.2)
|
||||
|
||||
# Take off 1 meter
|
||||
print('Take off 1 meter')
|
||||
navigate_wait(z=1, frame_id='body', auto_arm=True)
|
||||
|
||||
# Fly forward 1 m
|
||||
print('Fly forward 1 m')
|
||||
navigate_wait(x=1, frame_id='body')
|
||||
|
||||
# Land
|
||||
print('Land')
|
||||
land()
|
||||
15
clover/examples/subscriber.py
Normal file
@@ -0,0 +1,15 @@
|
||||
# Information: https://clover.coex.tech/en/laser.html
|
||||
|
||||
import rospy
|
||||
from sensor_msgs.msg import Range
|
||||
|
||||
rospy.init_node('process_rangefinder')
|
||||
|
||||
def range_callback(msg):
|
||||
# Process data from the rangefinder
|
||||
print('Rangefinder distance:', msg.range)
|
||||
|
||||
# Subscribe to laser rangefinder data
|
||||
rospy.Subscriber('rangefinder/range', Range, range_callback)
|
||||
|
||||
rospy.spin()
|
||||
@@ -3,7 +3,7 @@
|
||||
<arg name="aruco_map" default="false"/>
|
||||
<arg name="aruco_vpe" default="false"/>
|
||||
<arg name="placement" default="floor"/> <!-- markers placement: floor, ceiling, unknown -->
|
||||
<arg name="length" default="0.33"/> <!-- not-in-map markers length, m -->
|
||||
<arg name="length" default="0.22"/> <!-- not-in-map markers length, m -->
|
||||
<arg name="map" default="map.txt"/> <!-- markers map file name -->
|
||||
|
||||
<!-- For additional help go to https://clover.coex.tech/aruco -->
|
||||
@@ -12,7 +12,7 @@
|
||||
<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">
|
||||
<remap from="image_raw" to="main_camera/image_raw"/>
|
||||
<remap from="camera_info" to="main_camera/camera_info"/>
|
||||
<remap from="map_markers" to="aruco_map/markers" if="$(arg aruco_map)"/>
|
||||
<remap from="map_markers" to="aruco_map/markers"/>
|
||||
<param name="estimate_poses" value="true"/>
|
||||
<param name="send_tf" value="true"/>
|
||||
<param name="known_tilt" value="map" if="$(eval placement == 'floor')"/>
|
||||
|
||||
@@ -75,9 +75,6 @@ private:
|
||||
velo_pub_ = nh_priv.advertise<geometry_msgs::TwistStamped>("angular_velocity", 1);
|
||||
shift_pub_ = nh_priv.advertise<geometry_msgs::Vector3Stamped>("shift", 1);
|
||||
|
||||
flow_.integrated_xgyro = NAN; // no IMU available
|
||||
flow_.integrated_ygyro = NAN;
|
||||
flow_.integrated_zgyro = NAN;
|
||||
flow_.time_delta_distance_us = 0;
|
||||
flow_.distance = -1; // no distance sensor available
|
||||
flow_.temperature = 0;
|
||||
@@ -179,7 +176,7 @@ private:
|
||||
double flow_x = atan2(points_undist[0].x, focal_length_x);
|
||||
double flow_y = atan2(points_undist[0].y, focal_length_y);
|
||||
|
||||
// // Convert to FCU frame
|
||||
// Convert to FCU frame
|
||||
geometry_msgs::Vector3Stamped flow_camera, flow_fcu;
|
||||
flow_camera.header.frame_id = msg->header.frame_id;
|
||||
flow_camera.header.stamp = msg->header.stamp;
|
||||
@@ -196,6 +193,11 @@ private:
|
||||
ros::Duration integration_time = msg->header.stamp - prev_stamp_;
|
||||
uint32_t integration_time_us = integration_time.toSec() * 1.0e6;
|
||||
|
||||
// Calculate flow gyro
|
||||
flow_.integrated_xgyro = NAN;
|
||||
flow_.integrated_ygyro = NAN;
|
||||
flow_.integrated_zgyro = NAN;
|
||||
|
||||
if (calc_flow_gyro_) {
|
||||
try {
|
||||
auto flow_gyro_camera = calcFlowGyro(msg->header.frame_id, prev_stamp_, msg->header.stamp);
|
||||
@@ -205,9 +207,7 @@ private:
|
||||
flow_.integrated_ygyro = flow_gyro_fcu.vector.y;
|
||||
flow_.integrated_zgyro = flow_gyro_fcu.vector.z;
|
||||
} catch (const tf2::TransformException& e) {
|
||||
// Invalidate previous frame
|
||||
prev_.release();
|
||||
goto publish_debug;
|
||||
// Transform not available, keep NANs in flow gyro
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -183,7 +183,7 @@ inline bool waitTransform(const string& target, const string& source,
|
||||
}
|
||||
}
|
||||
|
||||
#define TIMEOUT(msg, timeout) (ros::Time::now() - msg.header.stamp > timeout)
|
||||
#define TIMEOUT(msg, timeout) (msg.header.stamp.isZero() || (ros::Time::now() - msg.header.stamp > timeout))
|
||||
|
||||
bool getTelemetry(GetTelemetry::Request& req, GetTelemetry::Response& res)
|
||||
{
|
||||
@@ -441,6 +441,10 @@ void publish(const ros::Time stamp)
|
||||
|
||||
// publish setpoint frame
|
||||
if (!setpoint.child_frame_id.empty()) {
|
||||
if (setpoint.header.stamp == position_msg.header.stamp) {
|
||||
return; // avoid TF_REPEATED_DATA warnings
|
||||
}
|
||||
|
||||
setpoint.transform.translation.x = position_msg.pose.position.x;
|
||||
setpoint.transform.translation.y = position_msg.pose.position.y;
|
||||
setpoint.transform.translation.z = position_msg.pose.position.z;
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
/tmp/clover.err
|
||||
1
clover/www/clover.log
Symbolic link
@@ -0,0 +1 @@
|
||||
/var/log/clover.log
|
||||
@@ -4,12 +4,12 @@
|
||||
|
||||
<ul>
|
||||
<li><a href="docs">View documentation</a> (snapshot of <a href="https://clover.coex.tech">clover.coex.tech</a>)</li>
|
||||
<li><a href="topics.html">View topics</a></li>
|
||||
<li><a href="" id="wvs">View image topics</a> (<code>web_video_server</code>)</li>
|
||||
<li><a href="" id="butterfly">Open web terminal</a> (<code>Butterfly</code>)</li>
|
||||
<li><a href="viz.html">View 3D visualization</a> (<code>ros3djs</code>)</li>
|
||||
<li><a href="aruco_map.html">3D visualization for markers map</a> (<code>ros3djs</code>)</li>
|
||||
<li>View <a href="viz.html">View 3D visualization</a>, <a href="aruco_map.html">3D visualization for markers map</a> (<code>ros3djs</code>)</li>
|
||||
<li><a href="../clover_blocks/">Blocks programming</a> (<code>Blockly</code>)</li>
|
||||
<li><a href="clover.err">Clover console</a> (<code>/tmp/clover.err</code>)</li>
|
||||
<li><a href="clover.log">Clover console</a> (<code>/var/log/clover.log</code>)</li>
|
||||
</ul>
|
||||
|
||||
<div class="version"></div>
|
||||
@@ -18,6 +18,14 @@
|
||||
document.querySelector("#wvs").href = location.protocol + '//' + location.hostname + ':8080';
|
||||
document.querySelector("#butterfly").href = location.protocol + '//' + location.hostname + ':57575';
|
||||
|
||||
document.querySelector("#butterfly").addEventListener('click', function(e) {
|
||||
if (location.hostname == 'localhost' || location.hostname == '127.0.0.1') {
|
||||
if (!confirm('Please use regular Terminal app on a local machine.\nClick OK to proceed to Butterfly anyway.')) {
|
||||
e.preventDefault();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Determine image version
|
||||
fetch('clover_version').then(function(response) {
|
||||
if (response.status !== 200) return;
|
||||
|
||||
236
clover/www/js/json-to-pretty-yaml.js
Normal file
@@ -0,0 +1,236 @@
|
||||
// Browserified https://www.npmjs.com/package/json-to-pretty-yaml module
|
||||
|
||||
(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
|
||||
(function() {
|
||||
"use strict";
|
||||
|
||||
var typeOf = require('remedial').typeOf;
|
||||
var trimWhitespace = require('remove-trailing-spaces');
|
||||
|
||||
function stringify(data) {
|
||||
var handlers, indentLevel = '';
|
||||
|
||||
handlers = {
|
||||
"undefined": function() {
|
||||
// objects will not have `undefined` converted to `null`
|
||||
// as this may have unintended consequences
|
||||
// For arrays, however, this behavior seems appropriate
|
||||
return 'null';
|
||||
},
|
||||
"null": function() {
|
||||
return 'null';
|
||||
},
|
||||
"number": function(x) {
|
||||
return x;
|
||||
},
|
||||
"boolean": function(x) {
|
||||
return x ? 'true' : 'false';
|
||||
},
|
||||
"string": function(x) {
|
||||
// to avoid the string "true" being confused with the
|
||||
// the literal `true`, we always wrap strings in quotes
|
||||
return JSON.stringify(x);
|
||||
},
|
||||
"array": function(x) {
|
||||
var output = '';
|
||||
|
||||
if (0 === x.length) {
|
||||
output += '[]';
|
||||
return output;
|
||||
}
|
||||
|
||||
indentLevel = indentLevel.replace(/$/, ' ');
|
||||
x.forEach(function(y, i) {
|
||||
// TODO how should `undefined` be handled?
|
||||
var handler = handlers[typeOf(y)];
|
||||
|
||||
if (!handler) {
|
||||
throw new Error('what the crap: ' + typeOf(y));
|
||||
}
|
||||
|
||||
output += '\n' + indentLevel + '- ' + handler(y, true);
|
||||
|
||||
});
|
||||
indentLevel = indentLevel.replace(/ /, '');
|
||||
|
||||
return output;
|
||||
},
|
||||
"object": function(x, inArray, rootNode) {
|
||||
var output = '';
|
||||
|
||||
if (0 === Object.keys(x).length) {
|
||||
output += '{}';
|
||||
return output;
|
||||
}
|
||||
|
||||
if (!rootNode) {
|
||||
indentLevel = indentLevel.replace(/$/, ' ');
|
||||
}
|
||||
|
||||
Object.keys(x).forEach(function(k, i) {
|
||||
var val = x[k],
|
||||
handler = handlers[typeOf(val)];
|
||||
|
||||
if ('undefined' === typeof val) {
|
||||
// the user should do
|
||||
// delete obj.key
|
||||
// and not
|
||||
// obj.key = undefined
|
||||
// but we'll error on the side of caution
|
||||
return;
|
||||
}
|
||||
|
||||
if (!handler) {
|
||||
throw new Error('what the crap: ' + typeOf(val));
|
||||
}
|
||||
|
||||
if (!(inArray && i === 0)) {
|
||||
output += '\n' + indentLevel;
|
||||
}
|
||||
|
||||
output += k + ': ' + handler(val);
|
||||
});
|
||||
indentLevel = indentLevel.replace(/ /, '');
|
||||
|
||||
return output;
|
||||
},
|
||||
"function": function() {
|
||||
// TODO this should throw or otherwise be ignored
|
||||
return '[object Function]';
|
||||
}
|
||||
};
|
||||
return trimWhitespace(handlers[typeOf(data)](data, true, true) + '\n');
|
||||
|
||||
}
|
||||
|
||||
window.yamlStringify = stringify;
|
||||
module.exports.stringify = stringify;
|
||||
}());
|
||||
|
||||
},{"remedial":2,"remove-trailing-spaces":3}],2:[function(require,module,exports){
|
||||
/*jslint onevar: true, undef: true, nomen: true, eqeqeq: true, plusplus: true, bitwise: true, regexp: true, newcap: true, immed: true */
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
var global = Function('return this')()
|
||||
, classes = "Boolean Number String Function Array Date RegExp Object".split(" ")
|
||||
, i
|
||||
, name
|
||||
, class2type = {}
|
||||
;
|
||||
|
||||
for (i in classes) {
|
||||
if (classes.hasOwnProperty(i)) {
|
||||
name = classes[i];
|
||||
class2type["[object " + name + "]"] = name.toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
function typeOf(obj) {
|
||||
return (null === obj || undefined === obj) ? String(obj) : class2type[Object.prototype.toString.call(obj)] || "object";
|
||||
}
|
||||
|
||||
function isEmpty(o) {
|
||||
var i, v;
|
||||
if (typeOf(o) === 'object') {
|
||||
for (i in o) { // fails jslint
|
||||
v = o[i];
|
||||
if (v !== undefined && typeOf(v) !== 'function') {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!String.prototype.entityify) {
|
||||
String.prototype.entityify = function () {
|
||||
return this.replace(/&/g, "&").replace(/</g,
|
||||
"<").replace(/>/g, ">");
|
||||
};
|
||||
}
|
||||
|
||||
if (!String.prototype.quote) {
|
||||
String.prototype.quote = function () {
|
||||
var c, i, l = this.length, o = '"';
|
||||
for (i = 0; i < l; i += 1) {
|
||||
c = this.charAt(i);
|
||||
if (c >= ' ') {
|
||||
if (c === '\\' || c === '"') {
|
||||
o += '\\';
|
||||
}
|
||||
o += c;
|
||||
} else {
|
||||
switch (c) {
|
||||
case '\b':
|
||||
o += '\\b';
|
||||
break;
|
||||
case '\f':
|
||||
o += '\\f';
|
||||
break;
|
||||
case '\n':
|
||||
o += '\\n';
|
||||
break;
|
||||
case '\r':
|
||||
o += '\\r';
|
||||
break;
|
||||
case '\t':
|
||||
o += '\\t';
|
||||
break;
|
||||
default:
|
||||
c = c.charCodeAt();
|
||||
o += '\\u00' + Math.floor(c / 16).toString(16) +
|
||||
(c % 16).toString(16);
|
||||
}
|
||||
}
|
||||
}
|
||||
return o + '"';
|
||||
};
|
||||
}
|
||||
|
||||
if (!String.prototype.supplant) {
|
||||
String.prototype.supplant = function (o) {
|
||||
return this.replace(/{([^{}]*)}/g,
|
||||
function (a, b) {
|
||||
var r = o[b];
|
||||
return typeof r === 'string' || typeof r === 'number' ? r : a;
|
||||
}
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
if (!String.prototype.trim) {
|
||||
String.prototype.trim = function () {
|
||||
return this.replace(/^\s*(\S*(?:\s+\S+)*)\s*$/, "$1");
|
||||
};
|
||||
}
|
||||
|
||||
// CommonJS / npm / Ender.JS
|
||||
module.exports = {
|
||||
typeOf: typeOf,
|
||||
isEmpty: isEmpty
|
||||
};
|
||||
global.typeOf = global.typeOf || typeOf;
|
||||
global.isEmpty = global.isEmpty || isEmpty;
|
||||
}());
|
||||
|
||||
},{}],3:[function(require,module,exports){
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* removeTrailingSpaces
|
||||
* Remove the trailing spaces from a string.
|
||||
*
|
||||
* @name removeTrailingSpaces
|
||||
* @function
|
||||
* @param {String} input The input string.
|
||||
* @returns {String} The output string.
|
||||
*/
|
||||
|
||||
module.exports = function removeTrailingSpaces(input) {
|
||||
// TODO If possible, use a regex
|
||||
return input.split("\n").map(function (x) {
|
||||
return x.trimRight();
|
||||
}).join("\n");
|
||||
};
|
||||
},{}]},{},[1]);
|
||||
100
clover/www/js/topics.js
Normal file
@@ -0,0 +1,100 @@
|
||||
const url = 'ws://' + location.hostname + ':9090';
|
||||
const ros = new ROSLIB.Ros({ url: url });
|
||||
|
||||
ros.on('connection', function () {
|
||||
document.body.classList.add('connected');
|
||||
init();
|
||||
});
|
||||
|
||||
ros.on('close', function () {
|
||||
document.body.classList.remove('connected');
|
||||
setTimeout(function() {
|
||||
// reconnect
|
||||
ros.connect(url);
|
||||
}, 2000);
|
||||
});
|
||||
|
||||
const title = document.querySelector('h1');
|
||||
const topicsList = document.querySelector('#topics');
|
||||
const topicMessage = document.querySelector('#topic-message');
|
||||
|
||||
function viewTopicsList() {
|
||||
title.innerHTML = 'Topics:';
|
||||
|
||||
ros.getTopics(function(topics) {
|
||||
topicsList.innerHTML = topics.topics.map(function(topic, i) {
|
||||
const type = topics.types[i];
|
||||
if (type == 'sensor_msgs/Image') {
|
||||
let url = `${location.protocol}//${location.hostname}:8080/stream_viewer?topic=${topic}`;
|
||||
return `<li><a href="${url}" class=topic title=${type}>${topic}</a> 🖼</li>`;
|
||||
} else {
|
||||
return `<li><a href="?topic=${topic}" class=topic title=${type}>${topic}</a></li>`;
|
||||
}
|
||||
}).join('');
|
||||
});
|
||||
}
|
||||
|
||||
let rosdistro;
|
||||
|
||||
function viewTopic(topic) {
|
||||
title.innerHTML = 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>`;
|
||||
});
|
||||
|
||||
new ROSLIB.Topic({ ros: ros, name: topic }).subscribe(function(msg) {
|
||||
document.title = topic;
|
||||
if (mouseDown) return;
|
||||
topicMessage.innerHTML = yamlStringify(msg); // JSON.stringify(msg, null, 4);
|
||||
});
|
||||
}
|
||||
|
||||
function viewParameters() {
|
||||
title.innerHTML = 'Parameters';
|
||||
topicMessage.style.display = 'block';
|
||||
|
||||
let names = ['aruco_detect', 'main_camera', 'main_camera_nodelet_manager', 'mavros', 'optical_flow',
|
||||
'rangefinder', 'rosapi', 'rosbridge_websocket', 'rosdistro', 'roslaunch', 'rosversion',
|
||||
'roswww_static', 'run_id', 'simple_offboard', 'visualization', 'web_video_server'];
|
||||
let params = {};
|
||||
// ros.getParams(function(params) {
|
||||
|
||||
Promise.all(names.map(function(name) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
new ROSLIB.Param({ ros, name }).get(function(value) {
|
||||
params[name] = value;
|
||||
resolve();
|
||||
})
|
||||
});
|
||||
})).then(function() {
|
||||
console.log(params);
|
||||
topicMessage.innerHTML = yamlStringify(params);
|
||||
});
|
||||
}
|
||||
|
||||
let mouseDown;
|
||||
|
||||
topicMessage.addEventListener('mousedown', function() { mouseDown = true; });
|
||||
topicMessage.addEventListener('mouseup', function() { mouseDown = false; });
|
||||
|
||||
function init() {
|
||||
const params = Object.fromEntries(new URLSearchParams(window.location.search).entries());
|
||||
|
||||
viewParameters();
|
||||
return;
|
||||
|
||||
if (!params.topic) {
|
||||
viewTopicsList();
|
||||
} else {
|
||||
new ROSLIB.Param({ ros: ros, name: '/rosdistro'}).get(function(value) {
|
||||
rosdistro = value.trim();
|
||||
viewTopic(params.topic);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
window.ros = ros;
|
||||
27
clover/www/topics.html
Normal file
@@ -0,0 +1,27 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>ROS topics</title>
|
||||
<script src="js/roslib.js"></script>
|
||||
<link rel="icon" href="data:,"> <!-- make chrome don't request icon -->
|
||||
<script type="module" src="js/topics.js"></script>
|
||||
<script src="js/json-to-pretty-yaml.js"></script>
|
||||
<style>
|
||||
#topics { line-height: 1.2em; }
|
||||
#topic-view {
|
||||
display: none;
|
||||
}
|
||||
#topic-message {
|
||||
display: none;
|
||||
white-space: pre;
|
||||
font-family: monospace;
|
||||
}
|
||||
#topic-type { font-family: monospace; font-size: 0.5em; vertical-align: super; font-weight: normal; }
|
||||
.topic { font-family: monospace; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1> </h1>
|
||||
<ul id="topics"></ul>
|
||||
<code id="topic-message" title="Hold mouse button to pause">No messages received</code>
|
||||
</body>
|
||||
</html>
|
||||
@@ -73,6 +73,13 @@ catkin_install_python(PROGRAMS src/clover_blocks
|
||||
DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
|
||||
)
|
||||
|
||||
# TODO: store programs in home directory?
|
||||
install(DIRECTORY programs
|
||||
DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}
|
||||
)
|
||||
|
||||
install(DIRECTORY www DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION})
|
||||
|
||||
#############
|
||||
## Testing ##
|
||||
#############
|
||||
|
||||
@@ -10,7 +10,7 @@ Internal package documentation is given below.
|
||||
|
||||
## Frontend
|
||||
|
||||
The frontend files are located in [`www`](./www/) subdirectory. The frontend application uses [`roblib.js`](http://wiki.ros.org/roslibjs) library for communicating with backend node and other ROS resources.
|
||||
The frontend files are located in [`www`](./www/) subdirectory. The frontend application uses [`roslib.js`](http://wiki.ros.org/roslibjs) library for communicating with backend node and other ROS resources.
|
||||
|
||||
## `clover_blocks` node
|
||||
|
||||
@@ -30,7 +30,8 @@ The frontend files are located in [`www`](./www/) subdirectory. The frontend app
|
||||
Parameters read by frontend:
|
||||
|
||||
* `~navigate_tolerance` (*float*) – distance tolerance in meters, used for navigate-like blocks (default: 0.2).
|
||||
* `~yaw_tolerance` (*float*) – yaw angle tolerance in degrees, used in set_yaw block (default: 20).
|
||||
* `~navigate_global_tolerance` (*float*) – distance tolerance for global coordinates navigation (default: 1).
|
||||
* `~yaw_tolerance` (*float*) – yaw angle tolerance in degrees, used in set_yaw block (default: 1).
|
||||
* `~sleep_time` (*float*) – duration of sleep in loop cycles, used for navigate-like blocks (default: 0.2).
|
||||
* `~confirm_run` (*bool*) – enable confirmation to run the program (default: true).
|
||||
|
||||
|
||||
@@ -146,6 +146,7 @@ def stop(req):
|
||||
return {'success': True}
|
||||
|
||||
|
||||
# TODO: find dir in installed package
|
||||
programs_path = rospy.get_param('~programs_dir', os.path.dirname(os.path.abspath(__file__)) + '/../programs')
|
||||
|
||||
|
||||
|
||||
@@ -31,6 +31,14 @@ function considerFrameId(e) {
|
||||
this.getInput('Y').fieldRow[0].setValue('y');
|
||||
this.getInput('Z').fieldRow[0].setValue('z');
|
||||
}
|
||||
if (this.getInput('LAT')) { // block has global coordinates
|
||||
let global = frameId.startsWith('GLOBAL');
|
||||
this.getInput('LAT').setVisible(global);
|
||||
this.getInput('LON').setVisible(global);
|
||||
this.getInput('X').setVisible(!global);
|
||||
this.getInput('Y').setVisible(!global);
|
||||
this.getInput('Z').fieldRow[0].setValue(frameId == 'GLOBAL' ? 'altitude' : 'z');
|
||||
}
|
||||
}
|
||||
if (this.getInput('ID')) { // block has marker id field
|
||||
this.getInput('ID').setVisible(frameId == 'ARUCO'); // toggle id field
|
||||
@@ -65,6 +73,9 @@ function updateSetpointBlock(e) {
|
||||
|
||||
Blockly.Blocks['navigate'] = {
|
||||
init: function () {
|
||||
let navFrameId = frameIds.slice();
|
||||
navFrameId.push(['global', 'GLOBAL_LOCAL'])
|
||||
navFrameId.push(['global, WGS 84 alt.', 'GLOBAL'])
|
||||
this.appendDummyInput()
|
||||
.appendField("navigate to point");
|
||||
this.appendValueInput("X")
|
||||
@@ -73,12 +84,20 @@ Blockly.Blocks['navigate'] = {
|
||||
this.appendValueInput("Y")
|
||||
.setCheck("Number")
|
||||
.appendField("left");
|
||||
this.appendValueInput("LAT")
|
||||
.setCheck("Number")
|
||||
.appendField("latitude")
|
||||
.setVisible(false);
|
||||
this.appendValueInput("LON")
|
||||
.setCheck("Number")
|
||||
.appendField("longitude")
|
||||
.setVisible(false)
|
||||
this.appendValueInput("Z")
|
||||
.setCheck("Number")
|
||||
.appendField("up");
|
||||
this.appendDummyInput()
|
||||
.appendField("relative to")
|
||||
.appendField(new Blockly.FieldDropdown(frameIds), "FRAME_ID");
|
||||
.appendField(new Blockly.FieldDropdown(navFrameId), "FRAME_ID");
|
||||
this.appendValueInput("ID")
|
||||
.setCheck("Number")
|
||||
.appendField("with ID")
|
||||
@@ -268,7 +287,7 @@ Blockly.Blocks['mode'] = {
|
||||
.appendField("current flight mode");
|
||||
this.setOutput(true, "String");
|
||||
this.setColour(COLOR_STATE);
|
||||
this.setTooltip("");
|
||||
this.setTooltip("Returns current flight mode (POSCTL, OFFBOARD, etc).");
|
||||
this.setHelpUrl(DOCS_URL + '#' + this.type);
|
||||
}
|
||||
};
|
||||
@@ -375,7 +394,7 @@ Blockly.Blocks['take_off'] = {
|
||||
this.setPreviousStatement(true, null);
|
||||
this.setNextStatement(true, null);
|
||||
this.setColour(COLOR_FLIGHT);
|
||||
this.setTooltip("Take off on desired altitude in meters");
|
||||
this.setTooltip("Take off on desired altitude in meters.");
|
||||
this.setHelpUrl(DOCS_URL + '#' + this.type);
|
||||
}
|
||||
};
|
||||
@@ -391,7 +410,7 @@ Blockly.Blocks['land'] = {
|
||||
this.setPreviousStatement(true, null);
|
||||
this.setNextStatement(true, null);
|
||||
this.setColour(COLOR_FLIGHT);
|
||||
this.setTooltip("");
|
||||
this.setTooltip("Land the drone.");
|
||||
this.setHelpUrl(DOCS_URL + '#' + this.type);
|
||||
}
|
||||
};
|
||||
@@ -400,10 +419,10 @@ Blockly.Blocks['global_position'] = {
|
||||
init: function () {
|
||||
this.appendDummyInput()
|
||||
.appendField("current")
|
||||
.appendField(new Blockly.FieldDropdown([["latitude", "LATITUDE"], ["longitude", "LONGITUDE"], ["altitude", "ALTITUDE"]]), "NAME");
|
||||
.appendField(new Blockly.FieldDropdown([["latitude", "LAT"], ["longitude", "LON"], ["altitude", "ALT"]]), "FIELD");
|
||||
this.setOutput(true, "Number");
|
||||
this.setColour(230);
|
||||
this.setTooltip("");
|
||||
this.setColour(COLOR_STATE);
|
||||
this.setTooltip("Returns current global position (latitude, longitude, altitude above the WGS 84 ellipsoid).");
|
||||
this.setHelpUrl(DOCS_URL + '#' + this.type);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -52,6 +52,8 @@
|
||||
<value name="X"><shadow type="math_number"><field name="NUM">0</field></shadow></value>
|
||||
<value name="Y"><shadow type="math_number"><field name="NUM">0</field></shadow></value>
|
||||
<value name="Z"><shadow type="math_number"><field name="NUM">0</field></shadow></value>
|
||||
<value name="LAT"><shadow type="math_number"><field name="NUM">47.397503</field></shadow></value>
|
||||
<value name="LON"><shadow type="math_number"><field name="NUM">8.544945</field></shadow></value>
|
||||
<value name="SPEED"><shadow type="math_number"><field name="NUM">0.5</field></shadow></value>
|
||||
<value name="ID"><shadow type="math_number"><field name="NUM">0</field></shadow></value>
|
||||
</block>
|
||||
@@ -85,6 +87,7 @@
|
||||
<value name="ID"><shadow type="math_number"><field name="NUM">0</field></shadow></value>
|
||||
</block>
|
||||
<block type="get_attitude"></block>
|
||||
<block type="global_position"></block>
|
||||
<block type="distance">
|
||||
<value name="X"><shadow type="math_number"><field name="NUM">0</field></shadow></value>
|
||||
<value name="Y"><shadow type="math_number"><field name="NUM">0</field></shadow></value>
|
||||
|
||||
@@ -39,7 +39,8 @@ var workspace = Blockly.inject('blockly', {
|
||||
function readParams() {
|
||||
return Promise.all([
|
||||
ros.readParam('navigate_tolerance', true, 0.2),
|
||||
ros.readParam('yaw_tolerance', true, 20),
|
||||
ros.readParam('navigate_global_tolerance', true, 1),
|
||||
ros.readParam('yaw_tolerance', true, 1),
|
||||
ros.readParam('sleep_time', true, 0.2),
|
||||
ros.readParam('confirm_run', true, true),
|
||||
]);
|
||||
@@ -210,7 +211,7 @@ function loadPrograms() {
|
||||
updateChanged();
|
||||
}, function(err) {
|
||||
document.querySelector('.backend-fail').style.display = 'inline';
|
||||
alert(`Error loading programs list.\n\nHave you enabled clover_blocks in clover.launch?`);
|
||||
alert(`Error loading programs list.\n\nHave you enabled 'blocks' in clover.launch?`);
|
||||
runButton.disabled = true;
|
||||
})
|
||||
}
|
||||
|
||||
@@ -33,6 +33,18 @@ const NAVIGATE_WAIT = () => `\ndef navigate_wait(x=0, y=0, z=0, speed=0.5, frame
|
||||
return
|
||||
rospy.sleep(${params.sleep_time})\n`;
|
||||
|
||||
const NAVIGATE_GLOBAL_WAIT = () => `\ndef navigate_global_wait(lat, lon, z, speed=0.5):
|
||||
res = navigate_global(lat=lat, lon=lon, z=z, yaw=float('inf'), speed=speed)
|
||||
|
||||
if not res.success:
|
||||
raise Exception(res.message)
|
||||
|
||||
while not rospy.is_shutdown():
|
||||
telem = get_telemetry(frame_id='navigate_target')
|
||||
if math.sqrt(telem.x ** 2 + telem.y ** 2 + telem.z ** 2) < ${params.navigate_global_tolerance}:
|
||||
return
|
||||
rospy.sleep(${params.sleep_time})\n`;
|
||||
|
||||
const LAND_WAIT = () => `\ndef land_wait():
|
||||
land()
|
||||
while get_telemetry().armed:
|
||||
@@ -68,6 +80,9 @@ function generateROSDefinitions() {
|
||||
if (rosDefinitions.offboard) {
|
||||
code += `get_telemetry = rospy.ServiceProxy('get_telemetry', srv.GetTelemetry)\n`;
|
||||
code += `navigate = rospy.ServiceProxy('navigate', srv.Navigate)\n`;
|
||||
if (rosDefinitions.navigateGlobal) {
|
||||
code += `navigate_global = rospy.ServiceProxy('navigate_global', srv.NavigateGlobal)\n`;
|
||||
}
|
||||
if (rosDefinitions.setVelocity) {
|
||||
code += `set_velocity = rospy.ServiceProxy('set_velocity', srv.SetVelocity)\n`;
|
||||
}
|
||||
@@ -94,6 +109,10 @@ function generateROSDefinitions() {
|
||||
Blockly.Python.definitions_['import_math'] = 'import math';
|
||||
code += NAVIGATE_WAIT();
|
||||
}
|
||||
if (rosDefinitions.navigateGlobalWait) {
|
||||
Blockly.Python.definitions_['import_math'] = 'import math';
|
||||
code += NAVIGATE_GLOBAL_WAIT();
|
||||
}
|
||||
if (rosDefinitions.landWait) {
|
||||
code += LAND_WAIT();
|
||||
}
|
||||
@@ -161,24 +180,48 @@ Blockly.Python.navigate = function(block) {
|
||||
let x = Blockly.Python.valueToCode(block, 'X', Blockly.Python.ORDER_NONE);
|
||||
let y = Blockly.Python.valueToCode(block, 'Y', Blockly.Python.ORDER_NONE);
|
||||
let z = Blockly.Python.valueToCode(block, 'Z', Blockly.Python.ORDER_NONE);
|
||||
let frameId = buildFrameId(block);
|
||||
let lat = Blockly.Python.valueToCode(block, 'LAT', Blockly.Python.ORDER_NONE);
|
||||
let lon = Blockly.Python.valueToCode(block, 'LON', Blockly.Python.ORDER_NONE);
|
||||
let wait = block.getFieldValue('WAIT') == 'TRUE';
|
||||
let frameId = block.getFieldValue('FRAME_ID');
|
||||
let speed = Blockly.Python.valueToCode(block, 'SPEED', Blockly.Python.ORDER_NONE);
|
||||
|
||||
let params = [`x=${x}`, `y=${y}`, `z=${z}`, `frame_id=${frameId}`, `speed=${speed}`];
|
||||
|
||||
simpleOffboard();
|
||||
|
||||
if (block.getFieldValue('WAIT') == 'TRUE') {
|
||||
rosDefinitions.navigateWait = true;
|
||||
// global coordinates
|
||||
if (frameId.startsWith('GLOBAL')) {
|
||||
rosDefinitions.navigateGlobal = true;
|
||||
simpleOffboard();
|
||||
|
||||
return `navigate_wait(${params.join(', ')})\n`;
|
||||
if (frameId == 'GLOBAL') {
|
||||
z = `${z} + get_telemetry().alt - get_telemetry().z`;
|
||||
}
|
||||
|
||||
if (wait) {
|
||||
rosDefinitions.navigateGlobalWait = true;
|
||||
simpleOffboard();
|
||||
return `navigate_global_wait(lat=${lat}, lon=${lon}, z=${z}, speed=${speed})\n`;
|
||||
|
||||
} else {
|
||||
return `navigate_global(lat=${lat}, lon=${lon}, z=${z}, yaw=float('inf'), speed=${speed})\n`;
|
||||
}
|
||||
|
||||
} else {
|
||||
if (frameId != 'body') {
|
||||
params.push(`yaw=float('nan')`);
|
||||
frameId = buildFrameId(block);
|
||||
let params = [`x=${x}`, `y=${y}`, `z=${z}`, `frame_id=${frameId}`, `speed=${speed}`];
|
||||
|
||||
if (wait) {
|
||||
rosDefinitions.navigateWait = true;
|
||||
simpleOffboard();
|
||||
|
||||
return `navigate_wait(${params.join(', ')})\n`;
|
||||
|
||||
} else {
|
||||
if (frameId != 'body') {
|
||||
params.push(`yaw=float('nan')`);
|
||||
}
|
||||
return `navigate(${params.join(', ')})\n`;
|
||||
}
|
||||
return `navigate(${params.join(', ')})\n`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -315,6 +358,12 @@ Blockly.Python.get_attitude = function(block) {
|
||||
return [code, Blockly.Python.ORDER_FUNCTION_CALL];
|
||||
}
|
||||
|
||||
Blockly.Python.global_position = function(block) {
|
||||
simpleOffboard();
|
||||
var code = `get_telemetry().${block.getFieldValue('FIELD').toLowerCase()}`;
|
||||
return [code, Blockly.Python.ORDER_FUNCTION_CALL];
|
||||
}
|
||||
|
||||
Blockly.Python.distance = function(block) {
|
||||
rosDefinitions.distance = true;
|
||||
simpleOffboard();
|
||||
|
||||
@@ -14,7 +14,7 @@ You may provide additional parameters for `spawn_drone.launch` as well:
|
||||
* `rangefinder` (*boolean*, default: *true*) - controls whether the drone will have a downward-facing laser rangefinder attached;
|
||||
* `led` (*boolean*, default: *true*) - controls whether the drone will have a programmable LED strip (requires plugins from `clover_simulation`);
|
||||
* `gps` (*boolean*, default: *true*) - controls whether the drone will have a simulated GPS attached (requires plugins from `sitl_gazebo`);
|
||||
* `maintain_camera_rate` (*boolean*, default: *false*) - slow down the simultion to maintain camera publishing rate (internally changes the camera plugin from `libgazebo_ros_camera.so` to `libthrottling_camera.so` from [`clover_simulation`](../clover_simulation/README.md#throttling-camera-plugin-throttling_camera)).
|
||||
* `maintain_camera_rate` (*boolean*, default: *false*) - slow down the simulation to maintain camera publishing rate (internally changes the camera plugin from `libgazebo_ros_camera.so` to `libthrottling_camera.so` from [`clover_simulation`](../clover_simulation/README.md#throttling-camera-plugin-throttling_camera)).
|
||||
|
||||
For example, in order to spawn a drone without a GPS module, you may use the following command:
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<launch>
|
||||
<arg name="model" default="$(find clover_description)/urdf/drones/clover4.xacro"/>
|
||||
<arg name="model" default="$(find clover_description)/urdf/clover/clover4.xacro"/>
|
||||
|
||||
<param name="robot_description" command="$(find xacro)/xacro.py $(arg model)"/>
|
||||
<param name="robot_description" command="xacro $(arg model)"/>
|
||||
|
||||
<node name="rviz" pkg="rviz" type="rviz" required="true"/>
|
||||
</launch>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0"?>
|
||||
<robot name="clover_camera" xmlns:xacro="http://ros.org/wiki/xacro">
|
||||
<robot name="clover" xmlns:xacro="http://ros.org/wiki/xacro">
|
||||
|
||||
<xacro:arg name="main_camera" default="true"/>
|
||||
<xacro:arg name="rangefinder" default="true"/>
|
||||
@@ -8,10 +8,10 @@
|
||||
<xacro:arg name="maintain_camera_rate" default="false"/>
|
||||
<xacro:arg name="use_clover_physics" default="false"/>
|
||||
|
||||
<xacro:include filename="$(find clover_description)/urdf/clover/clover4_base.xacro" />
|
||||
<xacro:include filename="$(find clover_description)/urdf/sensors/rpi_cam.urdf.xacro"/>
|
||||
<xacro:include filename="$(find clover_description)/urdf/sensors/distance_sensor.urdf.xacro"/>
|
||||
<xacro:include filename="$(find clover_description)/urdf/leds/led_strip.xacro"/>
|
||||
<xacro:include filename="clover4_base.xacro" />
|
||||
<xacro:include filename="../sensors/rpi_cam.urdf.xacro"/>
|
||||
<xacro:include filename="../sensors/distance_sensor.urdf.xacro"/>
|
||||
<xacro:include filename="../leds/led_strip.xacro"/>
|
||||
|
||||
<!-- Create camera plugin -->
|
||||
<xacro:if value="$(arg main_camera)">
|
||||
@@ -36,11 +36,17 @@
|
||||
</xacro:if>
|
||||
|
||||
<xacro:if value="$(arg gps)">
|
||||
<!-- Instantiate gps plugin. -->
|
||||
<xacro:gps_plugin_macro
|
||||
namespace="${namespace}"
|
||||
gps_noise="true"
|
||||
/>
|
||||
<gazebo>
|
||||
<include>
|
||||
<uri>model://gps</uri>
|
||||
<pose>0.1 0 0 0 0 0</pose>
|
||||
<name>gps0</name>
|
||||
</include>
|
||||
<joint name='gps0_joint' type='fixed'>
|
||||
<child>gps0::link</child>
|
||||
<parent>base_link</parent>
|
||||
</joint>
|
||||
</gazebo>
|
||||
</xacro:if>
|
||||
|
||||
</robot>
|
||||
|
||||
@@ -1,40 +1,15 @@
|
||||
<?xml version="1.0"?>
|
||||
|
||||
<robot name="clover" xmlns:xacro="http://ros.org/wiki/xacro">
|
||||
<!-- Properties that can be assigned at build time as arguments.
|
||||
Is there a reason not to make all properties arguments?
|
||||
-->
|
||||
<xacro:arg name='name' default='iris' />
|
||||
<xacro:arg name='mavlink_addr' default='INADDR_ANY' />
|
||||
<xacro:arg name='mavlink_udp_port' default='14560' />
|
||||
<xacro:arg name='mavlink_tcp_port' default='4560' />
|
||||
<xacro:arg name='serial_enabled' default='false' />
|
||||
<xacro:arg name='serial_device' default='/dev/ttyACM0' />
|
||||
<xacro:arg name='baudrate' default='921600' />
|
||||
<xacro:arg name='qgc_addr' default='INADDR_ANY' />
|
||||
<xacro:arg name='qgc_udp_port' default='14550' />
|
||||
<xacro:arg name='sdk_addr' default='INADDR_ANY' />
|
||||
<xacro:arg name='sdk_udp_port' default='14540' />
|
||||
<xacro:arg name='hil_mode' default='false' />
|
||||
<xacro:arg name='hil_state_level' default='false' />
|
||||
<xacro:arg name='send_vision_estimation' default='false' />
|
||||
<xacro:arg name='send_odometry' default='false' />
|
||||
<xacro:arg name='enable_lockstep' default='true' />
|
||||
<xacro:arg name='use_tcp' default='true' />
|
||||
<xacro:arg name='vehicle_is_tailsitter' default='false' />
|
||||
<xacro:arg name='visual_material' default='DarkGrey' />
|
||||
<xacro:arg name='enable_mavlink_interface' default='true' />
|
||||
<xacro:arg name='enable_wind' default='false' />
|
||||
<!-- The following causes segfault with multiple vehicles if defaults to true!!! -->
|
||||
<xacro:arg name='enable_ground_truth' default='false' />
|
||||
<xacro:arg name='enable_logging' default='false' />
|
||||
<xacro:arg name='log_file' default='iris' />
|
||||
<xacro:arg name='log_file' default='clover' />
|
||||
|
||||
<!-- macros for gazebo plugins, sensors -->
|
||||
<xacro:include filename="$(find clover_description)/urdf/component_snippets.urdf.xacro" />
|
||||
<xacro:include filename="../component_snippets.urdf.xacro" />
|
||||
|
||||
<!-- Instantiate iris "mechanics" -->
|
||||
<xacro:include filename="$(find clover_description)/urdf/clover/clover4_physics.xacro" />
|
||||
<xacro:include filename="clover4_physics.xacro" />
|
||||
|
||||
<xacro:if value="$(arg enable_wind)">
|
||||
<xacro:wind_plugin_macro
|
||||
@@ -49,126 +24,8 @@
|
||||
/>
|
||||
</xacro:if>
|
||||
|
||||
<!-- Instantiate magnetometer plugin. -->
|
||||
<xacro:magnetometer_plugin_macro
|
||||
namespace="${namespace}"
|
||||
pub_rate="20"
|
||||
noise_density="0.0004"
|
||||
random_walk="0.0000064"
|
||||
bias_correlation_time="600"
|
||||
mag_topic="/mag"
|
||||
>
|
||||
</xacro:magnetometer_plugin_macro>
|
||||
|
||||
<!-- Instantiate barometer plugin. -->
|
||||
<xacro:barometer_plugin_macro
|
||||
namespace="${namespace}"
|
||||
pub_rate="10"
|
||||
baro_topic="/baro"
|
||||
>
|
||||
</xacro:barometer_plugin_macro>
|
||||
|
||||
<xacro:if value="$(arg enable_mavlink_interface)">
|
||||
<!-- Instantiate mavlink telemetry interface. -->
|
||||
<xacro:mavlink_interface_macro
|
||||
namespace="${namespace}"
|
||||
imu_sub_topic="/imu"
|
||||
gps_sub_topic="/gps"
|
||||
mag_sub_topic="/mag"
|
||||
baro_sub_topic="/baro"
|
||||
mavlink_addr="$(arg mavlink_addr)"
|
||||
mavlink_udp_port="$(arg mavlink_udp_port)"
|
||||
mavlink_tcp_port="$(arg mavlink_tcp_port)"
|
||||
serial_enabled="$(arg serial_enabled)"
|
||||
serial_device="$(arg serial_device)"
|
||||
baudrate="$(arg baudrate)"
|
||||
qgc_addr="$(arg qgc_addr)"
|
||||
qgc_udp_port="$(arg qgc_udp_port)"
|
||||
sdk_addr="$(arg sdk_addr)"
|
||||
sdk_udp_port="$(arg sdk_udp_port)"
|
||||
hil_mode="$(arg hil_mode)"
|
||||
hil_state_level="$(arg hil_state_level)"
|
||||
vehicle_is_tailsitter="$(arg vehicle_is_tailsitter)"
|
||||
send_vision_estimation="$(arg send_vision_estimation)"
|
||||
send_odometry="$(arg send_odometry)"
|
||||
enable_lockstep="$(arg enable_lockstep)"
|
||||
use_tcp="$(arg use_tcp)"
|
||||
>
|
||||
</xacro:mavlink_interface_macro>
|
||||
</xacro:if>
|
||||
|
||||
<!-- Mount an ADIS16448 IMU. -->
|
||||
<xacro:imu_plugin_macro
|
||||
namespace="${namespace}"
|
||||
imu_suffix=""
|
||||
parent_link="base_link"
|
||||
imu_topic="/imu"
|
||||
mass_imu_sensor="0.015"
|
||||
gyroscope_noise_density="0.0003394"
|
||||
gyroscopoe_random_walk="0.000038785"
|
||||
gyroscope_bias_correlation_time="1000.0"
|
||||
gyroscope_turn_on_bias_sigma="0.0087"
|
||||
accelerometer_noise_density="0.004"
|
||||
accelerometer_random_walk="0.006"
|
||||
accelerometer_bias_correlation_time="300.0"
|
||||
accelerometer_turn_on_bias_sigma="0.1960"
|
||||
>
|
||||
<inertia ixx="0.00001" ixy="0.0" ixz="0.0" iyy="0.00001" iyz="0.0" izz="0.00001" />
|
||||
<origin xyz="0 0 0" rpy="0 0 0" />
|
||||
</xacro:imu_plugin_macro>
|
||||
|
||||
<xacro:if value="$(arg enable_ground_truth)">
|
||||
<!-- Mount an IMU providing ground truth. -->
|
||||
<xacro:imu_plugin_macro
|
||||
namespace="${namespace}"
|
||||
imu_suffix="gt"
|
||||
parent_link="base_link"
|
||||
imu_topic="ground_truth/imu"
|
||||
mass_imu_sensor="0.00001"
|
||||
gyroscope_noise_density="0.0"
|
||||
gyroscopoe_random_walk="0.0"
|
||||
gyroscope_bias_correlation_time="1000.0"
|
||||
gyroscope_turn_on_bias_sigma="0.0"
|
||||
accelerometer_noise_density="0.0"
|
||||
accelerometer_random_walk="0.0"
|
||||
accelerometer_bias_correlation_time="300.0"
|
||||
accelerometer_turn_on_bias_sigma="0.0"
|
||||
>
|
||||
<inertia ixx="0.00001" ixy="0.0" ixz="0.0" iyy="0.00001" iyz="0.0" izz="0.00001" />
|
||||
<origin xyz="0 0 0" rpy="0 0 0" />
|
||||
</xacro:imu_plugin_macro>
|
||||
|
||||
<!-- Mount a generic odometry sensor providing ground truth. -->
|
||||
<xacro:odometry_plugin_macro
|
||||
namespace="${namespace}/ground_truth"
|
||||
odometry_sensor_suffix="gt"
|
||||
parent_link="base_link"
|
||||
pose_topic="pose"
|
||||
pose_with_covariance_topic="pose_with_covariance"
|
||||
position_topic="position"
|
||||
transform_topic="transform"
|
||||
odometry_topic="odometry"
|
||||
parent_frame_id="world"
|
||||
mass_odometry_sensor="0.00001"
|
||||
measurement_divisor="1"
|
||||
measurement_delay="0"
|
||||
unknown_delay="0.0"
|
||||
noise_normal_position="0 0 0"
|
||||
noise_normal_quaternion="0 0 0"
|
||||
noise_normal_linear_velocity="0 0 0"
|
||||
noise_normal_angular_velocity="0 0 0"
|
||||
noise_uniform_position="0 0 0"
|
||||
noise_uniform_quaternion="0 0 0"
|
||||
noise_uniform_linear_velocity="0 0 0"
|
||||
noise_uniform_angular_velocity="0 0 0"
|
||||
enable_odometry_map="false"
|
||||
odometry_map=""
|
||||
image_scale=""
|
||||
>
|
||||
<inertia ixx="0.00001" ixy="0.0" ixz="0.0" iyy="0.00001" iyz="0.0" izz="0.00001" /> <!-- [kg.m^2] [kg.m^2] [kg.m^2] [kg.m^2] [kg.m^2] [kg.m^2] -->
|
||||
<origin xyz="0.0 0.0 0.0" rpy="0.0 0.0 0.0" />
|
||||
</xacro:odometry_plugin_macro>
|
||||
</xacro:if>
|
||||
<!-- Gazebo plugins -->
|
||||
<xacro:include filename="$(find clover_description)/urdf/clover/clover4_gazebo.xacro" />
|
||||
|
||||
<xacro:if value="$(arg enable_logging)">
|
||||
<!-- Instantiate a logger -->
|
||||
|
||||
183
clover_description/urdf/clover/clover4_gazebo.xacro
Normal file
@@ -0,0 +1,183 @@
|
||||
<?xml version="1.0"?>
|
||||
|
||||
<!-- Reference: https://github.com/PX4/PX4-SITL_gazebo/blob/7505aee97d2d3112fb2bd95198946345c6fa0b07/models/iris/iris.sdf.jinja#L430 -->
|
||||
|
||||
<robot xmlns:xacro="http://ros.org/wiki/xacro">
|
||||
|
||||
<!-- IMU link -->
|
||||
<link name="/imu_link">
|
||||
<inertial>
|
||||
<inertia ixx="0.00001" ixy="0.0" ixz="0.0" iyy="0.00001" iyz="0.0" izz="0.00001"/>
|
||||
<mass value="0.015"/>
|
||||
<!-- [kg] -->
|
||||
<origin rpy="0 0 0" xyz="0 0 0"/>
|
||||
</inertial>
|
||||
</link>
|
||||
|
||||
<!-- IMU joint -->
|
||||
<joint name="/imu_joint" type="fixed">
|
||||
<origin rpy="0 0 0" xyz="0 0 0"/>
|
||||
<parent link="base_link"/>
|
||||
<child link="/imu_link"/>
|
||||
<!-- <limit upper="0" lower="0" effort="0" velocity="0" /> -->
|
||||
</joint>
|
||||
|
||||
<gazebo reference="/imu_joint">
|
||||
<disableFixedJointLumping>true</disableFixedJointLumping>
|
||||
<preserveFixedJoint>true</preserveFixedJoint>
|
||||
</gazebo>
|
||||
|
||||
<gazebo>
|
||||
<plugin name='groundtruth_plugin' filename='libgazebo_groundtruth_plugin.so'>
|
||||
<robotNamespace/>
|
||||
</plugin>
|
||||
<plugin name='magnetometer_plugin' filename='libgazebo_magnetometer_plugin.so'>
|
||||
<robotNamespace/>
|
||||
<pubRate>100</pubRate>
|
||||
<noiseDensity>0.0004</noiseDensity>
|
||||
<randomWalk>6.4e-06</randomWalk>
|
||||
<biasCorrelationTime>600</biasCorrelationTime>
|
||||
<magTopic>/mag</magTopic>
|
||||
</plugin>
|
||||
<plugin name='barometer_plugin' filename='libgazebo_barometer_plugin.so'>
|
||||
<robotNamespace/>
|
||||
<pubRate>50</pubRate>
|
||||
<baroTopic>/baro</baroTopic>
|
||||
<baroDriftPaPerSec>0</baroDriftPaPerSec>
|
||||
</plugin>
|
||||
<plugin name='mavlink_interface' filename='libgazebo_mavlink_interface.so'>
|
||||
<robotNamespace/>
|
||||
<imuSubTopic>/imu</imuSubTopic>
|
||||
<magSubTopic>/mag</magSubTopic>
|
||||
<baroSubTopic>/baro</baroSubTopic>
|
||||
<mavlink_addr>INADDR_ANY</mavlink_addr>
|
||||
<mavlink_tcp_port>4560</mavlink_tcp_port>
|
||||
<mavlink_udp_port>14560</mavlink_udp_port>
|
||||
<serialEnabled>false</serialEnabled>
|
||||
<serialDevice>/dev/ttyACM0</serialDevice>
|
||||
<baudRate>921600</baudRate>
|
||||
<qgc_addr>INADDR_ANY</qgc_addr>
|
||||
<qgc_udp_port>14550</qgc_udp_port>
|
||||
<sdk_addr>INADDR_ANY</sdk_addr>
|
||||
<sdk_udp_port>14540</sdk_udp_port>
|
||||
<hil_mode>false</hil_mode>
|
||||
<hil_state_level>0</hil_state_level>
|
||||
<send_vision_estimation>0</send_vision_estimation>
|
||||
<send_odometry>1</send_odometry>
|
||||
<enable_lockstep>1</enable_lockstep>
|
||||
<use_tcp>1</use_tcp>
|
||||
<motorSpeedCommandPubTopic>/gazebo/command/motor_speed</motorSpeedCommandPubTopic>
|
||||
<control_channels>
|
||||
<channel name='rotor1'>
|
||||
<input_index>0</input_index>
|
||||
<input_offset>0</input_offset>
|
||||
<input_scaling>1000</input_scaling>
|
||||
<zero_position_disarmed>0</zero_position_disarmed>
|
||||
<zero_position_armed>100</zero_position_armed>
|
||||
<joint_control_type>velocity</joint_control_type>
|
||||
</channel>
|
||||
<channel name='rotor2'>
|
||||
<input_index>1</input_index>
|
||||
<input_offset>0</input_offset>
|
||||
<input_scaling>1000</input_scaling>
|
||||
<zero_position_disarmed>0</zero_position_disarmed>
|
||||
<zero_position_armed>100</zero_position_armed>
|
||||
<joint_control_type>velocity</joint_control_type>
|
||||
</channel>
|
||||
<channel name='rotor3'>
|
||||
<input_index>2</input_index>
|
||||
<input_offset>0</input_offset>
|
||||
<input_scaling>1000</input_scaling>
|
||||
<zero_position_disarmed>0</zero_position_disarmed>
|
||||
<zero_position_armed>100</zero_position_armed>
|
||||
<joint_control_type>velocity</joint_control_type>
|
||||
</channel>
|
||||
<channel name='rotor4'>
|
||||
<input_index>3</input_index>
|
||||
<input_offset>0</input_offset>
|
||||
<input_scaling>1000</input_scaling>
|
||||
<zero_position_disarmed>0</zero_position_disarmed>
|
||||
<zero_position_armed>100</zero_position_armed>
|
||||
<joint_control_type>velocity</joint_control_type>
|
||||
</channel>
|
||||
<channel name='rotor5'>
|
||||
<input_index>4</input_index>
|
||||
<input_offset>1</input_offset>
|
||||
<input_scaling>324.6</input_scaling>
|
||||
<zero_position_disarmed>0</zero_position_disarmed>
|
||||
<zero_position_armed>0</zero_position_armed>
|
||||
<joint_control_type>velocity</joint_control_type>
|
||||
<joint_control_pid>
|
||||
<p>0.1</p>
|
||||
<i>0</i>
|
||||
<d>0</d>
|
||||
<iMax>0.0</iMax>
|
||||
<iMin>0.0</iMin>
|
||||
<cmdMax>2</cmdMax>
|
||||
<cmdMin>-2</cmdMin>
|
||||
</joint_control_pid>
|
||||
<joint_name>zephyr_delta_wing::propeller_joint</joint_name>
|
||||
</channel>
|
||||
<channel name='rotor6'>
|
||||
<input_index>5</input_index>
|
||||
<input_offset>0</input_offset>
|
||||
<input_scaling>0.524</input_scaling>
|
||||
<zero_position_disarmed>0</zero_position_disarmed>
|
||||
<zero_position_armed>0</zero_position_armed>
|
||||
<joint_control_type>position</joint_control_type>
|
||||
<joint_name>zephyr_delta_wing::flap_left_joint</joint_name>
|
||||
<joint_control_pid>
|
||||
<p>10.0</p>
|
||||
<i>0</i>
|
||||
<d>0</d>
|
||||
<iMax>0</iMax>
|
||||
<iMin>0</iMin>
|
||||
<cmdMax>20</cmdMax>
|
||||
<cmdMin>-20</cmdMin>
|
||||
</joint_control_pid>
|
||||
</channel>
|
||||
<channel name='rotor7'>
|
||||
<input_index>6</input_index>
|
||||
<input_offset>0</input_offset>
|
||||
<input_scaling>0.524</input_scaling>
|
||||
<zero_position_disarmed>0</zero_position_disarmed>
|
||||
<zero_position_armed>0</zero_position_armed>
|
||||
<joint_control_type>position</joint_control_type>
|
||||
<joint_name>zephyr_delta_wing::flap_right_joint</joint_name>
|
||||
<joint_control_pid>
|
||||
<p>10.0</p>
|
||||
<i>0</i>
|
||||
<d>0</d>
|
||||
<iMax>0</iMax>
|
||||
<iMin>0</iMin>
|
||||
<cmdMax>20</cmdMax>
|
||||
<cmdMin>-20</cmdMin>
|
||||
</joint_control_pid>
|
||||
</channel>
|
||||
<channel name='rotor8'>
|
||||
<input_index>7</input_index>
|
||||
<input_offset>0</input_offset>
|
||||
<input_scaling>0.524</input_scaling>
|
||||
<zero_position_disarmed>0</zero_position_disarmed>
|
||||
<zero_position_armed>0</zero_position_armed>
|
||||
<joint_control_type>position</joint_control_type>
|
||||
</channel>
|
||||
</control_channels>
|
||||
</plugin>
|
||||
<static>0</static>
|
||||
<plugin name='rotors_gazebo_imu_plugin' filename='libgazebo_imu_plugin.so'>
|
||||
<robotNamespace/>
|
||||
<linkName>/imu_link</linkName>
|
||||
<imuTopic>/imu</imuTopic>
|
||||
<gyroscopeNoiseDensity>0.00018665</gyroscopeNoiseDensity>
|
||||
<gyroscopeRandomWalk>3.8785e-05</gyroscopeRandomWalk>
|
||||
<gyroscopeBiasCorrelationTime>1000.0</gyroscopeBiasCorrelationTime>
|
||||
<gyroscopeTurnOnBiasSigma>0.0087</gyroscopeTurnOnBiasSigma>
|
||||
<accelerometerNoiseDensity>0.00186</accelerometerNoiseDensity>
|
||||
<accelerometerRandomWalk>0.006</accelerometerRandomWalk>
|
||||
<accelerometerBiasCorrelationTime>300.0</accelerometerBiasCorrelationTime>
|
||||
<accelerometerTurnOnBiasSigma>0.196</accelerometerTurnOnBiasSigma>
|
||||
</plugin>
|
||||
</gazebo>
|
||||
|
||||
</robot>
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
-->
|
||||
|
||||
<robot name="iris" xmlns:xacro="http://ros.org/wiki/xacro">
|
||||
<robot name="clover" xmlns:xacro="http://ros.org/wiki/xacro">
|
||||
<!-- Properties -->
|
||||
<xacro:property name="namespace" value="" />
|
||||
<xacro:property name="rotor_velocity_slowdown_sim" value="10" />
|
||||
@@ -84,7 +84,7 @@
|
||||
</xacro:unless>
|
||||
|
||||
<!-- Included URDF Files -->
|
||||
<xacro:include filename="$(find clover_description)/urdf/clover/clover4_macros.xacro" />
|
||||
<xacro:include filename="clover4_macros.xacro" />
|
||||
|
||||
<!-- Instantiate multirotor_base_macro once -->
|
||||
<xacro:clover4_base_macro
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<robot name="rpi_camera" xmlns:xacro="http://ros.org/wiki/xacro">
|
||||
<xacro:include filename="$(find clover_description)/urdf/camera_sensor.urdf.xacro"/>
|
||||
<xacro:include filename="../camera_sensor.urdf.xacro"/>
|
||||
|
||||
<xacro:macro name="distance_sensor" params="name:=lidar_vl53l1x parent x:=0 y:=0 z:=0 roll:=0 pitch:=0 yaw:=0 range_min:=0.01 range_max:=4.0 resolution:=0.01 rate:=10">
|
||||
<joint name="${name}_joint" type="fixed">
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<robot name="pinhole_camera" xmlns:xacro="http://ros.org/wiki/xacro">
|
||||
<xacro:include filename="$(find clover_description)/urdf/camera_sensor.urdf.xacro"/>
|
||||
<xacro:include filename="../camera_sensor.urdf.xacro"/>
|
||||
<xacro:camera_sensor name="main_camera" width="640" height="480" rate="30" horizontal_fov="${120.0*pi/180.0}"/>
|
||||
</robot>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<robot name="rpi_camera" xmlns:xacro="http://ros.org/wiki/xacro">
|
||||
<xacro:include filename="$(find clover_description)/urdf/camera_sensor.urdf.xacro"/>
|
||||
<xacro:include filename="../camera_sensor.urdf.xacro"/>
|
||||
|
||||
<xacro:macro name="rpi_cam" params="name:=rpi_cam parent x:=0 y:=0 z:=0 roll:=0 pitch:=0 yaw:=0 width:=320 height:=240 rate:=40 do_throttling:=false">
|
||||
<joint name="${name}_joint" type="fixed">
|
||||
|
||||
@@ -55,7 +55,7 @@ A visual Gazebo plugin is used for the LED strip. An example of the plugin usage
|
||||
The plugin accepts the following parameters during instantiation:
|
||||
|
||||
* `robotNamespace` (*string*, default: "") - a ROS namespace for the plugin;
|
||||
* `ledCount` (*integer*, required) - total numer of LEDs in a strip.
|
||||
* `ledCount` (*integer*, required) - total number of LEDs in a strip.
|
||||
|
||||
The plugin will provide the following service:
|
||||
|
||||
|
||||
36
clover_simulation/airframes/4500_clover
Normal file
@@ -0,0 +1,36 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# @name COEX Clover Simulator
|
||||
#
|
||||
# @type Quadrotor X
|
||||
#
|
||||
|
||||
. ${R}etc/init.d-posix/airframes/10016_iris # base on iris
|
||||
|
||||
param set ATT_W_EXT_HDG 0.5
|
||||
param set ATT_EXT_HDG_M 1
|
||||
|
||||
param set COM_DISARM_LAND 1.0
|
||||
|
||||
param set LPE_FLW_SCALE 1.0
|
||||
param set LPE_FLW_R 0.2
|
||||
param set LPE_FLW_RR 0.0
|
||||
param set LPE_FLW_QMIN 10
|
||||
param set LPE_VIS_DELAY 0.0
|
||||
param set LPE_VIS_Z 0.1
|
||||
param set LPE_FUSION 86
|
||||
|
||||
param set SENS_FLOW_ROT 0
|
||||
param set SENS_FLOW_MINHGT 0.01
|
||||
param set SENS_FLOW_MAXHGT 4.0
|
||||
param set SENS_FLOW_MAXR 10.0
|
||||
|
||||
param set EKF2_AID_MASK 27 # gps + flow + vis pos + vis yaw
|
||||
param set EKF2_OF_DELAY 0
|
||||
param set EKF2_OF_QMIN 10
|
||||
param set EKF2_OF_N_MIN 0.05
|
||||
param set EKF2_OF_N_MAX 0.2
|
||||
param set EKF2_HGT_MODE 2
|
||||
param set EKF2_EVA_NOISE 0.1
|
||||
param set EKF2_EVP_NOISE 0.1
|
||||
param set EKF2_EV_DELAY 0
|
||||
@@ -8,12 +8,13 @@
|
||||
<arg name="led" default="true"/> <!-- Simulated LED strip, ws281x-like -->
|
||||
<arg name="gps" default="false"/> <!--Simulated GPS module -->
|
||||
<arg name="use_clover_physics" default="false"/> <!-- Use inertial parameters from CAD models -->
|
||||
<arg name="gui" default="true"/> <!-- Run Gazebo with GUI -->
|
||||
|
||||
<!-- Gazebo instance -->
|
||||
<include file="$(find gazebo_ros)/launch/empty_world.launch" if="$(eval type == 'gazebo')">
|
||||
<!-- Workaround for crashes in VMware -->
|
||||
<env name="SVGA_VGPU10" value="0"/>
|
||||
<arg name="gui" value="true"/>
|
||||
<arg name="gui" value="$(arg gui)"/>
|
||||
<arg name="world_name" value="$(find clover_simulation)/resources/worlds/clover_aruco.world"/>
|
||||
<arg name="verbose" value="true"/>
|
||||
</include>
|
||||
@@ -33,7 +34,9 @@
|
||||
<arg name="use_clover_physics" value="$(arg use_clover_physics)"/>
|
||||
</include>
|
||||
|
||||
<node name="jmavsim" pkg="px4" type="jmavsim_run.sh" output="screen" if="$(eval type == 'jmavsim')"/>
|
||||
<node name="jmavsim" pkg="px4" type="jmavsim_run.sh" output="screen" if="$(eval type == 'jmavsim')">
|
||||
<env name="HEADLESS" value="1" if="$(eval not gui)"/>
|
||||
</node>
|
||||
|
||||
<param name="use_sim_time" value="false" if="$(eval type == 'jmavsim')"/>
|
||||
|
||||
@@ -43,10 +46,10 @@
|
||||
<arg name="fcu_conn" value="sitl"/>
|
||||
<arg name="fcu_ip" value="127.0.0.1"/>
|
||||
<arg name="gcs_bridge" value=""/>
|
||||
<arg name="rc" default="false"/>
|
||||
<arg name="web_video_server" default="false" if="$(eval type == 'jmavsim')"/>
|
||||
<arg name="main_camera" default="false" if="$(eval type == 'jmavsim')"/>
|
||||
<arg name="aruco" default="false" if="$(eval type == 'jmavsim')"/>
|
||||
<arg name="optical_flow" default="false" if="$(eval type == 'jmavsim')"/>
|
||||
<arg name="led" default="false" if="$(eval type == 'jmavsim')"/>
|
||||
</include>
|
||||
</launch>
|
||||
|
||||
33
clover_simulation/models/camera/camera.sdf
Normal file
@@ -0,0 +1,33 @@
|
||||
<?xml version="1.0"?>
|
||||
<sdf version="1.5">
|
||||
<model name="camera">
|
||||
<static>true</static>
|
||||
<link name='camera_link'>
|
||||
<pose>0 0 0 0 0 0</pose>
|
||||
<sensor name='camera' type='camera'>
|
||||
<camera>
|
||||
<horizontal_fov>2.0944</horizontal_fov>
|
||||
<image>
|
||||
<format>B8G8R8</format>
|
||||
<width>800</width>
|
||||
<height>600</height>
|
||||
</image>
|
||||
<clip>
|
||||
<near>0.02</near>
|
||||
<far>300</far>
|
||||
</clip>
|
||||
</camera>
|
||||
<always_on>1</always_on>
|
||||
<update_rate>20</update_rate>
|
||||
<visualize>1</visualize>
|
||||
<plugin name='camera_plugin' filename='libgazebo_ros_camera.so'>
|
||||
<alwaysOn>1</alwaysOn>
|
||||
<imageTopicName>image_raw</imageTopicName>
|
||||
<cameraTopicName>camera_info</cameraTopicName>
|
||||
<cameraName>camera</cameraName>
|
||||
<frameName>/camera</frameName>
|
||||
</plugin>
|
||||
</sensor>
|
||||
</link>
|
||||
</model>
|
||||
</sdf>
|
||||
13
clover_simulation/models/camera/model.config
Normal file
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0"?>
|
||||
<model>
|
||||
<name>Camera</name>
|
||||
<version>1.0</version>
|
||||
<sdf version="1.5">camera.sdf</sdf>
|
||||
<author>
|
||||
<name>Oleg Kalachev</name>
|
||||
<email>okalachev@gmail.com</email>
|
||||
</author>
|
||||
<description>
|
||||
External camera
|
||||
</description>
|
||||
</model>
|
||||
@@ -41,4 +41,4 @@ def save_world(world, file):
|
||||
'''
|
||||
Save the world to file-like object
|
||||
'''
|
||||
return world.write(file)
|
||||
return world.write(file, encoding='unicode')
|
||||
|
||||
BIN
docs/assets/clover42-main-margin.png
Normal file
|
After Width: | Height: | Size: 209 KiB |
BIN
docs/assets/duocam/duocam_connections.jpg
Normal file
|
After Width: | Height: | Size: 182 KiB |
BIN
docs/assets/duocam/image_topics.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
docs/assets/duocam/qgc_rtsp_settings.png
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
docs/assets/gps/gps_1.jpg
Normal file
|
After Width: | Height: | Size: 104 KiB |
BIN
docs/assets/gps/gps_2.jpg
Normal file
|
After Width: | Height: | Size: 140 KiB |
BIN
docs/assets/grip_load/add-on_for_load_support.stl
Normal file
BIN
docs/assets/grip_load/load_for_magnetic_grip.stl
Normal file
BIN
docs/assets/grip_load/tennis_ball_stand_for_magnetic_grip.stl
Normal file
BIN
docs/assets/hawk_eye/button.jpg
Normal file
|
After Width: | Height: | Size: 142 KiB |
BIN
docs/assets/hawk_eye/button_2.jpg
Normal file
|
After Width: | Height: | Size: 102 KiB |
BIN
docs/assets/hawk_eye/cable.jpg
Normal file
|
After Width: | Height: | Size: 56 KiB |
BIN
docs/assets/hawk_eye/cam.jpg
Normal file
|
After Width: | Height: | Size: 79 KiB |
BIN
docs/assets/hawk_eye/cam_control.jpg
Normal file
|
After Width: | Height: | Size: 98 KiB |
BIN
docs/assets/hawk_eye/cam_mount.jpg
Normal file
|
After Width: | Height: | Size: 42 KiB |
BIN
docs/assets/hawk_eye/cam_placed_1.jpg
Normal file
|
After Width: | Height: | Size: 167 KiB |
BIN
docs/assets/hawk_eye/cam_placed_2.jpg
Normal file
|
After Width: | Height: | Size: 151 KiB |
BIN
docs/assets/hawk_eye/cam_placed_3.jpg
Normal file
|
After Width: | Height: | Size: 106 KiB |
BIN
docs/assets/hawk_eye/deck_1.jpg
Normal file
|
After Width: | Height: | Size: 91 KiB |
BIN
docs/assets/hawk_eye/deck_2.jpg
Normal file
|
After Width: | Height: | Size: 113 KiB |
BIN
docs/assets/hawk_eye/menu_1.png
Normal file
|
After Width: | Height: | Size: 31 KiB |
BIN
docs/assets/hawk_eye/menu_2.png
Normal file
|
After Width: | Height: | Size: 51 KiB |
BIN
docs/assets/hawk_eye/menu_3.png
Normal file
|
After Width: | Height: | Size: 69 KiB |
BIN
docs/assets/hawk_eye/menu_4.png
Normal file
|
After Width: | Height: | Size: 56 KiB |
BIN
docs/assets/hawk_eye/menu_5.png
Normal file
|
After Width: | Height: | Size: 61 KiB |
BIN
docs/assets/radio_telemetry/telem_box.jpg
Normal file
|
After Width: | Height: | Size: 83 KiB |
BIN
docs/assets/radio_telemetry/telem_connected.jpg
Normal file
|
After Width: | Height: | Size: 78 KiB |
BIN
docs/assets/radio_telemetry/telem_placed.jpg
Normal file
|
After Width: | Height: | Size: 123 KiB |
BIN
docs/assets/seeding_drone/ala-too.png
Normal file
|
After Width: | Height: | Size: 44 KiB |
BIN
docs/assets/serial_n.png
Normal file
|
After Width: | Height: | Size: 76 KiB |
@@ -4,17 +4,52 @@ The fourth generation mobile communication is a convenient tool for transmitting
|
||||
|
||||
> **Hint** To transfer any data from your drone to the ground control station (e.g., QGroundControl) and back, you need to set up your own VPN network.
|
||||
|
||||
## Connecting Raspberry Pi to the VPN
|
||||
## Connecting 4G modem to Raspberry Pi
|
||||
|
||||
Connect a 4G modem with SIM card to the USB port of your Raspberry Pi.
|
||||
|
||||
Note that when connected, the modem must be recognized in the system as a network card, without any additional settings.
|
||||
When connected, some modems recognize in the system as a network card, without any additional settings.
|
||||
|
||||
4G modem example: *USB 4G Huawei E3372H*
|
||||
|
||||
<img src="../assets/4g/huawei.jpg" width=300 class="zoom center border">
|
||||
|
||||
> **Hint** We suggest using the UDP transfer protocol to control the drone, which provides less delay, at the cost of no guarantee of receiving the package, which is very important during the flight.
|
||||
But some other popular modems, for instance *Quectel EP06*, do not start the internet connection automatic. In this case you should use utilities like `qmi-network` and `udhcpc`. To install this utilities enter the appropriate command line:
|
||||
|
||||
```bash
|
||||
sudo apt install libqmi-utils udhcpc
|
||||
```
|
||||
|
||||
Next to start the internet connection proceed following:
|
||||
|
||||
```bash
|
||||
sudo ip link set wwan0 down
|
||||
echo 'Y' | sudo tee /sys/class/net/wwan0/qmi/raw_ip
|
||||
sudo ip link set wwan0 up
|
||||
sudo qmi-network /dev/cdc-wdm0 start
|
||||
sudo udhcpc -q -f -i wwan0
|
||||
```
|
||||
|
||||
Reed more about it in [this article](https://docs.sixfab.com/page/setting-up-a-data-connection-over-qmi-interface-using-libqmi).
|
||||
|
||||
<!-- markdownlint-disable MD031 -->
|
||||
|
||||
> **Hint** To check the internet connection enter the appropriate command line:
|
||||
> ```bash
|
||||
> ping -I wwan0 -c 5 8.8.8.8
|
||||
> ```
|
||||
|
||||
<!-- -->
|
||||
|
||||
> **Hint** To check the speed of the internet connection you could use the `speedtest` utility:
|
||||
> ```bash
|
||||
> sudo apt install speedtest-cli
|
||||
> speedtest
|
||||
> ```
|
||||
|
||||
<!-- markdownlint-enable MD031 -->
|
||||
|
||||
## Connecting Raspberry Pi to the VPN
|
||||
|
||||
Create the VPN network keys to connect Raspberry Pi and the ground station.
|
||||
|
||||
@@ -40,8 +75,14 @@ If everything is done correctly, every time the system restarts, the service cli
|
||||
|
||||
> **Hint** Before starting work, do not forget to set up and enable VPN connection on your PC.
|
||||
|
||||
<!-- -->
|
||||
|
||||
> **Hint** Alternatively we recommend to use the [ZeroTier](zerotire_vpn.md) VPN-service.
|
||||
|
||||
## Copter control via QGroundControl
|
||||
|
||||
> **Hint** We suggest using the UDP transfer protocol to control the drone, which provides less delay, at the cost of no guarantee of receiving the package, which is very important during the flight.
|
||||
|
||||
Make sure your copter and ground station are connected to your network.
|
||||
|
||||
To do this, you can use the command `ip addr`. The result will be a numbered list of the active networks enabled on your device. Note the connection with the prefix *tun* and the IP address you specify; if it is present in your list, your copter is connected to the network.
|
||||
@@ -82,7 +123,8 @@ The drone is ready to fly!
|
||||
You can stream video from almost any camera connected to your Raspberry Pi. You will need to install or [build](https://github.com/sfalexrog/gst-rtsp-launch) the *gst-rtsp-launch* package:
|
||||
|
||||
```bash
|
||||
sudo apt-get install gst-rtsp-launch
|
||||
sudo apt update
|
||||
sudo apt install gst-rtsp-launch
|
||||
```
|
||||
|
||||
To start the transfer of images, you must enter the appropriate command line:
|
||||
@@ -90,12 +132,15 @@ To start the transfer of images, you must enter the appropriate command line:
|
||||
<a id="command_line"></a>
|
||||
|
||||
```bash
|
||||
gst-rtsp-launch "( v4l2src device=/dev/video0 ! video/x-raw,framerate=30/1,width=320,height=240 ! v4l2h264enc output-io-mode=4 extra-controls=\"encode,frame_level_rate_control_enable=1,h264_profile=4,h264_level=13,video_bitrate=300000,h264_i_frame_period=5;\" ! rtph264pay name=pay0 pt=96 )"
|
||||
gst-rtsp-launch "( v4l2src device=/dev/video0 ! video/x-raw,framerate=30/1,width=320,height=240 ! videoconvert ! v4l2h264enc output-io-mode=4 extra-controls=\"encode,frame_level_rate_control_enable=1,h264_profile=4,h264_level=13,video_bitrate=300000,h264_i_frame_period=5;\" ! rtph264pay name=pay0 pt=96 )"
|
||||
```
|
||||
|
||||
This command line contains the parameters of the video stream, such as the source video device, framerate, image height/width, encoding, etc. You can see more examples [in the `gst-rtsp-launch` repository](https://github.com/sfalexrog/gst-rtsp-launch/blob/master/README.md).
|
||||
|
||||
Make sure the stream is received and shown in QGroundControl.
|
||||
> **Info** A Raspberry Pi camera device `/dev/video0` could be used by `clover` systemd service. In this case `gst-rtsp-launch` will not get an access to the device. For stop the `clover` run command `sudo systemctl stop clover`.
|
||||
> Also you could stream from a USB-camera, for this change the source video device to `/dev/video1`.
|
||||
|
||||
Make sure the stream by the address `rtsp://192.168.11.1:8554/video` (the IP-address of your Raspberry Pi could be different) is received and shown in QGroundControl.
|
||||
|
||||
<img src="../assets/4g/video_stream.png" width=300 class="zoom center border">
|
||||
|
||||
@@ -113,7 +158,7 @@ In order to run the file, you have to mark it as executable.
|
||||
chmod a+x script_name.sh
|
||||
```
|
||||
|
||||
You can use systemd to launch this script every time on system startup. Create the *qgc_video.service* file in the */etc/systemd/system* directory:
|
||||
You can use systemd to launch this script every time on system startup. Create the `qgc_video.service` file in the `/etc/systemd/system` directory:
|
||||
|
||||
```bash
|
||||
sudo nano /etc/systemd/system/qgc_video.service
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
* [Marker detection](aruco_marker.md)
|
||||
* [Map-based navigation](aruco_map.md)
|
||||
* [Optical Flow](optical_flow.md)
|
||||
* [Simple OFFBOARD](simple_offboard.md)
|
||||
* [Autonomous flight (OFFBOARD)](simple_offboard.md)
|
||||
* [Coordinate systems (frames)](frames.md)
|
||||
* [Code snippets](snippets.md)
|
||||
* [Interfacing with a laser rangefinder](laser.md)
|
||||
|
||||
@@ -116,7 +116,7 @@ while (GPS_serial.available() && new_line_found == 0) {
|
||||
// If the received byte does not equal a $ character, increase the message_counter variable
|
||||
else if (message_counter <= 99)
|
||||
message_counter++;
|
||||
|
||||
|
||||
// Write the new received byte to the new position in the incoming_message array
|
||||
incoming_message[message_counter] = read_serial_byte;
|
||||
|
||||
@@ -163,7 +163,7 @@ if (new_line_found == 1) {
|
||||
lat_gps_actual /= 10;
|
||||
|
||||
// Filter minutes for the GGA line multiplied by 10
|
||||
lon_gps_actual = ((int)incoming_message[33] - 48) * (long)10000000;
|
||||
lon_gps_actual = ((int)incoming_message[33] - 48) * (long)10000000;
|
||||
lon_gps_actual += ((int)incoming_message[34] - 48) * (long)1000000;
|
||||
lon_gps_actual += ((int)incoming_message[36] - 48) * (long)100000;
|
||||
lon_gps_actual += ((int)incoming_message[37] - 48) * (long)10000;
|
||||
@@ -192,7 +192,7 @@ if (new_line_found == 1) {
|
||||
else
|
||||
// When flying west of the prime meridian the longiude_east variable will be set to 0
|
||||
longiude_east = 0;
|
||||
|
||||
|
||||
// Filter the number of satillites from the GGA line
|
||||
number_used_sats = ((int)incoming_message[46] - 48) * (long)10;
|
||||
number_used_sats += (int)incoming_message[47] - 48;
|
||||
@@ -255,9 +255,9 @@ In the future, this will be used for the smooth drone's acceleration and deceler
|
||||
### 1.5. Waypoint stabilization {#waypoint-stabilization}
|
||||
|
||||
```cpp
|
||||
if (waypoint_set == 1) {
|
||||
if (waypoint_set == 1) {
|
||||
//If the waypoints are stored
|
||||
|
||||
|
||||
// Adjust l_lat_waypoint
|
||||
if (l_lat_gps_float_adjust > 1) {
|
||||
l_lat_waypoint++;
|
||||
@@ -296,7 +296,7 @@ if (waypoint_set == 1) {
|
||||
gps_lon_rotating_mem[gps_rotating_mem_location] = gps_lon_error - gps_lon_error_previous;
|
||||
// Add the new value to the long term avarage value
|
||||
gps_lon_total_avarage += gps_lon_rotating_mem[gps_rotating_mem_location];
|
||||
|
||||
|
||||
// Increase the rotating memory location
|
||||
gps_rotating_mem_location++;
|
||||
if (gps_rotating_mem_location == 35)
|
||||
@@ -613,7 +613,7 @@ void speed_handler(void) {
|
||||
// Convert acceleration to G
|
||||
speed /= 4096.0;
|
||||
// Convert to m/s^2
|
||||
speed *= 9.81;
|
||||
speed *= 9.81;
|
||||
// Multiply by dt to get instant speed in m/ms
|
||||
speed *= (millis() - speed_loop_timer);
|
||||
|
||||
@@ -621,10 +621,10 @@ void speed_handler(void) {
|
||||
speed_loop_timer = millis();
|
||||
|
||||
// Convert to m/s
|
||||
speed /= 1000.0;
|
||||
speed /= 1000.0;
|
||||
// Convert to km/h
|
||||
speed *= 3.6;
|
||||
|
||||
speed *= 3.6;
|
||||
|
||||
// Accumulate instantaneous speed
|
||||
speed_accumulator += speed;
|
||||
|
||||
|
||||