Compare commits
123 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
72b343575f | ||
|
|
0849413fe2 | ||
|
|
2cab14c52f | ||
|
|
c20957cbf1 | ||
|
|
e9f892466f | ||
|
|
91061cc9f1 | ||
|
|
9db0c44177 | ||
|
|
5b3c3f0722 | ||
|
|
84b1318f3d | ||
|
|
955011e812 | ||
|
|
2561e8e6cb | ||
|
|
d1209fd064 | ||
|
|
e68fac8aad | ||
|
|
4f64fdf2e4 | ||
|
|
b77d4ed045 | ||
|
|
e2b8cb4be2 | ||
|
|
8db8075f15 | ||
|
|
578728b3a9 | ||
|
|
7154f5afc2 | ||
|
|
48fd45ea9a | ||
|
|
12ca9c0eb9 | ||
|
|
784ce35080 | ||
|
|
6c42c522ce | ||
|
|
b36d69b54f | ||
|
|
0056bb1810 | ||
|
|
37ec19a19f | ||
|
|
ea5151db51 | ||
|
|
bc032e5afb | ||
|
|
ed619935ce | ||
|
|
3b3b5b6a89 | ||
|
|
4190353569 | ||
|
|
7c2e020a89 | ||
|
|
6321ef8aa0 | ||
|
|
172890ed13 | ||
|
|
ae1e39dd82 | ||
|
|
241b766bad | ||
|
|
84bbe2e565 | ||
|
|
169680129b | ||
|
|
6541d60d08 | ||
|
|
e3addb9eb0 | ||
|
|
b7d74ef6c9 | ||
|
|
da92aea727 | ||
|
|
0b78c84ac0 | ||
|
|
de2467acb1 | ||
|
|
3d6b8b6a10 | ||
|
|
b6f1ca5d20 | ||
|
|
850b49b2b6 | ||
|
|
f21ba3feb4 | ||
|
|
9c3a97f945 | ||
|
|
293448028a | ||
|
|
b5cd9512ef | ||
|
|
dd74ceb383 | ||
|
|
e217678f7d | ||
|
|
dc06ba1bd2 | ||
|
|
21bbc8a86c | ||
|
|
76ef764143 | ||
|
|
d282098134 | ||
|
|
0f37f19b40 | ||
|
|
e9c3c6ff72 | ||
|
|
7909756046 | ||
|
|
1e8a4841af | ||
|
|
6ec574e193 | ||
|
|
8381aecd50 | ||
|
|
f5eb475660 | ||
|
|
928f4f135e | ||
|
|
8d15de0849 | ||
|
|
826f631b97 | ||
|
|
52b5d7b04e | ||
|
|
455d52007e | ||
|
|
e9e6cabbb9 | ||
|
|
8fcd6e9b9e | ||
|
|
24d3a1df8d | ||
|
|
9784e7bfa1 | ||
|
|
fbad85d87f | ||
|
|
c1ca40187e | ||
|
|
c1179869cd | ||
|
|
2096be5080 | ||
|
|
0c879f2aad | ||
|
|
f34e8b4774 | ||
|
|
be76ea82d7 | ||
|
|
6a8806c476 | ||
|
|
00a76a306e | ||
|
|
f66b53f9cb | ||
|
|
28927246db | ||
|
|
ca5817c3d2 | ||
|
|
7717461631 | ||
|
|
3f352ebc06 | ||
|
|
8c8fe5c40c | ||
|
|
d89e5eada7 | ||
|
|
2ee90e62fc | ||
|
|
848d9dcbe4 | ||
|
|
6d68d06787 | ||
|
|
d18ca32688 | ||
|
|
bf9f7d035f | ||
|
|
1aec5063d6 | ||
|
|
e7eae1c02d | ||
|
|
e3958d7fef | ||
|
|
fb47858010 | ||
|
|
a525714e3a | ||
|
|
29fdbf23af | ||
|
|
6eacb8966a | ||
|
|
d8afb711f0 | ||
|
|
cba12e115e | ||
|
|
bb6a6c81f3 | ||
|
|
d27bbf31bd | ||
|
|
8668295cfe | ||
|
|
535b366bab | ||
|
|
9f6aa7dabd | ||
|
|
f4d00a47af | ||
|
|
0f438235c2 | ||
|
|
e4ad687e28 | ||
|
|
5d58ffd1db | ||
|
|
b2ed1fccc6 | ||
|
|
aa136e7f15 | ||
|
|
9743bcbaaf | ||
|
|
75aed624db | ||
|
|
36a4962bc0 | ||
|
|
2cd3be1139 | ||
|
|
6909ba5819 | ||
|
|
f1783bdd0b | ||
|
|
528be179e6 | ||
|
|
fe588e7af9 | ||
|
|
15551db840 |
14
.github/workflows/build.yml
vendored
@@ -7,13 +7,13 @@ on:
|
|||||||
branches: [ master ]
|
branches: [ master ]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
melodic:
|
# 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:
|
noetic:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
|
|||||||
17
.github/workflows/docs.yml
vendored
@@ -10,6 +10,10 @@ jobs:
|
|||||||
docs:
|
docs:
|
||||||
runs-on: ubuntu-18.04
|
runs-on: ubuntu-18.04
|
||||||
steps:
|
steps:
|
||||||
|
- name: Cancel previous runs
|
||||||
|
uses: styfle/cancel-workflow-action@0.9.1
|
||||||
|
with:
|
||||||
|
access_token: ${{ github.token }}
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- name: Use Node.js
|
- name: Use Node.js
|
||||||
uses: actions/setup-node@v1
|
uses: actions/setup-node@v1
|
||||||
@@ -34,7 +38,11 @@ jobs:
|
|||||||
gitbook install
|
gitbook install
|
||||||
gitbook build
|
gitbook build
|
||||||
- name: Generate PDF
|
- name: Generate PDF
|
||||||
if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }}
|
id: generate-pdf
|
||||||
|
env:
|
||||||
|
GITBOOK_SKIP_PDF: ${{ secrets.GITBOOK_SKIP_PDF }}
|
||||||
|
continue-on-error: ${{ env.GITBOOK_SKIP_PDF != '' }}
|
||||||
|
if: ${{ github.event_name == 'push' }}
|
||||||
run: |
|
run: |
|
||||||
for i in 1 2 3 4; do gitbook pdf ./ _book/clover.pdf && break || sleep 1; done
|
for i in 1 2 3 4; do gitbook pdf ./ _book/clover.pdf && break || sleep 1; done
|
||||||
sudo apt-get -q install ghostscript
|
sudo apt-get -q install ghostscript
|
||||||
@@ -43,6 +51,13 @@ jobs:
|
|||||||
rm _book/clover_ru.pdf && mv _book/clover_ru_compressed.pdf _book/clover_ru.pdf
|
rm _book/clover_ru.pdf && mv _book/clover_ru_compressed.pdf _book/clover_ru.pdf
|
||||||
rm _book/clover_en.pdf && mv _book/clover_en_compressed.pdf _book/clover_en.pdf
|
rm _book/clover_en.pdf && mv _book/clover_en_compressed.pdf _book/clover_en.pdf
|
||||||
ls -lah _book/clover*.pdf
|
ls -lah _book/clover*.pdf
|
||||||
|
echo '::set-output name=GITBOOK_PDF_OK::1'
|
||||||
|
- name: Download older PDFs
|
||||||
|
if: ${{ !steps.generate-pdf.outputs.GITBOOK_PDF_OK }}
|
||||||
|
run: |
|
||||||
|
rm _book/clover*.pdf
|
||||||
|
wget --no-verbose https://clover.coex.tech/clover_ru.pdf -P _book/
|
||||||
|
wget --no-verbose https://clover.coex.tech/clover_en.pdf -P _book/
|
||||||
- name: Deploy
|
- name: Deploy
|
||||||
uses: JamesIves/github-pages-deploy-action@4.1.3
|
uses: JamesIves/github-pages-deploy-action@4.1.3
|
||||||
if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }}
|
if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }}
|
||||||
|
|||||||
3
.gitignore
vendored
@@ -7,3 +7,6 @@ package-lock.json
|
|||||||
clover_blocks/programs/*.*
|
clover_blocks/programs/*.*
|
||||||
!clover_blocks/programs/examples/*
|
!clover_blocks/programs/examples/*
|
||||||
/.vscode/
|
/.vscode/
|
||||||
|
docs/.vuepress/.cache/
|
||||||
|
docs/.vuepress/.temp/
|
||||||
|
docs/.vuepress/dist
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<?xml version="1.0"?>
|
<?xml version="1.0"?>
|
||||||
<package format="2">
|
<package format="2">
|
||||||
<name>aruco_pose</name>
|
<name>aruco_pose</name>
|
||||||
<version>0.21.1</version>
|
<version>0.23.0</version>
|
||||||
<description>Positioning with ArUco markers</description>
|
<description>Positioning with ArUco markers</description>
|
||||||
|
|
||||||
<maintainer email="okalachev@gmail.com">Oleg Kalachev</maintainer>
|
<maintainer email="okalachev@gmail.com">Oleg Kalachev</maintainer>
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ Options:
|
|||||||
-o <filename> Output map file name in the 'map' subdirectory of aruco_pose package
|
-o <filename> Output map file name in the 'map' subdirectory of aruco_pose package
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
rosrun aruco_pose genmap.py 0.33 2 4 1 1 0 > $(catkin_find aruco_pose map)/test_map.txt
|
rosrun aruco_pose genmap.py 0.33 2 4 1 1 0 -o test_map.txt
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ echo_stamp "Installing OpenCV 4.2-compatible ROS packages"
|
|||||||
apt install -y --no-install-recommends \
|
apt install -y --no-install-recommends \
|
||||||
ros-${ROS_DISTRO}-compressed-image-transport=1.14.0-0buster \
|
ros-${ROS_DISTRO}-compressed-image-transport=1.14.0-0buster \
|
||||||
ros-${ROS_DISTRO}-cv-bridge=1.15.0-0buster \
|
ros-${ROS_DISTRO}-cv-bridge=1.15.0-0buster \
|
||||||
ros-${ROS_DISTRO}-cv-camera=0.5.0-0buster \
|
ros-${ROS_DISTRO}-cv-camera=0.5.1-0buster \
|
||||||
ros-${ROS_DISTRO}-image-publisher=1.15.3-0buster \
|
ros-${ROS_DISTRO}-image-publisher=1.15.3-0buster \
|
||||||
ros-${ROS_DISTRO}-web-video-server=0.2.1-0buster
|
ros-${ROS_DISTRO}-web-video-server=0.2.1-0buster
|
||||||
apt-mark hold \
|
apt-mark hold \
|
||||||
@@ -112,7 +112,7 @@ my_travis_retry pip3 install wheel
|
|||||||
my_travis_retry pip3 install -r /home/pi/catkin_ws/src/clover/clover/requirements.txt
|
my_travis_retry pip3 install -r /home/pi/catkin_ws/src/clover/clover/requirements.txt
|
||||||
source /opt/ros/${ROS_DISTRO}/setup.bash
|
source /opt/ros/${ROS_DISTRO}/setup.bash
|
||||||
# Don't build simulation plugins for actual drone
|
# Don't build simulation plugins for actual drone
|
||||||
catkin_make -j2 -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCATKIN_BLACKLIST_PACKAGES=clover_gazebo_plugins
|
catkin_make -j2 -DCMAKE_BUILD_TYPE=RelWithDebInfo
|
||||||
source devel/setup.bash
|
source devel/setup.bash
|
||||||
|
|
||||||
echo_stamp "Install clever package (for backwards compatibility)"
|
echo_stamp "Install clever package (for backwards compatibility)"
|
||||||
|
|||||||
@@ -138,6 +138,7 @@ echo_stamp "Install and enable Butterfly (web terminal)"
|
|||||||
echo_stamp "Workaround for tornado >= 6.0 breaking butterfly"
|
echo_stamp "Workaround for tornado >= 6.0 breaking butterfly"
|
||||||
export CRYPTOGRAPHY_DONT_BUILD_RUST=1
|
export CRYPTOGRAPHY_DONT_BUILD_RUST=1
|
||||||
my_travis_retry pip3 install cryptography==3.4.6 # https://stackoverflow.com/a/68472128/6850197
|
my_travis_retry pip3 install cryptography==3.4.6 # https://stackoverflow.com/a/68472128/6850197
|
||||||
|
my_travis_retry pip3 install pyOpenSSL==20.0.1
|
||||||
my_travis_retry pip3 install tornado==5.1.1
|
my_travis_retry pip3 install tornado==5.1.1
|
||||||
my_travis_retry pip3 install butterfly
|
my_travis_retry pip3 install butterfly
|
||||||
my_travis_retry pip3 install butterfly[systemd]
|
my_travis_retry pip3 install butterfly[systemd]
|
||||||
|
|||||||
@@ -58,5 +58,9 @@ rosversion rosshow
|
|||||||
rosversion nodelet
|
rosversion nodelet
|
||||||
rosversion image_view
|
rosversion image_view
|
||||||
|
|
||||||
|
# validate some versions
|
||||||
|
[[ $(rosversion cv_camera) == "0.5.1" ]] # patched version with init fix
|
||||||
|
[[ $(rosversion ws281x) == "0.0.12" ]]
|
||||||
|
|
||||||
# validate examples are present
|
# validate examples are present
|
||||||
[[ $(ls /home/pi/examples/*) ]]
|
[[ $(ls /home/pi/examples/*) ]]
|
||||||
|
|||||||
@@ -8,8 +8,11 @@
|
|||||||
|
|
||||||
<!-- For additional help go to https://clover.coex.tech/aruco -->
|
<!-- For additional help go to https://clover.coex.tech/aruco -->
|
||||||
|
|
||||||
|
<arg name="force_init" default="false"/>
|
||||||
|
<arg name="disable" default="false"/> <!-- only force init -->
|
||||||
|
|
||||||
<!-- aruco_detect: detect aruco markers, estimate poses -->
|
<!-- aruco_detect: detect aruco markers, estimate poses -->
|
||||||
<node name="aruco_detect" pkg="nodelet" if="$(arg aruco_detect)" type="nodelet" args="load aruco_pose/aruco_detect main_camera_nodelet_manager" output="screen" clear_params="true" respawn="true">
|
<node name="aruco_detect" pkg="nodelet" if="$(eval aruco_detect and not disable)" type="nodelet" args="load aruco_pose/aruco_detect main_camera_nodelet_manager" output="screen" clear_params="true" respawn="true">
|
||||||
<remap from="image_raw" to="main_camera/image_raw"/>
|
<remap from="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"/>
|
||||||
@@ -26,7 +29,7 @@
|
|||||||
</node>
|
</node>
|
||||||
|
|
||||||
<!-- aruco_map: estimate aruco map pose -->
|
<!-- aruco_map: estimate aruco map pose -->
|
||||||
<node name="aruco_map" pkg="nodelet" type="nodelet" if="$(arg aruco_map)" args="load aruco_pose/aruco_map main_camera_nodelet_manager" output="screen" clear_params="true" respawn="true">
|
<node name="aruco_map" pkg="nodelet" type="nodelet" if="$(eval aruco_map and not disable)" args="load aruco_pose/aruco_map main_camera_nodelet_manager" output="screen" clear_params="true" respawn="true">
|
||||||
<remap from="image_raw" to="main_camera/image_raw"/>
|
<remap from="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="markers" to="aruco_detect/markers"/>
|
<remap from="markers" to="aruco_detect/markers"/>
|
||||||
@@ -41,11 +44,11 @@
|
|||||||
</node>
|
</node>
|
||||||
|
|
||||||
<!-- vpe publisher from aruco markers -->
|
<!-- vpe publisher from aruco markers -->
|
||||||
<node name="vpe_publisher" pkg="clover" type="vpe_publisher" if="$(arg aruco_vpe)" output="screen" clear_params="true">
|
<node name="vpe_publisher" pkg="clover" type="vpe_publisher" if="$(eval aruco_vpe or force_init)" output="screen" clear_params="true">
|
||||||
<remap from="~pose_cov" to="aruco_map/pose"/>
|
<remap from="~pose_cov" to="aruco_map/pose" if="$(arg aruco_vpe)"/>
|
||||||
<remap from="~vpe" to="mavros/vision_pose/pose"/>
|
<remap from="~vpe" to="mavros/vision_pose/pose"/>
|
||||||
<param name="frame_id" value="aruco_map_detected"/>
|
<param name="frame_id" value="aruco_map_detected" if="$(arg aruco_vpe)"/>
|
||||||
<param name="publish_zero" value="true"/>
|
<param name="force_init" value="$(arg force_init)"/>
|
||||||
<param name="offset_frame_id" value="aruco_map"/>
|
<param name="offset_frame_id" value="aruco_map"/>
|
||||||
</node>
|
</node>
|
||||||
</launch>
|
</launch>
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
<arg name="led" default="true"/>
|
<arg name="led" default="true"/>
|
||||||
<arg name="blocks" default="false"/>
|
<arg name="blocks" default="false"/>
|
||||||
<arg name="rc" default="false"/>
|
<arg name="rc" default="false"/>
|
||||||
|
<arg name="force_init" value="true"/> <!-- force estimator to init by publishing zero pose -->
|
||||||
|
|
||||||
<arg name="simulator" default="false"/> <!-- flag that we are operating on a simulated drone -->
|
<arg name="simulator" default="false"/> <!-- flag that we are operating on a simulated drone -->
|
||||||
|
|
||||||
@@ -33,7 +34,10 @@
|
|||||||
</node>
|
</node>
|
||||||
|
|
||||||
<!-- aruco markers -->
|
<!-- aruco markers -->
|
||||||
<include file="$(find clover)/launch/aruco.launch" if="$(arg aruco)"/>
|
<include file="$(find clover)/launch/aruco.launch" if="$(eval aruco or force_init)">
|
||||||
|
<arg name="force_init" value="$(arg force_init)"/>
|
||||||
|
<arg name="disable" value="$(eval not aruco)"/>
|
||||||
|
</include>
|
||||||
|
|
||||||
<!-- optical flow -->
|
<!-- optical flow -->
|
||||||
<node pkg="nodelet" type="nodelet" name="optical_flow" args="load clover/optical_flow main_camera_nodelet_manager" if="$(arg optical_flow)" clear_params="true" output="screen" respawn="true">
|
<node pkg="nodelet" type="nodelet" name="optical_flow" args="load clover/optical_flow main_camera_nodelet_manager" if="$(arg optical_flow)" clear_params="true" output="screen" respawn="true">
|
||||||
@@ -47,9 +51,6 @@
|
|||||||
|
|
||||||
<!-- simplified offboard control -->
|
<!-- simplified offboard control -->
|
||||||
<node name="simple_offboard" pkg="clover" type="simple_offboard" output="screen" clear_params="true">
|
<node name="simple_offboard" pkg="clover" type="simple_offboard" output="screen" clear_params="true">
|
||||||
<param name="reference_frames/body" value="map"/>
|
|
||||||
<param name="reference_frames/base_link" value="map"/>
|
|
||||||
<param name="reference_frames/navigate_target" value="map"/>
|
|
||||||
<param name="reference_frames/main_camera_optical" value="map"/>
|
<param name="reference_frames/main_camera_optical" value="map"/>
|
||||||
</node>
|
</node>
|
||||||
|
|
||||||
|
|||||||
@@ -39,7 +39,7 @@
|
|||||||
<rosparam command="load" file="$(find clover)/launch/mavros_config.yaml"/>
|
<rosparam command="load" file="$(find clover)/launch/mavros_config.yaml"/>
|
||||||
|
|
||||||
<!-- remap rangefinder -->
|
<!-- remap rangefinder -->
|
||||||
<remap from="mavros/distance_sensor/rangefinder_sub" to="rangefinder/range"/>
|
<remap from="mavros/distance_sensor/rangefinder_sub" to="$(arg distance_sensor_remap)" if="$(eval bool(distance_sensor_remap))"/>
|
||||||
|
|
||||||
<rosparam param="plugin_whitelist">
|
<rosparam param="plugin_whitelist">
|
||||||
- altitude
|
- altitude
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
# Config file for mavros
|
# Config file for mavros
|
||||||
# Based on https://raw.githubusercontent.com/mavlink/mavros/master/mavros/launch/px4_config.yaml
|
# Based on https://raw.githubusercontent.com/mavlink/mavros/master/mavros/launch/px4_config.yaml
|
||||||
|
|
||||||
startup_px4_usb_quirk: true
|
startup_px4_usb_quirk: false
|
||||||
|
|
||||||
conn:
|
conn:
|
||||||
heartbeat_rate: 1.0 # send hertbeat rate in Hertz
|
heartbeat_rate: 1.0 # send heartbeat rate in Hertz
|
||||||
timeout: 10.0 # hertbeat timeout in seconds
|
timeout: 10.0 # heartbeat timeout in seconds
|
||||||
timesync_rate: 10.0 # TIMESYNC rate in Hertz (feature disabled if 0.0)
|
timesync_rate: 10.0 # TIMESYNC rate in Hertz (feature disabled if 0.0)
|
||||||
system_time_rate: 1.0 # send system time to FCU rate in Hertz (disabled if 0.0)
|
system_time_rate: 1.0 # send system time to FCU rate in Hertz (disabled if 0.0)
|
||||||
|
|
||||||
@@ -13,6 +13,7 @@ time:
|
|||||||
time_ref_source: "fcu" # time_reference source
|
time_ref_source: "fcu" # time_reference source
|
||||||
timesync_mode: MAVLINK
|
timesync_mode: MAVLINK
|
||||||
timesync_avg_alpha: 0.6 # timesync averaging factor
|
timesync_avg_alpha: 0.6 # timesync averaging factor
|
||||||
|
publish_sim_time: false # don't publish /clock
|
||||||
|
|
||||||
global_position:
|
global_position:
|
||||||
frame_id: "map" # origin frame
|
frame_id: "map" # origin frame
|
||||||
@@ -77,6 +78,9 @@ distance_sensor:
|
|||||||
field_of_view: 0.5
|
field_of_view: 0.5
|
||||||
rangefinder_sub:
|
rangefinder_sub:
|
||||||
subscriber: true
|
subscriber: true
|
||||||
|
id: 1
|
||||||
|
orientation: PITCH_270
|
||||||
|
covariance: 1 # cm
|
||||||
|
|
||||||
# fake_gps
|
# fake_gps
|
||||||
fake_gps:
|
fake_gps:
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<?xml version="1.0"?>
|
<?xml version="1.0"?>
|
||||||
<package format="3">
|
<package format="3">
|
||||||
<name>clover</name>
|
<name>clover</name>
|
||||||
<version>0.21.1</version>
|
<version>0.23.0</version>
|
||||||
<description>The Clover package</description>
|
<description>The Clover package</description>
|
||||||
|
|
||||||
<maintainer email="okalachev@gmail.com">Oleg Kalachev</maintainer>
|
<maintainer email="okalachev@gmail.com">Oleg Kalachev</maintainer>
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ private:
|
|||||||
std::unique_ptr<tf2_ros::Buffer> tf_buffer_;
|
std::unique_ptr<tf2_ros::Buffer> tf_buffer_;
|
||||||
std::unique_ptr<tf2_ros::TransformListener> tf_listener_;
|
std::unique_ptr<tf2_ros::TransformListener> tf_listener_;
|
||||||
bool calc_flow_gyro_;
|
bool calc_flow_gyro_;
|
||||||
|
float flow_gyro_default_;
|
||||||
|
|
||||||
void onInit()
|
void onInit()
|
||||||
{
|
{
|
||||||
@@ -69,6 +70,7 @@ private:
|
|||||||
roi_px_ = nh_priv.param("roi", 128);
|
roi_px_ = nh_priv.param("roi", 128);
|
||||||
roi_rad_ = nh_priv.param("roi_rad", 0.0);
|
roi_rad_ = nh_priv.param("roi_rad", 0.0);
|
||||||
calc_flow_gyro_ = nh_priv.param("calc_flow_gyro", false);
|
calc_flow_gyro_ = nh_priv.param("calc_flow_gyro", false);
|
||||||
|
flow_gyro_default_ = nh_priv.param("flow_gyro_default", NAN);
|
||||||
|
|
||||||
img_pub_ = it_priv.advertise("debug", 1);
|
img_pub_ = it_priv.advertise("debug", 1);
|
||||||
flow_pub_ = nh.advertise<mavros_msgs::OpticalFlowRad>("mavros/px4flow/raw/send", 1);
|
flow_pub_ = nh.advertise<mavros_msgs::OpticalFlowRad>("mavros/px4flow/raw/send", 1);
|
||||||
@@ -194,9 +196,9 @@ private:
|
|||||||
uint32_t integration_time_us = integration_time.toSec() * 1.0e6;
|
uint32_t integration_time_us = integration_time.toSec() * 1.0e6;
|
||||||
|
|
||||||
// Calculate flow gyro
|
// Calculate flow gyro
|
||||||
flow_.integrated_xgyro = NAN;
|
flow_.integrated_xgyro = flow_gyro_default_;
|
||||||
flow_.integrated_ygyro = NAN;
|
flow_.integrated_ygyro = flow_gyro_default_;
|
||||||
flow_.integrated_zgyro = NAN;
|
flow_.integrated_zgyro = flow_gyro_default_;
|
||||||
|
|
||||||
if (calc_flow_gyro_) {
|
if (calc_flow_gyro_) {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ from visualization_msgs.msg import MarkerArray as VisualizationMarkerArray
|
|||||||
import tf.transformations as t
|
import tf.transformations as t
|
||||||
from aruco_pose.msg import MarkerArray
|
from aruco_pose.msg import MarkerArray
|
||||||
from mavros import mavlink
|
from mavros import mavlink
|
||||||
|
import locale
|
||||||
|
|
||||||
|
|
||||||
# TODO: check attitude is present
|
# TODO: check attitude is present
|
||||||
@@ -45,6 +46,8 @@ rospy.init_node('selfcheck')
|
|||||||
|
|
||||||
os.environ['ROSCONSOLE_FORMAT']='[${severity}]: ${message}'
|
os.environ['ROSCONSOLE_FORMAT']='[${severity}]: ${message}'
|
||||||
|
|
||||||
|
# use user's locale to convert numbers, etc
|
||||||
|
locale.setlocale(locale.LC_ALL, '')
|
||||||
|
|
||||||
tf_buffer = tf2_ros.Buffer()
|
tf_buffer = tf2_ros.Buffer()
|
||||||
tf_listener = tf2_ros.TransformListener(tf_buffer)
|
tf_listener = tf2_ros.TransformListener(tf_buffer)
|
||||||
@@ -195,24 +198,27 @@ def check_fcu():
|
|||||||
failure('no connection to the FCU (check wiring)')
|
failure('no connection to the FCU (check wiring)')
|
||||||
return
|
return
|
||||||
|
|
||||||
|
clover_tag = re.compile(r'-cl[oe]ver\.\d+$')
|
||||||
|
clover_fw = False
|
||||||
|
|
||||||
# Make sure the console is available to us
|
# Make sure the console is available to us
|
||||||
mavlink_exec('\n')
|
mavlink_exec('\n')
|
||||||
version_str = mavlink_exec('ver all')
|
version_str = mavlink_exec('ver all')
|
||||||
if version_str == '':
|
if version_str == '':
|
||||||
info('no version data available from SITL')
|
info('no version data available from SITL')
|
||||||
|
|
||||||
r = re.compile(r'^FW (git tag|version): (v?\d\.\d\.\d.*)$')
|
for line in version_str.split('\n'):
|
||||||
is_clover_firmware = False
|
if line.startswith('FW version: '):
|
||||||
for ver_line in version_str.split('\n'):
|
info(line[len('FW version: '):])
|
||||||
match = r.search(ver_line)
|
elif line.startswith('FW git tag: '): # only Clover's firmware
|
||||||
if match is not None:
|
tag = line[len('FW git tag: '):]
|
||||||
field, version = match.groups()
|
clover_fw = clover_tag.search(tag)
|
||||||
info('firmware %s: %s' % (field, version))
|
info(tag)
|
||||||
if 'clover' in version or 'clever' in version:
|
elif line.startswith('HW arch: '):
|
||||||
is_clover_firmware = True
|
info(line[len('HW arch: '):])
|
||||||
|
|
||||||
if not is_clover_firmware:
|
if not clover_fw:
|
||||||
failure('not running Clover PX4 firmware, https://clover.coex.tech/firmware')
|
info('not Clover PX4 firmware, check https://clover.coex.tech/firmware')
|
||||||
|
|
||||||
est = get_param('SYS_MC_EST_GROUP')
|
est = get_param('SYS_MC_EST_GROUP')
|
||||||
if est == 1:
|
if est == 1:
|
||||||
@@ -485,6 +491,12 @@ def check_local_position():
|
|||||||
failure('roll is %.2f deg; place copter horizontally or redo level horizon calib',
|
failure('roll is %.2f deg; place copter horizontally or redo level horizon calib',
|
||||||
math.degrees(roll))
|
math.degrees(roll))
|
||||||
|
|
||||||
|
if not tf_buffer.can_transform('base_link', pose.header.frame_id, rospy.get_rostime(), rospy.Duration(0.5)):
|
||||||
|
failure('can\'t transform from %s to base_link (timeout 0.5 s): is TF enabled?', pose.header.frame_id)
|
||||||
|
|
||||||
|
if not tf_buffer.can_transform('body', pose.header.frame_id, rospy.get_rostime(), rospy.Duration(0.5)):
|
||||||
|
failure('can\'t transform from %s to body (timeout 0.5 s)', pose.header.frame_id)
|
||||||
|
|
||||||
except rospy.ROSException:
|
except rospy.ROSException:
|
||||||
failure('no local position')
|
failure('no local position')
|
||||||
|
|
||||||
@@ -614,7 +626,7 @@ def check_boot_duration():
|
|||||||
output = subprocess.check_output('systemd-analyze').decode()
|
output = subprocess.check_output('systemd-analyze').decode()
|
||||||
r = re.compile(r'([\d\.]+)s\s*$', flags=re.MULTILINE)
|
r = re.compile(r'([\d\.]+)s\s*$', flags=re.MULTILINE)
|
||||||
duration = float(r.search(output).groups()[0])
|
duration = float(r.search(output).groups()[0])
|
||||||
if duration > 15:
|
if duration > 20:
|
||||||
failure('long Raspbian boot duration: %ss (systemd-analyze for analyzing)', duration)
|
failure('long Raspbian boot duration: %ss (systemd-analyze for analyzing)', duration)
|
||||||
|
|
||||||
|
|
||||||
@@ -629,7 +641,7 @@ def check_cpu_usage():
|
|||||||
continue
|
continue
|
||||||
pid, cpu, cmd = process.split('\t')
|
pid, cpu, cmd = process.split('\t')
|
||||||
|
|
||||||
if cmd.strip() not in WHITELIST and float(cpu) > 30:
|
if cmd.strip() not in WHITELIST and locale.atof(cpu) > 30:
|
||||||
failure('high CPU usage (%s%%) detected: %s (PID %s)',
|
failure('high CPU usage (%s%%) detected: %s (PID %s)',
|
||||||
cpu.strip(), cmd.strip(), pid.strip())
|
cpu.strip(), cmd.strip(), pid.strip())
|
||||||
|
|
||||||
@@ -648,13 +660,22 @@ def check_clover_service():
|
|||||||
elif 'failed' in output:
|
elif 'failed' in output:
|
||||||
failure('service failed to run, check your launch-files')
|
failure('service failed to run, check your launch-files')
|
||||||
|
|
||||||
r = re.compile(r'^(.*)\[(FATAL|ERROR)\] \[\d+.\d+\]: (.*?)(\x1b(.*))?$')
|
BLACKLIST = 'Unexpected command 520', 'Time jump detected', 'different index:'
|
||||||
|
|
||||||
|
r = re.compile(r'^(.*)\[(FATAL|ERROR| WARN)\] \[\d+.\d+\]: (.*?)(\x1b(.*))?$')
|
||||||
error_count = OrderedDict()
|
error_count = OrderedDict()
|
||||||
try:
|
try:
|
||||||
for line in open('/tmp/clover.err', 'r'):
|
for line in open('/tmp/clover.err', 'r'):
|
||||||
|
skip = False
|
||||||
|
for substr in BLACKLIST:
|
||||||
|
if substr in line:
|
||||||
|
skip = True
|
||||||
|
if skip:
|
||||||
|
continue
|
||||||
|
|
||||||
node_error = r.search(line)
|
node_error = r.search(line)
|
||||||
if node_error:
|
if node_error:
|
||||||
msg = node_error.groups()[1] + ': ' + node_error.groups()[2]
|
msg = node_error.groups()[1].strip() + ': ' + node_error.groups()[2]
|
||||||
if msg in error_count:
|
if msg in error_count:
|
||||||
error_count[msg] += 1
|
error_count[msg] += 1
|
||||||
else:
|
else:
|
||||||
@@ -725,6 +746,14 @@ def check_network():
|
|||||||
|
|
||||||
@check('RPi health')
|
@check('RPi health')
|
||||||
def check_rpi_health():
|
def check_rpi_health():
|
||||||
|
try:
|
||||||
|
import shutil
|
||||||
|
total, used, free = shutil.disk_usage('/')
|
||||||
|
if free < 1024 * 1024 * 1024:
|
||||||
|
failure('disk space is less than 1 GB; consider removing logs (~/.ros/log/)')
|
||||||
|
except Exception as e:
|
||||||
|
info('could not check the disk free space: %s', str(e))
|
||||||
|
|
||||||
# `vcgencmd get_throttled` output codes taken from
|
# `vcgencmd get_throttled` output codes taken from
|
||||||
# https://github.com/raspberrypi/documentation/blob/JamesH65-patch-vcgencmd-vcdbg-docs/raspbian/applications/vcgencmd.md#get_throttled
|
# https://github.com/raspberrypi/documentation/blob/JamesH65-patch-vcgencmd-vcdbg-docs/raspbian/applications/vcgencmd.md#get_throttled
|
||||||
# TODO: support more base platforms?
|
# TODO: support more base platforms?
|
||||||
|
|||||||
@@ -61,6 +61,7 @@ std::shared_ptr<tf2_ros::TransformBroadcaster> transform_broadcaster;
|
|||||||
std::shared_ptr<tf2_ros::StaticTransformBroadcaster> static_transform_broadcaster;
|
std::shared_ptr<tf2_ros::StaticTransformBroadcaster> static_transform_broadcaster;
|
||||||
|
|
||||||
// Parameters
|
// Parameters
|
||||||
|
string mavros;
|
||||||
string local_frame;
|
string local_frame;
|
||||||
string fcu_frame;
|
string fcu_frame;
|
||||||
ros::Duration transform_timeout;
|
ros::Duration transform_timeout;
|
||||||
@@ -181,6 +182,7 @@ inline bool waitTransform(const string& target, const string& source,
|
|||||||
ros::spinOnce();
|
ros::spinOnce();
|
||||||
r.sleep();
|
r.sleep();
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define TIMEOUT(msg, timeout) (msg.header.stamp.isZero() || (ros::Time::now() - msg.header.stamp > timeout))
|
#define TIMEOUT(msg, timeout) (msg.header.stamp.isZero() || (ros::Time::now() - msg.header.stamp > timeout))
|
||||||
@@ -847,6 +849,7 @@ bool land(std_srvs::Trigger::Request& req, std_srvs::Trigger::Response& res)
|
|||||||
busy = false;
|
busy = false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
@@ -859,8 +862,9 @@ int main(int argc, char **argv)
|
|||||||
static_transform_broadcaster = std::make_shared<tf2_ros::StaticTransformBroadcaster>();
|
static_transform_broadcaster = std::make_shared<tf2_ros::StaticTransformBroadcaster>();
|
||||||
|
|
||||||
// Params
|
// Params
|
||||||
nh.param<string>("mavros/local_position/tf/frame_id", local_frame, "map");
|
nh_priv.param("mavros", mavros, string("mavros")); // for case of using multiple connections
|
||||||
nh.param<string>("mavros/local_position/tf/child_frame_id", fcu_frame, "base_link");
|
nh.param<string>(mavros + "/local_position/tf/frame_id", local_frame, "map");
|
||||||
|
nh.param<string>(mavros + "/local_position/tf/child_frame_id", fcu_frame, "base_link");
|
||||||
nh_priv.param("target_frame", target.child_frame_id, string("navigate_target"));
|
nh_priv.param("target_frame", target.child_frame_id, string("navigate_target"));
|
||||||
nh_priv.param("setpoint", setpoint.child_frame_id, string("setpoint"));
|
nh_priv.param("setpoint", setpoint.child_frame_id, string("setpoint"));
|
||||||
nh_priv.param("auto_release", auto_release, true);
|
nh_priv.param("auto_release", auto_release, true);
|
||||||
@@ -871,6 +875,13 @@ int main(int argc, char **argv)
|
|||||||
nh_priv.param<string>("body_frame", body.child_frame_id, "body");
|
nh_priv.param<string>("body_frame", body.child_frame_id, "body");
|
||||||
nh_priv.getParam("reference_frames", reference_frames);
|
nh_priv.getParam("reference_frames", reference_frames);
|
||||||
|
|
||||||
|
// Default reference frames
|
||||||
|
std::map<string, string> default_reference_frames;
|
||||||
|
default_reference_frames[body.child_frame_id] = local_frame;
|
||||||
|
default_reference_frames[fcu_frame] = local_frame;
|
||||||
|
if (!target.child_frame_id.empty()) default_reference_frames[target.child_frame_id] = local_frame;
|
||||||
|
reference_frames.insert(default_reference_frames.begin(), default_reference_frames.end()); // merge defaults
|
||||||
|
|
||||||
state_timeout = ros::Duration(nh_priv.param("state_timeout", 3.0));
|
state_timeout = ros::Duration(nh_priv.param("state_timeout", 3.0));
|
||||||
local_position_timeout = ros::Duration(nh_priv.param("local_position_timeout", 2.0));
|
local_position_timeout = ros::Duration(nh_priv.param("local_position_timeout", 2.0));
|
||||||
velocity_timeout = ros::Duration(nh_priv.param("velocity_timeout", 2.0));
|
velocity_timeout = ros::Duration(nh_priv.param("velocity_timeout", 2.0));
|
||||||
@@ -885,25 +896,25 @@ int main(int argc, char **argv)
|
|||||||
arming_timeout = ros::Duration(nh_priv.param("arming_timeout", 4.0));
|
arming_timeout = ros::Duration(nh_priv.param("arming_timeout", 4.0));
|
||||||
|
|
||||||
// Service clients
|
// Service clients
|
||||||
arming = nh.serviceClient<mavros_msgs::CommandBool>("mavros/cmd/arming");
|
arming = nh.serviceClient<mavros_msgs::CommandBool>(mavros + "/cmd/arming");
|
||||||
set_mode = nh.serviceClient<mavros_msgs::SetMode>("mavros/set_mode");
|
set_mode = nh.serviceClient<mavros_msgs::SetMode>(mavros + "/set_mode");
|
||||||
|
|
||||||
// Telemetry subscribers
|
// Telemetry subscribers
|
||||||
auto state_sub = nh.subscribe("mavros/state", 1, &handleState);
|
auto state_sub = nh.subscribe(mavros + "/state", 1, &handleState);
|
||||||
auto velocity_sub = nh.subscribe("mavros/local_position/velocity_body", 1, &handleMessage<TwistStamped, velocity>);
|
auto velocity_sub = nh.subscribe(mavros + "/local_position/velocity_body", 1, &handleMessage<TwistStamped, velocity>);
|
||||||
auto global_position_sub = nh.subscribe("mavros/global_position/global", 1, &handleMessage<NavSatFix, global_position>);
|
auto global_position_sub = nh.subscribe(mavros + "/global_position/global", 1, &handleMessage<NavSatFix, global_position>);
|
||||||
auto battery_sub = nh.subscribe("mavros/battery", 1, &handleMessage<BatteryState, battery>);
|
auto battery_sub = nh.subscribe(mavros + "/battery", 1, &handleMessage<BatteryState, battery>);
|
||||||
auto statustext_sub = nh.subscribe("mavros/statustext/recv", 1, &handleMessage<mavros_msgs::StatusText, statustext>);
|
auto statustext_sub = nh.subscribe(mavros + "/statustext/recv", 1, &handleMessage<mavros_msgs::StatusText, statustext>);
|
||||||
auto manual_control_sub = nh.subscribe("mavros/manual_control/control", 1, &handleMessage<mavros_msgs::ManualControl, manual_control>);
|
auto manual_control_sub = nh.subscribe(mavros + "/manual_control/control", 1, &handleMessage<mavros_msgs::ManualControl, manual_control>);
|
||||||
auto local_position_sub = nh.subscribe("mavros/local_position/pose", 1, &handleLocalPosition);
|
auto local_position_sub = nh.subscribe(mavros + "/local_position/pose", 1, &handleLocalPosition);
|
||||||
|
|
||||||
// Setpoint publishers
|
// Setpoint publishers
|
||||||
position_pub = nh.advertise<PoseStamped>("mavros/setpoint_position/local", 1);
|
position_pub = nh.advertise<PoseStamped>(mavros + "/setpoint_position/local", 1);
|
||||||
position_raw_pub = nh.advertise<PositionTarget>("mavros/setpoint_raw/local", 1);
|
position_raw_pub = nh.advertise<PositionTarget>(mavros + "/setpoint_raw/local", 1);
|
||||||
attitude_pub = nh.advertise<PoseStamped>("mavros/setpoint_attitude/attitude", 1);
|
attitude_pub = nh.advertise<PoseStamped>(mavros + "/setpoint_attitude/attitude", 1);
|
||||||
attitude_raw_pub = nh.advertise<AttitudeTarget>("mavros/setpoint_raw/attitude", 1);
|
attitude_raw_pub = nh.advertise<AttitudeTarget>(mavros + "/setpoint_raw/attitude", 1);
|
||||||
rates_pub = nh.advertise<TwistStamped>("mavros/setpoint_attitude/cmd_vel", 1);
|
rates_pub = nh.advertise<TwistStamped>(mavros + "/setpoint_attitude/cmd_vel", 1);
|
||||||
thrust_pub = nh.advertise<Thrust>("mavros/setpoint_attitude/thrust", 1);
|
thrust_pub = nh.advertise<Thrust>(mavros + "/setpoint_attitude/thrust", 1);
|
||||||
|
|
||||||
// Service servers
|
// Service servers
|
||||||
auto gt_serv = nh.advertiseService("get_telemetry", &getTelemetry);
|
auto gt_serv = nh.advertiseService("get_telemetry", &getTelemetry);
|
||||||
|
|||||||
@@ -141,11 +141,11 @@ int main(int argc, char **argv) {
|
|||||||
vpe_pub = nh_priv.advertise<PoseStamped>("vpe", 1);
|
vpe_pub = nh_priv.advertise<PoseStamped>("vpe", 1);
|
||||||
//vpe_cov_pub = nh_priv_.advertise<PoseStamped>("pose_cov_pub", 1);
|
//vpe_cov_pub = nh_priv_.advertise<PoseStamped>("pose_cov_pub", 1);
|
||||||
|
|
||||||
if (nh_priv.param("publish_zero", false)) {
|
if (nh_priv.param("force_init", false) || nh_priv.param("publish_zero", false)) { // publish_zero is old name
|
||||||
// publish zero to initialize the local position
|
// publish zero to initialize the local position
|
||||||
zero_timer = nh.createTimer(ros::Duration(0.1), &publishZero);
|
zero_timer = nh.createTimer(ros::Duration(0.1), &publishZero);
|
||||||
publish_zero_timout = ros::Duration(nh_priv.param("publish_zero_timout", 5.0));
|
publish_zero_timout = ros::Duration(nh_priv.param("force_init_timeout", 5.0));
|
||||||
publish_zero_duration = ros::Duration(nh_priv.param("publish_zero_duration", 5.0));
|
publish_zero_duration = ros::Duration(nh_priv.param("force_init_duration", 5.0));
|
||||||
local_position_sub = nh.subscribe("mavros/local_position/pose", 1, &localPositionCallback);
|
local_position_sub = nh.subscribe("mavros/local_position/pose", 1, &localPositionCallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -33,3 +33,29 @@ def test_web_video_server(node):
|
|||||||
# Python 3
|
# Python 3
|
||||||
import urllib.request as urllib
|
import urllib.request as urllib
|
||||||
urllib.urlopen("http://localhost:8080").read()
|
urllib.urlopen("http://localhost:8080").read()
|
||||||
|
|
||||||
|
def test_blocks(node):
|
||||||
|
rospy.wait_for_service('clover_blocks/run', timeout=5)
|
||||||
|
rospy.wait_for_service('clover_blocks/stop', timeout=5)
|
||||||
|
rospy.wait_for_service('clover_blocks/load', timeout=5)
|
||||||
|
rospy.wait_for_service('clover_blocks/store', timeout=5)
|
||||||
|
|
||||||
|
from std_msgs.msg import String
|
||||||
|
from clover_blocks.srv import Run
|
||||||
|
|
||||||
|
def wait_print():
|
||||||
|
try:
|
||||||
|
wait_print.result = rospy.wait_for_message('clover_blocks/print', String, timeout=5).data
|
||||||
|
except Exception as e:
|
||||||
|
wait_print.result = str(e)
|
||||||
|
|
||||||
|
import threading
|
||||||
|
t = threading.Thread(target=wait_print)
|
||||||
|
t.start()
|
||||||
|
rospy.sleep(0.1)
|
||||||
|
|
||||||
|
run = rospy.ServiceProxy('clover_blocks/run', Run)
|
||||||
|
assert run(code='print("test")').success == True
|
||||||
|
|
||||||
|
t.join()
|
||||||
|
assert wait_print.result == 'test'
|
||||||
|
|||||||
@@ -23,10 +23,7 @@
|
|||||||
|
|
||||||
<node pkg="tf2_ros" type="static_transform_publisher" name="map_flipped_frame" args="0 0 0 3.1415926 3.1415926 0 map map_flipped" required="true"/>
|
<node pkg="tf2_ros" type="static_transform_publisher" name="map_flipped_frame" args="0 0 0 3.1415926 3.1415926 0 map map_flipped" required="true"/>
|
||||||
|
|
||||||
<node name="simple_offboard" pkg="clover" type="simple_offboard" required="true" output="screen">
|
<node name="simple_offboard" pkg="clover" type="simple_offboard" required="true" output="screen"/>
|
||||||
<param name="reference_frames/body" value="map"/>
|
|
||||||
<param name="reference_frames/base_link" value="map"/>
|
|
||||||
</node>
|
|
||||||
|
|
||||||
<node name="tf2_web_republisher" pkg="tf2_web_republisher" type="tf2_web_republisher" required="true"/>
|
<node name="tf2_web_republisher" pkg="tf2_web_republisher" type="tf2_web_republisher" required="true"/>
|
||||||
|
|
||||||
@@ -38,6 +35,8 @@
|
|||||||
<rosparam param="notify">startup: { r: 255, g: 255, b: 255 }</rosparam>
|
<rosparam param="notify">startup: { r: 255, g: 255, b: 255 }</rosparam>
|
||||||
</node>
|
</node>
|
||||||
|
|
||||||
|
<node name="clover_blocks" pkg="clover_blocks" type="clover_blocks" output="screen" required="true"/>
|
||||||
|
|
||||||
<param name="test_module" value="$(find clover)/test/basic.py"/>
|
<param name="test_module" value="$(find clover)/test/basic.py"/>
|
||||||
<test test-name="basic_test" pkg="ros_pytest" type="ros_pytest_runner"/>
|
<test test-name="basic_test" pkg="ros_pytest" type="ros_pytest_runner"/>
|
||||||
</launch>
|
</launch>
|
||||||
|
|||||||
23
clover/www/console.html
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<h1>
|
||||||
|
/var/log/clover.log
|
||||||
|
<a style="font-size: 0.5em; vertical-align: super; font-weight: normal" href="clover.log" download>download</a>
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<pre></pre>
|
||||||
|
|
||||||
|
<script type="module">
|
||||||
|
var pre = document.querySelector('pre');
|
||||||
|
|
||||||
|
fetch('clover.log?' + Math.random()).then(function(response) { // random to forbid caching
|
||||||
|
if (response.status == 404) {
|
||||||
|
pre.innerHTML = '/var/log/clover.log does not exist';
|
||||||
|
return;
|
||||||
|
} else if (response.status !== 200) {
|
||||||
|
pre.innerHTML('Error ' + response.status);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
response.text().then(function(content) {
|
||||||
|
pre.innerHTML = content;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
@@ -9,7 +9,7 @@
|
|||||||
<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>View <a href="viz.html">View 3D visualization</a>, <a href="aruco_map.html">3D visualization for markers map</a> (<code>ros3djs</code>)</li>
|
||||||
<li><a href="../clover_blocks/">Blocks programming</a> (<code>Blockly</code>)</li>
|
<li><a href="../clover_blocks/">Blocks programming</a> (<code>Blockly</code>)</li>
|
||||||
<li><a href="clover.log">Clover console</a> (<code>/var/log/clover.log</code>)</li>
|
<li><a href="console.html">Clover console</a> (<code>/var/log/clover.log</code>)</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<div class="version"></div>
|
<div class="version"></div>
|
||||||
|
|||||||
@@ -1,13 +1,16 @@
|
|||||||
const url = 'ws://' + location.hostname + ':9090';
|
const url = 'ws://' + location.hostname + ':9090';
|
||||||
const ros = new ROSLIB.Ros({ url: url });
|
const ros = new ROSLIB.Ros({ url: url });
|
||||||
|
const params = Object.fromEntries(new URLSearchParams(window.location.search).entries());
|
||||||
|
|
||||||
ros.on('connection', function () {
|
ros.on('connection', function () {
|
||||||
document.body.classList.add('connected');
|
document.body.classList.add('connected');
|
||||||
|
document.body.classList.remove('closed');
|
||||||
init();
|
init();
|
||||||
});
|
});
|
||||||
|
|
||||||
ros.on('close', function () {
|
ros.on('close', function () {
|
||||||
document.body.classList.remove('connected');
|
document.body.classList.remove('connected');
|
||||||
|
document.body.classList.add('closed');
|
||||||
setTimeout(function() {
|
setTimeout(function() {
|
||||||
// reconnect
|
// reconnect
|
||||||
ros.connect(url);
|
ros.connect(url);
|
||||||
@@ -37,18 +40,28 @@ function viewTopicsList() {
|
|||||||
let rosdistro;
|
let rosdistro;
|
||||||
|
|
||||||
function viewTopic(topic) {
|
function viewTopic(topic) {
|
||||||
title.innerHTML = topic;
|
let index = '<a href=topics.html>Topics</a>';
|
||||||
|
title.innerHTML = `${index}: ${topic}`;
|
||||||
topicMessage.style.display = 'block';
|
topicMessage.style.display = 'block';
|
||||||
|
|
||||||
ros.getTopicType(topic, function(typeStr) {
|
ros.getTopicType(topic, function(typeStr) {
|
||||||
const [pack, type] = typeStr.split('/');
|
const [pack, type] = typeStr.split('/');
|
||||||
let href = `https://docs.ros.org/en/${rosdistro}/api/${pack}/html/msg/${type}.html`;
|
let href = `https://docs.ros.org/en/${rosdistro}/api/${pack}/html/msg/${type}.html`;
|
||||||
title.innerHTML = `${topic} <a id="topic-type" href=${href} target="_blank">${typeStr}</a>`;
|
title.innerHTML = `${index}: ${topic} <a id="topic-type" href=${href} target="_blank">${typeStr}</a>`;
|
||||||
});
|
});
|
||||||
|
|
||||||
new ROSLIB.Topic({ ros: ros, name: topic }).subscribe(function(msg) {
|
new ROSLIB.Topic({ ros: ros, name: topic }).subscribe(function(msg) {
|
||||||
document.title = topic;
|
document.title = topic;
|
||||||
if (mouseDown) return;
|
if (mouseDown) return;
|
||||||
|
|
||||||
|
if (msg.header.stamp) {
|
||||||
|
if (params.date || params.offset) {
|
||||||
|
let date = new Date(msg.header.stamp.secs * 1e3 + msg.header.stamp.nsecs * 1e-6);
|
||||||
|
if (params.date) msg.header.date = date.toISOString();
|
||||||
|
if (params.offset) msg.header.offset = (new Date() - date) * 1e-3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
topicMessage.innerHTML = yamlStringify(msg); // JSON.stringify(msg, null, 4);
|
topicMessage.innerHTML = yamlStringify(msg); // JSON.stringify(msg, null, 4);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -59,8 +72,6 @@ topicMessage.addEventListener('mousedown', function() { mouseDown = true; });
|
|||||||
topicMessage.addEventListener('mouseup', function() { mouseDown = false; });
|
topicMessage.addEventListener('mouseup', function() { mouseDown = false; });
|
||||||
|
|
||||||
function init() {
|
function init() {
|
||||||
const params = Object.fromEntries(new URLSearchParams(window.location.search).entries());
|
|
||||||
|
|
||||||
if (!params.topic) {
|
if (!params.topic) {
|
||||||
viewTopicsList();
|
viewTopicsList();
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -17,11 +17,12 @@
|
|||||||
}
|
}
|
||||||
#topic-type { font-family: monospace; font-size: 0.5em; vertical-align: super; font-weight: normal; }
|
#topic-type { font-family: monospace; font-size: 0.5em; vertical-align: super; font-weight: normal; }
|
||||||
.topic { font-family: monospace; }
|
.topic { font-family: monospace; }
|
||||||
|
body.closed { background-color: rgb(207, 207, 207); }
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h1> </h1>
|
<h1> </h1>
|
||||||
<ul id="topics"></ul>
|
<ul id="topics"></ul>
|
||||||
<code id="topic-message" title="Hold mouse button to pause">No messages received</code>
|
<code id="topic-message">No messages received</code>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<?xml version="1.0"?>
|
<?xml version="1.0"?>
|
||||||
<package format="2">
|
<package format="2">
|
||||||
<name>clover_blocks</name>
|
<name>clover_blocks</name>
|
||||||
<version>0.21.1</version>
|
<version>0.23.0</version>
|
||||||
<description>Blockly programming support for Clover</description>
|
<description>Blockly programming support for Clover</description>
|
||||||
<maintainer email="okalachev@gmail.com">Oleg Kalachev</maintainer>
|
<maintainer email="okalachev@gmail.com">Oleg Kalachev</maintainer>
|
||||||
<license>MIT</license>
|
<license>MIT</license>
|
||||||
|
|||||||
@@ -464,7 +464,7 @@ Blockly.Python.led_count = function(block) {
|
|||||||
|
|
||||||
function pigpio() {
|
function pigpio() {
|
||||||
Blockly.Python.definitions_['import_pigpio'] = 'import pigpio';
|
Blockly.Python.definitions_['import_pigpio'] = 'import pigpio';
|
||||||
Blockly.Python.definitions_['init_pigpio'] = 'pi = pigpio.pi()';
|
Blockly.Python.definitions_['init_pigpio'] = 'pi = pigpio.pi()\nif not pi.connected: raise Exception(\'Cannot connect to pigpiod\')';
|
||||||
}
|
}
|
||||||
|
|
||||||
const GPIO_READ = `\ndef gpio_read(pin):
|
const GPIO_READ = `\ndef gpio_read(pin):
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<package format="2">
|
<package format="2">
|
||||||
<name>clover_description</name>
|
<name>clover_description</name>
|
||||||
<version>0.21.1</version>
|
<version>0.23.0</version>
|
||||||
<description>The clover_description package provides URDF models of the Clover series of quadcopters.</description>
|
<description>The clover_description package provides URDF models of the Clover series of quadcopters.</description>
|
||||||
|
|
||||||
<maintainer email="sfalexrog@gmail.com">Alexey Rogachevskiy</maintainer>
|
<maintainer email="sfalexrog@gmail.com">Alexey Rogachevskiy</maintainer>
|
||||||
|
|||||||
@@ -35,7 +35,7 @@
|
|||||||
<xacro:property name="sqrt2" value="1.4142135623730951" />
|
<xacro:property name="sqrt2" value="1.4142135623730951" />
|
||||||
<xacro:property name="rotor_drag_coefficient" value="1.75e-04" />
|
<xacro:property name="rotor_drag_coefficient" value="1.75e-04" />
|
||||||
<xacro:property name="rolling_moment_coefficient" value="0.000001" />
|
<xacro:property name="rolling_moment_coefficient" value="0.000001" />
|
||||||
<xacro:property name="color" value="$(arg visual_material)" />
|
<xacro:property name="color" value="DarkGrey" />
|
||||||
|
|
||||||
<!-- Property Blocks -->
|
<!-- Property Blocks -->
|
||||||
<!-- Clover body inertia -->
|
<!-- Clover body inertia -->
|
||||||
|
|||||||
@@ -64,6 +64,12 @@
|
|||||||
<!-- <gazebo>
|
<!-- <gazebo>
|
||||||
<static>true</static>
|
<static>true</static>
|
||||||
</gazebo> -->
|
</gazebo> -->
|
||||||
|
<gazebo>
|
||||||
|
<plugin name="${name}_ros_controller" filename="libsim_leds_controller.so">
|
||||||
|
<robotNamespace></robotNamespace>
|
||||||
|
<ledCount>${led_count}</ledCount>
|
||||||
|
</plugin>
|
||||||
|
</gazebo>
|
||||||
</xacro:macro>
|
</xacro:macro>
|
||||||
|
|
||||||
</robot>
|
</robot>
|
||||||
|
|||||||
@@ -37,6 +37,14 @@ target_compile_options(sim_leds PRIVATE -std=c++11)
|
|||||||
|
|
||||||
add_dependencies(sim_leds ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
|
add_dependencies(sim_leds ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
|
||||||
|
|
||||||
|
add_library(sim_leds_controller src/sim_leds_controller.cpp)
|
||||||
|
|
||||||
|
target_include_directories(sim_leds_controller PRIVATE ${catkin_INCLUDE_DIRS} ${GAZEBO_INCLUDE_DIRS})
|
||||||
|
target_link_libraries(sim_leds_controller PRIVATE ${catkin_LIBRARIES} ${GAZEBO_LIBRARIES})
|
||||||
|
target_compile_options(sim_leds_controller PRIVATE -std=c++11)
|
||||||
|
|
||||||
|
add_dependencies(sim_leds_controller ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
|
||||||
|
|
||||||
# Gazebo throttling camera plugin
|
# Gazebo throttling camera plugin
|
||||||
# for some reason, CMake does not support per-target link directories, and Gazebo does not put
|
# for some reason, CMake does not support per-target link directories, and Gazebo does not put
|
||||||
# CameraPlugin into ${GAZEBO_LIBRARIES}
|
# CameraPlugin into ${GAZEBO_LIBRARIES}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ param set LPE_VIS_Z 0.1
|
|||||||
param set LPE_FUSION 86
|
param set LPE_FUSION 86
|
||||||
|
|
||||||
param set SENS_FLOW_ROT 0
|
param set SENS_FLOW_ROT 0
|
||||||
param set SENS_FLOW_MINHGT 0.01
|
param set SENS_FLOW_MINHGT 0.0
|
||||||
param set SENS_FLOW_MAXHGT 4.0
|
param set SENS_FLOW_MAXHGT 4.0
|
||||||
param set SENS_FLOW_MAXR 10.0
|
param set SENS_FLOW_MAXR 10.0
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<package format="2">
|
<package format="2">
|
||||||
<name>clover_simulation</name>
|
<name>clover_simulation</name>
|
||||||
<version>0.21.1</version>
|
<version>0.23.0</version>
|
||||||
<description>The clover_simulation package provides worlds and launch files for Gazebo.</description>
|
<description>The clover_simulation package provides worlds and launch files for Gazebo.</description>
|
||||||
|
|
||||||
<maintainer email="okalachev@gmail.com">Oleg Kalachev</maintainer>
|
<maintainer email="okalachev@gmail.com">Oleg Kalachev</maintainer>
|
||||||
|
|||||||
@@ -49,14 +49,9 @@ private:
|
|||||||
|
|
||||||
std::unique_ptr<ros::NodeHandle> nh;
|
std::unique_ptr<ros::NodeHandle> nh;
|
||||||
|
|
||||||
ros::ServiceServer setLedsSrv;
|
|
||||||
// Note: LED state should only be published by the /gazebo node
|
|
||||||
led_msgs::LEDStateArray ledState;
|
|
||||||
ros::Publisher statePublisher;
|
|
||||||
// LED state will be read from the topic to avoid creating more services
|
// LED state will be read from the topic to avoid creating more services
|
||||||
ros::Subscriber stateSubscriber;
|
ros::Subscriber stateSubscriber;
|
||||||
|
|
||||||
bool setLeds(led_msgs::SetLEDs::Request& req, led_msgs::SetLEDs::Response& resp);
|
|
||||||
void handleLedsMsg(const led_msgs::LEDStateArrayConstPtr& leds);
|
void handleLedsMsg(const led_msgs::LEDStateArrayConstPtr& leds);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@@ -73,16 +68,8 @@ public:
|
|||||||
ROS_INFO_NAMED(("LedController_" + robotNamespace).c_str(), "LedController has started (as %s)", role == Role::Client ? "client" : "server");
|
ROS_INFO_NAMED(("LedController_" + robotNamespace).c_str(), "LedController has started (as %s)", role == Role::Client ? "client" : "server");
|
||||||
|
|
||||||
nh.reset(new ros::NodeHandle(robotNamespace));
|
nh.reset(new ros::NodeHandle(robotNamespace));
|
||||||
if (role == Role::Server)
|
|
||||||
{
|
stateSubscriber = nh->subscribe<led_msgs::LEDStateArray>("led/state", 1, &LedController::handleLedsMsg, this);
|
||||||
setLedsSrv = nh->advertiseService("led/set_leds", &LedController::setLeds, this);
|
|
||||||
statePublisher = nh->advertise<led_msgs::LEDStateArray>("led/state", 1, true);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// LED state should be published to the "led/state" topic, so we grab our data there
|
|
||||||
stateSubscriber = nh->subscribe<led_msgs::LEDStateArray>("led/state", 1, &LedController::handleLedsMsg, this);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
~LedController()
|
~LedController()
|
||||||
@@ -96,13 +83,9 @@ public:
|
|||||||
std::lock_guard<std::mutex> lock(registryMutex);
|
std::lock_guard<std::mutex> lock(registryMutex);
|
||||||
if (totalLeds > 0) {
|
if (totalLeds > 0) {
|
||||||
registeredLeds.resize(totalLeds);
|
registeredLeds.resize(totalLeds);
|
||||||
ledState.leds.resize(totalLeds);
|
|
||||||
}
|
}
|
||||||
ROS_DEBUG_NAMED(("LedController_" + robotNamespace).c_str(), "Registering LED visual plugin to %s (LED id=%d)", (role == Role::Client) ? "client" : "server", ledIdx);
|
ROS_DEBUG_NAMED(("LedController_" + robotNamespace).c_str(), "Registering LED visual plugin to %s (LED id=%d)", (role == Role::Client) ? "client" : "server", ledIdx);
|
||||||
registeredLeds[ledIdx] = plugin;
|
registeredLeds[ledIdx] = plugin;
|
||||||
ledState.leds[ledIdx].index = ledIdx;
|
|
||||||
if (role == Role::Server)
|
|
||||||
statePublisher.publish(ledState);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void unregisterPlugin(sim_led::LedVisualPlugin* plugin)
|
void unregisterPlugin(sim_led::LedVisualPlugin* plugin)
|
||||||
@@ -157,7 +140,8 @@ public:
|
|||||||
{
|
{
|
||||||
auto indexStr = parentName.substr(lastDashPos + 1);
|
auto indexStr = parentName.substr(lastDashPos + 1);
|
||||||
try {
|
try {
|
||||||
myIndex = std::stoi(indexStr);
|
if (indexStr == "visual") myIndex = 0; // the first visual doesn't have index
|
||||||
|
else myIndex = std::stoi(indexStr);
|
||||||
} catch(const std::exception &e) {
|
} catch(const std::exception &e) {
|
||||||
gzwarn << "Failed to convert " << indexStr << " to integer: " << e.what() << ", assuming 0\n";
|
gzwarn << "Failed to convert " << indexStr << " to integer: " << e.what() << ", assuming 0\n";
|
||||||
myIndex = 0;
|
myIndex = 0;
|
||||||
@@ -195,26 +179,6 @@ public:
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: These two functions basically do the same thing, maybe they can be merged?
|
|
||||||
bool led_controller::LedController::setLeds(led_msgs::SetLEDs::Request &req, led_msgs::SetLEDs::Response &resp)
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(registryMutex);
|
|
||||||
for(const auto& led : req.leds)
|
|
||||||
{
|
|
||||||
if (led.index < registeredLeds.size()) {
|
|
||||||
auto color = GazeboColor(led.r / 255.0f, led.g / 255.0f, led.b / 255.0f);
|
|
||||||
auto ledPlugin = registeredLeds[led.index];
|
|
||||||
if (ledPlugin) ledPlugin->SetColor(color);
|
|
||||||
ledState.leds[led.index].r = led.r;
|
|
||||||
ledState.leds[led.index].g = led.g;
|
|
||||||
ledState.leds[led.index].b = led.b;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
statePublisher.publish(ledState);
|
|
||||||
resp.success = true;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void led_controller::LedController::handleLedsMsg(const led_msgs::LEDStateArrayConstPtr& leds)
|
void led_controller::LedController::handleLedsMsg(const led_msgs::LEDStateArrayConstPtr& leds)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(registryMutex);
|
std::lock_guard<std::mutex> lock(registryMutex);
|
||||||
|
|||||||
71
clover_simulation/src/sim_leds_controller.cpp
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
#include <led_msgs/SetLEDs.h>
|
||||||
|
#include <led_msgs/LEDStateArray.h>
|
||||||
|
|
||||||
|
#include <ros/ros.h>
|
||||||
|
|
||||||
|
#include <gazebo/gazebo.hh>
|
||||||
|
#include <gazebo/physics/physics.hh>
|
||||||
|
#include <gazebo/common/common.hh>
|
||||||
|
|
||||||
|
class LedControllerPlugin : public gazebo::ModelPlugin {
|
||||||
|
private:
|
||||||
|
std::unique_ptr<ros::NodeHandle> nh;
|
||||||
|
std::string ns;
|
||||||
|
ros::ServiceServer setLedsSrv;
|
||||||
|
led_msgs::LEDStateArray ledState;
|
||||||
|
ros::Publisher statePublisher;
|
||||||
|
std::mutex handleMutex;
|
||||||
|
|
||||||
|
public:
|
||||||
|
bool setLeds(led_msgs::SetLEDs::Request &req, led_msgs::SetLEDs::Response &resp)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(handleMutex);
|
||||||
|
for(const auto& led : req.leds)
|
||||||
|
{
|
||||||
|
if (led.index < ledState.leds.size()) {
|
||||||
|
ledState.leds[led.index].r = led.r;
|
||||||
|
ledState.leds[led.index].g = led.g;
|
||||||
|
ledState.leds[led.index].b = led.b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
statePublisher.publish(ledState);
|
||||||
|
resp.success = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void Load(gazebo::physics::ModelPtr model, sdf::ElementPtr sdf) override
|
||||||
|
{
|
||||||
|
ROS_INFO("Initialize LED Controller");
|
||||||
|
|
||||||
|
// We need "libgazebo_ros_api.so" to be loaded
|
||||||
|
if (!ros::isInitialized())
|
||||||
|
{
|
||||||
|
ROS_FATAL_NAMED("LedController", "Tried to load ROS plugin when ROS Gazebo API is not loaded. Please use gazebo_ros node to"
|
||||||
|
"launch Gazebo.");
|
||||||
|
}
|
||||||
|
|
||||||
|
ns = "";
|
||||||
|
|
||||||
|
if (sdf->HasElement("robotNamespace")) {
|
||||||
|
ns = sdf->Get<std::string>("robotNamespace");
|
||||||
|
}
|
||||||
|
if (!sdf->HasElement("ledCount")) {
|
||||||
|
gzerr << "ledCount is not set, but is required for the plugin to function correctly\n";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int totalLeds = sdf->Get<int>("ledCount");
|
||||||
|
ledState.leds.resize(totalLeds);
|
||||||
|
for (int i = 0; i < totalLeds; i++) {
|
||||||
|
ledState.leds[i].index = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
nh.reset(new ros::NodeHandle(ns));
|
||||||
|
|
||||||
|
setLedsSrv = nh->advertiseService("led/set_leds", &LedControllerPlugin::setLeds, this);
|
||||||
|
statePublisher = nh->advertise<led_msgs::LEDStateArray>("led/state", 1, true);
|
||||||
|
|
||||||
|
statePublisher.publish(ledState);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
GZ_REGISTER_MODEL_PLUGIN(LedControllerPlugin);
|
||||||
83
docs/.vuepress/config.js
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
const sidebar = require('./sidebar');
|
||||||
|
|
||||||
|
const hostname = 'https://clover.coex.tech/';
|
||||||
|
const allowedTags = ['font', 'center', 'nobr']; // allow using some deprecated and non-standard html tags
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
lang: 'en-US',
|
||||||
|
title: 'Clover',
|
||||||
|
description: 'Clover Drone Kit',
|
||||||
|
// theme and its config
|
||||||
|
theme: '@vuepress/theme-default',
|
||||||
|
themeConfig: {
|
||||||
|
logo: 'clover-logo.png',
|
||||||
|
sidebar: {
|
||||||
|
'/ru/': sidebar.readSummary("./ru/SUMMARY.md"),
|
||||||
|
'/en/': sidebar.readSummary("./en/SUMMARY.md"),
|
||||||
|
},
|
||||||
|
sidebarDepth: 0,
|
||||||
|
locales: {
|
||||||
|
'/en/': {
|
||||||
|
selectLanguageName: 'English',
|
||||||
|
navbar: [
|
||||||
|
{ text: 'Official Site', link: 'https://coex.tech' },
|
||||||
|
{ text: 'Support Chat', link: 'https://t.me/COEXHelpdesk' },
|
||||||
|
]
|
||||||
|
},
|
||||||
|
'/ru/': {
|
||||||
|
selectLanguageName: 'Русский',
|
||||||
|
tip: 'СОВЕТ',
|
||||||
|
warning: 'ВНИМАНИЕ',
|
||||||
|
danger: 'ОПАСНО',
|
||||||
|
toggleDarkMode: 'Переключить темную тему',
|
||||||
|
navbar: [
|
||||||
|
{ text: 'Сайт', link: 'https://coex.tech' },
|
||||||
|
{ text: 'Чат поддержки', link: 'https://t.me/COEXHelpdesk' },
|
||||||
|
]
|
||||||
|
},
|
||||||
|
},
|
||||||
|
toggleSidebar: true,
|
||||||
|
repo: 'CopterExpress/clover',
|
||||||
|
docsBranch: 'master',
|
||||||
|
docsDir: 'docs',
|
||||||
|
lastUpdated: false,
|
||||||
|
contributors: false
|
||||||
|
},
|
||||||
|
pagePatterns: ['**/*.md', '!.vuepress', '!node_modules', '!ru/metodmaterials.md'],
|
||||||
|
locales: {
|
||||||
|
'/en/': {
|
||||||
|
lang: 'en',
|
||||||
|
title: 'Clover',
|
||||||
|
description: 'Clover Drone Kit'
|
||||||
|
},
|
||||||
|
'/ru/': {
|
||||||
|
lang: 'ru',
|
||||||
|
title: 'Клевер',
|
||||||
|
description: 'Конструктор квадрокоптера «Клевер»'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
markdown: {
|
||||||
|
code: {
|
||||||
|
lineNumbers: false
|
||||||
|
},
|
||||||
|
linkify: true,
|
||||||
|
},
|
||||||
|
extendsMarkdown(md) {
|
||||||
|
md.use(require('markdown-it-attrs')); // to use custom headers anchors
|
||||||
|
},
|
||||||
|
bundlerConfig: {
|
||||||
|
vuePluginOptions: {
|
||||||
|
template: {
|
||||||
|
compilerOptions: {
|
||||||
|
isCustomElement: tag => allowedTags.includes(tag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
'@vuepress/plugin-search',
|
||||||
|
'vuepress-plugin-copy-code2',
|
||||||
|
['sitemap2', { hostname, excludeUrls: ['/', '/LANGS.html'] }],
|
||||||
|
require('./rich-quotes')
|
||||||
|
]
|
||||||
|
}
|
||||||
BIN
docs/.vuepress/public/clover-logo.png
Normal file
|
After Width: | Height: | Size: 110 KiB |
37
docs/.vuepress/rich-quotes.js
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
// Plugin to convert GitBook rich quotes to custom containers
|
||||||
|
|
||||||
|
const types = {
|
||||||
|
info: 'tip',
|
||||||
|
note: 'tip',
|
||||||
|
tag: 'tip',
|
||||||
|
comment: 'tip',
|
||||||
|
hint: 'tip',
|
||||||
|
success: 'tip',
|
||||||
|
warning: 'warning',
|
||||||
|
caution: 'warning',
|
||||||
|
danger: 'danger',
|
||||||
|
quote: 'tip'
|
||||||
|
}
|
||||||
|
|
||||||
|
function replace(src) {
|
||||||
|
return src.replace(/^> \*\*(.*?)\*\* (.*\n(>.*\n)*)/gm, function (match, type, text) {
|
||||||
|
text = text.replace(/^>/gm, '');
|
||||||
|
return `::: ${types[type.toLowerCase()]}\n${text}\n:::`;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
name: 'vuepress-plugin-rich-quotes',
|
||||||
|
extendsMarkdown: (md) => {
|
||||||
|
var _render = md.render;
|
||||||
|
|
||||||
|
// TODO: a rough hack to replace rich quotes
|
||||||
|
// TODO: use proper plugin api
|
||||||
|
|
||||||
|
md.render = function(src, env) {
|
||||||
|
src = replace(src);
|
||||||
|
return _render.call(md, src, env);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
};
|
||||||
50
docs/.vuepress/sidebar.js
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
const fs = require('fs')
|
||||||
|
|
||||||
|
const regex = /(\s*?)\*\s\[(.*?)\]\((.*?)\)/;
|
||||||
|
|
||||||
|
exports.readSummary = function (path) {
|
||||||
|
let sidebar = [];
|
||||||
|
let lines = fs.readFileSync(path).toString().split('\n');
|
||||||
|
let item = {};
|
||||||
|
|
||||||
|
for (let line of lines) {
|
||||||
|
if (line.startsWith('#')) continue;
|
||||||
|
if (!line.trim()) continue;
|
||||||
|
|
||||||
|
let match = regex.exec(line);
|
||||||
|
if (!match) {
|
||||||
|
console.log('cannot parse', line);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
level = match[1].length / 2;
|
||||||
|
text = match[2];
|
||||||
|
path = match[3].trim();
|
||||||
|
|
||||||
|
if (level == 0) {
|
||||||
|
if (item.path) {
|
||||||
|
// push new item
|
||||||
|
if (item.children) {
|
||||||
|
sidebar.push(item);
|
||||||
|
} else {
|
||||||
|
sidebar.push(item.path)
|
||||||
|
}
|
||||||
|
item = {};
|
||||||
|
}
|
||||||
|
item.text = text;
|
||||||
|
item.path = path;
|
||||||
|
item.collapsible = true;
|
||||||
|
|
||||||
|
} else if (level == 1 || level == 2) {
|
||||||
|
if (!item.children) {
|
||||||
|
item.children = [];
|
||||||
|
if (item.path) item.children.push(item.path);
|
||||||
|
}
|
||||||
|
item.children.push(path);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
console.log('skip', text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return sidebar;
|
||||||
|
}
|
||||||
49
docs/.vuepress/styles/index.scss
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
.big-clover {
|
||||||
|
max-width: 80% !important;
|
||||||
|
display: block;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* change image for dark theme */
|
||||||
|
html .big-clover.dark { display: none; }
|
||||||
|
html.dark .big-clover { display: none; }
|
||||||
|
html.dark .big-clover.dark { display: block; }
|
||||||
|
|
||||||
|
img.logo {
|
||||||
|
transform: scale(2.5) translateX(-5%);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Centered images */
|
||||||
|
img.center {
|
||||||
|
display: block;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Images with border */
|
||||||
|
img.border {
|
||||||
|
border: 1px #e9e9e9 solid;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
html.dark img.border {
|
||||||
|
border: none;
|
||||||
|
background: #fffffa;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.versions td {
|
||||||
|
text-align: center;
|
||||||
|
background: white;
|
||||||
|
}
|
||||||
|
table.versions .subversion {
|
||||||
|
font-size: 80%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.circle {
|
||||||
|
width: 0.8em;
|
||||||
|
height: 0.8em;
|
||||||
|
border-radius: 50%;
|
||||||
|
display: inline-block;
|
||||||
|
margin-right: 0.5em;
|
||||||
|
}
|
||||||
4
docs/README.md
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
# Languages
|
||||||
|
|
||||||
|
* [English](en/)
|
||||||
|
* [Русский](ru/)
|
||||||
|
Before Width: | Height: | Size: 695 KiB After Width: | Height: | Size: 220 KiB |
BIN
docs/assets/lane_control_with_color.jpg
Normal file
|
After Width: | Height: | Size: 151 KiB |
BIN
docs/assets/lane_control_without_any_color.jpg
Normal file
|
After Width: | Height: | Size: 112 KiB |
|
Before Width: | Height: | Size: 972 KiB |
|
Before Width: | Height: | Size: 1.2 MiB After Width: | Height: | Size: 381 KiB |
BIN
docs/assets/simulation_ubuntu_account.png
Normal file
|
After Width: | Height: | Size: 178 KiB |
BIN
docs/assets/simulation_utm.png
Normal file
|
After Width: | Height: | Size: 395 KiB |
@@ -1,6 +1,7 @@
|
|||||||
# COEX Clover
|
# COEX Clover
|
||||||
|
|
||||||
<img class="center zoom big-clover" src="../assets/clover42-main.png" width="80%" alt="Clover 4.2">
|
<img class="big-clover light" src="../assets/clover42-main.png" alt="Clover 4.2">
|
||||||
|
<img class="big-clover dark" src="../assets/clover42-black.png" alt="Clover 4.2">
|
||||||
|
|
||||||
**Clover** is an educational kit of a programmable quadcopter that consists of popular open source components, and a set of necessary documentation and libraries for working with it.
|
**Clover** is an educational kit of a programmable quadcopter that consists of popular open source components, and a set of necessary documentation and libraries for working with it.
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,6 @@
|
|||||||
* [Power setup](power.md)
|
* [Power setup](power.md)
|
||||||
* [Failsafe configuration](failsafe.md)
|
* [Failsafe configuration](failsafe.md)
|
||||||
* [Manual flight](flight.md)
|
* [Manual flight](flight.md)
|
||||||
* [Basics](flight.md)
|
|
||||||
* [Exercises](flight_exercises.md)
|
* [Exercises](flight_exercises.md)
|
||||||
* [Working with Raspberry Pi](raspberry.md)
|
* [Working with Raspberry Pi](raspberry.md)
|
||||||
* [RPi Image](image.md)
|
* [RPi Image](image.md)
|
||||||
@@ -50,6 +49,7 @@
|
|||||||
* [Native setup](simulation_native.md)
|
* [Native setup](simulation_native.md)
|
||||||
* [VM setup](simulation_vm.md)
|
* [VM setup](simulation_vm.md)
|
||||||
* [Usage](simulation_usage.md)
|
* [Usage](simulation_usage.md)
|
||||||
|
* [Setup on M1 computers](simulation_m1.md)
|
||||||
* [ROS](ros.md)
|
* [ROS](ros.md)
|
||||||
* [MAVROS](mavros.md)
|
* [MAVROS](mavros.md)
|
||||||
* [Supplementary materials](supplementary.md)
|
* [Supplementary materials](supplementary.md)
|
||||||
@@ -70,7 +70,7 @@
|
|||||||
* [Remote control app](rc.md)
|
* [Remote control app](rc.md)
|
||||||
* [Wi-Fi Configuration](network.md)
|
* [Wi-Fi Configuration](network.md)
|
||||||
* [UART settings](uart.md)
|
* [UART settings](uart.md)
|
||||||
* [PX4 Parameters](px4_parameters.md)
|
* [PX4 Parameters](parameters.md)
|
||||||
* [PX4 Logs and Topics](flight_logs.md)
|
* [PX4 Logs and Topics](flight_logs.md)
|
||||||
* [PX4 Firmware](firmware.md)
|
* [PX4 Firmware](firmware.md)
|
||||||
* [MAVLink](mavlink.md)
|
* [MAVLink](mavlink.md)
|
||||||
@@ -100,6 +100,8 @@
|
|||||||
* [CopterHack-2019](copterhack2019.md)
|
* [CopterHack-2019](copterhack2019.md)
|
||||||
* [CopterHack-2018](copterhack2018.md)
|
* [CopterHack-2018](copterhack2018.md)
|
||||||
* [CopterHack-2017](copterhack2017.md)
|
* [CopterHack-2017](copterhack2017.md)
|
||||||
|
* [Video contest](video_contest.md)
|
||||||
|
* [Educational contests](educational_contests.md)
|
||||||
* [Clover-based projects](projects.md)
|
* [Clover-based projects](projects.md)
|
||||||
* [Autonomous Multirotor Landing System (AMLS)](amls.md)
|
* [Autonomous Multirotor Landing System (AMLS)](amls.md)
|
||||||
* [Drone show](clever-show.md)
|
* [Drone show](clever-show.md)
|
||||||
|
|||||||
@@ -75,9 +75,9 @@ else:
|
|||||||
shape = 'undefined'
|
shape = 'undefined'
|
||||||
color = 'undefined'
|
color = 'undefined'
|
||||||
|
|
||||||
if shape = 'brown':
|
if shape == 'brown':
|
||||||
culture = "greshiha"
|
culture = "greshiha"
|
||||||
if shape = 'yellow_orange':
|
if shape == 'yellow_orange':
|
||||||
culture = "pshenitsa"
|
culture = "pshenitsa"
|
||||||
|
|
||||||
image_sub = rospy.Subscriber('main_camera/image_raw', Image, image_colback_color)
|
image_sub = rospy.Subscriber('main_camera/image_raw', Image, image_colback_color)
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ This feature allows getting rid of the system interface elements. Let's go ahead
|
|||||||
|
|
||||||
This is how the transmitter looks at this stage:
|
This is how the transmitter looks at this stage:
|
||||||
|
|
||||||
<img src="../assets/IMG_4397.PNG" width="50%">
|
<img src="../assets/IMG_4397.png" width="50%">
|
||||||
|
|
||||||
If you run your application, you will see that the sticks are not functioning. This is due to the fact that *JavaScript* is disabled in our page. To enable it, write the following code:
|
If you run your application, you will see that the sticks are not functioning. This is due to the fact that *JavaScript* is disabled in our page. To enable it, write the following code:
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
# ArUco markers
|
# ArUco markers
|
||||||
|
|
||||||
> **Note** The following applies to [image versions](image.md) **0.16** and up. Older documentation is still available for [for version **0.15.1**](https://github.com/CopterExpress/clover/blob/v0.15.1/docs/en/aruco.md).
|
|
||||||
|
|
||||||
[ArUco markers](https://docs.opencv.org/3.2.0/d5/dae/tutorial_aruco_detection.html) are commonly used for vision-based position estimation.
|
[ArUco markers](https://docs.opencv.org/3.2.0/d5/dae/tutorial_aruco_detection.html) are commonly used for vision-based position estimation.
|
||||||
|
|
||||||
Examples of ArUco markers:
|
Examples of ArUco markers:
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ The marker map adheres to the [ROS coordinate system convention](http://www.ros.
|
|||||||
|
|
||||||
## VPE setup
|
## VPE setup
|
||||||
|
|
||||||
In order to enable vision position estimation you should use the following [PX4 parameters](px4_parameters.md).
|
In order to enable vision position estimation you should use the following [PX4 parameters](parameters.md).
|
||||||
|
|
||||||
If you're using **LPE** (`SYS_MC_EST_GROUP` parameter is set to `local_position_estimator,attitude_estimator_q`):
|
If you're using **LPE** (`SYS_MC_EST_GROUP` parameter is set to `local_position_estimator,attitude_estimator_q`):
|
||||||
|
|
||||||
@@ -152,7 +152,7 @@ If the drone's altitude is not stable, try increasing the `MPC_Z_VEL_P` paramete
|
|||||||
|
|
||||||
## Placing markers on the ceiling
|
## Placing markers on the ceiling
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
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).
|
||||||
|
|
||||||
|
|||||||
@@ -113,7 +113,3 @@ rospy.spin()
|
|||||||
```
|
```
|
||||||
|
|
||||||
Each message contains the marker ID, its corner points on the image and its position relative to the camera.
|
Each message contains the marker ID, its corner points on the image and its position relative to the camera.
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
Suggested reading: [map-based navigation](aruco_map.md)
|
|
||||||
|
|||||||
@@ -341,7 +341,7 @@ should be increased up to 4 – 5.
|
|||||||
|
|
||||||
### ESC assembly
|
### ESC assembly
|
||||||
|
|
||||||
1. Stick the double-sided adhesive tape to the base of the ESC protective case 
|
1. Stick the double-sided adhesive tape to the base of the ESC protective case.
|
||||||
2. Put the ESCs into protective cases. Fasten the assembly to the motor mounts of the frame. 
|
2. Put the ESCs into protective cases. Fasten the assembly to the motor mounts of the frame. 
|
||||||
|
|
||||||
### Installation of guard
|
### Installation of guard
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ TODO
|
|||||||
|
|
||||||
Cut the remaining part of the clamp (cable tie) with scissors.
|
Cut the remaining part of the clamp (cable tie) with scissors.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## Frame elements installation
|
## Frame elements installation
|
||||||
|
|
||||||
@@ -45,7 +45,7 @@ TODO
|
|||||||
3. Attach the assembled unit to the frame with M3x16 screws, complying with the layout.
|
3. Attach the assembled unit to the frame with M3x16 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.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## BEC voltage converter installation(to be soldered and tested)
|
## BEC voltage converter installation(to be soldered and tested)
|
||||||
|
|
||||||
@@ -93,7 +93,7 @@ TODO
|
|||||||
Black -> GND
|
Black -> GND
|
||||||
Blue -> Din
|
Blue -> Din
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## 4 in 1 ESC board and the PDB power-board installation
|
## 4 in 1 ESC board and the PDB power-board installation
|
||||||
|
|
||||||
@@ -108,7 +108,7 @@ TODO
|
|||||||
3. Install the PDB power distribution board as shown in the picture (the XT60 connector should point to the tail of the drone).
|
3. Install the PDB power distribution board as shown in the picture (the XT60 connector should point to the tail of the drone).
|
||||||
4. Connect the wires of the PCB power supply board and ESC XT30 board.
|
4. Connect the wires of the PCB power supply board and ESC XT30 board.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## Pairing the receiver and transmitter
|
## Pairing the receiver and transmitter
|
||||||
|
|
||||||
@@ -125,7 +125,7 @@ TODO
|
|||||||
* Remove the BIND connector from the receiver.
|
* Remove the BIND connector from the receiver.
|
||||||
* Disconnect the battery.
|
* Disconnect the battery.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
> **Hint** If the remote cannot be powered on, or is blocked, see
|
> **Hint** If the remote cannot be powered on, or is blocked, see
|
||||||
article [remote faults](radioerrors.md).
|
article [remote faults](radioerrors.md).
|
||||||
@@ -145,7 +145,7 @@ article [remote faults](radioerrors.md).
|
|||||||
4. Check the motor rotation direction according to the scheme. Repeat for each motor. Thus, it will be clear which motor is controlled.
|
4. Check the motor rotation direction according to the scheme. Repeat for each motor. Thus, it will be clear which motor is controlled.
|
||||||
5. If you have to change the rotation direction, swap any two phase wires of the motor (needs re-connection).
|
5. If you have to change the rotation direction, swap any two phase wires of the motor (needs re-connection).
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## Installation and connection of the Pixracer flight controller
|
## Installation and connection of the Pixracer flight controller
|
||||||
|
|
||||||
@@ -162,7 +162,7 @@ article [remote faults](radioerrors.md).
|
|||||||
|
|
||||||
4. Connect the ribbon cable from the radio receiver to the RCIN connector in Pixracer.
|
4. Connect the ribbon cable from the radio receiver to the RCIN connector in Pixracer.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## Raspberry installation
|
## Raspberry installation
|
||||||
|
|
||||||
@@ -186,7 +186,7 @@ article [remote faults](radioerrors.md).
|
|||||||
|
|
||||||
Use an M3x16 screw and an M3 nut
|
Use an M3x16 screw and an M3 nut
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## Arduino and FlySky radio receiver installation
|
## Arduino and FlySky radio receiver installation
|
||||||
|
|
||||||
@@ -200,7 +200,7 @@ article [remote faults](radioerrors.md).
|
|||||||
black -> GND
|
black -> GND
|
||||||
orange, green -> currently not used. They are set to the unused pins of the radio receiver.
|
orange, green -> currently not used. They are set to the unused pins of the radio receiver.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## RPi camera installation
|
## RPi camera installation
|
||||||
|
|
||||||
@@ -213,7 +213,7 @@ article [remote faults](radioerrors.md).
|
|||||||
|
|
||||||
5. Install the legs into the mounts (4 pcs).
|
5. Install the legs into the mounts (4 pcs).
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## Installation of the remaining structural elements
|
## Installation of the remaining structural elements
|
||||||
|
|
||||||
@@ -223,13 +223,13 @@ article [remote faults](radioerrors.md).
|
|||||||
|
|
||||||
Secure the upper deck with M3x8 screws (4 pcs.)
|
Secure the upper deck with M3x8 screws (4 pcs.)
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## USB connectors installation
|
## USB connectors installation
|
||||||
|
|
||||||
1. Connect Pixracer to Raspberry using the micro USB - USB cable.
|
1. Connect Pixracer to Raspberry using the micro USB - USB cable.
|
||||||
2. Connect Arduino to Raspberry using the micro USB - USB cable.
|
2. Connect Arduino to Raspberry using the micro USB - USB cable.
|
||||||
|
|
||||||
.
|
.
|
||||||
|
|
||||||
Read more about connection in [article](connection.md).
|
Read more about connection in [article](connection.md).
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ Software autorun
|
|||||||
systemd
|
systemd
|
||||||
---
|
---
|
||||||
|
|
||||||
Main documentation: [https://wiki.archlinux.org/index.php/Systemd_(Russian)](https://wiki.archlinux.org/index.php/Systemd_(Russian)).
|
Main documentation: https://wiki.archlinux.org/title/Systemd.
|
||||||
|
|
||||||
All automatically started Clover software is launched as a `clover.service` systemd service.
|
All automatically started Clover software is launched as a `clover.service` systemd service.
|
||||||
|
|
||||||
@@ -50,12 +50,12 @@ You can add your own node to the list of automatically launched ones. To do this
|
|||||||
|
|
||||||
The started file must have *permission* to run:
|
The started file must have *permission* to run:
|
||||||
|
|
||||||
```(bash)
|
```bash
|
||||||
chmod +x my_program.py
|
chmod +x my_program.py
|
||||||
```
|
```
|
||||||
|
|
||||||
When scripting languages are used, [shebang] should be placed at the beginning of the file (https://ru.wikipedia.org/wiki/Shebang_(Unix)), for example:
|
When scripting languages are used, a <a href="https://en.wikipedia.org/wiki/Shebang_(Unix)">shebang</a> should be placed at the beginning of the file, for example:
|
||||||
|
|
||||||
```(bash)
|
```bash
|
||||||
#!/usr/bin/env python
|
#!/usr/bin/env python3
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ The rest of categories contains standard Blockly's blocks.
|
|||||||
|
|
||||||
### take_off
|
### take_off
|
||||||
|
|
||||||
<img src="../assets/blocks/take-off.png" srcset="../assets/blocks/take-off.png 2x">
|
<img src="../assets/blocks/take-off.png" srcset="@source/assets/blocks/take-off.png 2x">
|
||||||
|
|
||||||
Take off to specified altitude in meters. The altitude may be an arbitrary block, that returns a number.
|
Take off to specified altitude in meters. The altitude may be an arbitrary block, that returns a number.
|
||||||
|
|
||||||
@@ -57,7 +57,7 @@ The `wait` flag specifies, if the drone should wait until take off is complete,
|
|||||||
|
|
||||||
### navigate
|
### navigate
|
||||||
|
|
||||||
<img src="../assets/blocks/navigate.png" srcset="../assets/blocks/navigate.png 2x">
|
<img src="../assets/blocks/navigate.png" srcset="@source/assets/blocks/navigate.png 2x">
|
||||||
|
|
||||||
Navigate to specified point. Coordinates are specified in meters.
|
Navigate to specified point. Coordinates are specified in meters.
|
||||||
|
|
||||||
@@ -77,7 +77,7 @@ This block allows to specify the [coordinate frame](frames.md) of the target poi
|
|||||||
|
|
||||||
### land
|
### land
|
||||||
|
|
||||||
<img src="../assets/blocks/land.png" srcset="../assets/blocks/land.png 2x">
|
<img src="../assets/blocks/land.png" srcset="@source/assets/blocks/land.png 2x">
|
||||||
|
|
||||||
Land the drone.
|
Land the drone.
|
||||||
|
|
||||||
@@ -85,31 +85,31 @@ The `wait` flag specifies, if the drone should wait until landing is complete, b
|
|||||||
|
|
||||||
### wait
|
### wait
|
||||||
|
|
||||||
<img src="../assets/blocks/wait.png" srcset="../assets/blocks/wait.png 2x">
|
<img src="../assets/blocks/wait.png" srcset="@source/assets/blocks/wait.png 2x">
|
||||||
|
|
||||||
Wait specified time period in seconds. The time period may be an arbitrary block, that returns a number.
|
Wait specified time period in seconds. The time period may be an arbitrary block, that returns a number.
|
||||||
|
|
||||||
### wait_arrival
|
### wait_arrival
|
||||||
|
|
||||||
<img src="../assets/blocks/wait-arrival.png" srcset="../assets/blocks/wait-arrival.png 2x">
|
<img src="../assets/blocks/wait-arrival.png" srcset="@source/assets/blocks/wait-arrival.png 2x">
|
||||||
|
|
||||||
Wait, until the drone reaches [navigate](#navigate)-block's target point.
|
Wait, until the drone reaches [navigate](#navigate)-block's target point.
|
||||||
|
|
||||||
### get_position
|
### get_position
|
||||||
|
|
||||||
<img src="../assets/blocks/get-position.png" srcset="../assets/blocks/get-position.png 2x">
|
<img src="../assets/blocks/get-position.png" srcset="@source/assets/blocks/get-position.png 2x">
|
||||||
|
|
||||||
The block returns current position, velocity or yaw angle of the drone relative to the specified [coordinate frame](#relative_to).
|
The block returns current position, velocity or yaw angle of the drone relative to the specified [coordinate frame](#relative_to).
|
||||||
|
|
||||||
### set_effect
|
### set_effect
|
||||||
|
|
||||||
<img src="../assets/blocks/set-effect.png" srcset="../assets/blocks/set-effect.png 2x">
|
<img src="../assets/blocks/set-effect.png" srcset="@source/assets/blocks/set-effect.png 2x">
|
||||||
|
|
||||||
The block allows to set animations to LED strip, similarly to [`set_effect`](leds.md#set_effect) ROS-service.
|
The block allows to set animations to LED strip, similarly to [`set_effect`](leds.md#set_effect) ROS-service.
|
||||||
|
|
||||||
Example of using the block with a random color (colors-related blocks are located in *Colour* category):
|
Example of using the block with a random color (colors-related blocks are located in *Colour* category):
|
||||||
|
|
||||||
<img src="../assets/blocks/random-color.png" srcset="../assets/blocks/random-color.png 2x">
|
<img src="../assets/blocks/random-color.png" srcset="@source/assets/blocks/random-color.png 2x">
|
||||||
|
|
||||||
### Work with GPIO {#GPIO}
|
### Work with GPIO {#GPIO}
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ In order to perform the sensor calibration, select the *Vehicle Setup* tab and c
|
|||||||
4. Put the drone in one of the orientations marked by the red outline and wait for the appropriate outline to turn yellow.
|
4. Put the drone in one of the orientations marked by the red outline and wait for the appropriate outline to turn yellow.
|
||||||
5. Spin the drone as required until the outline turns green. Do this for all orientations.
|
5. Spin the drone as required until the outline turns green. Do this for all orientations.
|
||||||
|
|
||||||
Read more in the PX4 docs: https://docs.px4.io/v1.9.0/en/config/compass.html.
|
Read more in the PX4 docs: https://docs.px4.io/master/en/config/compass.html.
|
||||||
|
|
||||||
## Gyroscope
|
## Gyroscope
|
||||||
|
|
||||||
@@ -27,7 +27,7 @@ Read more in the PX4 docs: https://docs.px4.io/v1.9.0/en/config/compass.html.
|
|||||||
|
|
||||||
> **Warning** The drone should stay completely still during the calibration.
|
> **Warning** The drone should stay completely still during the calibration.
|
||||||
|
|
||||||
Read more in the PX4 docs: https://docs.px4.io/v1.9.0/en/config/gyroscope.html.
|
Read more in the PX4 docs: https://docs.px4.io/master/en/config/gyroscope.html.
|
||||||
|
|
||||||
## Accelerometer
|
## Accelerometer
|
||||||
|
|
||||||
@@ -38,7 +38,7 @@ Read more in the PX4 docs: https://docs.px4.io/v1.9.0/en/config/gyroscope.html.
|
|||||||
3. Put the drone in one of the orientations marked by the red outline and wait for the appropriate outline to turn yellow.
|
3. Put the drone in one of the orientations marked by the red outline and wait for the appropriate outline to turn yellow.
|
||||||
4. Hold the drone in this orientation until the outline turns green. Do this for all orientations.
|
4. Hold the drone in this orientation until the outline turns green. Do this for all orientations.
|
||||||
|
|
||||||
Read more in the PX4 docs: https://docs.px4.io/v1.9.0/en/config/accelerometer.html.
|
Read more in the PX4 docs: https://docs.px4.io/master/en/config/accelerometer.html.
|
||||||
|
|
||||||
## Level horizon
|
## Level horizon
|
||||||
|
|
||||||
@@ -50,6 +50,4 @@ Read more in the PX4 docs: https://docs.px4.io/v1.9.0/en/config/accelerometer.ht
|
|||||||
4. Press *OK*.
|
4. Press *OK*.
|
||||||
5. Wait for the calibration to finish.
|
5. Wait for the calibration to finish.
|
||||||
|
|
||||||
Read more in the PX4 docs: https://docs.px4.io/v1.9.0/en/config/level_horizon_calibration.html.
|
Read more in the PX4 docs: https://docs.px4.io/master/en/config/level_horizon_calibration.html.
|
||||||
|
|
||||||
**Next**: [RC setup](radio.md).
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# COEX Pix
|
# COEX Pix
|
||||||
|
|
||||||
The **COEX Pix** flight controller is a modified [Pixracer](https://docs.px4.io/v1.9.0/en/flight_controller/pixracer.html) FCU. It is a part of the **Clover 4** quadrotor kit.
|
The **COEX Pix** flight controller is a modified [Pixracer](https://docs.px4.io/master/en/flight_controller/pixracer.html) FCU. It is a part of the **Clover 4** quadrotor kit.
|
||||||
|
|
||||||
> **Hint** The source files of the COEX Pix flight controller are [published](https://github.com/CopterExpress/hardware/tree/master/COEX%20Pix) under the CC BY-NC-SA license.
|
> **Hint** The source files of the COEX Pix flight controller are [published](https://github.com/CopterExpress/hardware/tree/master/COEX%20Pix) under the CC BY-NC-SA license.
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ USB connection is the preferred way to connect to the flight controller.
|
|||||||
|
|
||||||
The `connected` field should have the `True` value.s
|
The `connected` field should have the `True` value.s
|
||||||
|
|
||||||
> **Hint** You need to set the `CBRK_USB_CHK` [parameter](px4_parameters.md) to 197848 for the USB connection to work.
|
> **Hint** You need to set the `CBRK_USB_CHK` [parameter](parameters.md) to 197848 for the USB connection to work.
|
||||||
|
|
||||||
## UART connection
|
## UART connection
|
||||||
|
|
||||||
@@ -50,5 +50,3 @@ In order to connect to a local or a remote [SITL](sitl.md) instance set the `fcu
|
|||||||
<arg name="fcu_conn" default="udp"/>
|
<arg name="fcu_conn" default="udp"/>
|
||||||
<arg name="fcu_ip" default="127.0.0.1"/>
|
<arg name="fcu_ip" default="127.0.0.1"/>
|
||||||
```
|
```
|
||||||
|
|
||||||
**Next**: [Using QGroundControl over Wi-Fi](gcs_bridge.md)
|
|
||||||
|
|||||||
@@ -11,33 +11,35 @@ The proposed projects have to be open-source and be compatible with the Clover q
|
|||||||
## Projects of the contest's participants {#participants}
|
## Projects of the contest's participants {#participants}
|
||||||
|
|
||||||
|Place|Team|Project|Points|
|
|Place|Team|Project|Points|
|
||||||
|-|-|-|-|
|
|:-:|-|-|-|
|
||||||
||🇰🇬 Alatoo University Team|[Облачная платформа для симулятора Клевера](https://github.com/pteacher/clover/blob/clover_simulator/docs/ru/clover-development-studio.md)||
|
||🇧🇾 FTL|[Advanced Clover 2](https://github.com/FTL-team/clover/blob/FTL-advancedClover2/docs/ru/advanced_clover_simulator.md)||
|
||||||
||🇧🇾 FTL|[Advanced Clover 2](https://github.com/FTL-team/clover/blob/FTL-advancedClover2/docs/ru/advancedclover2.md)||
|
|
||||||
||🇺🇸 EnviroFleet|[EnviroFleet](https://github.com/gueyman/clover/blob/envirofleet/docs/en/enviro_fleet.md)||
|
|
||||||
||🇻🇳 Dragon&Tanker|[Dragon&Tanker](https://github.com/uml4/clover/blob/drone_observe_autonomous_car/docs/en/dragon_and_tanker_team.md)||
|
|
||||||
||🇷🇺 Stereo|[Neural obstacle avoidance](https://github.com/den250400/clover/blob/neural-obstacle-avoidance/docs/en/neural-obstacle-avoidance.md)||
|
||🇷🇺 Stereo|[Neural obstacle avoidance](https://github.com/den250400/clover/blob/neural-obstacle-avoidance/docs/en/neural-obstacle-avoidance.md)||
|
||||||
||🇷🇺 Space clowns|[Copter For Space](https://github.com/slavikyd/clover/blob/patch-3/docs/ru/c4s.md)||
|
||🇷🇺 Space clowns|[Copter For Space](https://github.com/slavikyd/clover/blob/patch-3/docs/ru/c4s.md)||
|
||||||
||🇷🇺 R.S.|[Drone Hawk](https://github.com/slavaroot/clover/blob/droneHawkSecurity/docs/ru/drone-hawk-security.md)||
|
||🇷🇺 R.S.|[Drone Hawk](https://github.com/slavaroot/clover/blob/droneHawkSecurity/docs/ru/drone-hawk-security.md)||
|
||||||
||🇲🇾 Moopt|[IoT Water Monitoring & Optimization](https://github.com/kafechew/clover/blob/master/docs/en/moopt-uav.md)||
|
||🇲🇾 Moopt|[IoT Water Monitoring & Optimization](https://github.com/kafechew/clover/blob/master/docs/en/moopt-uav.md)||
|
||||||
||🇧🇷 Atena - Grupo SEMEAR|[Swarm in Blocks](https://github.com/Grupo-SEMEAR-USP/clover/blob/Swarm_in_Blocks/docs/en/Swarm_in_Blocks.md)||
|
||🇧🇷 Atena - Grupo SEMEAR|[Swarm in Blocks](https://github.com/Grupo-SEMEAR-USP/clover/blob/Swarm_in_Blocks/docs/en/swarm_in_blocks.md)||
|
||||||
||🇷🇺 Clevertron|[Clevertron](https://github.com/Daniel-drone/clover/blob/Clevertron-1/docs/ru/clevertron.md)||
|
|
||||||
||🇷🇺 Clover Rescue Team|[Rescue Clover](https://github.com/DevMBS/clover/blob/CloverRescueTeam/docs/ru/clover-rescue-team.md)||
|
||🇷🇺 Clover Rescue Team|[Rescue Clover](https://github.com/DevMBS/clover/blob/CloverRescueTeam/docs/ru/clover-rescue-team.md)||
|
||||||
||🇵🇱 Edgenoon|[Neural and vision-based landing method](https://github.com/edgenoon-ai/clover/blob/neural_vision_based_landing_method/docs/en/neural_vision_based_landing_method.md)||
|
|
||||||
||🇷🇺 CopterCat|[CopterCat](https://github.com/matveylapin/clover/blob/CopterCat/docs/ru/сopter_сat.md)||
|
||🇷🇺 CopterCat|[CopterCat](https://github.com/matveylapin/clover/blob/CopterCat/docs/ru/сopter_сat.md)||
|
||||||
||🇷🇺 Дрой Ронов|[Clover Swarm](https://github.com/stinger000/clever/blob/clover_swarm_request/docs/ru/clover-swarm.md)||
|
||🇷🇺 Дрой Ронов|[Clover Swarm](https://github.com/stinger000/clever/blob/clover_swarm_request/docs/ru/clover-swarm.md)||
|
||||||
||🇩🇪 Inondro|[Inondro Pix](https://github.com/Inondro/clover/blob/inondro-pix/docs/en/inondro_copterhack22_pix.md)||
|
|
||||||
||🇷🇺 V-NAV|[Visual Navigation](https://github.com/v-nav/clover/blob/v-nav_article/docs/ru/v-nav.md)||
|
|
||||||
||🇷🇺 Бизнес-гуси|[Drone Rover Climbing System](https://github.com/HexaHEX/clover/blob/CopterHack2022_Business_Geese-1/docs/ru/business_geese.md)||
|
|
||||||
||🇷🇺 fuall|[Доставка дронами](https://github.com/Silly4s/clover/blob/master/docs/ru/dostavka.md)||
|
|
||||||
||🇮🇳 DJS Phoenix|[Autonomous valet parking drone assistance](https://github.com/DJSPhoenix/clover/blob/DJSPhoenix-Ikshana/docs/en/djs_phoenix_ikshana.md)||
|
||🇮🇳 DJS Phoenix|[Autonomous valet parking drone assistance](https://github.com/DJSPhoenix/clover/blob/DJSPhoenix-Ikshana/docs/en/djs_phoenix_ikshana.md)||
|
||||||
||🇷🇺 Джедаи 1581|[Ретранслятор на базе Клевера](https://github.com/JJNIK/clover/blob/patch-1/docs/ru/1581.md)||
|
|
||||||
||🇷🇺 SPECTRE|[SPECTRE](https://github.com/alakhmenev/clover/blob/spectre_team/docs/ru/spectre_team.md)||
|
||🇷🇺 SPECTRE|[SPECTRE](https://github.com/alakhmenev/clover/blob/spectre_team/docs/ru/spectre_team.md)||
|
||||||
||🇷🇺 Lucky flight|[Swarm of Improved Clover](https://github.com/bessiaka/clover/blob/Lucky-flight/docs/ru/lucky_flight.md)||
|
|
||||||
||🇷🇺 SolidEye|[Разработка лидара без движущихся частей](https://github.com/feanorgg/clover/blob/solideye/docs/ru/solid_eye.md)||
|
|
||||||
||🇰🇬 AI_U_CLOVER|[AIU_CLOVER](https://github.com/zhibekm/clover/blob/zhibekm-patch-1/docs/en/aiu-article.md)||
|
|
||||||
||🇷🇺 Scout_Drone|[Создание поисково-спасательного беспилотного летательного аппарата](https://github.com/MustafaNatur/clover/blob/Scout_Drone.md/docs/ru/scout_drone.md)||
|
|
||||||
||🇷🇺 С305|[Система мониторинга воздуха](https://github.com/Ruslan2288/clover/blob/master/docs/ru/air_monitor.md)| |
|
||🇷🇺 С305|[Система мониторинга воздуха](https://github.com/Ruslan2288/clover/blob/master/docs/ru/air_monitor.md)| |
|
||||||
|
|✕|🇰🇬 Alatoo University Team|[Облачная платформа для симулятора Клевера](https://github.com/pteacher/clover/blob/clover_simulator/docs/ru/clover-development-studio.md)||
|
||||||
|
|✕|🇷🇺 Clevertron|[Clevertron](https://github.com/Daniel-drone/clover/blob/Clevertron-1/docs/ru/clevertron.md)||
|
||||||
|
|✕|🇵🇱 Edgenoon|[Neural and vision-based landing method](https://github.com/edgenoon-ai/clover/blob/neural_vision_based_landing_method/docs/en/neural_vision_based_landing_method.md)||
|
||||||
|
|✕|🇩🇪 Inondro|[Inondro Pix](https://github.com/Inondro/clover/blob/inondro-pix/docs/en/inondro_copterhack22_pix.md)||
|
||||||
|
|✕|🇷🇺 SolidEye|[Разработка лидара без движущихся частей](https://github.com/feanorgg/clover/blob/solideye/docs/ru/solid_eye.md)||
|
||||||
|
|✕|🇰🇬 AI_U_CLOVER|[AIU_CLOVER](https://github.com/zhibekm/clover/blob/zhibekm-patch-1/docs/en/aiu-article.md)||
|
||||||
|
|✕|🇻🇳 Dragon&Tanker|[Dragon&Tanker](https://github.com/uml4/clover/blob/drone_observe_autonomous_car/docs/en/dragon_and_tanker_team.md)||
|
||||||
|
|✕|🇷🇺 V-NAV|[Visual Navigation](https://github.com/v-nav/clover/blob/v-nav_article/docs/ru/v-nav.md)||
|
||||||
|
|✕|🇷🇺 Джедаи 1581|[Ретранслятор на базе Клевера](https://github.com/JJNIK/clover/blob/patch-1/docs/ru/1581.md)||
|
||||||
|
|✕|🇷🇺 Lucky flight|[Swarm of Improved Clover](https://github.com/bessiaka/clover/blob/Lucky-flight/docs/ru/lucky_flight.md)||
|
||||||
|
|✕|🇺🇸 EnviroFleet|[EnviroFleet](https://github.com/gueyman/clover/blob/envirofleet/docs/en/enviro_fleet.md)||
|
||||||
|
|✕|🇷🇺 Бизнес-гуси|[Drone Rover Climbing System](https://github.com/HexaHEX/clover/blob/CopterHack2022_Business_Geese-1/docs/ru/business_geese.md)||
|
||||||
|
|✕|🇷🇺 fuall|[Доставка дронами](https://github.com/Silly4s/clover/blob/master/docs/ru/dostavka.md)||
|
||||||
|
|✕|🇷🇺 Scout_Drone|[Создание поисково-спасательного беспилотного летательного аппарата](https://github.com/MustafaNatur/clover/blob/Scout_Drone.md/docs/ru/scout_drone.md)| |
|
||||||
|
|
||||||
|
✕ – teams which haven't qualified for the Final.
|
||||||
|
|
||||||
## Company case competition
|
## Company case competition
|
||||||
|
|
||||||
|
|||||||
@@ -10,9 +10,9 @@ People strive to teach artificial intelligence everything they can do themselves
|
|||||||
|
|
||||||
## Models and assembly
|
## Models and assembly
|
||||||
|
|
||||||
<img class="center" src="../assets/ddrone/full_holder.png" width="300" class="zoom">
|
<img class="center zoom" src="../assets/ddrone/full_holder.png" width="300">
|
||||||
|
|
||||||
<img class="center" src="../assets/ddrone/full_holder_in_real.jpg" width="300" class="zoom">
|
<img class="center zoom" src="../assets/ddrone/full_holder_in_real.jpg" width="300">
|
||||||
|
|
||||||
To complete the project you need to have in stock:
|
To complete the project you need to have in stock:
|
||||||
|
|
||||||
@@ -43,9 +43,9 @@ If the diameter of the can is less than the diameter of the holder, we use the p
|
|||||||
|
|
||||||
**Pressing mechanism.** To push the valve, we will use a screw drive with a fixed nut. A bar with holes will be attached to the servo, which will include the racks attached to the nut. This helps the servo to move only on one axis, up and down. We also modeled the cap for the spray can button, since the surface of the nozzle is uneven.
|
**Pressing mechanism.** To push the valve, we will use a screw drive with a fixed nut. A bar with holes will be attached to the servo, which will include the racks attached to the nut. This helps the servo to move only on one axis, up and down. We also modeled the cap for the spray can button, since the surface of the nozzle is uneven.
|
||||||
|
|
||||||
<img class="center" src="../assets/ddrone/pressing_mechanism.png" width="300" class="zoom">
|
<img class="center zoom" src="../assets/ddrone/pressing_mechanism.png" width="300">
|
||||||
|
|
||||||
<img class="center" src="../assets/ddrone/pressing_mechanism_in_real.jpg" width="300" class="zoom">
|
<img class="center zoom" src="../assets/ddrone/pressing_mechanism_in_real.jpg" width="300">
|
||||||
|
|
||||||
## Before launching
|
## Before launching
|
||||||
|
|
||||||
@@ -81,11 +81,11 @@ Now to open the web interface, click on the link [http://192.168.11.1/clover/dro
|
|||||||
|
|
||||||
Our drone is launched via [website](https://perizatkurmanbaeva.github.io/visual_ddrone). The web interface allows you to draw and encode what you draw in G-code. The coordinate data will be transmitted for further processing and execution by the copter.
|
Our drone is launched via [website](https://perizatkurmanbaeva.github.io/visual_ddrone). The web interface allows you to draw and encode what you draw in G-code. The coordinate data will be transmitted for further processing and execution by the copter.
|
||||||
|
|
||||||
<img class="center" src="../assets/ddrone/screen_2.png" width="600" class="zoom">
|
<img class="center zoom" src="../assets/ddrone/screen_2.png" width="600">
|
||||||
|
|
||||||
We pick the web interface to control the copter because it is easier for the user.
|
We pick the web interface to control the copter because it is easier for the user.
|
||||||
|
|
||||||
<img class="center" src="../assets/ddrone/instruction.png" width="300" class="zoom">
|
<img class="center zoom" src="../assets/ddrone/instruction.png" width="300">
|
||||||
|
|
||||||
## Flights
|
## Flights
|
||||||
|
|
||||||
|
|||||||
118
docs/en/educational_contests.md
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
# Educational contests
|
||||||
|
|
||||||
|
## 1. Contest for the best educational lecture {#lecture}
|
||||||
|
|
||||||
|
The Copter Express company organizes a contest for the best educational lecture with COEX Clover 4 quadcopter kit application.
|
||||||
|
|
||||||
|
The main goal of the contest is aerial robotics popularization and community development.
|
||||||
|
|
||||||
|
### Lecture requirements
|
||||||
|
|
||||||
|
* The topic of the lecture is of free choice. Programmable quadcopter kit COEX Clover 4 and/or The Clover simulation environment should be used as the main tool in the lecture.
|
||||||
|
> **Note** *The version of COEX Clover is not earlier than [version 4](https://clover.coex.tech/en/assemble_4.html). The virtual machine image is not earlier than [version 1.0](https://github.com/CopterExpress/clover_vm/releases/tag/v1.0).
|
||||||
|
* The video is uploaded on YouTube or another public platform and is public accessible.
|
||||||
|
* The language of the lecture is any. The video contains subtitles in English in case the language is made neither of English nor Russian.
|
||||||
|
* The duration of the lecture is limited from 15 min. to 3 hours.
|
||||||
|
|
||||||
|
### Requirements for the participants
|
||||||
|
|
||||||
|
* The participant must be the author of the lesson.
|
||||||
|
* Third parties can provide technical support for recording a lecture.
|
||||||
|
* The status of the participant is unlimited (student, representative of a general education institution, representative of the industry, amateur).
|
||||||
|
|
||||||
|
Applications deadline: September 1, 2022.
|
||||||
|
|
||||||
|
### How to apply?
|
||||||
|
|
||||||
|
The application to the contest is performed via the [Google Form](https://docs.google.com/forms/d/e/1FAIpQLScE2kN5dO2OYNSM8hOYzOa5Qvh2uDdd9Fjx8OnL1W93bfEBgw/viewform) where the link to the video lecture should be attached.
|
||||||
|
|
||||||
|
Participants who are the authors of the lecture are allowed to participate in the competition.
|
||||||
|
|
||||||
|
### Prizes
|
||||||
|
|
||||||
|
Based on the results of the submitted application, the jury selects the winners of the competition. The quality of the video, it is content, and audience engagement are assessed.
|
||||||
|
|
||||||
|
* 1st place: $500.
|
||||||
|
* 2nd place: $400.
|
||||||
|
* 3rd place: $300.
|
||||||
|
* 4th place: $200.
|
||||||
|
* 5th place: $100.
|
||||||
|
|
||||||
|
## 2. Contest for the best school lesson {#lesson}
|
||||||
|
|
||||||
|
The Copter Express company organizes a contest for the best school lesson with COEX Clover 4 quadcopter kit application.
|
||||||
|
|
||||||
|
The main goal of the contest is aerial robotics popularization and community development.
|
||||||
|
|
||||||
|
### Lesson requirements
|
||||||
|
|
||||||
|
* Programmable quadcopter kit COEX Clover 4 should be used as the main tool for the lesson.
|
||||||
|
> **Note** *The version of COEX Clover is not earlier than [version 4](https://clover.coex.tech/en/assemble_4.html).
|
||||||
|
* Integration of the quadcopter into any of the general education disciplines (physics, mathematics, computer science, etc.).
|
||||||
|
* Practical use of the main tool in the lesson.
|
||||||
|
* Grade - no restrictions (primary, high school).
|
||||||
|
* Lesson duration is 30-45 minutes.
|
||||||
|
* Lesson format - offline.
|
||||||
|
* The video of the lesson was filmed in the classroom of a general education institution.
|
||||||
|
|
||||||
|
### Requirements for the participants
|
||||||
|
|
||||||
|
* The participant must be the author of the lesson.
|
||||||
|
* The participant must be a teacher of a general education institution
|
||||||
|
|
||||||
|
### How to apply?
|
||||||
|
|
||||||
|
The application to the contest is performed via the [Google Form](https://docs.google.com/forms/d/e/1FAIpQLSdelVy6yQ1iN6u88KeiEIKGj7gGaM0xccSt2tiYKB46ICmjkQ/viewform).
|
||||||
|
|
||||||
|
Applications deadline: September 1, 2022.
|
||||||
|
|
||||||
|
### Prizes
|
||||||
|
|
||||||
|
Based on the results of the submitted application, the jury selects the winners of the competition. The video and material quality are assessed.
|
||||||
|
|
||||||
|
* 1st place: $500.
|
||||||
|
* 2nd place: $400.
|
||||||
|
* 3rd place: $300.
|
||||||
|
* 4th place: $200.
|
||||||
|
* 5th place: $100.
|
||||||
|
|
||||||
|
## 3. Contest for the best online course {#course}
|
||||||
|
|
||||||
|
The Copter Express company organizes a contest for the best online course with COEX Clover 4 quadcopter kit application.
|
||||||
|
|
||||||
|
The main goal of the contest is aerial robotics popularization and community development.
|
||||||
|
|
||||||
|
The course is evaluated according to a separate, publicly available lesson submitted for the contest.
|
||||||
|
|
||||||
|
### Course requirements
|
||||||
|
|
||||||
|
* The course is related to the direction of Aerial robotics.
|
||||||
|
* Programmable quadcopter kit COEX Clover 4 and/or The Clover simulation environment should be used as the main tool in the course;
|
||||||
|
> **Note** *The version of COEX Clover is not earlier than [version 4](https://clover.coex.tech/en/assemble_4.html). The virtual machine image is not earlier than [version 1.0](https://github.com/CopterExpress/clover_vm/releases/tag/v1.0).
|
||||||
|
* The course is located on a public platform (e.g., Coursera).
|
||||||
|
* The course can be either paid or free of charge. One public lesson from the course is submitted for the competition;
|
||||||
|
* The lesson submitted for the contest should be publicly accessible.
|
||||||
|
* The language of the lesson is any. The video contains subtitles in English in case the language is made neither of English nor Russian (if there is a video in the lesson).
|
||||||
|
* The duration of the course and lesson is not limited.
|
||||||
|
|
||||||
|
### Requirements for the participants
|
||||||
|
|
||||||
|
* The participant must be the author of the course.
|
||||||
|
* Third parties can provide technical support for preparing a course.
|
||||||
|
* The status of the participant is unlimited (student, representative of a general education institution, representative of the industry, amateur).
|
||||||
|
|
||||||
|
### How to apply?
|
||||||
|
|
||||||
|
The application to the contest is performed via the [Google Form](https://docs.google.com/forms/d/e/1FAIpQLSdf2Q68X4hPnFE9f3EP95AxPNnzHKqIsFHtTRT6EBKiH93wzg/viewform) where the link to the video course should be attached.
|
||||||
|
|
||||||
|
Applications deadline: September 1, 2022.
|
||||||
|
|
||||||
|
### Prizes
|
||||||
|
|
||||||
|
Based on the results of the submitted application, the members of the Commission select the winners of the competition. The quality of the material, the format of the presentation of the material, the total volume and content of the course are assessed.
|
||||||
|
|
||||||
|
* 1st place: $1000.
|
||||||
|
* 2nd place: $800.
|
||||||
|
* 3rd place: $600.
|
||||||
|
* 4th place: $400.
|
||||||
|
* 5th place: $200.
|
||||||
@@ -4,10 +4,10 @@ Main article is available at https://docs.px4.io/master/en/config/safety.html.
|
|||||||
|
|
||||||
The *Safety* panel allows you to configure actions that should be performed when a failsafe is triggered. You should at the very least configure the RC Loss failsafe, which is triggered when the RC transmitter link is lost:
|
The *Safety* panel allows you to configure actions that should be performed when a failsafe is triggered. You should at the very least configure the RC Loss failsafe, which is triggered when the RC transmitter link is lost:
|
||||||
|
|
||||||
1. Open the *Safety* panel.
|
1. In QGroundControl software, go to the *Vehicle Setup* panel and choose the *Safety* menu.
|
||||||
2. Select one of the following actions in the *RC Loss Failsafe Trigger* option:
|
2. Select one of the following actions in the *RC Loss Failsafe Trigger* option:
|
||||||
* *Land mode* – transition to automatic land mode;
|
* *Land mode* – transition to automatic land mode;
|
||||||
* *Terminate* – set all outputs to their failsafe values.
|
* *Terminate* – set all outputs to their failsafe values.
|
||||||
3. Set the timeout value before RC Loss triggers in the *RC Loss Timeout* field. We recommend setting it to 0.5 s.
|
3. Set the timeout value before RC Loss triggers in the *RC Loss Timeout* field. We recommend setting it to 2 s.
|
||||||
|
|
||||||
<img src="../assets/qgc-failsafe.png" alt="QGroundControl failsafe" class="zoom">
|
<img src="../assets/qgc-failsafe.png" alt="QGroundControl failsafe" class="zoom">
|
||||||
|
|||||||
@@ -77,19 +77,19 @@ PX4 may be compiled from the source and automatically flashed to the flight cont
|
|||||||
To do this, clone the PX4 repository:
|
To do this, clone the PX4 repository:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git clone https://github.com/PX4/Firmware.git
|
git clone https://github.com/PX4/PX4-Autopilot.git
|
||||||
```
|
```
|
||||||
|
|
||||||
Select the appropriate version (tag) using `git checkout`. Then compile and upload the firmware:
|
Select the appropriate version (tag) using `git checkout`. Then compile and upload the firmware:
|
||||||
|
|
||||||
```
|
```bash
|
||||||
make px4fmu-v4_default upload
|
make px4_fmu-v4_default upload
|
||||||
```
|
```
|
||||||
|
|
||||||
Where `px4fmu-v4_default` is the required firmware variant.
|
Where `px4_fmu-v4_default` is the required firmware variant.
|
||||||
|
|
||||||
In order to upload the `v3` firmware to Pixhawk, you may need to use the `force_upload` option:
|
In order to upload the `v3` firmware to Pixhawk, you may need to use the `force_upload` option:
|
||||||
|
|
||||||
```
|
```bash
|
||||||
make px4fmu-v3_default force-upload
|
make px4_fmu-v3_default force-upload
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Flight
|
# Flight
|
||||||
|
|
||||||
> **Info** See also official PX4 flying guide: https://docs.px4.io/v1.9.0/en/flying/.
|
> **Info** See also official PX4 flying guide: https://docs.px4.io/master/en/flying/.
|
||||||
|
|
||||||
This section explains the basics of manual controlling the quadcopter in different modes using radio remote control (for autonomous flying see "[Programming](programming.md)") section.
|
This section explains the basics of manual controlling the quadcopter in different modes using radio remote control (for autonomous flying see "[Programming](programming.md)") section.
|
||||||
|
|
||||||
@@ -106,5 +106,3 @@ When the *Kill Switch* is activated, no control signals are sent to the motors a
|
|||||||
> **Caution** Be careful, *Kill Switch* does not put the copter into *Disarmed* state!
|
> **Caution** Be careful, *Kill Switch* does not put the copter into *Disarmed* state!
|
||||||
|
|
||||||
Before disabling the *Kill Switch*, make sure the throttle stick is its down position and the aircraft is in *Disarmed* state. If the throttle stick is not in the lower position, when the *Kill Switch* is turned off, a signal corresponding to the stick position will be sent to the motors, which will lead your copter to jerk.
|
Before disabling the *Kill Switch*, make sure the throttle stick is its down position and the aircraft is in *Disarmed* state. If the throttle stick is not in the lower position, when the *Kill Switch* is turned off, a signal corresponding to the stick position will be sent to the motors, which will lead your copter to jerk.
|
||||||
|
|
||||||
**Next**: [Drone control exercises](flight_exercises.md).
|
|
||||||
|
|||||||
@@ -9,7 +9,8 @@ Main frames in the `clover` package:
|
|||||||
* `base_link` is rigidly bound to the drone. It is shown by the simplified drone model on the image above;
|
* `base_link` is rigidly bound to the drone. It is shown by the simplified drone model on the image above;
|
||||||
* `body` is bound to the drone, but its Z axis points up regardless of the drone's pitch and roll. It is shown by the red, blue and green lines in the illustration;
|
* `body` is bound to the drone, but its Z axis points up regardless of the drone's pitch and roll. It is shown by the red, blue and green lines in the illustration;
|
||||||
* <a name="navigate_target"></a>`navigate_target` is bound to the current navigation target (as set by the [navigate](simple_offboard.md#navigate) service);
|
* <a name="navigate_target"></a>`navigate_target` is bound to the current navigation target (as set by the [navigate](simple_offboard.md#navigate) service);
|
||||||
* `setpoint` is current position setpoint.
|
* `setpoint` is current position setpoint;
|
||||||
|
* `main_camera_optical` is the coordinate system, [linked to the main camera](camera_setup.md#frame);
|
||||||
|
|
||||||
Additional frames become available when [ArUco positioning system](aruco.md) is active:
|
Additional frames become available when [ArUco positioning system](aruco.md) is active:
|
||||||
|
|
||||||
|
|||||||
@@ -67,5 +67,3 @@ Change parameter `gcs_bridge` in the launch file:
|
|||||||
```
|
```
|
||||||
|
|
||||||
After opening the QGroundControl application, the connection should be established automatically.
|
After opening the QGroundControl application, the connection should be established automatically.
|
||||||
|
|
||||||
**Next**: [Remote access using SSH](ssh.md)
|
|
||||||
|
|||||||
@@ -43,14 +43,14 @@ After printing the first version of the frame we discovered the following proble
|
|||||||
|
|
||||||
To conquer those problems we made several changes. We increased the minimal thickness for the generated structures and generated a new model. We changed the settings in the slicer so that the support structure could be removed easier as well as changed the infill structure. Finally we changed the filament and increased the printing temperature. Further we concluded that printing with a water dissolvable support structure would be optimal, however as of right now we don’t have access to a printer capable of that.
|
To conquer those problems we made several changes. We increased the minimal thickness for the generated structures and generated a new model. We changed the settings in the slicer so that the support structure could be removed easier as well as changed the infill structure. Finally we changed the filament and increased the printing temperature. Further we concluded that printing with a water dissolvable support structure would be optimal, however as of right now we don’t have access to a printer capable of that.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
#### Prototype 2
|
#### Prototype 2
|
||||||
|
|
||||||
This prototype took 48 hours of printing and used 277 grams of filament including 100 grams for the support. Installation of the components is very easy as no other tools than a screwdriver are needed. This prototype was the first to take flight in January 2021. Please see [this](https://youtu.be/M4f8_JmJADM) video.
|
This prototype took 48 hours of printing and used 277 grams of filament including 100 grams for the support. Installation of the components is very easy as no other tools than a screwdriver are needed. This prototype was the first to take flight in January 2021. Please see [this](https://youtu.be/M4f8_JmJADM) video.
|
||||||
|
|
||||||
<p float="left">
|
<p float="left">
|
||||||
<img src="../assets/generative-design-frame/p21.JPG" width="32%" class="zoom"/>
|
<img src="../assets/generative-design-frame/p21.jpg" width="32%" class="zoom"/>
|
||||||
<img src="../assets/generative-design-frame/p22.jpg" width="32%" class="zoom"/>
|
<img src="../assets/generative-design-frame/p22.jpg" width="32%" class="zoom"/>
|
||||||
<img src="../assets/generative-design-frame/p23.jpg" width="32%" class="zoom"/>
|
<img src="../assets/generative-design-frame/p23.jpg" width="32%" class="zoom"/>
|
||||||
</p>
|
</p>
|
||||||
@@ -73,7 +73,7 @@ Videos:
|
|||||||
|
|
||||||
In this final prototype we have changed the preserved geometry on the bottom to form a rectangle for added stability. We have also changed some of the forces on the points we observed breakings in our previous tests. We have also updated the prop guard to make it more stable and increased the area around the screws, so it would break harder. The frame without the prop guard weighs only 150g making it significantly lighter than the default frame.
|
In this final prototype we have changed the preserved geometry on the bottom to form a rectangle for added stability. We have also changed some of the forces on the points we observed breakings in our previous tests. We have also updated the prop guard to make it more stable and increased the area around the screws, so it would break harder. The frame without the prop guard weighs only 150g making it significantly lighter than the default frame.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
### Benefits
|
### Benefits
|
||||||
|
|
||||||
|
|||||||
@@ -15,5 +15,3 @@ The RPi image for Clover contains all the necessary software for working with Cl
|
|||||||
<img src="../assets/etcher.png" class="zoom">
|
<img src="../assets/etcher.png" class="zoom">
|
||||||
|
|
||||||
After flashing the image on the MicroSD-card, you can [connect to the Clover over Wi-Fi](wifi.md), use [wireless connection in QGroundControl](gcs_bridge.md), gain access to the Raspberry [over SSH](ssh.md) and use all the other features.
|
After flashing the image on the MicroSD-card, you can [connect to the Clover over Wi-Fi](wifi.md), use [wireless connection in QGroundControl](gcs_bridge.md), gain access to the Raspberry [over SSH](ssh.md) and use all the other features.
|
||||||
|
|
||||||
**Next:** [Connecting over Wi-Fi](wifi.md).
|
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ rostopic echo /rangefinder/range
|
|||||||
|
|
||||||
> **Hint** We recommend using our [custom PX4 firmware for Clover](firmware.md#modified-firmware-for-clover) for best laser rangefinder support.
|
> **Hint** We recommend using our [custom PX4 firmware for Clover](firmware.md#modified-firmware-for-clover) for best laser rangefinder support.
|
||||||
|
|
||||||
PX4 should be properly [configured](px4_parameters.md) to use the rangefinder data.
|
PX4 should be properly [configured](parameters.md) to use the rangefinder data.
|
||||||
|
|
||||||
Set the following parameters when EKF2 is used (`SYS_MC_EST_GROUP` = `ekf2`):
|
Set the following parameters when EKF2 is used (`SYS_MC_EST_GROUP` = `ekf2`):
|
||||||
|
|
||||||
@@ -75,7 +75,7 @@ from sensor_msgs.msg import Range
|
|||||||
|
|
||||||
# ...
|
# ...
|
||||||
|
|
||||||
data = rospy.wait_for_message('rangefinder/range', Range)
|
dist = rospy.wait_for_message('rangefinder/range', Range).range
|
||||||
```
|
```
|
||||||
|
|
||||||
### Data visualization
|
### Data visualization
|
||||||
|
|||||||
@@ -158,3 +158,9 @@ Current LED strip state is published in the `/led/state` ROS topic. You can view
|
|||||||
```bash
|
```bash
|
||||||
rostopic echo /led/state
|
rostopic echo /led/state
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Using the same topic you can get the configured number os LEDs, using Python:
|
||||||
|
|
||||||
|
```python
|
||||||
|
led_count = len(rospy.wait_for_message('led/state', LEDStateArray, timeout=10).leds)
|
||||||
|
```
|
||||||
|
|||||||
@@ -63,3 +63,64 @@ Then connect the signal output of the circuit to the selected port and solder th
|
|||||||
2. Use a zip tie to pull the assembled circuit to the back of the deck.
|
2. Use a zip tie to pull the assembled circuit to the back of the deck.
|
||||||
3. Plug the Arduino *D11* signal pin into one of the *AUX* pins on the flight controller.
|
3. Plug the Arduino *D11* signal pin into one of the *AUX* pins on the flight controller.
|
||||||
4. Plug the power wire of the electromagnetic gripper to JST 5V.
|
4. Plug the power wire of the electromagnetic gripper to JST 5V.
|
||||||
|
|
||||||
|
## Setting up electromagnetic gripper
|
||||||
|
|
||||||
|
To control the magnet through Arduino Nano, use the following code:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
void setup() {
|
||||||
|
pinMode(11, INPUT);
|
||||||
|
pinMode(13, OUTPUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
if (int duration = pulseIn(11, HIGH) > 1200) {
|
||||||
|
digitalWrite(13, HIGH);
|
||||||
|
} else {
|
||||||
|
digitalWrite(13, LOW);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
To monitor the status of the electromagnetic gripper, you can connect the *ws281x* LED strip (included to Clover kit). Connect it to power +5v – 5v, ground GND – GND, and signal wire DIN – Arduino D12.
|
||||||
|
|
||||||
|
To control the magnet and monitor it using the LED strip, use the following code:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#include <Adafruit_NeoPixel.h>
|
||||||
|
#define NUMPIXELS 72
|
||||||
|
#define PIN 12
|
||||||
|
int pin = 11;
|
||||||
|
int led = 13;
|
||||||
|
|
||||||
|
unsigned long duration;
|
||||||
|
Adafruit_NeoPixel strip (NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
strip.begin();
|
||||||
|
strip.setBrightness(10);
|
||||||
|
Serial.begin(9600);
|
||||||
|
pinMode(pin, INPUT);
|
||||||
|
pinMode(led, OUTPUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
duration = pulseIn(pin, HIGH);
|
||||||
|
Serial.println(duration);
|
||||||
|
delay(100);
|
||||||
|
if (duration >= 1500) {
|
||||||
|
digitalWrite(led, HIGH);
|
||||||
|
for (int i = -1; i < NUMPIXELS; i++) {
|
||||||
|
strip.setPixelColor(i, strip.Color(255, 0, 0));
|
||||||
|
strip.show();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
digitalWrite(led, LOW);
|
||||||
|
for (int i = -1; i < NUMPIXELS; i++) {
|
||||||
|
strip.setPixelColor(i, strip.Color(0, 255, 0));
|
||||||
|
strip.show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ Examples of MAVLink messages:
|
|||||||
* `GLOBAL_POSITION_INT` – global position of the quadcopter (latitude/longitude/altitude);
|
* `GLOBAL_POSITION_INT` – global position of the quadcopter (latitude/longitude/altitude);
|
||||||
* `COMMAND_LONG` – a command to the quadcopter (take off, land, toggle modes, etc).
|
* `COMMAND_LONG` – a command to the quadcopter (take off, land, toggle modes, etc).
|
||||||
|
|
||||||
A complete list of MAVLink messages is available in [MAVLink documentation] (http://mavlink.org/messages/common).
|
A complete list of MAVLink messages is available in [MAVLink documentation](https://mavlink.io/en/messages/common.html).
|
||||||
|
|
||||||
### System, system component
|
### System, system component
|
||||||
|
|
||||||
|
|||||||
@@ -56,13 +56,13 @@
|
|||||||
12. Install the assembled grip onto the aircraft from below.
|
12. Install the assembled grip onto the aircraft from below.
|
||||||
|
|
||||||
<div class="image-group">
|
<div class="image-group">
|
||||||
|
<img src="../assets/mechanical_grip/mech_grip_15.png" width=300 class="zoom border">
|
||||||
<img src="../assets/mechanical_grip/mech_grip_16.png" width=300 class="zoom border">
|
<img src="../assets/mechanical_grip/mech_grip_16.png" width=300 class="zoom border">
|
||||||
<img src="../assets/mechanical_grip/mech_grip_17.png" width=300 class="zoom border">
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
13. Insert the servo cable into the *AUX* 1-2 output on the flight controller.
|
13. Insert the servo cable into the *AUX* 1-2 output on the flight controller.
|
||||||
|
|
||||||
<img src="../assets/mechanical_grip/mech_grip_18.png" width=300 class="zoom border center">
|
<img src="../assets/mechanical_grip/mech_grip_17.png" width=300 class="zoom border center">
|
||||||
|
|
||||||
14. Go to the *Radio* tab to control capture with the remote control.
|
14. Go to the *Radio* tab to control capture with the remote control.
|
||||||
15. In the *AUX 1/2 Passthrough RC channel* parameter, select the desired channel.
|
15. In the *AUX 1/2 Passthrough RC channel* parameter, select the desired channel.
|
||||||
|
|||||||
@@ -4,10 +4,11 @@ PX4 **mode** determines how the vehicle should react to commands and RC signals.
|
|||||||
|
|
||||||
In order to configure flight modes:
|
In order to configure flight modes:
|
||||||
|
|
||||||
1. Open the *Vehicle Setup* tab in QGroundControl.
|
1. Open the *Vehicle Setup* panel in QGroundControl.
|
||||||
2. Select the *Flight Modes* menu.
|
2. Select the *Flight Modes* menu.
|
||||||
3. Choose SwC (Channel 6) as mode selection switch.
|
3. Set the *Mode Channel* to the SwC switch (*Channel 6*).
|
||||||
4. Set desired flight modes.
|
4. Optionally, set the *Emergency Kill Switch Channel* to SwA switch (*Channel 5*).
|
||||||
|
5. Set desired flight modes.
|
||||||
|
|
||||||
The following flight modes are recommended:
|
The following flight modes are recommended:
|
||||||
|
|
||||||
@@ -15,8 +16,8 @@ In order to configure flight modes:
|
|||||||
* Flight Mode 4: *Altitude*.
|
* Flight Mode 4: *Altitude*.
|
||||||
* Flight Mode 6: *Position*.
|
* Flight Mode 6: *Position*.
|
||||||
|
|
||||||
5. Check mode switching by changing the switch position.
|
6. Check mode switching by changing the switch position.
|
||||||
6. Choose SwA (Channel 5) as emergency motor stop (*Kill switch*).
|
7. Choose SwA (Channel 5) as emergency motor stop (*Kill switch*).
|
||||||
|
|
||||||
<img src="../assets/qgc-modes.png" class="zoom" alt="QGroundControl modes">
|
<img src="../assets/qgc-modes.png" class="zoom" alt="QGroundControl modes">
|
||||||
|
|
||||||
@@ -45,5 +46,3 @@ In autonomous flight modes the quadcopter ignores the control signals from the t
|
|||||||
* **AUTO.LAND** – the copter lands at the current position.
|
* **AUTO.LAND** – the copter lands at the current position.
|
||||||
|
|
||||||
Additional information: https://dev.px4.io/en/concept/flight_modes.html.
|
Additional information: https://dev.px4.io/en/concept/flight_modes.html.
|
||||||
|
|
||||||
**Next**: [Power setup](power.md).
|
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
# Use of Optical Flow
|
# Use of Optical Flow
|
||||||
|
|
||||||
Running the technology "Optical Flow" offers the possibility of POSCTL flight mode, and autonomous flight operating on a camera pointed downwards that detects changes of ground texture.
|
Running the "Optical Flow" function offers the possibility of POSCTL flight mode, and autonomous flight operating on a camera pointed downwards that detects changes of ground texture.
|
||||||
|
|
||||||
## Enabling
|
## Enabling
|
||||||
|
|
||||||
> **Hint** It is recommended to use [special PX4 firmware for Clover](firmware.md).
|
> **Hint** For Optical Flow to work it's required that the laser rangefinder is [connected and configured](laser.md).
|
||||||
|
|
||||||
The use of a rangefinder is essential. [Connect and setup laser-ranging sensor VL53L1X](laser.md), according to the manual.
|
|
||||||
|
|
||||||
Enable Optical Flow in the file `~/catkin_ws/src/clover/clover/launch/clover.launch`:
|
Enable Optical Flow in the file `~/catkin_ws/src/clover/clover/launch/clover.launch`:
|
||||||
|
|
||||||
@@ -14,7 +12,7 @@ Enable Optical Flow in the file `~/catkin_ws/src/clover/clover/launch/clover.lau
|
|||||||
<arg name="optical_flow" default="true"/>
|
<arg name="optical_flow" default="true"/>
|
||||||
```
|
```
|
||||||
|
|
||||||
Optical Flow publishes data in `mavros/px4flow/raw/send` topic. In the topic `optical_flow/debug` is also published a visualization, that can be viewed with [web_video_server](web_video_server.md).
|
Optical Flow publishes data in `/mavros/px4flow/raw/send` topic. In the topic `/optical_flow/debug` is also published a visualization, that can be viewed with [web_video_server](web_video_server.md).
|
||||||
|
|
||||||
> **Info** Correct connection and [setup](camera.md) of the camera module is needed for proper functioning.
|
> **Info** Correct connection and [setup](camera.md) of the camera module is needed for proper functioning.
|
||||||
|
|
||||||
@@ -31,7 +29,7 @@ When using **EKF2** (parameter `SYS_MC_EST_GROUP` = `ekf2`):
|
|||||||
* `EKF2_OF_N_MAX` - 0.2.
|
* `EKF2_OF_N_MAX` - 0.2.
|
||||||
* `SENS_FLOW_ROT` – No rotation.
|
* `SENS_FLOW_ROT` – No rotation.
|
||||||
* `SENS_FLOW_MAXHGT` – 4.0 (for the rangefinder VL53L1X)
|
* `SENS_FLOW_MAXHGT` – 4.0 (for the rangefinder VL53L1X)
|
||||||
* `SENS_FLOW_MINHGT` – 0.01 (for the rangefinder VL53L1X)
|
* `SENS_FLOW_MINHGT` – 0.0 (for the rangefinder VL53L1X)
|
||||||
* Optional: `EKF2_HGT_MODE` – range sensor (cf. [rangefinder setup](laser.md)).
|
* Optional: `EKF2_HGT_MODE` – range sensor (cf. [rangefinder setup](laser.md)).
|
||||||
|
|
||||||
When using **LPE** (parameter `SYS_MC_EST_GROUP` = `local_position_estimator, attitude_estimator_q`):
|
When using **LPE** (parameter `SYS_MC_EST_GROUP` = `local_position_estimator, attitude_estimator_q`):
|
||||||
@@ -43,7 +41,7 @@ When using **LPE** (parameter `SYS_MC_EST_GROUP` = `local_position_estimator, at
|
|||||||
* `LPE_FLW_RR` – 0.0.
|
* `LPE_FLW_RR` – 0.0.
|
||||||
* `SENS_FLOW_ROT` – No rotation.
|
* `SENS_FLOW_ROT` – No rotation.
|
||||||
* `SENS_FLOW_MAXHGT` – 4.0 (for the rangefinder VL53L1X)
|
* `SENS_FLOW_MAXHGT` – 4.0 (for the rangefinder VL53L1X)
|
||||||
* `SENS_FLOW_MINHGT` – 0.01 (for the rangefinder VL53L1X)
|
* `SENS_FLOW_MINHGT` – 0.0 (for the rangefinder VL53L1X)
|
||||||
* Optional: `LPE_FUSION` – flag 'pub agl as lpos down' is on (see [rangefinder setup](laser.md).
|
* Optional: `LPE_FUSION` – flag 'pub agl as lpos down' is on (see [rangefinder setup](laser.md).
|
||||||
|
|
||||||
[The `selfcheck.py` utility](selfcheck.md) will help you verify that all settings are correctly set.
|
[The `selfcheck.py` utility](selfcheck.md) will help you verify that all settings are correctly set.
|
||||||
|
|||||||
112
docs/en/parameters.md
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
# PX4 Parameters
|
||||||
|
|
||||||
|
Full documentation on PX4 parameters: https://docs.px4.io/master/en/advanced_config/parameter_reference.html.
|
||||||
|
|
||||||
|
For changing PX4 parameters, use QGroundControl software, [connect to Clover over Wi-Fi](gcs_bridge.md) or USB. Go to *Vehicle Setup* panel (click on the QGroundControl logo in the top-left corner) and choose *Parameters* menu.
|
||||||
|
|
||||||
|
## Recommended values
|
||||||
|
|
||||||
|
### Common parameters
|
||||||
|
|
||||||
|
|Parameter|Value|Comment|
|
||||||
|
|-|-|-|
|
||||||
|
|`SENS_FLOW_ROT`|0 (*No rotation*)|If using *PX4Flow* hardware, keep the default value|
|
||||||
|
|`SENS_FLOW_MINHGT`|0.0|For [VL53L1X](laser.md) rangefinder|
|
||||||
|
|`SENS_FLOW_MAXHGT`|4.0|For [VL53L1X](laser.md) rangefinder|
|
||||||
|
|`SENS_FLOW_MAXR`|10.0||
|
||||||
|
|`SYS_HAS_MAG`|0|If impossible to run the magnetometer (*No mags found* error)|
|
||||||
|
|
||||||
|
### Estimator subsystem parameters
|
||||||
|
|
||||||
|
In case of using LPE ([COEX patched firmware](firmware.md)):
|
||||||
|
|
||||||
|
|Parameter|Value|Comment|
|
||||||
|
|-|-|-|
|
||||||
|
|`LPE_FUSION`|86|Checkboxes: *flow* + *vis* + *land Detector* + *gyro comp*. If flying over horizontal floor *pub agl as lpos down* checkbox is allowed.<br>Details: [Optical Flow](optical_flow.md), [ArUco markers](aruco_map.md), [GPS](gps.md).|
|
||||||
|
|`LPE_VIS_DELAY`|0.0||
|
||||||
|
|`LPE_VIS_Z`|0.1||
|
||||||
|
|`LPE_FLW_SCALE`|1.0||
|
||||||
|
|`LPE_FLW_R`|0.2||
|
||||||
|
|`LPE_FLW_RR`|0.0||
|
||||||
|
|`LPE_FLW_QMIN`|10||
|
||||||
|
|`ATT_W_EXT_HDG`|0.5|Enabling usage of external yaw angle (when navigating using [markers map](aruco_map.md))|
|
||||||
|
|`ATT_EXT_HDG_M`|1 (*Vision*)||
|
||||||
|
|`ATT_W_MAG`|0|Disabling usage of the magnetometer (when navigating indoor)|
|
||||||
|
|
||||||
|
In case of using EKF2 (official firmware):
|
||||||
|
|
||||||
|
<!-- markdownlint-disable MD044 -->
|
||||||
|
|
||||||
|
|Parameter|Value|Comment|
|
||||||
|
|-|-|-|
|
||||||
|
|`EKF2_AID_MASK`|27|Checkboxes: (optionally) *gps* + *flow* + *vision position* + *vision yaw*.<br>Details: [Optical Flow](optical_flow.md), [ArUco markers](aruco_map.md), [GPS](gps.md).|
|
||||||
|
|`EKF2_OF_DELAY`|0||
|
||||||
|
|`EKF2_OF_QMIN`|10||
|
||||||
|
|`EKF2_OF_N_MIN`|0.05||
|
||||||
|
|`EKF2_OF_N_MAX`|0.2||
|
||||||
|
|`EKF2_HGT_MODE`|2 (*Range sensor*)|If the [rangefinder](laser.md) is present and flying over horizontal floor|
|
||||||
|
|`EKF2_EVA_NOISE`|0.1||
|
||||||
|
|`EKF2_EVP_NOISE`|0.1||
|
||||||
|
|`EKF2_EV_DELAY`|0||
|
||||||
|
|`EKF2_MAG_TYPE`|5 (*None*)|Disabling usage of the magnetometer (when navigating indoor)|
|
||||||
|
|
||||||
|
<!-- markdownlint-enable MD031 -->
|
||||||
|
|
||||||
|
> **Info** See also: list of default parameters of the [Clover simulator](simulation.md): https://github.com/CopterExpress/clover/blob/master/clover_simulation/airframes/4500_clover.
|
||||||
|
|
||||||
|
## Additional information
|
||||||
|
|
||||||
|
The `SYS_MC_EST_GROUP` parameter defines the estimator subsystem to use.
|
||||||
|
|
||||||
|
Estimator subsystem is a group of modules that calculates the current state of the copter using readings from the sensors. The copter state includes:
|
||||||
|
|
||||||
|
* Angle rate of the copter – pitch_rate, roll_rate, yaw_rate;
|
||||||
|
* Copter orientation (in the local coordinate system) – pitch, roll, yaw (one of presentations);
|
||||||
|
* Copter position (in the local coordinate system) – x, y, z;
|
||||||
|
* Copter speed (in the local coordinate system) – vx, vy, vz;
|
||||||
|
* Global coordinates of the copter – latitude, longitude, altitude;
|
||||||
|
* Altitude above the surface;
|
||||||
|
* Other parameters (the drift of gyroscopes, wind speed, etc.).
|
||||||
|
|
||||||
|
`SYS_AUTOCONFIG` — resets all parameters (sets to 1).
|
||||||
|
|
||||||
|
## EKF2
|
||||||
|
|
||||||
|
`EKF2_AID_MASK` — selects sensors that are used by EKF2 to calculate the copter state.
|
||||||
|
|
||||||
|
`EKF2_HGT_MODE` is the main source of height data (z in the local coordinate system):
|
||||||
|
|
||||||
|
* 0 – pressure reading on the barometer.
|
||||||
|
* 1 – GPS.
|
||||||
|
* 2 – distance meter (for example, vl53l1x).
|
||||||
|
* 3 – data from VPE.
|
||||||
|
|
||||||
|
Variant 2 is the most accurate; however, it is correct to use it only if the surface the copter flies over is flat. Otherwise, the Z axis origin will move up and down with the altitude of the surface.
|
||||||
|
|
||||||
|
## Multicopter Position Control
|
||||||
|
|
||||||
|
These parameters adjust the flight of the copter by position (POSCTL, OFFBOARD, AUTO modes).
|
||||||
|
|
||||||
|
`MPC_THR_HOVER` — hovering throttle. This option is to set to the approximate percentage of throttle needed to make the copter maintain its altitude. If copter has a tendency to gain or lose altitude during the hovering mode, reduce or increase this value.
|
||||||
|
|
||||||
|
`MPC_XY_P` – position factor *P* of the ESC. This parameter affects how sharply the copter will react to the position commands. A too high value may cause overshoots.
|
||||||
|
|
||||||
|
`MPC_XY_VEL_P` – speed factor *P* of the ESC. This parameter also affects the accuracy and sharpness of copter execution of the given position. A too high value may cause overshoots.
|
||||||
|
|
||||||
|
`MPC_XY_VEL_MAX` — the maximum horizontal speed in POSCTL, OFFBOARD, AUTO modes.
|
||||||
|
|
||||||
|
`MPC_Z_P`, `MPC_Z_VEL_P` – vertical position and speed factors *P* of the ESCs they determine the copter's ability to maintain the desired altitude.
|
||||||
|
|
||||||
|
`MPC_LAND_SPEED` is the vertical velocity of landing in the LAND mode.
|
||||||
|
|
||||||
|
## LPE + Q attitude estimator
|
||||||
|
|
||||||
|
These parameters configure the behavior of the `lpe` and `q` modules, which compute the state (orientation, position) of the copter. These parameters apply **only** if the `SYS_MC_EST_GROUP` parameter is set to `1` (local_position_estimator, attitude_estimator_q).
|
||||||
|
|
||||||
|
## Commander
|
||||||
|
|
||||||
|
Prearm checks, switching the modes and states of the copter.
|
||||||
|
|
||||||
|
## Sensors
|
||||||
|
|
||||||
|
Enabling, disabling and configuring various sensors.
|
||||||
@@ -6,15 +6,16 @@ Open the *Vehicle Setup* tab and select the *Power* menu.
|
|||||||
|
|
||||||
> **Note** Power sensor calibration should be done with the battery pack connected to the drone.
|
> **Note** Power sensor calibration should be done with the battery pack connected to the drone.
|
||||||
|
|
||||||
If there is no voltage indicator or manual calibration is not possible, set the average value of the voltage divider for the Clover 4 kit (*Voltage divider* = 11).
|
1. In QGroundControl software, go the *Vehicle Setup* panel and choose the *Power* menu.
|
||||||
|
2. Set the *Number of cells* parameter according to the number of cells in your battery (*3* for the Clover 4 drone).
|
||||||
1. Set the *Number of cells* parameter according to the number of cells in your battery (*3* for the Clover 4 drone).
|
3. Calculate the voltage divider:
|
||||||
2. Calculate the voltage divider:
|
|
||||||
* Measure voltage across the battery (you may use a battery voltage tester for that).
|
* Measure voltage across the battery (you may use a battery voltage tester for that).
|
||||||
* Press the *Calculate* button next to the *Voltage divider* label.
|
* Press the *Calculate* button next to the *Voltage divider* label.
|
||||||
* Put the battery voltage into the prompt and click *Calculate*.
|
* Put the battery voltage into the prompt and click *Calculate*.
|
||||||
* Press *Close* to save the calculated value.
|
* Press *Close* to save the calculated value.
|
||||||
|
|
||||||
|
If there is no voltage indicator or manual calibration is not possible, set the average value of the voltage divider for the Clover 4 kit (*Voltage divider* = 11).
|
||||||
|
|
||||||
<img src="../assets/qgc-voltage-divider.png" class="zoom">
|
<img src="../assets/qgc-voltage-divider.png" class="zoom">
|
||||||
|
|
||||||
Further reading: https://docs.qgroundcontrol.com/en/SetupView/Power.html.
|
Further reading: https://docs.qgroundcontrol.com/en/SetupView/Power.html.
|
||||||
@@ -30,6 +31,4 @@ Further reading: https://docs.qgroundcontrol.com/en/SetupView/Power.html.
|
|||||||
|
|
||||||
<img src="../assets/qgc-power.png" class="zoom">
|
<img src="../assets/qgc-power.png" class="zoom">
|
||||||
|
|
||||||
Further reading: https://docs.px4.io/v1.9.0/en/advanced_config/esc_calibration.html.
|
Further reading: https://docs.px4.io/master/en/advanced_config/esc_calibration.html.
|
||||||
|
|
||||||
**Next**: [Failsafe configuration](failsafe.md)
|
|
||||||
|
|||||||
@@ -51,8 +51,6 @@ python3 flight.py
|
|||||||
Below is a complete flight program that performs a takeoff, flies forward and lands:
|
Below is a complete flight program that performs a takeoff, flies forward and lands:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
#coding: utf8
|
|
||||||
|
|
||||||
import rospy
|
import rospy
|
||||||
from clover import srv
|
from clover import srv
|
||||||
from std_srvs.srv import Trigger
|
from std_srvs.srv import Trigger
|
||||||
|
|||||||
@@ -1,72 +0,0 @@
|
|||||||
# PX4 Parameters
|
|
||||||
|
|
||||||
Main article: https://dev.px4.io/en/advanced/parameter_reference.html
|
|
||||||
|
|
||||||
> **Note** This is a description some of the most important PX4 parameters as of version 1.8.0. The full list is available at the link above.
|
|
||||||
|
|
||||||
To change PX4 parameters, you can use the QGroundControl application [by connecting to Clover via Wi-Fi](gcs_bridge.md):
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
## Main parameters
|
|
||||||
|
|
||||||
The most important parameters are listed in this paragraph.
|
|
||||||
|
|
||||||
`SYS_MC_EST_GROUP` – select the estimator module.
|
|
||||||
|
|
||||||
This is a group of modules that calculates the current state of the copter using readings from the sensors. The copter state includes:
|
|
||||||
|
|
||||||
* Angle rate of the copter – pitch_rate, roll_rate, yaw_rate;
|
|
||||||
* Copter orientation (in the local coordinate system) – pitch, roll, yaw (one of presentations);
|
|
||||||
* Copter position (in the local coordinate system) – x, y, z;
|
|
||||||
* Copter speed (in the local coordinate system) – vx, vy, vz;
|
|
||||||
* Global coordinates of the copter – latitude, longitude, altitude;
|
|
||||||
* Altitude above the surface;
|
|
||||||
* Other parameters (the drift of gyroscopes, wind speed, etc.).
|
|
||||||
|
|
||||||
`SYS_AUTOCONFIG` — resets all parameters (sets to 1).
|
|
||||||
|
|
||||||
## EKF2
|
|
||||||
|
|
||||||
`EKF2_AID_MASK` — selects sensors that are used by EKF2 to calculate the copter state.
|
|
||||||
|
|
||||||
`EKF2_HGT_MODE` is the main source of height data (z in the local coordinate system):
|
|
||||||
|
|
||||||
* 0 – pressure reading on the barometer.
|
|
||||||
* 1 – GPS.
|
|
||||||
* 2 – distance meter (for example, vl53l1x).
|
|
||||||
* 3 – data from VPE.
|
|
||||||
|
|
||||||
Variant 2 is the most accurate; however, it is correct to use it only if the surface the copter flies over is flat. Otherwise, the Z axis origin will move up and down with the altitude of the surface.
|
|
||||||
|
|
||||||
## Multicopter Position Control
|
|
||||||
|
|
||||||
These parameters adjust the flight of the copter by position (POSCTL, OFFBOARD, AUTO modes).
|
|
||||||
|
|
||||||
`MPC_THR_HOVER` — hovering throttle. This option is to set to the approximate percentage of throttle needed to make the copter maintain its altitude. If copter has a tendency to gain or lose altitude during the hovering mode, reduce or increase this value.
|
|
||||||
|
|
||||||
`MPC_XY_P` – position factor *P* of the ESC. This parameter affects how sharply the copter will react to the position commands. A too high value may cause overshoots.
|
|
||||||
|
|
||||||
`MPC_XY_VEL_P` – speed factor *P* of the ESC. This parameter also affects the accuracy and sharpness of copter execution of the given position. A too high value may cause overshoots.
|
|
||||||
|
|
||||||
`MPC_XY_VEL_MAX` — the maximum horizontal speed in POSCTL, OFFBOARD, AUTO modes.
|
|
||||||
|
|
||||||
`MPC_Z_P`, `MPC_Z_VEL_P` – vertical position and speed factors *P* of the ESCs they determine the copter's ability to maintain the desired altitude.
|
|
||||||
|
|
||||||
`MPC_LAND_SPEED` is the vertical velocity of landing in the LAND mode.
|
|
||||||
|
|
||||||
## LPE + Q attitude estimator
|
|
||||||
|
|
||||||
These parameters configure the behavior of the `lpe` and `q` modules, which compute the state (orientation, position) of the copter. These parameters apply **only** if the `SYS_MC_EST_GROUP` parameter is set to `1` (local_position_estimator, attitude_estimator_q)
|
|
||||||
|
|
||||||
TODO
|
|
||||||
|
|
||||||
## Commander
|
|
||||||
|
|
||||||
Prearm checks, switching the modes and states of the copter.
|
|
||||||
|
|
||||||
## Sensors
|
|
||||||
|
|
||||||
Enabling, disabling and configuring various sensors.
|
|
||||||
|
|
||||||
TODO
|
|
||||||
@@ -9,7 +9,7 @@ Before connecting and calibrating the RC, make sure that:
|
|||||||
|
|
||||||
## Connecting the RC transmitter
|
## Connecting the RC transmitter
|
||||||
|
|
||||||
1. Open the *Vehicle Setup* tab and select the *Radio* menu.
|
1. In QGroundControl software, go the *Vehicle Setup* panel and choose the *Radio* menu.
|
||||||
2. Power on the transmitter by sliding the **POWER** slider up.
|
2. Power on the transmitter by sliding the **POWER** slider up.
|
||||||
3. Make sure the transmitter-receiver link is working.
|
3. Make sure the transmitter-receiver link is working.
|
||||||
|
|
||||||
@@ -36,5 +36,3 @@ Before connecting and calibrating the RC, make sure that:
|
|||||||
8. When you get the *"All settings have been captured. Click Next to write the new parameters to your board"*, press *Next*.
|
8. When you get the *"All settings have been captured. Click Next to write the new parameters to your board"*, press *Next*.
|
||||||
|
|
||||||
Further reading: https://docs.qgroundcontrol.com/en/SetupView/Radio.html
|
Further reading: https://docs.qgroundcontrol.com/en/SetupView/Radio.html
|
||||||
|
|
||||||
**Next**: [Flight modes](modes.md).
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
Raspberry Pi
|
Raspberry Pi
|
||||||
============
|
============
|
||||||
|
|
||||||
**Raspberry Pi** is a single-board computer that fits in the palm, created on the basis of ARM mobile microprocessor. It features low energy consumption, and it can even run on solar panels. Raspberry Pi 3 is included in the kits for programmable quadcopters "Clover".
|
**Raspberry Pi** is a single-board computer that fits in the palm, created on the basis of ARM mobile microprocessor. It features low energy consumption, and it can even run on solar panels. A Raspberry Pi is included in the kits for programmable quadcopters "Clover".
|
||||||
|
|
||||||
<img src="../assets/raspberry.png" class="center zoom" alt="Raspberry Pi 3" width="400">
|
<img src="../assets/raspberry.png" class="center zoom" alt="Raspberry Pi 3" width="400">
|
||||||
|
|
||||||
@@ -15,5 +15,3 @@ Technical specifications:
|
|||||||
* An HDMI port.
|
* An HDMI port.
|
||||||
|
|
||||||
Raspberry Pi is connected to the flight controller in the Clover kit and is used as a companion computer. It can be used to [connect to the drone over Wi-Fi](wifi.md), perform autonomous flights, access peripherals and much more.
|
Raspberry Pi is connected to the flight controller in the Clover kit and is used as a companion computer. It can be used to [connect to the drone over Wi-Fi](wifi.md), perform autonomous flights, access peripherals and much more.
|
||||||
|
|
||||||
**Next**: [Raspberry Pi image](image.md)
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ Controlling Clover from a smartphone
|
|||||||
|
|
||||||
To control Clover from a smartphone via Wi-Fi, you have to install the appropriate application – [iOS](https://itunes.apple.com/ru/app/clever-rc/id1396166572?mt=8), Android (https://play.google.com/store/apps/details?id=express.copter.cleverrc).
|
To control Clover from a smartphone via Wi-Fi, you have to install the appropriate application – [iOS](https://itunes.apple.com/ru/app/clever-rc/id1396166572?mt=8), Android (https://play.google.com/store/apps/details?id=express.copter.cleverrc).
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
> **Warning** The mobile transmitter is mainly intended for indoor flights to the range not exceeding 10-15 m. Many Wi-Fi networks may also impair responsiveness and the range of the transmitter.
|
> **Warning** The mobile transmitter is mainly intended for indoor flights to the range not exceeding 10-15 m. Many Wi-Fi networks may also impair responsiveness and the range of the transmitter.
|
||||||
|
|
||||||
|
|||||||
@@ -66,8 +66,9 @@ Connect your receiver to the RC IN port on your flight controller:
|
|||||||
<img src="../assets/flysky_a8s/14_coexpix_rcin.png" width=300 class="zoom border center" alt="coex pix connection">
|
<img src="../assets/flysky_a8s/14_coexpix_rcin.png" width=300 class="zoom border center" alt="coex pix connection">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
> **Hint** Double check that you're using the RC IN port on the COEX Pix:
|
Double check that you're using the RC IN port on the COEX Pix:
|
||||||
<img src="../assets/coexpix-bottom.jpg" width=300 class="zoom border center" alt="coex pix pinout">
|
|
||||||
|
<img src="../assets/coex_pix/coexpix-bottom.jpg" width=300 class="zoom border center" alt="coex pix pinout">
|
||||||
|
|
||||||
## Binding your transmitter {#rc_bind}
|
## Binding your transmitter {#rc_bind}
|
||||||
|
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ Each topic has the a of messages it passes. ROS include a lot of standard messag
|
|||||||
|Message type|Description|
|
|Message type|Description|
|
||||||
|-|-|
|
|-|-|
|
||||||
|[`std_msgs/Int64`](https://docs.ros.org/api/std_msgs/html/msg/Int64.html)|Integer number.|
|
|[`std_msgs/Int64`](https://docs.ros.org/api/std_msgs/html/msg/Int64.html)|Integer number.|
|
||||||
|[`std_msgs/Float64`](https://docs.ros.org/api/std_msgs/html/msg/Float64.html)|Double-precision floating-point number|
|
|[`std_msgs/Float64`](https://docs.ros.org/api/std_msgs/html/msg/Float64.html)|Double-precision floating-point number.|
|
||||||
|[`std_msgs/String`](https://docs.ros.org/api/std_msgs/html/msg/String.html)|String.|
|
|[`std_msgs/String`](https://docs.ros.org/api/std_msgs/html/msg/String.html)|String.|
|
||||||
|[`geometry_msgs/PoseStamped`](https://docs.ros.org/api/geometry_msgs/html/msg/PoseStamped.html)|Position and orientation of an object in a given [coordinate system](frames.md) and a time stamp (widely used for passing the robot pose or some robot's part pose).|
|
|[`geometry_msgs/PoseStamped`](https://docs.ros.org/api/geometry_msgs/html/msg/PoseStamped.html)|Position and orientation of an object in a given [coordinate system](frames.md) and a time stamp (widely used for passing the robot pose or some robot's part pose).|
|
||||||
|[`geometry_msgs/TwistStamped`](https://docs.ros.org/api/geometry_msgs/html/msg/TwistStamped.html)|Linear and angular velocity of an object in a given coordinate system and a time stamp.|
|
|[`geometry_msgs/TwistStamped`](https://docs.ros.org/api/geometry_msgs/html/msg/TwistStamped.html)|Linear and angular velocity of an object in a given coordinate system and a time stamp.|
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ Axis or Grid configured to frame `aruco_map` will visualize the location [on the
|
|||||||
|
|
||||||
### jsk_rviz_plugins
|
### jsk_rviz_plugins
|
||||||
|
|
||||||
It is also recommended to install additional useful plugins for rviz [jsk_rviz_plugins](https://jsk-docs.readthedocs.io/en/latest/jsk_visualization/doc/jsk_rviz_plugins/index.html). This kit allows visualizing topics like `TwistStamped` (velocity) `CameraInfo`, `PolygonArray`, and many more. To install, use command:
|
It is also recommended to install additional useful plugins for rviz [jsk_rviz_plugins](https://jsk-visualization.readthedocs.io/en/latest/jsk_rviz_plugins/index.html). This kit allows visualizing topics like `TwistStamped` (velocity) `CameraInfo`, `PolygonArray`, and many more. To install, use command:
|
||||||
|
|
||||||
```(bash)
|
```(bash)
|
||||||
sudo apt-get install ros-melodic-jsk-visualization
|
sudo apt-get install ros-melodic-jsk-visualization
|
||||||
|
|||||||
@@ -115,23 +115,23 @@ After finishing step 4, at section Installing guard of Clover 4.2 assembly.
|
|||||||
|
|
||||||
1. Install the Lower Tank Holders to top Deck mount and fix with the M3x8 screws.
|
1. Install the Lower Tank Holders to top Deck mount and fix with the M3x8 screws.
|
||||||
|
|
||||||
<img src="../assets/seeding_drone/mechanismpictures/1.PNG" width="400px" class="center"/>
|
<img src="../assets/seeding_drone/mechanismpictures/1.png" width="400px" class="center"/>
|
||||||
|
|
||||||
2. Install Nylon rack(40 mm) to 4 sides of the Deck mount.
|
2. Install Nylon rack(40 mm) to 4 sides of the Deck mount.
|
||||||
|
|
||||||
<img src="../assets/seeding_drone/mechanismpictures/2.PNG" width="400px" class="center"/>
|
<img src="../assets/seeding_drone/mechanismpictures/2.png" width="400px" class="center"/>
|
||||||
|
|
||||||
3. Install the Grab deck and fix with the M3x8 screws.
|
3. Install the Grab deck and fix with the M3x8 screws.
|
||||||
|
|
||||||
<img src="../assets/seeding_drone/mechanismpictures/3.PNG" width="400px" class="center"/>
|
<img src="../assets/seeding_drone/mechanismpictures/3.png" width="400px" class="center"/>
|
||||||
|
|
||||||
4. Install the Upper Tank Holders to top Grab mount and fix with the M3x8 screws.
|
4. Install the Upper Tank Holders to top Grab mount and fix with the M3x8 screws.
|
||||||
|
|
||||||
<img src="../assets/seeding_drone/mechanismpictures/4.PNG" width="400px" class="center"/>
|
<img src="../assets/seeding_drone/mechanismpictures/4.png" width="400px" class="center"/>
|
||||||
|
|
||||||
5. Connect the Tanks carefully to Tank Holders.
|
5. Connect the Tanks carefully to Tank Holders.
|
||||||
|
|
||||||
<img src="../assets/seeding_drone/mechanismpictures/5.PNG" width="400px" class="center"/>
|
<img src="../assets/seeding_drone/mechanismpictures/5.png" width="400px" class="center"/>
|
||||||
|
|
||||||
6. Connect SG90 servo motors to Tank using zip tie.
|
6. Connect SG90 servo motors to Tank using zip tie.
|
||||||
|
|
||||||
|
|||||||
@@ -27,28 +27,29 @@ Main article: https://docs.qgroundcontrol.com/en/SetupView/Firmware.html
|
|||||||
|
|
||||||
> **Note** Do not connect your flight controller prior to flashing.
|
> **Note** Do not connect your flight controller prior to flashing.
|
||||||
|
|
||||||
We recommend using the modified version of PX4 by CopterExpress for the Clover drone, especially for autonomous flights. Download the latest stable version **<a class="latest-firmware v4" href="https://github.com/CopterExpress/Firmware/releases">from our GitHub</a>**.
|
We recommend using the modified version of [PX4 with COEX patches](firmware.md) for the Clover drone, especially for autonomous flights. Download the latest stable version **<a class="latest-firmware v4" href="https://github.com/CopterExpress/Firmware/releases">from our GitHub</a>**.
|
||||||
|
|
||||||
> **Info** For Pixhawk-based quadcopters there is a separate firmware version. See details in "[Pixhawk / Pixracer firmware flashing](firmware.md)" article.
|
To use all the most recent PX4 functions you also can use the latest official firmware version (experimentally).
|
||||||
|
|
||||||
Flash the flight controller with this firmware:
|
Flash the flight controller with this firmware:
|
||||||
|
|
||||||
<img src="../assets/qgc-firmware.png" alt="QGroundControl firmware upload" class="zoom">
|
<img src="../assets/qgc-firmware.png" alt="QGroundControl firmware upload" class="zoom">
|
||||||
|
|
||||||
1. Launch QGroundControl software.
|
1. Disconnect the flight controller from computer (if connected).
|
||||||
2. Open the *Vehicle Setup* tab.
|
2. Launch QGroundControl software.
|
||||||
3. Select the *Firmware* menu.
|
3. Go to *Vehicle Setup* panel (click on the QGroundControl logo in the top-left corner) and select *Firmware* menu.
|
||||||
4. Connect your flight controller to your PC over USB.
|
4. Connect your flight controller to your PC over USB.
|
||||||
5. Wait for the flight controller to connect to QGroundControl.
|
5. Select *PX4 Flight Stack* in the right bar appeared.
|
||||||
6. Select *PX4 Flight Stack* in the right bar.
|
|
||||||
|
|
||||||
To use the recommended Copter Express firmware:
|
<img src="../assets/qgc-firmware.png" alt="QGroundControl firmware upload" class="zoom">
|
||||||
|
|
||||||
* Check *Advanced Settings* checkbox.
|
6. To use **COEX patched firmware**:
|
||||||
* Select *Custom firmware file...* from the dropdown list.
|
|
||||||
* Press *OK* and select the file that you've downloaded.
|
|
||||||
|
|
||||||
To use the latest official stable firmware just press *OK*.
|
* Check *Advanced Settings* checkbox.
|
||||||
|
* Select *Custom firmware file...* from the dropdown list.
|
||||||
|
* Press *OK* and select the file that you've downloaded.
|
||||||
|
|
||||||
|
To use the latest **official stable firmware** just press *OK*.
|
||||||
|
|
||||||
Wait for QGroundControl to finish flashing the flight controller.
|
Wait for QGroundControl to finish flashing the flight controller.
|
||||||
|
|
||||||
@@ -82,7 +83,7 @@ This is how the main QGroundControl settings window will look like:
|
|||||||
|
|
||||||
### Setting parameters
|
### Setting parameters
|
||||||
|
|
||||||
Open the *Vehicle Setup* tab and select the *Parameters* menu. You can use the *Search* field to find parameters by name.
|
Open the *Vehicle Setup* tab and select the *Parameters* menu. You can use the *Search* field to find parameters by name. Recommended parameters values are given in the further documentation and also in the [parameters summary article](parameters.md).
|
||||||
|
|
||||||
<img src="../assets/qgc-parameters.png" alt="QGroundControl parameters" class="zoom">
|
<img src="../assets/qgc-parameters.png" alt="QGroundControl parameters" class="zoom">
|
||||||
|
|
||||||
@@ -123,5 +124,3 @@ Press the *Save* button to save the changed value to the flight controller. Chan
|
|||||||
|
|
||||||
1. Set `CBRK_USB_CHK` to 197848 to allow flights with the USB cable connected.
|
1. Set `CBRK_USB_CHK` to 197848 to allow flights with the USB cable connected.
|
||||||
2. Disable safety switch check: `CBRK_IO_SAFETY` = 22027.
|
2. Disable safety switch check: `CBRK_IO_SAFETY` = 22027.
|
||||||
|
|
||||||
**Next**: [Sensor calibration](calibration.md).
|
|
||||||
|
|||||||
@@ -1,11 +1,4 @@
|
|||||||
Autonomous flight (OFFBOARD)
|
# Autonomous flight
|
||||||
===
|
|
||||||
|
|
||||||
> **Note** In the image version **0.20** `clever` package was renamed to `clover`. See [previous version of the article](https://github.com/CopterExpress/clover/blob/v0.19/docs/en/simple_offboard.md) for older images.
|
|
||||||
|
|
||||||
<!-- -->
|
|
||||||
|
|
||||||
> **Hint** We recommend using our [custom PX4 firmware for Clover](firmware.md#modified-firmware-for-clover) for autonomous flights.
|
|
||||||
|
|
||||||
The `simple_offboard` module of the `clover` package is intended for simplified programming of the autonomous drone flight (`OFFBOARD` [flight mode](modes.md)). It allows setting the desired flight tasks, and automatically transforms [coordinates between frames](frames.md).
|
The `simple_offboard` module of the `clover` package is intended for simplified programming of the autonomous drone flight (`OFFBOARD` [flight mode](modes.md)). It allows setting the desired flight tasks, and automatically transforms [coordinates between frames](frames.md).
|
||||||
|
|
||||||
@@ -13,8 +6,7 @@ The `simple_offboard` module of the `clover` package is intended for simplified
|
|||||||
|
|
||||||
Main services are [`get_telemetry`](#gettelemetry) (receive telemetry data), [`navigate`](#navigate) (fly to a given point along a straight line), [`navigate_global`](#navigateglobal) (fly to a point specified as latitude and longitude along a straight line), [`land`](#land) (switch to landing mode).
|
Main services are [`get_telemetry`](#gettelemetry) (receive telemetry data), [`navigate`](#navigate) (fly to a given point along a straight line), [`navigate_global`](#navigateglobal) (fly to a point specified as latitude and longitude along a straight line), [`land`](#land) (switch to landing mode).
|
||||||
|
|
||||||
Python examples
|
## Python usage
|
||||||
---
|
|
||||||
|
|
||||||
You need to create proxies for services before calling them. Use the following template for your programs:
|
You need to create proxies for services before calling them. Use the following template for your programs:
|
||||||
|
|
||||||
@@ -37,8 +29,7 @@ land = rospy.ServiceProxy('land', Trigger)
|
|||||||
|
|
||||||
Unused proxy functions may be removed from the code.
|
Unused proxy functions may be removed from the code.
|
||||||
|
|
||||||
API description
|
## API description
|
||||||
---
|
|
||||||
|
|
||||||
> **Note** Omitted numeric parameters are set to 0.
|
> **Note** Omitted numeric parameters are set to 0.
|
||||||
|
|
||||||
@@ -295,7 +286,7 @@ The positive direction of `yaw_rate` rotation (when viewed from the top) is coun
|
|||||||
|
|
||||||
Switch the drone to landing [mode](modes.md) (`AUTO.LAND` or similar).
|
Switch the drone to landing [mode](modes.md) (`AUTO.LAND` or similar).
|
||||||
|
|
||||||
> **Note** Set the `COM_DISARM_LAND` [PX4 parameter](px4_parameters.md) to a value greater than 0 to enable automatic disarm after landing.
|
> **Note** Set the `COM_DISARM_LAND` [PX4 parameter](parameters.md) to a value greater than 0 to enable automatic disarm after landing.
|
||||||
|
|
||||||
Landing the drone:
|
Landing the drone:
|
||||||
|
|
||||||
@@ -312,14 +303,9 @@ Landing the drone (command line):
|
|||||||
rosservice call /land "{}"
|
rosservice call /land "{}"
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--
|
> **Caution** In recent PX4 versions, the vehicle will be switched out of LAND mode to manual mode, if the remote control sticks are moved significantly.
|
||||||
### release
|
|
||||||
|
|
||||||
Stop publishing setpoints to the drone (release control). Required to continue monitoring by means of [MAVROS](mavros.md).
|
## Additional materials
|
||||||
-->
|
|
||||||
|
|
||||||
Additional materials
|
|
||||||
------------------------
|
|
||||||
|
|
||||||
* [ArUco-based position estimation and navigation](aruco.md).
|
* [ArUco-based position estimation and navigation](aruco.md).
|
||||||
* [Program samples and snippets](snippets.md).
|
* [Program samples and snippets](snippets.md).
|
||||||
|
|||||||
56
docs/en/simulation_m1.md
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
# Running simulator on M1 powered computer
|
||||||
|
|
||||||
|
There is no preconfigured VM image for ARM64 architecture of M1 chip (Apple Silicon), so the only possibility is to install the simulation software manually.
|
||||||
|
|
||||||
|
The recommended virtual machine hypervisor is [UTM app](https://mac.getutm.app/). Also it's possible to use **VMware Fusion Public Tech Preview** with M1 support.
|
||||||
|
|
||||||
|
## Simulation installation with UTM
|
||||||
|
|
||||||
|
<img src="../assets/simulation_utm.png" width=500 class="center zoom">
|
||||||
|
|
||||||
|
1. Download UTM App from the official site [mac.getutm.app](https://mac.getutm.app/) and install it.
|
||||||
|
2. Download Ubuntu Linux 20.04 installation iso-file for ARM64 architecture using the link: https://cdimage.ubuntu.com/focal/daily-live/current/focal-desktop-arm64.iso.
|
||||||
|
3. Create a new virtual machine in UTM, using the following settings:
|
||||||
|
|
||||||
|
* **Type**: Virtualize.
|
||||||
|
* **Operating System**: Linux.
|
||||||
|
* **Boot ISO Image**: choose downloaded file `focal-desktop-arm64.iso`.
|
||||||
|
* **Memory**: 4096 MB or more.
|
||||||
|
* **CPU Cores**: 4 or more.
|
||||||
|
* Turn on *Enable hardware OpenGL acceleration* option.
|
||||||
|
* **Storage**: 20 GB or more.
|
||||||
|
|
||||||
|
4. Run the created virtual machine.
|
||||||
|
5. Choose *Install Ubuntu* in the menu and install it using the installation master.
|
||||||
|
|
||||||
|
* Recommended apps: *Minimal installation*.
|
||||||
|
* Installation type: *Erase disk and install Ubuntu*.
|
||||||
|
* Input your account parameters, for example:
|
||||||
|
|
||||||
|
<img src="../assets/simulation_ubuntu_account.png" width=400 class="center zoom">
|
||||||
|
|
||||||
|
6. Finish the installation and run the system.
|
||||||
|
7. Install the simulation using the [native setup manual](simulation_native.md).
|
||||||
|
|
||||||
|
### Troubleshooting
|
||||||
|
|
||||||
|
#### Black screen
|
||||||
|
|
||||||
|
If you see a black screen on your virtual machine, try to run the machine without the GPU support.
|
||||||
|
|
||||||
|
In virtual machine settings, choose *Display*, and set *Emulated Display Card* menu to *virtio-ramfb*. Run you machine. If it runs successfully, change the setting back to *virtio-ramfb-gl (GPU Supported)* and run it again.
|
||||||
|
|
||||||
|
#### Problem with `git clone`
|
||||||
|
|
||||||
|
The following error can occur while performing `git clone`:
|
||||||
|
|
||||||
|
```txt
|
||||||
|
on git clone if error: RPC failed; curl 56 GnuTLS recv error (-54): Error in the pull function.
|
||||||
|
fatal: the remote end hung up unexpectedly
|
||||||
|
fatal: early EOF
|
||||||
|
fatal: index-pack failed
|
||||||
|
```
|
||||||
|
|
||||||
|
In this case, change the type of the network card to bridged. In the virtual machine settings, choose *Network*, and set *Network Mode* menu to *Bridged (Advanced)*.
|
||||||
|
|
||||||
|
Later, if some network issues occur, change the network mode back to *Shared Network*.
|
||||||
@@ -4,31 +4,56 @@ Setting up the simulation environment from scratch requires some effort, but res
|
|||||||
|
|
||||||
> **Hint** See up-to-date commands set for installation Clover simulation software in the script, that builds the virtual machine image with the simulator: [`install_software.sh`](https://github.com/CopterExpress/clover_vm/blob/master/scripts/install_software.sh).
|
> **Hint** See up-to-date commands set for installation Clover simulation software in the script, that builds the virtual machine image with the simulator: [`install_software.sh`](https://github.com/CopterExpress/clover_vm/blob/master/scripts/install_software.sh).
|
||||||
|
|
||||||
Prerequisites: Ubuntu 20.04 and [ROS Noetic](http://wiki.ros.org/noetic/Installation/Ubuntu).
|
Prerequisites: **Ubuntu 20.04**.
|
||||||
|
|
||||||
|
## Install ROS
|
||||||
|
|
||||||
|
Install ROS Noetic using the [official installation manual](http://wiki.ros.org/noetic/Installation/Ubuntu) (Desktop or Full install).
|
||||||
|
|
||||||
|
Add sourcing ROS' `setup.bash` initialization script to your `.bashrc`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
echo "source /opt/ros/noetic/setup.bash" >> ~/.bashrc
|
||||||
|
source ~/.bashrc
|
||||||
|
```
|
||||||
|
|
||||||
|
Install required tools:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo apt install build-essential git python3-pip python3-rosdep
|
||||||
|
```
|
||||||
|
|
||||||
## Create a workspace for the simulation
|
## Create a workspace for the simulation
|
||||||
|
|
||||||
Throughout this guide we will be using the `catkin_ws` as the workspace name. Feel free to change it in your setup. We will be creating it in the home directory of the current user (`~`).
|
Create a workspace for the simulation:
|
||||||
|
|
||||||
Create the workspace and clone Clover sources:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
mkdir -p ~/catkin_ws/src
|
mkdir -p ~/catkin_ws/src
|
||||||
|
cd ~/catkin_ws
|
||||||
|
catkin_make
|
||||||
|
echo "source ~/catkin_ws/devel/setup.bash" >> ~/.bashrc
|
||||||
|
source ~/.bashrc
|
||||||
|
```
|
||||||
|
|
||||||
|
Clone Clover sources:
|
||||||
|
|
||||||
|
```bash
|
||||||
cd ~/catkin_ws/src
|
cd ~/catkin_ws/src
|
||||||
git clone --depth 1 https://github.com/CopterExpress/clover
|
git clone --depth 1 https://github.com/CopterExpress/clover
|
||||||
git clone --depth 1 https://github.com/CopterExpress/ros_led
|
git clone --depth 1 https://github.com/CopterExpress/ros_led
|
||||||
git clone --depth 1 https://github.com/ethz-asl/mav_comm
|
git clone --depth 1 https://github.com/ethz-asl/mav_comm
|
||||||
```
|
```
|
||||||
|
|
||||||
Install all prerequisites using `rosdep`:
|
Install all dependencies using `rosdep`:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd ~/catkin_ws
|
cd ~/catkin_ws
|
||||||
|
sudo rosdep init
|
||||||
rosdep update
|
rosdep update
|
||||||
rosdep install --from-paths src --ignore-src -y
|
rosdep install --from-paths src --ignore-src -y
|
||||||
```
|
```
|
||||||
|
|
||||||
Install Python-dependencies:
|
Install Python dependencies:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo /usr/bin/python3 -m pip install -r ~/catkin_ws/src/clover/clover/requirements.txt
|
sudo /usr/bin/python3 -m pip install -r ~/catkin_ws/src/clover/clover/requirements.txt
|
||||||
@@ -36,15 +61,19 @@ sudo /usr/bin/python3 -m pip install -r ~/catkin_ws/src/clover/clover/requiremen
|
|||||||
|
|
||||||
## Get PX4 sources
|
## Get PX4 sources
|
||||||
|
|
||||||
PX4 will be built along with the other packages in our workspace. You may clone it directly into the workspace or put it somewhere and symlink to `~/catkin_ws/src`. We will need to put its `sitl_gazebo` submodule in `~/catkin_ws/src` as well. For simplicity's sake we will clone the firmware directly to the workspace:
|
PX4 will be built along with the other packages in our workspace. You may clone it directly into the workspace or put it somewhere and symlink to `~/catkin_ws/src`. We will need to put its `sitl_gazebo` and `mavlink` submodules into `~/catkin_ws/src` as well.
|
||||||
|
|
||||||
|
Clone PX4 sources and make the required symlinks:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd ~/catkin_ws/src
|
|
||||||
git clone --recursive --depth 1 --branch v1.12.0 https://github.com/PX4/PX4-Autopilot.git ~/PX4-Autopilot
|
git clone --recursive --depth 1 --branch v1.12.0 https://github.com/PX4/PX4-Autopilot.git ~/PX4-Autopilot
|
||||||
ln -s ~/PX4-Autopilot ~/catkin_ws/src/PX4-Autopilot
|
ln -s ~/PX4-Autopilot ~/catkin_ws/src/
|
||||||
ln -s ~/PX4-Autopilot/Tools/sitl_gazebo ~/catkin_ws/src/sitl_gazebo
|
ln -s ~/PX4-Autopilot/Tools/sitl_gazebo ~/catkin_ws/src/
|
||||||
|
ln -s ~/PX4-Autopilot/mavlink ~/catkin_ws/src/
|
||||||
```
|
```
|
||||||
|
|
||||||
|
> **Hint** You may use more recent PX4 version, but there would be more risk of something would not be working.
|
||||||
|
|
||||||
## Install PX4 prerequisites
|
## Install PX4 prerequisites
|
||||||
|
|
||||||
PX4 comes with its own script for dependency installation. We may as well leverage it:
|
PX4 comes with its own script for dependency installation. We may as well leverage it:
|
||||||
@@ -56,10 +85,12 @@ sudo ./ubuntu.sh
|
|||||||
|
|
||||||
This will install everything required to build PX4 and its SITL environment.
|
This will install everything required to build PX4 and its SITL environment.
|
||||||
|
|
||||||
You may want to skip installing the ARM toolchain if you're not planning on compiling PX4 for your flight controller. To do this, use the `--no-nuttx` flag:
|
> **Hint** You may want to skip installing the ARM toolchain if you're not planning on compiling PX4 for your flight controller. To do this, use the `--no-nuttx` flag: `sudo ./ubuntu.sh --no-nuttx`.
|
||||||
|
|
||||||
```
|
Install more required Python packages:
|
||||||
sudo ./ubuntu.sh --no-nuttx
|
|
||||||
|
```bash
|
||||||
|
pip3 install --user toml
|
||||||
```
|
```
|
||||||
|
|
||||||
## Add the Clover airframe
|
## Add the Clover airframe
|
||||||
@@ -67,7 +98,7 @@ sudo ./ubuntu.sh --no-nuttx
|
|||||||
Add the Clover airframe to PX4 using the command:
|
Add the Clover airframe to PX4 using the command:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
ln -s "$(catkin_find clover_simulation airframes)"/* ~/PX4-Autopilot/ROMFS/px4fmu_common/init.d-posix/airframes/
|
ln -s ~/catkin_ws/src/clover/clover_simulation/airframes/* ~/PX4-Autopilot/ROMFS/px4fmu_common/init.d-posix/airframes/
|
||||||
```
|
```
|
||||||
|
|
||||||
## Install geographiclib datasets
|
## Install geographiclib datasets
|
||||||
@@ -80,20 +111,44 @@ sudo /opt/ros/noetic/lib/mavros/install_geographiclib_datasets.sh
|
|||||||
|
|
||||||
## Build the simulator
|
## Build the simulator
|
||||||
|
|
||||||
With all dependencies installed, you can build your workspace:
|
Build your workspace:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd ~/catkin_ws
|
cd ~/catkin_ws
|
||||||
catkin_make
|
catkin_make
|
||||||
```
|
```
|
||||||
|
|
||||||
> **Note** Some of the files - particularly Gazebo plugins - require large amounts of RAM to be built. You may wish to reduce the number of parallel jobs; the number of parallel jobs should be equal to the amount of RAM in gigabytes divided by 2 - so a 16GB machine should use no more than 8 jobs. You can specify the number of jobs using the `-j` flag: `catkin_make -j8`
|
> **Note** If building fails with RAM issues (`c++: fatal error: Killed signal terminated program cc1plus`), reduce the number of parallel jobs using `-j` key. For example, to use only two parallel jobs use `catkin_make -j2` command.
|
||||||
|
|
||||||
## Run the simulator
|
## Run the simulator
|
||||||
|
|
||||||
In order to be sure that everything was built correctly, try running the simulator for the first time:
|
In order to be sure that everything was built correctly, try running the simulator for the first time:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
source ~/catkin_ws/devel/setup.bash
|
|
||||||
roslaunch clover_simulation simulator.launch
|
roslaunch clover_simulation simulator.launch
|
||||||
```
|
```
|
||||||
|
|
||||||
|
You can test autonomous flight using example scripts in `~/catkin_ws/src/clover/clover/examples` directory.
|
||||||
|
|
||||||
|
## Additional steps
|
||||||
|
|
||||||
|
Optionally, install roscore systemd service to have roscore running in background:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sed -i "s/pi/$USER/g" ~/catkin_ws/src/clover/builder/assets/roscore.service
|
||||||
|
sudo cp ~/catkin_ws/src/clover/builder/assets/roscore.service /etc/systemd/system
|
||||||
|
sudo systemctl enable roscore
|
||||||
|
sudo systemctl start roscore
|
||||||
|
```
|
||||||
|
|
||||||
|
Install any web server to serve Clover's web tools (`~/.ros/www` directory), e. g. Monkey:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
wget https://github.com/CopterExpress/clover_vm/raw/master/assets/packages/monkey_1.6.9-1_$(dpkg --print-architecture).deb -P /tmp
|
||||||
|
sudo dpkg -i /tmp/monkey_*.deb
|
||||||
|
sed "s/pi/$USER/g" ~/catkin_ws/src/clover/builder/assets/monkey | sudo tee /etc/monkey/sites/default
|
||||||
|
sudo sed -i 's/SymLink Off/SymLink On/' /etc/monkey/monkey.conf
|
||||||
|
sudo cp ~/catkin_ws/src/clover/builder/assets/monkey.service /etc/systemd/system/monkey.service
|
||||||
|
sudo systemctl enable monkey
|
||||||
|
sudo systemctl start monkey
|
||||||
|
```
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ The plugin will collect publishing rate statistics and slow the simulation down
|
|||||||
|
|
||||||
### Set simulation speed
|
### Set simulation speed
|
||||||
|
|
||||||
Since v1.9 the PX4 SITL setup supports [setting the simulation speed](https://dev.px4.io/v1.9.0/en/simulation/#simulation_speed) by setting the `PX4_SIM_SPEED_FACTOR` environment variable. Its value is picked up by PX4 startup scripts, which in turn reconfigure it to expect a certain speedup/slowdown.
|
Since v1.9 the PX4 SITL setup supports [setting the simulation speed](https://docs.px4.io/master/en/simulation/#run-simulation-faster-than-realtime) by setting the `PX4_SIM_SPEED_FACTOR` environment variable. Its value is picked up by PX4 startup scripts, which in turn reconfigure it to expect a certain speedup/slowdown.
|
||||||
|
|
||||||
You should set its value to the actual real time factor that you get with `throttling_camera`. The real time factor may be found in the Gazebo GUI window at the bottom:
|
You should set its value to the actual real time factor that you get with `throttling_camera`. The real time factor may be found in the Gazebo GUI window at the bottom:
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ PX4 Simulation
|
|||||||
|
|
||||||
Main article: https://dev.px4.io/en/simulation/
|
Main article: https://dev.px4.io/en/simulation/
|
||||||
|
|
||||||
PX4 simulation is possible in Linux and macOS with the use of physical environment simulation systems [jMAVSim](https://pixhawk.org/dev/hil/jmavsim) and [the Gazebo](http://gazebosim.org).
|
PX4 simulation is possible in Linux and macOS with the use of physical environment simulation systems [jMAVSim](https://docs.px4.io/master/en/simulation/jmavsim.html) and [the Gazebo](http://gazebosim.org).
|
||||||
|
|
||||||
jMAVSim is a lightweight environment intended only for testing multi-rotor aircraft systems; Gazebo is a versatile environment for all types of robots.
|
jMAVSim is a lightweight environment intended only for testing multi-rotor aircraft systems; Gazebo is a versatile environment for all types of robots.
|
||||||
|
|
||||||
|
|||||||
@@ -8,13 +8,11 @@
|
|||||||
|
|
||||||
<a name="block-takeoff"></a><!-- old name of anchor -->
|
<a name="block-takeoff"></a><!-- old name of anchor -->
|
||||||
|
|
||||||
Fly towards a point and wait for copter's arrival:
|
Function to fly to a point and wait for copter's arrival:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
import math
|
import math
|
||||||
|
|
||||||
#...
|
|
||||||
|
|
||||||
def navigate_wait(x=0, y=0, z=0, yaw=float('nan'), speed=0.5, frame_id='', auto_arm=False, tolerance=0.2):
|
def navigate_wait(x=0, y=0, z=0, yaw=float('nan'), speed=0.5, frame_id='', auto_arm=False, tolerance=0.2):
|
||||||
navigate(x=x, y=y, z=z, yaw=yaw, speed=speed, frame_id=frame_id, auto_arm=auto_arm)
|
navigate(x=x, y=y, z=z, yaw=yaw, speed=speed, frame_id=frame_id, auto_arm=auto_arm)
|
||||||
|
|
||||||
@@ -64,8 +62,6 @@ Wait for copter's arrival to the [navigate](simple_offboard.md#navigate) target:
|
|||||||
```python
|
```python
|
||||||
import math
|
import math
|
||||||
|
|
||||||
# ...
|
|
||||||
|
|
||||||
def wait_arrival(tolerance=0.2):
|
def wait_arrival(tolerance=0.2):
|
||||||
while not rospy.is_shutdown():
|
while not rospy.is_shutdown():
|
||||||
telem = get_telemetry(frame_id='navigate_target')
|
telem = get_telemetry(frame_id='navigate_target')
|
||||||
@@ -79,6 +75,8 @@ def wait_arrival(tolerance=0.2):
|
|||||||
Calculate the distance between two points (**important**: the points are to be in the same [coordinate system](frames.md)):
|
Calculate the distance between two points (**important**: the points are to be in the same [coordinate system](frames.md)):
|
||||||
|
|
||||||
```python
|
```python
|
||||||
|
import math
|
||||||
|
|
||||||
def get_distance(x1, y1, z1, x2, y2, z2):
|
def get_distance(x1, y1, z1, x2, y2, z2):
|
||||||
return math.sqrt((x1 - x2) ** 2 + (y1 - y2) ** 2 + (z1 - z2) ** 2)
|
return math.sqrt((x1 - x2) ** 2 + (y1 - y2) ** 2 + (z1 - z2) ** 2)
|
||||||
```
|
```
|
||||||
@@ -88,6 +86,8 @@ def get_distance(x1, y1, z1, x2, y2, z2):
|
|||||||
Approximation of distance (in meters) between two global coordinates (latitude/longitude):
|
Approximation of distance (in meters) between two global coordinates (latitude/longitude):
|
||||||
|
|
||||||
```python
|
```python
|
||||||
|
import math
|
||||||
|
|
||||||
def get_distance_global(lat1, lon1, lat2, lon2):
|
def get_distance_global(lat1, lon1, lat2, lon2):
|
||||||
return math.hypot(lat1 - lat2, lon1 - lon2) * 1.113195e5
|
return math.hypot(lat1 - lat2, lon1 - lon2) * 1.113195e5
|
||||||
```
|
```
|
||||||
@@ -203,19 +203,16 @@ from geometry_msgs.msg import PoseStamped, TwistStamped
|
|||||||
from sensor_msgs.msg import BatteryState
|
from sensor_msgs.msg import BatteryState
|
||||||
from mavros_msgs.msg import RCIn
|
from mavros_msgs.msg import RCIn
|
||||||
|
|
||||||
# ...
|
|
||||||
|
|
||||||
def pose_update(pose):
|
def pose_update(pose):
|
||||||
# Processing new data of copter's position
|
# Processing new data of copter's position
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# Other handler functions
|
|
||||||
# ...
|
|
||||||
|
|
||||||
rospy.Subscriber('/mavros/local_position/pose', PoseStamped, pose_update)
|
rospy.Subscriber('/mavros/local_position/pose', PoseStamped, pose_update)
|
||||||
rospy.Subscriber('/mavros/local_position/velocity', TwistStamped, velocity_update)
|
rospy.Subscriber('/mavros/local_position/velocity', TwistStamped, velocity_update)
|
||||||
rospy.Subscriber('/mavros/battery', BatteryState, battery_update)
|
rospy.Subscriber('/mavros/battery', BatteryState, battery_update)
|
||||||
rospy.Subscriber('mavros/rc/in', RCIn, rc_callback)
|
rospy.Subscriber('mavros/rc/in', RCIn, rc_callback)
|
||||||
|
|
||||||
|
rospy.spin()
|
||||||
```
|
```
|
||||||
|
|
||||||
Information about MAVROS topics is available at [the link](mavros.md).
|
Information about MAVROS topics is available at [the link](mavros.md).
|
||||||
@@ -229,18 +226,13 @@ Information about MAVROS topics is available at [the link](mavros.md).
|
|||||||
Send an arbitrary [MAVLink message](mavlink.md) to the copter:
|
Send an arbitrary [MAVLink message](mavlink.md) to the copter:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
# ...
|
|
||||||
|
|
||||||
from mavros_msgs.msg import Mavlink
|
from mavros_msgs.msg import Mavlink
|
||||||
from mavros import mavlink
|
from mavros import mavlink
|
||||||
from pymavlink import mavutil
|
from pymavlink import mavutil
|
||||||
|
|
||||||
# ...
|
|
||||||
|
|
||||||
mavlink_pub = rospy.Publisher('mavlink/to', Mavlink, queue_size=1)
|
mavlink_pub = rospy.Publisher('mavlink/to', Mavlink, queue_size=1)
|
||||||
|
|
||||||
# Sending a HEARTBEAT message:
|
# Sending a HEARTBEAT message:
|
||||||
|
|
||||||
msg = mavutil.mavlink.MAVLink_heartbeat_message(mavutil.mavlink.MAV_TYPE_GCS, 0, 0, 0, 0, 0)
|
msg = mavutil.mavlink.MAVLink_heartbeat_message(mavutil.mavlink.MAV_TYPE_GCS, 0, 0, 0, 0, 0)
|
||||||
msg.pack(mavutil.mavlink.MAVLink('', 2, 1))
|
msg.pack(mavutil.mavlink.MAVLink('', 2, 1))
|
||||||
ros_msg = mavlink.convert_to_rosmsg(msg)
|
ros_msg = mavlink.convert_to_rosmsg(msg)
|
||||||
@@ -281,8 +273,6 @@ Change the [flight mode](modes.md) to arbitrary one:
|
|||||||
```python
|
```python
|
||||||
from mavros_msgs.srv import SetMode
|
from mavros_msgs.srv import SetMode
|
||||||
|
|
||||||
# ...
|
|
||||||
|
|
||||||
set_mode = rospy.ServiceProxy('mavros/set_mode', SetMode)
|
set_mode = rospy.ServiceProxy('mavros/set_mode', SetMode)
|
||||||
|
|
||||||
# ...
|
# ...
|
||||||
@@ -297,8 +287,6 @@ Flip:
|
|||||||
```python
|
```python
|
||||||
import math
|
import math
|
||||||
|
|
||||||
# ...
|
|
||||||
|
|
||||||
PI_2 = math.pi / 2
|
PI_2 = math.pi / 2
|
||||||
|
|
||||||
def flip():
|
def flip():
|
||||||
@@ -337,8 +325,6 @@ from pymavlink import mavutil
|
|||||||
from mavros_msgs.srv import CommandLong
|
from mavros_msgs.srv import CommandLong
|
||||||
from mavros_msgs.msg import State
|
from mavros_msgs.msg import State
|
||||||
|
|
||||||
# ...
|
|
||||||
|
|
||||||
send_command = rospy.ServiceProxy('/mavros/cmd/command', CommandLong)
|
send_command = rospy.ServiceProxy('/mavros/cmd/command', CommandLong)
|
||||||
|
|
||||||
def calibrate_gyro():
|
def calibrate_gyro():
|
||||||
@@ -372,8 +358,6 @@ Enable and disable [ArUco markers recognition](aruco_marker.md) dynamically (for
|
|||||||
import rospy
|
import rospy
|
||||||
import dynamic_reconfigure.client
|
import dynamic_reconfigure.client
|
||||||
|
|
||||||
# ...
|
|
||||||
|
|
||||||
client = dynamic_reconfigure.client.Client('aruco_detect')
|
client = dynamic_reconfigure.client.Client('aruco_detect')
|
||||||
|
|
||||||
# Turn markers recognition off
|
# Turn markers recognition off
|
||||||
@@ -392,10 +376,42 @@ Wait for global position to appear (finishing [GPS receiver](gps.md) initializat
|
|||||||
```python
|
```python
|
||||||
import math
|
import math
|
||||||
|
|
||||||
# ...
|
|
||||||
|
|
||||||
while not rospy.is_shutdown():
|
while not rospy.is_shutdown():
|
||||||
if math.isfinite(get_telemetry().lat):
|
if math.isfinite(get_telemetry().lat):
|
||||||
break
|
break
|
||||||
rospy.sleep(0.2)
|
rospy.sleep(0.2)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### # {#get-param}
|
||||||
|
|
||||||
|
Read flight controller's parameter:
|
||||||
|
|
||||||
|
```python
|
||||||
|
from mavros_msgs.srv import ParamGet
|
||||||
|
from mavros_msgs.msg import ParamValue
|
||||||
|
|
||||||
|
param_get = rospy.ServiceProxy('mavros/param/get', ParamGet)
|
||||||
|
|
||||||
|
# Read parameter of type INT
|
||||||
|
value = param_get(param_id='COM_FLTMODE1').value.integer
|
||||||
|
|
||||||
|
# Read parameter of type FLOAT
|
||||||
|
value = param_get(param_id='MPC_Z_P').value.float
|
||||||
|
```
|
||||||
|
|
||||||
|
### # {#set-param}
|
||||||
|
|
||||||
|
Set flight controller's parameter:
|
||||||
|
|
||||||
|
```python
|
||||||
|
from mavros_msgs.srv import ParamSet
|
||||||
|
from mavros_msgs.msg import ParamValue
|
||||||
|
|
||||||
|
param_set = rospy.ServiceProxy('mavros/param/set', ParamSet)
|
||||||
|
|
||||||
|
# Set parameter of type INT:
|
||||||
|
param_set(param_id='COM_FLTMODE1', value=ParamValue(integer=8))
|
||||||
|
|
||||||
|
# Set parameter of type FLOAT:
|
||||||
|
param_set(param_id='MPC_Z_P', value=ParamValue(real=1.5))
|
||||||
|
```
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ while True:
|
|||||||
|
|
||||||
### Filtering the data
|
### Filtering the data
|
||||||
|
|
||||||
To filter (smooth out) the data and delete [outliers](https://en.wikipedia.org/wiki/Outlier), [Kalman filter](https://en.wikipedia.org/wiki/Kalman_filter) or a simple [median filter](https://ru.wikipedia.org/wiki/Median_filter) can be used. An example of median filtering implementation:
|
To filter (smooth out) the data and delete [outliers](https://en.wikipedia.org/wiki/Outlier), [Kalman filter](https://en.wikipedia.org/wiki/Kalman_filter) or a simple [median filter](https://en.wikipedia.org/wiki/Median_filter) can be used. An example of median filtering implementation:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
import collections
|
import collections
|
||||||
|
|||||||
@@ -25,5 +25,3 @@ Web access
|
|||||||
Starting with version 0.11.4 [of the image](image.md), access to the shell is also available via a web browser (using [Butterfly](https://github.com/paradoxxxzero/butterfly)). To gain access, open web page http://192.168.11.1, and select link *Open web terminal*:
|
Starting with version 0.11.4 [of the image](image.md), access to the shell is also available via a web browser (using [Butterfly](https://github.com/paradoxxxzero/butterfly)). To gain access, open web page http://192.168.11.1, and select link *Open web terminal*:
|
||||||
|
|
||||||
<img src="../assets/butterfly.png">
|
<img src="../assets/butterfly.png">
|
||||||
|
|
||||||
**Next**: [Command-line interface](cli.md)
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ Using a multimeter, check the absence of a short circuit (check the loop):
|
|||||||
* Test the multimeter by shorting the probes. A unit that operated properly makes a distinctive sound.
|
* Test the multimeter by shorting the probes. A unit that operated properly makes a distinctive sound.
|
||||||
* The red probe is connected to the “+ ”pin, the black probe — to the “-” / ”GND” pin. If the circuit is short, a sound is heard.
|
* The red probe is connected to the “+ ”pin, the black probe — to the “-” / ”GND” pin. If the circuit is short, a sound is heard.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
1\. Check OPEN CONDITION of the following circuits (absence of the multimeter sound signal):
|
1\. Check OPEN CONDITION of the following circuits (absence of the multimeter sound signal):
|
||||||
|
|
||||||
|
|||||||
25
docs/en/video_contest.md
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
# Contest for the best educational video on assembly and configuration
|
||||||
|
|
||||||
|
Requirements:
|
||||||
|
|
||||||
|
- the video contains the entire process of assembling and configuring the Clover 4.2 drone kit: from opening the box with components to flying the copter in Position mode using ArUco markers;
|
||||||
|
- the video is uploaded to YouTube and is public accessible;
|
||||||
|
- the video contains voice-over in English;
|
||||||
|
- the video lasts from 6 to 60 minutes.
|
||||||
|
|
||||||
|
Dates of the contest: February 12 – December 13, 2021.
|
||||||
|
|
||||||
|
## Prizes
|
||||||
|
|
||||||
|
- 🥇 1st place: $500 (USD).
|
||||||
|
- 🥈 2nd place: $300 (USD).
|
||||||
|
- 🥉 3rd place: $200 (USD).
|
||||||
|
|
||||||
|
## Results
|
||||||
|
|
||||||
|
|Place|Participant|Link to the video|
|
||||||
|
|:-:|-|-|
|
||||||
|
|1|🇷🇺 Philipp Batalin|https://www.youtube.com/watch?v=f0rpdulOSEk|
|
||||||
|
|2|🇮🇹 Sara Pettinari|https://www.youtube.com/watch?v=PxxfyVH6RRA|
|
||||||
|
|3|🇲🇾 Kai Feng Chew|https://www.youtube.com/watch?v=skgSwFle6Ms|
|
||||||
|
|3|🇰🇿 Nikita Lobanov|https://www.youtube.com/watch?v=93b1epEM3SQ|
|
||||||
@@ -19,5 +19,3 @@ To edit Wi-Fi settings, or to obtain more detailed information about the network
|
|||||||
After connecting to Clover Wi-Fi, open http://192.168.11.1 in you web browser. It contains all the basic web tools of Clover: viewing image topics, web terminal (Butterfly), and the full copy of this documentation.
|
After connecting to Clover Wi-Fi, open http://192.168.11.1 in you web browser. It contains all the basic web tools of Clover: viewing image topics, web terminal (Butterfly), and the full copy of this documentation.
|
||||||
|
|
||||||
<img src="../assets/web.png" alt="Clover Web Interface" class="zoom">
|
<img src="../assets/web.png" alt="Clover Web Interface" class="zoom">
|
||||||
|
|
||||||
**Next**: [Connecting Raspberry Pi to the flight controller](connection.md).
|
|
||||||
|
|||||||