Compare commits
10 Commits
web-params
...
install
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0666bfdf33 | ||
|
|
7c57581c33 | ||
|
|
881daaa389 | ||
|
|
0dcf16de6b | ||
|
|
8abb40249c | ||
|
|
3ab73edb74 | ||
|
|
aeb1c8ac11 | ||
|
|
2d3df6a94e | ||
|
|
0249d01ca7 | ||
|
|
71100a9545 |
6
.github/workflows/build-image.yaml
vendored
@@ -1,4 +1,4 @@
|
|||||||
name: RPi image
|
name: Build RPi image
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
@@ -9,7 +9,7 @@ on:
|
|||||||
types: [ created ]
|
types: [ created ]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build-image:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- 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
|
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: |
|
run: |
|
||||||
cd images && sudo chmod -R 777 . && zip -9 $(echo clover_*).zip clover_* && ls -l . && unzip -l clover_*.zip
|
sudo chmod -R 777 images && zip -9 $(echo images/clover_*).zip images/clover_* && ls -l images
|
||||||
- name: Upload image
|
- name: Upload image
|
||||||
uses: softprops/action-gh-release@v1
|
uses: softprops/action-gh-release@v1
|
||||||
if: ${{ github.event_name == 'release' }}
|
if: ${{ github.event_name == 'release' }}
|
||||||
|
|||||||
4
.github/workflows/build.yml
vendored
@@ -7,14 +7,14 @@ on:
|
|||||||
branches: [ master ]
|
branches: [ master ]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
melodic:
|
build-melodic:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- name: Native Melodic build
|
- name: Native Melodic build
|
||||||
run: |
|
run: |
|
||||||
docker run --rm -v $(pwd):/root/catkin_ws/src/clover ros:melodic-ros-base /root/catkin_ws/src/clover/builder/standalone-install.sh
|
docker run --rm -v $(pwd):/root/catkin_ws/src/clover ros:melodic-ros-base /root/catkin_ws/src/clover/builder/standalone-install.sh
|
||||||
noetic:
|
build-noetic:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
|
|||||||
7
.github/workflows/docs.yml
vendored
@@ -7,7 +7,7 @@ on:
|
|||||||
branches: [ master ]
|
branches: [ master ]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
docs:
|
documentation:
|
||||||
runs-on: ubuntu-18.04
|
runs-on: ubuntu-18.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
@@ -18,8 +18,9 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
sudo sh -c "echo ttf-mscorefonts-installer msttcorefonts/accepted-mscorefonts-eula select true | debconf-set-selections"
|
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
|
sudo apt-get update && sudo apt-get install -y calibre msttcorefonts
|
||||||
builder/assets/install_gitbook.sh
|
npm install gitbook-cli -g
|
||||||
npm install markdownlint-cli@0.28.1 -g # FIXME: https://github.com/DavidAnson/markdownlint/issues/435
|
gitbook fetch 3.2.3 && npm i npm@3.10.10 --prefix=~/.gitbook/versions/3.2.3/ # fixing https://travis-ci.org/github/CopterExpress/clover/jobs/766541125#L932
|
||||||
|
npm install markdownlint-cli -g
|
||||||
npm install svgexport -g
|
npm install svgexport -g
|
||||||
gitbook -V
|
gitbook -V
|
||||||
markdownlint -V
|
markdownlint -V
|
||||||
|
|||||||
4
.github/workflows/editorconfig.yaml
vendored
@@ -1,4 +1,4 @@
|
|||||||
name: Editorconfig
|
name: Editorconfig lint
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
@@ -15,4 +15,4 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
wget --no-verbose https://github.com/okalachev/editorconfig-checker/releases/download/1.2.1-disable-spaces-amount/ec-linux-amd64
|
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
|
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|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,4 +6,3 @@ _book/
|
|||||||
package-lock.json
|
package-lock.json
|
||||||
clover_blocks/programs/*.*
|
clover_blocks/programs/*.*
|
||||||
!clover_blocks/programs/examples/*
|
!clover_blocks/programs/examples/*
|
||||||
/.vscode/
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# clover🍀: create autonomous drones easily
|
# clover🍀: create autonomous drones easily
|
||||||
|
|
||||||
<img src="docs/assets/clover42-main-margin.png" align="right" width="400px" alt="COEX Clover Drone">
|
<img src="docs/assets/clover42-main.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.
|
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,4 +1,5 @@
|
|||||||
# iOS-приложение для управления Клевером
|
iOS-приложение для управления Клевером
|
||||||
|
--------------------------------------
|
||||||
|
|
||||||
Для установки зависимостей необходим [CocoaPods](https://cocoapods.org):
|
Для установки зависимостей необходим [CocoaPods](https://cocoapods.org):
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
* `~map` – path to text file with markers list
|
||||||
* `~frame_id` – published frame id (default: `aruco_map`)
|
* `~frame_id` – published frame id (default: `aruco_map`)
|
||||||
* `~known_tilt` – known tilt (pitch and roll) of markers map as a frame
|
* `~known_tilt` – debug image width
|
||||||
* `~image_width` – debug image width (default: 2000)
|
* `~image_width` – debug image width (default: 2000)
|
||||||
* `~image_height` – debug image height (default: 2000)
|
* `~image_height` – debug image height (default: 2000)
|
||||||
* `~image_margin` – debug image margin (default: 200)
|
* `~image_margin` – debug image margin (default: 200)
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
"yametrika",
|
"yametrika",
|
||||||
"anchors",
|
"anchors",
|
||||||
"collapsible-menu",
|
"collapsible-menu",
|
||||||
"validate-links@https://github.com/okalachev/gitbook-plugin-validate-links.git",
|
"validate-links",
|
||||||
"bulk-redirect@https://github.com/okalachev/gitbook-plugin-bulk-redirect.git",
|
"bulk-redirect@https://github.com/okalachev/gitbook-plugin-bulk-redirect.git",
|
||||||
"sitemap@https://github.com/okalachev/plugin-sitemap.git",
|
"sitemap@https://github.com/okalachev/plugin-sitemap.git",
|
||||||
"toolbar@https://github.com/hamishwillee/gitbook-plugin-toolbar.git",
|
"toolbar@https://github.com/hamishwillee/gitbook-plugin-toolbar.git",
|
||||||
|
|||||||
@@ -8,9 +8,5 @@ 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 exec stdbuf -o L roslaunch clover clover.launch --wait --screen --skip-log-check \
|
||||||
2> >(tee /tmp/clover.err)"
|
2> >(tee /tmp/clover.err)"
|
||||||
|
|
||||||
ExecStartPre=+rm /var/log/clover.log
|
|
||||||
StandardOutput=file:/var/log/clover.log
|
|
||||||
StandardError=file:/var/log/clover.log
|
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
WantedBy=multi-user.target
|
WantedBy=multi-user.target
|
||||||
|
|||||||
@@ -1,9 +0,0 @@
|
|||||||
#!/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
|
|
||||||
@@ -113,11 +113,15 @@ ${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
|
# If RPi then use a one thread to build a ROS package on RPi, else use all
|
||||||
[[ $(arch) == 'armv7l' ]] && NUMBER_THREADS=1 || NUMBER_THREADS=$(nproc --all)
|
[[ $(arch) == 'armv7l' ]] && NUMBER_THREADS=1 || NUMBER_THREADS=$(nproc --all)
|
||||||
# Clover
|
# 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/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/ros_python_paths' '/etc/sudoers.d/'
|
||||||
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/pigpiod.service' '/lib/systemd/system/'
|
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/pigpiod.service' '/lib/systemd/system/'
|
||||||
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/launch.nanorc' '/usr/share/nano/'
|
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/launch.nanorc' '/usr/share/nano/'
|
||||||
# ${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/kinetic-ros-clover.rosinstall' '/home/pi/ros_catkin_ws/'
|
# ${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
|
# 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-ros.sh' ${REPO_URL} ${IMAGE_VERSION} false false ${NUMBER_THREADS}
|
||||||
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} exec ${SCRIPTS_DIR}'/image-validate.sh'
|
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} exec ${SCRIPTS_DIR}'/image-validate.sh'
|
||||||
|
|||||||
@@ -122,8 +122,9 @@ rm -rf build # remove build artifacts
|
|||||||
|
|
||||||
echo_stamp "Build Clover documentation"
|
echo_stamp "Build Clover documentation"
|
||||||
cd /home/pi/catkin_ws/src/clover
|
cd /home/pi/catkin_ws/src/clover
|
||||||
builder/assets/install_gitbook.sh
|
NPM_CONFIG_UNSAFE_PERM=true npm install gitbook-cli -g
|
||||||
gitbook install
|
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
|
||||||
gitbook build
|
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
|
touch node_modules/CATKIN_IGNORE docs/CATKIN_IGNORE _book/CATKIN_IGNORE clover/www/CATKIN_IGNORE apps/CATKIN_IGNORE # ignore documentation files by catkin
|
||||||
|
|
||||||
@@ -155,16 +156,6 @@ echo_stamp "Make \$HOME/examples symlink"
|
|||||||
ln -s "$(catkin_find clover examples --first-only)" /home/pi
|
ln -s "$(catkin_find clover examples --first-only)" /home/pi
|
||||||
chown -Rf pi:pi /home/pi/examples
|
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"
|
echo_stamp "Setup ROS environment"
|
||||||
cat << EOF >> /home/pi/.bashrc
|
cat << EOF >> /home/pi/.bashrc
|
||||||
LANG='C.UTF-8'
|
LANG='C.UTF-8'
|
||||||
|
|||||||
@@ -31,9 +31,5 @@ cd /home/pi/catkin_ws/src/clover/builder/test/
|
|||||||
|
|
||||||
systemctl stop roscore
|
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"
|
echo "Move /etc/ld.so.preload back to its original position"
|
||||||
mv /etc/ld.so.preload.disabled-for-build /etc/ld.so.preload
|
mv /etc/ld.so.preload.disabled-for-build /etc/ld.so.preload
|
||||||
|
|||||||
@@ -34,7 +34,6 @@ i2cdetect -V
|
|||||||
butterfly -h
|
butterfly -h
|
||||||
# espeak --version
|
# espeak --version
|
||||||
mjpg_streamer --version
|
mjpg_streamer --version
|
||||||
systemctl --version
|
|
||||||
|
|
||||||
# ros stuff
|
# ros stuff
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import sys
|
|||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
EXCLUDE = ('clever4-front-white.png', 'clever4-front-white-large.png', '.DS_Store',
|
EXCLUDE = ('clever4-front-white.png', 'clever4-front-white-large.png', '.DS_Store',
|
||||||
'clever4-front-black-large.png','clover42-black.png', 'clover42-main-margin.png')
|
'clever4-front-black-large.png','clover42-black.png')
|
||||||
|
|
||||||
code = 0
|
code = 0
|
||||||
|
|
||||||
|
|||||||
@@ -270,8 +270,6 @@ install(DIRECTORY launch DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION})
|
|||||||
|
|
||||||
install(DIRECTORY examples DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION})
|
install(DIRECTORY examples DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION})
|
||||||
|
|
||||||
install(DIRECTORY www DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION})
|
|
||||||
|
|
||||||
# TODO: install www
|
# TODO: install www
|
||||||
|
|
||||||
# Only install udev rules when building a Debian package
|
# Only install udev rules when building a Debian package
|
||||||
@@ -280,7 +278,7 @@ string(FIND ${CMAKE_INSTALL_PREFIX} "/opt/ros" _PREFIX_INDEX)
|
|||||||
if (${_PREFIX_INDEX} EQUAL 0)
|
if (${_PREFIX_INDEX} EQUAL 0)
|
||||||
message(STATUS "Building as a Debian package - adding udev rules as installable files")
|
message(STATUS "Building as a Debian package - adding udev rules as installable files")
|
||||||
install(FILES
|
install(FILES
|
||||||
udev/99-px4fmu.rules
|
config/99-px4fmu.rules
|
||||||
DESTINATION /lib/udev/rules.d
|
DESTINATION /lib/udev/rules.d
|
||||||
)
|
)
|
||||||
else()
|
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:
|
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
|
```bash
|
||||||
cd ~/catkin_ws/src/clover/clover/udev
|
cd ~/catkin_ws/src/clover/clover/config
|
||||||
sudo cp 99-px4fmu.rules /lib/udev/rules.d
|
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)
|
set_rates = rospy.ServiceProxy('set_rates', srv.SetRates)
|
||||||
land = rospy.ServiceProxy('land', Trigger)
|
land = rospy.ServiceProxy('land', Trigger)
|
||||||
|
|
||||||
print('Take off and hover 1 m above the ground')
|
# Take off and hover 1 m above the ground
|
||||||
navigate(x=0, y=0, z=1, frame_id='body', auto_arm=True)
|
navigate(x=0, y=0, z=1, frame_id='body', auto_arm=True)
|
||||||
|
|
||||||
# Wait for 5 seconds
|
# Wait for 3 seconds
|
||||||
rospy.sleep(5)
|
rospy.sleep(3)
|
||||||
|
|
||||||
print('Fly forward 1 m')
|
# Fly forward 1 m
|
||||||
navigate(x=1, y=0, z=0, frame_id='body')
|
navigate(x=1, y=0, z=0, frame_id='body')
|
||||||
|
|
||||||
# Wait for 5 seconds
|
# Wait for 3 seconds
|
||||||
rospy.sleep(5)
|
rospy.sleep(3)
|
||||||
|
|
||||||
print('Perform landing')
|
# Perform landing
|
||||||
land()
|
land()
|
||||||
|
|||||||
@@ -15,23 +15,23 @@ set_attitude = rospy.ServiceProxy('set_attitude', srv.SetAttitude)
|
|||||||
set_rates = rospy.ServiceProxy('set_rates', srv.SetRates)
|
set_rates = rospy.ServiceProxy('set_rates', srv.SetRates)
|
||||||
land = rospy.ServiceProxy('land', Trigger)
|
land = rospy.ServiceProxy('land', Trigger)
|
||||||
|
|
||||||
print('Take off and hover 1 m above the ground')
|
# Take off and hover 1 m above the ground
|
||||||
navigate(x=0, y=0, z=1, frame_id='body', auto_arm=True)
|
navigate(x=0, y=0, z=1, frame_id='body', auto_arm=True)
|
||||||
|
|
||||||
# Wait for 5 seconds
|
# Wait for 3 seconds
|
||||||
rospy.sleep(5)
|
rospy.sleep(3)
|
||||||
|
|
||||||
print('Fly 1 meter above ArUco marker 0')
|
# Fly 1 meter above ArUco marker 0
|
||||||
navigate(x=0, y=0, z=1, frame_id='aruco_0')
|
navigate(x=0, y=0, z=1, frame_id='aruco_0')
|
||||||
|
|
||||||
# Wait for 5 seconds
|
# Wait for 3 seconds
|
||||||
rospy.sleep(5)
|
rospy.sleep(3)
|
||||||
|
|
||||||
print('Fly to x=1 y=1 z=1 relative to ArUco markers map')
|
# Fly to x=1 y=1 z=1 relative to ArUco markers map
|
||||||
navigate(x=1, y=1, z=1, frame_id='aruco_map')
|
navigate(x=1, y=1, z=1, frame_id='aruco_map')
|
||||||
|
|
||||||
# Wait for 5 seconds
|
# Wait for 3 seconds
|
||||||
rospy.sleep(5)
|
rospy.sleep(3)
|
||||||
|
|
||||||
print('Perform landing')
|
# Perform landing
|
||||||
land()
|
land()
|
||||||
|
|||||||
@@ -1,47 +0,0 @@
|
|||||||
# 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
|
return res
|
||||||
rospy.sleep(0.2)
|
rospy.sleep(0.2)
|
||||||
|
|
||||||
print('Take off 1 meter')
|
# Take off 1 meter
|
||||||
navigate_wait(z=1, frame_id='body', auto_arm=True)
|
navigate_wait(z=1, frame_id='body', auto_arm=True)
|
||||||
|
|
||||||
print('Fly forward 1 m')
|
# Fly forward 1 m
|
||||||
navigate_wait(x=1, frame_id='body')
|
navigate_wait(x=1, frame_id='body')
|
||||||
|
|
||||||
print('Land')
|
# Land
|
||||||
land()
|
land()
|
||||||
|
|||||||
@@ -1,15 +0,0 @@
|
|||||||
# 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_map" default="false"/>
|
||||||
<arg name="aruco_vpe" default="false"/>
|
<arg name="aruco_vpe" default="false"/>
|
||||||
<arg name="placement" default="floor"/> <!-- markers placement: floor, ceiling, unknown -->
|
<arg name="placement" default="floor"/> <!-- markers placement: floor, ceiling, unknown -->
|
||||||
<arg name="length" default="0.22"/> <!-- not-in-map markers length, m -->
|
<arg name="length" default="0.33"/> <!-- not-in-map markers length, m -->
|
||||||
<arg name="map" default="map.txt"/> <!-- markers map file name -->
|
<arg name="map" default="map.txt"/> <!-- markers map file name -->
|
||||||
|
|
||||||
<!-- For additional help go to https://clover.coex.tech/aruco -->
|
<!-- 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">
|
<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="image_raw" to="main_camera/image_raw"/>
|
||||||
<remap from="camera_info" to="main_camera/camera_info"/>
|
<remap from="camera_info" to="main_camera/camera_info"/>
|
||||||
<remap from="map_markers" to="aruco_map/markers"/>
|
<remap from="map_markers" to="aruco_map/markers" if="$(arg aruco_map)"/>
|
||||||
<param name="estimate_poses" value="true"/>
|
<param name="estimate_poses" value="true"/>
|
||||||
<param name="send_tf" value="true"/>
|
<param name="send_tf" value="true"/>
|
||||||
<param name="known_tilt" value="map" if="$(eval placement == 'floor')"/>
|
<param name="known_tilt" value="map" if="$(eval placement == 'floor')"/>
|
||||||
|
|||||||
@@ -75,6 +75,9 @@ private:
|
|||||||
velo_pub_ = nh_priv.advertise<geometry_msgs::TwistStamped>("angular_velocity", 1);
|
velo_pub_ = nh_priv.advertise<geometry_msgs::TwistStamped>("angular_velocity", 1);
|
||||||
shift_pub_ = nh_priv.advertise<geometry_msgs::Vector3Stamped>("shift", 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_.time_delta_distance_us = 0;
|
||||||
flow_.distance = -1; // no distance sensor available
|
flow_.distance = -1; // no distance sensor available
|
||||||
flow_.temperature = 0;
|
flow_.temperature = 0;
|
||||||
@@ -176,7 +179,7 @@ private:
|
|||||||
double flow_x = atan2(points_undist[0].x, focal_length_x);
|
double flow_x = atan2(points_undist[0].x, focal_length_x);
|
||||||
double flow_y = atan2(points_undist[0].y, focal_length_y);
|
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;
|
geometry_msgs::Vector3Stamped flow_camera, flow_fcu;
|
||||||
flow_camera.header.frame_id = msg->header.frame_id;
|
flow_camera.header.frame_id = msg->header.frame_id;
|
||||||
flow_camera.header.stamp = msg->header.stamp;
|
flow_camera.header.stamp = msg->header.stamp;
|
||||||
@@ -193,11 +196,6 @@ private:
|
|||||||
ros::Duration integration_time = msg->header.stamp - prev_stamp_;
|
ros::Duration integration_time = msg->header.stamp - prev_stamp_;
|
||||||
uint32_t integration_time_us = integration_time.toSec() * 1.0e6;
|
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_) {
|
if (calc_flow_gyro_) {
|
||||||
try {
|
try {
|
||||||
auto flow_gyro_camera = calcFlowGyro(msg->header.frame_id, prev_stamp_, msg->header.stamp);
|
auto flow_gyro_camera = calcFlowGyro(msg->header.frame_id, prev_stamp_, msg->header.stamp);
|
||||||
@@ -207,7 +205,9 @@ private:
|
|||||||
flow_.integrated_ygyro = flow_gyro_fcu.vector.y;
|
flow_.integrated_ygyro = flow_gyro_fcu.vector.y;
|
||||||
flow_.integrated_zgyro = flow_gyro_fcu.vector.z;
|
flow_.integrated_zgyro = flow_gyro_fcu.vector.z;
|
||||||
} catch (const tf2::TransformException& e) {
|
} catch (const tf2::TransformException& e) {
|
||||||
// Transform not available, keep NANs in flow gyro
|
// Invalidate previous frame
|
||||||
|
prev_.release();
|
||||||
|
goto publish_debug;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -183,7 +183,7 @@ inline bool waitTransform(const string& target, const string& source,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#define TIMEOUT(msg, timeout) (msg.header.stamp.isZero() || (ros::Time::now() - msg.header.stamp > timeout))
|
#define TIMEOUT(msg, timeout) (ros::Time::now() - msg.header.stamp > timeout)
|
||||||
|
|
||||||
bool getTelemetry(GetTelemetry::Request& req, GetTelemetry::Response& res)
|
bool getTelemetry(GetTelemetry::Request& req, GetTelemetry::Response& res)
|
||||||
{
|
{
|
||||||
@@ -441,10 +441,6 @@ void publish(const ros::Time stamp)
|
|||||||
|
|
||||||
// publish setpoint frame
|
// publish setpoint frame
|
||||||
if (!setpoint.child_frame_id.empty()) {
|
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.x = position_msg.pose.position.x;
|
||||||
setpoint.transform.translation.y = position_msg.pose.position.y;
|
setpoint.transform.translation.y = position_msg.pose.position.y;
|
||||||
setpoint.transform.translation.z = position_msg.pose.position.z;
|
setpoint.transform.translation.z = position_msg.pose.position.z;
|
||||||
|
|||||||
1
clover/www/clover.err
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
/tmp/clover.err
|
||||||
@@ -1 +0,0 @@
|
|||||||
/var/log/clover.log
|
|
||||||
@@ -4,12 +4,12 @@
|
|||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
<li><a href="docs">View documentation</a> (snapshot of <a href="https://clover.coex.tech">clover.coex.tech</a>)</li>
|
<li><a href="docs">View documentation</a> (snapshot of <a href="https://clover.coex.tech">clover.coex.tech</a>)</li>
|
||||||
<li><a href="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="wvs">View image topics</a> (<code>web_video_server</code>)</li>
|
||||||
<li><a href="" id="butterfly">Open web terminal</a> (<code>Butterfly</code>)</li>
|
<li><a href="" id="butterfly">Open web terminal</a> (<code>Butterfly</code>)</li>
|
||||||
<li>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="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><a href="../clover_blocks/">Blocks programming</a> (<code>Blockly</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="clover.err">Clover console</a> (<code>/tmp/clover.err</code>)</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<div class="version"></div>
|
<div class="version"></div>
|
||||||
@@ -18,14 +18,6 @@
|
|||||||
document.querySelector("#wvs").href = location.protocol + '//' + location.hostname + ':8080';
|
document.querySelector("#wvs").href = location.protocol + '//' + location.hostname + ':8080';
|
||||||
document.querySelector("#butterfly").href = location.protocol + '//' + location.hostname + ':57575';
|
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
|
// Determine image version
|
||||||
fetch('clover_version').then(function(response) {
|
fetch('clover_version').then(function(response) {
|
||||||
if (response.status !== 200) return;
|
if (response.status !== 200) return;
|
||||||
|
|||||||
@@ -1,236 +0,0 @@
|
|||||||
// 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]);
|
|
||||||
@@ -1,100 +0,0 @@
|
|||||||
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;
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
<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>
|
|
||||||
@@ -78,8 +78,6 @@ install(DIRECTORY programs
|
|||||||
DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}
|
DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}
|
||||||
)
|
)
|
||||||
|
|
||||||
install(DIRECTORY www DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION})
|
|
||||||
|
|
||||||
#############
|
#############
|
||||||
## Testing ##
|
## Testing ##
|
||||||
#############
|
#############
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ Internal package documentation is given below.
|
|||||||
|
|
||||||
## Frontend
|
## Frontend
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
## `clover_blocks` node
|
## `clover_blocks` node
|
||||||
|
|
||||||
@@ -30,8 +30,7 @@ The frontend files are located in [`www`](./www/) subdirectory. The frontend app
|
|||||||
Parameters read by frontend:
|
Parameters read by frontend:
|
||||||
|
|
||||||
* `~navigate_tolerance` (*float*) – distance tolerance in meters, used for navigate-like blocks (default: 0.2).
|
* `~navigate_tolerance` (*float*) – distance tolerance in meters, used for navigate-like blocks (default: 0.2).
|
||||||
* `~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: 20).
|
||||||
* `~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).
|
* `~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).
|
* `~confirm_run` (*bool*) – enable confirmation to run the program (default: true).
|
||||||
|
|
||||||
|
|||||||
@@ -31,14 +31,6 @@ function considerFrameId(e) {
|
|||||||
this.getInput('Y').fieldRow[0].setValue('y');
|
this.getInput('Y').fieldRow[0].setValue('y');
|
||||||
this.getInput('Z').fieldRow[0].setValue('z');
|
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
|
if (this.getInput('ID')) { // block has marker id field
|
||||||
this.getInput('ID').setVisible(frameId == 'ARUCO'); // toggle id field
|
this.getInput('ID').setVisible(frameId == 'ARUCO'); // toggle id field
|
||||||
@@ -73,9 +65,6 @@ function updateSetpointBlock(e) {
|
|||||||
|
|
||||||
Blockly.Blocks['navigate'] = {
|
Blockly.Blocks['navigate'] = {
|
||||||
init: function () {
|
init: function () {
|
||||||
let navFrameId = frameIds.slice();
|
|
||||||
navFrameId.push(['global', 'GLOBAL_LOCAL'])
|
|
||||||
navFrameId.push(['global, WGS 84 alt.', 'GLOBAL'])
|
|
||||||
this.appendDummyInput()
|
this.appendDummyInput()
|
||||||
.appendField("navigate to point");
|
.appendField("navigate to point");
|
||||||
this.appendValueInput("X")
|
this.appendValueInput("X")
|
||||||
@@ -84,20 +73,12 @@ Blockly.Blocks['navigate'] = {
|
|||||||
this.appendValueInput("Y")
|
this.appendValueInput("Y")
|
||||||
.setCheck("Number")
|
.setCheck("Number")
|
||||||
.appendField("left");
|
.appendField("left");
|
||||||
this.appendValueInput("LAT")
|
|
||||||
.setCheck("Number")
|
|
||||||
.appendField("latitude")
|
|
||||||
.setVisible(false);
|
|
||||||
this.appendValueInput("LON")
|
|
||||||
.setCheck("Number")
|
|
||||||
.appendField("longitude")
|
|
||||||
.setVisible(false)
|
|
||||||
this.appendValueInput("Z")
|
this.appendValueInput("Z")
|
||||||
.setCheck("Number")
|
.setCheck("Number")
|
||||||
.appendField("up");
|
.appendField("up");
|
||||||
this.appendDummyInput()
|
this.appendDummyInput()
|
||||||
.appendField("relative to")
|
.appendField("relative to")
|
||||||
.appendField(new Blockly.FieldDropdown(navFrameId), "FRAME_ID");
|
.appendField(new Blockly.FieldDropdown(frameIds), "FRAME_ID");
|
||||||
this.appendValueInput("ID")
|
this.appendValueInput("ID")
|
||||||
.setCheck("Number")
|
.setCheck("Number")
|
||||||
.appendField("with ID")
|
.appendField("with ID")
|
||||||
@@ -287,7 +268,7 @@ Blockly.Blocks['mode'] = {
|
|||||||
.appendField("current flight mode");
|
.appendField("current flight mode");
|
||||||
this.setOutput(true, "String");
|
this.setOutput(true, "String");
|
||||||
this.setColour(COLOR_STATE);
|
this.setColour(COLOR_STATE);
|
||||||
this.setTooltip("Returns current flight mode (POSCTL, OFFBOARD, etc).");
|
this.setTooltip("");
|
||||||
this.setHelpUrl(DOCS_URL + '#' + this.type);
|
this.setHelpUrl(DOCS_URL + '#' + this.type);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -394,7 +375,7 @@ Blockly.Blocks['take_off'] = {
|
|||||||
this.setPreviousStatement(true, null);
|
this.setPreviousStatement(true, null);
|
||||||
this.setNextStatement(true, null);
|
this.setNextStatement(true, null);
|
||||||
this.setColour(COLOR_FLIGHT);
|
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);
|
this.setHelpUrl(DOCS_URL + '#' + this.type);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -410,7 +391,7 @@ Blockly.Blocks['land'] = {
|
|||||||
this.setPreviousStatement(true, null);
|
this.setPreviousStatement(true, null);
|
||||||
this.setNextStatement(true, null);
|
this.setNextStatement(true, null);
|
||||||
this.setColour(COLOR_FLIGHT);
|
this.setColour(COLOR_FLIGHT);
|
||||||
this.setTooltip("Land the drone.");
|
this.setTooltip("");
|
||||||
this.setHelpUrl(DOCS_URL + '#' + this.type);
|
this.setHelpUrl(DOCS_URL + '#' + this.type);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -419,10 +400,10 @@ Blockly.Blocks['global_position'] = {
|
|||||||
init: function () {
|
init: function () {
|
||||||
this.appendDummyInput()
|
this.appendDummyInput()
|
||||||
.appendField("current")
|
.appendField("current")
|
||||||
.appendField(new Blockly.FieldDropdown([["latitude", "LAT"], ["longitude", "LON"], ["altitude", "ALT"]]), "FIELD");
|
.appendField(new Blockly.FieldDropdown([["latitude", "LATITUDE"], ["longitude", "LONGITUDE"], ["altitude", "ALTITUDE"]]), "NAME");
|
||||||
this.setOutput(true, "Number");
|
this.setOutput(true, "Number");
|
||||||
this.setColour(COLOR_STATE);
|
this.setColour(230);
|
||||||
this.setTooltip("Returns current global position (latitude, longitude, altitude above the WGS 84 ellipsoid).");
|
this.setTooltip("");
|
||||||
this.setHelpUrl(DOCS_URL + '#' + this.type);
|
this.setHelpUrl(DOCS_URL + '#' + this.type);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -52,8 +52,6 @@
|
|||||||
<value name="X"><shadow type="math_number"><field name="NUM">0</field></shadow></value>
|
<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="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="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="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>
|
<value name="ID"><shadow type="math_number"><field name="NUM">0</field></shadow></value>
|
||||||
</block>
|
</block>
|
||||||
@@ -87,7 +85,6 @@
|
|||||||
<value name="ID"><shadow type="math_number"><field name="NUM">0</field></shadow></value>
|
<value name="ID"><shadow type="math_number"><field name="NUM">0</field></shadow></value>
|
||||||
</block>
|
</block>
|
||||||
<block type="get_attitude"></block>
|
<block type="get_attitude"></block>
|
||||||
<block type="global_position"></block>
|
|
||||||
<block type="distance">
|
<block type="distance">
|
||||||
<value name="X"><shadow type="math_number"><field name="NUM">0</field></shadow></value>
|
<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="Y"><shadow type="math_number"><field name="NUM">0</field></shadow></value>
|
||||||
|
|||||||
@@ -39,8 +39,7 @@ var workspace = Blockly.inject('blockly', {
|
|||||||
function readParams() {
|
function readParams() {
|
||||||
return Promise.all([
|
return Promise.all([
|
||||||
ros.readParam('navigate_tolerance', true, 0.2),
|
ros.readParam('navigate_tolerance', true, 0.2),
|
||||||
ros.readParam('navigate_global_tolerance', true, 1),
|
ros.readParam('yaw_tolerance', true, 20),
|
||||||
ros.readParam('yaw_tolerance', true, 1),
|
|
||||||
ros.readParam('sleep_time', true, 0.2),
|
ros.readParam('sleep_time', true, 0.2),
|
||||||
ros.readParam('confirm_run', true, true),
|
ros.readParam('confirm_run', true, true),
|
||||||
]);
|
]);
|
||||||
@@ -211,7 +210,7 @@ function loadPrograms() {
|
|||||||
updateChanged();
|
updateChanged();
|
||||||
}, function(err) {
|
}, function(err) {
|
||||||
document.querySelector('.backend-fail').style.display = 'inline';
|
document.querySelector('.backend-fail').style.display = 'inline';
|
||||||
alert(`Error loading programs list.\n\nHave you enabled 'blocks' in clover.launch?`);
|
alert(`Error loading programs list.\n\nHave you enabled clover_blocks in clover.launch?`);
|
||||||
runButton.disabled = true;
|
runButton.disabled = true;
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,18 +33,6 @@ const NAVIGATE_WAIT = () => `\ndef navigate_wait(x=0, y=0, z=0, speed=0.5, frame
|
|||||||
return
|
return
|
||||||
rospy.sleep(${params.sleep_time})\n`;
|
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():
|
const LAND_WAIT = () => `\ndef land_wait():
|
||||||
land()
|
land()
|
||||||
while get_telemetry().armed:
|
while get_telemetry().armed:
|
||||||
@@ -80,9 +68,6 @@ function generateROSDefinitions() {
|
|||||||
if (rosDefinitions.offboard) {
|
if (rosDefinitions.offboard) {
|
||||||
code += `get_telemetry = rospy.ServiceProxy('get_telemetry', srv.GetTelemetry)\n`;
|
code += `get_telemetry = rospy.ServiceProxy('get_telemetry', srv.GetTelemetry)\n`;
|
||||||
code += `navigate = rospy.ServiceProxy('navigate', srv.Navigate)\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) {
|
if (rosDefinitions.setVelocity) {
|
||||||
code += `set_velocity = rospy.ServiceProxy('set_velocity', srv.SetVelocity)\n`;
|
code += `set_velocity = rospy.ServiceProxy('set_velocity', srv.SetVelocity)\n`;
|
||||||
}
|
}
|
||||||
@@ -109,10 +94,6 @@ function generateROSDefinitions() {
|
|||||||
Blockly.Python.definitions_['import_math'] = 'import math';
|
Blockly.Python.definitions_['import_math'] = 'import math';
|
||||||
code += NAVIGATE_WAIT();
|
code += NAVIGATE_WAIT();
|
||||||
}
|
}
|
||||||
if (rosDefinitions.navigateGlobalWait) {
|
|
||||||
Blockly.Python.definitions_['import_math'] = 'import math';
|
|
||||||
code += NAVIGATE_GLOBAL_WAIT();
|
|
||||||
}
|
|
||||||
if (rosDefinitions.landWait) {
|
if (rosDefinitions.landWait) {
|
||||||
code += LAND_WAIT();
|
code += LAND_WAIT();
|
||||||
}
|
}
|
||||||
@@ -180,48 +161,24 @@ Blockly.Python.navigate = function(block) {
|
|||||||
let x = Blockly.Python.valueToCode(block, 'X', Blockly.Python.ORDER_NONE);
|
let x = Blockly.Python.valueToCode(block, 'X', Blockly.Python.ORDER_NONE);
|
||||||
let y = Blockly.Python.valueToCode(block, 'Y', 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 z = Blockly.Python.valueToCode(block, 'Z', Blockly.Python.ORDER_NONE);
|
||||||
let lat = Blockly.Python.valueToCode(block, 'LAT', Blockly.Python.ORDER_NONE);
|
let frameId = buildFrameId(block);
|
||||||
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 speed = Blockly.Python.valueToCode(block, 'SPEED', Blockly.Python.ORDER_NONE);
|
||||||
|
|
||||||
|
let params = [`x=${x}`, `y=${y}`, `z=${z}`, `frame_id=${frameId}`, `speed=${speed}`];
|
||||||
|
|
||||||
simpleOffboard();
|
simpleOffboard();
|
||||||
|
|
||||||
// global coordinates
|
if (block.getFieldValue('WAIT') == 'TRUE') {
|
||||||
if (frameId.startsWith('GLOBAL')) {
|
rosDefinitions.navigateWait = true;
|
||||||
rosDefinitions.navigateGlobal = true;
|
|
||||||
simpleOffboard();
|
simpleOffboard();
|
||||||
|
|
||||||
if (frameId == 'GLOBAL') {
|
return `navigate_wait(${params.join(', ')})\n`;
|
||||||
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 {
|
} else {
|
||||||
frameId = buildFrameId(block);
|
if (frameId != 'body') {
|
||||||
let params = [`x=${x}`, `y=${y}`, `z=${z}`, `frame_id=${frameId}`, `speed=${speed}`];
|
params.push(`yaw=float('nan')`);
|
||||||
|
|
||||||
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`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -358,12 +315,6 @@ Blockly.Python.get_attitude = function(block) {
|
|||||||
return [code, Blockly.Python.ORDER_FUNCTION_CALL];
|
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) {
|
Blockly.Python.distance = function(block) {
|
||||||
rosDefinitions.distance = true;
|
rosDefinitions.distance = true;
|
||||||
simpleOffboard();
|
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;
|
* `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`);
|
* `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`);
|
* `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 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)).
|
* `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)).
|
||||||
|
|
||||||
For example, in order to spawn a drone without a GPS module, you may use the following command:
|
For example, in order to spawn a drone without a GPS module, you may use the following command:
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<launch>
|
<launch>
|
||||||
<arg name="model" default="$(find clover_description)/urdf/clover/clover4.xacro"/>
|
<arg name="model" default="$(find clover_description)/urdf/drones/clover4.xacro"/>
|
||||||
|
|
||||||
<param name="robot_description" command="xacro $(arg model)"/>
|
<param name="robot_description" command="$(find xacro)/xacro.py $(arg model)"/>
|
||||||
|
|
||||||
<node name="rviz" pkg="rviz" type="rviz" required="true"/>
|
<node name="rviz" pkg="rviz" type="rviz" required="true"/>
|
||||||
</launch>
|
</launch>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0"?>
|
<?xml version="1.0"?>
|
||||||
<robot name="clover" xmlns:xacro="http://ros.org/wiki/xacro">
|
<robot name="clover_camera" xmlns:xacro="http://ros.org/wiki/xacro">
|
||||||
|
|
||||||
<xacro:arg name="main_camera" default="true"/>
|
<xacro:arg name="main_camera" default="true"/>
|
||||||
<xacro:arg name="rangefinder" default="true"/>
|
<xacro:arg name="rangefinder" default="true"/>
|
||||||
@@ -8,10 +8,10 @@
|
|||||||
<xacro:arg name="maintain_camera_rate" default="false"/>
|
<xacro:arg name="maintain_camera_rate" default="false"/>
|
||||||
<xacro:arg name="use_clover_physics" default="false"/>
|
<xacro:arg name="use_clover_physics" default="false"/>
|
||||||
|
|
||||||
<xacro:include filename="clover4_base.xacro" />
|
<xacro:include filename="$(find clover_description)/urdf/clover/clover4_base.xacro" />
|
||||||
<xacro:include filename="../sensors/rpi_cam.urdf.xacro"/>
|
<xacro:include filename="$(find clover_description)/urdf/sensors/rpi_cam.urdf.xacro"/>
|
||||||
<xacro:include filename="../sensors/distance_sensor.urdf.xacro"/>
|
<xacro:include filename="$(find clover_description)/urdf/sensors/distance_sensor.urdf.xacro"/>
|
||||||
<xacro:include filename="../leds/led_strip.xacro"/>
|
<xacro:include filename="$(find clover_description)/urdf/leds/led_strip.xacro"/>
|
||||||
|
|
||||||
<!-- Create camera plugin -->
|
<!-- Create camera plugin -->
|
||||||
<xacro:if value="$(arg main_camera)">
|
<xacro:if value="$(arg main_camera)">
|
||||||
@@ -36,17 +36,11 @@
|
|||||||
</xacro:if>
|
</xacro:if>
|
||||||
|
|
||||||
<xacro:if value="$(arg gps)">
|
<xacro:if value="$(arg gps)">
|
||||||
<gazebo>
|
<!-- Instantiate gps plugin. -->
|
||||||
<include>
|
<xacro:gps_plugin_macro
|
||||||
<uri>model://gps</uri>
|
namespace="${namespace}"
|
||||||
<pose>0.1 0 0 0 0 0</pose>
|
gps_noise="true"
|
||||||
<name>gps0</name>
|
/>
|
||||||
</include>
|
|
||||||
<joint name='gps0_joint' type='fixed'>
|
|
||||||
<child>gps0::link</child>
|
|
||||||
<parent>base_link</parent>
|
|
||||||
</joint>
|
|
||||||
</gazebo>
|
|
||||||
</xacro:if>
|
</xacro:if>
|
||||||
|
|
||||||
</robot>
|
</robot>
|
||||||
|
|||||||
@@ -1,15 +1,40 @@
|
|||||||
<?xml version="1.0"?>
|
<?xml version="1.0"?>
|
||||||
|
|
||||||
<robot name="clover" xmlns:xacro="http://ros.org/wiki/xacro">
|
<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' />
|
<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='enable_logging' default='false' />
|
||||||
<xacro:arg name='log_file' default='clover' />
|
<xacro:arg name='log_file' default='iris' />
|
||||||
|
|
||||||
<!-- macros for gazebo plugins, sensors -->
|
<!-- macros for gazebo plugins, sensors -->
|
||||||
<xacro:include filename="../component_snippets.urdf.xacro" />
|
<xacro:include filename="$(find clover_description)/urdf/component_snippets.urdf.xacro" />
|
||||||
|
|
||||||
<!-- Instantiate iris "mechanics" -->
|
<!-- Instantiate iris "mechanics" -->
|
||||||
<xacro:include filename="clover4_physics.xacro" />
|
<xacro:include filename="$(find clover_description)/urdf/clover/clover4_physics.xacro" />
|
||||||
|
|
||||||
<xacro:if value="$(arg enable_wind)">
|
<xacro:if value="$(arg enable_wind)">
|
||||||
<xacro:wind_plugin_macro
|
<xacro:wind_plugin_macro
|
||||||
@@ -24,8 +49,126 @@
|
|||||||
/>
|
/>
|
||||||
</xacro:if>
|
</xacro:if>
|
||||||
|
|
||||||
<!-- Gazebo plugins -->
|
<!-- Instantiate magnetometer plugin. -->
|
||||||
<xacro:include filename="$(find clover_description)/urdf/clover/clover4_gazebo.xacro" />
|
<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>
|
||||||
|
|
||||||
<xacro:if value="$(arg enable_logging)">
|
<xacro:if value="$(arg enable_logging)">
|
||||||
<!-- Instantiate a logger -->
|
<!-- Instantiate a logger -->
|
||||||
|
|||||||
@@ -1,183 +0,0 @@
|
|||||||
<?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="clover" xmlns:xacro="http://ros.org/wiki/xacro">
|
<robot name="iris" xmlns:xacro="http://ros.org/wiki/xacro">
|
||||||
<!-- Properties -->
|
<!-- Properties -->
|
||||||
<xacro:property name="namespace" value="" />
|
<xacro:property name="namespace" value="" />
|
||||||
<xacro:property name="rotor_velocity_slowdown_sim" value="10" />
|
<xacro:property name="rotor_velocity_slowdown_sim" value="10" />
|
||||||
@@ -84,7 +84,7 @@
|
|||||||
</xacro:unless>
|
</xacro:unless>
|
||||||
|
|
||||||
<!-- Included URDF Files -->
|
<!-- Included URDF Files -->
|
||||||
<xacro:include filename="clover4_macros.xacro" />
|
<xacro:include filename="$(find clover_description)/urdf/clover/clover4_macros.xacro" />
|
||||||
|
|
||||||
<!-- Instantiate multirotor_base_macro once -->
|
<!-- Instantiate multirotor_base_macro once -->
|
||||||
<xacro:clover4_base_macro
|
<xacro:clover4_base_macro
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<robot name="rpi_camera" xmlns:xacro="http://ros.org/wiki/xacro">
|
<robot name="rpi_camera" xmlns:xacro="http://ros.org/wiki/xacro">
|
||||||
<xacro:include filename="../camera_sensor.urdf.xacro"/>
|
<xacro:include filename="$(find clover_description)/urdf/camera_sensor.urdf.xacro"/>
|
||||||
|
|
||||||
<xacro:macro name="distance_sensor" params="name:=lidar_vl53l1x parent x:=0 y:=0 z:=0 roll:=0 pitch:=0 yaw:=0 range_min:=0.01 range_max:=4.0 resolution:=0.01 rate:=10">
|
<xacro:macro name="distance_sensor" params="name:=lidar_vl53l1x parent x:=0 y:=0 z:=0 roll:=0 pitch:=0 yaw:=0 range_min:=0.01 range_max:=4.0 resolution:=0.01 rate:=10">
|
||||||
<joint name="${name}_joint" type="fixed">
|
<joint name="${name}_joint" type="fixed">
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<robot name="pinhole_camera" xmlns:xacro="http://ros.org/wiki/xacro">
|
<robot name="pinhole_camera" xmlns:xacro="http://ros.org/wiki/xacro">
|
||||||
<xacro:include filename="../camera_sensor.urdf.xacro"/>
|
<xacro:include filename="$(find clover_description)/urdf/camera_sensor.urdf.xacro"/>
|
||||||
<xacro:camera_sensor name="main_camera" width="640" height="480" rate="30" horizontal_fov="${120.0*pi/180.0}"/>
|
<xacro:camera_sensor name="main_camera" width="640" height="480" rate="30" horizontal_fov="${120.0*pi/180.0}"/>
|
||||||
</robot>
|
</robot>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<robot name="rpi_camera" xmlns:xacro="http://ros.org/wiki/xacro">
|
<robot name="rpi_camera" xmlns:xacro="http://ros.org/wiki/xacro">
|
||||||
<xacro:include filename="../camera_sensor.urdf.xacro"/>
|
<xacro:include filename="$(find clover_description)/urdf/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">
|
<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">
|
<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:
|
The plugin accepts the following parameters during instantiation:
|
||||||
|
|
||||||
* `robotNamespace` (*string*, default: "") - a ROS namespace for the plugin;
|
* `robotNamespace` (*string*, default: "") - a ROS namespace for the plugin;
|
||||||
* `ledCount` (*integer*, required) - total number of LEDs in a strip.
|
* `ledCount` (*integer*, required) - total numer of LEDs in a strip.
|
||||||
|
|
||||||
The plugin will provide the following service:
|
The plugin will provide the following service:
|
||||||
|
|
||||||
|
|||||||
@@ -1,36 +0,0 @@
|
|||||||
#!/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,13 +8,12 @@
|
|||||||
<arg name="led" default="true"/> <!-- Simulated LED strip, ws281x-like -->
|
<arg name="led" default="true"/> <!-- Simulated LED strip, ws281x-like -->
|
||||||
<arg name="gps" default="false"/> <!--Simulated GPS module -->
|
<arg name="gps" default="false"/> <!--Simulated GPS module -->
|
||||||
<arg name="use_clover_physics" default="false"/> <!-- Use inertial parameters from CAD models -->
|
<arg name="use_clover_physics" default="false"/> <!-- Use inertial parameters from CAD models -->
|
||||||
<arg name="gui" default="true"/> <!-- Run Gazebo with GUI -->
|
|
||||||
|
|
||||||
<!-- Gazebo instance -->
|
<!-- Gazebo instance -->
|
||||||
<include file="$(find gazebo_ros)/launch/empty_world.launch" if="$(eval type == 'gazebo')">
|
<include file="$(find gazebo_ros)/launch/empty_world.launch" if="$(eval type == 'gazebo')">
|
||||||
<!-- Workaround for crashes in VMware -->
|
<!-- Workaround for crashes in VMware -->
|
||||||
<env name="SVGA_VGPU10" value="0"/>
|
<env name="SVGA_VGPU10" value="0"/>
|
||||||
<arg name="gui" value="$(arg gui)"/>
|
<arg name="gui" value="true"/>
|
||||||
<arg name="world_name" value="$(find clover_simulation)/resources/worlds/clover_aruco.world"/>
|
<arg name="world_name" value="$(find clover_simulation)/resources/worlds/clover_aruco.world"/>
|
||||||
<arg name="verbose" value="true"/>
|
<arg name="verbose" value="true"/>
|
||||||
</include>
|
</include>
|
||||||
@@ -34,9 +33,7 @@
|
|||||||
<arg name="use_clover_physics" value="$(arg use_clover_physics)"/>
|
<arg name="use_clover_physics" value="$(arg use_clover_physics)"/>
|
||||||
</include>
|
</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')"/>
|
<param name="use_sim_time" value="false" if="$(eval type == 'jmavsim')"/>
|
||||||
|
|
||||||
@@ -46,10 +43,10 @@
|
|||||||
<arg name="fcu_conn" value="sitl"/>
|
<arg name="fcu_conn" value="sitl"/>
|
||||||
<arg name="fcu_ip" value="127.0.0.1"/>
|
<arg name="fcu_ip" value="127.0.0.1"/>
|
||||||
<arg name="gcs_bridge" value=""/>
|
<arg name="gcs_bridge" value=""/>
|
||||||
|
<arg name="rc" default="false"/>
|
||||||
<arg name="web_video_server" default="false" if="$(eval type == 'jmavsim')"/>
|
<arg name="web_video_server" default="false" if="$(eval type == 'jmavsim')"/>
|
||||||
<arg name="main_camera" 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="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')"/>
|
<arg name="led" default="false" if="$(eval type == 'jmavsim')"/>
|
||||||
</include>
|
</include>
|
||||||
</launch>
|
</launch>
|
||||||
|
|||||||
@@ -1,33 +0,0 @@
|
|||||||
<?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>
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
<?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
|
Save the world to file-like object
|
||||||
'''
|
'''
|
||||||
return world.write(file, encoding='unicode')
|
return world.write(file)
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 209 KiB |
|
Before Width: | Height: | Size: 182 KiB |
|
Before Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 28 KiB |
4644
docs/assets/dxf/4.2/big_leg.dxf
Normal file
21446
docs/assets/dxf/4.2/deck_mount.dxf
Normal file
18312
docs/assets/dxf/4.2/deck_mount_small.dxf
Normal file
20006
docs/assets/dxf/4.2/grab_deck.dxf
Normal file
4202
docs/assets/dxf/4.2/grip_spacer.dxf
Normal file
15620
docs/assets/dxf/4.2/led_mount_plate.dxf
Normal file
15252
docs/assets/dxf/4.2/prop_guard.dxf
Normal file
15648
docs/assets/dxf/4.2/prop_guard_mount.dxf
Normal file
15904
docs/assets/dxf/4.2/small_leg.dxf
Normal file
|
Before Width: | Height: | Size: 104 KiB |
|
Before Width: | Height: | Size: 140 KiB |
|
Before Width: | Height: | Size: 142 KiB |
|
Before Width: | Height: | Size: 102 KiB |
|
Before Width: | Height: | Size: 56 KiB |
|
Before Width: | Height: | Size: 79 KiB |
|
Before Width: | Height: | Size: 98 KiB |
|
Before Width: | Height: | Size: 42 KiB |
|
Before Width: | Height: | Size: 167 KiB |
|
Before Width: | Height: | Size: 151 KiB |
|
Before Width: | Height: | Size: 106 KiB |
|
Before Width: | Height: | Size: 91 KiB |
|
Before Width: | Height: | Size: 113 KiB |
|
Before Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 51 KiB |
|
Before Width: | Height: | Size: 69 KiB |
|
Before Width: | Height: | Size: 56 KiB |
|
Before Width: | Height: | Size: 61 KiB |
|
Before Width: | Height: | Size: 83 KiB |
|
Before Width: | Height: | Size: 78 KiB |
|
Before Width: | Height: | Size: 123 KiB |
|
Before Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 76 KiB |
@@ -4,52 +4,17 @@ 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.
|
> **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 4G modem to Raspberry Pi
|
## Connecting Raspberry Pi to the VPN
|
||||||
|
|
||||||
Connect a 4G modem with SIM card to the USB port of your Raspberry Pi.
|
Connect a 4G modem with SIM card to the USB port of your Raspberry Pi.
|
||||||
|
|
||||||
When connected, some modems recognize in the system as a network card, without any additional settings.
|
Note that when connected, the modem must be recognized in the system as a network card, without any additional settings.
|
||||||
|
|
||||||
4G modem example: *USB 4G Huawei E3372H*
|
4G modem example: *USB 4G Huawei E3372H*
|
||||||
|
|
||||||
<img src="../assets/4g/huawei.jpg" width=300 class="zoom center border">
|
<img src="../assets/4g/huawei.jpg" width=300 class="zoom center border">
|
||||||
|
|
||||||
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:
|
> **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.
|
||||||
|
|
||||||
```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.
|
Create the VPN network keys to connect Raspberry Pi and the ground station.
|
||||||
|
|
||||||
@@ -75,14 +40,8 @@ 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** 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
|
## 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.
|
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.
|
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.
|
||||||
@@ -123,8 +82,7 @@ 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:
|
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
|
```bash
|
||||||
sudo apt update
|
sudo apt-get install gst-rtsp-launch
|
||||||
sudo apt install gst-rtsp-launch
|
|
||||||
```
|
```
|
||||||
|
|
||||||
To start the transfer of images, you must enter the appropriate command line:
|
To start the transfer of images, you must enter the appropriate command line:
|
||||||
@@ -132,15 +90,12 @@ To start the transfer of images, you must enter the appropriate command line:
|
|||||||
<a id="command_line"></a>
|
<a id="command_line"></a>
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
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 )"
|
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 )"
|
||||||
```
|
```
|
||||||
|
|
||||||
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).
|
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).
|
||||||
|
|
||||||
> **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`.
|
Make sure the stream is received and shown in QGroundControl.
|
||||||
> 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">
|
<img src="../assets/4g/video_stream.png" width=300 class="zoom center border">
|
||||||
|
|
||||||
@@ -158,7 +113,7 @@ In order to run the file, you have to mark it as executable.
|
|||||||
chmod a+x script_name.sh
|
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
|
```bash
|
||||||
sudo nano /etc/systemd/system/qgc_video.service
|
sudo nano /etc/systemd/system/qgc_video.service
|
||||||
|
|||||||
@@ -34,7 +34,7 @@
|
|||||||
* [Marker detection](aruco_marker.md)
|
* [Marker detection](aruco_marker.md)
|
||||||
* [Map-based navigation](aruco_map.md)
|
* [Map-based navigation](aruco_map.md)
|
||||||
* [Optical Flow](optical_flow.md)
|
* [Optical Flow](optical_flow.md)
|
||||||
* [Autonomous flight (OFFBOARD)](simple_offboard.md)
|
* [Simple OFFBOARD](simple_offboard.md)
|
||||||
* [Coordinate systems (frames)](frames.md)
|
* [Coordinate systems (frames)](frames.md)
|
||||||
* [Code snippets](snippets.md)
|
* [Code snippets](snippets.md)
|
||||||
* [Interfacing with a laser rangefinder](laser.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
|
// If the received byte does not equal a $ character, increase the message_counter variable
|
||||||
else if (message_counter <= 99)
|
else if (message_counter <= 99)
|
||||||
message_counter++;
|
message_counter++;
|
||||||
|
|
||||||
// Write the new received byte to the new position in the incoming_message array
|
// Write the new received byte to the new position in the incoming_message array
|
||||||
incoming_message[message_counter] = read_serial_byte;
|
incoming_message[message_counter] = read_serial_byte;
|
||||||
|
|
||||||
@@ -163,7 +163,7 @@ if (new_line_found == 1) {
|
|||||||
lat_gps_actual /= 10;
|
lat_gps_actual /= 10;
|
||||||
|
|
||||||
// Filter minutes for the GGA line multiplied by 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[34] - 48) * (long)1000000;
|
||||||
lon_gps_actual += ((int)incoming_message[36] - 48) * (long)100000;
|
lon_gps_actual += ((int)incoming_message[36] - 48) * (long)100000;
|
||||||
lon_gps_actual += ((int)incoming_message[37] - 48) * (long)10000;
|
lon_gps_actual += ((int)incoming_message[37] - 48) * (long)10000;
|
||||||
@@ -192,7 +192,7 @@ if (new_line_found == 1) {
|
|||||||
else
|
else
|
||||||
// When flying west of the prime meridian the longiude_east variable will be set to 0
|
// When flying west of the prime meridian the longiude_east variable will be set to 0
|
||||||
longiude_east = 0;
|
longiude_east = 0;
|
||||||
|
|
||||||
// Filter the number of satillites from the GGA line
|
// 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[46] - 48) * (long)10;
|
||||||
number_used_sats += (int)incoming_message[47] - 48;
|
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}
|
### 1.5. Waypoint stabilization {#waypoint-stabilization}
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
if (waypoint_set == 1) {
|
if (waypoint_set == 1) {
|
||||||
//If the waypoints are stored
|
//If the waypoints are stored
|
||||||
|
|
||||||
// Adjust l_lat_waypoint
|
// Adjust l_lat_waypoint
|
||||||
if (l_lat_gps_float_adjust > 1) {
|
if (l_lat_gps_float_adjust > 1) {
|
||||||
l_lat_waypoint++;
|
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;
|
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
|
// Add the new value to the long term avarage value
|
||||||
gps_lon_total_avarage += gps_lon_rotating_mem[gps_rotating_mem_location];
|
gps_lon_total_avarage += gps_lon_rotating_mem[gps_rotating_mem_location];
|
||||||
|
|
||||||
// Increase the rotating memory location
|
// Increase the rotating memory location
|
||||||
gps_rotating_mem_location++;
|
gps_rotating_mem_location++;
|
||||||
if (gps_rotating_mem_location == 35)
|
if (gps_rotating_mem_location == 35)
|
||||||
@@ -613,7 +613,7 @@ void speed_handler(void) {
|
|||||||
// Convert acceleration to G
|
// Convert acceleration to G
|
||||||
speed /= 4096.0;
|
speed /= 4096.0;
|
||||||
// Convert to m/s^2
|
// Convert to m/s^2
|
||||||
speed *= 9.81;
|
speed *= 9.81;
|
||||||
// Multiply by dt to get instant speed in m/ms
|
// Multiply by dt to get instant speed in m/ms
|
||||||
speed *= (millis() - speed_loop_timer);
|
speed *= (millis() - speed_loop_timer);
|
||||||
|
|
||||||
@@ -621,10 +621,10 @@ void speed_handler(void) {
|
|||||||
speed_loop_timer = millis();
|
speed_loop_timer = millis();
|
||||||
|
|
||||||
// Convert to m/s
|
// Convert to m/s
|
||||||
speed /= 1000.0;
|
speed /= 1000.0;
|
||||||
// Convert to km/h
|
// Convert to km/h
|
||||||
speed *= 3.6;
|
speed *= 3.6;
|
||||||
|
|
||||||
// Accumulate instantaneous speed
|
// Accumulate instantaneous speed
|
||||||
speed_accumulator += speed;
|
speed_accumulator += speed;
|
||||||
|
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ marker_id marker_size x y z z_angle y_angle x_angle
|
|||||||
|
|
||||||
`N_angle` is the angle of rotation along the `N` axis in radians.
|
`N_angle` is the angle of rotation along the `N` axis in radians.
|
||||||
|
|
||||||
|
Файлы карт располагаются в каталоге `~/catkin_ws/src/clover/aruco_pose/map`. Название файла с картой задается в аргументе `map`:
|
||||||
Map files are located at the `~/catkin_ws/src/clover/aruco_pose/map` directory. Map file name is defined in the `map` argument:
|
Map files are located at the `~/catkin_ws/src/clover/aruco_pose/map` directory. Map file name is defined in the `map` argument:
|
||||||
|
|
||||||
```xml
|
```xml
|
||||||
@@ -156,19 +157,13 @@ If the drone's altitude is not stable, try increasing the `MPC_Z_VEL_P` paramete
|
|||||||
|
|
||||||
In order to navigate using markers on the ceiling, mount the onboard camera so that it points up and [adjust the camera frame accordingly](camera_setup.md).
|
In order to navigate using markers on the ceiling, mount the onboard camera so that it points up and [adjust the camera frame accordingly](camera_setup.md).
|
||||||
|
|
||||||
You should also set the `placement` parameter to `ceiling` in `~/catkin_ws/src/clover/clover/launch/aruco.launch`:
|
You should also set the `placement` parameter to `ceilin` in `~/catkin_ws/src/clover/clover/launch/aruco.launch`:
|
||||||
|
|
||||||
```xml
|
```xml
|
||||||
<arg name="placement" default="ceiling"/>
|
<arg name="placement" default="ceiling"/>
|
||||||
```
|
```
|
||||||
|
|
||||||
With such a camera orientation the [Optical Flow](optical_flow.md) technology cannot work, so it should be disabled in the `~/catkin_ws/src/clover/clover/launch/clover.launch` file:
|
This will flip the `aruco_map` frame (making its **<font color=blue>z</font>** axis point downward). Thus, in order to fly 2 metres below ceiling, the `z` argument for the `navigate` service should be set to 2:
|
||||||
|
|
||||||
```xml
|
|
||||||
<arg name="optical_flow" default="false"/>
|
|
||||||
```
|
|
||||||
|
|
||||||
Such setup will flip the `aruco_map` frame (making its **<font color=blue>z</font>** axis point downward). Thus, in order to fly 2 metres below ceiling, the `z` argument for the `navigate` service should be set to 2:
|
|
||||||
|
|
||||||
```python
|
```python
|
||||||
navigate(x=1, y=1.1, z=2, speed=0.5, frame_id='aruco_map')
|
navigate(x=1, y=1.1, z=2, speed=0.5, frame_id='aruco_map')
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ Clover 2 construction kit assembly instruction
|
|||||||
* Side guard ×16.
|
* Side guard ×16.
|
||||||
* Dalprop 5045 plastic propeller ×4.
|
* Dalprop 5045 plastic propeller ×4.
|
||||||
* Racerstar BR2205 2300kV brushless motor ×4.
|
* Racerstar BR2205 2300kV brushless motor ×4.
|
||||||
* Speed controllers ESC, DYS XSD20A ×4.
|
* Speed controllers ESC, DYS XSD20А ×4.
|
||||||
* Power controller XT60 pin ×1.
|
* Power controller XT60 pin ×1.
|
||||||
* Power connector XT60 socket ×1.
|
* Power connector XT60 socket ×1.
|
||||||
* Three-wire female-female flat cable ×2.
|
* Three-wire female-female flat cable ×2.
|
||||||
@@ -40,9 +40,9 @@ Clover 2 construction kit assembly instruction
|
|||||||
|
|
||||||
* 6 mm plastic legs ×28.
|
* 6 mm plastic legs ×28.
|
||||||
* 30 mm plastic legs ×32.
|
* 30 mm plastic legs ×32.
|
||||||
* M3x8 screws ×48.
|
* М3х8 screws ×48.
|
||||||
* M3x12 screws ×24.
|
* М3х12 screws ×24.
|
||||||
* M3x16 screws ×40.
|
* М3х16 screws ×40.
|
||||||
* Plastic nuts ×8.
|
* Plastic nuts ×8.
|
||||||
* Metal nuts ×48.
|
* Metal nuts ×48.
|
||||||
* Stickers for the battery compartment ×8.
|
* Stickers for the battery compartment ×8.
|
||||||
@@ -117,7 +117,7 @@ Tin wires
|
|||||||
#### Fix the motor on the mount
|
#### Fix the motor on the mount
|
||||||
|
|
||||||
* Install the motor on the engraved side of the mount.
|
* Install the motor on the engraved side of the mount.
|
||||||
* Attach the motors to the mounts with M3x8 screws using a screwdriver.
|
* Attach the motors to the mounts with М3х8 screws using a screwdriver.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
@@ -248,14 +248,14 @@ Solder the 5V connector, taking into account the polarity on the contact pads.
|
|||||||
|
|
||||||
### Installation of the power distribution board
|
### Installation of the power distribution board
|
||||||
|
|
||||||
* Fix the power board to the frame with M3x8 screws and plastic nuts. 
|
* Fix the power board to the frame with М3х8 screws and plastic nuts. 
|
||||||
> **IMPORTANT** The white arrow on the BeeRotor board points towards the fore cutout.
|
> **IMPORTANT** The white arrow on the BeeRotor board points towards the fore cutout.
|
||||||

|

|
||||||
|
|
||||||
#### Installation of elements
|
#### Installation of elements
|
||||||
|
|
||||||
1. Install the nuts into plastic holders. 
|
1. Install the nuts into plastic holders. 
|
||||||
2. Fix the motor mounts to the frame with M3x16 screws.
|
2. Fix the motor mounts to the frame with М3х16 screws.
|
||||||
* The mounts are installed above the frame.
|
* The mounts are installed above the frame.
|
||||||
* Plastic holders are installed beneath the frame. 
|
* Plastic holders are installed beneath the frame. 
|
||||||
3. Arrangement of motors. Check arrangement of the motors (the motors with black nuts should be in the top left and lower right corners). 
|
3. Arrangement of motors. Check arrangement of the motors (the motors with black nuts should be in the top left and lower right corners). 
|
||||||
@@ -309,12 +309,12 @@ IMPORTANT NOTE about polarity
|
|||||||
|
|
||||||
### Installation of the radio receiver
|
### Installation of the radio receiver
|
||||||
|
|
||||||
1. Install the 30 mm plastic legs on the frame with M3x8 screws.
|
1. Install the 30 mm plastic legs on the frame with М3х8 screws.
|
||||||
2. Pass the 5V power connector through the slit. 
|
2. Pass the 5V power connector through the slit. 
|
||||||
3. Attach the receiver to the bottom of the additional frame using double-sided adhesive tape and following the orientation of the engraved arrow. The antennas are to be pointing forward. 
|
3. Attach the receiver to the bottom of the additional frame using double-sided adhesive tape and following the orientation of the engraved arrow. The antennas are to be pointing forward. 
|
||||||
4. Install the 3-wire flat cable into the PPM / CH1 channel. 
|
4. Install the 3-wire flat cable into the PPM / CH1 channel. 
|
||||||
5. Pass them through the slit to the 5 V connector.
|
5. Pass them through the slit to the 5 V connector.
|
||||||
6. Screw the bottom an additional frame to the legs on the central frame with M3x8 screws. 
|
6. Screw the bottom an additional frame to the legs on the central frame with М3х8 screws. 
|
||||||
> **IMPORTANT** The directions of the arrows on the power supply board and the additional frame should coincide
|
> **IMPORTANT** The directions of the arrows on the power supply board and the additional frame should coincide
|
||||||
|
|
||||||
### Installation of the flight controller
|
### Installation of the flight controller
|
||||||
@@ -346,22 +346,22 @@ should be increased up to 4 – 5.
|
|||||||
|
|
||||||
### Installation of guard
|
### Installation of guard
|
||||||
|
|
||||||
1. Attach the lower guard with M3x16 screw to the motor mounts of the frame. 
|
1. Attach the lower guard with М3х16 screw to the motor mounts of the frame. 
|
||||||
2. Attach the feet to the plastic holders with M3x16 screws. 
|
2. Attach the feet to the plastic holders with М3х16 screws. 
|
||||||
3. Attach the 30 mm long legs to the holes of the lower guard with M3x12 screw. 
|
3. Attach the 30 mm long legs to the holes of the lower guard with М3х12 screw. 
|
||||||
4. Attach the top guard with M3x12 screws. 
|
4. Attach the top guard with М3х12 screws. 
|
||||||
|
|
||||||
### Installation of the battery compartment
|
### Installation of the battery compartment
|
||||||
|
|
||||||
Requires the following components:
|
Requires the following components:
|
||||||
|
|
||||||
* M3x12 screws (4 pcs)
|
* М3х12 screws (4 pcs)
|
||||||
* M3 nuts (4 pcs)
|
* M3 nuts (4 pcs)
|
||||||
* Additional frame (1 pc)
|
* Additional frame (1 pc)
|
||||||
* Battery compartment (1 pc)
|
* Battery compartment (1 pc)
|
||||||
|
|
||||||
1. Attach the battery compartment on top of the additional frame with M3x12 screws and nuts. 
|
1. Attach the battery compartment on top of the additional frame with М3х12 screws and nuts. 
|
||||||
2. Attach the top additional frame to the legs with M3x8 screws. 
|
2. Attach the top additional frame to the legs with М3х8 screws. 
|
||||||
3. Install the battery into the battery compartment.
|
3. Install the battery into the battery compartment.
|
||||||
|
|
||||||
### Installation of antennas
|
### Installation of antennas
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ TODO
|
|||||||
## Motor installation
|
## Motor installation
|
||||||
|
|
||||||
1. Unpack the motors.
|
1. Unpack the motors.
|
||||||
2. Attach a motor to the motor mount with M3x6 hex screws (the shortest screws supplied with the motors).
|
2. Attach a motor to the motor mount with М3х6 hex screws (the shortest screws supplied with the motors).
|
||||||
|
|
||||||
A hex wrench is included.
|
A hex wrench is included.
|
||||||
|
|
||||||
@@ -31,7 +31,7 @@ TODO
|
|||||||
|
|
||||||
The choice is yours to use a long screw or pliers.
|
The choice is yours to use a long screw or pliers.
|
||||||
|
|
||||||
4. Secure the motor mount, the lower motor mount guard and the holder with M3x12 screws, using a Phillips screwdriver.
|
4. Secure the motor mount, the lower motor mount guard and the holder with М3х12 screws, using a Phillips screwdriver.
|
||||||
5. Using a clamp connect the motor mount and its bottom guard.
|
5. Using a clamp connect the motor mount and its bottom guard.
|
||||||
|
|
||||||
Cut the remaining part of the clamp (cable tie) with scissors.
|
Cut the remaining part of the clamp (cable tie) with scissors.
|
||||||
@@ -40,9 +40,9 @@ TODO
|
|||||||
|
|
||||||
## Frame elements installation
|
## Frame elements installation
|
||||||
|
|
||||||
1. Insert the M3 plastic nuts (4 pcs) for mounting the PDB on the frame with M3x8 screws.
|
1. Insert the M3 plastic nuts (4 pcs) for mounting the PDB on the frame with М3х8 screws.
|
||||||
2. Install 6 mm legs (4 pcs) for attaching the Raspberry Pi to the frame with M3x8 screws.
|
2. Install 6 mm legs (4 pcs) for attaching the Raspberry Pi to the frame with М3х8 screws.
|
||||||
3. Attach the assembled unit to the frame with M3x16 screws, complying with the layout.
|
3. Attach the assembled unit to the frame with М3х16 screws, complying with the layout.
|
||||||
4. Install the frame for the LED strip, using the slots in the leg holders.
|
4. Install the frame for the LED strip, using the slots in the leg holders.
|
||||||
|
|
||||||

|

|
||||||
@@ -150,9 +150,9 @@ article [remote faults](radioerrors.md).
|
|||||||
## Installation and connection of the Pixracer flight controller
|
## Installation and connection of the Pixracer flight controller
|
||||||
|
|
||||||
1. Install the Pixracer flight controller on double-sided 3M adhesive tape (2 – 3 layers).
|
1. Install the Pixracer flight controller on double-sided 3M adhesive tape (2 – 3 layers).
|
||||||
The flight controller may also be removed from the housing and firmly mounted on the M3x6 leg.
|
The flight controller may also be removed from the housing and firmly mounted on the М3х6 leg.
|
||||||
|
|
||||||
2. Install 40 mm legs using M3x8 screws.
|
2. Install 40 mm legs using М3х8 screws.
|
||||||
|
|
||||||
Connect the POWER connector.
|
Connect the POWER connector.
|
||||||
|
|
||||||
@@ -184,14 +184,14 @@ article [remote faults](radioerrors.md).
|
|||||||
|
|
||||||
4. Assembling the mount for the RPi camera.
|
4. Assembling the mount for the RPi camera.
|
||||||
|
|
||||||
Use an M3x16 screw and an M3 nut
|
Use an М3х16 screw and an M3 nut
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## Arduino and FlySky radio receiver installation
|
## Arduino and FlySky radio receiver installation
|
||||||
|
|
||||||
1. Solder Arduino Nano micro-controller pins to its board.
|
1. Solder Arduino Nano micro-controller pins to its board.
|
||||||
2. Install the micro-controller into a special mount, and attach to the lower deck using M3x16 screws (4 pcs).
|
2. Install the micro-controller into a special mount, and attach to the lower deck using М3х16 screws (4 pcs).
|
||||||
3. Using double-sided tape, attach the receiver as shown in the picture.
|
3. Using double-sided tape, attach the receiver as shown in the picture.
|
||||||
4. Connect the ribbon cable from the radio receiver to Pixracer as shown in the picture.
|
4. Connect the ribbon cable from the radio receiver to Pixracer as shown in the picture.
|
||||||
|
|
||||||
@@ -204,12 +204,12 @@ article [remote faults](radioerrors.md).
|
|||||||
|
|
||||||
## RPi camera installation
|
## RPi camera installation
|
||||||
|
|
||||||
1. Attach the mount for the RPi camera assembly to the lower deck with M3x12 screws (2 pcs.)
|
1. Attach the mount for the RPi camera assembly to the lower deck with М3х12 screws (2 pcs.)
|
||||||
2. Connect the ribbon cable to the RPi camera.
|
2. Connect the ribbon cable to the RPi camera.
|
||||||
3. Install the camera into the mount, secure it with M2 self-tappers.
|
3. Install the camera into the mount, secure it with M2 self-tappers.
|
||||||
4. Attach Raspberry with 30 mm legs (4 pcs.).
|
4. Attach Raspberry with 30 mm legs (4 pcs.).
|
||||||
|
|
||||||
Attach the lower deck assembly to the rack with M3x8 screws (4 pcs.)
|
Attach the lower deck assembly to the rack with М3х8 screws (4 pcs.)
|
||||||
|
|
||||||
5. Install the legs into the mounts (4 pcs).
|
5. Install the legs into the mounts (4 pcs).
|
||||||
|
|
||||||
@@ -217,11 +217,11 @@ article [remote faults](radioerrors.md).
|
|||||||
|
|
||||||
## Installation of the remaining structural elements
|
## Installation of the remaining structural elements
|
||||||
|
|
||||||
1. Install the bottom guard using M3x12 screws (8 units) and the 30 mm legs (8 pcs).
|
1. Install the bottom guard using М3х12 screws (8 units) and the 30 mm legs (8 pcs).
|
||||||
2. Install the top guard using M3x12 screws (8 pcs).
|
2. Install the top guard using М3х12 screws (8 pcs).
|
||||||
3. Insert the strap into the upper deck for attaching the battery.
|
3. Insert the strap into the upper deck for attaching the battery.
|
||||||
|
|
||||||
Secure the upper deck with M3x8 screws (4 pcs.)
|
Secure the upper deck with М3х8 screws (4 pcs.)
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ Dimensional drawing – [clover-4.2.pdf](https://github.com/CopterExpress/clove
|
|||||||
<img src="../assets/assembling_clever4_2/frame_2.png" width=300 class="zoom border">
|
<img src="../assets/assembling_clever4_2/frame_2.png" width=300 class="zoom border">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
2. Install 2 aluminum 15mm posts on the center holes in the main deck and fix them with the M3x8 screws.
|
2. Install 2 15mm posts on the center holes in the main deck and fix them with the M3x8 screws.
|
||||||
|
|
||||||
<img src="../assets/assembling_clever4_2/frame_3.png" width=300 class="zoom border center">
|
<img src="../assets/assembling_clever4_2/frame_3.png" width=300 class="zoom border center">
|
||||||
|
|
||||||
@@ -146,7 +146,7 @@ Install the damper struts, fix COEX Pix on them with nylon nuts.
|
|||||||
|
|
||||||
<img src="../assets/assembling_clever4_2/fc_connection_2.png" width=300 class="zoom border center">
|
<img src="../assets/assembling_clever4_2/fc_connection_2.png" width=300 class="zoom border center">
|
||||||
|
|
||||||
3. Install 40mm aluminum racks on the M3x10 screws.
|
3. Install 40mm aluminum racks on the M3x12 screws.
|
||||||
|
|
||||||
<div class="image-group">
|
<div class="image-group">
|
||||||
<img src="../assets/assembling_clever4_2/fc_connection_3.png" width=300 class="zoom border">
|
<img src="../assets/assembling_clever4_2/fc_connection_3.png" width=300 class="zoom border">
|
||||||
@@ -162,7 +162,7 @@ Install the damper struts, fix COEX Pix on them with nylon nuts.
|
|||||||
<img src="../assets/assembling_clever4_2/fc_connection_6.png" width=300 class="zoom border">
|
<img src="../assets/assembling_clever4_2/fc_connection_6.png" width=300 class="zoom border">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
2. On a mounting deck, install M2.5x6 mm racks and M3x30 mm racks, fasten them with the M2.5x4 and M3x10 bolts, respectively.
|
2. On a mounting deck, install 6 mm racks and 30 mm racks, fasten them with the M3x5 and M3x12 bolts, respectively.
|
||||||
|
|
||||||
<img src="../assets/assembling_clever4_2/raspberry_1.png" width=300 class="zoom border center">
|
<img src="../assets/assembling_clever4_2/raspberry_1.png" width=300 class="zoom border center">
|
||||||
|
|
||||||
@@ -170,7 +170,7 @@ Install the damper struts, fix COEX Pix on them with nylon nuts.
|
|||||||
|
|
||||||
<img src="../assets/assembling_clever4_2/raspberry_2.png" width=300 class="zoom border center">
|
<img src="../assets/assembling_clever4_2/raspberry_2.png" width=300 class="zoom border center">
|
||||||
|
|
||||||
4. Install the Raspberry Pi circuit board and fix with M2.5x4 bolts.
|
4. Install the Raspberry Pi circuit board and fix with nylon nuts.
|
||||||
|
|
||||||
<img src="../assets/assembling_clever4_2/raspberry_3.png" width=300 class="zoom border center">
|
<img src="../assets/assembling_clever4_2/raspberry_3.png" width=300 class="zoom border center">
|
||||||
|
|
||||||
@@ -220,25 +220,25 @@ Install the damper struts, fix COEX Pix on them with nylon nuts.
|
|||||||
|
|
||||||
<img src="../assets/assembling_clever4_2/led_4.png" width=300 class="zoom border center">
|
<img src="../assets/assembling_clever4_2/led_4.png" width=300 class="zoom border center">
|
||||||
|
|
||||||
5. Behind fasten the legs with self-locking nuts and M3x10 screws.
|
5. Behind fasten the legs with self-locking nuts and M3x8 screws.
|
||||||
|
|
||||||
<img src="../assets/assembling_clever4_2/led_5.png" width=300 class="zoom border center">
|
<img src="../assets/assembling_clever4_2/led_5.png" width=300 class="zoom border center">
|
||||||
|
|
||||||
6. Connect the LED strip power (red, black cables) to the short JST connector on the PDB.
|
6. Connect the LED strip power (red, black cables) to the short JST connector on the PDB.
|
||||||
|
|
||||||
<img src="../assets/assembling_soldering_clever_4/led_7.png" width=300 class="zoom border center">
|
<img src="../assets/assembling_clever4_2/led_5.png" width=300 class="zoom border center">
|
||||||
|
|
||||||
7. Connect the signal output of the LED strip (white cable) to Raspberry Ri, to pin *GPIO21*.
|
7. Connect the signal output of the LED strip (white cable) to Raspberry Ri, to pin *GPIO21*.
|
||||||
|
|
||||||
<img src="../assets/assembling_soldering_clever_4/led_8.png" width=300 class="zoom border center">
|
<img src="../assets/assembling_clever4_2/led_6.png" width=300 class="zoom border center">
|
||||||
|
|
||||||
## Installing guard
|
## Installing guard
|
||||||
|
|
||||||
1. Assemble the lower level of guard with 40mm racks and M3x10 screws.
|
1. Assemble the lower level of guard with 40mm racks and M3x12 screws.
|
||||||
|
|
||||||
<img src="../assets/assembling_clever4_2/guard_1.png" width=300 class="zoom border center">
|
<img src="../assets/assembling_clever4_2/guard_1.png" width=300 class="zoom border center">
|
||||||
|
|
||||||
2. Assemble the top level of protection with the M3x10 screws.
|
2. Assemble the top level of protection with the M3x12 screws.
|
||||||
|
|
||||||
<img src="../assets/assembling_clever4_2/guard_2.png" width=300 class="zoom border center">
|
<img src="../assets/assembling_clever4_2/guard_2.png" width=300 class="zoom border center">
|
||||||
|
|
||||||
@@ -246,7 +246,7 @@ Install the damper struts, fix COEX Pix on them with nylon nuts.
|
|||||||
|
|
||||||
<img src="../assets/assembling_clever4_2/guard_3.png" width=300 class="zoom border center">
|
<img src="../assets/assembling_clever4_2/guard_3.png" width=300 class="zoom border center">
|
||||||
|
|
||||||
4. Establish protection and fix on beams by means of self-locking nuts and M3x10 screws.
|
4. Establish protection and fix on beams by means of self-locking nuts and M3x8 screws.
|
||||||
|
|
||||||
<img src="../assets/assembling_clever4_2/guard_4.png" width=300 class="zoom border center">
|
<img src="../assets/assembling_clever4_2/guard_4.png" width=300 class="zoom border center">
|
||||||
|
|
||||||
|
|||||||