mirror of
https://github.com/CopterExpress/clover.git
synced 2026-06-01 15:39:32 +00:00
Compare commits
171 Commits
0.21.3
...
web-params
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2930eb9c22 | ||
|
|
64b083b242 | ||
|
|
b2ec48f0f3 | ||
|
|
b249524828 | ||
|
|
d0dcc0e72a | ||
|
|
4c6e7029e8 | ||
|
|
613f70fd25 | ||
|
|
77832e65fa | ||
|
|
01edd129ab | ||
|
|
d03acff31b | ||
|
|
22e74febd6 | ||
|
|
989d9b66f1 | ||
|
|
f8eb8e1e67 | ||
|
|
b92ebe7d60 | ||
|
|
af51e88179 | ||
|
|
59c8debcab | ||
|
|
ec6f3089e3 | ||
|
|
2b88d21792 | ||
|
|
274b81c50f | ||
|
|
33a6dffb1f | ||
|
|
5f9b3e82db | ||
|
|
5f43367d82 | ||
|
|
7809e7ed2d | ||
|
|
1688b97091 | ||
|
|
1c6129fde8 | ||
|
|
dae9599d64 | ||
|
|
c0677f6aa3 | ||
|
|
e7bbf21700 | ||
|
|
58c10d7cb8 | ||
|
|
b6bd6bdde8 | ||
|
|
3374c7756c | ||
|
|
0dffeca55f | ||
|
|
8cb911854d | ||
|
|
a1b3efe67d | ||
|
|
6700d8728f | ||
|
|
be2e6ae198 | ||
|
|
b9eed0f3ad | ||
|
|
853a7fcf67 | ||
|
|
e342796f07 | ||
|
|
4fa70aa73a | ||
|
|
226c91c3d8 | ||
|
|
e33c9e78ea | ||
|
|
18c927469e | ||
|
|
a465afd65c | ||
|
|
a2c65d2466 | ||
|
|
ef7faa126a | ||
|
|
d0666ca9d7 | ||
|
|
b48f22cd35 | ||
|
|
731f908053 | ||
|
|
505a1efebd | ||
|
|
f1b5484e65 | ||
|
|
3343477a02 | ||
|
|
60da608191 | ||
|
|
7e383d713d | ||
|
|
c2a60380b7 | ||
|
|
7f82f8683f | ||
|
|
e28cbea8d9 | ||
|
|
27528c20dc | ||
|
|
e3503fca35 | ||
|
|
40b8941cab | ||
|
|
0a9b6fff95 | ||
|
|
4a4e539edd | ||
|
|
8d24a737ac | ||
|
|
20af13947d | ||
|
|
e91609ff61 | ||
|
|
0024372c45 | ||
|
|
b62d202a29 | ||
|
|
84d685469a | ||
|
|
3f6bb0cd68 | ||
|
|
c01a145a16 | ||
|
|
6d28bf4ef9 | ||
|
|
f6ea7209db | ||
|
|
21727ef76d | ||
|
|
9847b7a71c | ||
|
|
a5d44ff63a | ||
|
|
391a2f9af9 | ||
|
|
5918702fbd | ||
|
|
69fe288a41 | ||
|
|
5154720348 | ||
|
|
cc1694661d | ||
|
|
d5efa962d8 | ||
|
|
1195336cbc | ||
|
|
5fc67b8e65 | ||
|
|
c1409d4467 | ||
|
|
7a1f09da98 | ||
|
|
6934cc7a1a | ||
|
|
2550ffe532 | ||
|
|
9def866a30 | ||
|
|
802f8eeaa4 | ||
|
|
504aa53b1a | ||
|
|
015cf730c2 | ||
|
|
8e6ef727ce | ||
|
|
973e1f1181 | ||
|
|
275faa78a4 | ||
|
|
fc7d010881 | ||
|
|
242e35f84a | ||
|
|
3f07d28e0f | ||
|
|
613d378e66 | ||
|
|
769c421898 | ||
|
|
3830ceea04 | ||
|
|
df3a11b035 | ||
|
|
ef5700845f | ||
|
|
921084504e | ||
|
|
6550780afb | ||
|
|
2448915300 | ||
|
|
247a7917d9 | ||
|
|
37f2c78b36 | ||
|
|
e76618bd3b | ||
|
|
9fbfcfbd2e | ||
|
|
2003b4516a | ||
|
|
03985ae1b8 | ||
|
|
a47d5d1bfe | ||
|
|
20075dd40f | ||
|
|
c247c75d17 | ||
|
|
c36279e536 | ||
|
|
1471a53b3a | ||
|
|
931f50a458 | ||
|
|
118b4573fe | ||
|
|
f77843f4a5 | ||
|
|
5f62a8639a | ||
|
|
fa1db1d90b | ||
|
|
1a2e87bb6a | ||
|
|
7dbd983ec5 | ||
|
|
d2d395f1fc | ||
|
|
ff93f79c0a | ||
|
|
5deb09eb45 | ||
|
|
70b8be5c5d | ||
|
|
2a08e20b47 | ||
|
|
3328d8f4ac | ||
|
|
f7fb814894 | ||
|
|
3a3b0bbd80 | ||
|
|
ca095f3f16 | ||
|
|
baf2467939 | ||
|
|
abba3bf876 | ||
|
|
346373ed23 | ||
|
|
bb996056c9 | ||
|
|
0e0b1cdc31 | ||
|
|
eceaa0ec91 | ||
|
|
f29686b9f4 | ||
|
|
b7f1f2b391 | ||
|
|
6b0bb41564 | ||
|
|
563e5acad6 | ||
|
|
5932faa29c | ||
|
|
bcc2e86e6f | ||
|
|
e80a1cc7d6 | ||
|
|
5fd3a92c7b | ||
|
|
84b87055df | ||
|
|
7cc0f066c7 | ||
|
|
868fc728dd | ||
|
|
faa90b89f6 | ||
|
|
f4d07e2c2c | ||
|
|
fad7886012 | ||
|
|
7eb139fd22 | ||
|
|
855d13e210 | ||
|
|
781b8962be | ||
|
|
047a965f9f | ||
|
|
47060db84b | ||
|
|
2693fd4ace | ||
|
|
faa702cab0 | ||
|
|
150ecbe29d | ||
|
|
df5e83e416 | ||
|
|
1f2ba65669 | ||
|
|
27be9eb281 | ||
|
|
f8222e1028 | ||
|
|
dce0c00773 | ||
|
|
dc8c5d9db9 | ||
|
|
261faaec0e | ||
|
|
dbd9a4a238 | ||
|
|
80d446e857 | ||
|
|
609a7ab014 | ||
|
|
c0d9bd7ef0 |
29
.github/workflows/build-image.yaml
vendored
Normal file
29
.github/workflows/build-image.yaml
vendored
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
name: RPi image
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ '*' ]
|
||||||
|
pull_request:
|
||||||
|
branches: [ master ]
|
||||||
|
release:
|
||||||
|
types: [ created ]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Build image
|
||||||
|
run: |
|
||||||
|
docker run --privileged --rm -v /dev:/dev -v $(pwd):/builder/repo -e TRAVIS_TAG="${{ github.event.release.tag_name }}" sfalexrog/img-tool:qemu-update
|
||||||
|
- name: Compress image
|
||||||
|
run: |
|
||||||
|
cd images && sudo chmod -R 777 . && zip -9 $(echo clover_*).zip clover_* && ls -l . && unzip -l clover_*.zip
|
||||||
|
- name: Upload image
|
||||||
|
uses: softprops/action-gh-release@v1
|
||||||
|
if: ${{ github.event_name == 'release' }}
|
||||||
|
with:
|
||||||
|
files: images/clover_*.zip
|
||||||
|
prerelease: true
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
23
.github/workflows/build.yml
vendored
Normal file
23
.github/workflows/build.yml
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
name: Build
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ '*' ]
|
||||||
|
pull_request:
|
||||||
|
branches: [ master ]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
melodic:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Native Melodic build
|
||||||
|
run: |
|
||||||
|
docker run --rm -v $(pwd):/root/catkin_ws/src/clover ros:melodic-ros-base /root/catkin_ws/src/clover/builder/standalone-install.sh
|
||||||
|
noetic:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Native Noetic build
|
||||||
|
run: |
|
||||||
|
docker run --rm -v $(pwd):/root/catkin_ws/src/clover ros:noetic-ros-base /root/catkin_ws/src/clover/builder/standalone-install.sh
|
||||||
53
.github/workflows/docs.yml
vendored
Normal file
53
.github/workflows/docs.yml
vendored
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
name: Documentation
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ '*' ]
|
||||||
|
pull_request:
|
||||||
|
branches: [ master ]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
docs:
|
||||||
|
runs-on: ubuntu-18.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Use Node.js
|
||||||
|
uses: actions/setup-node@v1
|
||||||
|
with: { node-version: '10' }
|
||||||
|
- name: Setup tools
|
||||||
|
run: |
|
||||||
|
sudo sh -c "echo ttf-mscorefonts-installer msttcorefonts/accepted-mscorefonts-eula select true | debconf-set-selections"
|
||||||
|
sudo apt-get update && sudo apt-get install -y calibre msttcorefonts
|
||||||
|
builder/assets/install_gitbook.sh
|
||||||
|
npm install markdownlint-cli@0.28.1 -g # FIXME: https://github.com/DavidAnson/markdownlint/issues/435
|
||||||
|
npm install svgexport -g
|
||||||
|
gitbook -V
|
||||||
|
markdownlint -V
|
||||||
|
- name: Run markdownlint
|
||||||
|
run: markdownlint docs
|
||||||
|
- name: Check Assets
|
||||||
|
run: |
|
||||||
|
./check_assets_size.py
|
||||||
|
./check_unused_assets.py
|
||||||
|
- name: Build GitBook
|
||||||
|
run: |
|
||||||
|
gitbook install
|
||||||
|
gitbook build
|
||||||
|
- name: Generate PDF
|
||||||
|
if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }}
|
||||||
|
run: |
|
||||||
|
for i in 1 2 3 4; do gitbook pdf ./ _book/clover.pdf && break || sleep 1; done
|
||||||
|
sudo apt-get -q install ghostscript
|
||||||
|
gs -sDEVICE=pdfwrite -dCompatibilityLevel=1.4 -dPDFSETTINGS=/default -dNOPAUSE -dQUIET -dBATCH -dDetectDuplicateImages -dCompressFonts=true -r150 -sOutputFile=_book/clover_ru_compressed.pdf _book/clover_ru.pdf
|
||||||
|
gs -sDEVICE=pdfwrite -dCompatibilityLevel=1.4 -dPDFSETTINGS=/default -dNOPAUSE -dQUIET -dBATCH -dDetectDuplicateImages -dCompressFonts=true -r150 -sOutputFile=_book/clover_en_compressed.pdf _book/clover_en.pdf
|
||||||
|
rm _book/clover_ru.pdf && mv _book/clover_ru_compressed.pdf _book/clover_ru.pdf
|
||||||
|
rm _book/clover_en.pdf && mv _book/clover_en_compressed.pdf _book/clover_en.pdf
|
||||||
|
ls -lah _book/clover*.pdf
|
||||||
|
- name: Deploy
|
||||||
|
uses: JamesIves/github-pages-deploy-action@4.1.3
|
||||||
|
if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }}
|
||||||
|
with:
|
||||||
|
branch: gh-pages
|
||||||
|
folder: _book
|
||||||
|
clean: true
|
||||||
|
single-commit: true # to avoid multiple copies of large pdf files
|
||||||
18
.github/workflows/editorconfig.yaml
vendored
Normal file
18
.github/workflows/editorconfig.yaml
vendored
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
name: Editorconfig
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ '*' ]
|
||||||
|
pull_request:
|
||||||
|
branches: [ master ]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
editorconfig:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: .editorconfig Linter
|
||||||
|
run: |
|
||||||
|
wget --no-verbose https://github.com/okalachev/editorconfig-checker/releases/download/1.2.1-disable-spaces-amount/ec-linux-amd64
|
||||||
|
chmod +x ec-linux-amd64
|
||||||
|
./ec-linux-amd64 -spaces-after-tabs -e "roslib.js|ros3d.js|eventemitter2.js|json-to-pretty-yaml.js|draw.cpp|BinUtils.swift|\.idea|apps/android/app|blockly/|clover_blocks/programs/|highlight/|python.js|Assets.xcassets|test_parser_pass.txt|test_node_failure.txt|aruco_pose/vendor|\.stl|\.dxf|\.dae"
|
||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -6,3 +6,4 @@ _book/
|
|||||||
package-lock.json
|
package-lock.json
|
||||||
clover_blocks/programs/*.*
|
clover_blocks/programs/*.*
|
||||||
!clover_blocks/programs/examples/*
|
!clover_blocks/programs/examples/*
|
||||||
|
/.vscode/
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
"ROS",
|
"ROS",
|
||||||
"ROS Kinetic",
|
"ROS Kinetic",
|
||||||
"ROS Melodic",
|
"ROS Melodic",
|
||||||
|
"ROS Noetic",
|
||||||
"OpenCV",
|
"OpenCV",
|
||||||
"OpenVPN",
|
"OpenVPN",
|
||||||
"Gazebo",
|
"Gazebo",
|
||||||
@@ -109,7 +110,8 @@
|
|||||||
"Li-ion",
|
"Li-ion",
|
||||||
"Nvidia",
|
"Nvidia",
|
||||||
"VirtualBox",
|
"VirtualBox",
|
||||||
"VMware"
|
"VMware",
|
||||||
|
"DuoCam"
|
||||||
],
|
],
|
||||||
"code_blocks": false
|
"code_blocks": false
|
||||||
},
|
},
|
||||||
|
|||||||
124
.travis.yml
124
.travis.yml
@@ -1,124 +0,0 @@
|
|||||||
os: linux
|
|
||||||
dist: xenial
|
|
||||||
language: generic
|
|
||||||
services:
|
|
||||||
- docker
|
|
||||||
env:
|
|
||||||
global:
|
|
||||||
- DOCKER="sfalexrog/img-tool:qemu-update"
|
|
||||||
- TARGET_REPO="https://github.com/${TRAVIS_REPO_SLUG}.git"
|
|
||||||
- IMAGE_VERSION=${TRAVIS_TAG:-${TRAVIS_COMMIT:0:7}}
|
|
||||||
- IMAGE_NAME="$(basename -s '.git' ${TARGET_REPO})_${IMAGE_VERSION}.img"
|
|
||||||
git:
|
|
||||||
depth: 1
|
|
||||||
jobs:
|
|
||||||
fast_finish: true
|
|
||||||
include:
|
|
||||||
- stage: Build
|
|
||||||
name: "Raspberry Pi Image Build"
|
|
||||||
cache:
|
|
||||||
directories:
|
|
||||||
- imgcache
|
|
||||||
before_script:
|
|
||||||
- docker pull ${DOCKER}
|
|
||||||
# Check if there are any cached images, copy them to our "images" directory
|
|
||||||
- if [ -n "$(ls -A imgcache/*.zip)" ]; then mkdir -p images && cp imgcache/*.zip images; fi
|
|
||||||
script:
|
|
||||||
- if [[ -z ${TRAVIS_TAG} && "${TRAVIS_PULL_REQUEST}" != "false" ]]; then
|
|
||||||
echo "Commit range is ${TRAVIS_COMMIT_RANGE}" &&
|
|
||||||
if [ $(git diff --name-only ${TRAVIS_COMMIT_RANGE} | grep -v ^"docs/" | wc -l) -eq 0 ]; then
|
|
||||||
echo " === Docs-only change; skipping build ===" &&
|
|
||||||
export SKIP_BUILD=true;
|
|
||||||
fi;
|
|
||||||
fi
|
|
||||||
- if [ -z ${SKIP_BUILD} ]; then
|
|
||||||
docker run --privileged --rm -v /dev:/dev -v $(pwd):/builder/repo -e TRAVIS_TAG="${TRAVIS_TAG}" ${DOCKER};
|
|
||||||
fi
|
|
||||||
before_cache:
|
|
||||||
- cp images/*.zip imgcache
|
|
||||||
after_success:
|
|
||||||
- sudo chmod -R 777 *
|
|
||||||
- cd images && zip -9 ${IMAGE_NAME}.zip ${IMAGE_NAME} && stat --printf="Compressed image size:%s\n" ${IMAGE_NAME}.zip
|
|
||||||
before_deploy:
|
|
||||||
# Set up git user name and tag this commit
|
|
||||||
- git config --local user.name "goldarte"
|
|
||||||
- git config --local user.email "goldartt@gmail.com"
|
|
||||||
deploy:
|
|
||||||
provider: releases
|
|
||||||
token: ${GITHUB_OAUTH_TOKEN}
|
|
||||||
file: ${IMAGE_NAME}.zip
|
|
||||||
skip_cleanup: true
|
|
||||||
on:
|
|
||||||
tags: true
|
|
||||||
draft: true
|
|
||||||
name: ${TRAVIS_TAG}
|
|
||||||
- stage: Build
|
|
||||||
name: "Native Kinetic build"
|
|
||||||
env:
|
|
||||||
- NATIVE_DOCKER=ros:kinetic-ros-base
|
|
||||||
before_script:
|
|
||||||
- docker pull ${NATIVE_DOCKER}
|
|
||||||
script:
|
|
||||||
- docker run --rm -v $(pwd):/root/catkin_ws/src/clover ${NATIVE_DOCKER} /root/catkin_ws/src/clover/builder/standalone-install.sh
|
|
||||||
- stage: Build
|
|
||||||
name: "Native Melodic build"
|
|
||||||
env:
|
|
||||||
- NATIVE_DOCKER=ros:melodic-ros-base
|
|
||||||
before_script:
|
|
||||||
- docker pull ${NATIVE_DOCKER}
|
|
||||||
script:
|
|
||||||
- docker run --rm -v $(pwd):/root/catkin_ws/src/clover ${NATIVE_DOCKER} /root/catkin_ws/src/clover/builder/standalone-install.sh
|
|
||||||
- stage: Build
|
|
||||||
name: "Documentation"
|
|
||||||
language: node_js
|
|
||||||
node_js:
|
|
||||||
- "10"
|
|
||||||
before_script:
|
|
||||||
- sudo sh -c "echo ttf-mscorefonts-installer msttcorefonts/accepted-mscorefonts-eula select true | debconf-set-selections"
|
|
||||||
- sudo apt update && sudo apt install -y calibre msttcorefonts
|
|
||||||
- npm install gitbook-cli -g
|
|
||||||
- gitbook fetch 3.2.3 && npm i npm@3.10.10 --prefix=~/.gitbook/versions/3.2.3/ # fixing https://travis-ci.org/github/CopterExpress/clover/jobs/766541125#L932
|
|
||||||
- npm install markdownlint-cli -g
|
|
||||||
- npm install svgexport -g
|
|
||||||
- gitbook -V
|
|
||||||
- markdownlint -V
|
|
||||||
script:
|
|
||||||
- markdownlint docs
|
|
||||||
- ./check_assets_size.py
|
|
||||||
- ./check_unused_assets.py
|
|
||||||
- gitbook install
|
|
||||||
- gitbook build
|
|
||||||
- for i in 1 2 3 4; do gitbook pdf ./ _book/clover.pdf && break || sleep 1; done
|
|
||||||
- sudo apt-get install ghostscript
|
|
||||||
- gs -sDEVICE=pdfwrite -dCompatibilityLevel=1.4 -dPDFSETTINGS=/default -dNOPAUSE -dQUIET -dBATCH -dDetectDuplicateImages -dCompressFonts=true -r150 -sOutputFile=_book/clover_ru_compressed.pdf _book/clover_ru.pdf
|
|
||||||
- gs -sDEVICE=pdfwrite -dCompatibilityLevel=1.4 -dPDFSETTINGS=/default -dNOPAUSE -dQUIET -dBATCH -dDetectDuplicateImages -dCompressFonts=true -r150 -sOutputFile=_book/clover_en_compressed.pdf _book/clover_en.pdf
|
|
||||||
- rm _book/clover_ru.pdf && mv _book/clover_ru_compressed.pdf _book/clover_ru.pdf
|
|
||||||
- rm _book/clover_en.pdf && mv _book/clover_en_compressed.pdf _book/clover_en.pdf
|
|
||||||
- ls -lah _book/clover*.pdf
|
|
||||||
deploy:
|
|
||||||
provider: pages
|
|
||||||
local_dir: _book
|
|
||||||
skip_cleanup: true
|
|
||||||
token: ${GITHUB_OAUTH_TOKEN}
|
|
||||||
keep_history: true
|
|
||||||
target_branch: master
|
|
||||||
repo: CopterExpress/clover.coex.tech
|
|
||||||
fqdn: clover.coex.tech
|
|
||||||
verbose: true
|
|
||||||
on:
|
|
||||||
branch: master
|
|
||||||
- stage: Build
|
|
||||||
name: Editorconfig-lint
|
|
||||||
language: generic
|
|
||||||
before_script:
|
|
||||||
- wget https://github.com/okalachev/editorconfig-checker/releases/download/1.2.1-disable-spaces-amount/ec-linux-amd64
|
|
||||||
- chmod +x ec-linux-amd64
|
|
||||||
script:
|
|
||||||
- ./ec-linux-amd64 -spaces-after-tabs -e "roslib.js|ros3d.js|eventemitter2.js|draw.cpp|BinUtils.swift|\.idea|apps/android/app|blockly/|clover_blocks/programs/|highlight/|python.js|Assets.xcassets|test_parser_pass.txt|test_node_failure.txt|aruco_pose/vendor|\.stl|\.dxf|\.dae"
|
|
||||||
stages:
|
|
||||||
- Build
|
|
||||||
# More info there
|
|
||||||
# https://github.com/travis-ci/travis-ci/issues/6893
|
|
||||||
# https://docs.travis-ci.com/user/customizing-the-build/
|
|
||||||
# https://docs.travis-ci.com/user/deployment/releases
|
|
||||||
# https://docs.travis-ci.com/user/environment-variables/
|
|
||||||
10
README.md
10
README.md
@@ -1,6 +1,6 @@
|
|||||||
# clover🍀: create autonomous drones easily
|
# clover🍀: create autonomous drones easily
|
||||||
|
|
||||||
<img src="docs/assets/clover42-main.png" align="right" width="400px" alt="COEX Clover Drone">
|
<img src="docs/assets/clover42-main-margin.png" align="right" width="400px" alt="COEX Clover Drone">
|
||||||
|
|
||||||
Clover is an open source [ROS](https://www.ros.org)-based framework, providing user-friendly tools to control [PX4](https://px4.io)-powered drones. Clover is available as a ROS package, but is shipped mainly as a preconfigured image for Raspberry Pi. Once you've installed Raspberry Pi on your drone and flashed the image to its microSD card, taking the drone up in the air is a matter of minutes.
|
Clover is an open source [ROS](https://www.ros.org)-based framework, providing user-friendly tools to control [PX4](https://px4.io)-powered drones. Clover is available as a ROS package, but is shipped mainly as a preconfigured image for Raspberry Pi. Once you've installed Raspberry Pi on your drone and flashed the image to its microSD card, taking the drone up in the air is a matter of minutes.
|
||||||
|
|
||||||
@@ -20,13 +20,13 @@ Clover drone is used on a wide range of educational events, including [Copter Ha
|
|||||||
|
|
||||||
Preconfigured image for Raspberry Pi with installed and configured software, ready to fly, is available [in the Releases section](https://github.com/CopterExpress/clover/releases).
|
Preconfigured image for Raspberry Pi with installed and configured software, ready to fly, is available [in the Releases section](https://github.com/CopterExpress/clover/releases).
|
||||||
|
|
||||||
[](https://travis-ci.org/CopterExpress/clover)
|

|
||||||

|

|
||||||
|
|
||||||
Image features:
|
Image features:
|
||||||
|
|
||||||
* Raspbian Buster
|
* Raspbian Buster
|
||||||
* [ROS Melodic](http://wiki.ros.org/melodic)
|
* [ROS Noetic](http://wiki.ros.org/noetic)
|
||||||
* Configured networking
|
* Configured networking
|
||||||
* OpenCV
|
* OpenCV
|
||||||
* [`mavros`](http://wiki.ros.org/mavros)
|
* [`mavros`](http://wiki.ros.org/mavros)
|
||||||
@@ -38,6 +38,10 @@ API description for autonomous flights is available [on GitBook](https://clover.
|
|||||||
|
|
||||||
For manual package installation and running see [`clover` package documentation](clover/README.md).
|
For manual package installation and running see [`clover` package documentation](clover/README.md).
|
||||||
|
|
||||||
|
## Support
|
||||||
|
|
||||||
|
[](https://t.me/COEXHelpdesk)
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
While the Clover platform source code is available under the MIT License, note, that the [documentation](docs/) is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
|
While the Clover platform source code is available under the MIT License, note, that the [documentation](docs/) is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
iOS-приложение для управления Клевером
|
# iOS-приложение для управления Клевером
|
||||||
--------------------------------------
|
|
||||||
|
|
||||||
Для установки зависимостей необходим [CocoaPods](https://cocoapods.org):
|
Для установки зависимостей необходим [CocoaPods](https://cocoapods.org):
|
||||||
|
|
||||||
|
|||||||
@@ -22,13 +22,21 @@ find_package(catkin REQUIRED COMPONENTS
|
|||||||
dynamic_reconfigure
|
dynamic_reconfigure
|
||||||
)
|
)
|
||||||
|
|
||||||
find_package(OpenCV 3 REQUIRED COMPONENTS core imgproc calib3d)
|
# Workaround for OpenCV 3/4 support
|
||||||
|
set(_opencv_version 4)
|
||||||
|
find_package(OpenCV ${_opencv_version} QUIET COMPONENTS core imgproc calib3d)
|
||||||
|
if (NOT OpenCV_FOUND)
|
||||||
|
message(STATUS "Did not find OpenCV 4, searching for OpenCV 3")
|
||||||
|
set(_opencv_version 3)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
find_package(OpenCV ${_opencv_version} REQUIRED COMPONENTS core imgproc calib3d)
|
||||||
if ("${OpenCV_VERSION_MINOR}" LESS "9")
|
if ("${OpenCV_VERSION_MINOR}" LESS "9")
|
||||||
message(STATUS "OpenCV version too low, using vendored ArUco package")
|
message(STATUS "OpenCV version too low, using vendored ArUco package")
|
||||||
include(vendor/VendorOpenCV.cmake)
|
include(vendor/VendorOpenCV.cmake)
|
||||||
else()
|
else()
|
||||||
message(STATUS "Using system OpenCV ArUco package")
|
message(STATUS "Using system OpenCV ArUco package")
|
||||||
find_package(OpenCV 3 REQUIRED COMPONENTS aruco)
|
find_package(OpenCV ${_opencv_version} REQUIRED COMPONENTS aruco)
|
||||||
endif()
|
endif()
|
||||||
message(STATUS "OpenCV include dirs: ${OpenCV_INCLUDE_DIRS}")
|
message(STATUS "OpenCV include dirs: ${OpenCV_INCLUDE_DIRS}")
|
||||||
message(STATUS "OpenCV libraries: ${OpenCV_LIBRARIES}")
|
message(STATUS "OpenCV libraries: ${OpenCV_LIBRARIES}")
|
||||||
@@ -172,6 +180,13 @@ target_link_libraries(aruco_pose
|
|||||||
${OpenCV_LIBRARIES}
|
${OpenCV_LIBRARIES}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Prevent aruco_pose from having undefined symbols
|
||||||
|
set_property(TARGET aruco_pose
|
||||||
|
APPEND
|
||||||
|
PROPERTY LINK_FLAGS
|
||||||
|
-Wl,--no-undefined
|
||||||
|
)
|
||||||
|
|
||||||
#############
|
#############
|
||||||
## Install ##
|
## Install ##
|
||||||
#############
|
#############
|
||||||
@@ -187,11 +202,11 @@ target_link_libraries(aruco_pose
|
|||||||
# )
|
# )
|
||||||
|
|
||||||
## Mark executables and/or libraries for installation
|
## Mark executables and/or libraries for installation
|
||||||
# install(TARGETS ${PROJECT_NAME} ${PROJECT_NAME}_node
|
install(TARGETS ${PROJECT_NAME}
|
||||||
# ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
|
ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
|
||||||
# LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
|
LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
|
||||||
# RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
|
RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
|
||||||
# )
|
)
|
||||||
|
|
||||||
## Mark cpp header files for installation
|
## Mark cpp header files for installation
|
||||||
# install(DIRECTORY include/${PROJECT_NAME}/
|
# install(DIRECTORY include/${PROJECT_NAME}/
|
||||||
@@ -207,6 +222,14 @@ target_link_libraries(aruco_pose
|
|||||||
# DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}
|
# DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}
|
||||||
# )
|
# )
|
||||||
|
|
||||||
|
catkin_install_python(PROGRAMS src/genmap.py
|
||||||
|
DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
|
||||||
|
)
|
||||||
|
|
||||||
|
install(DIRECTORY launch DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION})
|
||||||
|
|
||||||
|
install(DIRECTORY map DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION})
|
||||||
|
|
||||||
#############
|
#############
|
||||||
## Testing ##
|
## Testing ##
|
||||||
#############
|
#############
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ It's recommended to run it within the same nodelet manager with the camera nodel
|
|||||||
|
|
||||||
* `~map` – path to text file with markers list
|
* `~map` – path to text file with markers list
|
||||||
* `~frame_id` – published frame id (default: `aruco_map`)
|
* `~frame_id` – published frame id (default: `aruco_map`)
|
||||||
* `~known_tilt` – debug image width
|
* `~known_tilt` – known tilt (pitch and roll) of markers map as a frame
|
||||||
* `~image_width` – debug image width (default: 2000)
|
* `~image_width` – debug image width (default: 2000)
|
||||||
* `~image_height` – debug image height (default: 2000)
|
* `~image_height` – debug image height (default: 2000)
|
||||||
* `~image_margin` – debug image margin (default: 200)
|
* `~image_margin` – debug image margin (default: 200)
|
||||||
|
|||||||
@@ -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.3</version>
|
<version>0.21.1</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>
|
||||||
|
|||||||
@@ -111,17 +111,14 @@ public:
|
|||||||
image_transport::ImageTransport it(nh_);
|
image_transport::ImageTransport it(nh_);
|
||||||
image_transport::ImageTransport it_priv(nh_priv_);
|
image_transport::ImageTransport it_priv(nh_priv_);
|
||||||
|
|
||||||
map_markers_sub_ = nh_.subscribe("map_markers", 1, &ArucoDetect::mapMarkersCallback, this);
|
dyn_srv_ = std::make_shared<dynamic_reconfigure::Server<aruco_pose::DetectorConfig>>(nh_priv_);
|
||||||
|
dyn_srv_->setCallback(std::bind(&ArucoDetect::paramCallback, this, std::placeholders::_1, std::placeholders::_2));
|
||||||
|
|
||||||
debug_pub_ = it_priv.advertise("debug", 1);
|
debug_pub_ = it_priv.advertise("debug", 1);
|
||||||
markers_pub_ = nh_priv_.advertise<aruco_pose::MarkerArray>("markers", 1);
|
markers_pub_ = nh_priv_.advertise<aruco_pose::MarkerArray>("markers", 1);
|
||||||
vis_markers_pub_ = nh_priv_.advertise<visualization_msgs::MarkerArray>("visualization", 1);
|
vis_markers_pub_ = nh_priv_.advertise<visualization_msgs::MarkerArray>("visualization", 1);
|
||||||
img_sub_ = it.subscribeCamera("image_raw", 1, &ArucoDetect::imageCallback, this);
|
img_sub_ = it.subscribeCamera("image_raw", 1, &ArucoDetect::imageCallback, this);
|
||||||
|
map_markers_sub_ = nh_.subscribe("map_markers", 1, &ArucoDetect::mapMarkersCallback, this);
|
||||||
dyn_srv_ = std::make_shared<dynamic_reconfigure::Server<aruco_pose::DetectorConfig>>(nh_priv_);
|
|
||||||
dynamic_reconfigure::Server<aruco_pose::DetectorConfig>::CallbackType cb;
|
|
||||||
|
|
||||||
cb = std::bind(&ArucoDetect::paramCallback, this, std::placeholders::_1, std::placeholders::_2);
|
|
||||||
dyn_srv_->setCallback(cb);
|
|
||||||
|
|
||||||
NODELET_INFO("ready");
|
NODELET_INFO("ready");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -124,6 +124,11 @@ public:
|
|||||||
vis_markers_pub_ = nh_priv_.advertise<visualization_msgs::MarkerArray>("visualization", 1, true);
|
vis_markers_pub_ = nh_priv_.advertise<visualization_msgs::MarkerArray>("visualization", 1, true);
|
||||||
debug_pub_ = it_priv.advertise("debug", 1);
|
debug_pub_ = it_priv.advertise("debug", 1);
|
||||||
|
|
||||||
|
publishMarkersFrames();
|
||||||
|
publishMarkers();
|
||||||
|
publishMapImage();
|
||||||
|
vis_markers_pub_.publish(vis_array_);
|
||||||
|
|
||||||
image_sub_.subscribe(nh_, "image_raw", 1);
|
image_sub_.subscribe(nh_, "image_raw", 1);
|
||||||
info_sub_.subscribe(nh_, "camera_info", 1);
|
info_sub_.subscribe(nh_, "camera_info", 1);
|
||||||
markers_sub_.subscribe(nh_, "markers", 1);
|
markers_sub_.subscribe(nh_, "markers", 1);
|
||||||
@@ -131,11 +136,6 @@ public:
|
|||||||
sync_.reset(new message_filters::Synchronizer<SyncPolicy>(SyncPolicy(10), image_sub_, info_sub_, markers_sub_));
|
sync_.reset(new message_filters::Synchronizer<SyncPolicy>(SyncPolicy(10), image_sub_, info_sub_, markers_sub_));
|
||||||
sync_->registerCallback(boost::bind(&ArucoMap::callback, this, _1, _2, _3));
|
sync_->registerCallback(boost::bind(&ArucoMap::callback, this, _1, _2, _3));
|
||||||
|
|
||||||
publishMarkersFrames();
|
|
||||||
publishMarkers();
|
|
||||||
publishMapImage();
|
|
||||||
vis_markers_pub_.publish(vis_array_);
|
|
||||||
|
|
||||||
NODELET_INFO("ready");
|
NODELET_INFO("ready");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,26 +3,11 @@
|
|||||||
|
|
||||||
#include "draw.h"
|
#include "draw.h"
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
using namespace cv;
|
using namespace cv;
|
||||||
using namespace cv::aruco;
|
using namespace cv::aruco;
|
||||||
|
|
||||||
static void _cvProjectPoints2( const CvMat* object_points, const CvMat* rotation_vector,
|
|
||||||
const CvMat* translation_vector, const CvMat* camera_matrix,
|
|
||||||
const CvMat* distortion_coeffs, CvMat* image_points,
|
|
||||||
CvMat* dpdrot CV_DEFAULT(NULL), CvMat* dpdt CV_DEFAULT(NULL),
|
|
||||||
CvMat* dpdf CV_DEFAULT(NULL), CvMat* dpdc CV_DEFAULT(NULL),
|
|
||||||
CvMat* dpddist CV_DEFAULT(NULL),
|
|
||||||
double aspect_ratio CV_DEFAULT(0));
|
|
||||||
|
|
||||||
static void _projectPoints( InputArray objectPoints,
|
|
||||||
InputArray rvec, InputArray tvec,
|
|
||||||
InputArray cameraMatrix, InputArray distCoeffs,
|
|
||||||
OutputArray imagePoints,
|
|
||||||
OutputArray jacobian = noArray(),
|
|
||||||
double aspectRatio = 0 );
|
|
||||||
|
|
||||||
|
|
||||||
void _drawPlanarBoard(Board *_board, Size outSize, OutputArray _img, int marginSize,
|
void _drawPlanarBoard(Board *_board, Size outSize, OutputArray _img, int marginSize,
|
||||||
int borderBits, bool drawAxis) {
|
int borderBits, bool drawAxis) {
|
||||||
|
|
||||||
@@ -142,35 +127,194 @@ void _drawPlanarBoard(Board *_board, Size outSize, OutputArray _img, int marginS
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Draw a (potentially partially visible) line. */
|
/**
|
||||||
static void linePartial(InputOutputArray image, Point3f p1, Point3f p2, const Scalar& color,
|
* @brief Convert point coordinates from world space to camera space.
|
||||||
int thickness = 1, int lineType = LINE_8, int shift = 0)
|
*
|
||||||
|
* @param points A vector of points in world space.
|
||||||
|
* @param rvec Rotation matrix or Rodrigues rotation vector.
|
||||||
|
* @param tvec Translation vector from world to camera space.
|
||||||
|
*
|
||||||
|
* @return A vector of points in camera space.
|
||||||
|
*/
|
||||||
|
template<typename CvPointType>
|
||||||
|
static std::vector<CvPointType> worldToCamera(const std::vector<CvPointType>& points,
|
||||||
|
const cv::Mat& rvec, const cv::Mat& tvec)
|
||||||
{
|
{
|
||||||
// If both points are behind the screen, don't draw anything
|
// We operate with CV_64F matrices internally to avoid precision loss
|
||||||
if (p1.z <= 0 && p2.z <= 0) {
|
cv::Mat rvec_64f;
|
||||||
return;
|
cv::Mat tvec_64f;
|
||||||
|
rvec.convertTo(rvec_64f, CV_64F);
|
||||||
|
tvec.convertTo(tvec_64f, CV_64F);
|
||||||
|
|
||||||
|
// Convert Rodrigues vector to rotation matrix
|
||||||
|
cv::Mat rmat;
|
||||||
|
if ((rvec_64f.cols == 3 && rvec_64f.rows == 1) ||
|
||||||
|
(rvec_64f.cols == 1 && rvec_64f.rows == 3))
|
||||||
|
{
|
||||||
|
Rodrigues(rvec_64f, rmat);
|
||||||
}
|
}
|
||||||
Point2f p1p{p1.x, p1.y};
|
else
|
||||||
Point2f p2p{p2.x, p2.y};
|
{
|
||||||
// If points are on the different sides of the plane, compute intersection point
|
rmat = rvec_64f.clone();
|
||||||
if (p1.z * p2.z < 0) {
|
|
||||||
// Compute intersection point with the screen
|
|
||||||
// We denote alpha as such:
|
|
||||||
// xi = (1 - alpha) * x1 + alpha * x2
|
|
||||||
// yi = (1 - alpha) * y1 + alpha * y2
|
|
||||||
// zi = (1 - alpha) * z1 + alpha * z2 = 0
|
|
||||||
// Thus, alpha can be expressed as
|
|
||||||
// alpha = z1 / (z1 - z2)
|
|
||||||
float alpha = p1.z / (p1.z - p2.z);
|
|
||||||
Point2f pi{(1 - alpha) * p1.x + alpha * p2.x, (1 - alpha) * p1.y + alpha * p2.y};
|
|
||||||
// Now, if z1 is negative, we draw the line from (xi, yi) to (x2, y2), else we draw from (x1, y1) to (xi, yi)
|
|
||||||
if (p1.z < 0) {
|
|
||||||
p1p = pi;
|
|
||||||
} else {
|
|
||||||
p2p = pi;
|
|
||||||
}
|
}
|
||||||
|
// Make sure tvec has a size of (3, 1)
|
||||||
|
if (tvec_64f.rows == 1)
|
||||||
|
{
|
||||||
|
tvec_64f = tvec_64f.t();
|
||||||
}
|
}
|
||||||
line(image, p1p, p2p, color, thickness, lineType, shift);
|
std::vector<CvPointType> result;
|
||||||
|
result.reserve(points.size());
|
||||||
|
for(const auto& point : points)
|
||||||
|
{
|
||||||
|
// Calculate point coordinates in camera frame
|
||||||
|
// static_casts are here to silence potential narrowing conversion warnings
|
||||||
|
CvPointType camPoint{
|
||||||
|
static_cast<decltype(CvPointType::x)>(point.x * rmat.at<double>(0,0) + point.y * rmat.at<double>(0,1) + point.z * rmat.at<double>(0,2) + tvec_64f.at<double>(0)),
|
||||||
|
static_cast<decltype(CvPointType::y)>(point.x * rmat.at<double>(1,0) + point.y * rmat.at<double>(1,1) + point.z * rmat.at<double>(1,2) + tvec_64f.at<double>(1)),
|
||||||
|
static_cast<decltype(CvPointType::z)>(point.x * rmat.at<double>(2,0) + point.y * rmat.at<double>(2,1) + point.z * rmat.at<double>(2,2) + tvec_64f.at<double>(2))
|
||||||
|
};
|
||||||
|
result.push_back(camPoint);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Project points from camera space to screen space, applying distortion in the process.
|
||||||
|
*
|
||||||
|
* @param points A vector of points in camera space.
|
||||||
|
* @param cameraMatrix OpenCV intrinsic camera matrix.
|
||||||
|
* @param distCoeffs OpenCV distortion model coefficients.
|
||||||
|
*
|
||||||
|
* @return A vector of points in screen space.
|
||||||
|
*/
|
||||||
|
template<typename CvPointType>
|
||||||
|
static std::vector<CvPointType> cameraToScreen(const std::vector<CvPointType>& points,
|
||||||
|
const cv::Mat& cameraMatrix,
|
||||||
|
const cv::Mat& distCoeffs)
|
||||||
|
{
|
||||||
|
// We operate with CV_64F matrices internally to avoid precision loss
|
||||||
|
cv::Mat cm_64f; // camera matrix, CV_64F
|
||||||
|
cv::Mat dc_64f; // distortion coefficients, CV_64F
|
||||||
|
cameraMatrix.convertTo(cm_64f, CV_64F);
|
||||||
|
distCoeffs.convertTo(dc_64f, CV_64F);
|
||||||
|
|
||||||
|
// Make sure distortion vector has a size of (N, 1)
|
||||||
|
if (dc_64f.rows == 1)
|
||||||
|
{
|
||||||
|
dc_64f = dc_64f.t();
|
||||||
|
}
|
||||||
|
|
||||||
|
// We will always use 12 distortion coefficients,
|
||||||
|
// and we can safely pad missing ones with zeroes
|
||||||
|
dc_64f.resize(12, 0.0);
|
||||||
|
|
||||||
|
std::vector<CvPointType> result;
|
||||||
|
result.reserve(points.size());
|
||||||
|
|
||||||
|
for(const auto& point : points)
|
||||||
|
{
|
||||||
|
// Apply perspective projection, preserving initial Z coordinate
|
||||||
|
// Always use double-precision
|
||||||
|
cv::Point3d camPoint{
|
||||||
|
point.x / point.z,
|
||||||
|
point.y / point.z,
|
||||||
|
point.z
|
||||||
|
};
|
||||||
|
|
||||||
|
// Apply distortion
|
||||||
|
// Note that we do not consider tilted sensor distortion
|
||||||
|
// r^2 - distance from the image center squared
|
||||||
|
double r2 = camPoint.x * camPoint.x + camPoint.y * camPoint.y;
|
||||||
|
// r^4 - same, but to the 4th power
|
||||||
|
double r4 = r2 * r2;
|
||||||
|
// r^6 - same, but to the 6th power
|
||||||
|
double r6 = r4 * r2;
|
||||||
|
// tg1 - first tangential shift factor (2 * x * y)
|
||||||
|
double tg1 = 2 * camPoint.x * camPoint.y;
|
||||||
|
// tg2 - second tangential shift factor (r^2 + 2 * x^2)
|
||||||
|
double tg2 = r2 + 2 * camPoint.x * camPoint.x;
|
||||||
|
// tg3 - third tangential shift factor (r^2 + 2 * y^2)
|
||||||
|
double tg3 = r2 + 2 * camPoint.y * camPoint.y;
|
||||||
|
// polynomial distortion factor (numerator)
|
||||||
|
double pndist = 1 + dc_64f.at<double>(0) * r2 + dc_64f.at<double>(1) * r4 + dc_64f.at<double>(4) * r6;
|
||||||
|
// polynomial distortion factror (denominator)
|
||||||
|
double pddist = 1.0 / (1 + dc_64f.at<double>(5) * r2 + dc_64f.at<double>(6) * r4 + dc_64f.at<double>(7) * r6);
|
||||||
|
// Distorted point coordinates (always double-precision)
|
||||||
|
cv::Point3d distortedPoint{
|
||||||
|
camPoint.x * pndist * pddist + dc_64f.at<double>(2) * tg1 + dc_64f.at<double>(3) * tg2 + dc_64f.at<double>(8) * r2 + dc_64f.at<double>(9) * r4,
|
||||||
|
camPoint.y * pndist * pddist + dc_64f.at<double>(2) * tg3 + dc_64f.at<double>(3) * tg1 + dc_64f.at<double>(10) * r2 + dc_64f.at<double>(11) * r4,
|
||||||
|
camPoint.z
|
||||||
|
};
|
||||||
|
|
||||||
|
// Convert to screen space
|
||||||
|
// We use static_cast here to silence potential warnings about narrowing conversions
|
||||||
|
// (we expect that to be the case)
|
||||||
|
CvPointType screenPoint{
|
||||||
|
static_cast<decltype(CvPointType::x)>(distortedPoint.x * cm_64f.at<double>(0, 0) + cm_64f.at<double>(0, 2)),
|
||||||
|
static_cast<decltype(CvPointType::y)>(distortedPoint.y * cm_64f.at<double>(1, 1) + cm_64f.at<double>(1, 2)),
|
||||||
|
static_cast<decltype(CvPointType::z)>(distortedPoint.z)
|
||||||
|
};
|
||||||
|
|
||||||
|
result.push_back(screenPoint);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Clip a line against a clip plane.
|
||||||
|
*
|
||||||
|
* This function "clips" a line (described by two points in *camera space*)
|
||||||
|
* against a clip plane that is `clipPlaneDistance` meters away from the
|
||||||
|
* camera focal point. If both points are further away from the focal point
|
||||||
|
* than `clipPlaneDistance`, they will be returned unmodified. If one of the
|
||||||
|
* points is behind the clipping plane, a point *on* the clipping plane will
|
||||||
|
* be computed and returned as one of the points.
|
||||||
|
*
|
||||||
|
* If none of the points are visible, an empty vector will be returned.
|
||||||
|
*
|
||||||
|
* @param p1 First point on the line, in camera space.
|
||||||
|
* @param p2 Second point on the line, in camera space.
|
||||||
|
* @param clipPlaneDistance Distance from the focal point to the clipping plane.
|
||||||
|
* @return A vector of zero or two points on the clipped line, in camera space.
|
||||||
|
*/
|
||||||
|
static std::vector<Point3f> lineClip(Point3f p1, Point3f p2, float clipPlaneDistance = 0.1f)
|
||||||
|
{
|
||||||
|
// We don't need to compute an intersection if both points are
|
||||||
|
// behind us
|
||||||
|
if (p1.z < clipPlaneDistance && p2.z < clipPlaneDistance)
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
// We don't need to compute an intersection if both points are
|
||||||
|
// in front of us
|
||||||
|
if (p1.z > clipPlaneDistance && p2.z > clipPlaneDistance)
|
||||||
|
{
|
||||||
|
return {p1, p2};
|
||||||
|
}
|
||||||
|
// We don't really want to compute an intersection if both Z coordinates
|
||||||
|
// are sufficiently close to each other
|
||||||
|
if (std::abs(p1.z - p2.z) < 0.0001) // The number here is chosen arbitrarily
|
||||||
|
{
|
||||||
|
return {p1, p2};
|
||||||
|
}
|
||||||
|
// We compute the intersection as such:
|
||||||
|
// zi = (1 - alpha) * p1.z + alpha * p2.z = clipPlaneDistance
|
||||||
|
// alpha = (p1.z - clipPlaneDistance) / (p1.z - p2.z)
|
||||||
|
double alpha = (p1.z - clipPlaneDistance) / (p1.z - p2.z);
|
||||||
|
Point3f clipPlanePoint{
|
||||||
|
static_cast<float>((1 - alpha) * p1.x + alpha * p2.x),
|
||||||
|
static_cast<float>((1 - alpha) * p1.y + alpha * p2.y),
|
||||||
|
clipPlaneDistance
|
||||||
|
};
|
||||||
|
if (p1.z < clipPlaneDistance)
|
||||||
|
{
|
||||||
|
return {clipPlanePoint, p2};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return {p1, clipPlanePoint};
|
||||||
|
}
|
||||||
|
// Unreachable?
|
||||||
}
|
}
|
||||||
|
|
||||||
void _drawAxis(InputOutputArray _image, InputArray _cameraMatrix, InputArray _distCoeffs,
|
void _drawAxis(InputOutputArray _image, InputArray _cameraMatrix, InputArray _distCoeffs,
|
||||||
@@ -186,647 +330,23 @@ void _drawAxis(InputOutputArray _image, InputArray _cameraMatrix, InputArray _di
|
|||||||
axisPoints.push_back(Point3f(length, 0, 0));
|
axisPoints.push_back(Point3f(length, 0, 0));
|
||||||
axisPoints.push_back(Point3f(0, length, 0));
|
axisPoints.push_back(Point3f(0, length, 0));
|
||||||
axisPoints.push_back(Point3f(0, 0, length));
|
axisPoints.push_back(Point3f(0, 0, length));
|
||||||
std::vector<Point3f> imagePointsZ;
|
auto camAxisPoints = worldToCamera(axisPoints, _rvec.getMat(), _tvec.getMat());
|
||||||
_projectPoints(axisPoints, _rvec, _tvec, _cameraMatrix, _distCoeffs, imagePointsZ);
|
auto axisX = cameraToScreen(lineClip(camAxisPoints[0], camAxisPoints[1]), _cameraMatrix.getMat(), _distCoeffs.getMat());
|
||||||
|
auto axisY = cameraToScreen(lineClip(camAxisPoints[0], camAxisPoints[2]), _cameraMatrix.getMat(), _distCoeffs.getMat());
|
||||||
// draw axis lines
|
auto axisZ = cameraToScreen(lineClip(camAxisPoints[0], camAxisPoints[3]), _cameraMatrix.getMat(), _distCoeffs.getMat());
|
||||||
linePartial(_image, imagePointsZ[0], imagePointsZ[1], Scalar(0, 0, 255), 3);
|
if (axisX.size() > 0)
|
||||||
linePartial(_image, imagePointsZ[0], imagePointsZ[2], Scalar(0, 255, 0), 3);
|
{
|
||||||
linePartial(_image, imagePointsZ[0], imagePointsZ[3], Scalar(255, 0, 0), 3);
|
line(_image, Point2f{axisX[0].x, axisX[0].y}, Point2f{axisX[1].x, axisX[1].y},
|
||||||
|
Scalar(0, 0, 255), 3);
|
||||||
}
|
}
|
||||||
|
if (axisY.size() > 0)
|
||||||
static CvMat _cvMat(const cv::Mat& m)
|
|
||||||
{
|
{
|
||||||
CvMat self;
|
line(_image, Point2f{axisY[0].x, axisY[0].y}, Point2f{axisY[1].x, axisY[1].y},
|
||||||
CV_DbgAssert(m.dims <= 2);
|
Scalar(0, 255, 0), 3);
|
||||||
self = cvMat(m.rows, m.dims == 1 ? 1 : m.cols, m.type(), m.data);
|
|
||||||
self.step = (int)m.step[0];
|
|
||||||
self.type = (self.type & ~cv::Mat::CONTINUOUS_FLAG) | (m.flags & cv::Mat::CONTINUOUS_FLAG);
|
|
||||||
return self;
|
|
||||||
}
|
}
|
||||||
|
if (axisZ.size() > 0)
|
||||||
static void _projectPoints( InputArray _opoints,
|
|
||||||
InputArray _rvec,
|
|
||||||
InputArray _tvec,
|
|
||||||
InputArray _cameraMatrix,
|
|
||||||
InputArray _distCoeffs,
|
|
||||||
OutputArray _ipoints,
|
|
||||||
OutputArray _jacobian,
|
|
||||||
double aspectRatio )
|
|
||||||
{
|
{
|
||||||
Mat opoints = _opoints.getMat();
|
line(_image, Point2f{axisZ[0].x, axisZ[0].y}, Point2f{axisZ[1].x, axisZ[1].y},
|
||||||
int npoints = opoints.checkVector(3), depth = opoints.depth();
|
Scalar(255, 0, 0), 3);
|
||||||
CV_Assert(npoints >= 0 && (depth == CV_32F || depth == CV_64F));
|
|
||||||
|
|
||||||
CvMat dpdrot, dpdt, dpdf, dpdc, dpddist;
|
|
||||||
CvMat *pdpdrot = 0, *pdpdt = 0, *pdpdf = 0, *pdpdc = 0, *pdpddist = 0;
|
|
||||||
|
|
||||||
CV_Assert(_ipoints.needed());
|
|
||||||
|
|
||||||
_ipoints.create(npoints, 1, CV_MAKETYPE(depth, 3), -1, true);
|
|
||||||
Mat imagePoints = _ipoints.getMat();
|
|
||||||
CvMat c_imagePoints = _cvMat(imagePoints);
|
|
||||||
CvMat c_objectPoints = _cvMat(opoints);
|
|
||||||
Mat cameraMatrix = _cameraMatrix.getMat();
|
|
||||||
|
|
||||||
Mat rvec = _rvec.getMat(), tvec = _tvec.getMat();
|
|
||||||
CvMat c_cameraMatrix = _cvMat(cameraMatrix);
|
|
||||||
CvMat c_rvec = _cvMat(rvec), c_tvec = _cvMat(tvec);
|
|
||||||
|
|
||||||
double dc0buf[5] = {0};
|
|
||||||
Mat dc0(5, 1, CV_64F, dc0buf);
|
|
||||||
Mat distCoeffs = _distCoeffs.getMat();
|
|
||||||
if (distCoeffs.empty())
|
|
||||||
distCoeffs = dc0;
|
|
||||||
CvMat c_distCoeffs = _cvMat(distCoeffs);
|
|
||||||
int ndistCoeffs = distCoeffs.rows + distCoeffs.cols - 1;
|
|
||||||
|
|
||||||
Mat jacobian;
|
|
||||||
if (_jacobian.needed())
|
|
||||||
{
|
|
||||||
_jacobian.create(npoints * 2, 3 + 3 + 2 + 2 + ndistCoeffs, CV_64F);
|
|
||||||
jacobian = _jacobian.getMat();
|
|
||||||
pdpdrot = &(dpdrot = _cvMat(jacobian.colRange(0, 3)));
|
|
||||||
pdpdt = &(dpdt = _cvMat(jacobian.colRange(3, 6)));
|
|
||||||
pdpdf = &(dpdf = _cvMat(jacobian.colRange(6, 8)));
|
|
||||||
pdpdc = &(dpdc = _cvMat(jacobian.colRange(8, 10)));
|
|
||||||
pdpddist = &(dpddist = _cvMat(jacobian.colRange(10, 10 + ndistCoeffs)));
|
|
||||||
}
|
|
||||||
|
|
||||||
_cvProjectPoints2(&c_objectPoints, &c_rvec, &c_tvec, &c_cameraMatrix, &c_distCoeffs,
|
|
||||||
&c_imagePoints, pdpdrot, pdpdt, pdpdf, pdpdc, pdpddist, aspectRatio);
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace _detail
|
|
||||||
{
|
|
||||||
template <typename FLOAT>
|
|
||||||
void computeTiltProjectionMatrix(FLOAT tauX,
|
|
||||||
FLOAT tauY,
|
|
||||||
Matx<FLOAT, 3, 3>* matTilt = 0,
|
|
||||||
Matx<FLOAT, 3, 3>* dMatTiltdTauX = 0,
|
|
||||||
Matx<FLOAT, 3, 3>* dMatTiltdTauY = 0,
|
|
||||||
Matx<FLOAT, 3, 3>* invMatTilt = 0)
|
|
||||||
{
|
|
||||||
FLOAT cTauX = cos(tauX);
|
|
||||||
FLOAT sTauX = sin(tauX);
|
|
||||||
FLOAT cTauY = cos(tauY);
|
|
||||||
FLOAT sTauY = sin(tauY);
|
|
||||||
Matx<FLOAT, 3, 3> matRotX = Matx<FLOAT, 3, 3>(1,0,0,0,cTauX,sTauX,0,-sTauX,cTauX);
|
|
||||||
Matx<FLOAT, 3, 3> matRotY = Matx<FLOAT, 3, 3>(cTauY,0,-sTauY,0,1,0,sTauY,0,cTauY);
|
|
||||||
Matx<FLOAT, 3, 3> matRotXY = matRotY * matRotX;
|
|
||||||
Matx<FLOAT, 3, 3> matProjZ = Matx<FLOAT, 3, 3>(matRotXY(2,2),0,-matRotXY(0,2),0,matRotXY(2,2),-matRotXY(1,2),0,0,1);
|
|
||||||
if (matTilt)
|
|
||||||
{
|
|
||||||
// Matrix for trapezoidal distortion of tilted image sensor
|
|
||||||
*matTilt = matProjZ * matRotXY;
|
|
||||||
}
|
|
||||||
if (dMatTiltdTauX)
|
|
||||||
{
|
|
||||||
// Derivative with respect to tauX
|
|
||||||
Matx<FLOAT, 3, 3> dMatRotXYdTauX = matRotY * Matx<FLOAT, 3, 3>(0,0,0,0,-sTauX,cTauX,0,-cTauX,-sTauX);
|
|
||||||
Matx<FLOAT, 3, 3> dMatProjZdTauX = Matx<FLOAT, 3, 3>(dMatRotXYdTauX(2,2),0,-dMatRotXYdTauX(0,2),
|
|
||||||
0,dMatRotXYdTauX(2,2),-dMatRotXYdTauX(1,2),0,0,0);
|
|
||||||
*dMatTiltdTauX = (matProjZ * dMatRotXYdTauX) + (dMatProjZdTauX * matRotXY);
|
|
||||||
}
|
|
||||||
if (dMatTiltdTauY)
|
|
||||||
{
|
|
||||||
// Derivative with respect to tauY
|
|
||||||
Matx<FLOAT, 3, 3> dMatRotXYdTauY = Matx<FLOAT, 3, 3>(-sTauY,0,-cTauY,0,0,0,cTauY,0,-sTauY) * matRotX;
|
|
||||||
Matx<FLOAT, 3, 3> dMatProjZdTauY = Matx<FLOAT, 3, 3>(dMatRotXYdTauY(2,2),0,-dMatRotXYdTauY(0,2),
|
|
||||||
0,dMatRotXYdTauY(2,2),-dMatRotXYdTauY(1,2),0,0,0);
|
|
||||||
*dMatTiltdTauY = (matProjZ * dMatRotXYdTauY) + (dMatProjZdTauY * matRotXY);
|
|
||||||
}
|
|
||||||
if (invMatTilt)
|
|
||||||
{
|
|
||||||
FLOAT inv = 1./matRotXY(2,2);
|
|
||||||
Matx<FLOAT, 3, 3> invMatProjZ = Matx<FLOAT, 3, 3>(inv,0,inv*matRotXY(0,2),0,inv,inv*matRotXY(1,2),0,0,1);
|
|
||||||
*invMatTilt = matRotXY.t()*invMatProjZ;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
static const char* cvDistCoeffErr = "Distortion coefficients must be 1x4, 4x1, 1x5, 5x1, 1x8, 8x1, 1x12, 12x1, 1x14 or 14x1 floating-point vector";
|
|
||||||
|
|
||||||
static void _cvProjectPoints2Internal( const CvMat* objectPoints,
|
|
||||||
const CvMat* r_vec,
|
|
||||||
const CvMat* t_vec,
|
|
||||||
const CvMat* A,
|
|
||||||
const CvMat* distCoeffs,
|
|
||||||
CvMat* imagePoints, CvMat* dpdr CV_DEFAULT(NULL),
|
|
||||||
CvMat* dpdt CV_DEFAULT(NULL), CvMat* dpdf CV_DEFAULT(NULL),
|
|
||||||
CvMat* dpdc CV_DEFAULT(NULL), CvMat* dpdk CV_DEFAULT(NULL),
|
|
||||||
CvMat* dpdo CV_DEFAULT(NULL),
|
|
||||||
double aspectRatio CV_DEFAULT(0) )
|
|
||||||
{
|
|
||||||
Ptr<CvMat> matM, _m;
|
|
||||||
Ptr<CvMat> _dpdr, _dpdt, _dpdc, _dpdf, _dpdk;
|
|
||||||
Ptr<CvMat> _dpdo;
|
|
||||||
|
|
||||||
int i, j, count;
|
|
||||||
int calc_derivatives;
|
|
||||||
const CvPoint3D64f* M;
|
|
||||||
CvPoint3D64f* m;
|
|
||||||
double r[3], R[9], dRdr[27], t[3], a[9], k[14] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0}, fx, fy, cx, cy;
|
|
||||||
Matx33d matTilt = Matx33d::eye();
|
|
||||||
Matx33d dMatTiltdTauX(0,0,0,0,0,0,0,-1,0);
|
|
||||||
Matx33d dMatTiltdTauY(0,0,0,0,0,0,1,0,0);
|
|
||||||
CvMat _r, _t, _a = cvMat( 3, 3, CV_64F, a ), _k;
|
|
||||||
CvMat matR = cvMat( 3, 3, CV_64F, R ), _dRdr = cvMat( 3, 9, CV_64F, dRdr );
|
|
||||||
double *dpdr_p = 0, *dpdt_p = 0, *dpdk_p = 0, *dpdf_p = 0, *dpdc_p = 0;
|
|
||||||
double* dpdo_p = 0;
|
|
||||||
int dpdr_step = 0, dpdt_step = 0, dpdk_step = 0, dpdf_step = 0, dpdc_step = 0;
|
|
||||||
int dpdo_step = 0;
|
|
||||||
bool fixedAspectRatio = aspectRatio > FLT_EPSILON;
|
|
||||||
|
|
||||||
if( !CV_IS_MAT(objectPoints) || !CV_IS_MAT(r_vec) ||
|
|
||||||
!CV_IS_MAT(t_vec) || !CV_IS_MAT(A) ||
|
|
||||||
/*!CV_IS_MAT(distCoeffs) ||*/ !CV_IS_MAT(imagePoints) )
|
|
||||||
CV_Error( CV_StsBadArg, "One of required arguments is not a valid matrix" );
|
|
||||||
|
|
||||||
int total = objectPoints->rows * objectPoints->cols * CV_MAT_CN(objectPoints->type);
|
|
||||||
if(total % 3 != 0)
|
|
||||||
{
|
|
||||||
//we have stopped support of homogeneous coordinates because it cause ambiguity in interpretation of the input data
|
|
||||||
CV_Error( CV_StsBadArg, "Homogeneous coordinates are not supported" );
|
|
||||||
}
|
|
||||||
count = total / 3;
|
|
||||||
|
|
||||||
if( CV_IS_CONT_MAT(objectPoints->type) &&
|
|
||||||
(CV_MAT_DEPTH(objectPoints->type) == CV_32F || CV_MAT_DEPTH(objectPoints->type) == CV_64F)&&
|
|
||||||
((objectPoints->rows == 1 && CV_MAT_CN(objectPoints->type) == 3) ||
|
|
||||||
(objectPoints->rows == count && CV_MAT_CN(objectPoints->type)*objectPoints->cols == 3) ||
|
|
||||||
(objectPoints->rows == 3 && CV_MAT_CN(objectPoints->type) == 1 && objectPoints->cols == count)))
|
|
||||||
{
|
|
||||||
matM.reset(cvCreateMat( objectPoints->rows, objectPoints->cols, CV_MAKETYPE(CV_64F,CV_MAT_CN(objectPoints->type)) ));
|
|
||||||
cvConvert(objectPoints, matM);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// matM = cvCreateMat( 1, count, CV_64FC3 );
|
|
||||||
// cvConvertPointsHomogeneous( objectPoints, matM );
|
|
||||||
CV_Error( CV_StsBadArg, "Homogeneous coordinates are not supported" );
|
|
||||||
}
|
|
||||||
|
|
||||||
if( CV_IS_CONT_MAT(imagePoints->type) &&
|
|
||||||
(CV_MAT_DEPTH(imagePoints->type) == CV_32F || CV_MAT_DEPTH(imagePoints->type) == CV_64F) &&
|
|
||||||
((imagePoints->rows == 1 && CV_MAT_CN(imagePoints->type) == 3) ||
|
|
||||||
(imagePoints->rows == count && CV_MAT_CN(imagePoints->type)*imagePoints->cols == 3) ||
|
|
||||||
(imagePoints->rows == 3 && CV_MAT_CN(imagePoints->type) == 1 && imagePoints->cols == count)))
|
|
||||||
{
|
|
||||||
_m.reset(cvCreateMat( imagePoints->rows, imagePoints->cols, CV_MAKETYPE(CV_64F,CV_MAT_CN(imagePoints->type)) ));
|
|
||||||
cvConvert(imagePoints, _m);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// _m = cvCreateMat( 1, count, CV_64FC2 );
|
|
||||||
CV_Error( CV_StsBadArg, "Homogeneous coordinates are not supported" );
|
|
||||||
}
|
|
||||||
|
|
||||||
M = (CvPoint3D64f*)matM->data.db;
|
|
||||||
m = (CvPoint3D64f*)_m->data.db;
|
|
||||||
|
|
||||||
if( (CV_MAT_DEPTH(r_vec->type) != CV_64F && CV_MAT_DEPTH(r_vec->type) != CV_32F) ||
|
|
||||||
(((r_vec->rows != 1 && r_vec->cols != 1) ||
|
|
||||||
r_vec->rows*r_vec->cols*CV_MAT_CN(r_vec->type) != 3) &&
|
|
||||||
((r_vec->rows != 3 && r_vec->cols != 3) || CV_MAT_CN(r_vec->type) != 1)))
|
|
||||||
CV_Error( CV_StsBadArg, "Rotation must be represented by 1x3 or 3x1 "
|
|
||||||
"floating-point rotation vector, or 3x3 rotation matrix" );
|
|
||||||
|
|
||||||
if( r_vec->rows == 3 && r_vec->cols == 3 )
|
|
||||||
{
|
|
||||||
_r = cvMat( 3, 1, CV_64FC1, r );
|
|
||||||
cvRodrigues2( r_vec, &_r );
|
|
||||||
cvRodrigues2( &_r, &matR, &_dRdr );
|
|
||||||
cvCopy( r_vec, &matR );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_r = cvMat( r_vec->rows, r_vec->cols, CV_MAKETYPE(CV_64F,CV_MAT_CN(r_vec->type)), r );
|
|
||||||
cvConvert( r_vec, &_r );
|
|
||||||
cvRodrigues2( &_r, &matR, &_dRdr );
|
|
||||||
}
|
|
||||||
|
|
||||||
if( (CV_MAT_DEPTH(t_vec->type) != CV_64F && CV_MAT_DEPTH(t_vec->type) != CV_32F) ||
|
|
||||||
(t_vec->rows != 1 && t_vec->cols != 1) ||
|
|
||||||
t_vec->rows*t_vec->cols*CV_MAT_CN(t_vec->type) != 3 )
|
|
||||||
CV_Error( CV_StsBadArg,
|
|
||||||
"Translation vector must be 1x3 or 3x1 floating-point vector" );
|
|
||||||
|
|
||||||
_t = cvMat( t_vec->rows, t_vec->cols, CV_MAKETYPE(CV_64F,CV_MAT_CN(t_vec->type)), t );
|
|
||||||
cvConvert( t_vec, &_t );
|
|
||||||
|
|
||||||
if( (CV_MAT_TYPE(A->type) != CV_64FC1 && CV_MAT_TYPE(A->type) != CV_32FC1) ||
|
|
||||||
A->rows != 3 || A->cols != 3 )
|
|
||||||
CV_Error( CV_StsBadArg, "Instrinsic parameters must be 3x3 floating-point matrix" );
|
|
||||||
|
|
||||||
cvConvert( A, &_a );
|
|
||||||
fx = a[0]; fy = a[4];
|
|
||||||
cx = a[2]; cy = a[5];
|
|
||||||
|
|
||||||
if( fixedAspectRatio )
|
|
||||||
fx = fy*aspectRatio;
|
|
||||||
|
|
||||||
if( distCoeffs )
|
|
||||||
{
|
|
||||||
if( !CV_IS_MAT(distCoeffs) ||
|
|
||||||
(CV_MAT_DEPTH(distCoeffs->type) != CV_64F &&
|
|
||||||
CV_MAT_DEPTH(distCoeffs->type) != CV_32F) ||
|
|
||||||
(distCoeffs->rows != 1 && distCoeffs->cols != 1) ||
|
|
||||||
(distCoeffs->rows*distCoeffs->cols*CV_MAT_CN(distCoeffs->type) != 4 &&
|
|
||||||
distCoeffs->rows*distCoeffs->cols*CV_MAT_CN(distCoeffs->type) != 5 &&
|
|
||||||
distCoeffs->rows*distCoeffs->cols*CV_MAT_CN(distCoeffs->type) != 8 &&
|
|
||||||
distCoeffs->rows*distCoeffs->cols*CV_MAT_CN(distCoeffs->type) != 12 &&
|
|
||||||
distCoeffs->rows*distCoeffs->cols*CV_MAT_CN(distCoeffs->type) != 14) )
|
|
||||||
CV_Error( CV_StsBadArg, cvDistCoeffErr );
|
|
||||||
|
|
||||||
_k = cvMat( distCoeffs->rows, distCoeffs->cols,
|
|
||||||
CV_MAKETYPE(CV_64F,CV_MAT_CN(distCoeffs->type)), k );
|
|
||||||
cvConvert( distCoeffs, &_k );
|
|
||||||
if(k[12] != 0 || k[13] != 0)
|
|
||||||
{
|
|
||||||
_detail::computeTiltProjectionMatrix(k[12], k[13],
|
|
||||||
&matTilt, &dMatTiltdTauX, &dMatTiltdTauY);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if( dpdr )
|
|
||||||
{
|
|
||||||
if( !CV_IS_MAT(dpdr) ||
|
|
||||||
(CV_MAT_TYPE(dpdr->type) != CV_32FC1 &&
|
|
||||||
CV_MAT_TYPE(dpdr->type) != CV_64FC1) ||
|
|
||||||
dpdr->rows != count*2 || dpdr->cols != 3 )
|
|
||||||
CV_Error( CV_StsBadArg, "dp/drot must be 2Nx3 floating-point matrix" );
|
|
||||||
|
|
||||||
if( CV_MAT_TYPE(dpdr->type) == CV_64FC1 )
|
|
||||||
{
|
|
||||||
_dpdr.reset(cvCloneMat(dpdr));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
_dpdr.reset(cvCreateMat( 2*count, 3, CV_64FC1 ));
|
|
||||||
dpdr_p = _dpdr->data.db;
|
|
||||||
dpdr_step = _dpdr->step/sizeof(dpdr_p[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if( dpdt )
|
|
||||||
{
|
|
||||||
if( !CV_IS_MAT(dpdt) ||
|
|
||||||
(CV_MAT_TYPE(dpdt->type) != CV_32FC1 &&
|
|
||||||
CV_MAT_TYPE(dpdt->type) != CV_64FC1) ||
|
|
||||||
dpdt->rows != count*2 || dpdt->cols != 3 )
|
|
||||||
CV_Error( CV_StsBadArg, "dp/dT must be 2Nx3 floating-point matrix" );
|
|
||||||
|
|
||||||
if( CV_MAT_TYPE(dpdt->type) == CV_64FC1 )
|
|
||||||
{
|
|
||||||
_dpdt.reset(cvCloneMat(dpdt));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
_dpdt.reset(cvCreateMat( 2*count, 3, CV_64FC1 ));
|
|
||||||
dpdt_p = _dpdt->data.db;
|
|
||||||
dpdt_step = _dpdt->step/sizeof(dpdt_p[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if( dpdf )
|
|
||||||
{
|
|
||||||
if( !CV_IS_MAT(dpdf) ||
|
|
||||||
(CV_MAT_TYPE(dpdf->type) != CV_32FC1 && CV_MAT_TYPE(dpdf->type) != CV_64FC1) ||
|
|
||||||
dpdf->rows != count*2 || dpdf->cols != 2 )
|
|
||||||
CV_Error( CV_StsBadArg, "dp/df must be 2Nx2 floating-point matrix" );
|
|
||||||
|
|
||||||
if( CV_MAT_TYPE(dpdf->type) == CV_64FC1 )
|
|
||||||
{
|
|
||||||
_dpdf.reset(cvCloneMat(dpdf));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
_dpdf.reset(cvCreateMat( 2*count, 2, CV_64FC1 ));
|
|
||||||
dpdf_p = _dpdf->data.db;
|
|
||||||
dpdf_step = _dpdf->step/sizeof(dpdf_p[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if( dpdc )
|
|
||||||
{
|
|
||||||
if( !CV_IS_MAT(dpdc) ||
|
|
||||||
(CV_MAT_TYPE(dpdc->type) != CV_32FC1 && CV_MAT_TYPE(dpdc->type) != CV_64FC1) ||
|
|
||||||
dpdc->rows != count*2 || dpdc->cols != 2 )
|
|
||||||
CV_Error( CV_StsBadArg, "dp/dc must be 2Nx2 floating-point matrix" );
|
|
||||||
|
|
||||||
if( CV_MAT_TYPE(dpdc->type) == CV_64FC1 )
|
|
||||||
{
|
|
||||||
_dpdc.reset(cvCloneMat(dpdc));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
_dpdc.reset(cvCreateMat( 2*count, 2, CV_64FC1 ));
|
|
||||||
dpdc_p = _dpdc->data.db;
|
|
||||||
dpdc_step = _dpdc->step/sizeof(dpdc_p[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if( dpdk )
|
|
||||||
{
|
|
||||||
if( !CV_IS_MAT(dpdk) ||
|
|
||||||
(CV_MAT_TYPE(dpdk->type) != CV_32FC1 && CV_MAT_TYPE(dpdk->type) != CV_64FC1) ||
|
|
||||||
dpdk->rows != count*2 || (dpdk->cols != 14 && dpdk->cols != 12 && dpdk->cols != 8 && dpdk->cols != 5 && dpdk->cols != 4 && dpdk->cols != 2) )
|
|
||||||
CV_Error( CV_StsBadArg, "dp/df must be 2Nx14, 2Nx12, 2Nx8, 2Nx5, 2Nx4 or 2Nx2 floating-point matrix" );
|
|
||||||
|
|
||||||
if( !distCoeffs )
|
|
||||||
CV_Error( CV_StsNullPtr, "distCoeffs is NULL while dpdk is not" );
|
|
||||||
|
|
||||||
if( CV_MAT_TYPE(dpdk->type) == CV_64FC1 )
|
|
||||||
{
|
|
||||||
_dpdk.reset(cvCloneMat(dpdk));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
_dpdk.reset(cvCreateMat( dpdk->rows, dpdk->cols, CV_64FC1 ));
|
|
||||||
dpdk_p = _dpdk->data.db;
|
|
||||||
dpdk_step = _dpdk->step/sizeof(dpdk_p[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if( dpdo )
|
|
||||||
{
|
|
||||||
if( !CV_IS_MAT( dpdo ) || ( CV_MAT_TYPE( dpdo->type ) != CV_32FC1
|
|
||||||
&& CV_MAT_TYPE( dpdo->type ) != CV_64FC1 )
|
|
||||||
|| dpdo->rows != count * 2 || dpdo->cols != count * 3 )
|
|
||||||
CV_Error( CV_StsBadArg, "dp/do must be 2Nx3N floating-point matrix" );
|
|
||||||
|
|
||||||
if( CV_MAT_TYPE( dpdo->type ) == CV_64FC1 )
|
|
||||||
{
|
|
||||||
_dpdo.reset( cvCloneMat( dpdo ) );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
_dpdo.reset( cvCreateMat( 2 * count, 3 * count, CV_64FC1 ) );
|
|
||||||
cvZero(_dpdo);
|
|
||||||
dpdo_p = _dpdo->data.db;
|
|
||||||
dpdo_step = _dpdo->step / sizeof( dpdo_p[0] );
|
|
||||||
}
|
|
||||||
|
|
||||||
calc_derivatives = dpdr || dpdt || dpdf || dpdc || dpdk || dpdo;
|
|
||||||
|
|
||||||
for( i = 0; i < count; i++ )
|
|
||||||
{
|
|
||||||
double X = M[i].x, Y = M[i].y, Z = M[i].z;
|
|
||||||
double x = R[0]*X + R[1]*Y + R[2]*Z + t[0];
|
|
||||||
double y = R[3]*X + R[4]*Y + R[5]*Z + t[1];
|
|
||||||
double z = R[6]*X + R[7]*Y + R[8]*Z + t[2];
|
|
||||||
double r2, r4, r6, a1, a2, a3, cdist, icdist2;
|
|
||||||
double xd, yd, xd0, yd0, invProj;
|
|
||||||
Vec3d vecTilt;
|
|
||||||
Vec3d dVecTilt;
|
|
||||||
Matx22d dMatTilt;
|
|
||||||
Vec2d dXdYd;
|
|
||||||
|
|
||||||
double z0 = z;
|
|
||||||
z = z ? 1./z : 1;
|
|
||||||
x *= z; y *= z;
|
|
||||||
|
|
||||||
r2 = x*x + y*y;
|
|
||||||
r4 = r2*r2;
|
|
||||||
r6 = r4*r2;
|
|
||||||
a1 = 2*x*y;
|
|
||||||
a2 = r2 + 2*x*x;
|
|
||||||
a3 = r2 + 2*y*y;
|
|
||||||
cdist = 1 + k[0]*r2 + k[1]*r4 + k[4]*r6;
|
|
||||||
icdist2 = 1./(1 + k[5]*r2 + k[6]*r4 + k[7]*r6);
|
|
||||||
xd0 = x*cdist*icdist2 + k[2]*a1 + k[3]*a2 + k[8]*r2+k[9]*r4;
|
|
||||||
yd0 = y*cdist*icdist2 + k[2]*a3 + k[3]*a1 + k[10]*r2+k[11]*r4;
|
|
||||||
|
|
||||||
// additional distortion by projecting onto a tilt plane
|
|
||||||
vecTilt = matTilt*Vec3d(xd0, yd0, 1);
|
|
||||||
invProj = vecTilt(2) ? 1./vecTilt(2) : 1;
|
|
||||||
xd = invProj * vecTilt(0);
|
|
||||||
yd = invProj * vecTilt(1);
|
|
||||||
|
|
||||||
m[i].x = xd*fx + cx;
|
|
||||||
m[i].y = yd*fy + cy;
|
|
||||||
m[i].z = z; // Just put the projected Z coordinate here, we mainly care about the sign
|
|
||||||
|
|
||||||
if( calc_derivatives )
|
|
||||||
{
|
|
||||||
if( dpdc_p )
|
|
||||||
{
|
|
||||||
dpdc_p[0] = 1; dpdc_p[1] = 0; // dp_xdc_x; dp_xdc_y
|
|
||||||
dpdc_p[dpdc_step] = 0;
|
|
||||||
dpdc_p[dpdc_step+1] = 1;
|
|
||||||
dpdc_p += dpdc_step*2;
|
|
||||||
}
|
|
||||||
|
|
||||||
if( dpdf_p )
|
|
||||||
{
|
|
||||||
if( fixedAspectRatio )
|
|
||||||
{
|
|
||||||
dpdf_p[0] = 0; dpdf_p[1] = xd*aspectRatio; // dp_xdf_x; dp_xdf_y
|
|
||||||
dpdf_p[dpdf_step] = 0;
|
|
||||||
dpdf_p[dpdf_step+1] = yd;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
dpdf_p[0] = xd; dpdf_p[1] = 0;
|
|
||||||
dpdf_p[dpdf_step] = 0;
|
|
||||||
dpdf_p[dpdf_step+1] = yd;
|
|
||||||
}
|
|
||||||
dpdf_p += dpdf_step*2;
|
|
||||||
}
|
|
||||||
for (int row = 0; row < 2; ++row)
|
|
||||||
for (int col = 0; col < 2; ++col)
|
|
||||||
dMatTilt(row,col) = matTilt(row,col)*vecTilt(2)
|
|
||||||
- matTilt(2,col)*vecTilt(row);
|
|
||||||
double invProjSquare = (invProj*invProj);
|
|
||||||
dMatTilt *= invProjSquare;
|
|
||||||
if( dpdk_p )
|
|
||||||
{
|
|
||||||
dXdYd = dMatTilt*Vec2d(x*icdist2*r2, y*icdist2*r2);
|
|
||||||
dpdk_p[0] = fx*dXdYd(0);
|
|
||||||
dpdk_p[dpdk_step] = fy*dXdYd(1);
|
|
||||||
dXdYd = dMatTilt*Vec2d(x*icdist2*r4, y*icdist2*r4);
|
|
||||||
dpdk_p[1] = fx*dXdYd(0);
|
|
||||||
dpdk_p[dpdk_step+1] = fy*dXdYd(1);
|
|
||||||
if( _dpdk->cols > 2 )
|
|
||||||
{
|
|
||||||
dXdYd = dMatTilt*Vec2d(a1, a3);
|
|
||||||
dpdk_p[2] = fx*dXdYd(0);
|
|
||||||
dpdk_p[dpdk_step+2] = fy*dXdYd(1);
|
|
||||||
dXdYd = dMatTilt*Vec2d(a2, a1);
|
|
||||||
dpdk_p[3] = fx*dXdYd(0);
|
|
||||||
dpdk_p[dpdk_step+3] = fy*dXdYd(1);
|
|
||||||
if( _dpdk->cols > 4 )
|
|
||||||
{
|
|
||||||
dXdYd = dMatTilt*Vec2d(x*icdist2*r6, y*icdist2*r6);
|
|
||||||
dpdk_p[4] = fx*dXdYd(0);
|
|
||||||
dpdk_p[dpdk_step+4] = fy*dXdYd(1);
|
|
||||||
|
|
||||||
if( _dpdk->cols > 5 )
|
|
||||||
{
|
|
||||||
dXdYd = dMatTilt*Vec2d(
|
|
||||||
x*cdist*(-icdist2)*icdist2*r2, y*cdist*(-icdist2)*icdist2*r2);
|
|
||||||
dpdk_p[5] = fx*dXdYd(0);
|
|
||||||
dpdk_p[dpdk_step+5] = fy*dXdYd(1);
|
|
||||||
dXdYd = dMatTilt*Vec2d(
|
|
||||||
x*cdist*(-icdist2)*icdist2*r4, y*cdist*(-icdist2)*icdist2*r4);
|
|
||||||
dpdk_p[6] = fx*dXdYd(0);
|
|
||||||
dpdk_p[dpdk_step+6] = fy*dXdYd(1);
|
|
||||||
dXdYd = dMatTilt*Vec2d(
|
|
||||||
x*cdist*(-icdist2)*icdist2*r6, y*cdist*(-icdist2)*icdist2*r6);
|
|
||||||
dpdk_p[7] = fx*dXdYd(0);
|
|
||||||
dpdk_p[dpdk_step+7] = fy*dXdYd(1);
|
|
||||||
if( _dpdk->cols > 8 )
|
|
||||||
{
|
|
||||||
dXdYd = dMatTilt*Vec2d(r2, 0);
|
|
||||||
dpdk_p[8] = fx*dXdYd(0); //s1
|
|
||||||
dpdk_p[dpdk_step+8] = fy*dXdYd(1); //s1
|
|
||||||
dXdYd = dMatTilt*Vec2d(r4, 0);
|
|
||||||
dpdk_p[9] = fx*dXdYd(0); //s2
|
|
||||||
dpdk_p[dpdk_step+9] = fy*dXdYd(1); //s2
|
|
||||||
dXdYd = dMatTilt*Vec2d(0, r2);
|
|
||||||
dpdk_p[10] = fx*dXdYd(0);//s3
|
|
||||||
dpdk_p[dpdk_step+10] = fy*dXdYd(1); //s3
|
|
||||||
dXdYd = dMatTilt*Vec2d(0, r4);
|
|
||||||
dpdk_p[11] = fx*dXdYd(0);//s4
|
|
||||||
dpdk_p[dpdk_step+11] = fy*dXdYd(1); //s4
|
|
||||||
if( _dpdk->cols > 12 )
|
|
||||||
{
|
|
||||||
dVecTilt = dMatTiltdTauX * Vec3d(xd0, yd0, 1);
|
|
||||||
dpdk_p[12] = fx * invProjSquare * (
|
|
||||||
dVecTilt(0) * vecTilt(2) - dVecTilt(2) * vecTilt(0));
|
|
||||||
dpdk_p[dpdk_step+12] = fy*invProjSquare * (
|
|
||||||
dVecTilt(1) * vecTilt(2) - dVecTilt(2) * vecTilt(1));
|
|
||||||
dVecTilt = dMatTiltdTauY * Vec3d(xd0, yd0, 1);
|
|
||||||
dpdk_p[13] = fx * invProjSquare * (
|
|
||||||
dVecTilt(0) * vecTilt(2) - dVecTilt(2) * vecTilt(0));
|
|
||||||
dpdk_p[dpdk_step+13] = fy * invProjSquare * (
|
|
||||||
dVecTilt(1) * vecTilt(2) - dVecTilt(2) * vecTilt(1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
dpdk_p += dpdk_step*2;
|
|
||||||
}
|
|
||||||
|
|
||||||
if( dpdt_p )
|
|
||||||
{
|
|
||||||
double dxdt[] = { z, 0, -x*z }, dydt[] = { 0, z, -y*z };
|
|
||||||
for( j = 0; j < 3; j++ )
|
|
||||||
{
|
|
||||||
double dr2dt = 2*x*dxdt[j] + 2*y*dydt[j];
|
|
||||||
double dcdist_dt = k[0]*dr2dt + 2*k[1]*r2*dr2dt + 3*k[4]*r4*dr2dt;
|
|
||||||
double dicdist2_dt = -icdist2*icdist2*(k[5]*dr2dt + 2*k[6]*r2*dr2dt + 3*k[7]*r4*dr2dt);
|
|
||||||
double da1dt = 2*(x*dydt[j] + y*dxdt[j]);
|
|
||||||
double dmxdt = (dxdt[j]*cdist*icdist2 + x*dcdist_dt*icdist2 + x*cdist*dicdist2_dt +
|
|
||||||
k[2]*da1dt + k[3]*(dr2dt + 4*x*dxdt[j]) + k[8]*dr2dt + 2*r2*k[9]*dr2dt);
|
|
||||||
double dmydt = (dydt[j]*cdist*icdist2 + y*dcdist_dt*icdist2 + y*cdist*dicdist2_dt +
|
|
||||||
k[2]*(dr2dt + 4*y*dydt[j]) + k[3]*da1dt + k[10]*dr2dt + 2*r2*k[11]*dr2dt);
|
|
||||||
dXdYd = dMatTilt*Vec2d(dmxdt, dmydt);
|
|
||||||
dpdt_p[j] = fx*dXdYd(0);
|
|
||||||
dpdt_p[dpdt_step+j] = fy*dXdYd(1);
|
|
||||||
}
|
|
||||||
dpdt_p += dpdt_step*2;
|
|
||||||
}
|
|
||||||
|
|
||||||
if( dpdr_p )
|
|
||||||
{
|
|
||||||
double dx0dr[] =
|
|
||||||
{
|
|
||||||
X*dRdr[0] + Y*dRdr[1] + Z*dRdr[2],
|
|
||||||
X*dRdr[9] + Y*dRdr[10] + Z*dRdr[11],
|
|
||||||
X*dRdr[18] + Y*dRdr[19] + Z*dRdr[20]
|
|
||||||
};
|
|
||||||
double dy0dr[] =
|
|
||||||
{
|
|
||||||
X*dRdr[3] + Y*dRdr[4] + Z*dRdr[5],
|
|
||||||
X*dRdr[12] + Y*dRdr[13] + Z*dRdr[14],
|
|
||||||
X*dRdr[21] + Y*dRdr[22] + Z*dRdr[23]
|
|
||||||
};
|
|
||||||
double dz0dr[] =
|
|
||||||
{
|
|
||||||
X*dRdr[6] + Y*dRdr[7] + Z*dRdr[8],
|
|
||||||
X*dRdr[15] + Y*dRdr[16] + Z*dRdr[17],
|
|
||||||
X*dRdr[24] + Y*dRdr[25] + Z*dRdr[26]
|
|
||||||
};
|
|
||||||
for( j = 0; j < 3; j++ )
|
|
||||||
{
|
|
||||||
double dxdr = z*(dx0dr[j] - x*dz0dr[j]);
|
|
||||||
double dydr = z*(dy0dr[j] - y*dz0dr[j]);
|
|
||||||
double dr2dr = 2*x*dxdr + 2*y*dydr;
|
|
||||||
double dcdist_dr = (k[0] + 2*k[1]*r2 + 3*k[4]*r4)*dr2dr;
|
|
||||||
double dicdist2_dr = -icdist2*icdist2*(k[5] + 2*k[6]*r2 + 3*k[7]*r4)*dr2dr;
|
|
||||||
double da1dr = 2*(x*dydr + y*dxdr);
|
|
||||||
double dmxdr = (dxdr*cdist*icdist2 + x*dcdist_dr*icdist2 + x*cdist*dicdist2_dr +
|
|
||||||
k[2]*da1dr + k[3]*(dr2dr + 4*x*dxdr) + (k[8] + 2*r2*k[9])*dr2dr);
|
|
||||||
double dmydr = (dydr*cdist*icdist2 + y*dcdist_dr*icdist2 + y*cdist*dicdist2_dr +
|
|
||||||
k[2]*(dr2dr + 4*y*dydr) + k[3]*da1dr + (k[10] + 2*r2*k[11])*dr2dr);
|
|
||||||
dXdYd = dMatTilt*Vec2d(dmxdr, dmydr);
|
|
||||||
dpdr_p[j] = fx*dXdYd(0);
|
|
||||||
dpdr_p[dpdr_step+j] = fy*dXdYd(1);
|
|
||||||
}
|
|
||||||
dpdr_p += dpdr_step*2;
|
|
||||||
}
|
|
||||||
|
|
||||||
if( dpdo_p )
|
|
||||||
{
|
|
||||||
double dxdo[] = { z * ( R[0] - x * z * z0 * R[6] ),
|
|
||||||
z * ( R[1] - x * z * z0 * R[7] ),
|
|
||||||
z * ( R[2] - x * z * z0 * R[8] ) };
|
|
||||||
double dydo[] = { z * ( R[3] - y * z * z0 * R[6] ),
|
|
||||||
z * ( R[4] - y * z * z0 * R[7] ),
|
|
||||||
z * ( R[5] - y * z * z0 * R[8] ) };
|
|
||||||
for( j = 0; j < 3; j++ )
|
|
||||||
{
|
|
||||||
double dr2do = 2 * x * dxdo[j] + 2 * y * dydo[j];
|
|
||||||
double dr4do = 2 * r2 * dr2do;
|
|
||||||
double dr6do = 3 * r4 * dr2do;
|
|
||||||
double da1do = 2 * y * dxdo[j] + 2 * x * dydo[j];
|
|
||||||
double da2do = dr2do + 4 * x * dxdo[j];
|
|
||||||
double da3do = dr2do + 4 * y * dydo[j];
|
|
||||||
double dcdist_do
|
|
||||||
= k[0] * dr2do + k[1] * dr4do + k[4] * dr6do;
|
|
||||||
double dicdist2_do = -icdist2 * icdist2
|
|
||||||
* ( k[5] * dr2do + k[6] * dr4do + k[7] * dr6do );
|
|
||||||
double dxd0_do = cdist * icdist2 * dxdo[j]
|
|
||||||
+ x * icdist2 * dcdist_do + x * cdist * dicdist2_do
|
|
||||||
+ k[2] * da1do + k[3] * da2do + k[8] * dr2do
|
|
||||||
+ k[9] * dr4do;
|
|
||||||
double dyd0_do = cdist * icdist2 * dydo[j]
|
|
||||||
+ y * icdist2 * dcdist_do + y * cdist * dicdist2_do
|
|
||||||
+ k[2] * da3do + k[3] * da1do + k[10] * dr2do
|
|
||||||
+ k[11] * dr4do;
|
|
||||||
dXdYd = dMatTilt * Vec2d( dxd0_do, dyd0_do );
|
|
||||||
dpdo_p[i * 3 + j] = fx * dXdYd( 0 );
|
|
||||||
dpdo_p[dpdo_step + i * 3 + j] = fy * dXdYd( 1 );
|
|
||||||
}
|
|
||||||
dpdo_p += dpdo_step * 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if( _m != imagePoints )
|
|
||||||
cvConvert( _m, imagePoints );
|
|
||||||
|
|
||||||
if( _dpdr != dpdr )
|
|
||||||
cvConvert( _dpdr, dpdr );
|
|
||||||
|
|
||||||
if( _dpdt != dpdt )
|
|
||||||
cvConvert( _dpdt, dpdt );
|
|
||||||
|
|
||||||
if( _dpdf != dpdf )
|
|
||||||
cvConvert( _dpdf, dpdf );
|
|
||||||
|
|
||||||
if( _dpdc != dpdc )
|
|
||||||
cvConvert( _dpdc, dpdc );
|
|
||||||
|
|
||||||
if( _dpdk != dpdk )
|
|
||||||
cvConvert( _dpdk, dpdk );
|
|
||||||
|
|
||||||
if( _dpdo != dpdo )
|
|
||||||
cvConvert( _dpdo, dpdo );
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _cvProjectPoints2( const CvMat* objectPoints,
|
|
||||||
const CvMat* r_vec,
|
|
||||||
const CvMat* t_vec,
|
|
||||||
const CvMat* A,
|
|
||||||
const CvMat* distCoeffs,
|
|
||||||
CvMat* imagePoints, CvMat* dpdr,
|
|
||||||
CvMat* dpdt, CvMat* dpdf,
|
|
||||||
CvMat* dpdc, CvMat* dpdk,
|
|
||||||
double aspectRatio )
|
|
||||||
{
|
|
||||||
_cvProjectPoints2Internal( objectPoints, r_vec, t_vec, A, distCoeffs, imagePoints, dpdr, dpdt,
|
|
||||||
dpdf, dpdc, dpdk, NULL, aspectRatio );
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
Generate map file for aruco_map nodelet.
|
Generate map file for aruco_map nodelet.
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
genmap.py <length> <x> <y> <dist_x> <dist_y> [<first>] [<x0>] [<y0>] [--top-left | --bottom-left]
|
genmap.py <length> <x> <y> <dist_x> <dist_y> [<first>] [<x0>] [<y0>] [--top-left | --bottom-left] [-o <filename>]
|
||||||
genmap.py (-h | --help)
|
genmap.py (-h | --help)
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
@@ -27,6 +27,7 @@ Options:
|
|||||||
<y0> Y coordinate for the first marker [default: 0]
|
<y0> Y coordinate for the first marker [default: 0]
|
||||||
--top-left First marker is on top-left (default)
|
--top-left First marker is on top-left (default)
|
||||||
--bottom-left First marker is on bottom-left
|
--bottom-left First marker is on bottom-left
|
||||||
|
-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 > $(catkin_find aruco_pose map)/test_map.txt
|
||||||
@@ -34,6 +35,8 @@ Example:
|
|||||||
|
|
||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
|
|
||||||
|
import sys
|
||||||
|
from os import path
|
||||||
from docopt import docopt
|
from docopt import docopt
|
||||||
|
|
||||||
|
|
||||||
@@ -49,14 +52,19 @@ dist_x = float(arguments['<dist_x>'])
|
|||||||
dist_y = float(arguments['<dist_y>'])
|
dist_y = float(arguments['<dist_y>'])
|
||||||
bottom_left = arguments['--bottom-left']
|
bottom_left = arguments['--bottom-left']
|
||||||
|
|
||||||
|
if arguments['-o'] is None:
|
||||||
|
output = sys.stdout
|
||||||
|
else:
|
||||||
|
output = open(path.join(path.dirname(__file__), '..', 'map', arguments['-o']), 'w')
|
||||||
|
|
||||||
max_y = y0 + (markers_y - 1) * dist_y
|
max_y = y0 + (markers_y - 1) * dist_y
|
||||||
|
|
||||||
print('# id\tlength\tx\ty\tz\trot_z\trot_y\trot_x')
|
output.write('# id\tlength\tx\ty\tz\trot_z\trot_y\trot_x\n')
|
||||||
for y in range(markers_y):
|
for y in range(markers_y):
|
||||||
for x in range(markers_x):
|
for x in range(markers_x):
|
||||||
pos_x = x0 + x * dist_x
|
pos_x = x0 + x * dist_x
|
||||||
pos_y = y0 + y * dist_y
|
pos_y = y0 + y * dist_y
|
||||||
if not bottom_left:
|
if not bottom_left:
|
||||||
pos_y = max_y - pos_y
|
pos_y = max_y - pos_y
|
||||||
print('{}\t{}\t{}\t{}\t{}\t{}\t{}\t{}'.format(first, length, pos_x, pos_y, 0, 0, 0, 0))
|
output.write('{}\t{}\t{}\t{}\t{}\t{}\t{}\t{}\n'.format(first, length, pos_x, pos_y, 0, 0, 0, 0))
|
||||||
first += 1
|
first += 1
|
||||||
|
|||||||
3
aruco_pose/vendor/VendorOpenCV.cmake
vendored
3
aruco_pose/vendor/VendorOpenCV.cmake
vendored
@@ -7,6 +7,7 @@ endif()
|
|||||||
|
|
||||||
message(STATUS "Adding vendored aruco_pose OpenCV module")
|
message(STATUS "Adding vendored aruco_pose OpenCV module")
|
||||||
add_library(_opencv_aruco STATIC
|
add_library(_opencv_aruco STATIC
|
||||||
|
vendor/aruco/src/apriltag_quad_thresh.cpp
|
||||||
vendor/aruco/src/aruco.cpp
|
vendor/aruco/src/aruco.cpp
|
||||||
vendor/aruco/src/charuco.cpp
|
vendor/aruco/src/charuco.cpp
|
||||||
vendor/aruco/src/dictionary.cpp
|
vendor/aruco/src/dictionary.cpp
|
||||||
@@ -23,7 +24,7 @@ target_compile_definitions(_opencv_aruco PRIVATE
|
|||||||
CV_OVERRIDE=override
|
CV_OVERRIDE=override
|
||||||
)
|
)
|
||||||
target_compile_options(_opencv_aruco PRIVATE
|
target_compile_options(_opencv_aruco PRIVATE
|
||||||
-fpic -fPIC
|
-fpic -fPIC -fvisibility=hidden
|
||||||
)
|
)
|
||||||
|
|
||||||
target_include_directories(_opencv_aruco PUBLIC
|
target_include_directories(_opencv_aruco PUBLIC
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ void ptsort_(struct pt *pts, int sz)
|
|||||||
|
|
||||||
// Use stack storage if it's not too big.
|
// Use stack storage if it's not too big.
|
||||||
cv::AutoBuffer<struct pt, 1024> _tmp_stack(sz);
|
cv::AutoBuffer<struct pt, 1024> _tmp_stack(sz);
|
||||||
memcpy(_tmp_stack.data(), pts, sizeof(struct pt) * sz);
|
memcpy(_tmp_stack, pts, sizeof(struct pt) * sz);
|
||||||
|
|
||||||
int asz = sz/2;
|
int asz = sz/2;
|
||||||
int bsz = sz - asz;
|
int bsz = sz - asz;
|
||||||
@@ -470,11 +470,11 @@ int quad_segment_agg(int sz, struct line_fit_pt *lfps, int indices[4]){
|
|||||||
int rvalloc_pos = 0;
|
int rvalloc_pos = 0;
|
||||||
int rvalloc_size = 3*sz;
|
int rvalloc_size = 3*sz;
|
||||||
cv::AutoBuffer<struct remove_vertex, 0> rvalloc_(std::max(1, rvalloc_size));
|
cv::AutoBuffer<struct remove_vertex, 0> rvalloc_(std::max(1, rvalloc_size));
|
||||||
memset(rvalloc_.data(), 0, sizeof(rvalloc_[0]) * rvalloc_.size()); // TODO Add AutoBuffer zero fill
|
memset(rvalloc_, 0, sizeof(rvalloc_[0]) * rvalloc_.size()); // TODO Add AutoBuffer zero fill
|
||||||
struct remove_vertex *rvalloc = rvalloc_.data();
|
struct remove_vertex *rvalloc = rvalloc_;
|
||||||
cv::AutoBuffer<struct segment, 0> segs_(std::max(1, sz)); // TODO Add AutoBuffer zero fill
|
cv::AutoBuffer<struct segment, 0> segs_(std::max(1, sz)); // TODO Add AutoBuffer zero fill
|
||||||
memset(segs_.data(), 0, sizeof(segs_[0]) * segs_.size());
|
memset(segs_, 0, sizeof(segs_[0]) * segs_.size());
|
||||||
struct segment *segs = segs_.data();
|
struct segment *segs = segs_;
|
||||||
|
|
||||||
// populate with initial entries
|
// populate with initial entries
|
||||||
for (int i = 0; i < sz; i++) {
|
for (int i = 0; i < sz; i++) {
|
||||||
@@ -753,8 +753,8 @@ int fit_quad(const Ptr<DetectorParameters> &_params, const Mat im, zarray_t *clu
|
|||||||
// efficiently computed for any contiguous range of indices.
|
// efficiently computed for any contiguous range of indices.
|
||||||
|
|
||||||
cv::AutoBuffer<struct line_fit_pt, 64> lfps_(sz);
|
cv::AutoBuffer<struct line_fit_pt, 64> lfps_(sz);
|
||||||
memset(lfps_.data(), 0, sizeof(lfps_[0]) * lfps_.size()); // TODO Add AutoBuffer zero fill
|
memset(lfps_, 0, sizeof(lfps_[0]) * lfps_.size()); // TODO Add AutoBuffer zero fill
|
||||||
struct line_fit_pt *lfps = lfps_.data();
|
struct line_fit_pt *lfps = lfps_;
|
||||||
|
|
||||||
for (int i = 0; i < sz; i++) {
|
for (int i = 0; i < sz; i++) {
|
||||||
struct pt *p;
|
struct pt *p;
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
"yametrika",
|
"yametrika",
|
||||||
"anchors",
|
"anchors",
|
||||||
"collapsible-menu",
|
"collapsible-menu",
|
||||||
"validate-links",
|
"validate-links@https://github.com/okalachev/gitbook-plugin-validate-links.git",
|
||||||
"bulk-redirect@https://github.com/okalachev/gitbook-plugin-bulk-redirect.git",
|
"bulk-redirect@https://github.com/okalachev/gitbook-plugin-bulk-redirect.git",
|
||||||
"sitemap@https://github.com/okalachev/plugin-sitemap.git",
|
"sitemap@https://github.com/okalachev/plugin-sitemap.git",
|
||||||
"toolbar@https://github.com/hamishwillee/gitbook-plugin-toolbar.git",
|
"toolbar@https://github.com/hamishwillee/gitbook-plugin-toolbar.git",
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
from distutils.core import setup
|
from distutils.core import setup
|
||||||
|
|
||||||
|
|||||||
@@ -8,5 +8,9 @@ ExecStart=/bin/bash -c ". /home/pi/catkin_ws/devel/setup.sh; \
|
|||||||
ROS_HOSTNAME=`hostname`.local exec stdbuf -o L roslaunch clover clover.launch --wait --screen --skip-log-check \
|
ROS_HOSTNAME=`hostname`.local exec stdbuf -o L roslaunch clover clover.launch --wait --screen --skip-log-check \
|
||||||
2> >(tee /tmp/clover.err)"
|
2> >(tee /tmp/clover.err)"
|
||||||
|
|
||||||
|
ExecStartPre=+rm /var/log/clover.log
|
||||||
|
StandardOutput=file:/var/log/clover.log
|
||||||
|
StandardError=file:/var/log/clover.log
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
WantedBy=multi-user.target
|
WantedBy=multi-user.target
|
||||||
|
|||||||
9
builder/assets/install_gitbook.sh
Executable file
9
builder/assets/install_gitbook.sh
Executable file
@@ -0,0 +1,9 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# GitBook CLI is deprecated, its installation is broken.
|
||||||
|
# This script fixes it until we stop using GitBook.
|
||||||
|
|
||||||
|
export NPM_CONFIG_UNSAFE_PERM=1
|
||||||
|
|
||||||
|
npm install gitbook-cli -g
|
||||||
|
gitbook fetch 3.2.3 && npm i npm@3.10.10 --prefix=~/.gitbook/versions/3.2.3/ # fixing https://travis-ci.org/github/CopterExpress/clover/jobs/766541125#L932
|
||||||
18
builder/assets/noetic-rosdep-clover.yaml
Normal file
18
builder/assets/noetic-rosdep-clover.yaml
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
async_web_server_cpp:
|
||||||
|
debian:
|
||||||
|
buster: [ros-noetic-async-web-server-cpp]
|
||||||
|
led_msgs:
|
||||||
|
debian:
|
||||||
|
buster: [ros-noetic-led-msgs]
|
||||||
|
ros_pytest:
|
||||||
|
debian:
|
||||||
|
buster: [ros-noetic-ros-pytest]
|
||||||
|
tf2_web_republisher:
|
||||||
|
debian:
|
||||||
|
buster: [ros-noetic-tf2-web-republisher]
|
||||||
|
web_video_server:
|
||||||
|
debian:
|
||||||
|
buster: [ros-noetic-web-video-server]
|
||||||
|
ws281x:
|
||||||
|
debian:
|
||||||
|
buster: [ros-noetic-ws281x]
|
||||||
@@ -3,7 +3,7 @@ Description=Launcher for the ROS master, parameter server and rosout logging nod
|
|||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
User=pi
|
User=pi
|
||||||
ExecStart=/bin/sh -c ". /opt/ros/melodic/setup.sh; ROS_HOSTNAME=`hostname`.local exec roscore"
|
ExecStart=/bin/sh -c ". /opt/ros/noetic/setup.sh; ROS_HOSTNAME=`hostname`.local exec roscore"
|
||||||
Restart=on-failure
|
Restart=on-failure
|
||||||
RestartSec=3
|
RestartSec=3
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,8 @@
|
|||||||
|
|
||||||
set -e # Exit immidiately on non-zero result
|
set -e # Exit immidiately on non-zero result
|
||||||
|
|
||||||
SOURCE_IMAGE="https://downloads.raspberrypi.org/raspios_lite_armhf/images/raspios_lite_armhf-2021-01-12/2021-01-11-raspios-buster-armhf-lite.zip"
|
# https://www.raspberrypi.org/software/operating-systems/#raspberry-pi-os-32-bit
|
||||||
|
SOURCE_IMAGE="https://downloads.raspberrypi.org/raspios_lite_armhf/images/raspios_lite_armhf-2021-05-28/2021-05-07-raspios-buster-armhf-lite.zip"
|
||||||
|
|
||||||
export DEBIAN_FRONTEND=${DEBIAN_FRONTEND:='noninteractive'}
|
export DEBIAN_FRONTEND=${DEBIAN_FRONTEND:='noninteractive'}
|
||||||
export LANG=${LANG:='C.UTF-8'}
|
export LANG=${LANG:='C.UTF-8'}
|
||||||
@@ -104,8 +105,6 @@ ${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/butterf
|
|||||||
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/monkey.service' '/lib/systemd/system/'
|
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/monkey.service' '/lib/systemd/system/'
|
||||||
# software install
|
# software install
|
||||||
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} exec ${SCRIPTS_DIR}'/image-software.sh'
|
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} exec ${SCRIPTS_DIR}'/image-software.sh'
|
||||||
# examples
|
|
||||||
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/examples' '/home/pi/' # TODO: symlink?
|
|
||||||
# network setup
|
# network setup
|
||||||
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} exec ${SCRIPTS_DIR}'/image-network.sh'
|
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} exec ${SCRIPTS_DIR}'/image-network.sh'
|
||||||
# avahi setup
|
# avahi setup
|
||||||
@@ -114,15 +113,11 @@ ${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/avahi-s
|
|||||||
# If RPi then use a one thread to build a ROS package on RPi, else use all
|
# If RPi then use a one thread to build a ROS package on RPi, else use all
|
||||||
[[ $(arch) == 'armv7l' ]] && NUMBER_THREADS=1 || NUMBER_THREADS=$(nproc --all)
|
[[ $(arch) == 'armv7l' ]] && NUMBER_THREADS=1 || NUMBER_THREADS=$(nproc --all)
|
||||||
# Clover
|
# Clover
|
||||||
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/clover.service' '/lib/systemd/system/'
|
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/noetic-rosdep-clover.yaml' '/etc/ros/rosdep/'
|
||||||
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/roscore.service' '/lib/systemd/system/'
|
|
||||||
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/melodic-rosdep-clover.yaml' '/etc/ros/rosdep/'
|
|
||||||
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/ros_python_paths' '/etc/sudoers.d/'
|
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/ros_python_paths' '/etc/sudoers.d/'
|
||||||
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/pigpiod.service' '/lib/systemd/system/'
|
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/pigpiod.service' '/lib/systemd/system/'
|
||||||
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/launch.nanorc' '/usr/share/nano/'
|
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/launch.nanorc' '/usr/share/nano/'
|
||||||
# ${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/kinetic-ros-clover.rosinstall' '/home/pi/ros_catkin_ws/'
|
# ${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/kinetic-ros-clover.rosinstall' '/home/pi/ros_catkin_ws/'
|
||||||
# Add PX4 udev rules
|
|
||||||
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${REPO_DIR}'/clover/config/99-px4fmu.rules' '/lib/udev/rules.d/'
|
|
||||||
# Add rename script
|
# Add rename script
|
||||||
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} exec ${SCRIPTS_DIR}'/image-ros.sh' ${REPO_URL} ${IMAGE_VERSION} false false ${NUMBER_THREADS}
|
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} exec ${SCRIPTS_DIR}'/image-ros.sh' ${REPO_URL} ${IMAGE_VERSION} false false ${NUMBER_THREADS}
|
||||||
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} exec ${SCRIPTS_DIR}'/image-validate.sh'
|
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} exec ${SCRIPTS_DIR}'/image-validate.sh'
|
||||||
|
|||||||
@@ -21,6 +21,9 @@ INSTALL_ROS_PACK_SOURCES=$3
|
|||||||
DISCOVER_ROS_PACK=$4
|
DISCOVER_ROS_PACK=$4
|
||||||
NUMBER_THREADS=$5
|
NUMBER_THREADS=$5
|
||||||
|
|
||||||
|
# Current ROS distribution
|
||||||
|
ROS_DISTRO=noetic
|
||||||
|
|
||||||
echo_stamp() {
|
echo_stamp() {
|
||||||
# TEMPLATE: echo_stamp <TEXT> <TYPE>
|
# TEMPLATE: echo_stamp <TEXT> <TYPE>
|
||||||
# TYPE: SUCCESS, ERROR, INFO
|
# TYPE: SUCCESS, ERROR, INFO
|
||||||
@@ -68,7 +71,8 @@ my_travis_retry() {
|
|||||||
# TODO: 'kinetic-rosdep-clover.yaml' should add only if we use our repo?
|
# TODO: 'kinetic-rosdep-clover.yaml' should add only if we use our repo?
|
||||||
echo_stamp "Init rosdep"
|
echo_stamp "Init rosdep"
|
||||||
my_travis_retry rosdep init
|
my_travis_retry rosdep init
|
||||||
echo "yaml file:///etc/ros/rosdep/melodic-rosdep-clover.yaml" >> /etc/ros/rosdep/sources.list.d/20-default.list
|
# FIXME: Re-add this after missing packages are built
|
||||||
|
echo "yaml file:///etc/ros/rosdep/${ROS_DISTRO}-rosdep-clover.yaml" >> /etc/ros/rosdep/sources.list.d/20-default.list
|
||||||
my_travis_retry rosdep update
|
my_travis_retry rosdep update
|
||||||
|
|
||||||
echo_stamp "Populate rosdep for ROS user"
|
echo_stamp "Populate rosdep for ROS user"
|
||||||
@@ -76,22 +80,40 @@ my_travis_retry sudo -u pi rosdep update
|
|||||||
|
|
||||||
export ROS_IP='127.0.0.1' # needed for running tests
|
export ROS_IP='127.0.0.1' # needed for running tests
|
||||||
|
|
||||||
# echo_stamp "Reconfiguring Clover repository for simplier unshallowing" # TODO: bring back
|
# echo_stamp "Reconfiguring Clover repository for simplier unshallowing"
|
||||||
# cd /home/pi/catkin_ws/src/clover
|
cd /home/pi/catkin_ws/src/clover
|
||||||
# git config remote.origin.fetch "+refs/heads/*:refs/remotes/origin/*"
|
git config remote.origin.fetch "+refs/heads/*:refs/remotes/origin/*"
|
||||||
echo_stamp "Remove .git from Clover to reduce the size"
|
|
||||||
rm -rf /home/pi/catkin_ws/src/clover/.git # TODO: remove
|
# This is sort of a hack to force "custom" packages to be installed - the ones built by COEX, linked against OpenCV 4.2
|
||||||
|
# I **wish** OpenCV would not be such a mess, but, well, here we are.
|
||||||
|
echo_stamp "Installing OpenCV 4.2-compatible ROS packages"
|
||||||
|
apt install -y --no-install-recommends \
|
||||||
|
ros-${ROS_DISTRO}-compressed-image-transport=1.14.0-0buster \
|
||||||
|
ros-${ROS_DISTRO}-cv-bridge=1.15.0-0buster \
|
||||||
|
ros-${ROS_DISTRO}-cv-camera=0.5.0-0buster \
|
||||||
|
ros-${ROS_DISTRO}-image-publisher=1.15.3-0buster \
|
||||||
|
ros-${ROS_DISTRO}-web-video-server=0.2.1-0buster
|
||||||
|
apt-mark hold \
|
||||||
|
ros-${ROS_DISTRO}-compressed-image-transport \
|
||||||
|
ros-${ROS_DISTRO}-cv-bridge \
|
||||||
|
ros-${ROS_DISTRO}-cv-camera \
|
||||||
|
ros-${ROS_DISTRO}-image-publisher \
|
||||||
|
ros-${ROS_DISTRO}-web-video-server
|
||||||
|
|
||||||
|
echo_stamp "Installing libboost-dev" # https://travis-ci.org/github/CopterExpress/clover/jobs/766318908#L6536
|
||||||
|
my_travis_retry apt-get install -y --no-install-recommends libboost-dev libboost-all-dev
|
||||||
|
|
||||||
echo_stamp "Build and install Clover"
|
echo_stamp "Build and install Clover"
|
||||||
cd /home/pi/catkin_ws
|
cd /home/pi/catkin_ws
|
||||||
# Don't try to install gazebo_ros
|
# Don't try to install gazebo_ros
|
||||||
my_travis_retry rosdep install -y --from-paths src --ignore-src --rosdistro melodic --os=debian:buster \
|
my_travis_retry rosdep install -y --from-paths src --ignore-src --rosdistro ${ROS_DISTRO} --os=debian:buster \
|
||||||
--skip-keys=gazebo_ros --skip-keys=gazebo_plugins
|
--skip-keys=gazebo_ros --skip-keys=gazebo_plugins
|
||||||
my_travis_retry pip install wheel
|
my_travis_retry pip3 install wheel
|
||||||
my_travis_retry pip 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/melodic/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=Release -DCATKIN_BLACKLIST_PACKAGES=clover_gazebo_plugins
|
catkin_make -j2 -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCATKIN_BLACKLIST_PACKAGES=clover_gazebo_plugins
|
||||||
|
source devel/setup.bash
|
||||||
|
|
||||||
echo_stamp "Install clever package (for backwards compatibility)"
|
echo_stamp "Install clever package (for backwards compatibility)"
|
||||||
cd /home/pi/catkin_ws/src/clover/builder/assets/clever
|
cd /home/pi/catkin_ws/src/clover/builder/assets/clever
|
||||||
@@ -100,31 +122,27 @@ rm -rf build # remove build artifacts
|
|||||||
|
|
||||||
echo_stamp "Build Clover documentation"
|
echo_stamp "Build Clover documentation"
|
||||||
cd /home/pi/catkin_ws/src/clover
|
cd /home/pi/catkin_ws/src/clover
|
||||||
NPM_CONFIG_UNSAFE_PERM=true npm install gitbook-cli -g
|
builder/assets/install_gitbook.sh
|
||||||
NPM_CONFIG_UNSAFE_PERM=true gitbook fetch 3.2.3 && npm i npm@3.10.10 --prefix=~/.gitbook/versions/3.2.3/ # fixing https://travis-ci.org/github/CopterExpress/clover/jobs/766541125#L932
|
gitbook install
|
||||||
NPM_CONFIG_UNSAFE_PERM=true gitbook install
|
|
||||||
gitbook build
|
gitbook build
|
||||||
touch node_modules/CATKIN_IGNORE docs/CATKIN_IGNORE _book/CATKIN_IGNORE clover/www/CATKIN_IGNORE apps/CATKIN_IGNORE # ignore documentation files by catkin
|
touch node_modules/CATKIN_IGNORE docs/CATKIN_IGNORE _book/CATKIN_IGNORE clover/www/CATKIN_IGNORE apps/CATKIN_IGNORE # ignore documentation files by catkin
|
||||||
|
|
||||||
echo_stamp "Installing additional ROS packages"
|
echo_stamp "Installing additional ROS packages"
|
||||||
my_travis_retry apt-get install -y --no-install-recommends \
|
my_travis_retry apt-get install -y --no-install-recommends \
|
||||||
ros-melodic-dynamic-reconfigure \
|
ros-${ROS_DISTRO}-dynamic-reconfigure \
|
||||||
ros-melodic-compressed-image-transport \
|
ros-${ROS_DISTRO}-rosbridge-suite \
|
||||||
ros-melodic-rosbridge-suite \
|
ros-${ROS_DISTRO}-rosserial \
|
||||||
ros-melodic-rosserial \
|
ros-${ROS_DISTRO}-usb-cam \
|
||||||
ros-melodic-usb-cam \
|
ros-${ROS_DISTRO}-vl53l1x \
|
||||||
ros-melodic-vl53l1x \
|
ros-${ROS_DISTRO}-ws281x \
|
||||||
ros-melodic-ws281x \
|
ros-${ROS_DISTRO}-rosshow \
|
||||||
ros-melodic-rosshow
|
ros-${ROS_DISTRO}-cmake-modules \
|
||||||
|
ros-${ROS_DISTRO}-image-view
|
||||||
|
|
||||||
# TODO move GeographicLib datasets to Mavros debian package
|
# TODO move GeographicLib datasets to Mavros debian package
|
||||||
echo_stamp "Install GeographicLib datasets (needed for mavros)" \
|
echo_stamp "Install GeographicLib datasets (needed for mavros)" \
|
||||||
&& wget -qO- https://raw.githubusercontent.com/mavlink/mavros/master/mavros/scripts/install_geographiclib_datasets.sh | bash
|
&& wget -qO- https://raw.githubusercontent.com/mavlink/mavros/master/mavros/scripts/install_geographiclib_datasets.sh | bash
|
||||||
|
|
||||||
# FIXME: Buster comes with tornado==5.1.1 but we need tornado==4.2.1 for rosbridge_suite
|
|
||||||
# (note that Python 3 will still have a more recent version)
|
|
||||||
pip install tornado==4.2.1
|
|
||||||
|
|
||||||
echo_stamp "Running tests"
|
echo_stamp "Running tests"
|
||||||
cd /home/pi/catkin_ws
|
cd /home/pi/catkin_ws
|
||||||
# FIXME: Investigate failing tests
|
# FIXME: Investigate failing tests
|
||||||
@@ -133,15 +151,26 @@ catkin_make run_tests #&& catkin_test_results
|
|||||||
echo_stamp "Change permissions for catkin_ws"
|
echo_stamp "Change permissions for catkin_ws"
|
||||||
chown -Rf pi:pi /home/pi/catkin_ws
|
chown -Rf pi:pi /home/pi/catkin_ws
|
||||||
|
|
||||||
echo_stamp "Change permissions for examples"
|
echo_stamp "Make \$HOME/examples symlink"
|
||||||
|
ln -s "$(catkin_find clover examples --first-only)" /home/pi
|
||||||
chown -Rf pi:pi /home/pi/examples
|
chown -Rf pi:pi /home/pi/examples
|
||||||
|
|
||||||
|
echo_stamp "Make systemd services symlinks"
|
||||||
|
ln -s /home/pi/catkin_ws/src/clover/builder/assets/clover.service /lib/systemd/system/
|
||||||
|
ln -s /home/pi/catkin_ws/src/clover/builder/assets/roscore.service /lib/systemd/system/
|
||||||
|
# validate
|
||||||
|
[ -f /lib/systemd/system/clover.service ]
|
||||||
|
[ -f /lib/systemd/system/roscore.service ]
|
||||||
|
|
||||||
|
echo_stamp "Make udev rules symlink"
|
||||||
|
ln -s "$(catkin_find clover udev --first-only)"/* /lib/udev/rules.d/
|
||||||
|
|
||||||
echo_stamp "Setup ROS environment"
|
echo_stamp "Setup ROS environment"
|
||||||
cat << EOF >> /home/pi/.bashrc
|
cat << EOF >> /home/pi/.bashrc
|
||||||
LANG='C.UTF-8'
|
LANG='C.UTF-8'
|
||||||
LC_ALL='C.UTF-8'
|
LC_ALL='C.UTF-8'
|
||||||
export ROS_HOSTNAME=\`hostname\`.local
|
export ROS_HOSTNAME=\`hostname\`.local
|
||||||
source /opt/ros/melodic/setup.bash
|
source /opt/ros/${ROS_DISTRO}/setup.bash
|
||||||
source /home/pi/catkin_ws/devel/setup.bash
|
source /home/pi/catkin_ws/devel/setup.bash
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
|
|||||||
@@ -64,15 +64,14 @@ echo "APT::Acquire::Retries \"3\";" > /etc/apt/apt.conf.d/80-retries
|
|||||||
echo_stamp "Install apt keys & repos"
|
echo_stamp "Install apt keys & repos"
|
||||||
|
|
||||||
# TODO: This STDOUT consist 'OK'
|
# TODO: This STDOUT consist 'OK'
|
||||||
curl http://deb.coex.tech/aptly_repo_signing.key 2> /dev/null | apt-key add -
|
|
||||||
apt-get update \
|
apt-get update \
|
||||||
&& apt-get install --no-install-recommends -y dirmngr > /dev/null \
|
&& apt-get install --no-install-recommends -y dirmngr > /dev/null \
|
||||||
&& apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-key C1CF6E31E6BADE8868B172B4F42ED6FBAB17C654
|
&& apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-key C1CF6E31E6BADE8868B172B4F42ED6FBAB17C654
|
||||||
|
|
||||||
echo "deb http://packages.ros.org/ros/ubuntu buster main" > /etc/apt/sources.list.d/ros-latest.list
|
echo "deb http://packages.ros.org/ros/ubuntu buster main" > /etc/apt/sources.list.d/ros-latest.list
|
||||||
echo "deb http://deb.coex.tech/opencv3 buster main" > /etc/apt/sources.list.d/opencv3.list
|
|
||||||
echo "deb http://deb.coex.tech/rpi-ros-melodic buster main" > /etc/apt/sources.list.d/rpi-ros-melodic.list
|
wget -O - 'http://packages.coex.tech/key.asc' | apt-key add -
|
||||||
echo "deb http://deb.coex.tech/clover buster main" > /etc/apt/sources.list.d/clover.list
|
echo 'deb http://packages.coex.tech buster main' >> /etc/apt/sources.list
|
||||||
|
|
||||||
echo_stamp "Update apt cache"
|
echo_stamp "Update apt cache"
|
||||||
|
|
||||||
@@ -99,18 +98,18 @@ tree \
|
|||||||
vim \
|
vim \
|
||||||
libjpeg8 \
|
libjpeg8 \
|
||||||
tcpdump \
|
tcpdump \
|
||||||
ltrace \
|
|
||||||
libpoco-dev \
|
libpoco-dev \
|
||||||
libzbar0 \
|
libzbar0 \
|
||||||
python-rosdep \
|
python3-rosdep \
|
||||||
python-rosinstall-generator \
|
python3-rosinstall-generator \
|
||||||
python-wstool \
|
python3-wstool \
|
||||||
python-rosinstall \
|
python3-rosinstall \
|
||||||
build-essential \
|
build-essential \
|
||||||
libffi-dev \
|
libffi-dev \
|
||||||
monkey \
|
monkey \
|
||||||
pigpio python-pigpio python3-pigpio \
|
pigpio python-pigpio python3-pigpio \
|
||||||
i2c-tools \
|
i2c-tools \
|
||||||
|
espeak espeak-data python-espeak python3-espeak \
|
||||||
ntpdate \
|
ntpdate \
|
||||||
python-dev \
|
python-dev \
|
||||||
python3-dev \
|
python3-dev \
|
||||||
@@ -144,7 +143,7 @@ my_travis_retry pip3 install butterfly[systemd]
|
|||||||
systemctl enable butterfly.socket
|
systemctl enable butterfly.socket
|
||||||
|
|
||||||
echo_stamp "Install ws281x library"
|
echo_stamp "Install ws281x library"
|
||||||
my_travis_retry pip install --prefer-binary rpi_ws281x
|
my_travis_retry pip3 install --prefer-binary rpi_ws281x
|
||||||
|
|
||||||
echo_stamp "Setup Monkey"
|
echo_stamp "Setup Monkey"
|
||||||
mv /etc/monkey/sites/default /etc/monkey/sites/default.orig
|
mv /etc/monkey/sites/default /etc/monkey/sites/default.orig
|
||||||
@@ -154,7 +153,7 @@ systemctl enable monkey.service
|
|||||||
|
|
||||||
echo_stamp "Install Node.js"
|
echo_stamp "Install Node.js"
|
||||||
cd /home/pi
|
cd /home/pi
|
||||||
wget https://nodejs.org/dist/v10.15.0/node-v10.15.0-linux-armv6l.tar.gz
|
wget --no-verbose https://nodejs.org/dist/v10.15.0/node-v10.15.0-linux-armv6l.tar.gz
|
||||||
tar -xzf node-v10.15.0-linux-armv6l.tar.gz
|
tar -xzf node-v10.15.0-linux-armv6l.tar.gz
|
||||||
cp -R node-v10.15.0-linux-armv6l/* /usr/local/
|
cp -R node-v10.15.0-linux-armv6l/* /usr/local/
|
||||||
rm -rf node-v10.15.0-linux-armv6l/
|
rm -rf node-v10.15.0-linux-armv6l/
|
||||||
|
|||||||
@@ -16,16 +16,24 @@ set -ex
|
|||||||
|
|
||||||
echo "Run image tests"
|
echo "Run image tests"
|
||||||
|
|
||||||
export ROS_DISTRO='melodic'
|
export ROS_DISTRO='noetic'
|
||||||
export ROS_IP='127.0.0.1'
|
export ROS_IP='127.0.0.1'
|
||||||
source /opt/ros/melodic/setup.bash
|
source /opt/ros/${ROS_DISTRO}/setup.bash
|
||||||
source /home/pi/catkin_ws/devel/setup.bash
|
source /home/pi/catkin_ws/devel/setup.bash
|
||||||
|
systemctl start roscore
|
||||||
|
|
||||||
cd /home/pi/catkin_ws/src/clover/builder/test/
|
cd /home/pi/catkin_ws/src/clover/builder/test/
|
||||||
./tests.sh
|
./tests.sh
|
||||||
./tests.py
|
./tests.py
|
||||||
./tests_py3.py
|
./tests_py3.py
|
||||||
|
[[ $(./test_qr.py) == "Found QRCODE with data Проверка Unicode with center at x=66.0, y=66.0" ]]
|
||||||
[[ $(./tests_clever.py) == "Warning: clever package is renamed to clover" ]] # test backwards compatibility
|
[[ $(./tests_clever.py) == "Warning: clever package is renamed to clover" ]] # test backwards compatibility
|
||||||
|
|
||||||
|
systemctl stop roscore
|
||||||
|
|
||||||
|
# check documented packages available
|
||||||
|
apt-cache show gst-rtsp-launch
|
||||||
|
apt-cache show openvpn
|
||||||
|
|
||||||
echo "Move /etc/ld.so.preload back to its original position"
|
echo "Move /etc/ld.so.preload back to its original position"
|
||||||
mv /etc/ld.so.preload.disabled-for-build /etc/ld.so.preload
|
mv /etc/ld.so.preload.disabled-for-build /etc/ld.so.preload
|
||||||
|
|||||||
@@ -3,25 +3,45 @@
|
|||||||
# Perform a "standalone install" in a Docker container
|
# Perform a "standalone install" in a Docker container
|
||||||
set -e
|
set -e
|
||||||
# Step 1: Install pip
|
# Step 1: Install pip
|
||||||
apt update
|
apt-key adv --keyserver 'hkp://keyserver.ubuntu.com:80' --recv-key C1CF6E31E6BADE8868B172B4F42ED6FBAB17C654 # https://github.com/osrf/docker_images/issues/535
|
||||||
apt install -y curl
|
apt-get update
|
||||||
|
apt-get install -y curl
|
||||||
|
if [ "x${ROS_PYTHON_VERSION}" = "x3" ]; then
|
||||||
|
PYTHON=python3
|
||||||
|
curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py
|
||||||
|
else
|
||||||
|
PYTHON=python
|
||||||
curl https://bootstrap.pypa.io/pip/2.7/get-pip.py -o get-pip.py
|
curl https://bootstrap.pypa.io/pip/2.7/get-pip.py -o get-pip.py
|
||||||
python ./get-pip.py
|
fi
|
||||||
|
${PYTHON} ./get-pip.py
|
||||||
|
|
||||||
# Step 1.5: Add deb.coex.tech to apt
|
# Step 1.5: Add deb.coex.tech to apt
|
||||||
curl http://deb.coex.tech/aptly_repo_signing.key 2> /dev/null | apt-key add -
|
curl http://deb.coex.tech/aptly_repo_signing.key 2> /dev/null | apt-key add -
|
||||||
echo "deb http://deb.coex.tech/ros xenial main" > /etc/apt/sources.list.d/coex.tech.list
|
echo "deb http://deb.coex.tech/ros xenial main" > /etc/apt/sources.list.d/coex.tech.list
|
||||||
echo "yaml file:///etc/ros/rosdep/coex.yaml" > /etc/ros/rosdep/sources.list.d/99-coex.list
|
echo "yaml file:///etc/ros/rosdep/coex.yaml" > /etc/ros/rosdep/sources.list.d/99-coex.list
|
||||||
|
CODENAME=$(lsb_release -sc)
|
||||||
|
|
||||||
cat <<EOF > /etc/ros/rosdep/coex.yaml
|
cat <<EOF > /etc/ros/rosdep/coex.yaml
|
||||||
led_msgs:
|
led_msgs:
|
||||||
ubuntu:
|
ubuntu:
|
||||||
xenial: ros-kinetic-led-msgs
|
${CODENAME}: [ros-${ROS_DISTRO}-led-msgs]
|
||||||
bionic: ros-melodic-led-msgs
|
async_web_server_cpp:
|
||||||
debian:
|
ubuntu:
|
||||||
stretch: ros-kinetic-led-msgs
|
${CODENAME}: [ros-${ROS_DISTRO}-async-web-server-cpp]
|
||||||
buster: ros-melodic-led-msgs
|
ros_pytest:
|
||||||
|
ubuntu:
|
||||||
|
${CODENAME}: [ros-${ROS_DISTRO}-ros-pytest]
|
||||||
|
tf2_web_republisher:
|
||||||
|
ubuntu:
|
||||||
|
${CODENAME}: [ros-${ROS_DISTRO}-tf2-web-republisher]
|
||||||
|
web_video_server:
|
||||||
|
ubuntu:
|
||||||
|
${CODENAME}: [ros-${ROS_DISTRO}-web-video-server]
|
||||||
|
ws281x:
|
||||||
|
ubuntu:
|
||||||
|
${CODENAME}: [ros-${ROS_DISTRO}-ws281x]
|
||||||
EOF
|
EOF
|
||||||
apt update
|
apt-get update
|
||||||
rosdep update
|
rosdep update
|
||||||
|
|
||||||
# Step 2: Run rosdep to install all dependencies
|
# Step 2: Run rosdep to install all dependencies
|
||||||
@@ -37,7 +57,7 @@ cd /root/catkin_ws
|
|||||||
catkin_make
|
catkin_make
|
||||||
|
|
||||||
# Step 4: Run tests
|
# Step 4: Run tests
|
||||||
pip install --upgrade pytest
|
${PYTHON} -m pip install --upgrade pytest
|
||||||
cd /root/catkin_ws
|
cd /root/catkin_ws
|
||||||
source devel/setup.bash
|
source devel/setup.bash
|
||||||
catkin_make run_tests && catkin_test_results
|
catkin_make run_tests && catkin_test_results
|
||||||
|
|||||||
BIN
builder/test/qr.png
Normal file
BIN
builder/test/qr.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.8 KiB |
42
builder/test/test_qr.py
Executable file
42
builder/test/test_qr.py
Executable file
@@ -0,0 +1,42 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
# Test QG recognition example
|
||||||
|
# Should be synced with the documentation: /docs/en/camera.md, /docs/ru/camera.md
|
||||||
|
# TODO: use real ROS topics
|
||||||
|
|
||||||
|
import rospy
|
||||||
|
from pyzbar import pyzbar
|
||||||
|
from cv_bridge import CvBridge
|
||||||
|
from sensor_msgs.msg import Image
|
||||||
|
|
||||||
|
bridge = CvBridge()
|
||||||
|
|
||||||
|
# rospy.init_node('barcode_test')
|
||||||
|
|
||||||
|
# Image subscriber callback function
|
||||||
|
def image_callback(data):
|
||||||
|
cv_image = bridge.imgmsg_to_cv2(data, 'bgr8') # OpenCV image
|
||||||
|
barcodes = pyzbar.decode(cv_image)
|
||||||
|
for barcode in barcodes:
|
||||||
|
b_data = barcode.data.decode("utf-8")
|
||||||
|
b_type = barcode.type
|
||||||
|
(x, y, w, h) = barcode.rect
|
||||||
|
xc = x + w/2
|
||||||
|
yc = y + h/2
|
||||||
|
print("Found {} with data {} with center at x={}, y={}".format(b_type, b_data, xc, yc))
|
||||||
|
# rospy.signal_shutdown('done')
|
||||||
|
|
||||||
|
# image_sub = rospy.Subscriber('main_camera/image_raw', Image, image_callback, queue_size=1)
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
# Publish test image
|
||||||
|
# rospy.sleep(2)
|
||||||
|
|
||||||
|
import cv2
|
||||||
|
img = cv2.imread('qr.png')
|
||||||
|
image_callback(bridge.cv2_to_imgmsg(img, 'bgr8'))
|
||||||
|
|
||||||
|
# image_pub = rospy.Publisher('/main_camera/image_raw', Image, queue_size=1, latch=True)
|
||||||
|
# image_pub.publish(bridge.cv2_to_imgmsg(img, 'bgr8'))
|
||||||
|
|
||||||
|
# rospy.spin()
|
||||||
@@ -1,21 +1,29 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
# validate all required modules installed
|
# validate all required modules installed
|
||||||
|
|
||||||
import rospy
|
import rospy
|
||||||
from geometry_msgs.msg import PoseStamped
|
from geometry_msgs.msg import PoseStamped
|
||||||
|
from sensor_msgs.msg import Range, BatteryState
|
||||||
|
|
||||||
import cv2
|
import cv2
|
||||||
import cv2.aruco
|
import cv2.aruco
|
||||||
|
from sensor_msgs.msg import Image
|
||||||
|
from cv_bridge import CvBridge
|
||||||
|
|
||||||
import numpy
|
import numpy
|
||||||
import mavros
|
import mavros
|
||||||
from mavros_msgs.msg import State, StatusText, ExtendedState
|
from mavros_msgs.msg import State, StatusText, ExtendedState, RCIn, Mavlink
|
||||||
from mavros_msgs.srv import CommandBool, CommandLong, SetMode
|
from mavros_msgs.srv import CommandBool, CommandLong, SetMode
|
||||||
|
|
||||||
from std_srvs.srv import Trigger
|
from std_srvs.srv import Trigger
|
||||||
from clover.srv import GetTelemetry, Navigate, NavigateGlobal, SetPosition, SetVelocity, \
|
from clover.srv import GetTelemetry, Navigate, NavigateGlobal, SetPosition, SetVelocity, \
|
||||||
SetAttitude, SetRates, SetLEDEffect
|
SetAttitude, SetRates, SetLEDEffect
|
||||||
|
from led_msgs.srv import SetLEDs
|
||||||
|
from led_msgs.msg import LEDStateArray, LEDState
|
||||||
|
from aruco_pose.msg import Marker, MarkerArray, Point2D
|
||||||
|
|
||||||
|
import dynamic_reconfigure.client
|
||||||
|
|
||||||
import tf2_ros
|
import tf2_ros
|
||||||
import tf2_geometry_msgs
|
import tf2_geometry_msgs
|
||||||
@@ -28,4 +36,4 @@ import pigpio
|
|||||||
# from espeak import espeak
|
# from espeak import espeak
|
||||||
from pyzbar import pyzbar
|
from pyzbar import pyzbar
|
||||||
|
|
||||||
print cv2.getBuildInformation()
|
print(cv2.getBuildInformation())
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ i2cdetect -V
|
|||||||
butterfly -h
|
butterfly -h
|
||||||
# espeak --version
|
# espeak --version
|
||||||
mjpg_streamer --version
|
mjpg_streamer --version
|
||||||
|
systemctl --version
|
||||||
|
|
||||||
# ros stuff
|
# ros stuff
|
||||||
|
|
||||||
@@ -54,6 +55,8 @@ rosversion usb_cam
|
|||||||
rosversion cv_camera
|
rosversion cv_camera
|
||||||
rosversion web_video_server
|
rosversion web_video_server
|
||||||
rosversion rosshow
|
rosversion rosshow
|
||||||
|
rosversion nodelet
|
||||||
|
rosversion image_view
|
||||||
|
|
||||||
# validate examples are present
|
# validate examples are present
|
||||||
[[ $(ls /home/pi/examples/*) ]]
|
[[ $(ls /home/pi/examples/*) ]]
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
# test backwards compatibility
|
# test backwards compatibility
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import sys
|
|||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
EXCLUDE = ('clever4-front-white.png', 'clever4-front-white-large.png', '.DS_Store',
|
EXCLUDE = ('clever4-front-white.png', 'clever4-front-white-large.png', '.DS_Store',
|
||||||
'clever4-front-black-large.png','clover42-black.png')
|
'clever4-front-black-large.png','clover42-black.png', 'clover42-main-margin.png')
|
||||||
|
|
||||||
code = 0
|
code = 0
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
cmake_minimum_required(VERSION 2.8.3)
|
cmake_minimum_required(VERSION 3.0)
|
||||||
project(clover)
|
project(clover)
|
||||||
|
|
||||||
## Compile as C++11, supported in ROS Kinetic and newer
|
## Compile as C++11, supported in ROS Kinetic and newer
|
||||||
@@ -24,14 +24,21 @@ find_package(catkin REQUIRED COMPONENTS
|
|||||||
tf2_ros
|
tf2_ros
|
||||||
image_transport
|
image_transport
|
||||||
cv_bridge
|
cv_bridge
|
||||||
catkin_virtualenv
|
|
||||||
)
|
)
|
||||||
|
|
||||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake")
|
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake")
|
||||||
|
|
||||||
find_package(GeographicLib REQUIRED)
|
find_package(GeographicLib REQUIRED)
|
||||||
|
|
||||||
find_package(OpenCV 3 REQUIRED
|
# Workaround for OpenCV 3/4 support
|
||||||
|
set(_opencv_version 4)
|
||||||
|
find_package(OpenCV ${_opencv_version} QUIET COMPONENTS calib3d imgproc)
|
||||||
|
if (NOT OpenCV_FOUND)
|
||||||
|
message(STATUS "Did not find OpenCV 4, searching for OpenCV 3")
|
||||||
|
set(_opencv_version 3)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
find_package(OpenCV ${_opencv_version} REQUIRED
|
||||||
COMPONENTS
|
COMPONENTS
|
||||||
calib3d
|
calib3d
|
||||||
imgproc
|
imgproc
|
||||||
@@ -133,16 +140,6 @@ generate_messages(
|
|||||||
## LIBRARIES: libraries you create in this project that dependent projects also need
|
## LIBRARIES: libraries you create in this project that dependent projects also need
|
||||||
## CATKIN_DEPENDS: catkin_packages dependent projects also need
|
## CATKIN_DEPENDS: catkin_packages dependent projects also need
|
||||||
## DEPENDS: system dependencies of this project that dependent projects also need
|
## DEPENDS: system dependencies of this project that dependent projects also need
|
||||||
|
|
||||||
# Generate the virtualenv
|
|
||||||
catkin_generate_virtualenv(INPUT_REQUIREMENTS requirements.in)
|
|
||||||
|
|
||||||
# Make sure your python executables are installed using `catkin_install_python`:
|
|
||||||
catkin_install_python(
|
|
||||||
PROGRAMS
|
|
||||||
src/selfcheck.py
|
|
||||||
DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION})
|
|
||||||
|
|
||||||
catkin_package(
|
catkin_package(
|
||||||
# INCLUDE_DIRS include
|
# INCLUDE_DIRS include
|
||||||
LIBRARIES ${PROJECT_NAME}
|
LIBRARIES ${PROJECT_NAME}
|
||||||
@@ -244,12 +241,12 @@ target_link_libraries(${PROJECT_NAME}
|
|||||||
# DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
|
# DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
|
||||||
# )
|
# )
|
||||||
|
|
||||||
## Mark executables and/or libraries for installation
|
# Mark executables and/or libraries for installation
|
||||||
# install(TARGETS ${PROJECT_NAME} ${PROJECT_NAME}_node
|
install(TARGETS simple_offboard clover_rc camera_markers vpe_publisher clover_led shell clover
|
||||||
# ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
|
ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
|
||||||
# LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
|
LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
|
||||||
# RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
|
RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
|
||||||
# )
|
)
|
||||||
|
|
||||||
## Mark cpp header files for installation
|
## Mark cpp header files for installation
|
||||||
# install(DIRECTORY include/${PROJECT_NAME}/
|
# install(DIRECTORY include/${PROJECT_NAME}/
|
||||||
@@ -259,8 +256,23 @@ target_link_libraries(${PROJECT_NAME}
|
|||||||
# )
|
# )
|
||||||
|
|
||||||
## Mark other files for installation (e.g. launch and bag files, etc.)
|
## Mark other files for installation (e.g. launch and bag files, etc.)
|
||||||
install(FILES requirements.in
|
# install(FILES
|
||||||
DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION})
|
# # myfile1
|
||||||
|
# # myfile2
|
||||||
|
# DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}
|
||||||
|
# )
|
||||||
|
|
||||||
|
catkin_install_python(PROGRAMS src/selfcheck.py
|
||||||
|
DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
|
||||||
|
)
|
||||||
|
|
||||||
|
install(DIRECTORY launch DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION})
|
||||||
|
|
||||||
|
install(DIRECTORY examples DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION})
|
||||||
|
|
||||||
|
install(DIRECTORY www DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION})
|
||||||
|
|
||||||
|
# TODO: install www
|
||||||
|
|
||||||
# Only install udev rules when building a Debian package
|
# Only install udev rules when building a Debian package
|
||||||
# FIXME: Other operating systems may have other prefixes
|
# FIXME: Other operating systems may have other prefixes
|
||||||
@@ -268,7 +280,7 @@ string(FIND ${CMAKE_INSTALL_PREFIX} "/opt/ros" _PREFIX_INDEX)
|
|||||||
if (${_PREFIX_INDEX} EQUAL 0)
|
if (${_PREFIX_INDEX} EQUAL 0)
|
||||||
message(STATUS "Building as a Debian package - adding udev rules as installable files")
|
message(STATUS "Building as a Debian package - adding udev rules as installable files")
|
||||||
install(FILES
|
install(FILES
|
||||||
config/99-px4fmu.rules
|
udev/99-px4fmu.rules
|
||||||
DESTINATION /lib/udev/rules.d
|
DESTINATION /lib/udev/rules.d
|
||||||
)
|
)
|
||||||
else()
|
else()
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ A bundle for autonomous navigation and drone control.
|
|||||||
|
|
||||||
## Manual installation
|
## Manual installation
|
||||||
|
|
||||||
Install ROS Melodic according to the [documentation](http://wiki.ros.org/melodic/Installation), then [create a Catkin workspace](http://wiki.ros.org/catkin/Tutorials/create_a_workspace).
|
Install ROS Noetic according to the [documentation](http://wiki.ros.org/noetic/Installation), then [create a Catkin workspace](http://wiki.ros.org/catkin/Tutorials/create_a_workspace).
|
||||||
|
|
||||||
Clone this repo to directory `~/catkin_ws/src/clover`:
|
Clone this repo to directory `~/catkin_ws/src/clover`:
|
||||||
|
|
||||||
@@ -36,7 +36,7 @@ curl https://raw.githubusercontent.com/mavlink/mavros/master/mavros/scripts/inst
|
|||||||
You may optionally install udev rules to provide `/dev/px4fmu` symlink to your PX4-based flight controller connected over USB. Copy `99-px4fmu.rules` to your `/lib/udev/rules.d` folder:
|
You may optionally install udev rules to provide `/dev/px4fmu` symlink to your PX4-based flight controller connected over USB. Copy `99-px4fmu.rules` to your `/lib/udev/rules.d` folder:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd ~/catkin_ws/src/clover/clover/config
|
cd ~/catkin_ws/src/clover/clover/udev
|
||||||
sudo cp 99-px4fmu.rules /lib/udev/rules.d
|
sudo cp 99-px4fmu.rules /lib/udev/rules.d
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -15,17 +15,17 @@ set_attitude = rospy.ServiceProxy('set_attitude', srv.SetAttitude)
|
|||||||
set_rates = rospy.ServiceProxy('set_rates', srv.SetRates)
|
set_rates = rospy.ServiceProxy('set_rates', srv.SetRates)
|
||||||
land = rospy.ServiceProxy('land', Trigger)
|
land = rospy.ServiceProxy('land', Trigger)
|
||||||
|
|
||||||
# Take off and hover 1 m above the ground
|
print('Take off and hover 1 m above the ground')
|
||||||
navigate(x=0, y=0, z=1, frame_id='body', auto_arm=True)
|
navigate(x=0, y=0, z=1, frame_id='body', auto_arm=True)
|
||||||
|
|
||||||
# Wait for 3 seconds
|
# Wait for 5 seconds
|
||||||
rospy.sleep(3)
|
rospy.sleep(5)
|
||||||
|
|
||||||
# Fly forward 1 m
|
print('Fly forward 1 m')
|
||||||
navigate(x=1, y=0, z=0, frame_id='body')
|
navigate(x=1, y=0, z=0, frame_id='body')
|
||||||
|
|
||||||
# Wait for 3 seconds
|
# Wait for 5 seconds
|
||||||
rospy.sleep(3)
|
rospy.sleep(5)
|
||||||
|
|
||||||
# Perform landing
|
print('Perform landing')
|
||||||
land()
|
land()
|
||||||
@@ -15,23 +15,23 @@ set_attitude = rospy.ServiceProxy('set_attitude', srv.SetAttitude)
|
|||||||
set_rates = rospy.ServiceProxy('set_rates', srv.SetRates)
|
set_rates = rospy.ServiceProxy('set_rates', srv.SetRates)
|
||||||
land = rospy.ServiceProxy('land', Trigger)
|
land = rospy.ServiceProxy('land', Trigger)
|
||||||
|
|
||||||
# Take off and hover 1 m above the ground
|
print('Take off and hover 1 m above the ground')
|
||||||
navigate(x=0, y=0, z=1, frame_id='body', auto_arm=True)
|
navigate(x=0, y=0, z=1, frame_id='body', auto_arm=True)
|
||||||
|
|
||||||
# Wait for 3 seconds
|
# Wait for 5 seconds
|
||||||
rospy.sleep(3)
|
rospy.sleep(5)
|
||||||
|
|
||||||
# Fly 1 meter above ArUco marker 0
|
print('Fly 1 meter above ArUco marker 0')
|
||||||
navigate(x=0, y=0, z=1, frame_id='aruco_0')
|
navigate(x=0, y=0, z=1, frame_id='aruco_0')
|
||||||
|
|
||||||
# Wait for 3 seconds
|
# Wait for 5 seconds
|
||||||
rospy.sleep(3)
|
rospy.sleep(5)
|
||||||
|
|
||||||
# Fly to x=1 y=1 z=1 relative to ArUco markers map
|
print('Fly to x=1 y=1 z=1 relative to ArUco markers map')
|
||||||
navigate(x=1, y=1, z=1, frame_id='aruco_map')
|
navigate(x=1, y=1, z=1, frame_id='aruco_map')
|
||||||
|
|
||||||
# Wait for 3 seconds
|
# Wait for 5 seconds
|
||||||
rospy.sleep(3)
|
rospy.sleep(5)
|
||||||
|
|
||||||
# Perform landing
|
print('Perform landing')
|
||||||
land()
|
land()
|
||||||
47
clover/examples/gps.py
Normal file
47
clover/examples/gps.py
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
# Information: https://clover.coex.tech/en/simple_offboard.html#navigateglobal
|
||||||
|
|
||||||
|
import rospy
|
||||||
|
from clover import srv
|
||||||
|
from std_srvs.srv import Trigger
|
||||||
|
import math
|
||||||
|
|
||||||
|
rospy.init_node('flight')
|
||||||
|
|
||||||
|
get_telemetry = rospy.ServiceProxy('get_telemetry', srv.GetTelemetry)
|
||||||
|
navigate = rospy.ServiceProxy('navigate', srv.Navigate)
|
||||||
|
navigate_global = rospy.ServiceProxy('navigate_global', srv.NavigateGlobal)
|
||||||
|
set_position = rospy.ServiceProxy('set_position', srv.SetPosition)
|
||||||
|
set_velocity = rospy.ServiceProxy('set_velocity', srv.SetVelocity)
|
||||||
|
set_attitude = rospy.ServiceProxy('set_attitude', srv.SetAttitude)
|
||||||
|
set_rates = rospy.ServiceProxy('set_rates', srv.SetRates)
|
||||||
|
land = rospy.ServiceProxy('land', Trigger)
|
||||||
|
|
||||||
|
# https://clover.coex.tech/en/snippets.html#wait_arrival
|
||||||
|
def wait_arrival(tolerance=0.2):
|
||||||
|
while not rospy.is_shutdown():
|
||||||
|
telem = get_telemetry(frame_id='navigate_target')
|
||||||
|
if math.sqrt(telem.x ** 2 + telem.y ** 2 + telem.z ** 2) < tolerance:
|
||||||
|
break
|
||||||
|
rospy.sleep(0.2)
|
||||||
|
|
||||||
|
start = get_telemetry()
|
||||||
|
|
||||||
|
if math.isnan(start.lat):
|
||||||
|
raise Exception('No global position, install and configure GPS sensor: https://clover.coex.tech/gps')
|
||||||
|
|
||||||
|
print('Start point global position: lat={}, lon={}'.format(start.lat, start.lon))
|
||||||
|
|
||||||
|
print('Take off 3 meters')
|
||||||
|
navigate(x=0, y=0, z=3, frame_id='body', auto_arm=True)
|
||||||
|
wait_arrival()
|
||||||
|
|
||||||
|
print('Fly 1 arcsecond to the North (approx. 30 meters)')
|
||||||
|
navigate_global(lat=start.lat+1.0/60/60, lon=start.lon, z=start.z+3, yaw=math.inf, speed=5)
|
||||||
|
wait_arrival()
|
||||||
|
|
||||||
|
print('Fly to home position')
|
||||||
|
navigate_global(lat=start.lat, lon=start.lon, z=start.z+3, yaw=math.inf, speed=5)
|
||||||
|
wait_arrival()
|
||||||
|
|
||||||
|
print('Land')
|
||||||
|
land()
|
||||||
@@ -31,11 +31,11 @@ def navigate_wait(x=0, y=0, z=0, yaw=float('nan'), yaw_rate=0, speed=0.5, \
|
|||||||
return res
|
return res
|
||||||
rospy.sleep(0.2)
|
rospy.sleep(0.2)
|
||||||
|
|
||||||
# Take off 1 meter
|
print('Take off 1 meter')
|
||||||
navigate_wait(z=1, frame_id='body', auto_arm=True)
|
navigate_wait(z=1, frame_id='body', auto_arm=True)
|
||||||
|
|
||||||
# Fly forward 1 m
|
print('Fly forward 1 m')
|
||||||
navigate_wait(x=1, frame_id='body')
|
navigate_wait(x=1, frame_id='body')
|
||||||
|
|
||||||
# Land
|
print('Land')
|
||||||
land()
|
land()
|
||||||
15
clover/examples/subscriber.py
Normal file
15
clover/examples/subscriber.py
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
# Information: https://clover.coex.tech/en/laser.html
|
||||||
|
|
||||||
|
import rospy
|
||||||
|
from sensor_msgs.msg import Range
|
||||||
|
|
||||||
|
rospy.init_node('process_rangefinder')
|
||||||
|
|
||||||
|
def range_callback(msg):
|
||||||
|
# Process data from the rangefinder
|
||||||
|
print('Rangefinder distance:', msg.range)
|
||||||
|
|
||||||
|
# Subscribe to laser rangefinder data
|
||||||
|
rospy.Subscriber('rangefinder/range', Range, range_callback)
|
||||||
|
|
||||||
|
rospy.spin()
|
||||||
@@ -2,30 +2,37 @@
|
|||||||
<arg name="aruco_detect" default="true"/>
|
<arg name="aruco_detect" default="true"/>
|
||||||
<arg name="aruco_map" default="false"/>
|
<arg name="aruco_map" default="false"/>
|
||||||
<arg name="aruco_vpe" default="false"/>
|
<arg name="aruco_vpe" default="false"/>
|
||||||
|
<arg name="placement" default="floor"/> <!-- markers placement: floor, ceiling, unknown -->
|
||||||
|
<arg name="length" default="0.22"/> <!-- not-in-map markers length, m -->
|
||||||
|
<arg name="map" default="map.txt"/> <!-- markers map file name -->
|
||||||
|
|
||||||
<!-- For additional help go to https://clover.coex.tech/aruco -->
|
<!-- For additional help go to https://clover.coex.tech/aruco -->
|
||||||
|
|
||||||
<!-- 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 nodelet_manager" output="screen" clear_params="true">
|
<node name="aruco_detect" pkg="nodelet" if="$(arg aruco_detect)" type="nodelet" args="load aruco_pose/aruco_detect main_camera_nodelet_manager" output="screen" clear_params="true" respawn="true">
|
||||||
<remap from="image_raw" to="main_camera/image_raw"/>
|
<remap from="image_raw" to="main_camera/image_raw"/>
|
||||||
<remap from="camera_info" to="main_camera/camera_info"/>
|
<remap from="camera_info" to="main_camera/camera_info"/>
|
||||||
<remap from="map_markers" to="aruco_map/markers" if="$(arg aruco_map)"/>
|
<remap from="map_markers" to="aruco_map/markers"/>
|
||||||
<param name="estimate_poses" value="true"/>
|
<param name="estimate_poses" value="true"/>
|
||||||
<param name="send_tf" value="true"/>
|
<param name="send_tf" value="true"/>
|
||||||
<param name="known_tilt" value="map"/>
|
<param name="known_tilt" value="map" if="$(eval placement == 'floor')"/>
|
||||||
<param name="length" value="0.33"/>
|
<param name="known_tilt" value="map_flipped" if="$(eval placement == 'ceiling')"/>
|
||||||
|
<param name="length" value="$(arg length)"/>
|
||||||
<!-- aruco detector parameters -->
|
<!-- aruco detector parameters -->
|
||||||
<param name="cornerRefinementMethod" value="2"/> <!-- contour refinement -->
|
<param name="cornerRefinementMethod" value="2"/> <!-- contour refinement -->
|
||||||
<param name="minMarkerPerimeterRate" value="0.075"/> <!-- 0.075 for 320x240, 0.0375 for 640x480 -->
|
<param name="minMarkerPerimeterRate" value="0.075"/> <!-- 0.075 for 320x240, 0.0375 for 640x480 -->
|
||||||
|
<!-- length override example: -->
|
||||||
|
<!-- <param name="length_override/3" value="0.1"/> -->
|
||||||
</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 nodelet_manager" output="screen" clear_params="true">
|
<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">
|
||||||
<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"/>
|
||||||
<param name="map" value="$(find aruco_pose)/map/map.txt"/>
|
<param name="map" value="$(find aruco_pose)/map/$(arg map)"/>
|
||||||
<param name="known_tilt" value="map"/>
|
<param name="known_tilt" value="map" if="$(eval placement == 'floor')"/>
|
||||||
|
<param name="known_tilt" value="map_flipped" if="$(eval placement == 'ceiling')"/>
|
||||||
<param name="image_axis" value="true"/>
|
<param name="image_axis" value="true"/>
|
||||||
<param name="frame_id" value="aruco_map_detected" if="$(arg aruco_vpe)"/>
|
<param name="frame_id" value="aruco_map_detected" if="$(arg aruco_vpe)"/>
|
||||||
<param name="frame_id" value="aruco_map" unless="$(arg aruco_vpe)"/>
|
<param name="frame_id" value="aruco_map" unless="$(arg aruco_vpe)"/>
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
<arg name="rangefinder_vl53l1x" default="true"/>
|
<arg name="rangefinder_vl53l1x" default="true"/>
|
||||||
<arg name="led" default="true"/>
|
<arg name="led" default="true"/>
|
||||||
<arg name="blocks" default="false"/>
|
<arg name="blocks" default="false"/>
|
||||||
<arg name="rc" default="true"/>
|
<arg name="rc" default="false"/>
|
||||||
|
|
||||||
<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 -->
|
||||||
|
|
||||||
@@ -36,18 +36,13 @@
|
|||||||
<include file="$(find clover)/launch/aruco.launch" if="$(arg aruco)"/>
|
<include file="$(find clover)/launch/aruco.launch" if="$(arg aruco)"/>
|
||||||
|
|
||||||
<!-- optical flow -->
|
<!-- optical flow -->
|
||||||
<node pkg="nodelet" type="nodelet" name="optical_flow" args="load clover/optical_flow nodelet_manager" if="$(arg optical_flow)" clear_params="true" output="screen">
|
<node pkg="nodelet" type="nodelet" name="optical_flow" args="load clover/optical_flow main_camera_nodelet_manager" if="$(arg optical_flow)" clear_params="true" output="screen" respawn="true">
|
||||||
<remap from="image_raw" to="main_camera/image_raw"/>
|
<remap from="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"/>
|
||||||
<param name="calc_flow_gyro" value="true"/>
|
<param name="calc_flow_gyro" value="true"/>
|
||||||
<param name="roi_rad" value="0.8"/>
|
<param name="roi_rad" value="0.8"/>
|
||||||
</node>
|
</node>
|
||||||
|
|
||||||
<!-- main nodelet manager -->
|
|
||||||
<node pkg="nodelet" type="nodelet" name="nodelet_manager" args="manager" output="screen" clear_params="true">
|
|
||||||
<param name="num_worker_threads" value="2"/>
|
|
||||||
</node>
|
|
||||||
|
|
||||||
<node pkg="tf2_ros" type="static_transform_publisher" name="map_flipped_frame" args="0 0 0 3.1415926 3.1415926 0 map map_flipped"/>
|
<node pkg="tf2_ros" type="static_transform_publisher" name="map_flipped_frame" args="0 0 0 3.1415926 3.1415926 0 map map_flipped"/>
|
||||||
|
|
||||||
<!-- simplified offboard control -->
|
<!-- simplified offboard control -->
|
||||||
|
|||||||
@@ -36,7 +36,7 @@
|
|||||||
posctl: { r: 50, g: 100, b: 220 }
|
posctl: { r: 50, g: 100, b: 220 }
|
||||||
offboard: { r: 220, g: 20, b: 250 }
|
offboard: { r: 220, g: 20, b: 250 }
|
||||||
low_battery: { threshold: 3.6, effect: blink_fast, r: 255, g: 0, b: 0 }
|
low_battery: { threshold: 3.6, effect: blink_fast, r: 255, g: 0, b: 0 }
|
||||||
error: { effect: flash, r: 255, g: 0, b: 0 }
|
error: { effect: flash, r: 255, g: 0, b: 0, ignore: [ "[lpe] vision position timeout" ]}
|
||||||
</rosparam>
|
</rosparam>
|
||||||
</node>
|
</node>
|
||||||
</launch>
|
</launch>
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
<arg name="direction_z" default="down"/> <!-- direction the camera points: down, up -->
|
<arg name="direction_z" default="down"/> <!-- direction the camera points: down, up -->
|
||||||
<arg name="direction_y" default="backward"/> <!-- direction the camera cable points: backward, forward -->
|
<arg name="direction_y" default="backward"/> <!-- direction the camera cable points: backward, forward -->
|
||||||
|
<arg name="device" default="/dev/video0"/> <!-- v4l2 device -->
|
||||||
<arg name="simulator" default="false"/>
|
<arg name="simulator" default="false"/>
|
||||||
|
|
||||||
<node if="$(eval direction_z == 'down' and direction_y == 'backward')" pkg="tf2_ros" type="static_transform_publisher" name="main_camera_frame" args="0.05 0 -0.07 -1.5707963 0 3.1415926 base_link main_camera_optical"/>
|
<node if="$(eval direction_z == 'down' and direction_y == 'backward')" pkg="tf2_ros" type="static_transform_publisher" name="main_camera_frame" args="0.05 0 -0.07 -1.5707963 0 3.1415926 base_link main_camera_optical"/>
|
||||||
@@ -17,9 +18,14 @@
|
|||||||
<!-- static_transform_publisher arguments: x y z yaw pitch roll frame_id child_frame_id -->
|
<!-- static_transform_publisher arguments: x y z yaw pitch roll frame_id child_frame_id -->
|
||||||
<!-- <node pkg="tf2_ros" type="static_transform_publisher" name="main_camera_frame" args="0.05 0 -0.07 -1.5707963 0 3.1415926 base_link main_camera_optical"/> -->
|
<!-- <node pkg="tf2_ros" type="static_transform_publisher" name="main_camera_frame" args="0.05 0 -0.07 -1.5707963 0 3.1415926 base_link main_camera_optical"/> -->
|
||||||
|
|
||||||
|
<!-- camera nodelet manager -->
|
||||||
|
<node pkg="nodelet" type="nodelet" name="main_camera_nodelet_manager" args="manager" output="screen" clear_params="true" respawn="true">
|
||||||
|
<param name="num_worker_threads" value="2"/>
|
||||||
|
</node>
|
||||||
|
|
||||||
<!-- camera node -->
|
<!-- camera node -->
|
||||||
<node pkg="nodelet" type="nodelet" name="main_camera" args="load cv_camera/CvCameraNodelet nodelet_manager" clear_params="true" unless="$(arg simulator)">
|
<node pkg="nodelet" type="nodelet" name="main_camera" args="load cv_camera/CvCameraNodelet main_camera_nodelet_manager" launch-prefix="rosrun clover waitfile $(arg device)" clear_params="true" unless="$(arg simulator)" respawn="true">
|
||||||
<param name="device_path" value="/dev/video0"/> <!-- v4l2 device -->
|
<param name="device_path" value="$(arg device)"/>
|
||||||
<param name="frame_id" value="main_camera_optical"/>
|
<param name="frame_id" value="main_camera_optical"/>
|
||||||
<param name="camera_info_url" value="file://$(find clover)/camera_info/fisheye_cam.yaml"/>
|
<param name="camera_info_url" value="file://$(find clover)/camera_info/fisheye_cam.yaml"/>
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,8 @@
|
|||||||
<arg name="respawn" default="true"/>
|
<arg name="respawn" default="true"/>
|
||||||
<arg name="distance_sensor_remap" default="rangefinder/range"/>
|
<arg name="distance_sensor_remap" default="rangefinder/range"/>
|
||||||
<arg name="usb_device" default="/dev/px4fmu"/>
|
<arg name="usb_device" default="/dev/px4fmu"/>
|
||||||
<arg name="prefix" default="bash -c 'while [ ! -e $(arg usb_device) ]; do sleep 1; done; $0 $@'" if="$(eval fcu_conn == 'usb')"/>
|
<arg name="prefix" default="" unless="$(eval fcu_conn == 'usb')"/>
|
||||||
|
<arg name="prefix" default="rosrun clover waitfile $(arg usb_device)" if="$(eval fcu_conn == 'usb')"/>
|
||||||
|
|
||||||
<node pkg="mavros" type="mavros_node" name="mavros" launch-prefix="$(arg prefix)" required="false" clear_params="true" respawn="$(arg respawn)" unless="$(eval fcu_conn == 'none')" respawn_delay="1" output="screen">
|
<node pkg="mavros" type="mavros_node" name="mavros" launch-prefix="$(arg prefix)" required="false" clear_params="true" respawn="$(arg respawn)" unless="$(eval fcu_conn == 'none')" respawn_delay="1" output="screen">
|
||||||
<!-- UART connection -->
|
<!-- UART connection -->
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<?xml version="1.0"?>
|
<?xml version="1.0"?>
|
||||||
<package format="2">
|
<package format="3">
|
||||||
<name>clover</name>
|
<name>clover</name>
|
||||||
<version>0.21.3</version>
|
<version>0.21.1</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>
|
||||||
@@ -15,7 +15,6 @@
|
|||||||
|
|
||||||
<!-- Package format specifier version 2.0 allows specifying dependencies for both
|
<!-- Package format specifier version 2.0 allows specifying dependencies for both
|
||||||
build- and runtime in a single <depend> element -->
|
build- and runtime in a single <depend> element -->
|
||||||
<build_depend>catkin_virtualenv</build_depend>
|
|
||||||
<depend>message_generation</depend>
|
<depend>message_generation</depend>
|
||||||
<depend>roscpp</depend>
|
<depend>roscpp</depend>
|
||||||
<depend>rospy</depend>
|
<depend>rospy</depend>
|
||||||
@@ -38,9 +37,9 @@
|
|||||||
<depend>rosbridge_server</depend>
|
<depend>rosbridge_server</depend>
|
||||||
<depend>web_video_server</depend>
|
<depend>web_video_server</depend>
|
||||||
<depend>tf2_web_republisher</depend>
|
<depend>tf2_web_republisher</depend>
|
||||||
<depend>python-lxml</depend>
|
<depend condition="$ROS_PYTHON_VERSION == 2">python-lxml</depend>
|
||||||
<test_depend>ros_pytest</test_depend>
|
<depend condition="$ROS_PYTHON_VERSION == 3">python3-lxml</depend>
|
||||||
<!-- <exec_depend>python-pymavlink</exec_depend> -->
|
<exec_depend>python-pymavlink</exec_depend>
|
||||||
<!-- Use test_depend for packages you need only for testing: -->
|
<!-- Use test_depend for packages you need only for testing: -->
|
||||||
<!-- <test_depend>gtest</test_depend> -->
|
<!-- <test_depend>gtest</test_depend> -->
|
||||||
|
|
||||||
@@ -48,6 +47,5 @@
|
|||||||
<export>
|
<export>
|
||||||
<nodelet plugin="${prefix}/nodelet_plugins.xml" />
|
<nodelet plugin="${prefix}/nodelet_plugins.xml" />
|
||||||
<!-- Other tools can request additional information be placed here -->
|
<!-- Other tools can request additional information be placed here -->
|
||||||
<pip_requirements>requirements.in</pip_requirements>
|
|
||||||
</export>
|
</export>
|
||||||
</package>
|
</package>
|
||||||
|
|||||||
@@ -1,13 +0,0 @@
|
|||||||
click>=7.1.2
|
|
||||||
docopt>=0.6.2
|
|
||||||
flask>=1.1.1
|
|
||||||
future>=0.18.2
|
|
||||||
geopy>=1.11.0
|
|
||||||
itsdangerous>=1.1.0
|
|
||||||
jinja2>=2.11.3
|
|
||||||
lxml>=4.6.3
|
|
||||||
markupsafe>=1.1.1
|
|
||||||
pymavlink>=2.4.14
|
|
||||||
smbus2>=0.3.0
|
|
||||||
vl53l1x>=0.0.5
|
|
||||||
werkzeug>=1.0.1
|
|
||||||
5
clover/requirements.txt
Normal file
5
clover/requirements.txt
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
flask==1.1.1
|
||||||
|
docopt==0.6.2
|
||||||
|
geopy==1.11.0
|
||||||
|
smbus2==0.3.0
|
||||||
|
VL53L1X==0.0.5
|
||||||
@@ -12,6 +12,7 @@
|
|||||||
|
|
||||||
#include <ros/ros.h>
|
#include <ros/ros.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
#include <boost/algorithm/string.hpp>
|
#include <boost/algorithm/string.hpp>
|
||||||
|
|
||||||
#include <clover/SetLEDEffect.h>
|
#include <clover/SetLEDEffect.h>
|
||||||
@@ -29,6 +30,7 @@ ros::Timer timer;
|
|||||||
ros::Time start_time;
|
ros::Time start_time;
|
||||||
double blink_rate, blink_fast_rate, flash_delay, fade_period, wipe_period, rainbow_period;
|
double blink_rate, blink_fast_rate, flash_delay, fade_period, wipe_period, rainbow_period;
|
||||||
double low_battery_threshold;
|
double low_battery_threshold;
|
||||||
|
std::vector<std::string> error_ignore;
|
||||||
bool blink_state;
|
bool blink_state;
|
||||||
led_msgs::SetLEDs set_leds;
|
led_msgs::SetLEDs set_leds;
|
||||||
led_msgs::LEDStateArray state, start_state;
|
led_msgs::LEDStateArray state, start_state;
|
||||||
@@ -274,6 +276,10 @@ void handleMavrosState(const mavros_msgs::State& msg)
|
|||||||
void handleLog(const rosgraph_msgs::Log& log)
|
void handleLog(const rosgraph_msgs::Log& log)
|
||||||
{
|
{
|
||||||
if (log.level >= rosgraph_msgs::Log::ERROR) {
|
if (log.level >= rosgraph_msgs::Log::ERROR) {
|
||||||
|
// check if ignored
|
||||||
|
for (auto const& str : error_ignore) {
|
||||||
|
if (log.msg.find(str) != std::string::npos) return;
|
||||||
|
}
|
||||||
notify("error");
|
notify("error");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -302,6 +308,7 @@ int main(int argc, char **argv)
|
|||||||
nh_priv.param("rainbow_period", rainbow_period, 5.0);
|
nh_priv.param("rainbow_period", rainbow_period, 5.0);
|
||||||
|
|
||||||
nh_priv.param("notify/low_battery/threshold", low_battery_threshold, 3.7);
|
nh_priv.param("notify/low_battery/threshold", low_battery_threshold, 3.7);
|
||||||
|
nh_priv.param("notify/error/ignore", error_ignore, {});
|
||||||
|
|
||||||
ros::service::waitForService("set_leds"); // cannot work without set_leds service
|
ros::service::waitForService("set_leds"); // cannot work without set_leds service
|
||||||
set_leds_srv = nh.serviceClient<led_msgs::SetLEDs>("set_leds", true);
|
set_leds_srv = nh.serviceClient<led_msgs::SetLEDs>("set_leds", true);
|
||||||
|
|||||||
@@ -70,19 +70,17 @@ private:
|
|||||||
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);
|
||||||
|
|
||||||
img_sub_ = it.subscribeCamera("image_raw", 1, &OpticalFlow::flow, this);
|
|
||||||
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);
|
||||||
velo_pub_ = nh_priv.advertise<geometry_msgs::TwistStamped>("angular_velocity", 1);
|
velo_pub_ = nh_priv.advertise<geometry_msgs::TwistStamped>("angular_velocity", 1);
|
||||||
shift_pub_ = nh_priv.advertise<geometry_msgs::Vector3Stamped>("shift", 1);
|
shift_pub_ = nh_priv.advertise<geometry_msgs::Vector3Stamped>("shift", 1);
|
||||||
|
|
||||||
flow_.integrated_xgyro = NAN; // no IMU available
|
|
||||||
flow_.integrated_ygyro = NAN;
|
|
||||||
flow_.integrated_zgyro = NAN;
|
|
||||||
flow_.time_delta_distance_us = 0;
|
flow_.time_delta_distance_us = 0;
|
||||||
flow_.distance = -1; // no distance sensor available
|
flow_.distance = -1; // no distance sensor available
|
||||||
flow_.temperature = 0;
|
flow_.temperature = 0;
|
||||||
|
|
||||||
|
img_sub_ = it.subscribeCamera("image_raw", 1, &OpticalFlow::flow, this);
|
||||||
|
|
||||||
NODELET_INFO("Optical Flow initialized");
|
NODELET_INFO("Optical Flow initialized");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -152,7 +150,7 @@ private:
|
|||||||
cv::Point2d shift = cv::phaseCorrelate(prev_, curr_, hann_, &response);
|
cv::Point2d shift = cv::phaseCorrelate(prev_, curr_, hann_, &response);
|
||||||
|
|
||||||
// Publish raw shift in pixels
|
// Publish raw shift in pixels
|
||||||
static geometry_msgs::Vector3Stamped shift_vec;
|
geometry_msgs::Vector3Stamped shift_vec;
|
||||||
shift_vec.header.stamp = msg->header.stamp;
|
shift_vec.header.stamp = msg->header.stamp;
|
||||||
shift_vec.header.frame_id = msg->header.frame_id;
|
shift_vec.header.frame_id = msg->header.frame_id;
|
||||||
shift_vec.vector.x = shift.x;
|
shift_vec.vector.x = shift.x;
|
||||||
@@ -178,8 +176,8 @@ private:
|
|||||||
double flow_x = atan2(points_undist[0].x, focal_length_x);
|
double flow_x = atan2(points_undist[0].x, focal_length_x);
|
||||||
double flow_y = atan2(points_undist[0].y, focal_length_y);
|
double flow_y = atan2(points_undist[0].y, focal_length_y);
|
||||||
|
|
||||||
// // Convert to FCU frame
|
// Convert to FCU frame
|
||||||
static geometry_msgs::Vector3Stamped flow_camera, flow_fcu;
|
geometry_msgs::Vector3Stamped flow_camera, flow_fcu;
|
||||||
flow_camera.header.frame_id = msg->header.frame_id;
|
flow_camera.header.frame_id = msg->header.frame_id;
|
||||||
flow_camera.header.stamp = msg->header.stamp;
|
flow_camera.header.stamp = msg->header.stamp;
|
||||||
flow_camera.vector.x = flow_y; // +y means counter-clockwise rotation around Y axis
|
flow_camera.vector.x = flow_y; // +y means counter-clockwise rotation around Y axis
|
||||||
@@ -195,18 +193,21 @@ private:
|
|||||||
ros::Duration integration_time = msg->header.stamp - prev_stamp_;
|
ros::Duration integration_time = msg->header.stamp - prev_stamp_;
|
||||||
uint32_t integration_time_us = integration_time.toSec() * 1.0e6;
|
uint32_t integration_time_us = integration_time.toSec() * 1.0e6;
|
||||||
|
|
||||||
|
// Calculate flow gyro
|
||||||
|
flow_.integrated_xgyro = NAN;
|
||||||
|
flow_.integrated_ygyro = NAN;
|
||||||
|
flow_.integrated_zgyro = NAN;
|
||||||
|
|
||||||
if (calc_flow_gyro_) {
|
if (calc_flow_gyro_) {
|
||||||
try {
|
try {
|
||||||
auto flow_gyro_camera = calcFlowGyro(msg->header.frame_id, prev_stamp_, msg->header.stamp);
|
auto flow_gyro_camera = calcFlowGyro(msg->header.frame_id, prev_stamp_, msg->header.stamp);
|
||||||
static geometry_msgs::Vector3Stamped flow_gyro_fcu;
|
geometry_msgs::Vector3Stamped flow_gyro_fcu;
|
||||||
tf_buffer_->transform(flow_gyro_camera, flow_gyro_fcu, fcu_frame_id_);
|
tf_buffer_->transform(flow_gyro_camera, flow_gyro_fcu, fcu_frame_id_);
|
||||||
flow_.integrated_xgyro = flow_gyro_fcu.vector.x;
|
flow_.integrated_xgyro = flow_gyro_fcu.vector.x;
|
||||||
flow_.integrated_ygyro = flow_gyro_fcu.vector.y;
|
flow_.integrated_ygyro = flow_gyro_fcu.vector.y;
|
||||||
flow_.integrated_zgyro = flow_gyro_fcu.vector.z;
|
flow_.integrated_zgyro = flow_gyro_fcu.vector.z;
|
||||||
} catch (const tf2::TransformException& e) {
|
} catch (const tf2::TransformException& e) {
|
||||||
// Invalidate previous frame
|
// Transform not available, keep NANs in flow gyro
|
||||||
prev_.release();
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -218,6 +219,10 @@ private:
|
|||||||
flow_.quality = (uint8_t)(response * 255);
|
flow_.quality = (uint8_t)(response * 255);
|
||||||
flow_pub_.publish(flow_);
|
flow_pub_.publish(flow_);
|
||||||
|
|
||||||
|
prev_ = curr_.clone();
|
||||||
|
prev_stamp_ = msg->header.stamp;
|
||||||
|
|
||||||
|
publish_debug:
|
||||||
// Publish debug image
|
// Publish debug image
|
||||||
if (img_pub_.getNumSubscribers() > 0) {
|
if (img_pub_.getNumSubscribers() > 0) {
|
||||||
// publish debug image
|
// publish debug image
|
||||||
@@ -231,15 +236,12 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Publish estimated angular velocity
|
// Publish estimated angular velocity
|
||||||
static geometry_msgs::TwistStamped velo;
|
geometry_msgs::TwistStamped velo;
|
||||||
velo.header.stamp = msg->header.stamp;
|
velo.header.stamp = msg->header.stamp;
|
||||||
velo.header.frame_id = fcu_frame_id_;
|
velo.header.frame_id = fcu_frame_id_;
|
||||||
velo.twist.angular.x = flow_.integrated_x / integration_time.toSec();
|
velo.twist.angular.x = flow_fcu.vector.x / integration_time.toSec();
|
||||||
velo.twist.angular.y = flow_.integrated_y / integration_time.toSec();
|
velo.twist.angular.y = flow_fcu.vector.y / integration_time.toSec();
|
||||||
velo_pub_.publish(velo);
|
velo_pub_.publish(velo);
|
||||||
|
|
||||||
prev_ = curr_.clone();
|
|
||||||
prev_stamp_ = msg->header.stamp;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -138,7 +138,7 @@ def mavlink_exec(cmd, timeout=3.0):
|
|||||||
timeout=3,
|
timeout=3,
|
||||||
baudrate=0,
|
baudrate=0,
|
||||||
count=len(cmd),
|
count=len(cmd),
|
||||||
data=map(ord, cmd.ljust(70, '\0')))
|
data=[ord(c) for c in cmd.ljust(70, '\0')])
|
||||||
msg.pack(link)
|
msg.pack(link)
|
||||||
ros_msg = mavlink.convert_to_rosmsg(msg)
|
ros_msg = mavlink.convert_to_rosmsg(msg)
|
||||||
mavlink_pub.publish(ros_msg)
|
mavlink_pub.publish(ros_msg)
|
||||||
@@ -609,7 +609,7 @@ def check_rangefinder():
|
|||||||
|
|
||||||
@check('Boot duration')
|
@check('Boot duration')
|
||||||
def check_boot_duration():
|
def check_boot_duration():
|
||||||
output = subprocess.check_output('systemd-analyze')
|
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 > 15:
|
||||||
@@ -620,7 +620,7 @@ def check_boot_duration():
|
|||||||
def check_cpu_usage():
|
def check_cpu_usage():
|
||||||
WHITELIST = 'nodelet',
|
WHITELIST = 'nodelet',
|
||||||
CMD = "top -n 1 -b -i | tail -n +8 | awk '{ printf(\"%-8s\\t%-8s\\t%-8s\\n\", $1, $9, $12); }'"
|
CMD = "top -n 1 -b -i | tail -n +8 | awk '{ printf(\"%-8s\\t%-8s\\t%-8s\\n\", $1, $9, $12); }'"
|
||||||
output = subprocess.check_output(CMD, shell=True)
|
output = subprocess.check_output(CMD, shell=True).decode()
|
||||||
processes = output.split('\n')
|
processes = output.split('\n')
|
||||||
for process in processes:
|
for process in processes:
|
||||||
if not process:
|
if not process:
|
||||||
@@ -636,7 +636,7 @@ def check_cpu_usage():
|
|||||||
def check_clover_service():
|
def check_clover_service():
|
||||||
try:
|
try:
|
||||||
output = subprocess.check_output('systemctl show -p ActiveState --value clover.service'.split(),
|
output = subprocess.check_output('systemctl show -p ActiveState --value clover.service'.split(),
|
||||||
stderr=subprocess.STDOUT)
|
stderr=subprocess.STDOUT).decode()
|
||||||
except subprocess.CalledProcessError as e:
|
except subprocess.CalledProcessError as e:
|
||||||
failure('systemctl returned %s: %s', e.returncode, e.output)
|
failure('systemctl returned %s: %s', e.returncode, e.output)
|
||||||
return
|
return
|
||||||
@@ -751,7 +751,7 @@ def check_rpi_health():
|
|||||||
# <parameter>=<value>
|
# <parameter>=<value>
|
||||||
# In case of `get_throttled`, <value> is a hexadecimal number
|
# In case of `get_throttled`, <value> is a hexadecimal number
|
||||||
# with some of the FLAGs OR'ed together
|
# with some of the FLAGs OR'ed together
|
||||||
output = subprocess.check_output(['vcgencmd', 'get_throttled'])
|
output = subprocess.check_output(['vcgencmd', 'get_throttled']).decode()
|
||||||
except OSError:
|
except OSError:
|
||||||
failure('could not call vcgencmd binary; not a Raspberry Pi?')
|
failure('could not call vcgencmd binary; not a Raspberry Pi?')
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -183,7 +183,7 @@ inline bool waitTransform(const string& target, const string& source,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#define TIMEOUT(msg, timeout) (ros::Time::now() - msg.header.stamp > timeout)
|
#define TIMEOUT(msg, timeout) (msg.header.stamp.isZero() || (ros::Time::now() - msg.header.stamp > timeout))
|
||||||
|
|
||||||
bool getTelemetry(GetTelemetry::Request& req, GetTelemetry::Response& res)
|
bool getTelemetry(GetTelemetry::Request& req, GetTelemetry::Response& res)
|
||||||
{
|
{
|
||||||
@@ -441,6 +441,10 @@ void publish(const ros::Time stamp)
|
|||||||
|
|
||||||
// publish setpoint frame
|
// publish setpoint frame
|
||||||
if (!setpoint.child_frame_id.empty()) {
|
if (!setpoint.child_frame_id.empty()) {
|
||||||
|
if (setpoint.header.stamp == position_msg.header.stamp) {
|
||||||
|
return; // avoid TF_REPEATED_DATA warnings
|
||||||
|
}
|
||||||
|
|
||||||
setpoint.transform.translation.x = position_msg.pose.position.x;
|
setpoint.transform.translation.x = position_msg.pose.position.x;
|
||||||
setpoint.transform.translation.y = position_msg.pose.position.y;
|
setpoint.transform.translation.y = position_msg.pose.position.y;
|
||||||
setpoint.transform.translation.z = position_msg.pose.position.z;
|
setpoint.transform.translation.z = position_msg.pose.position.z;
|
||||||
@@ -712,7 +716,7 @@ bool serve(enum setpoint_type_t sp_type, float x, float y, float z, float vx, fl
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (sp_type == VELOCITY) {
|
if (sp_type == VELOCITY) {
|
||||||
static Vector3Stamped vel;
|
Vector3Stamped vel;
|
||||||
vel.header.frame_id = frame_id;
|
vel.header.frame_id = frame_id;
|
||||||
vel.header.stamp = stamp;
|
vel.header.stamp = stamp;
|
||||||
vel.vector.x = vx;
|
vel.vector.x = vx;
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ void publishZero(const ros::TimerEvent& e)
|
|||||||
}
|
}
|
||||||
|
|
||||||
ROS_INFO_THROTTLE(10, "publish zero");
|
ROS_INFO_THROTTLE(10, "publish zero");
|
||||||
static geometry_msgs::PoseStamped zero;
|
geometry_msgs::PoseStamped zero;
|
||||||
zero.header.frame_id = local_frame_id;
|
zero.header.frame_id = local_frame_id;
|
||||||
zero.header.stamp = e.current_real;
|
zero.header.stamp = e.current_real;
|
||||||
zero.pose.orientation.w = 1;
|
zero.pose.orientation.w = 1;
|
||||||
|
|||||||
9
clover/src/waitfile
Executable file
9
clover/src/waitfile
Executable file
@@ -0,0 +1,9 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# $ ./waitfile <file> <command> <args...>
|
||||||
|
# wait until <file> appears and then invoke <command> with <args>
|
||||||
|
|
||||||
|
echo "wait for file $1"
|
||||||
|
while [ ! -e "$1" ]; do sleep 1; done;
|
||||||
|
echo "file $1 appeared"
|
||||||
|
exec "${@:2}"
|
||||||
@@ -26,5 +26,10 @@ def test_simple_offboard_services_available():
|
|||||||
rospy.wait_for_service('land', timeout=5)
|
rospy.wait_for_service('land', timeout=5)
|
||||||
|
|
||||||
def test_web_video_server(node):
|
def test_web_video_server(node):
|
||||||
import urllib2
|
try:
|
||||||
urllib2.urlopen("http://localhost:8080").read()
|
# Python 2
|
||||||
|
import urllib2 as urllib
|
||||||
|
except ModuleNotFoundError:
|
||||||
|
# Python 3
|
||||||
|
import urllib.request as urllib
|
||||||
|
urllib.urlopen("http://localhost:8080").read()
|
||||||
|
|||||||
1
clover/www/clover.log
Symbolic link
1
clover/www/clover.log
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
/var/log/clover.log
|
||||||
@@ -4,11 +4,12 @@
|
|||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
<li><a href="docs">View documentation</a> (snapshot of <a href="https://clover.coex.tech">clover.coex.tech</a>)</li>
|
<li><a href="docs">View documentation</a> (snapshot of <a href="https://clover.coex.tech">clover.coex.tech</a>)</li>
|
||||||
|
<li><a href="topics.html">View topics</a></li>
|
||||||
<li><a href="" id="wvs">View image topics</a> (<code>web_video_server</code>)</li>
|
<li><a href="" id="wvs">View image topics</a> (<code>web_video_server</code>)</li>
|
||||||
<li><a href="" id="butterfly">Open web terminal</a> (<code>Butterfly</code>)</li>
|
<li><a href="" id="butterfly">Open web terminal</a> (<code>Butterfly</code>)</li>
|
||||||
<li><a href="viz.html">View 3D visualization</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="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>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<div class="version"></div>
|
<div class="version"></div>
|
||||||
@@ -17,6 +18,14 @@
|
|||||||
document.querySelector("#wvs").href = location.protocol + '//' + location.hostname + ':8080';
|
document.querySelector("#wvs").href = location.protocol + '//' + location.hostname + ':8080';
|
||||||
document.querySelector("#butterfly").href = location.protocol + '//' + location.hostname + ':57575';
|
document.querySelector("#butterfly").href = location.protocol + '//' + location.hostname + ':57575';
|
||||||
|
|
||||||
|
document.querySelector("#butterfly").addEventListener('click', function(e) {
|
||||||
|
if (location.hostname == 'localhost' || location.hostname == '127.0.0.1') {
|
||||||
|
if (!confirm('Please use regular Terminal app on a local machine.\nClick OK to proceed to Butterfly anyway.')) {
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Determine image version
|
// Determine image version
|
||||||
fetch('clover_version').then(function(response) {
|
fetch('clover_version').then(function(response) {
|
||||||
if (response.status !== 200) return;
|
if (response.status !== 200) return;
|
||||||
|
|||||||
236
clover/www/js/json-to-pretty-yaml.js
Normal file
236
clover/www/js/json-to-pretty-yaml.js
Normal file
@@ -0,0 +1,236 @@
|
|||||||
|
// Browserified https://www.npmjs.com/package/json-to-pretty-yaml module
|
||||||
|
|
||||||
|
(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
|
||||||
|
(function() {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var typeOf = require('remedial').typeOf;
|
||||||
|
var trimWhitespace = require('remove-trailing-spaces');
|
||||||
|
|
||||||
|
function stringify(data) {
|
||||||
|
var handlers, indentLevel = '';
|
||||||
|
|
||||||
|
handlers = {
|
||||||
|
"undefined": function() {
|
||||||
|
// objects will not have `undefined` converted to `null`
|
||||||
|
// as this may have unintended consequences
|
||||||
|
// For arrays, however, this behavior seems appropriate
|
||||||
|
return 'null';
|
||||||
|
},
|
||||||
|
"null": function() {
|
||||||
|
return 'null';
|
||||||
|
},
|
||||||
|
"number": function(x) {
|
||||||
|
return x;
|
||||||
|
},
|
||||||
|
"boolean": function(x) {
|
||||||
|
return x ? 'true' : 'false';
|
||||||
|
},
|
||||||
|
"string": function(x) {
|
||||||
|
// to avoid the string "true" being confused with the
|
||||||
|
// the literal `true`, we always wrap strings in quotes
|
||||||
|
return JSON.stringify(x);
|
||||||
|
},
|
||||||
|
"array": function(x) {
|
||||||
|
var output = '';
|
||||||
|
|
||||||
|
if (0 === x.length) {
|
||||||
|
output += '[]';
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
indentLevel = indentLevel.replace(/$/, ' ');
|
||||||
|
x.forEach(function(y, i) {
|
||||||
|
// TODO how should `undefined` be handled?
|
||||||
|
var handler = handlers[typeOf(y)];
|
||||||
|
|
||||||
|
if (!handler) {
|
||||||
|
throw new Error('what the crap: ' + typeOf(y));
|
||||||
|
}
|
||||||
|
|
||||||
|
output += '\n' + indentLevel + '- ' + handler(y, true);
|
||||||
|
|
||||||
|
});
|
||||||
|
indentLevel = indentLevel.replace(/ /, '');
|
||||||
|
|
||||||
|
return output;
|
||||||
|
},
|
||||||
|
"object": function(x, inArray, rootNode) {
|
||||||
|
var output = '';
|
||||||
|
|
||||||
|
if (0 === Object.keys(x).length) {
|
||||||
|
output += '{}';
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!rootNode) {
|
||||||
|
indentLevel = indentLevel.replace(/$/, ' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.keys(x).forEach(function(k, i) {
|
||||||
|
var val = x[k],
|
||||||
|
handler = handlers[typeOf(val)];
|
||||||
|
|
||||||
|
if ('undefined' === typeof val) {
|
||||||
|
// the user should do
|
||||||
|
// delete obj.key
|
||||||
|
// and not
|
||||||
|
// obj.key = undefined
|
||||||
|
// but we'll error on the side of caution
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!handler) {
|
||||||
|
throw new Error('what the crap: ' + typeOf(val));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(inArray && i === 0)) {
|
||||||
|
output += '\n' + indentLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
output += k + ': ' + handler(val);
|
||||||
|
});
|
||||||
|
indentLevel = indentLevel.replace(/ /, '');
|
||||||
|
|
||||||
|
return output;
|
||||||
|
},
|
||||||
|
"function": function() {
|
||||||
|
// TODO this should throw or otherwise be ignored
|
||||||
|
return '[object Function]';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return trimWhitespace(handlers[typeOf(data)](data, true, true) + '\n');
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
window.yamlStringify = stringify;
|
||||||
|
module.exports.stringify = stringify;
|
||||||
|
}());
|
||||||
|
|
||||||
|
},{"remedial":2,"remove-trailing-spaces":3}],2:[function(require,module,exports){
|
||||||
|
/*jslint onevar: true, undef: true, nomen: true, eqeqeq: true, plusplus: true, bitwise: true, regexp: true, newcap: true, immed: true */
|
||||||
|
(function () {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var global = Function('return this')()
|
||||||
|
, classes = "Boolean Number String Function Array Date RegExp Object".split(" ")
|
||||||
|
, i
|
||||||
|
, name
|
||||||
|
, class2type = {}
|
||||||
|
;
|
||||||
|
|
||||||
|
for (i in classes) {
|
||||||
|
if (classes.hasOwnProperty(i)) {
|
||||||
|
name = classes[i];
|
||||||
|
class2type["[object " + name + "]"] = name.toLowerCase();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function typeOf(obj) {
|
||||||
|
return (null === obj || undefined === obj) ? String(obj) : class2type[Object.prototype.toString.call(obj)] || "object";
|
||||||
|
}
|
||||||
|
|
||||||
|
function isEmpty(o) {
|
||||||
|
var i, v;
|
||||||
|
if (typeOf(o) === 'object') {
|
||||||
|
for (i in o) { // fails jslint
|
||||||
|
v = o[i];
|
||||||
|
if (v !== undefined && typeOf(v) !== 'function') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!String.prototype.entityify) {
|
||||||
|
String.prototype.entityify = function () {
|
||||||
|
return this.replace(/&/g, "&").replace(/</g,
|
||||||
|
"<").replace(/>/g, ">");
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!String.prototype.quote) {
|
||||||
|
String.prototype.quote = function () {
|
||||||
|
var c, i, l = this.length, o = '"';
|
||||||
|
for (i = 0; i < l; i += 1) {
|
||||||
|
c = this.charAt(i);
|
||||||
|
if (c >= ' ') {
|
||||||
|
if (c === '\\' || c === '"') {
|
||||||
|
o += '\\';
|
||||||
|
}
|
||||||
|
o += c;
|
||||||
|
} else {
|
||||||
|
switch (c) {
|
||||||
|
case '\b':
|
||||||
|
o += '\\b';
|
||||||
|
break;
|
||||||
|
case '\f':
|
||||||
|
o += '\\f';
|
||||||
|
break;
|
||||||
|
case '\n':
|
||||||
|
o += '\\n';
|
||||||
|
break;
|
||||||
|
case '\r':
|
||||||
|
o += '\\r';
|
||||||
|
break;
|
||||||
|
case '\t':
|
||||||
|
o += '\\t';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
c = c.charCodeAt();
|
||||||
|
o += '\\u00' + Math.floor(c / 16).toString(16) +
|
||||||
|
(c % 16).toString(16);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return o + '"';
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!String.prototype.supplant) {
|
||||||
|
String.prototype.supplant = function (o) {
|
||||||
|
return this.replace(/{([^{}]*)}/g,
|
||||||
|
function (a, b) {
|
||||||
|
var r = o[b];
|
||||||
|
return typeof r === 'string' || typeof r === 'number' ? r : a;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!String.prototype.trim) {
|
||||||
|
String.prototype.trim = function () {
|
||||||
|
return this.replace(/^\s*(\S*(?:\s+\S+)*)\s*$/, "$1");
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// CommonJS / npm / Ender.JS
|
||||||
|
module.exports = {
|
||||||
|
typeOf: typeOf,
|
||||||
|
isEmpty: isEmpty
|
||||||
|
};
|
||||||
|
global.typeOf = global.typeOf || typeOf;
|
||||||
|
global.isEmpty = global.isEmpty || isEmpty;
|
||||||
|
}());
|
||||||
|
|
||||||
|
},{}],3:[function(require,module,exports){
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* removeTrailingSpaces
|
||||||
|
* Remove the trailing spaces from a string.
|
||||||
|
*
|
||||||
|
* @name removeTrailingSpaces
|
||||||
|
* @function
|
||||||
|
* @param {String} input The input string.
|
||||||
|
* @returns {String} The output string.
|
||||||
|
*/
|
||||||
|
|
||||||
|
module.exports = function removeTrailingSpaces(input) {
|
||||||
|
// TODO If possible, use a regex
|
||||||
|
return input.split("\n").map(function (x) {
|
||||||
|
return x.trimRight();
|
||||||
|
}).join("\n");
|
||||||
|
};
|
||||||
|
},{}]},{},[1]);
|
||||||
100
clover/www/js/topics.js
Normal file
100
clover/www/js/topics.js
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
const url = 'ws://' + location.hostname + ':9090';
|
||||||
|
const ros = new ROSLIB.Ros({ url: url });
|
||||||
|
|
||||||
|
ros.on('connection', function () {
|
||||||
|
document.body.classList.add('connected');
|
||||||
|
init();
|
||||||
|
});
|
||||||
|
|
||||||
|
ros.on('close', function () {
|
||||||
|
document.body.classList.remove('connected');
|
||||||
|
setTimeout(function() {
|
||||||
|
// reconnect
|
||||||
|
ros.connect(url);
|
||||||
|
}, 2000);
|
||||||
|
});
|
||||||
|
|
||||||
|
const title = document.querySelector('h1');
|
||||||
|
const topicsList = document.querySelector('#topics');
|
||||||
|
const topicMessage = document.querySelector('#topic-message');
|
||||||
|
|
||||||
|
function viewTopicsList() {
|
||||||
|
title.innerHTML = 'Topics:';
|
||||||
|
|
||||||
|
ros.getTopics(function(topics) {
|
||||||
|
topicsList.innerHTML = topics.topics.map(function(topic, i) {
|
||||||
|
const type = topics.types[i];
|
||||||
|
if (type == 'sensor_msgs/Image') {
|
||||||
|
let url = `${location.protocol}//${location.hostname}:8080/stream_viewer?topic=${topic}`;
|
||||||
|
return `<li><a href="${url}" class=topic title=${type}>${topic}</a> 🖼</li>`;
|
||||||
|
} else {
|
||||||
|
return `<li><a href="?topic=${topic}" class=topic title=${type}>${topic}</a></li>`;
|
||||||
|
}
|
||||||
|
}).join('');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let rosdistro;
|
||||||
|
|
||||||
|
function viewTopic(topic) {
|
||||||
|
title.innerHTML = topic;
|
||||||
|
topicMessage.style.display = 'block';
|
||||||
|
|
||||||
|
ros.getTopicType(topic, function(typeStr) {
|
||||||
|
const [pack, type] = typeStr.split('/');
|
||||||
|
let href = `https://docs.ros.org/en/${rosdistro}/api/${pack}/html/msg/${type}.html`;
|
||||||
|
title.innerHTML = `${topic} <a id="topic-type" href=${href} target="_blank">${typeStr}</a>`;
|
||||||
|
});
|
||||||
|
|
||||||
|
new ROSLIB.Topic({ ros: ros, name: topic }).subscribe(function(msg) {
|
||||||
|
document.title = topic;
|
||||||
|
if (mouseDown) return;
|
||||||
|
topicMessage.innerHTML = yamlStringify(msg); // JSON.stringify(msg, null, 4);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function viewParameters() {
|
||||||
|
title.innerHTML = 'Parameters';
|
||||||
|
topicMessage.style.display = 'block';
|
||||||
|
|
||||||
|
let names = ['aruco_detect', 'main_camera', 'main_camera_nodelet_manager', 'mavros', 'optical_flow',
|
||||||
|
'rangefinder', 'rosapi', 'rosbridge_websocket', 'rosdistro', 'roslaunch', 'rosversion',
|
||||||
|
'roswww_static', 'run_id', 'simple_offboard', 'visualization', 'web_video_server'];
|
||||||
|
let params = {};
|
||||||
|
// ros.getParams(function(params) {
|
||||||
|
|
||||||
|
Promise.all(names.map(function(name) {
|
||||||
|
return new Promise(function(resolve, reject) {
|
||||||
|
new ROSLIB.Param({ ros, name }).get(function(value) {
|
||||||
|
params[name] = value;
|
||||||
|
resolve();
|
||||||
|
})
|
||||||
|
});
|
||||||
|
})).then(function() {
|
||||||
|
console.log(params);
|
||||||
|
topicMessage.innerHTML = yamlStringify(params);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let mouseDown;
|
||||||
|
|
||||||
|
topicMessage.addEventListener('mousedown', function() { mouseDown = true; });
|
||||||
|
topicMessage.addEventListener('mouseup', function() { mouseDown = false; });
|
||||||
|
|
||||||
|
function init() {
|
||||||
|
const params = Object.fromEntries(new URLSearchParams(window.location.search).entries());
|
||||||
|
|
||||||
|
viewParameters();
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!params.topic) {
|
||||||
|
viewTopicsList();
|
||||||
|
} else {
|
||||||
|
new ROSLIB.Param({ ros: ros, name: '/rosdistro'}).get(function(value) {
|
||||||
|
rosdistro = value.trim();
|
||||||
|
viewTopic(params.topic);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.ros = ros;
|
||||||
27
clover/www/topics.html
Normal file
27
clover/www/topics.html
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<title>ROS topics</title>
|
||||||
|
<script src="js/roslib.js"></script>
|
||||||
|
<link rel="icon" href="data:,"> <!-- make chrome don't request icon -->
|
||||||
|
<script type="module" src="js/topics.js"></script>
|
||||||
|
<script src="js/json-to-pretty-yaml.js"></script>
|
||||||
|
<style>
|
||||||
|
#topics { line-height: 1.2em; }
|
||||||
|
#topic-view {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
#topic-message {
|
||||||
|
display: none;
|
||||||
|
white-space: pre;
|
||||||
|
font-family: monospace;
|
||||||
|
}
|
||||||
|
#topic-type { font-family: monospace; font-size: 0.5em; vertical-align: super; font-weight: normal; }
|
||||||
|
.topic { font-family: monospace; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1> </h1>
|
||||||
|
<ul id="topics"></ul>
|
||||||
|
<code id="topic-message" title="Hold mouse button to pause">No messages received</code>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -73,6 +73,13 @@ catkin_install_python(PROGRAMS src/clover_blocks
|
|||||||
DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
|
DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# TODO: store programs in home directory?
|
||||||
|
install(DIRECTORY programs
|
||||||
|
DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}
|
||||||
|
)
|
||||||
|
|
||||||
|
install(DIRECTORY www DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION})
|
||||||
|
|
||||||
#############
|
#############
|
||||||
## Testing ##
|
## Testing ##
|
||||||
#############
|
#############
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ Internal package documentation is given below.
|
|||||||
|
|
||||||
## Frontend
|
## Frontend
|
||||||
|
|
||||||
The frontend files are located in [`www`](./www/) subdirectory. The frontend application uses [`roblib.js`](http://wiki.ros.org/roslibjs) library for communicating with backend node and other ROS resources.
|
The frontend files are located in [`www`](./www/) subdirectory. The frontend application uses [`roslib.js`](http://wiki.ros.org/roslibjs) library for communicating with backend node and other ROS resources.
|
||||||
|
|
||||||
## `clover_blocks` node
|
## `clover_blocks` node
|
||||||
|
|
||||||
@@ -30,7 +30,8 @@ The frontend files are located in [`www`](./www/) subdirectory. The frontend app
|
|||||||
Parameters read by frontend:
|
Parameters read by frontend:
|
||||||
|
|
||||||
* `~navigate_tolerance` (*float*) – distance tolerance in meters, used for navigate-like blocks (default: 0.2).
|
* `~navigate_tolerance` (*float*) – distance tolerance in meters, used for navigate-like blocks (default: 0.2).
|
||||||
* `~yaw_tolerance` (*float*) – yaw angle tolerance in degrees, used in set_yaw block (default: 20).
|
* `~navigate_global_tolerance` (*float*) – distance tolerance for global coordinates navigation (default: 1).
|
||||||
|
* `~yaw_tolerance` (*float*) – yaw angle tolerance in degrees, used in set_yaw block (default: 1).
|
||||||
* `~sleep_time` (*float*) – duration of sleep in loop cycles, used for navigate-like blocks (default: 0.2).
|
* `~sleep_time` (*float*) – duration of sleep in loop cycles, used for navigate-like blocks (default: 0.2).
|
||||||
* `~confirm_run` (*bool*) – enable confirmation to run the program (default: true).
|
* `~confirm_run` (*bool*) – enable confirmation to run the program (default: true).
|
||||||
|
|
||||||
|
|||||||
@@ -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.3</version>
|
<version>0.21.1</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>
|
||||||
|
|||||||
@@ -11,7 +11,8 @@
|
|||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
|
|
||||||
import rospy
|
import rospy
|
||||||
import os
|
import os, sys
|
||||||
|
import traceback
|
||||||
import threading
|
import threading
|
||||||
import re
|
import re
|
||||||
import uuid
|
import uuid
|
||||||
@@ -111,12 +112,17 @@ def run(req):
|
|||||||
'print': _print,
|
'print': _print,
|
||||||
'raw_input': _input}
|
'raw_input': _input}
|
||||||
try:
|
try:
|
||||||
exec req.code in g
|
exec(req.code, g)
|
||||||
except Stop:
|
except Stop:
|
||||||
rospy.loginfo('Program forced to stop')
|
rospy.loginfo('Program forced to stop')
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
rospy.logerr(str(e))
|
rospy.logerr(str(e))
|
||||||
error_pub.publish(str(e))
|
traceback.print_exc()
|
||||||
|
etype, value, tb = sys.exc_info()
|
||||||
|
fmt = traceback.format_exception(etype, value, tb)
|
||||||
|
fmt.pop(1) # remove 'clover_blocks' file frame
|
||||||
|
exc_info = ''.join(fmt)
|
||||||
|
error_pub.publish(str(e) + '\n\n' + exc_info)
|
||||||
|
|
||||||
rospy.loginfo('Program terminated')
|
rospy.loginfo('Program terminated')
|
||||||
running_lock.release()
|
running_lock.release()
|
||||||
@@ -140,6 +146,7 @@ def stop(req):
|
|||||||
return {'success': True}
|
return {'success': True}
|
||||||
|
|
||||||
|
|
||||||
|
# TODO: find dir in installed package
|
||||||
programs_path = rospy.get_param('~programs_dir', os.path.dirname(os.path.abspath(__file__)) + '/../programs')
|
programs_path = rospy.get_param('~programs_dir', os.path.dirname(os.path.abspath(__file__)) + '/../programs')
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -31,6 +31,14 @@ function considerFrameId(e) {
|
|||||||
this.getInput('Y').fieldRow[0].setValue('y');
|
this.getInput('Y').fieldRow[0].setValue('y');
|
||||||
this.getInput('Z').fieldRow[0].setValue('z');
|
this.getInput('Z').fieldRow[0].setValue('z');
|
||||||
}
|
}
|
||||||
|
if (this.getInput('LAT')) { // block has global coordinates
|
||||||
|
let global = frameId.startsWith('GLOBAL');
|
||||||
|
this.getInput('LAT').setVisible(global);
|
||||||
|
this.getInput('LON').setVisible(global);
|
||||||
|
this.getInput('X').setVisible(!global);
|
||||||
|
this.getInput('Y').setVisible(!global);
|
||||||
|
this.getInput('Z').fieldRow[0].setValue(frameId == 'GLOBAL' ? 'altitude' : 'z');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (this.getInput('ID')) { // block has marker id field
|
if (this.getInput('ID')) { // block has marker id field
|
||||||
this.getInput('ID').setVisible(frameId == 'ARUCO'); // toggle id field
|
this.getInput('ID').setVisible(frameId == 'ARUCO'); // toggle id field
|
||||||
@@ -65,6 +73,9 @@ function updateSetpointBlock(e) {
|
|||||||
|
|
||||||
Blockly.Blocks['navigate'] = {
|
Blockly.Blocks['navigate'] = {
|
||||||
init: function () {
|
init: function () {
|
||||||
|
let navFrameId = frameIds.slice();
|
||||||
|
navFrameId.push(['global', 'GLOBAL_LOCAL'])
|
||||||
|
navFrameId.push(['global, WGS 84 alt.', 'GLOBAL'])
|
||||||
this.appendDummyInput()
|
this.appendDummyInput()
|
||||||
.appendField("navigate to point");
|
.appendField("navigate to point");
|
||||||
this.appendValueInput("X")
|
this.appendValueInput("X")
|
||||||
@@ -73,12 +84,20 @@ Blockly.Blocks['navigate'] = {
|
|||||||
this.appendValueInput("Y")
|
this.appendValueInput("Y")
|
||||||
.setCheck("Number")
|
.setCheck("Number")
|
||||||
.appendField("left");
|
.appendField("left");
|
||||||
|
this.appendValueInput("LAT")
|
||||||
|
.setCheck("Number")
|
||||||
|
.appendField("latitude")
|
||||||
|
.setVisible(false);
|
||||||
|
this.appendValueInput("LON")
|
||||||
|
.setCheck("Number")
|
||||||
|
.appendField("longitude")
|
||||||
|
.setVisible(false)
|
||||||
this.appendValueInput("Z")
|
this.appendValueInput("Z")
|
||||||
.setCheck("Number")
|
.setCheck("Number")
|
||||||
.appendField("up");
|
.appendField("up");
|
||||||
this.appendDummyInput()
|
this.appendDummyInput()
|
||||||
.appendField("relative to")
|
.appendField("relative to")
|
||||||
.appendField(new Blockly.FieldDropdown(frameIds), "FRAME_ID");
|
.appendField(new Blockly.FieldDropdown(navFrameId), "FRAME_ID");
|
||||||
this.appendValueInput("ID")
|
this.appendValueInput("ID")
|
||||||
.setCheck("Number")
|
.setCheck("Number")
|
||||||
.appendField("with ID")
|
.appendField("with ID")
|
||||||
@@ -268,7 +287,7 @@ Blockly.Blocks['mode'] = {
|
|||||||
.appendField("current flight mode");
|
.appendField("current flight mode");
|
||||||
this.setOutput(true, "String");
|
this.setOutput(true, "String");
|
||||||
this.setColour(COLOR_STATE);
|
this.setColour(COLOR_STATE);
|
||||||
this.setTooltip("");
|
this.setTooltip("Returns current flight mode (POSCTL, OFFBOARD, etc).");
|
||||||
this.setHelpUrl(DOCS_URL + '#' + this.type);
|
this.setHelpUrl(DOCS_URL + '#' + this.type);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -375,7 +394,7 @@ Blockly.Blocks['take_off'] = {
|
|||||||
this.setPreviousStatement(true, null);
|
this.setPreviousStatement(true, null);
|
||||||
this.setNextStatement(true, null);
|
this.setNextStatement(true, null);
|
||||||
this.setColour(COLOR_FLIGHT);
|
this.setColour(COLOR_FLIGHT);
|
||||||
this.setTooltip("Take off on desired altitude in meters");
|
this.setTooltip("Take off on desired altitude in meters.");
|
||||||
this.setHelpUrl(DOCS_URL + '#' + this.type);
|
this.setHelpUrl(DOCS_URL + '#' + this.type);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -391,7 +410,7 @@ Blockly.Blocks['land'] = {
|
|||||||
this.setPreviousStatement(true, null);
|
this.setPreviousStatement(true, null);
|
||||||
this.setNextStatement(true, null);
|
this.setNextStatement(true, null);
|
||||||
this.setColour(COLOR_FLIGHT);
|
this.setColour(COLOR_FLIGHT);
|
||||||
this.setTooltip("");
|
this.setTooltip("Land the drone.");
|
||||||
this.setHelpUrl(DOCS_URL + '#' + this.type);
|
this.setHelpUrl(DOCS_URL + '#' + this.type);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -400,10 +419,10 @@ Blockly.Blocks['global_position'] = {
|
|||||||
init: function () {
|
init: function () {
|
||||||
this.appendDummyInput()
|
this.appendDummyInput()
|
||||||
.appendField("current")
|
.appendField("current")
|
||||||
.appendField(new Blockly.FieldDropdown([["latitude", "LATITUDE"], ["longitude", "LONGITUDE"], ["altitude", "ALTITUDE"]]), "NAME");
|
.appendField(new Blockly.FieldDropdown([["latitude", "LAT"], ["longitude", "LON"], ["altitude", "ALT"]]), "FIELD");
|
||||||
this.setOutput(true, "Number");
|
this.setOutput(true, "Number");
|
||||||
this.setColour(230);
|
this.setColour(COLOR_STATE);
|
||||||
this.setTooltip("");
|
this.setTooltip("Returns current global position (latitude, longitude, altitude above the WGS 84 ellipsoid).");
|
||||||
this.setHelpUrl(DOCS_URL + '#' + this.type);
|
this.setHelpUrl(DOCS_URL + '#' + this.type);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -52,6 +52,8 @@
|
|||||||
<value name="X"><shadow type="math_number"><field name="NUM">0</field></shadow></value>
|
<value name="X"><shadow type="math_number"><field name="NUM">0</field></shadow></value>
|
||||||
<value name="Y"><shadow type="math_number"><field name="NUM">0</field></shadow></value>
|
<value name="Y"><shadow type="math_number"><field name="NUM">0</field></shadow></value>
|
||||||
<value name="Z"><shadow type="math_number"><field name="NUM">0</field></shadow></value>
|
<value name="Z"><shadow type="math_number"><field name="NUM">0</field></shadow></value>
|
||||||
|
<value name="LAT"><shadow type="math_number"><field name="NUM">47.397503</field></shadow></value>
|
||||||
|
<value name="LON"><shadow type="math_number"><field name="NUM">8.544945</field></shadow></value>
|
||||||
<value name="SPEED"><shadow type="math_number"><field name="NUM">0.5</field></shadow></value>
|
<value name="SPEED"><shadow type="math_number"><field name="NUM">0.5</field></shadow></value>
|
||||||
<value name="ID"><shadow type="math_number"><field name="NUM">0</field></shadow></value>
|
<value name="ID"><shadow type="math_number"><field name="NUM">0</field></shadow></value>
|
||||||
</block>
|
</block>
|
||||||
@@ -85,6 +87,7 @@
|
|||||||
<value name="ID"><shadow type="math_number"><field name="NUM">0</field></shadow></value>
|
<value name="ID"><shadow type="math_number"><field name="NUM">0</field></shadow></value>
|
||||||
</block>
|
</block>
|
||||||
<block type="get_attitude"></block>
|
<block type="get_attitude"></block>
|
||||||
|
<block type="global_position"></block>
|
||||||
<block type="distance">
|
<block type="distance">
|
||||||
<value name="X"><shadow type="math_number"><field name="NUM">0</field></shadow></value>
|
<value name="X"><shadow type="math_number"><field name="NUM">0</field></shadow></value>
|
||||||
<value name="Y"><shadow type="math_number"><field name="NUM">0</field></shadow></value>
|
<value name="Y"><shadow type="math_number"><field name="NUM">0</field></shadow></value>
|
||||||
|
|||||||
@@ -39,7 +39,8 @@ var workspace = Blockly.inject('blockly', {
|
|||||||
function readParams() {
|
function readParams() {
|
||||||
return Promise.all([
|
return Promise.all([
|
||||||
ros.readParam('navigate_tolerance', true, 0.2),
|
ros.readParam('navigate_tolerance', true, 0.2),
|
||||||
ros.readParam('yaw_tolerance', true, 20),
|
ros.readParam('navigate_global_tolerance', true, 1),
|
||||||
|
ros.readParam('yaw_tolerance', true, 1),
|
||||||
ros.readParam('sleep_time', true, 0.2),
|
ros.readParam('sleep_time', true, 0.2),
|
||||||
ros.readParam('confirm_run', true, true),
|
ros.readParam('confirm_run', true, true),
|
||||||
]);
|
]);
|
||||||
@@ -210,7 +211,7 @@ function loadPrograms() {
|
|||||||
updateChanged();
|
updateChanged();
|
||||||
}, function(err) {
|
}, function(err) {
|
||||||
document.querySelector('.backend-fail').style.display = 'inline';
|
document.querySelector('.backend-fail').style.display = 'inline';
|
||||||
alert(`Error loading programs list.\n\nHave you enabled clover_blocks in clover.launch?`);
|
alert(`Error loading programs list.\n\nHave you enabled 'blocks' in clover.launch?`);
|
||||||
runButton.disabled = true;
|
runButton.disabled = true;
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,6 +33,18 @@ const NAVIGATE_WAIT = () => `\ndef navigate_wait(x=0, y=0, z=0, speed=0.5, frame
|
|||||||
return
|
return
|
||||||
rospy.sleep(${params.sleep_time})\n`;
|
rospy.sleep(${params.sleep_time})\n`;
|
||||||
|
|
||||||
|
const NAVIGATE_GLOBAL_WAIT = () => `\ndef navigate_global_wait(lat, lon, z, speed=0.5):
|
||||||
|
res = navigate_global(lat=lat, lon=lon, z=z, yaw=float('inf'), speed=speed)
|
||||||
|
|
||||||
|
if not res.success:
|
||||||
|
raise Exception(res.message)
|
||||||
|
|
||||||
|
while not rospy.is_shutdown():
|
||||||
|
telem = get_telemetry(frame_id='navigate_target')
|
||||||
|
if math.sqrt(telem.x ** 2 + telem.y ** 2 + telem.z ** 2) < ${params.navigate_global_tolerance}:
|
||||||
|
return
|
||||||
|
rospy.sleep(${params.sleep_time})\n`;
|
||||||
|
|
||||||
const LAND_WAIT = () => `\ndef land_wait():
|
const LAND_WAIT = () => `\ndef land_wait():
|
||||||
land()
|
land()
|
||||||
while get_telemetry().armed:
|
while get_telemetry().armed:
|
||||||
@@ -68,6 +80,9 @@ function generateROSDefinitions() {
|
|||||||
if (rosDefinitions.offboard) {
|
if (rosDefinitions.offboard) {
|
||||||
code += `get_telemetry = rospy.ServiceProxy('get_telemetry', srv.GetTelemetry)\n`;
|
code += `get_telemetry = rospy.ServiceProxy('get_telemetry', srv.GetTelemetry)\n`;
|
||||||
code += `navigate = rospy.ServiceProxy('navigate', srv.Navigate)\n`;
|
code += `navigate = rospy.ServiceProxy('navigate', srv.Navigate)\n`;
|
||||||
|
if (rosDefinitions.navigateGlobal) {
|
||||||
|
code += `navigate_global = rospy.ServiceProxy('navigate_global', srv.NavigateGlobal)\n`;
|
||||||
|
}
|
||||||
if (rosDefinitions.setVelocity) {
|
if (rosDefinitions.setVelocity) {
|
||||||
code += `set_velocity = rospy.ServiceProxy('set_velocity', srv.SetVelocity)\n`;
|
code += `set_velocity = rospy.ServiceProxy('set_velocity', srv.SetVelocity)\n`;
|
||||||
}
|
}
|
||||||
@@ -94,6 +109,10 @@ function generateROSDefinitions() {
|
|||||||
Blockly.Python.definitions_['import_math'] = 'import math';
|
Blockly.Python.definitions_['import_math'] = 'import math';
|
||||||
code += NAVIGATE_WAIT();
|
code += NAVIGATE_WAIT();
|
||||||
}
|
}
|
||||||
|
if (rosDefinitions.navigateGlobalWait) {
|
||||||
|
Blockly.Python.definitions_['import_math'] = 'import math';
|
||||||
|
code += NAVIGATE_GLOBAL_WAIT();
|
||||||
|
}
|
||||||
if (rosDefinitions.landWait) {
|
if (rosDefinitions.landWait) {
|
||||||
code += LAND_WAIT();
|
code += LAND_WAIT();
|
||||||
}
|
}
|
||||||
@@ -161,14 +180,37 @@ Blockly.Python.navigate = function(block) {
|
|||||||
let x = Blockly.Python.valueToCode(block, 'X', Blockly.Python.ORDER_NONE);
|
let x = Blockly.Python.valueToCode(block, 'X', Blockly.Python.ORDER_NONE);
|
||||||
let y = Blockly.Python.valueToCode(block, 'Y', Blockly.Python.ORDER_NONE);
|
let y = Blockly.Python.valueToCode(block, 'Y', Blockly.Python.ORDER_NONE);
|
||||||
let z = Blockly.Python.valueToCode(block, 'Z', Blockly.Python.ORDER_NONE);
|
let z = Blockly.Python.valueToCode(block, 'Z', Blockly.Python.ORDER_NONE);
|
||||||
let frameId = buildFrameId(block);
|
let lat = Blockly.Python.valueToCode(block, 'LAT', Blockly.Python.ORDER_NONE);
|
||||||
|
let lon = Blockly.Python.valueToCode(block, 'LON', Blockly.Python.ORDER_NONE);
|
||||||
|
let wait = block.getFieldValue('WAIT') == 'TRUE';
|
||||||
|
let frameId = block.getFieldValue('FRAME_ID');
|
||||||
let speed = Blockly.Python.valueToCode(block, 'SPEED', Blockly.Python.ORDER_NONE);
|
let speed = Blockly.Python.valueToCode(block, 'SPEED', Blockly.Python.ORDER_NONE);
|
||||||
|
|
||||||
let params = [`x=${x}`, `y=${y}`, `z=${z}`, `frame_id=${frameId}`, `speed=${speed}`];
|
|
||||||
|
|
||||||
simpleOffboard();
|
simpleOffboard();
|
||||||
|
|
||||||
if (block.getFieldValue('WAIT') == 'TRUE') {
|
// global coordinates
|
||||||
|
if (frameId.startsWith('GLOBAL')) {
|
||||||
|
rosDefinitions.navigateGlobal = true;
|
||||||
|
simpleOffboard();
|
||||||
|
|
||||||
|
if (frameId == 'GLOBAL') {
|
||||||
|
z = `${z} + get_telemetry().alt - get_telemetry().z`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wait) {
|
||||||
|
rosDefinitions.navigateGlobalWait = true;
|
||||||
|
simpleOffboard();
|
||||||
|
return `navigate_global_wait(lat=${lat}, lon=${lon}, z=${z}, speed=${speed})\n`;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
return `navigate_global(lat=${lat}, lon=${lon}, z=${z}, yaw=float('inf'), speed=${speed})\n`;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
frameId = buildFrameId(block);
|
||||||
|
let params = [`x=${x}`, `y=${y}`, `z=${z}`, `frame_id=${frameId}`, `speed=${speed}`];
|
||||||
|
|
||||||
|
if (wait) {
|
||||||
rosDefinitions.navigateWait = true;
|
rosDefinitions.navigateWait = true;
|
||||||
simpleOffboard();
|
simpleOffboard();
|
||||||
|
|
||||||
@@ -181,6 +223,7 @@ Blockly.Python.navigate = function(block) {
|
|||||||
return `navigate(${params.join(', ')})\n`;
|
return `navigate(${params.join(', ')})\n`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Blockly.Python.set_velocity = function(block) {
|
Blockly.Python.set_velocity = function(block) {
|
||||||
let x = Blockly.Python.valueToCode(block, 'X', Blockly.Python.ORDER_NONE);
|
let x = Blockly.Python.valueToCode(block, 'X', Blockly.Python.ORDER_NONE);
|
||||||
@@ -315,6 +358,12 @@ Blockly.Python.get_attitude = function(block) {
|
|||||||
return [code, Blockly.Python.ORDER_FUNCTION_CALL];
|
return [code, Blockly.Python.ORDER_FUNCTION_CALL];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Blockly.Python.global_position = function(block) {
|
||||||
|
simpleOffboard();
|
||||||
|
var code = `get_telemetry().${block.getFieldValue('FIELD').toLowerCase()}`;
|
||||||
|
return [code, Blockly.Python.ORDER_FUNCTION_CALL];
|
||||||
|
}
|
||||||
|
|
||||||
Blockly.Python.distance = function(block) {
|
Blockly.Python.distance = function(block) {
|
||||||
rosDefinitions.distance = true;
|
rosDefinitions.distance = true;
|
||||||
simpleOffboard();
|
simpleOffboard();
|
||||||
@@ -391,7 +440,7 @@ Blockly.Python.set_led = function(block) {
|
|||||||
|
|
||||||
if (/^'(.*)'$/.test(colorCode)) { // is simple string
|
if (/^'(.*)'$/.test(colorCode)) { // is simple string
|
||||||
let color = parseColor(colorCode);
|
let color = parseColor(colorCode);
|
||||||
return `set_leds([LEDState(index=${index}, r=${color.r}, g=${color.g}, b=${color.b})])\n`;
|
return `set_leds([LEDState(index=int(${index}), r=${color.r}, g=${color.g}, b=${color.b})])\n`; // TODO: check for simple int
|
||||||
} else {
|
} else {
|
||||||
let parseColor = Blockly.Python.provideFunction_('parse_color', [PARSE_COLOR]);
|
let parseColor = Blockly.Python.provideFunction_('parse_color', [PARSE_COLOR]);
|
||||||
return `set_leds([LEDState(index=${index}, **${parseColor}(${colorCode}))])\n`;
|
return `set_leds([LEDState(index=${index}, **${parseColor}(${colorCode}))])\n`;
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ You may provide additional parameters for `spawn_drone.launch` as well:
|
|||||||
* `rangefinder` (*boolean*, default: *true*) - controls whether the drone will have a downward-facing laser rangefinder attached;
|
* `rangefinder` (*boolean*, default: *true*) - controls whether the drone will have a downward-facing laser rangefinder attached;
|
||||||
* `led` (*boolean*, default: *true*) - controls whether the drone will have a programmable LED strip (requires plugins from `clover_simulation`);
|
* `led` (*boolean*, default: *true*) - controls whether the drone will have a programmable LED strip (requires plugins from `clover_simulation`);
|
||||||
* `gps` (*boolean*, default: *true*) - controls whether the drone will have a simulated GPS attached (requires plugins from `sitl_gazebo`);
|
* `gps` (*boolean*, default: *true*) - controls whether the drone will have a simulated GPS attached (requires plugins from `sitl_gazebo`);
|
||||||
* `maintain_camera_rate` (*boolean*, default: *false*) - slow down the simultion to maintain camera publishing rate (internally changes the camera plugin from `libgazebo_ros_camera.so` to `libthrottling_camera.so` from [`clover_simulation`](../clover_simulation/README.md#throttling-camera-plugin-throttling_camera)).
|
* `maintain_camera_rate` (*boolean*, default: *false*) - slow down the simulation to maintain camera publishing rate (internally changes the camera plugin from `libgazebo_ros_camera.so` to `libthrottling_camera.so` from [`clover_simulation`](../clover_simulation/README.md#throttling-camera-plugin-throttling_camera)).
|
||||||
|
|
||||||
For example, in order to spawn a drone without a GPS module, you may use the following command:
|
For example, in order to spawn a drone without a GPS module, you may use the following command:
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<launch>
|
<launch>
|
||||||
<arg name="model" default="$(find clover_description)/urdf/drones/clover4.xacro"/>
|
<arg name="model" default="$(find clover_description)/urdf/clover/clover4.xacro"/>
|
||||||
|
|
||||||
<param name="robot_description" command="$(find xacro)/xacro.py $(arg model)"/>
|
<param name="robot_description" command="xacro $(arg model)"/>
|
||||||
|
|
||||||
<node name="rviz" pkg="rviz" type="rviz" required="true"/>
|
<node name="rviz" pkg="rviz" type="rviz" required="true"/>
|
||||||
</launch>
|
</launch>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<package format="2">
|
<package format="2">
|
||||||
<name>clover_description</name>
|
<name>clover_description</name>
|
||||||
<version>0.21.3</version>
|
<version>0.21.1</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>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0"?>
|
<?xml version="1.0"?>
|
||||||
<robot name="clover_camera" xmlns:xacro="http://ros.org/wiki/xacro">
|
<robot name="clover" xmlns:xacro="http://ros.org/wiki/xacro">
|
||||||
|
|
||||||
<xacro:arg name="main_camera" default="true"/>
|
<xacro:arg name="main_camera" default="true"/>
|
||||||
<xacro:arg name="rangefinder" default="true"/>
|
<xacro:arg name="rangefinder" default="true"/>
|
||||||
@@ -8,10 +8,10 @@
|
|||||||
<xacro:arg name="maintain_camera_rate" default="false"/>
|
<xacro:arg name="maintain_camera_rate" default="false"/>
|
||||||
<xacro:arg name="use_clover_physics" default="false"/>
|
<xacro:arg name="use_clover_physics" default="false"/>
|
||||||
|
|
||||||
<xacro:include filename="$(find clover_description)/urdf/clover/clover4_base.xacro" />
|
<xacro:include filename="clover4_base.xacro" />
|
||||||
<xacro:include filename="$(find clover_description)/urdf/sensors/rpi_cam.urdf.xacro"/>
|
<xacro:include filename="../sensors/rpi_cam.urdf.xacro"/>
|
||||||
<xacro:include filename="$(find clover_description)/urdf/sensors/distance_sensor.urdf.xacro"/>
|
<xacro:include filename="../sensors/distance_sensor.urdf.xacro"/>
|
||||||
<xacro:include filename="$(find clover_description)/urdf/leds/led_strip.xacro"/>
|
<xacro:include filename="../leds/led_strip.xacro"/>
|
||||||
|
|
||||||
<!-- Create camera plugin -->
|
<!-- Create camera plugin -->
|
||||||
<xacro:if value="$(arg main_camera)">
|
<xacro:if value="$(arg main_camera)">
|
||||||
@@ -36,11 +36,17 @@
|
|||||||
</xacro:if>
|
</xacro:if>
|
||||||
|
|
||||||
<xacro:if value="$(arg gps)">
|
<xacro:if value="$(arg gps)">
|
||||||
<!-- Instantiate gps plugin. -->
|
<gazebo>
|
||||||
<xacro:gps_plugin_macro
|
<include>
|
||||||
namespace="${namespace}"
|
<uri>model://gps</uri>
|
||||||
gps_noise="true"
|
<pose>0.1 0 0 0 0 0</pose>
|
||||||
/>
|
<name>gps0</name>
|
||||||
|
</include>
|
||||||
|
<joint name='gps0_joint' type='fixed'>
|
||||||
|
<child>gps0::link</child>
|
||||||
|
<parent>base_link</parent>
|
||||||
|
</joint>
|
||||||
|
</gazebo>
|
||||||
</xacro:if>
|
</xacro:if>
|
||||||
|
|
||||||
</robot>
|
</robot>
|
||||||
|
|||||||
@@ -1,40 +1,15 @@
|
|||||||
<?xml version="1.0"?>
|
<?xml version="1.0"?>
|
||||||
|
|
||||||
<robot name="clover" xmlns:xacro="http://ros.org/wiki/xacro">
|
<robot name="clover" xmlns:xacro="http://ros.org/wiki/xacro">
|
||||||
<!-- Properties that can be assigned at build time as arguments.
|
|
||||||
Is there a reason not to make all properties arguments?
|
|
||||||
-->
|
|
||||||
<xacro:arg name='name' default='iris' />
|
|
||||||
<xacro:arg name='mavlink_addr' default='INADDR_ANY' />
|
|
||||||
<xacro:arg name='mavlink_udp_port' default='14560' />
|
|
||||||
<xacro:arg name='mavlink_tcp_port' default='4560' />
|
|
||||||
<xacro:arg name='serial_enabled' default='false' />
|
|
||||||
<xacro:arg name='serial_device' default='/dev/ttyACM0' />
|
|
||||||
<xacro:arg name='baudrate' default='921600' />
|
|
||||||
<xacro:arg name='qgc_addr' default='INADDR_ANY' />
|
|
||||||
<xacro:arg name='qgc_udp_port' default='14550' />
|
|
||||||
<xacro:arg name='sdk_addr' default='INADDR_ANY' />
|
|
||||||
<xacro:arg name='sdk_udp_port' default='14540' />
|
|
||||||
<xacro:arg name='hil_mode' default='false' />
|
|
||||||
<xacro:arg name='hil_state_level' default='false' />
|
|
||||||
<xacro:arg name='send_vision_estimation' default='false' />
|
|
||||||
<xacro:arg name='send_odometry' default='false' />
|
|
||||||
<xacro:arg name='enable_lockstep' default='true' />
|
|
||||||
<xacro:arg name='use_tcp' default='true' />
|
|
||||||
<xacro:arg name='vehicle_is_tailsitter' default='false' />
|
|
||||||
<xacro:arg name='visual_material' default='DarkGrey' />
|
|
||||||
<xacro:arg name='enable_mavlink_interface' default='true' />
|
|
||||||
<xacro:arg name='enable_wind' default='false' />
|
<xacro:arg name='enable_wind' default='false' />
|
||||||
<!-- The following causes segfault with multiple vehicles if defaults to true!!! -->
|
|
||||||
<xacro:arg name='enable_ground_truth' default='false' />
|
|
||||||
<xacro:arg name='enable_logging' default='false' />
|
<xacro:arg name='enable_logging' default='false' />
|
||||||
<xacro:arg name='log_file' default='iris' />
|
<xacro:arg name='log_file' default='clover' />
|
||||||
|
|
||||||
<!-- macros for gazebo plugins, sensors -->
|
<!-- macros for gazebo plugins, sensors -->
|
||||||
<xacro:include filename="$(find clover_description)/urdf/component_snippets.urdf.xacro" />
|
<xacro:include filename="../component_snippets.urdf.xacro" />
|
||||||
|
|
||||||
<!-- Instantiate iris "mechanics" -->
|
<!-- Instantiate iris "mechanics" -->
|
||||||
<xacro:include filename="$(find clover_description)/urdf/clover/clover4_physics.xacro" />
|
<xacro:include filename="clover4_physics.xacro" />
|
||||||
|
|
||||||
<xacro:if value="$(arg enable_wind)">
|
<xacro:if value="$(arg enable_wind)">
|
||||||
<xacro:wind_plugin_macro
|
<xacro:wind_plugin_macro
|
||||||
@@ -49,126 +24,8 @@
|
|||||||
/>
|
/>
|
||||||
</xacro:if>
|
</xacro:if>
|
||||||
|
|
||||||
<!-- Instantiate magnetometer plugin. -->
|
<!-- Gazebo plugins -->
|
||||||
<xacro:magnetometer_plugin_macro
|
<xacro:include filename="$(find clover_description)/urdf/clover/clover4_gazebo.xacro" />
|
||||||
namespace="${namespace}"
|
|
||||||
pub_rate="20"
|
|
||||||
noise_density="0.0004"
|
|
||||||
random_walk="0.0000064"
|
|
||||||
bias_correlation_time="600"
|
|
||||||
mag_topic="/mag"
|
|
||||||
>
|
|
||||||
</xacro:magnetometer_plugin_macro>
|
|
||||||
|
|
||||||
<!-- Instantiate barometer plugin. -->
|
|
||||||
<xacro:barometer_plugin_macro
|
|
||||||
namespace="${namespace}"
|
|
||||||
pub_rate="10"
|
|
||||||
baro_topic="/baro"
|
|
||||||
>
|
|
||||||
</xacro:barometer_plugin_macro>
|
|
||||||
|
|
||||||
<xacro:if value="$(arg enable_mavlink_interface)">
|
|
||||||
<!-- Instantiate mavlink telemetry interface. -->
|
|
||||||
<xacro:mavlink_interface_macro
|
|
||||||
namespace="${namespace}"
|
|
||||||
imu_sub_topic="/imu"
|
|
||||||
gps_sub_topic="/gps"
|
|
||||||
mag_sub_topic="/mag"
|
|
||||||
baro_sub_topic="/baro"
|
|
||||||
mavlink_addr="$(arg mavlink_addr)"
|
|
||||||
mavlink_udp_port="$(arg mavlink_udp_port)"
|
|
||||||
mavlink_tcp_port="$(arg mavlink_tcp_port)"
|
|
||||||
serial_enabled="$(arg serial_enabled)"
|
|
||||||
serial_device="$(arg serial_device)"
|
|
||||||
baudrate="$(arg baudrate)"
|
|
||||||
qgc_addr="$(arg qgc_addr)"
|
|
||||||
qgc_udp_port="$(arg qgc_udp_port)"
|
|
||||||
sdk_addr="$(arg sdk_addr)"
|
|
||||||
sdk_udp_port="$(arg sdk_udp_port)"
|
|
||||||
hil_mode="$(arg hil_mode)"
|
|
||||||
hil_state_level="$(arg hil_state_level)"
|
|
||||||
vehicle_is_tailsitter="$(arg vehicle_is_tailsitter)"
|
|
||||||
send_vision_estimation="$(arg send_vision_estimation)"
|
|
||||||
send_odometry="$(arg send_odometry)"
|
|
||||||
enable_lockstep="$(arg enable_lockstep)"
|
|
||||||
use_tcp="$(arg use_tcp)"
|
|
||||||
>
|
|
||||||
</xacro:mavlink_interface_macro>
|
|
||||||
</xacro:if>
|
|
||||||
|
|
||||||
<!-- Mount an ADIS16448 IMU. -->
|
|
||||||
<xacro:imu_plugin_macro
|
|
||||||
namespace="${namespace}"
|
|
||||||
imu_suffix=""
|
|
||||||
parent_link="base_link"
|
|
||||||
imu_topic="/imu"
|
|
||||||
mass_imu_sensor="0.015"
|
|
||||||
gyroscope_noise_density="0.0003394"
|
|
||||||
gyroscopoe_random_walk="0.000038785"
|
|
||||||
gyroscope_bias_correlation_time="1000.0"
|
|
||||||
gyroscope_turn_on_bias_sigma="0.0087"
|
|
||||||
accelerometer_noise_density="0.004"
|
|
||||||
accelerometer_random_walk="0.006"
|
|
||||||
accelerometer_bias_correlation_time="300.0"
|
|
||||||
accelerometer_turn_on_bias_sigma="0.1960"
|
|
||||||
>
|
|
||||||
<inertia ixx="0.00001" ixy="0.0" ixz="0.0" iyy="0.00001" iyz="0.0" izz="0.00001" />
|
|
||||||
<origin xyz="0 0 0" rpy="0 0 0" />
|
|
||||||
</xacro:imu_plugin_macro>
|
|
||||||
|
|
||||||
<xacro:if value="$(arg enable_ground_truth)">
|
|
||||||
<!-- Mount an IMU providing ground truth. -->
|
|
||||||
<xacro:imu_plugin_macro
|
|
||||||
namespace="${namespace}"
|
|
||||||
imu_suffix="gt"
|
|
||||||
parent_link="base_link"
|
|
||||||
imu_topic="ground_truth/imu"
|
|
||||||
mass_imu_sensor="0.00001"
|
|
||||||
gyroscope_noise_density="0.0"
|
|
||||||
gyroscopoe_random_walk="0.0"
|
|
||||||
gyroscope_bias_correlation_time="1000.0"
|
|
||||||
gyroscope_turn_on_bias_sigma="0.0"
|
|
||||||
accelerometer_noise_density="0.0"
|
|
||||||
accelerometer_random_walk="0.0"
|
|
||||||
accelerometer_bias_correlation_time="300.0"
|
|
||||||
accelerometer_turn_on_bias_sigma="0.0"
|
|
||||||
>
|
|
||||||
<inertia ixx="0.00001" ixy="0.0" ixz="0.0" iyy="0.00001" iyz="0.0" izz="0.00001" />
|
|
||||||
<origin xyz="0 0 0" rpy="0 0 0" />
|
|
||||||
</xacro:imu_plugin_macro>
|
|
||||||
|
|
||||||
<!-- Mount a generic odometry sensor providing ground truth. -->
|
|
||||||
<xacro:odometry_plugin_macro
|
|
||||||
namespace="${namespace}/ground_truth"
|
|
||||||
odometry_sensor_suffix="gt"
|
|
||||||
parent_link="base_link"
|
|
||||||
pose_topic="pose"
|
|
||||||
pose_with_covariance_topic="pose_with_covariance"
|
|
||||||
position_topic="position"
|
|
||||||
transform_topic="transform"
|
|
||||||
odometry_topic="odometry"
|
|
||||||
parent_frame_id="world"
|
|
||||||
mass_odometry_sensor="0.00001"
|
|
||||||
measurement_divisor="1"
|
|
||||||
measurement_delay="0"
|
|
||||||
unknown_delay="0.0"
|
|
||||||
noise_normal_position="0 0 0"
|
|
||||||
noise_normal_quaternion="0 0 0"
|
|
||||||
noise_normal_linear_velocity="0 0 0"
|
|
||||||
noise_normal_angular_velocity="0 0 0"
|
|
||||||
noise_uniform_position="0 0 0"
|
|
||||||
noise_uniform_quaternion="0 0 0"
|
|
||||||
noise_uniform_linear_velocity="0 0 0"
|
|
||||||
noise_uniform_angular_velocity="0 0 0"
|
|
||||||
enable_odometry_map="false"
|
|
||||||
odometry_map=""
|
|
||||||
image_scale=""
|
|
||||||
>
|
|
||||||
<inertia ixx="0.00001" ixy="0.0" ixz="0.0" iyy="0.00001" iyz="0.0" izz="0.00001" /> <!-- [kg.m^2] [kg.m^2] [kg.m^2] [kg.m^2] [kg.m^2] [kg.m^2] -->
|
|
||||||
<origin xyz="0.0 0.0 0.0" rpy="0.0 0.0 0.0" />
|
|
||||||
</xacro:odometry_plugin_macro>
|
|
||||||
</xacro:if>
|
|
||||||
|
|
||||||
<xacro:if value="$(arg enable_logging)">
|
<xacro:if value="$(arg enable_logging)">
|
||||||
<!-- Instantiate a logger -->
|
<!-- Instantiate a logger -->
|
||||||
|
|||||||
183
clover_description/urdf/clover/clover4_gazebo.xacro
Normal file
183
clover_description/urdf/clover/clover4_gazebo.xacro
Normal file
@@ -0,0 +1,183 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
|
||||||
|
<!-- Reference: https://github.com/PX4/PX4-SITL_gazebo/blob/7505aee97d2d3112fb2bd95198946345c6fa0b07/models/iris/iris.sdf.jinja#L430 -->
|
||||||
|
|
||||||
|
<robot xmlns:xacro="http://ros.org/wiki/xacro">
|
||||||
|
|
||||||
|
<!-- IMU link -->
|
||||||
|
<link name="/imu_link">
|
||||||
|
<inertial>
|
||||||
|
<inertia ixx="0.00001" ixy="0.0" ixz="0.0" iyy="0.00001" iyz="0.0" izz="0.00001"/>
|
||||||
|
<mass value="0.015"/>
|
||||||
|
<!-- [kg] -->
|
||||||
|
<origin rpy="0 0 0" xyz="0 0 0"/>
|
||||||
|
</inertial>
|
||||||
|
</link>
|
||||||
|
|
||||||
|
<!-- IMU joint -->
|
||||||
|
<joint name="/imu_joint" type="fixed">
|
||||||
|
<origin rpy="0 0 0" xyz="0 0 0"/>
|
||||||
|
<parent link="base_link"/>
|
||||||
|
<child link="/imu_link"/>
|
||||||
|
<!-- <limit upper="0" lower="0" effort="0" velocity="0" /> -->
|
||||||
|
</joint>
|
||||||
|
|
||||||
|
<gazebo reference="/imu_joint">
|
||||||
|
<disableFixedJointLumping>true</disableFixedJointLumping>
|
||||||
|
<preserveFixedJoint>true</preserveFixedJoint>
|
||||||
|
</gazebo>
|
||||||
|
|
||||||
|
<gazebo>
|
||||||
|
<plugin name='groundtruth_plugin' filename='libgazebo_groundtruth_plugin.so'>
|
||||||
|
<robotNamespace/>
|
||||||
|
</plugin>
|
||||||
|
<plugin name='magnetometer_plugin' filename='libgazebo_magnetometer_plugin.so'>
|
||||||
|
<robotNamespace/>
|
||||||
|
<pubRate>100</pubRate>
|
||||||
|
<noiseDensity>0.0004</noiseDensity>
|
||||||
|
<randomWalk>6.4e-06</randomWalk>
|
||||||
|
<biasCorrelationTime>600</biasCorrelationTime>
|
||||||
|
<magTopic>/mag</magTopic>
|
||||||
|
</plugin>
|
||||||
|
<plugin name='barometer_plugin' filename='libgazebo_barometer_plugin.so'>
|
||||||
|
<robotNamespace/>
|
||||||
|
<pubRate>50</pubRate>
|
||||||
|
<baroTopic>/baro</baroTopic>
|
||||||
|
<baroDriftPaPerSec>0</baroDriftPaPerSec>
|
||||||
|
</plugin>
|
||||||
|
<plugin name='mavlink_interface' filename='libgazebo_mavlink_interface.so'>
|
||||||
|
<robotNamespace/>
|
||||||
|
<imuSubTopic>/imu</imuSubTopic>
|
||||||
|
<magSubTopic>/mag</magSubTopic>
|
||||||
|
<baroSubTopic>/baro</baroSubTopic>
|
||||||
|
<mavlink_addr>INADDR_ANY</mavlink_addr>
|
||||||
|
<mavlink_tcp_port>4560</mavlink_tcp_port>
|
||||||
|
<mavlink_udp_port>14560</mavlink_udp_port>
|
||||||
|
<serialEnabled>false</serialEnabled>
|
||||||
|
<serialDevice>/dev/ttyACM0</serialDevice>
|
||||||
|
<baudRate>921600</baudRate>
|
||||||
|
<qgc_addr>INADDR_ANY</qgc_addr>
|
||||||
|
<qgc_udp_port>14550</qgc_udp_port>
|
||||||
|
<sdk_addr>INADDR_ANY</sdk_addr>
|
||||||
|
<sdk_udp_port>14540</sdk_udp_port>
|
||||||
|
<hil_mode>false</hil_mode>
|
||||||
|
<hil_state_level>0</hil_state_level>
|
||||||
|
<send_vision_estimation>0</send_vision_estimation>
|
||||||
|
<send_odometry>1</send_odometry>
|
||||||
|
<enable_lockstep>1</enable_lockstep>
|
||||||
|
<use_tcp>1</use_tcp>
|
||||||
|
<motorSpeedCommandPubTopic>/gazebo/command/motor_speed</motorSpeedCommandPubTopic>
|
||||||
|
<control_channels>
|
||||||
|
<channel name='rotor1'>
|
||||||
|
<input_index>0</input_index>
|
||||||
|
<input_offset>0</input_offset>
|
||||||
|
<input_scaling>1000</input_scaling>
|
||||||
|
<zero_position_disarmed>0</zero_position_disarmed>
|
||||||
|
<zero_position_armed>100</zero_position_armed>
|
||||||
|
<joint_control_type>velocity</joint_control_type>
|
||||||
|
</channel>
|
||||||
|
<channel name='rotor2'>
|
||||||
|
<input_index>1</input_index>
|
||||||
|
<input_offset>0</input_offset>
|
||||||
|
<input_scaling>1000</input_scaling>
|
||||||
|
<zero_position_disarmed>0</zero_position_disarmed>
|
||||||
|
<zero_position_armed>100</zero_position_armed>
|
||||||
|
<joint_control_type>velocity</joint_control_type>
|
||||||
|
</channel>
|
||||||
|
<channel name='rotor3'>
|
||||||
|
<input_index>2</input_index>
|
||||||
|
<input_offset>0</input_offset>
|
||||||
|
<input_scaling>1000</input_scaling>
|
||||||
|
<zero_position_disarmed>0</zero_position_disarmed>
|
||||||
|
<zero_position_armed>100</zero_position_armed>
|
||||||
|
<joint_control_type>velocity</joint_control_type>
|
||||||
|
</channel>
|
||||||
|
<channel name='rotor4'>
|
||||||
|
<input_index>3</input_index>
|
||||||
|
<input_offset>0</input_offset>
|
||||||
|
<input_scaling>1000</input_scaling>
|
||||||
|
<zero_position_disarmed>0</zero_position_disarmed>
|
||||||
|
<zero_position_armed>100</zero_position_armed>
|
||||||
|
<joint_control_type>velocity</joint_control_type>
|
||||||
|
</channel>
|
||||||
|
<channel name='rotor5'>
|
||||||
|
<input_index>4</input_index>
|
||||||
|
<input_offset>1</input_offset>
|
||||||
|
<input_scaling>324.6</input_scaling>
|
||||||
|
<zero_position_disarmed>0</zero_position_disarmed>
|
||||||
|
<zero_position_armed>0</zero_position_armed>
|
||||||
|
<joint_control_type>velocity</joint_control_type>
|
||||||
|
<joint_control_pid>
|
||||||
|
<p>0.1</p>
|
||||||
|
<i>0</i>
|
||||||
|
<d>0</d>
|
||||||
|
<iMax>0.0</iMax>
|
||||||
|
<iMin>0.0</iMin>
|
||||||
|
<cmdMax>2</cmdMax>
|
||||||
|
<cmdMin>-2</cmdMin>
|
||||||
|
</joint_control_pid>
|
||||||
|
<joint_name>zephyr_delta_wing::propeller_joint</joint_name>
|
||||||
|
</channel>
|
||||||
|
<channel name='rotor6'>
|
||||||
|
<input_index>5</input_index>
|
||||||
|
<input_offset>0</input_offset>
|
||||||
|
<input_scaling>0.524</input_scaling>
|
||||||
|
<zero_position_disarmed>0</zero_position_disarmed>
|
||||||
|
<zero_position_armed>0</zero_position_armed>
|
||||||
|
<joint_control_type>position</joint_control_type>
|
||||||
|
<joint_name>zephyr_delta_wing::flap_left_joint</joint_name>
|
||||||
|
<joint_control_pid>
|
||||||
|
<p>10.0</p>
|
||||||
|
<i>0</i>
|
||||||
|
<d>0</d>
|
||||||
|
<iMax>0</iMax>
|
||||||
|
<iMin>0</iMin>
|
||||||
|
<cmdMax>20</cmdMax>
|
||||||
|
<cmdMin>-20</cmdMin>
|
||||||
|
</joint_control_pid>
|
||||||
|
</channel>
|
||||||
|
<channel name='rotor7'>
|
||||||
|
<input_index>6</input_index>
|
||||||
|
<input_offset>0</input_offset>
|
||||||
|
<input_scaling>0.524</input_scaling>
|
||||||
|
<zero_position_disarmed>0</zero_position_disarmed>
|
||||||
|
<zero_position_armed>0</zero_position_armed>
|
||||||
|
<joint_control_type>position</joint_control_type>
|
||||||
|
<joint_name>zephyr_delta_wing::flap_right_joint</joint_name>
|
||||||
|
<joint_control_pid>
|
||||||
|
<p>10.0</p>
|
||||||
|
<i>0</i>
|
||||||
|
<d>0</d>
|
||||||
|
<iMax>0</iMax>
|
||||||
|
<iMin>0</iMin>
|
||||||
|
<cmdMax>20</cmdMax>
|
||||||
|
<cmdMin>-20</cmdMin>
|
||||||
|
</joint_control_pid>
|
||||||
|
</channel>
|
||||||
|
<channel name='rotor8'>
|
||||||
|
<input_index>7</input_index>
|
||||||
|
<input_offset>0</input_offset>
|
||||||
|
<input_scaling>0.524</input_scaling>
|
||||||
|
<zero_position_disarmed>0</zero_position_disarmed>
|
||||||
|
<zero_position_armed>0</zero_position_armed>
|
||||||
|
<joint_control_type>position</joint_control_type>
|
||||||
|
</channel>
|
||||||
|
</control_channels>
|
||||||
|
</plugin>
|
||||||
|
<static>0</static>
|
||||||
|
<plugin name='rotors_gazebo_imu_plugin' filename='libgazebo_imu_plugin.so'>
|
||||||
|
<robotNamespace/>
|
||||||
|
<linkName>/imu_link</linkName>
|
||||||
|
<imuTopic>/imu</imuTopic>
|
||||||
|
<gyroscopeNoiseDensity>0.00018665</gyroscopeNoiseDensity>
|
||||||
|
<gyroscopeRandomWalk>3.8785e-05</gyroscopeRandomWalk>
|
||||||
|
<gyroscopeBiasCorrelationTime>1000.0</gyroscopeBiasCorrelationTime>
|
||||||
|
<gyroscopeTurnOnBiasSigma>0.0087</gyroscopeTurnOnBiasSigma>
|
||||||
|
<accelerometerNoiseDensity>0.00186</accelerometerNoiseDensity>
|
||||||
|
<accelerometerRandomWalk>0.006</accelerometerRandomWalk>
|
||||||
|
<accelerometerBiasCorrelationTime>300.0</accelerometerBiasCorrelationTime>
|
||||||
|
<accelerometerTurnOnBiasSigma>0.196</accelerometerTurnOnBiasSigma>
|
||||||
|
</plugin>
|
||||||
|
</gazebo>
|
||||||
|
|
||||||
|
</robot>
|
||||||
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<robot name="iris" xmlns:xacro="http://ros.org/wiki/xacro">
|
<robot name="clover" xmlns:xacro="http://ros.org/wiki/xacro">
|
||||||
<!-- Properties -->
|
<!-- Properties -->
|
||||||
<xacro:property name="namespace" value="" />
|
<xacro:property name="namespace" value="" />
|
||||||
<xacro:property name="rotor_velocity_slowdown_sim" value="10" />
|
<xacro:property name="rotor_velocity_slowdown_sim" value="10" />
|
||||||
@@ -84,7 +84,7 @@
|
|||||||
</xacro:unless>
|
</xacro:unless>
|
||||||
|
|
||||||
<!-- Included URDF Files -->
|
<!-- Included URDF Files -->
|
||||||
<xacro:include filename="$(find clover_description)/urdf/clover/clover4_macros.xacro" />
|
<xacro:include filename="clover4_macros.xacro" />
|
||||||
|
|
||||||
<!-- Instantiate multirotor_base_macro once -->
|
<!-- Instantiate multirotor_base_macro once -->
|
||||||
<xacro:clover4_base_macro
|
<xacro:clover4_base_macro
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<robot name="rpi_camera" xmlns:xacro="http://ros.org/wiki/xacro">
|
<robot name="rpi_camera" xmlns:xacro="http://ros.org/wiki/xacro">
|
||||||
<xacro:include filename="$(find clover_description)/urdf/camera_sensor.urdf.xacro"/>
|
<xacro:include filename="../camera_sensor.urdf.xacro"/>
|
||||||
|
|
||||||
<xacro:macro name="distance_sensor" params="name:=lidar_vl53l1x parent x:=0 y:=0 z:=0 roll:=0 pitch:=0 yaw:=0 range_min:=0.01 range_max:=4.0 resolution:=0.01 rate:=10">
|
<xacro:macro name="distance_sensor" params="name:=lidar_vl53l1x parent x:=0 y:=0 z:=0 roll:=0 pitch:=0 yaw:=0 range_min:=0.01 range_max:=4.0 resolution:=0.01 rate:=10">
|
||||||
<joint name="${name}_joint" type="fixed">
|
<joint name="${name}_joint" type="fixed">
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<robot name="pinhole_camera" xmlns:xacro="http://ros.org/wiki/xacro">
|
<robot name="pinhole_camera" xmlns:xacro="http://ros.org/wiki/xacro">
|
||||||
<xacro:include filename="$(find clover_description)/urdf/camera_sensor.urdf.xacro"/>
|
<xacro:include filename="../camera_sensor.urdf.xacro"/>
|
||||||
<xacro:camera_sensor name="main_camera" width="640" height="480" rate="30" horizontal_fov="${120.0*pi/180.0}"/>
|
<xacro:camera_sensor name="main_camera" width="640" height="480" rate="30" horizontal_fov="${120.0*pi/180.0}"/>
|
||||||
</robot>
|
</robot>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<robot name="rpi_camera" xmlns:xacro="http://ros.org/wiki/xacro">
|
<robot name="rpi_camera" xmlns:xacro="http://ros.org/wiki/xacro">
|
||||||
<xacro:include filename="$(find clover_description)/urdf/camera_sensor.urdf.xacro"/>
|
<xacro:include filename="../camera_sensor.urdf.xacro"/>
|
||||||
|
|
||||||
<xacro:macro name="rpi_cam" params="name:=rpi_cam parent x:=0 y:=0 z:=0 roll:=0 pitch:=0 yaw:=0 width:=320 height:=240 rate:=40 do_throttling:=false">
|
<xacro:macro name="rpi_cam" params="name:=rpi_cam parent x:=0 y:=0 z:=0 roll:=0 pitch:=0 yaw:=0 width:=320 height:=240 rate:=40 do_throttling:=false">
|
||||||
<joint name="${name}_joint" type="fixed">
|
<joint name="${name}_joint" type="fixed">
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ A visual Gazebo plugin is used for the LED strip. An example of the plugin usage
|
|||||||
The plugin accepts the following parameters during instantiation:
|
The plugin accepts the following parameters during instantiation:
|
||||||
|
|
||||||
* `robotNamespace` (*string*, default: "") - a ROS namespace for the plugin;
|
* `robotNamespace` (*string*, default: "") - a ROS namespace for the plugin;
|
||||||
* `ledCount` (*integer*, required) - total numer of LEDs in a strip.
|
* `ledCount` (*integer*, required) - total number of LEDs in a strip.
|
||||||
|
|
||||||
The plugin will provide the following service:
|
The plugin will provide the following service:
|
||||||
|
|
||||||
|
|||||||
36
clover_simulation/airframes/4500_clover
Normal file
36
clover_simulation/airframes/4500_clover
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
#
|
||||||
|
# @name COEX Clover Simulator
|
||||||
|
#
|
||||||
|
# @type Quadrotor X
|
||||||
|
#
|
||||||
|
|
||||||
|
. ${R}etc/init.d-posix/airframes/10016_iris # base on iris
|
||||||
|
|
||||||
|
param set ATT_W_EXT_HDG 0.5
|
||||||
|
param set ATT_EXT_HDG_M 1
|
||||||
|
|
||||||
|
param set COM_DISARM_LAND 1.0
|
||||||
|
|
||||||
|
param set LPE_FLW_SCALE 1.0
|
||||||
|
param set LPE_FLW_R 0.2
|
||||||
|
param set LPE_FLW_RR 0.0
|
||||||
|
param set LPE_FLW_QMIN 10
|
||||||
|
param set LPE_VIS_DELAY 0.0
|
||||||
|
param set LPE_VIS_Z 0.1
|
||||||
|
param set LPE_FUSION 86
|
||||||
|
|
||||||
|
param set SENS_FLOW_ROT 0
|
||||||
|
param set SENS_FLOW_MINHGT 0.01
|
||||||
|
param set SENS_FLOW_MAXHGT 4.0
|
||||||
|
param set SENS_FLOW_MAXR 10.0
|
||||||
|
|
||||||
|
param set EKF2_AID_MASK 27 # gps + flow + vis pos + vis yaw
|
||||||
|
param set EKF2_OF_DELAY 0
|
||||||
|
param set EKF2_OF_QMIN 10
|
||||||
|
param set EKF2_OF_N_MIN 0.05
|
||||||
|
param set EKF2_OF_N_MAX 0.2
|
||||||
|
param set EKF2_HGT_MODE 2
|
||||||
|
param set EKF2_EVA_NOISE 0.1
|
||||||
|
param set EKF2_EVP_NOISE 0.1
|
||||||
|
param set EKF2_EV_DELAY 0
|
||||||
@@ -8,12 +8,13 @@
|
|||||||
<arg name="led" default="true"/> <!-- Simulated LED strip, ws281x-like -->
|
<arg name="led" default="true"/> <!-- Simulated LED strip, ws281x-like -->
|
||||||
<arg name="gps" default="false"/> <!--Simulated GPS module -->
|
<arg name="gps" default="false"/> <!--Simulated GPS module -->
|
||||||
<arg name="use_clover_physics" default="false"/> <!-- Use inertial parameters from CAD models -->
|
<arg name="use_clover_physics" default="false"/> <!-- Use inertial parameters from CAD models -->
|
||||||
|
<arg name="gui" default="true"/> <!-- Run Gazebo with GUI -->
|
||||||
|
|
||||||
<!-- Gazebo instance -->
|
<!-- Gazebo instance -->
|
||||||
<include file="$(find gazebo_ros)/launch/empty_world.launch" if="$(eval type == 'gazebo')">
|
<include file="$(find gazebo_ros)/launch/empty_world.launch" if="$(eval type == 'gazebo')">
|
||||||
<!-- Workaround for crashes in VMware -->
|
<!-- Workaround for crashes in VMware -->
|
||||||
<env name="SVGA_VGPU10" value="0"/>
|
<env name="SVGA_VGPU10" value="0"/>
|
||||||
<arg name="gui" value="true"/>
|
<arg name="gui" value="$(arg gui)"/>
|
||||||
<arg name="world_name" value="$(find clover_simulation)/resources/worlds/clover_aruco.world"/>
|
<arg name="world_name" value="$(find clover_simulation)/resources/worlds/clover_aruco.world"/>
|
||||||
<arg name="verbose" value="true"/>
|
<arg name="verbose" value="true"/>
|
||||||
</include>
|
</include>
|
||||||
@@ -33,7 +34,9 @@
|
|||||||
<arg name="use_clover_physics" value="$(arg use_clover_physics)"/>
|
<arg name="use_clover_physics" value="$(arg use_clover_physics)"/>
|
||||||
</include>
|
</include>
|
||||||
|
|
||||||
<node name="jmavsim" pkg="px4" type="jmavsim_run.sh" output="screen" if="$(eval type == 'jmavsim')"/>
|
<node name="jmavsim" pkg="px4" type="jmavsim_run.sh" output="screen" if="$(eval type == 'jmavsim')">
|
||||||
|
<env name="HEADLESS" value="1" if="$(eval not gui)"/>
|
||||||
|
</node>
|
||||||
|
|
||||||
<param name="use_sim_time" value="false" if="$(eval type == 'jmavsim')"/>
|
<param name="use_sim_time" value="false" if="$(eval type == 'jmavsim')"/>
|
||||||
|
|
||||||
@@ -43,10 +46,10 @@
|
|||||||
<arg name="fcu_conn" value="sitl"/>
|
<arg name="fcu_conn" value="sitl"/>
|
||||||
<arg name="fcu_ip" value="127.0.0.1"/>
|
<arg name="fcu_ip" value="127.0.0.1"/>
|
||||||
<arg name="gcs_bridge" value=""/>
|
<arg name="gcs_bridge" value=""/>
|
||||||
<arg name="rc" default="false"/>
|
|
||||||
<arg name="web_video_server" default="false" if="$(eval type == 'jmavsim')"/>
|
<arg name="web_video_server" default="false" if="$(eval type == 'jmavsim')"/>
|
||||||
<arg name="main_camera" default="false" if="$(eval type == 'jmavsim')"/>
|
<arg name="main_camera" default="false" if="$(eval type == 'jmavsim')"/>
|
||||||
<arg name="aruco" default="false" if="$(eval type == 'jmavsim')"/>
|
<arg name="aruco" default="false" if="$(eval type == 'jmavsim')"/>
|
||||||
|
<arg name="optical_flow" default="false" if="$(eval type == 'jmavsim')"/>
|
||||||
<arg name="led" default="false" if="$(eval type == 'jmavsim')"/>
|
<arg name="led" default="false" if="$(eval type == 'jmavsim')"/>
|
||||||
</include>
|
</include>
|
||||||
</launch>
|
</launch>
|
||||||
|
|||||||
33
clover_simulation/models/camera/camera.sdf
Normal file
33
clover_simulation/models/camera/camera.sdf
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
<sdf version="1.5">
|
||||||
|
<model name="camera">
|
||||||
|
<static>true</static>
|
||||||
|
<link name='camera_link'>
|
||||||
|
<pose>0 0 0 0 0 0</pose>
|
||||||
|
<sensor name='camera' type='camera'>
|
||||||
|
<camera>
|
||||||
|
<horizontal_fov>2.0944</horizontal_fov>
|
||||||
|
<image>
|
||||||
|
<format>B8G8R8</format>
|
||||||
|
<width>800</width>
|
||||||
|
<height>600</height>
|
||||||
|
</image>
|
||||||
|
<clip>
|
||||||
|
<near>0.02</near>
|
||||||
|
<far>300</far>
|
||||||
|
</clip>
|
||||||
|
</camera>
|
||||||
|
<always_on>1</always_on>
|
||||||
|
<update_rate>20</update_rate>
|
||||||
|
<visualize>1</visualize>
|
||||||
|
<plugin name='camera_plugin' filename='libgazebo_ros_camera.so'>
|
||||||
|
<alwaysOn>1</alwaysOn>
|
||||||
|
<imageTopicName>image_raw</imageTopicName>
|
||||||
|
<cameraTopicName>camera_info</cameraTopicName>
|
||||||
|
<cameraName>camera</cameraName>
|
||||||
|
<frameName>/camera</frameName>
|
||||||
|
</plugin>
|
||||||
|
</sensor>
|
||||||
|
</link>
|
||||||
|
</model>
|
||||||
|
</sdf>
|
||||||
13
clover_simulation/models/camera/model.config
Normal file
13
clover_simulation/models/camera/model.config
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
<model>
|
||||||
|
<name>Camera</name>
|
||||||
|
<version>1.0</version>
|
||||||
|
<sdf version="1.5">camera.sdf</sdf>
|
||||||
|
<author>
|
||||||
|
<name>Oleg Kalachev</name>
|
||||||
|
<email>okalachev@gmail.com</email>
|
||||||
|
</author>
|
||||||
|
<description>
|
||||||
|
External camera
|
||||||
|
</description>
|
||||||
|
</model>
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<package format="2">
|
<package format="2">
|
||||||
<name>clover_simulation</name>
|
<name>clover_simulation</name>
|
||||||
<version>0.21.3</version>
|
<version>0.21.1</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>
|
||||||
|
|||||||
@@ -88,6 +88,6 @@ def aruco_gen():
|
|||||||
off_x + marker.x, off_y + marker.y, off_z + marker.z,
|
off_x + marker.x, off_y + marker.y, off_z + marker.z,
|
||||||
marker.roll, marker.pitch, marker.yaw)
|
marker.roll, marker.pitch, marker.yaw)
|
||||||
|
|
||||||
output = open(source_world, 'w') if inplace else stdout
|
output = open(source_world, 'wb') if inplace else stdout
|
||||||
|
|
||||||
save_world(world_tree, output)
|
save_world(world_tree, output)
|
||||||
|
|||||||
@@ -41,4 +41,4 @@ def save_world(world, file):
|
|||||||
'''
|
'''
|
||||||
Save the world to file-like object
|
Save the world to file-like object
|
||||||
'''
|
'''
|
||||||
return world.write(file)
|
return world.write(file, encoding='unicode')
|
||||||
|
|||||||
BIN
docs/assets/clover42-main-margin.png
Normal file
BIN
docs/assets/clover42-main-margin.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 209 KiB |
43
docs/assets/copterhack2022.svg
Normal file
43
docs/assets/copterhack2022.svg
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 23.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<svg version="1.1" id="Слой_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
viewBox="0 0 566.93 226.77" style="enable-background:new 0 0 566.93 226.77;" xml:space="preserve">
|
||||||
|
<style type="text/css">
|
||||||
|
.st0{fill:#3679BC;}
|
||||||
|
.st1{fill:#27B4AA;}
|
||||||
|
</style>
|
||||||
|
<g>
|
||||||
|
<polygon class="st0" points="356.5,37.35 287.97,37.35 287.97,56.03 312.9,56.03 312.9,105.92 331.58,105.92 331.58,56.03
|
||||||
|
356.5,56.03 "/>
|
||||||
|
<polygon class="st0" points="434.54,56.03 434.54,37.35 366,37.35 366,37.39 365.97,37.39 365.97,105.92 366,105.92 384.65,105.92
|
||||||
|
434.54,105.92 434.54,87.24 384.65,87.24 384.65,80.99 415.89,80.99 415.89,62.31 384.65,62.31 384.65,56.03 "/>
|
||||||
|
<polygon class="st0" points="356.55,134.06 356.52,120.82 343.35,120.86 318.39,145.81 306.6,145.81 306.6,120.86 287.92,120.86
|
||||||
|
287.92,189.39 306.6,189.39 306.6,164.49 318.45,164.49 343.35,189.39 356.52,189.42 356.55,176.18 335.5,155.12 "/>
|
||||||
|
<path class="st0" d="M88.62,56.3h34.3V37.35h-34.3c-18.94,0-34.3,15.36-34.3,34.3s15.36,34.3,34.3,34.3h34.3V87h-34.3
|
||||||
|
c-8.48,0-15.35-6.87-15.35-15.35C73.28,63.17,80.15,56.3,88.62,56.3z"/>
|
||||||
|
<path class="st0" d="M181.76,155.12v34.3h18.95v-34.3c0-18.94-15.36-34.3-34.3-34.3s-34.3,15.36-34.3,34.3v34.3h18.95v-34.3
|
||||||
|
c0-8.48,6.87-15.35,15.35-15.35C174.89,139.78,181.76,146.65,181.76,155.12z"/>
|
||||||
|
<path class="st0" d="M244.21,139.78h34.3v-18.95h-34.3c-18.94,0-34.3,15.36-34.3,34.3s15.36,34.3,34.3,34.3h34.3v-18.95h-34.3
|
||||||
|
c-8.48,0-15.35-6.87-15.35-15.35C228.86,146.65,235.73,139.78,244.21,139.78z"/>
|
||||||
|
<polygon class="st0" points="104.15,120.82 104.15,145.55 73.46,145.55 73.46,120.82 54.32,120.82 54.32,189.42 73.46,189.42
|
||||||
|
73.46,164.69 104.15,164.69 104.15,189.42 122.92,189.42 122.92,120.82 "/>
|
||||||
|
<path class="st1" d="M453.24,145.34c-0.23-13.68-11.67-24.52-25.35-24.52h-24.53v18.68h24.77c3.37,0,6.27,2.59,6.43,5.96
|
||||||
|
c0.17,3.6-2.7,6.56-6.25,6.56h-6.26c-10.32,0-18.68,8.36-18.68,18.68v0v18.65h49.88v-18.65H428.3
|
||||||
|
C442.22,170.71,453.47,159.31,453.24,145.34z"/>
|
||||||
|
<path class="st1" d="M512.59,145.34c-0.23-13.68-11.67-24.52-25.35-24.52h-24.53v18.68h24.77c3.37,0,6.27,2.59,6.43,5.96
|
||||||
|
c0.17,3.6-2.7,6.56-6.25,6.56h-6.26c-10.32,0-18.68,8.36-18.68,18.68v0v18.65h49.88v-18.65h-24.94
|
||||||
|
C501.57,170.71,512.82,159.31,512.59,145.34z"/>
|
||||||
|
<path class="st0" d="M200.29,66.27c-2.97-18.71-20.55-31.47-39.25-28.49c-18.71,2.97-31.47,20.55-28.49,39.25
|
||||||
|
c2.97,18.71,20.55,31.47,39.25,28.49C190.5,102.56,203.26,84.98,200.29,66.27z M166.42,87c-8.48,0-15.35-6.87-15.35-15.35
|
||||||
|
c0-8.48,6.87-15.35,15.35-15.35c8.48,0,15.35,6.87,15.35,15.35C181.76,80.13,174.89,87,166.42,87z"/>
|
||||||
|
|
||||||
|
<rect x="375.22" y="120.82" transform="matrix(-1 -1.224647e-16 1.224647e-16 -1 769.1138 266.6382)" class="st1" width="18.68" height="24.99"/>
|
||||||
|
<path class="st0" d="M253.7,37.39v-0.04h-33.19v0.04h-10.61v68.53h18.68V87h25.11c13.7,0,24.81-11.11,24.81-24.81
|
||||||
|
C278.51,48.49,267.4,37.39,253.7,37.39z M250.69,71.46h-22.1V53.17h22.1v0.01c5.05,0,9.14,4.09,9.14,9.14
|
||||||
|
C259.83,67.37,255.74,71.46,250.69,71.46z"/>
|
||||||
|
<path class="st0" d="M512.61,62.19c0-13.7-11.11-24.81-24.81-24.81v-0.04h-33.19v0.01h-10.61v68.6h18.68V87h17.79l18.92,18.92
|
||||||
|
l13.17,0.03l0.04-13.24l-10.36-10.36C508.52,77.85,512.61,70.5,512.61,62.19z M484.79,71.46
|
||||||
|
C484.79,71.46,484.79,71.46,484.79,71.46h-22.1V53.17h22.1v0.01c0,0,0,0,0,0c5.05,0,9.14,4.09,9.14,9.14
|
||||||
|
C493.92,67.37,489.83,71.46,484.79,71.46z"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 3.4 KiB |
BIN
docs/assets/duocam/duocam_connections.jpg
Normal file
BIN
docs/assets/duocam/duocam_connections.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 182 KiB |
BIN
docs/assets/duocam/image_topics.png
Normal file
BIN
docs/assets/duocam/image_topics.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
BIN
docs/assets/duocam/qgc_rtsp_settings.png
Normal file
BIN
docs/assets/duocam/qgc_rtsp_settings.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 28 KiB |
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user