Compare commits
58 Commits
v0.20-rc.3
...
travis_siz
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3460fec25e | ||
|
|
fa44e4b42f | ||
|
|
4bc985a7f4 | ||
|
|
c2bfa07a36 | ||
|
|
6ba27ef15d | ||
|
|
6b5e3464f1 | ||
|
|
768c0be5a5 | ||
|
|
892c6f853b | ||
|
|
8b690900b9 | ||
|
|
17c210919d | ||
|
|
ebeb3f58d6 | ||
|
|
3b19346a44 | ||
|
|
32d27f3f66 | ||
|
|
585af026d4 | ||
|
|
b52f702723 | ||
|
|
3e4f1ab8a9 | ||
|
|
513f4f419a | ||
|
|
6db5602a5a | ||
|
|
fb17a3f926 | ||
|
|
70e1675f9a | ||
|
|
e03eaa51c4 | ||
|
|
b346af92ae | ||
|
|
cb2cf48f39 | ||
|
|
ebec850010 | ||
|
|
5a8db188f6 | ||
|
|
4376bbb723 | ||
|
|
1d694e1a15 | ||
|
|
54de7fca3a | ||
|
|
32f5584082 | ||
|
|
d7c0fb33ff | ||
|
|
d16e7bf155 | ||
|
|
26bd1e2d8f | ||
|
|
e12577cf0e | ||
|
|
12544a69af | ||
|
|
f471280bef | ||
|
|
2ff9887ac4 | ||
|
|
2ecfb28a5d | ||
|
|
091483226f | ||
|
|
af16414c77 | ||
|
|
696214e717 | ||
|
|
15fd156ce0 | ||
|
|
82bfb961a4 | ||
|
|
c50637c60f | ||
|
|
a26f0f41e7 | ||
|
|
28690491c2 | ||
|
|
ca1323faf3 | ||
|
|
b713b49f3f | ||
|
|
62a77519e6 | ||
|
|
e1bf8aade5 | ||
|
|
0172d6e892 | ||
|
|
bcb7351a90 | ||
|
|
c71a46ce9d | ||
|
|
91dd7799ef | ||
|
|
3682e253a7 | ||
|
|
66ecbb4d09 | ||
|
|
f041b6125b | ||
|
|
a4f2bab3d7 | ||
|
|
56bcfa5c87 |
37
.travis.yml
@@ -7,7 +7,7 @@ env:
|
||||
global:
|
||||
- DOCKER="sfalexrog/img-tool:qemu-update"
|
||||
- TARGET_REPO="https://github.com/${TRAVIS_REPO_SLUG}.git"
|
||||
- if [[ -z ${TRAVIS_TAG} ]]; then IMAGE_VERSION="${TRAVIS_COMMIT}}"; else IMAGE_VERSION="${TRAVIS_TAG}"; fi
|
||||
- IMAGE_VERSION=${TRAVIS_TAG:-${TRAVIS_COMMIT:0:7}}
|
||||
- IMAGE_NAME="$(basename -s '.git' ${TARGET_REPO})_${IMAGE_VERSION}.img"
|
||||
git:
|
||||
depth: 50
|
||||
@@ -36,12 +36,13 @@ jobs:
|
||||
fi
|
||||
before_cache:
|
||||
- cp images/*.zip imgcache
|
||||
after_success:
|
||||
- sudo chmod -R 777 *
|
||||
- cd images && zip ${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"
|
||||
- sudo chmod -R 777 *
|
||||
- cd images && zip ${IMAGE_NAME}.zip ${IMAGE_NAME}
|
||||
deploy:
|
||||
provider: releases
|
||||
token: ${GITHUB_OAUTH_TOKEN}
|
||||
@@ -73,8 +74,11 @@ jobs:
|
||||
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
|
||||
- npm install markdownlint-cli -g
|
||||
- npm install svgexport -g
|
||||
- gitbook -V
|
||||
- markdownlint -V
|
||||
script:
|
||||
@@ -83,18 +87,19 @@ jobs:
|
||||
- ./check_unused_assets.py
|
||||
- gitbook install
|
||||
- gitbook build
|
||||
# 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
|
||||
- gitbook pdf ./ _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: Annotate
|
||||
name: Auto-generate changelog
|
||||
language: python
|
||||
@@ -110,7 +115,7 @@ jobs:
|
||||
- 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|Assets.xcassets|test_parser_pass.txt|test_node_failure.txt|aruco_pose/vendor|\.stl|\.dxf"
|
||||
- ./ec-linux-amd64 -spaces-after-tabs -e "roslib.js|ros3d.js|eventemitter2.js|draw.cpp|BinUtils.swift|\.idea|apps/android/app|Assets.xcassets|test_parser_pass.txt|test_node_failure.txt|aruco_pose/vendor|\.stl|\.dxf|\.dae"
|
||||
stages:
|
||||
- Build
|
||||
- Annotate
|
||||
|
||||
0
apps/CATKIN_IGNORE
Normal file
@@ -13,7 +13,7 @@
|
||||
Generate map file for aruco_map nodelet.
|
||||
|
||||
Usage:
|
||||
genmap.py <length> <x> <y> <dist_x> <dist_y> [<first>] [--top-left | --bottom-left]
|
||||
genmap.py <length> <x> <y> <dist_x> <dist_y> [<first>] [<x0>] [<y0>] [--top-left | --bottom-left]
|
||||
genmap.py (-h | --help)
|
||||
|
||||
Options:
|
||||
@@ -23,6 +23,8 @@ Options:
|
||||
<dist_x> Distance between markers along X axis
|
||||
<dist_y> Distance between markers along Y axis
|
||||
<first> First marker ID [default: 0]
|
||||
<x0> X 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)
|
||||
--bottom-left First marker is on bottom-left
|
||||
|
||||
@@ -39,20 +41,22 @@ arguments = docopt(__doc__)
|
||||
|
||||
length = float(arguments['<length>'])
|
||||
first = int(arguments['<first>'] if arguments['<first>'] is not None else 0)
|
||||
x0 = float(arguments['<x0>'] if arguments['<x0>'] is not None else 0)
|
||||
y0 = float(arguments['<y0>'] if arguments['<y0>'] is not None else 0)
|
||||
markers_x = int(arguments['<x>'])
|
||||
markers_y = int(arguments['<y>'])
|
||||
dist_x = float(arguments['<dist_x>'])
|
||||
dist_y = float(arguments['<dist_y>'])
|
||||
bottom_left = arguments['--bottom-left']
|
||||
|
||||
max_y = (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')
|
||||
for y in range(markers_y):
|
||||
for x in range(markers_x):
|
||||
pos_x = x * dist_x
|
||||
pos_y = y * dist_y
|
||||
pos_x = x0 + x * dist_x
|
||||
pos_y = y0 + y * dist_y
|
||||
if not bottom_left:
|
||||
pos_y = max_y - pos_y
|
||||
print('{}\t{}\t{}\t{}\t{}\t{}\t{}\t{}\t'.format(first, length, pos_x, pos_y, 0, 0, 0, 0))
|
||||
print('{}\t{}\t{}\t{}\t{}\t{}\t{}\t{}'.format(first, length, pos_x, pos_y, 0, 0, 0, 0))
|
||||
first += 1
|
||||
|
||||
34
builder/assets/avahi-services/sftp-ssh.service
Normal file
@@ -0,0 +1,34 @@
|
||||
<?xml version="1.0" standalone='no'?><!--*-nxml-*-->
|
||||
<!DOCTYPE service-group SYSTEM "avahi-service.dtd">
|
||||
|
||||
<!--
|
||||
This file is part of avahi.
|
||||
|
||||
avahi is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU Lesser General Public License as
|
||||
published by the Free Software Foundation; either version 2 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
avahi is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with avahi; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
02111-1307 USA.
|
||||
-->
|
||||
|
||||
<!-- See avahi.service(5) for more information about this configuration file -->
|
||||
|
||||
<service-group>
|
||||
|
||||
<name replace-wildcards="yes">%h</name>
|
||||
|
||||
<service>
|
||||
<type>_sftp-ssh._tcp</type>
|
||||
<port>22</port>
|
||||
</service>
|
||||
|
||||
</service-group>
|
||||
@@ -20,7 +20,7 @@
|
||||
# Example:
|
||||
# DocumentRoot /home/krypton/htdocs
|
||||
|
||||
DocumentRoot /home/pi/catkin_ws/src/clover/clover/www
|
||||
DocumentRoot /home/pi/.ros/www
|
||||
|
||||
# Redirect:
|
||||
# ---------
|
||||
|
||||
@@ -108,6 +108,8 @@ ${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} exec ${SCRIPTS_DIR}'/image-software
|
||||
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/examples' '/home/pi/'
|
||||
# network setup
|
||||
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} exec ${SCRIPTS_DIR}'/image-network.sh'
|
||||
# avahi setup
|
||||
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/avahi-services/sftp-ssh.service' '/etc/avahi/services'
|
||||
|
||||
# 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)
|
||||
|
||||
@@ -74,18 +74,6 @@ my_travis_retry rosdep update
|
||||
echo_stamp "Populate rosdep for ROS user"
|
||||
my_travis_retry sudo -u pi rosdep update
|
||||
|
||||
resolve_rosdep() {
|
||||
# TEMPLATE: resolve_rosdep <CATKIN_PATH> <ROS_DISTRO> <OS_DISTRO> <OS_VERSION>
|
||||
CATKIN_PATH=$1
|
||||
ROS_DISTRO='melodic'
|
||||
OS_DISTRO='debian'
|
||||
OS_VERSION='buster'
|
||||
|
||||
echo_stamp "Installing dependencies apps with rosdep in ${CATKIN_PATH}"
|
||||
cd ${CATKIN_PATH}
|
||||
my_travis_retry rosdep install -y --from-paths src --ignore-src --rosdistro ${ROS_DISTRO} --os=${OS_DISTRO}:${OS_VERSION}
|
||||
}
|
||||
|
||||
export ROS_IP='127.0.0.1' # needed for running tests
|
||||
|
||||
echo_stamp "Reconfiguring Clover repository for simplier unshallowing"
|
||||
@@ -94,11 +82,14 @@ git config remote.origin.fetch "+refs/heads/*:refs/remotes/origin/*"
|
||||
|
||||
echo_stamp "Build and install Clover"
|
||||
cd /home/pi/catkin_ws
|
||||
resolve_rosdep $(pwd)
|
||||
# Don't try to install gazebo_ros
|
||||
my_travis_retry rosdep install -y --from-paths src --ignore-src --rosdistro melodic --os=debian:buster \
|
||||
--skip-keys=gazebo_ros --skip-keys=gazebo_plugins
|
||||
my_travis_retry pip install wheel
|
||||
my_travis_retry pip install -r /home/pi/catkin_ws/src/clover/clover/requirements.txt
|
||||
source /opt/ros/melodic/setup.bash
|
||||
catkin_make -j2 -DCMAKE_BUILD_TYPE=Release
|
||||
# Don't build simulation plugins for actual drone
|
||||
catkin_make -j2 -DCMAKE_BUILD_TYPE=Release -DCATKIN_BLACKLIST_PACKAGES=clover_gazebo_plugins
|
||||
|
||||
echo_stamp "Install clever package (for backwards compatibility)"
|
||||
cd /home/pi/catkin_ws/src/clover/builder/assets/clever
|
||||
@@ -113,7 +104,7 @@ 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
|
||||
|
||||
echo_stamp "Installing additional ROS packages"
|
||||
apt-get install -y --no-install-recommends \
|
||||
my_travis_retry apt-get install -y --no-install-recommends \
|
||||
ros-melodic-dynamic-reconfigure \
|
||||
ros-melodic-compressed-image-transport \
|
||||
ros-melodic-rosbridge-suite \
|
||||
@@ -143,7 +134,6 @@ echo_stamp "Setup ROS environment"
|
||||
cat << EOF >> /home/pi/.bashrc
|
||||
LANG='C.UTF-8'
|
||||
LC_ALL='C.UTF-8'
|
||||
ROS_DISTRO='melodic'
|
||||
export ROS_HOSTNAME=\`hostname\`.local
|
||||
source /opt/ros/melodic/setup.bash
|
||||
source /home/pi/catkin_ws/devel/setup.bash
|
||||
|
||||
@@ -80,8 +80,9 @@ echo_stamp "Update apt cache"
|
||||
apt-get update
|
||||
# && apt upgrade -y
|
||||
|
||||
# Let's retry fetching those packages several times, just in case
|
||||
echo_stamp "Software installing"
|
||||
apt-get install --no-install-recommends -y \
|
||||
my_travis_retry apt-get install --no-install-recommends -y \
|
||||
unzip \
|
||||
zip \
|
||||
ipython \
|
||||
@@ -115,9 +116,7 @@ python-dev \
|
||||
python3-dev \
|
||||
python-systemd \
|
||||
mjpg-streamer \
|
||||
python3-opencv \
|
||||
&& echo_stamp "Everything was installed!" "SUCCESS" \
|
||||
|| (echo_stamp "Some packages wasn't installed!" "ERROR"; exit 1)
|
||||
python3-opencv
|
||||
|
||||
# Deny byobu to check available updates
|
||||
sed -i "s/updates_available//" /usr/share/byobu/status/status
|
||||
|
||||
@@ -166,13 +166,14 @@ add_library(${PROJECT_NAME}
|
||||
## The recommended prefix ensures that target names across packages don't collide
|
||||
add_executable(simple_offboard src/simple_offboard.cpp)
|
||||
|
||||
add_executable(rc src/rc.cpp)
|
||||
# PX4 already has rc and led targets, so we prefix ours with clover_
|
||||
add_executable(clover_rc src/rc.cpp)
|
||||
|
||||
add_executable(camera_markers src/camera_markers.cpp)
|
||||
|
||||
add_executable(vpe_publisher src/vpe_publisher.cpp)
|
||||
|
||||
add_executable(led src/led.cpp)
|
||||
add_executable(clover_led src/led.cpp)
|
||||
|
||||
add_executable(shell src/shell.cpp)
|
||||
|
||||
@@ -181,19 +182,24 @@ target_link_libraries(simple_offboard
|
||||
${GeographicLib_LIBRARIES}
|
||||
)
|
||||
|
||||
target_link_libraries(rc ${catkin_LIBRARIES})
|
||||
# Don't change actual binary names
|
||||
set_target_properties(clover_rc PROPERTIES OUTPUT_NAME rc)
|
||||
|
||||
set_target_properties(clover_led PROPERTIES OUTPUT_NAME led)
|
||||
|
||||
target_link_libraries(clover_rc ${catkin_LIBRARIES})
|
||||
|
||||
target_link_libraries(camera_markers ${catkin_LIBRARIES})
|
||||
|
||||
target_link_libraries(vpe_publisher ${catkin_LIBRARIES})
|
||||
|
||||
target_link_libraries(led ${catkin_LIBRARIES})
|
||||
target_link_libraries(clover_led ${catkin_LIBRARIES})
|
||||
|
||||
target_link_libraries(shell ${catkin_LIBRARIES})
|
||||
|
||||
add_dependencies(simple_offboard ${PROJECT_NAME}_generate_messages_cpp)
|
||||
|
||||
add_dependencies(led ${PROJECT_NAME}_generate_messages_cpp)
|
||||
add_dependencies(clover_led ${PROJECT_NAME}_generate_messages_cpp)
|
||||
|
||||
add_dependencies(shell ${PROJECT_NAME}_generate_messages_cpp)
|
||||
|
||||
|
||||
@@ -44,13 +44,6 @@ Alternatively you may change the `fcu_url` property in `mavros.launch` file to p
|
||||
|
||||
## Running
|
||||
|
||||
Enable systemd service `roscore` (if not running):
|
||||
|
||||
```bash
|
||||
sudo systemctl enable /home/<username>/catkin_ws/src/clover/builder/assets/roscore.service
|
||||
sudo systemctl start roscore
|
||||
```
|
||||
|
||||
To start connection to SITL, use:
|
||||
|
||||
```bash
|
||||
@@ -64,10 +57,3 @@ roslaunch clover clover.launch
|
||||
```
|
||||
|
||||
> Note that the package is configured to connect to `/dev/px4fmu` by default (see [previous section](#manual-installation)). Install udev rules or specify path to your FCU device in `mavros.launch`.
|
||||
|
||||
Also, you can enable and start the systemd service:
|
||||
|
||||
```bash
|
||||
sudo systemctl enable /home/<username>/catkin_ws/src/clover/deploy/clover.service
|
||||
sudo systemctl start clover
|
||||
```
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
<arg name="rc" default="true"/>
|
||||
<arg name="shell" default="true"/>
|
||||
|
||||
<arg name="simulator" default="false"/> <!-- flag that we are operating on a simulated drone -->
|
||||
|
||||
<!-- log formatting -->
|
||||
<env name="ROSCONSOLE_FORMAT" value="[${severity}] [${time}]: ${logger}: ${message}"/>
|
||||
|
||||
@@ -56,7 +58,9 @@
|
||||
</node>
|
||||
|
||||
<!-- main camera -->
|
||||
<include file="$(find clover)/launch/main_camera.launch" if="$(arg main_camera)"/>
|
||||
<include file="$(find clover)/launch/main_camera.launch" if="$(arg main_camera)">
|
||||
<arg name="simulator" value="$(arg simulator)"/>
|
||||
</include>
|
||||
|
||||
<!-- rosbridge -->
|
||||
<include file="$(find rosbridge_server)/launch/rosbridge_websocket.launch" if="$(eval rosbridge or rc)"/>
|
||||
@@ -65,14 +69,16 @@
|
||||
<node name="tf2_web_republisher" pkg="tf2_web_republisher" type="tf2_web_republisher" output="screen" if="$(arg rosbridge)"/>
|
||||
|
||||
<!-- vl53l1x ToF rangefinder -->
|
||||
<node name="rangefinder" pkg="vl53l1x" type="vl53l1x_node" output="screen" if="$(arg rangefinder_vl53l1x)">
|
||||
<node name="rangefinder" pkg="vl53l1x" type="vl53l1x_node" output="screen" if="$(eval rangefinder_vl53l1x and not simulator)">
|
||||
<param name="frame_id" value="rangefinder"/>
|
||||
<param name="min_signal" value="0.4"/>
|
||||
<param name="pass_statuses" type="yaml" value="[0, 6, 7, 11]"/>
|
||||
</node>
|
||||
|
||||
<!-- led strip -->
|
||||
<include file="$(find clover)/launch/led.launch" if="$(arg led)"/>
|
||||
<include file="$(find clover)/launch/led.launch" if="$(arg led)">
|
||||
<arg name="simulator" value="$(arg simulator)"/>
|
||||
</include>
|
||||
|
||||
<!-- rc backend -->
|
||||
<node name="rc" pkg="clover" type="rc" output="screen" if="$(arg rc)" clear_params="true">
|
||||
@@ -82,4 +88,9 @@
|
||||
|
||||
<!-- Shell access through ROS service -->
|
||||
<node name="shell" pkg="clover" type="shell" output="screen" if="$(arg shell)"/>
|
||||
|
||||
<!-- Update static directory -->
|
||||
<node pkg="roswww_static" name="roswww_static" type="main.py" clear_params="true">
|
||||
<param name="default_package" value="clover"/>
|
||||
</node>
|
||||
</launch>
|
||||
|
||||
@@ -2,11 +2,12 @@
|
||||
<arg name="ws281x" default="true"/>
|
||||
<arg name="led_effect" default="true"/>
|
||||
<arg name="led_notify" default="true"/>
|
||||
<arg name="simulator" default="false"/>
|
||||
|
||||
<!-- For additional help go to https://clover.coex.tech/led -->
|
||||
|
||||
<!-- ws281x led strip driver -->
|
||||
<node pkg="ws281x" name="led" type="ws281x_node" clear_params="true" output="screen" if="$(arg ws281x)">
|
||||
<node pkg="ws281x" name="led" type="ws281x_node" clear_params="true" output="screen" if="$(eval ws281x and not simulator)">
|
||||
<param name="led_count" value="58"/>
|
||||
<param name="gpio_pin" value="21"/>
|
||||
<param name="brightness" value="64"/>
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
<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="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 == 'forward')" 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"/>
|
||||
@@ -15,7 +16,7 @@
|
||||
<!-- <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 node -->
|
||||
<node pkg="nodelet" type="nodelet" name="main_camera" args="load cv_camera/CvCameraNodelet nodelet_manager" clear_params="true">
|
||||
<node pkg="nodelet" type="nodelet" name="main_camera" args="load cv_camera/CvCameraNodelet nodelet_manager" clear_params="true" unless="$(arg simulator)">
|
||||
<param name="device_path" value="/dev/video0"/> <!-- v4l2 device -->
|
||||
<param name="frame_id" value="main_camera_optical"/>
|
||||
<param name="camera_info_url" value="file://$(find clover)/camera_info/fisheye_cam.yaml"/>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
flask==1.1.1
|
||||
docopt==0.6.2
|
||||
geopy==1.11.0
|
||||
smbus2==0.2.1
|
||||
VL53L1X==0.0.2
|
||||
smbus2==0.3.0
|
||||
VL53L1X==0.0.5
|
||||
|
||||
@@ -701,7 +701,7 @@ def check_preflight_status():
|
||||
|
||||
@check('Network')
|
||||
def check_network():
|
||||
ros_hostname = os.environ.get('ROS_HOSTNAME').strip()
|
||||
ros_hostname = os.environ.get('ROS_HOSTNAME', '').strip()
|
||||
|
||||
if not ros_hostname:
|
||||
failure('no ROS_HOSTNAME is set')
|
||||
|
||||
@@ -417,8 +417,9 @@ void publish(const ros::Time stamp)
|
||||
}
|
||||
|
||||
if (setpoint_type == POSITION || setpoint_type == NAVIGATE || setpoint_type == NAVIGATE_GLOBAL) {
|
||||
position_msg.header.stamp = stamp;
|
||||
|
||||
if (setpoint_yaw_type == YAW || setpoint_yaw_type == TOWARDS) {
|
||||
position_msg.header.stamp = stamp;
|
||||
position_pub.publish(position_msg);
|
||||
|
||||
} else {
|
||||
@@ -605,12 +606,13 @@ bool serve(enum setpoint_type_t sp_type, float x, float y, float z, float vx, fl
|
||||
|
||||
if (sp_type == POSITION || sp_type == NAVIGATE || sp_type == NAVIGATE_GLOBAL || sp_type == VELOCITY || sp_type == ATTITUDE) {
|
||||
// destination point and/or yaw
|
||||
static PoseStamped ps;
|
||||
PoseStamped ps;
|
||||
ps.header.frame_id = frame_id;
|
||||
ps.header.stamp = stamp;
|
||||
ps.pose.position.x = x;
|
||||
ps.pose.position.y = y;
|
||||
ps.pose.position.z = z;
|
||||
ps.pose.orientation.w = 1.0; // Ensure quaternion is always valid
|
||||
|
||||
if (std::isnan(yaw)) {
|
||||
setpoint_yaw_type = YAW_RATE;
|
||||
|
||||
0
clover/www/CATKIN_IGNORE
Normal file
@@ -12,8 +12,8 @@
|
||||
|
||||
<script src="js/roslib.js"></script>
|
||||
<script type="text/javascript">
|
||||
document.querySelector("#wvs").href = location.origin + ':8080';
|
||||
document.querySelector("#butterfly").href = location.origin + ':57575';
|
||||
document.querySelector("#wvs").href = location.protocol + '//' + location.hostname + ':8080';
|
||||
document.querySelector("#butterfly").href = location.protocol + '//' + location.hostname + ':57575';
|
||||
|
||||
// Determine image version
|
||||
var ros = new ROSLIB.Ros({ url: 'ws://' + location.hostname + ':9090' });
|
||||
|
||||
10
clover_description/CMakeLists.txt
Normal file
@@ -0,0 +1,10 @@
|
||||
cmake_minimum_required(VERSION 3.0)
|
||||
project(clover_description)
|
||||
|
||||
find_package(catkin REQUIRED)
|
||||
|
||||
catkin_package()
|
||||
|
||||
install(DIRECTORY launch DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION})
|
||||
install(DIRECTORY meshes DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION})
|
||||
install(DIRECTORY urdf DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION})
|
||||
29
clover_description/README.md
Normal file
@@ -0,0 +1,29 @@
|
||||
# `clover_description` ROS package
|
||||
|
||||
This package contains models and URDF descriptions for the Clover 4 drone. These descriptions can be used for Gazebo simulation environment.
|
||||
|
||||
Note that in order to use these descriptions in Gazebo, you need to use the plugins from [PX4 `sitl_gazebo` package](https://github.com/PX4/sitl_gazebo) and `clover_simulation` package.
|
||||
|
||||
## Usage
|
||||
|
||||
The descriptions are provided as [`xacro`-enabled](https://wiki.ros.org/xacro) URDF files. A [`spawn_drone.launch`](launch/spawn_drone.launch) file that spawns the model in a running Gazebo instance is also provided for convenience.
|
||||
|
||||
You may provide additional parameters for `spawn_drone.launch` as well:
|
||||
|
||||
* `main_camera` (*boolean*, default: *true*) - controls whether the drone will have a downward-facing camera 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`);
|
||||
* `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)).
|
||||
|
||||
For example, in order to spawn a drone without a GPS module, you may use the following command:
|
||||
|
||||
```bash
|
||||
roslaunch clover_description spawn_drone.launch gps:=false
|
||||
```
|
||||
|
||||
## Tweaking
|
||||
|
||||
By default, the `spawn_drone.launch` command will use the [`clover4.xacro` description file](urdf/clover/clover4.xacro). This is a "high-level" description of the drone, mainly used to attach additional sensors.
|
||||
|
||||
The drone "physics" may be tweaked by changing the [`clover4_physics.xacro` file](urdf/clover/clover4_physics.xacro).
|
||||
6
clover_description/launch/spawn_camera.launch
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<launch>
|
||||
<arg name="cmd" default="$(find xacro)/xacro $(find clover_description)/urdf/sensors/rpi_cam.urdf.xacro"/>
|
||||
<param command="$(arg cmd)" name="camera_description"/>
|
||||
<node name="$(anon spawn)" output="screen" pkg="gazebo_ros" type="spawn_model" args="-urdf -param camera_description -model rpi_camera -package_to_model"/>
|
||||
</launch>
|
||||
18
clover_description/launch/spawn_drone.launch
Normal file
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<launch>
|
||||
<!-- Toggleable model parameters -->
|
||||
<!-- Main camera -->
|
||||
<arg name="main_camera" default="true"/>
|
||||
<!-- Slow simulation down to maintain camera rate -->
|
||||
<arg name="maintain_camera_rate" default="false"/>
|
||||
<arg name="rangefinder" default="true"/>
|
||||
<arg name="led" default="true"/>
|
||||
<arg name="gps" default="true"/>
|
||||
<!-- Use physics parameters from CAD programs -->
|
||||
<arg name="use_clover_physics" default="false"/>
|
||||
|
||||
<arg name="cmd" default="$(find xacro)/xacro $(find clover_description)/urdf/clover/clover4.xacro main_camera:=$(arg main_camera) rangefinder:=$(arg rangefinder) led:=$(arg led) gps:=$(arg gps) maintain_camera_rate:=$(arg maintain_camera_rate) use_clover_physics:=$(arg use_clover_physics)"/>
|
||||
<param command="$(arg cmd)" name="drone_description"/>
|
||||
<!-- Note: -package_to_model replaces all mentions of "package://" with "model://" in urdf URIs -->
|
||||
<node name="$(anon spawn)" output="screen" pkg="gazebo_ros" type="spawn_model" args="-urdf -param drone_description -model clover -z 0.3"/>
|
||||
</launch>
|
||||
7
clover_description/launch/view_drone.launch
Normal file
@@ -0,0 +1,7 @@
|
||||
<launch>
|
||||
<arg name="model" default="$(find clover_description)/urdf/drones/clover4.xacro"/>
|
||||
|
||||
<param name="robot_description" command="$(find xacro)/xacro.py $(arg model)"/>
|
||||
|
||||
<node name="rviz" pkg="rviz" type="rviz" required="true"/>
|
||||
</launch>
|
||||
@@ -0,0 +1,21 @@
|
||||
material Polycarbonate
|
||||
{
|
||||
technique
|
||||
{
|
||||
pass
|
||||
{
|
||||
scene_blend alpha_blend
|
||||
depth_write off
|
||||
|
||||
ambient 0.57 0.531 0.444 1
|
||||
diffuse 0.57 0.531 0.444 1
|
||||
specular 0.5 0.5 0.5 1
|
||||
|
||||
texture_unit
|
||||
{
|
||||
colour_op_ex source1 src_current src_current 0 1 0
|
||||
alpha_op_ex source1 src_manual src_current 0.72
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
BIN
clover_description/meshes/clover4/Color_tex.png.001.png
Normal file
|
After Width: | Height: | Size: 1.7 MiB |
BIN
clover_description/meshes/clover4/LED_144.png.001.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
clover_description/meshes/clover4/carbon_tex.png
Normal file
|
After Width: | Height: | Size: 149 KiB |
217
clover_description/meshes/clover4/clover_body_solid.dae
Normal file
116
clover_description/meshes/clover4/clover_guards_transparent.dae
Normal file
115
clover_description/meshes/clover4/prop_ccw.dae
Normal file
115
clover_description/meshes/clover4/prop_cw.dae
Normal file
27
clover_description/package.xml
Normal file
@@ -0,0 +1,27 @@
|
||||
<package format="2">
|
||||
<name>clover_description</name>
|
||||
<version>0.0.1</version>
|
||||
<description>The clover_description package provides URDF models of the Clover series of quadcopters.</description>
|
||||
|
||||
<maintainer email="sfalexrog@gmail.com">Alexey Rogachevskiy</maintainer>
|
||||
|
||||
<author>Alexey Rogachevskiy</author>
|
||||
<author>Andrey Ryabov</author>
|
||||
<author>Arthur Golubtsov</author>
|
||||
<author>Oleg Kalachev</author>
|
||||
<author>Svyatoslav Zhuravlev</author>
|
||||
|
||||
<license>MIT</license>
|
||||
|
||||
<url type="website">https://github.com/CopterExpress/clover</url>
|
||||
<url type="bugtracker">https://github.com/CopterExpress/clover/issues</url>
|
||||
|
||||
<buildtool_depend>catkin</buildtool_depend>
|
||||
<depend>xacro</depend>
|
||||
<exec_depend>gazebo_ros</exec_depend>
|
||||
<exec_depend>gazebo_plugins</exec_depend>
|
||||
|
||||
<export>
|
||||
<gazebo_ros gazebo_media_path="${prefix}"/>
|
||||
</export>
|
||||
</package>
|
||||
83
clover_description/urdf/camera_sensor.urdf.xacro
Normal file
@@ -0,0 +1,83 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<robot name="camera_sensor" xmlns:xacro="http://ros.org/wiki/xacro">
|
||||
<xacro:macro name="camera_sensor_base"
|
||||
params="name width height rate horizontal_fov frame_name k1 k2 k3 p1 p2 cx cy cx_prime ros_plugin_name min_rate window_size max_st_dev">
|
||||
<gazebo reference="${name}_link">
|
||||
<sensor type="camera" name="${name}">
|
||||
<camera>
|
||||
<horizontal_fov>${horizontal_fov}</horizontal_fov>
|
||||
<image>
|
||||
<format>B8G8R8</format>
|
||||
<width>${width}</width>
|
||||
<height>${height}</height>
|
||||
</image>
|
||||
<clip>
|
||||
<near>0.02</near>
|
||||
<far>1000</far>
|
||||
</clip>
|
||||
<distortion>
|
||||
<k1>${k1}</k1>
|
||||
<k2>${k2}</k2>
|
||||
<k3>${k3}</k3>
|
||||
<p1>${p1}</p1>
|
||||
<p2>${p2}</p2>
|
||||
<center>${(cx - 0.5)/ width} ${(cy - 0.5) / height}</center>
|
||||
</distortion>
|
||||
</camera>
|
||||
<always_on>1</always_on>
|
||||
<update_rate>${rate}</update_rate>
|
||||
<visualize>1</visualize>
|
||||
<plugin name="camera_plugin" filename="${ros_plugin_name}">
|
||||
<alwaysOn>true</alwaysOn>
|
||||
<imageTopicName>image_raw</imageTopicName>
|
||||
<cameraTopicName>camera_info</cameraTopicName>
|
||||
<updateRate>${rate}</updateRate>
|
||||
<cameraName>${name}</cameraName>
|
||||
<frameName>${frame_name}</frameName>
|
||||
<CxPrime>${cx_prime}</CxPrime>
|
||||
<Cx>${cx}</Cx>
|
||||
<Cy>${cy}</Cy>
|
||||
<distortionK1>${k1}</distortionK1>
|
||||
<distortionK2>${k2}</distortionK2>
|
||||
<distortionK3>${k3}</distortionK3>
|
||||
<distortionT1>${p1}</distortionT1>
|
||||
<distortionT2>${p2}</distortionT2>
|
||||
<!-- throttling_camera specific options start here -->
|
||||
<minUpdateRate>${min_rate}</minUpdateRate>
|
||||
<windowSize>${window_size}</windowSize>
|
||||
<maxStDev>${max_st_dev}</maxStDev>
|
||||
</plugin>
|
||||
</sensor>
|
||||
</gazebo>
|
||||
</xacro:macro>
|
||||
|
||||
<xacro:macro name="camera_sensor" params="name width height rate horizontal_fov k1:=0 k2:=0 k3:=0 p1:=0 p2:=0 do_throttling:=false">
|
||||
<xacro:if value="${do_throttling}">
|
||||
<xacro:property name="ros_plugin_name" value="libthrottling_camera.so"/>
|
||||
</xacro:if>
|
||||
<xacro:unless value="${do_throttling}">
|
||||
<xacro:property name="ros_plugin_name" value="libgazebo_ros_camera.so"/>
|
||||
</xacro:unless>
|
||||
<xacro:camera_sensor_base
|
||||
name="${name}"
|
||||
width="${width}"
|
||||
height="${height}"
|
||||
rate="${rate}"
|
||||
horizontal_fov="${horizontal_fov}"
|
||||
frame_name="/${name}_optical"
|
||||
k1="${k1}"
|
||||
k2="${k2}"
|
||||
k3="${k3}"
|
||||
p1="${p1}"
|
||||
p2="${p2}"
|
||||
cx="${(width + 1.0)/2.0}"
|
||||
cy="${(height + 1.0)/2.0}"
|
||||
cx_prime="${(width + 1.0)/2.0}"
|
||||
ros_plugin_name="${ros_plugin_name}"
|
||||
min_rate="${rate * 0.95}"
|
||||
window_size="${rate}"
|
||||
max_st_dev="${3.0 / rate}"
|
||||
/>
|
||||
|
||||
</xacro:macro>
|
||||
</robot>
|
||||
46
clover_description/urdf/clover/clover4.xacro
Normal file
@@ -0,0 +1,46 @@
|
||||
<?xml version="1.0"?>
|
||||
<robot name="clover_camera" xmlns:xacro="http://ros.org/wiki/xacro">
|
||||
|
||||
<xacro:arg name="main_camera" default="true"/>
|
||||
<xacro:arg name="rangefinder" default="true"/>
|
||||
<xacro:arg name="led" default="true"/>
|
||||
<xacro:arg name="gps" default="true"/>
|
||||
<xacro:arg name="maintain_camera_rate" default="false"/>
|
||||
<xacro:arg name="use_clover_physics" default="false"/>
|
||||
|
||||
<xacro:include filename="$(find clover_description)/urdf/clover/clover4_base.xacro" />
|
||||
<xacro:include filename="$(find clover_description)/urdf/sensors/rpi_cam.urdf.xacro"/>
|
||||
<xacro:include filename="$(find clover_description)/urdf/sensors/distance_sensor.urdf.xacro"/>
|
||||
<xacro:include filename="$(find clover_description)/urdf/leds/led_strip.xacro"/>
|
||||
|
||||
<!-- Create camera plugin -->
|
||||
<xacro:if value="$(arg main_camera)">
|
||||
<xacro:rpi_cam name="main_camera" parent="base_link" x="0.055" y="0.0" z="-0.03" roll="0" pitch="${pi / 2}" yaw="0" width="320" height="240" rate="40" do_throttling="$(arg maintain_camera_rate)"/>
|
||||
</xacro:if>
|
||||
|
||||
<!-- Create rangefinder plugin -->
|
||||
<xacro:if value="$(arg rangefinder)">
|
||||
<xacro:distance_sensor parent="base_link" x="0.0" y="0.0" z="-0.04" roll="0" pitch="${pi / 2}" yaw="0"/>
|
||||
</xacro:if>
|
||||
|
||||
<!-- Instantiate LED strip -->
|
||||
<xacro:if value="$(arg led)">
|
||||
<xacro:led_strip
|
||||
name="led"
|
||||
parent="base_link"
|
||||
radius="0.08"
|
||||
bulb_radius="0.006"
|
||||
led_count="58"
|
||||
use_plugin="true"
|
||||
z="-0.002"/>
|
||||
</xacro:if>
|
||||
|
||||
<xacro:if value="$(arg gps)">
|
||||
<!-- Instantiate gps plugin. -->
|
||||
<xacro:gps_plugin_macro
|
||||
namespace="${namespace}"
|
||||
gps_noise="true"
|
||||
/>
|
||||
</xacro:if>
|
||||
|
||||
</robot>
|
||||
183
clover_description/urdf/clover/clover4_base.xacro
Normal file
@@ -0,0 +1,183 @@
|
||||
<?xml version="1.0"?>
|
||||
|
||||
<robot name="clover" xmlns:xacro="http://ros.org/wiki/xacro">
|
||||
<!-- Properties that can be assigned at build time as arguments.
|
||||
Is there a reason not to make all properties arguments?
|
||||
-->
|
||||
<xacro:arg name='name' default='iris' />
|
||||
<xacro:arg name='mavlink_addr' default='INADDR_ANY' />
|
||||
<xacro:arg name='mavlink_udp_port' default='14560' />
|
||||
<xacro:arg name='mavlink_tcp_port' default='4560' />
|
||||
<xacro:arg name='serial_enabled' default='false' />
|
||||
<xacro:arg name='serial_device' default='/dev/ttyACM0' />
|
||||
<xacro:arg name='baudrate' default='921600' />
|
||||
<xacro:arg name='qgc_addr' default='INADDR_ANY' />
|
||||
<xacro:arg name='qgc_udp_port' default='14550' />
|
||||
<xacro:arg name='sdk_addr' default='INADDR_ANY' />
|
||||
<xacro:arg name='sdk_udp_port' default='14540' />
|
||||
<xacro:arg name='hil_mode' default='false' />
|
||||
<xacro:arg name='hil_state_level' default='false' />
|
||||
<xacro:arg name='send_vision_estimation' default='false' />
|
||||
<xacro:arg name='send_odometry' default='false' />
|
||||
<xacro:arg name='enable_lockstep' default='true' />
|
||||
<xacro:arg name='use_tcp' default='true' />
|
||||
<xacro:arg name='vehicle_is_tailsitter' default='false' />
|
||||
<xacro:arg name='visual_material' default='DarkGrey' />
|
||||
<xacro:arg name='enable_mavlink_interface' default='true' />
|
||||
<xacro:arg name='enable_wind' default='false' />
|
||||
<!-- The following causes segfault with multiple vehicles if defaults to true!!! -->
|
||||
<xacro:arg name='enable_ground_truth' default='false' />
|
||||
<xacro:arg name='enable_logging' default='false' />
|
||||
<xacro:arg name='log_file' default='iris' />
|
||||
|
||||
<!-- macros for gazebo plugins, sensors -->
|
||||
<xacro:include filename="$(find clover_description)/urdf/component_snippets.urdf.xacro" />
|
||||
|
||||
<!-- Instantiate iris "mechanics" -->
|
||||
<xacro:include filename="$(find clover_description)/urdf/clover/clover4_physics.xacro" />
|
||||
|
||||
<xacro:if value="$(arg enable_wind)">
|
||||
<xacro:wind_plugin_macro
|
||||
namespace="${namespace}"
|
||||
wind_direction="0 0 1"
|
||||
wind_force_mean="0.7"
|
||||
xyz_offset="1 0 0"
|
||||
wind_gust_direction="0 0 0"
|
||||
wind_gust_duration="0"
|
||||
wind_gust_start="0"
|
||||
wind_gust_force_mean="0"
|
||||
/>
|
||||
</xacro:if>
|
||||
|
||||
<!-- Instantiate magnetometer plugin. -->
|
||||
<xacro:magnetometer_plugin_macro
|
||||
namespace="${namespace}"
|
||||
pub_rate="20"
|
||||
noise_density="0.0004"
|
||||
random_walk="0.0000064"
|
||||
bias_correlation_time="600"
|
||||
mag_topic="/mag"
|
||||
>
|
||||
</xacro:magnetometer_plugin_macro>
|
||||
|
||||
<!-- Instantiate barometer plugin. -->
|
||||
<xacro:barometer_plugin_macro
|
||||
namespace="${namespace}"
|
||||
pub_rate="10"
|
||||
baro_topic="/baro"
|
||||
>
|
||||
</xacro:barometer_plugin_macro>
|
||||
|
||||
<xacro:if value="$(arg enable_mavlink_interface)">
|
||||
<!-- Instantiate mavlink telemetry interface. -->
|
||||
<xacro:mavlink_interface_macro
|
||||
namespace="${namespace}"
|
||||
imu_sub_topic="/imu"
|
||||
gps_sub_topic="/gps"
|
||||
mag_sub_topic="/mag"
|
||||
baro_sub_topic="/baro"
|
||||
mavlink_addr="$(arg mavlink_addr)"
|
||||
mavlink_udp_port="$(arg mavlink_udp_port)"
|
||||
mavlink_tcp_port="$(arg mavlink_tcp_port)"
|
||||
serial_enabled="$(arg serial_enabled)"
|
||||
serial_device="$(arg serial_device)"
|
||||
baudrate="$(arg baudrate)"
|
||||
qgc_addr="$(arg qgc_addr)"
|
||||
qgc_udp_port="$(arg qgc_udp_port)"
|
||||
sdk_addr="$(arg sdk_addr)"
|
||||
sdk_udp_port="$(arg sdk_udp_port)"
|
||||
hil_mode="$(arg hil_mode)"
|
||||
hil_state_level="$(arg hil_state_level)"
|
||||
vehicle_is_tailsitter="$(arg vehicle_is_tailsitter)"
|
||||
send_vision_estimation="$(arg send_vision_estimation)"
|
||||
send_odometry="$(arg send_odometry)"
|
||||
enable_lockstep="$(arg enable_lockstep)"
|
||||
use_tcp="$(arg use_tcp)"
|
||||
>
|
||||
</xacro:mavlink_interface_macro>
|
||||
</xacro:if>
|
||||
|
||||
<!-- Mount an ADIS16448 IMU. -->
|
||||
<xacro:imu_plugin_macro
|
||||
namespace="${namespace}"
|
||||
imu_suffix=""
|
||||
parent_link="base_link"
|
||||
imu_topic="/imu"
|
||||
mass_imu_sensor="0.015"
|
||||
gyroscope_noise_density="0.0003394"
|
||||
gyroscopoe_random_walk="0.000038785"
|
||||
gyroscope_bias_correlation_time="1000.0"
|
||||
gyroscope_turn_on_bias_sigma="0.0087"
|
||||
accelerometer_noise_density="0.004"
|
||||
accelerometer_random_walk="0.006"
|
||||
accelerometer_bias_correlation_time="300.0"
|
||||
accelerometer_turn_on_bias_sigma="0.1960"
|
||||
>
|
||||
<inertia ixx="0.00001" ixy="0.0" ixz="0.0" iyy="0.00001" iyz="0.0" izz="0.00001" />
|
||||
<origin xyz="0 0 0" rpy="0 0 0" />
|
||||
</xacro:imu_plugin_macro>
|
||||
|
||||
<xacro:if value="$(arg enable_ground_truth)">
|
||||
<!-- Mount an IMU providing ground truth. -->
|
||||
<xacro:imu_plugin_macro
|
||||
namespace="${namespace}"
|
||||
imu_suffix="gt"
|
||||
parent_link="base_link"
|
||||
imu_topic="ground_truth/imu"
|
||||
mass_imu_sensor="0.00001"
|
||||
gyroscope_noise_density="0.0"
|
||||
gyroscopoe_random_walk="0.0"
|
||||
gyroscope_bias_correlation_time="1000.0"
|
||||
gyroscope_turn_on_bias_sigma="0.0"
|
||||
accelerometer_noise_density="0.0"
|
||||
accelerometer_random_walk="0.0"
|
||||
accelerometer_bias_correlation_time="300.0"
|
||||
accelerometer_turn_on_bias_sigma="0.0"
|
||||
>
|
||||
<inertia ixx="0.00001" ixy="0.0" ixz="0.0" iyy="0.00001" iyz="0.0" izz="0.00001" />
|
||||
<origin xyz="0 0 0" rpy="0 0 0" />
|
||||
</xacro:imu_plugin_macro>
|
||||
|
||||
<!-- Mount a generic odometry sensor providing ground truth. -->
|
||||
<xacro:odometry_plugin_macro
|
||||
namespace="${namespace}/ground_truth"
|
||||
odometry_sensor_suffix="gt"
|
||||
parent_link="base_link"
|
||||
pose_topic="pose"
|
||||
pose_with_covariance_topic="pose_with_covariance"
|
||||
position_topic="position"
|
||||
transform_topic="transform"
|
||||
odometry_topic="odometry"
|
||||
parent_frame_id="world"
|
||||
mass_odometry_sensor="0.00001"
|
||||
measurement_divisor="1"
|
||||
measurement_delay="0"
|
||||
unknown_delay="0.0"
|
||||
noise_normal_position="0 0 0"
|
||||
noise_normal_quaternion="0 0 0"
|
||||
noise_normal_linear_velocity="0 0 0"
|
||||
noise_normal_angular_velocity="0 0 0"
|
||||
noise_uniform_position="0 0 0"
|
||||
noise_uniform_quaternion="0 0 0"
|
||||
noise_uniform_linear_velocity="0 0 0"
|
||||
noise_uniform_angular_velocity="0 0 0"
|
||||
enable_odometry_map="false"
|
||||
odometry_map=""
|
||||
image_scale=""
|
||||
>
|
||||
<inertia ixx="0.00001" ixy="0.0" ixz="0.0" iyy="0.00001" iyz="0.0" izz="0.00001" /> <!-- [kg.m^2] [kg.m^2] [kg.m^2] [kg.m^2] [kg.m^2] [kg.m^2] -->
|
||||
<origin xyz="0.0 0.0 0.0" rpy="0.0 0.0 0.0" />
|
||||
</xacro:odometry_plugin_macro>
|
||||
</xacro:if>
|
||||
|
||||
<xacro:if value="$(arg enable_logging)">
|
||||
<!-- Instantiate a logger -->
|
||||
<xacro:bag_plugin_macro
|
||||
namespace="${namespace}"
|
||||
bag_file="$(arg log_file)"
|
||||
rotor_velocity_slowdown_sim="${rotor_velocity_slowdown_sim}"
|
||||
>
|
||||
</xacro:bag_plugin_macro>
|
||||
</xacro:if>
|
||||
|
||||
</robot>
|
||||
235
clover_description/urdf/clover/clover4_macros.xacro
Normal file
@@ -0,0 +1,235 @@
|
||||
<?xml version="1.0"?>
|
||||
<!--
|
||||
Copyright 2015 Fadri Furrer, ASL, ETH Zurich, Switzerland
|
||||
Copyright 2015 Michael Burri, ASL, ETH Zurich, Switzerland
|
||||
Copyright 2015 Mina Kamel, ASL, ETH Zurich, Switzerland
|
||||
Copyright 2015 Janosch Nikolic, ASL, ETH Zurich, Switzerland
|
||||
Copyright 2015 Markus Achtelik, ASL, ETH Zurich, Switzerland
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<!--
|
||||
Clover 4 base model, collision boxes, and other fun stuff. Derived heavily from multirotor_base.xacro
|
||||
from PX4 simulation config files.
|
||||
-->
|
||||
|
||||
<robot xmlns:xacro="http://ros.org/wiki/xacro">
|
||||
<!-- Main multirotor link -->
|
||||
<!-- We still allow specifying a different mass and inertia matrix.
|
||||
Body width and height are allowed as parameters, but are ignored for the most part.
|
||||
Color is not applicable, since the model has textures and is a compound thingy anyway.
|
||||
-->
|
||||
<xacro:macro name="clover4_base_macro"
|
||||
params="robot_namespace mass body_width body_height *inertia">
|
||||
<link name="base_link"></link>
|
||||
|
||||
<!-- Note: For some reason this file relies on base_link_inertia becoming base_link.
|
||||
I don't even want to know.
|
||||
-sfalexrog
|
||||
-->
|
||||
<joint name="base_joint" type="fixed">
|
||||
<origin xyz="0 0 0" rpy="0 0 0" />
|
||||
<parent link="base_link" />
|
||||
<child link="base_link_inertia" />
|
||||
</joint>
|
||||
|
||||
<link name="base_link_inertia">
|
||||
<inertial>
|
||||
<mass value="${mass}" /> <!-- [kg] -->
|
||||
<origin xyz="0 0 0" />
|
||||
<xacro:insert_block name="inertia" />
|
||||
</inertial>
|
||||
<visual name="body">
|
||||
<origin xyz="0 0 0.025" rpy="0 0 0" />
|
||||
<geometry>
|
||||
<!-- Note: Texture files are expected to be in the same directory as the model -->
|
||||
<mesh filename="package://clover_description/meshes/clover4/clover_body_solid.dae"
|
||||
scale="1 1 1"/>
|
||||
</geometry>
|
||||
</visual>
|
||||
<!-- Now, a person with some experience in building urdf/xacro files would say something along the lines of:
|
||||
"Hey, a link may have multiple visual tags, each with its own material!"
|
||||
Hear my answer, o experienced one. First, you are absolutely correct, you may add multiple visual
|
||||
elements to a single link. Second, once you try to put some transparency on your visual elements,
|
||||
you'll have it vanish into thin fucking air - Gazebo (as of version 9!) gives fuck all about your
|
||||
"color" attributes and whatnot. Third, oh, you want to reference the visual element from your
|
||||
"gazebo" tag down the line? Well fuck you, buddy, because you can only reference links and joints!
|
||||
So yeah, good luck improving this.
|
||||
- sfalexrog
|
||||
-->
|
||||
<collision>
|
||||
<origin xyz="0 0 0" rpy="0 0 0" />
|
||||
<geometry>
|
||||
<box size="${body_width} ${body_width} ${body_height}" /> <!-- [m] [m] [m] -->
|
||||
</geometry>
|
||||
</collision>
|
||||
</link>
|
||||
|
||||
<!-- This link is here for a single reason: to draw transparent parts (oh, and also to be the bane of my existence).
|
||||
I'm going to leave helpful comments for the poor soul who's going to attempt to improve this file.
|
||||
-sfalexrog
|
||||
-->
|
||||
<link name="base_guard_link">
|
||||
<!-- So we've established we actually need a link. Now, if you look at the SDF spec for some reason
|
||||
(much like I did), you'll notice you don't really need much for a link to exist. And that is a
|
||||
big fucking lie. Sure, go ahead, create a link without inertial or collision properties,
|
||||
see how far that gets you (spoiler: it will get you fucking nowhere, because Gazebo will
|
||||
devour your puny link and all that it stands for).
|
||||
- sfalexrog
|
||||
-->
|
||||
<inertial>
|
||||
<!-- We give an oh-so-tiny mass to the link to avoid it being destroyed by Gazebo -->
|
||||
<mass value="0.015"/> <!-- [kg] -->
|
||||
<origin xyz="0 0 0" rpy="0 0 0"/>
|
||||
<!-- We also give it some inertia, so that the physics engine does not go crazy
|
||||
(and to avoid it being devoured by Gazebo)
|
||||
-->
|
||||
<inertia
|
||||
ixx="${1/12 * 0.015 * 0.01 * 0.01}"
|
||||
iyy="${1/12 * 0.015 * 0.01 * 0.01}"
|
||||
izz="${1/12 * 0.015 * 0.01 * 0.01}"
|
||||
ixy="0.0" ixz="0.0" iyz="0.0"
|
||||
/>
|
||||
</inertial>
|
||||
<!-- We may want to have proper collision tucked away here at some point, though -->
|
||||
<collision>
|
||||
<origin xyz="0 0 0" rpy="0 0 0" />
|
||||
<geometry>
|
||||
<box size="0.01 0.01 0.01" /> <!-- [m] [m] [m] -->
|
||||
</geometry>
|
||||
</collision>
|
||||
<visual>
|
||||
<!-- This origin should be the same as in "base_link_inertia" visuals -->
|
||||
<origin xyz="0 0 0.025" rpy="0 0 0"/>
|
||||
<geometry>
|
||||
<mesh filename="package://clover_description/meshes/clover4/clover_guards_transparent.dae"
|
||||
scale="1 1 1"/>
|
||||
</geometry>
|
||||
<!-- Let me reiterate that you can't put the "material" tag to any effect -->
|
||||
</visual>
|
||||
</link>
|
||||
|
||||
<!-- Now, the last thing to do is to attach the base_guard_link to base_link. Since
|
||||
base_guard_link has no meaning by itself, it must be fixed rigidly to base_link.
|
||||
Except Gazebo absolutely loves "optimizing away" your fixed joints and all
|
||||
child links with them! How do we deal with that nonsense? Read on to find out!
|
||||
- sfalexrog
|
||||
-->
|
||||
<joint name="base_guard_joint" type="fixed">
|
||||
<origin xyz="0 0 0" rpy="0 0 0"/>
|
||||
<parent link="base_link" />
|
||||
<child link="base_guard_link" />
|
||||
</joint>
|
||||
|
||||
<!-- attach multirotor_base_plugin to the base_link -->
|
||||
<gazebo>
|
||||
<plugin filename="libgazebo_multirotor_base_plugin.so" name="rosbag">
|
||||
<robotNamespace>${robot_namespace}</robotNamespace>
|
||||
<linkName>base_link</linkName>
|
||||
<rotorVelocitySlowdownSim>${rotor_velocity_slowdown_sim}</rotorVelocitySlowdownSim>
|
||||
</plugin>
|
||||
</gazebo>
|
||||
<!-- And here's what you've been waiting for: the way to save a fixed joint from
|
||||
Gazebo's wrath! You plead for it to save your joint, and it might answer
|
||||
(depending on the version, apparently).
|
||||
Note that it is done in other places as well.
|
||||
- sfalexrog
|
||||
-->
|
||||
<gazebo reference="base_guard_joint">
|
||||
<disableFixedJointLumping>true</disableFixedJointLumping>
|
||||
<preserveFixedJoint>true</preserveFixedJoint>
|
||||
</gazebo>
|
||||
<!-- ...and here's another absolutely terrible idea: you can only set a material for
|
||||
the whole link, not for its part. And that's incompatible with URDF's material
|
||||
definition. Yeah.
|
||||
- sfalexrog
|
||||
-->
|
||||
<gazebo reference="base_guard_link">
|
||||
<material>Polycarbonate</material>
|
||||
</gazebo>
|
||||
<gazebo reference="base_link">
|
||||
<kp>10000.0</kp>
|
||||
<kd>10.0</kd>
|
||||
<maxVel>10.0</maxVel>
|
||||
<minDepth>0.001</minDepth>
|
||||
</gazebo>
|
||||
</xacro:macro>
|
||||
|
||||
<!-- Rotor joint and link, modified for Clover purposes -->
|
||||
<xacro:macro name="vertical_clover_rotor"
|
||||
params="robot_namespace suffix direction motor_constant moment_constant parent mass_rotor radius_rotor time_constant_up time_constant_down max_rot_velocity motor_number rotor_drag_coefficient rolling_moment_coefficient mesh *origin *inertia">
|
||||
<joint name="rotor_${motor_number}_joint" type="continuous">
|
||||
<xacro:insert_block name="origin" />
|
||||
<axis xyz="0 0 1" />
|
||||
<!-- TODO(ff): not currently set because it's not yet supported -->
|
||||
<!-- <limit effort="2000" velocity="${max_rot_velocity}" /> -->
|
||||
<parent link="${parent}" />
|
||||
<child link="rotor_${motor_number}" />
|
||||
|
||||
</joint>
|
||||
<!-- TODO(ff): not currently set because it's not yet supported -->
|
||||
<!-- <gazebo reference="rotor_${motor_number}_joint"> <axis> <xyz>0 0 1</xyz>
|
||||
<limit> <velocity> ${max_rot_velocity} </velocity> </limit> </axis> </gazebo> -->
|
||||
<link name="rotor_${motor_number}">
|
||||
<inertial>
|
||||
<mass value="${mass_rotor}" /> <!-- [kg] -->
|
||||
<xacro:insert_block name="inertia" />
|
||||
</inertial>
|
||||
<visual>
|
||||
<geometry>
|
||||
<mesh filename="package://clover_description/meshes/${mesh}"
|
||||
scale="1 1 1" />
|
||||
</geometry>
|
||||
</visual>
|
||||
<collision>
|
||||
<geometry>
|
||||
<cylinder length="0.005" radius="${radius_rotor}" /> <!-- [m] -->
|
||||
</geometry>
|
||||
</collision>
|
||||
</link>
|
||||
<gazebo>
|
||||
<plugin name="${suffix}_motor_model" filename="libgazebo_motor_model.so">
|
||||
<robotNamespace>${robot_namespace}</robotNamespace>
|
||||
<jointName>rotor_${motor_number}_joint</jointName>
|
||||
<linkName>rotor_${motor_number}</linkName>
|
||||
<turningDirection>${direction}</turningDirection>
|
||||
<timeConstantUp>${time_constant_up}</timeConstantUp>
|
||||
<timeConstantDown>${time_constant_down}</timeConstantDown>
|
||||
<maxRotVelocity>${max_rot_velocity}</maxRotVelocity>
|
||||
<motorConstant>${motor_constant}</motorConstant>
|
||||
<momentConstant>${moment_constant}</momentConstant>
|
||||
<commandSubTopic>/gazebo/command/motor_speed</commandSubTopic>
|
||||
<motorNumber>${motor_number}</motorNumber>
|
||||
<rotorDragCoefficient>${rotor_drag_coefficient}</rotorDragCoefficient>
|
||||
<rollingMomentCoefficient>${rolling_moment_coefficient}</rollingMomentCoefficient>
|
||||
<motorSpeedPubTopic>/motor_speed/${motor_number}</motorSpeedPubTopic>
|
||||
<rotorVelocitySlowdownSim>${rotor_velocity_slowdown_sim}</rotorVelocitySlowdownSim>
|
||||
<!--
|
||||
<gazebo_joint_control_pid>
|
||||
<p>0.1</p>
|
||||
<i>0</i>
|
||||
<d>0</d>
|
||||
<iMax>0</iMax>
|
||||
<iMin>0</iMin>
|
||||
<cmdMax>3</cmdMax>
|
||||
<cmdMin>-3</cmdMin>
|
||||
</gazebo_joint_control_pid>
|
||||
-->
|
||||
</plugin>
|
||||
</gazebo>
|
||||
<!-- <gazebo reference="rotor_${motor_number}">
|
||||
<material>Gazebo/${color}</material>
|
||||
</gazebo> -->
|
||||
</xacro:macro>
|
||||
</robot>
|
||||
178
clover_description/urdf/clover/clover4_physics.xacro
Normal file
@@ -0,0 +1,178 @@
|
||||
<?xml version="1.0"?>
|
||||
|
||||
<!-- Typical physics values for the Clover drone.
|
||||
NB: Right now the values are wrong, and taken from the 3DR Iris drone description.
|
||||
This is subject to change as correct values are calculated.
|
||||
|
||||
-->
|
||||
|
||||
<robot name="iris" xmlns:xacro="http://ros.org/wiki/xacro">
|
||||
<!-- Properties -->
|
||||
<xacro:property name="namespace" value="" />
|
||||
<xacro:property name="rotor_velocity_slowdown_sim" value="10" />
|
||||
<xacro:if value="$(arg use_clover_physics)">
|
||||
<xacro:property name="mass" value="0.7" /> <!-- [kg] -->
|
||||
</xacro:if>
|
||||
<xacro:unless value="$(arg use_clover_physics)">
|
||||
<xacro:property name="mass" value="1.5" /> <!-- [kg] -->
|
||||
</xacro:unless>
|
||||
<xacro:property name="body_width" value="0.35" /> <!-- [m] -->
|
||||
<xacro:property name="body_height" value="0.124" /> <!-- [m] -->
|
||||
<xacro:property name="mass_rotor" value="0.01" /> <!-- [kg] -->
|
||||
<xacro:property name="arm_length_front_x" value="0.083" /> <!-- [m] -->
|
||||
<xacro:property name="arm_length_back_x" value="0.083" /> <!-- [m] -->
|
||||
<xacro:property name="arm_length_front_y" value="0.083" /> <!-- [m] -->
|
||||
<xacro:property name="arm_length_back_y" value="0.083" /> <!-- [m] -->
|
||||
<xacro:property name="rotor_offset_top" value="0.026" /> <!-- [m] -->
|
||||
<xacro:property name="radius_rotor" value="0.06" /> <!-- [m] -->
|
||||
<xacro:property name="motor_constant" value="8.54858e-06" /> <!-- [kg.m/s^2] -->
|
||||
<xacro:property name="moment_constant" value="0.06" /> <!-- [m] -->
|
||||
<xacro:property name="time_constant_up" value="0.0125" /> <!-- [s] -->
|
||||
<xacro:property name="time_constant_down" value="0.025" /> <!-- [s] -->
|
||||
<xacro:property name="max_rot_velocity" value="1100" /> <!-- [rad/s] -->
|
||||
<xacro:property name="sin30" value="0.5" />
|
||||
<xacro:property name="cos30" value="0.866025403784" />
|
||||
<xacro:property name="sqrt2" value="1.4142135623730951" />
|
||||
<xacro:property name="rotor_drag_coefficient" value="1.75e-04" />
|
||||
<xacro:property name="rolling_moment_coefficient" value="0.000001" />
|
||||
<xacro:property name="color" value="$(arg visual_material)" />
|
||||
|
||||
<!-- Property Blocks -->
|
||||
<!-- Clover body inertia -->
|
||||
<!-- Values from CAD program -->
|
||||
<xacro:if value="$(arg use_clover_physics)">
|
||||
<xacro:property name="body_inertia">
|
||||
<inertia
|
||||
ixx="0.0347563"
|
||||
iyy="0.0458929"
|
||||
izz="0.0977"
|
||||
ixy="0.0" ixz="0.0" iyz="0.0" /> <!-- [kg.m^2] [kg.m^2] [kg.m^2] [kg.m^2] [kg.m^2] [kg.m^2] -->
|
||||
</xacro:property>
|
||||
</xacro:if>
|
||||
<!-- Box-like inertia -->
|
||||
<xacro:unless value="$(arg use_clover_physics)">
|
||||
<xacro:property name="body_inertia">
|
||||
<inertia
|
||||
ixx="${1/12 * mass * (body_height * body_height + body_width * body_width)}"
|
||||
iyy="${1/12 * mass * (body_height * body_height + body_width * body_width)}"
|
||||
izz="${1/12 * mass * (body_width * body_width + body_width * body_width)}"
|
||||
ixy="0.0" ixz="0.0" iyz="0.0" /> <!-- [kg.m^2] [kg.m^2] [kg.m^2] [kg.m^2] [kg.m^2] [kg.m^2] -->
|
||||
</xacro:property>
|
||||
</xacro:unless>
|
||||
|
||||
<!-- inertia of a single rotor -->
|
||||
<!-- Values from CAD program -->
|
||||
<xacro:if value="$(arg use_clover_physics)">
|
||||
<xacro:property name="rotor_inertia">
|
||||
<inertia
|
||||
ixx="${1.375 * 0.001 * 0.001 * rotor_velocity_slowdown_sim}"
|
||||
iyy="${2.153 * 0.001 * 0.001 * rotor_velocity_slowdown_sim}"
|
||||
izz="${1.375 * 0.001 * 0.001 * rotor_velocity_slowdown_sim}"
|
||||
ixy="0.0" ixz="0.0" iyz="0.0" /> <!-- [kg.m^2] [kg.m^2] [kg.m^2] [kg.m^2] [kg.m^2] [kg.m^2] -->
|
||||
</xacro:property>
|
||||
</xacro:if>
|
||||
|
||||
<!-- Values for a cuboid (cuboid. Height=3mm, width=15mm) -->
|
||||
<xacro:unless value="$(arg use_clover_physics)">
|
||||
<xacro:property name="rotor_inertia">
|
||||
<inertia
|
||||
ixx="${1/12 * mass_rotor * (0.015 * 0.015 + 0.003 * 0.003) * rotor_velocity_slowdown_sim}"
|
||||
iyy="${1/12 * mass_rotor * (4 * radius_rotor * radius_rotor + 0.003 * 0.003) * rotor_velocity_slowdown_sim}"
|
||||
izz="${1/12 * mass_rotor * (4 * radius_rotor * radius_rotor + 0.015 * 0.015) * rotor_velocity_slowdown_sim}"
|
||||
ixy="0.0" ixz="0.0" iyz="0.0" /> <!-- [kg.m^2] [kg.m^2] [kg.m^2] [kg.m^2] [kg.m^2] [kg.m^2] -->
|
||||
</xacro:property>
|
||||
</xacro:unless>
|
||||
|
||||
<!-- Included URDF Files -->
|
||||
<xacro:include filename="$(find clover_description)/urdf/clover/clover4_macros.xacro" />
|
||||
|
||||
<!-- Instantiate multirotor_base_macro once -->
|
||||
<xacro:clover4_base_macro
|
||||
robot_namespace="${namespace}"
|
||||
mass="${mass}"
|
||||
body_width="${body_width}"
|
||||
body_height="${body_height}"
|
||||
>
|
||||
<xacro:insert_block name="body_inertia" />
|
||||
</xacro:clover4_base_macro>
|
||||
|
||||
<!-- Instantiate rotors -->
|
||||
<xacro:vertical_clover_rotor
|
||||
robot_namespace="${namespace}"
|
||||
suffix="front_right"
|
||||
direction="ccw"
|
||||
motor_constant="${motor_constant}"
|
||||
moment_constant="${moment_constant}"
|
||||
parent="base_link"
|
||||
mass_rotor="${mass_rotor}"
|
||||
radius_rotor="${radius_rotor}"
|
||||
time_constant_up="${time_constant_up}"
|
||||
time_constant_down="${time_constant_down}"
|
||||
max_rot_velocity="${max_rot_velocity}"
|
||||
motor_number="0"
|
||||
rotor_drag_coefficient="${rotor_drag_coefficient}"
|
||||
rolling_moment_coefficient="${rolling_moment_coefficient}"
|
||||
mesh="clover4/prop_ccw.dae">
|
||||
<origin xyz="${arm_length_front_x} -${arm_length_front_y} ${rotor_offset_top}" rpy="0 0 0" />
|
||||
<xacro:insert_block name="rotor_inertia" />
|
||||
</xacro:vertical_clover_rotor>
|
||||
|
||||
<xacro:vertical_clover_rotor
|
||||
robot_namespace="${namespace}"
|
||||
suffix="back_left"
|
||||
direction="ccw"
|
||||
motor_constant="${motor_constant}"
|
||||
moment_constant="${moment_constant}"
|
||||
parent="base_link"
|
||||
mass_rotor="${mass_rotor}"
|
||||
radius_rotor="${radius_rotor}"
|
||||
time_constant_up="${time_constant_up}"
|
||||
time_constant_down="${time_constant_down}"
|
||||
max_rot_velocity="${max_rot_velocity}"
|
||||
motor_number="1"
|
||||
rotor_drag_coefficient="${rotor_drag_coefficient}"
|
||||
rolling_moment_coefficient="${rolling_moment_coefficient}"
|
||||
mesh="clover4/prop_ccw.dae">
|
||||
<origin xyz="-${arm_length_back_x} ${arm_length_back_y} ${rotor_offset_top}" rpy="0 0 0" />
|
||||
<xacro:insert_block name="rotor_inertia" />
|
||||
</xacro:vertical_clover_rotor>
|
||||
|
||||
<xacro:vertical_clover_rotor robot_namespace="${namespace}"
|
||||
suffix="front_left"
|
||||
direction="cw"
|
||||
motor_constant="${motor_constant}"
|
||||
moment_constant="${moment_constant}"
|
||||
parent="base_link"
|
||||
mass_rotor="${mass_rotor}"
|
||||
radius_rotor="${radius_rotor}"
|
||||
time_constant_up="${time_constant_up}"
|
||||
time_constant_down="${time_constant_down}"
|
||||
max_rot_velocity="${max_rot_velocity}"
|
||||
motor_number="2"
|
||||
rotor_drag_coefficient="${rotor_drag_coefficient}"
|
||||
rolling_moment_coefficient="${rolling_moment_coefficient}"
|
||||
mesh="clover4/prop_cw.dae">
|
||||
<origin xyz="${arm_length_front_x} ${arm_length_front_y} ${rotor_offset_top}" rpy="0 0 0" />
|
||||
<xacro:insert_block name="rotor_inertia" />
|
||||
</xacro:vertical_clover_rotor>
|
||||
|
||||
<xacro:vertical_clover_rotor robot_namespace="${namespace}"
|
||||
suffix="back_right"
|
||||
direction="cw"
|
||||
motor_constant="${motor_constant}"
|
||||
moment_constant="${moment_constant}"
|
||||
parent="base_link"
|
||||
mass_rotor="${mass_rotor}"
|
||||
radius_rotor="${radius_rotor}"
|
||||
time_constant_up="${time_constant_up}"
|
||||
time_constant_down="${time_constant_down}"
|
||||
max_rot_velocity="${max_rot_velocity}"
|
||||
motor_number="3"
|
||||
rotor_drag_coefficient="${rotor_drag_coefficient}"
|
||||
rolling_moment_coefficient="${rolling_moment_coefficient}"
|
||||
mesh="clover4/prop_cw.dae">
|
||||
<origin xyz="-${arm_length_back_x} -${arm_length_back_y} ${rotor_offset_top}" rpy="0 0 0" />
|
||||
<xacro:insert_block name="rotor_inertia" />
|
||||
</xacro:vertical_clover_rotor>
|
||||
|
||||
</robot>
|
||||
613
clover_description/urdf/component_snippets.urdf.xacro
Normal file
@@ -0,0 +1,613 @@
|
||||
<?xml version="1.0"?>
|
||||
<!--
|
||||
Copyright 2015 Fadri Furrer, ASL, ETH Zurich, Switzerland
|
||||
Copyright 2015 Michael Burri, ASL, ETH Zurich, Switzerland
|
||||
Copyright 2015 Mina Kamel, ASL, ETH Zurich, Switzerland
|
||||
Copyright 2015 Janosch Nikolic, ASL, ETH Zurich, Switzerland
|
||||
Copyright 2015 Markus Achtelik, ASL, ETH Zurich, Switzerland
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<robot xmlns:xacro="http://ros.org/wiki/xacro">
|
||||
<!-- Macro to add logging to a bag file. -->
|
||||
<xacro:macro name="bag_plugin_macro"
|
||||
params="namespace bag_file rotor_velocity_slowdown_sim">
|
||||
<gazebo>
|
||||
<plugin filename="librotors_gazebo_bag_plugin.so" name="rosbag">
|
||||
<robotNamespace>${namespace}</robotNamespace>
|
||||
<bagFileName>${bag_file}</bagFileName>
|
||||
<linkName>base_link</linkName>
|
||||
<frameId>base_link</frameId>
|
||||
<commandAttitudeThrustSubTopic>command/attitude</commandAttitudeThrustSubTopic>
|
||||
<commandAttitudeThrustPubTopic>command/attitude</commandAttitudeThrustPubTopic>
|
||||
<rotorVelocitySlowdownSim>${rotor_velocity_slowdown_sim}</rotorVelocitySlowdownSim>
|
||||
</plugin>
|
||||
</gazebo>
|
||||
</xacro:macro>
|
||||
|
||||
<!-- Macro to add a camera. -->
|
||||
<xacro:macro name="camera_macro"
|
||||
params="namespace parent_link camera_suffix frame_rate
|
||||
horizontal_fov image_width image_height image_format min_distance
|
||||
max_distance noise_mean noise_stddev enable_visual *cylinder *origin">
|
||||
<link name="${namespace}/camera_${camera_suffix}_link">
|
||||
<collision>
|
||||
<origin xyz="0 0 0" rpy="0 0 0" />
|
||||
<geometry>
|
||||
<xacro:insert_block name="cylinder" />
|
||||
</geometry>
|
||||
</collision>
|
||||
<xacro:if value="${enable_visual}">
|
||||
<visual>
|
||||
<origin xyz="0 0 0" rpy="0 1.57079632679 0" />
|
||||
<geometry>
|
||||
<xacro:insert_block name="cylinder" />
|
||||
</geometry>
|
||||
<material name="red" />
|
||||
</visual>
|
||||
</xacro:if>
|
||||
<inertial>
|
||||
<mass value="1e-5" />
|
||||
<origin xyz="0 0 0" rpy="0 0 0" />
|
||||
<inertia ixx="1e-6" ixy="0" ixz="0" iyy="1e-6" iyz="0" izz="1e-6" />
|
||||
</inertial>
|
||||
</link>
|
||||
<joint name="${namespace}/camera_${camera_suffix}_joint" type="fixed">
|
||||
<xacro:insert_block name="origin" />
|
||||
<parent link="${parent_link}" />
|
||||
<child link="${namespace}/camera_${camera_suffix}_link" />
|
||||
</joint>
|
||||
<gazebo reference="${namespace}/camera_${camera_suffix}_link">
|
||||
<sensor type="camera" name="${namespace}_camera_${camera_suffix}">
|
||||
<update_rate>${frame_rate}</update_rate>
|
||||
<camera name="head">
|
||||
<horizontal_fov>${horizontal_fov}</horizontal_fov>
|
||||
<image>
|
||||
<width>${image_width}</width>
|
||||
<height>${image_height}</height>
|
||||
<format>${image_format}</format>
|
||||
</image>
|
||||
<clip>
|
||||
<near>${min_distance}</near>
|
||||
<far>${max_distance}</far>
|
||||
</clip>
|
||||
<noise>
|
||||
<type>gaussian</type>
|
||||
<!-- Noise is sampled independently per pixel on each frame.
|
||||
That pixel's noise value is added to each of its color
|
||||
channels, which at that point lie in the range [0,1]. -->
|
||||
<mean>${noise_mean}</mean>
|
||||
<stddev>${noise_stddev}</stddev>
|
||||
</noise>
|
||||
</camera>
|
||||
<plugin name="${namespace}_camera_${camera_suffix}_controller" filename="libgazebo_ros_camera.so">
|
||||
<robotNamespace>${namespace}</robotNamespace>
|
||||
<alwaysOn>true</alwaysOn>
|
||||
<updateRate>${frame_rate}</updateRate>
|
||||
<cameraName>camera_${camera_suffix}</cameraName>
|
||||
<imageTopicName>image_raw</imageTopicName>
|
||||
<cameraInfoTopicName>camera_info</cameraInfoTopicName>
|
||||
<frameName>camera_${camera_suffix}_link</frameName>
|
||||
<hackBaseline>0.0</hackBaseline>
|
||||
<distortionK1>0.0</distortionK1>
|
||||
<distortionK2>0.0</distortionK2>
|
||||
<distortionK3>0.0</distortionK3>
|
||||
<distortionT1>0.0</distortionT1>
|
||||
<distortionT2>0.0</distortionT2>
|
||||
</plugin>
|
||||
</sensor>
|
||||
</gazebo>
|
||||
</xacro:macro>
|
||||
|
||||
<!-- Macro to add the controller interface. -->
|
||||
<xacro:macro name="controller_plugin_macro" params="namespace imu_sub_topic">
|
||||
<gazebo>
|
||||
<plugin name="controller_interface" filename="libgazebo_controller_interface.so">
|
||||
<robotNamespace>${namespace}</robotNamespace>
|
||||
<commandAttitudeThrustSubTopic>/command/attitude</commandAttitudeThrustSubTopic>
|
||||
<commandRateThrustSubTopic>/command/rate</commandRateThrustSubTopic>
|
||||
<commandMotorSpeedSubTopic>/command/motor_speed</commandMotorSpeedSubTopic>
|
||||
<imuSubTopic>/${imu_sub_topic}</imuSubTopic>
|
||||
<motorSpeedCommandPubTopic>/gazebo/command/motor_speed</motorSpeedCommandPubTopic>
|
||||
</plugin>
|
||||
</gazebo>
|
||||
</xacro:macro>
|
||||
|
||||
<!-- Macro to add the gps_plugin. -->
|
||||
<xacro:macro name="gps_plugin_macro" params="namespace gps_noise">
|
||||
<gazebo>
|
||||
<plugin name="gps_plugin" filename="libgazebo_gps_plugin.so">
|
||||
<robotNamespace>${namespace}</robotNamespace>
|
||||
<gpsNoise>${gps_noise}</gpsNoise>
|
||||
</plugin>
|
||||
</gazebo>
|
||||
</xacro:macro>
|
||||
|
||||
<!-- Macro to add the magnetometer_plugin. -->
|
||||
<xacro:macro name="magnetometer_plugin_macro" params="namespace pub_rate noise_density random_walk bias_correlation_time mag_topic">
|
||||
<gazebo>
|
||||
<plugin name="magnetometer_plugin" filename="libgazebo_magnetometer_plugin.so">
|
||||
<robotNamespace>${namespace}</robotNamespace>
|
||||
<pubRate>${pub_rate}</pubRate>
|
||||
<noiseDensity>${noise_density}</noiseDensity>
|
||||
<randomWalk>${random_walk}</randomWalk>
|
||||
<biasCorrelationTime>${bias_correlation_time}</biasCorrelationTime>
|
||||
<magTopic>${mag_topic}</magTopic>
|
||||
</plugin>
|
||||
</gazebo>
|
||||
</xacro:macro>
|
||||
|
||||
<!-- Macro to add the barometer_plugin. -->
|
||||
<xacro:macro name="barometer_plugin_macro" params="namespace pub_rate baro_topic">
|
||||
<gazebo>
|
||||
<plugin name="barometer_plugin" filename="libgazebo_barometer_plugin.so">
|
||||
<robotNamespace>${namespace}</robotNamespace>
|
||||
<pubRate>${pub_rate}</pubRate>
|
||||
<baroTopic>${baro_topic}</baroTopic>
|
||||
</plugin>
|
||||
</gazebo>
|
||||
</xacro:macro>
|
||||
|
||||
<!-- Macro to add the mavlink interface. -->
|
||||
<xacro:macro name="mavlink_interface_macro" params="namespace imu_sub_topic gps_sub_topic mag_sub_topic baro_sub_topic mavlink_addr mavlink_udp_port mavlink_tcp_port serial_enabled serial_device baudrate qgc_addr qgc_udp_port sdk_addr sdk_udp_port hil_mode hil_state_level vehicle_is_tailsitter send_vision_estimation send_odometry enable_lockstep use_tcp">
|
||||
<gazebo>
|
||||
<plugin name="mavlink_interface" filename="libgazebo_mavlink_interface.so">
|
||||
<robotNamespace>${namespace}</robotNamespace>
|
||||
<imuSubTopic>${imu_sub_topic}</imuSubTopic>
|
||||
<gpsSubTopic>${gps_sub_topic}</gpsSubTopic>
|
||||
<magSubTopic>${mag_sub_topic}</magSubTopic>
|
||||
<baroSubTopic>${baro_sub_topic}</baroSubTopic>
|
||||
<mavlink_addr>$(arg mavlink_addr)</mavlink_addr>
|
||||
<mavlink_udp_port>$(arg mavlink_udp_port)</mavlink_udp_port>
|
||||
<mavlink_tcp_port>$(arg mavlink_tcp_port)</mavlink_tcp_port>
|
||||
<serialEnabled>$(arg serial_enabled)</serialEnabled>
|
||||
<serialDevice>$(arg serial_device)</serialDevice>
|
||||
<baudRate>$(arg baudrate)</baudRate>
|
||||
<qgc_addr>$(arg qgc_addr)</qgc_addr>
|
||||
<qgc_udp_port>$(arg qgc_udp_port)</qgc_udp_port>
|
||||
<sdk_addr>$(arg sdk_addr)</sdk_addr>
|
||||
<sdk_udp_port>$(arg sdk_udp_port)</sdk_udp_port>
|
||||
<hil_mode>$(arg hil_mode)</hil_mode>
|
||||
<hil_state_level>$(arg hil_state_level)</hil_state_level>
|
||||
<vehicle_is_tailsitter>$(arg vehicle_is_tailsitter)</vehicle_is_tailsitter>
|
||||
<send_vision_estimation>$(arg send_vision_estimation)</send_vision_estimation>
|
||||
<send_odometry>$(arg send_odometry)</send_odometry>
|
||||
<enable_lockstep>$(arg enable_lockstep)</enable_lockstep>
|
||||
<use_tcp>$(arg use_tcp)</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>
|
||||
<!-- joint limits [-0.524, 0.524] -->
|
||||
<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>
|
||||
<!-- joint limits [-0.524, 0.524] -->
|
||||
<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>
|
||||
</gazebo>
|
||||
</xacro:macro>
|
||||
|
||||
<!-- Macro to add an IMU. -->
|
||||
<xacro:macro name="imu_plugin_macro"
|
||||
params="namespace imu_suffix parent_link imu_topic
|
||||
mass_imu_sensor gyroscope_noise_density gyroscopoe_random_walk
|
||||
gyroscope_bias_correlation_time gyroscope_turn_on_bias_sigma
|
||||
accelerometer_noise_density accelerometer_random_walk
|
||||
accelerometer_bias_correlation_time accelerometer_turn_on_bias_sigma
|
||||
*inertia *origin">
|
||||
<!-- IMU link -->
|
||||
<link name="${namespace}/imu${imu_suffix}_link">
|
||||
<inertial>
|
||||
<xacro:insert_block name="inertia" />
|
||||
<mass value="${mass_imu_sensor}" /> <!-- [kg] -->
|
||||
<origin xyz="0 0 0" rpy="0 0 0" />
|
||||
</inertial>
|
||||
</link>
|
||||
<!-- IMU joint -->
|
||||
<!-- For some reason, PX4 (and RotorS before them) used a "revolute" joint. Since IMU should be fixed relative to the drone frame, I'm changing this to "fixed", we'll see how that goes. -sfalexrog -->
|
||||
<joint name="${namespace}/imu${imu_suffix}_joint" type="fixed">
|
||||
<xacro:insert_block name="origin" />
|
||||
<parent link="${parent_link}" />
|
||||
<child link="${namespace}/imu${imu_suffix}_link" />
|
||||
<!-- <limit upper="0" lower="0" effort="0" velocity="0" /> -->
|
||||
</joint>
|
||||
<!-- Well, now I know why: because Gazebo helpfully tries to "lump together" all fixed joints. Apparently there's a way to disable this behavior that is implemented here in its entirety: -->
|
||||
<gazebo reference="${namespace}/imu${imu_suffix}_joint">
|
||||
<disableFixedJointLumping>true</disableFixedJointLumping>
|
||||
<preserveFixedJoint>true</preserveFixedJoint>
|
||||
</gazebo>
|
||||
<gazebo>
|
||||
<plugin filename="libgazebo_imu_plugin.so" name="rotors_gazebo_imu${imu_suffix}_plugin">
|
||||
<!-- A good description of the IMU parameters can be found in the kalibr documentation:
|
||||
https://github.com/ethz-asl/kalibr/wiki/IMU-Noise-Model-and-Intrinsics -->
|
||||
<robotNamespace>${namespace}</robotNamespace> <!-- (string, required): ros namespace in which the messages are published -->
|
||||
<linkName>${namespace}/imu${imu_suffix}_link</linkName> <!-- (string, required): name of the body which holds the IMU sensor -->
|
||||
<imuTopic>${imu_topic}</imuTopic> <!-- (string): name of the sensor output topic and prefix of service names (defaults to imu) -->
|
||||
<gyroscopeNoiseDensity>${gyroscope_noise_density}</gyroscopeNoiseDensity> <!-- Gyroscope noise density (two-sided spectrum) [rad/s/sqrt(Hz)] -->
|
||||
<gyroscopeRandomWalk>${gyroscopoe_random_walk}</gyroscopeRandomWalk> <!-- Gyroscope bias random walk [rad/s/s/sqrt(Hz)] -->
|
||||
<gyroscopeBiasCorrelationTime>${gyroscope_bias_correlation_time}</gyroscopeBiasCorrelationTime> <!-- Gyroscope bias correlation time constant [s] -->
|
||||
<gyroscopeTurnOnBiasSigma>${gyroscope_turn_on_bias_sigma}</gyroscopeTurnOnBiasSigma> <!-- Gyroscope turn on bias standard deviation [rad/s] -->
|
||||
<accelerometerNoiseDensity>${accelerometer_noise_density}</accelerometerNoiseDensity> <!-- Accelerometer noise density (two-sided spectrum) [m/s^2/sqrt(Hz)] -->
|
||||
<accelerometerRandomWalk>${accelerometer_random_walk}</accelerometerRandomWalk> <!-- Accelerometer bias random walk. [m/s^2/s/sqrt(Hz)] -->
|
||||
<accelerometerBiasCorrelationTime>${accelerometer_bias_correlation_time}</accelerometerBiasCorrelationTime> <!-- Accelerometer bias correlation time constant [s] -->
|
||||
<accelerometerTurnOnBiasSigma>${accelerometer_turn_on_bias_sigma}</accelerometerTurnOnBiasSigma> <!-- Accelerometer turn on bias standard deviation [m/s^2] -->
|
||||
</plugin>
|
||||
</gazebo>
|
||||
</xacro:macro>
|
||||
|
||||
<!-- Macro to add a generic odometry sensor. -->
|
||||
<xacro:macro name="odometry_plugin_macro"
|
||||
params="
|
||||
namespace odometry_sensor_suffix parent_link pose_topic pose_with_covariance_topic
|
||||
position_topic transform_topic odometry_topic parent_frame_id
|
||||
mass_odometry_sensor measurement_divisor measurement_delay unknown_delay
|
||||
noise_normal_position noise_normal_quaternion noise_normal_linear_velocity
|
||||
noise_normal_angular_velocity noise_uniform_position
|
||||
noise_uniform_quaternion noise_uniform_linear_velocity
|
||||
noise_uniform_angular_velocity enable_odometry_map odometry_map
|
||||
image_scale *inertia *origin">
|
||||
<!-- odometry link -->
|
||||
<link name="${namespace}/odometry_sensor${odometry_sensor_suffix}_link">
|
||||
<inertial>
|
||||
<xacro:insert_block name="inertia" />
|
||||
<mass value="${mass_odometry_sensor}" /> <!-- [kg] -->
|
||||
</inertial>
|
||||
</link>
|
||||
<!-- odometry joint -->
|
||||
<joint name="${namespace}/odometry_sensor${odometry_sensor_suffix}_joint" type="revolute">
|
||||
<parent link="${parent_link}" />
|
||||
<xacro:insert_block name="origin" />
|
||||
<child link="${namespace}/odometry_sensor${odometry_sensor_suffix}_link" />
|
||||
<limit upper="0" lower="0" effort="0" velocity="0" />
|
||||
</joint>
|
||||
<gazebo>
|
||||
<plugin filename="librotors_gazebo_odometry_plugin.so" name="odometry_sensor${odometry_sensor_suffix}">
|
||||
<linkName>${namespace}/odometry_sensor${odometry_sensor_suffix}_link</linkName>
|
||||
<robotNamespace>${namespace}</robotNamespace>
|
||||
<poseTopic>${pose_topic}</poseTopic>
|
||||
<poseWithCovarianceTopic>${pose_with_covariance_topic}</poseWithCovarianceTopic>
|
||||
<positionTopic>${position_topic}</positionTopic>
|
||||
<transformTopic>${transform_topic}</transformTopic>
|
||||
<odometryTopic>${odometry_topic}</odometryTopic>
|
||||
<parentFrameId>${parent_frame_id}</parentFrameId> <!-- Use the scoped link name here. e.g. Model::link. -->
|
||||
<measurementDivisor>${measurement_divisor}</measurementDivisor> <!-- only every (seq % measurementDivisor) == 0 measurement is published [int] -->
|
||||
<measurementDelay>${measurement_delay}</measurementDelay> <!-- time that measurement gets held back before it's published in [simulation cycles (int)] -->
|
||||
<unknownDelay>${unknown_delay}</unknownDelay> <!-- additional delay, that just gets added to the timestamp [s] -->
|
||||
<noiseNormalPosition>${noise_normal_position}</noiseNormalPosition> <!-- standard deviation of additive white gaussian noise [m] -->
|
||||
<noiseNormalQuaternion>${noise_normal_quaternion}</noiseNormalQuaternion> <!-- standard deviation white gaussian noise [rad]: q_m = q*quaternionFromSmallAngleApproximation(noiseNormalQ) -->
|
||||
<noiseNormalLinearVelocity>${noise_normal_linear_velocity}</noiseNormalLinearVelocity> <!-- standard deviation of additive white gaussian noise [m/s] -->
|
||||
<noiseNormalAngularVelocity>${noise_normal_angular_velocity}</noiseNormalAngularVelocity> <!-- standard deviation of additive white gaussian noise [rad/s] -->
|
||||
<noiseUniformPosition>${noise_uniform_position}</noiseUniformPosition> <!-- symmetric bounds of uniform noise [m] -->
|
||||
<noiseUniformQuaternion>${noise_uniform_quaternion}</noiseUniformQuaternion> <!-- symmetric bounds of uniform noise [rad], computation see above -->
|
||||
<noiseUniformLinearVelocity>${noise_uniform_linear_velocity}</noiseUniformLinearVelocity> <!-- symmetric bounds of uniform noise [m/s] -->
|
||||
<noiseUniformAngularVelocity>${noise_uniform_angular_velocity}</noiseUniformAngularVelocity> <!-- symmetric bounds of uniform noise [rad/s] -->
|
||||
<xacro:if value="${enable_odometry_map}">
|
||||
<covarianceImage>package://rotors_gazebo/resource/${odometry_map}</covarianceImage>
|
||||
<covarianceImageScale>${image_scale}</covarianceImageScale>
|
||||
</xacro:if>
|
||||
</plugin>
|
||||
</gazebo>
|
||||
</xacro:macro>
|
||||
|
||||
<!-- Macro to add the wind plugin. -->
|
||||
<xacro:macro name="wind_plugin_macro"
|
||||
params="namespace xyz_offset wind_direction wind_force_mean
|
||||
wind_gust_direction wind_gust_duration wind_gust_start
|
||||
wind_gust_force_mean">
|
||||
<gazebo>
|
||||
<plugin filename="libgazebo_wind_plugin.so" name="wind_plugin">
|
||||
<frameId>base_link</frameId>
|
||||
<linkName>base_link</linkName>
|
||||
<robotNamespace>${namespace}</robotNamespace>
|
||||
<xyzOffset>${xyz_offset}</xyzOffset> <!-- [m] [m] [m] -->
|
||||
<windDirection>${wind_direction}</windDirection>
|
||||
<windForceMean>${wind_force_mean}</windForceMean> <!-- [N] -->
|
||||
<windGustDirection>${wind_gust_direction}</windGustDirection>
|
||||
<windGustDuration>${wind_gust_duration}</windGustDuration> <!-- [s] -->
|
||||
<windGustStart>${wind_gust_start}</windGustStart> <!-- [s] -->
|
||||
<windGustForceMean>${wind_gust_force_mean}</windGustForceMean> <!-- [N] -->
|
||||
</plugin>
|
||||
</gazebo>
|
||||
</xacro:macro>
|
||||
|
||||
<!-- VI sensor macros -->
|
||||
<!-- Macro to add a VI-sensor camera. -->
|
||||
<xacro:macro name="vi_sensor_camera_macro"
|
||||
params="namespace parent_link camera_suffix frame_rate *origin">
|
||||
<xacro:camera_macro
|
||||
namespace="${namespace}"
|
||||
parent_link="${parent_link}"
|
||||
camera_suffix="${camera_suffix}"
|
||||
frame_rate="${frame_rate}"
|
||||
horizontal_fov="1.3962634"
|
||||
image_width="752"
|
||||
image_height="480"
|
||||
image_format="L8"
|
||||
min_distance="0.02"
|
||||
max_distance="30"
|
||||
noise_mean="0.0"
|
||||
noise_stddev="0.007"
|
||||
enable_visual="false">
|
||||
<cylinder length="0.01" radius="0.007" />
|
||||
<xacro:insert_block name="origin" />
|
||||
</xacro:camera_macro>
|
||||
</xacro:macro>
|
||||
|
||||
<!-- Macro to add a depth camera on the VI-sensor. -->
|
||||
<xacro:macro name="vi_sensor_depth_macro"
|
||||
params="namespace parent_link camera_suffix frame_rate *origin">
|
||||
<link name="${namespace}/camera_${camera_suffix}_link">
|
||||
<collision>
|
||||
<origin xyz="0 0 0" rpy="0 0 0" />
|
||||
<geometry>
|
||||
<cylinder length="0.01" radius="0.007" />
|
||||
</geometry>
|
||||
</collision>
|
||||
<inertial>
|
||||
<mass value="1e-5" />
|
||||
<origin xyz="0 0 0" rpy="0 0 0" />
|
||||
<inertia ixx="1e-6" ixy="0" ixz="0" iyy="1e-6" iyz="0" izz="1e-6" />
|
||||
</inertial>
|
||||
</link>
|
||||
<joint name="${namespace}/camera_${camera_suffix}_joint" type="fixed">
|
||||
<xacro:insert_block name="origin" />
|
||||
<parent link="${parent_link}" />
|
||||
<child link="${namespace}/camera_${camera_suffix}_link" />
|
||||
<limit upper="0" lower="0" effort="0" velocity="0" />
|
||||
</joint>
|
||||
<!-- Optical center of camera -->
|
||||
<link name="${namespace}/camera_${camera_suffix}_optical_center_link" />
|
||||
<joint name="${namespace}/camera_${camera_suffix}_optical_center_joint" type="fixed">
|
||||
<origin xyz="0 0 0" rpy="${-PI/2} 0 ${-PI/2}" />
|
||||
<parent link="${namespace}/camera_${camera_suffix}_link" />
|
||||
<child link="${namespace}/camera_${camera_suffix}_optical_center_link" />
|
||||
<limit upper="0" lower="0" effort="0" velocity="0" />
|
||||
</joint>
|
||||
<gazebo reference="${namespace}/camera_${camera_suffix}_link">
|
||||
<sensor type="depth" name="${namespace}_camera_{camera_suffix}">
|
||||
<always_on>true</always_on>
|
||||
<update_rate>${frame_rate}</update_rate>
|
||||
<camera>
|
||||
<horizontal_fov>2</horizontal_fov>
|
||||
<image>
|
||||
<format>L8</format>
|
||||
<width>640</width>
|
||||
<height>480</height>
|
||||
</image>
|
||||
<clip>
|
||||
<near>0.01</near>
|
||||
<far>100</far>
|
||||
</clip>
|
||||
</camera>
|
||||
<plugin name="${namespace}_camera_{camera_suffix}" filename="libgazebo_ros_openni_kinect.so">
|
||||
<robotNamespace>${namespace}</robotNamespace>
|
||||
<alwaysOn>true</alwaysOn>
|
||||
<baseline>0.11</baseline>
|
||||
<updateRate>${frame_rate}</updateRate>
|
||||
<cameraName>camera_${camera_suffix}</cameraName>
|
||||
<imageTopicName>camera/image_raw</imageTopicName>
|
||||
<cameraInfoTopicName>camera/camera_info</cameraInfoTopicName>
|
||||
<depthImageTopicName>depth/disparity</depthImageTopicName>
|
||||
<depthImageCameraInfoTopicName>depth/camera_info</depthImageCameraInfoTopicName>
|
||||
<pointCloudTopicName>depth/points</pointCloudTopicName>
|
||||
<frameName>camera_${camera_suffix}_optical_center_link</frameName>
|
||||
<pointCloudCutoff>0.5</pointCloudCutoff>
|
||||
<distortionK1>0.0</distortionK1>
|
||||
<distortionK2>0.0</distortionK2>
|
||||
<distortionK3>0.0</distortionK3>
|
||||
<distortionT1>0.0</distortionT1>
|
||||
<distortionT2>0.0</distortionT2>
|
||||
</plugin>
|
||||
</sensor>
|
||||
</gazebo>
|
||||
</xacro:macro>
|
||||
|
||||
<!-- VI-Sensor Macro -->
|
||||
<xacro:macro name="vi_sensor_macro" params="namespace parent_link *origin">
|
||||
<!-- Vi Sensor Link -->
|
||||
<link name="${namespace}/vi_sensor_link">
|
||||
<collision>
|
||||
<origin xyz="0 0 0" rpy="0 0 0" />
|
||||
<geometry>
|
||||
<box size="0.03 0.133 0.057" />
|
||||
</geometry>
|
||||
</collision>
|
||||
|
||||
<visual>
|
||||
<origin xyz="0 0 0" rpy="0 0 0" />
|
||||
<geometry>
|
||||
<mesh filename="package://rotors_description/meshes/vi_sensor.dae" scale="1 1 1" />
|
||||
</geometry>
|
||||
</visual>
|
||||
|
||||
<inertial>
|
||||
<mass value="0.13" />
|
||||
<origin xyz="0 0 0" rpy="0 0 0" />
|
||||
<inertia ixx="1e-6" ixy="0" ixz="0" iyy="1e-6" iyz="0" izz="1e-6" />
|
||||
</inertial>
|
||||
</link>
|
||||
<joint name="{namespace}_vi_sensor_joint" type="fixed">
|
||||
<xacro:insert_block name="origin" />
|
||||
<parent link="${parent_link}" />
|
||||
<child link="${namespace}/vi_sensor_link" />
|
||||
</joint>
|
||||
<!-- Cameras -->
|
||||
<xacro:if value="$(arg enable_cameras)">
|
||||
<!-- Left Camera -->
|
||||
<xacro:vi_sensor_camera_macro
|
||||
namespace="${namespace}" parent_link="${namespace}/vi_sensor_link"
|
||||
camera_suffix="left" frame_rate="30.0">
|
||||
<origin xyz="0.015 0.055 0.0065" rpy="0 0 0" />
|
||||
</xacro:vi_sensor_camera_macro>
|
||||
<!-- Right Camera -->
|
||||
<xacro:vi_sensor_camera_macro namespace="${namespace}"
|
||||
parent_link="${namespace}/vi_sensor_link"
|
||||
camera_suffix="right" frame_rate="30.0">
|
||||
<origin xyz="0.015 -0.055 0.0065" rpy="0 0 0" />
|
||||
</xacro:vi_sensor_camera_macro>
|
||||
</xacro:if>
|
||||
|
||||
<!-- Depth Sensor -->
|
||||
<xacro:if value="$(arg enable_depth)">
|
||||
<xacro:vi_sensor_depth_macro
|
||||
namespace="${namespace}" parent_link="${namespace}/vi_sensor_link"
|
||||
camera_suffix="depth" frame_rate="30.0">
|
||||
<origin xyz="0.015 0.055 0.0065" rpy="0 0 0" />
|
||||
</xacro:vi_sensor_depth_macro>
|
||||
</xacro:if>
|
||||
|
||||
<!-- Groundtruth -->
|
||||
<xacro:if value="$(arg enable_ground_truth)">
|
||||
<!-- Odometry Sensor -->
|
||||
<xacro:odometry_plugin_macro
|
||||
namespace="${namespace}/ground_truth"
|
||||
odometry_sensor_suffix=""
|
||||
parent_link="${namespace}/vi_sensor_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>
|
||||
|
||||
<!-- ADIS16448 IMU. -->
|
||||
<xacro:imu_plugin_macro
|
||||
namespace="${namespace}"
|
||||
imu_suffix=""
|
||||
parent_link="${namespace}/vi_sensor_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.015 0 0.0113" rpy="0 0 0" />
|
||||
</xacro:imu_plugin_macro>
|
||||
</xacro:macro>
|
||||
|
||||
</robot>
|
||||
69
clover_description/urdf/leds/led_strip.xacro
Normal file
@@ -0,0 +1,69 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<robot name="led_strip" xmlns:xacro="http://ros.org/wiki/xacro">
|
||||
|
||||
<!-- LED strip macro
|
||||
Parameters:
|
||||
name: Name of the LED strip
|
||||
parent: Name of the parent link (likely to be base_link)
|
||||
radius: Radius of the strip
|
||||
bulb_radius: Radius (for spheres)/side (for boxes) of the LED
|
||||
led_count: Number of LEDs in a strip
|
||||
use_plugin: Attach the controller plugin to LEDs
|
||||
-->
|
||||
<xacro:macro name="led_strip" params="name parent radius:=0.08 bulb_radius:=0.005 led_count:=58 shift:=0 use_plugin:=false x:=0 y:=0 z:=0 roll:=0 pitch:=0 yaw:=0">
|
||||
<joint name="${name}_joint" type="fixed">
|
||||
<origin xyz="${x} ${y} ${z}"
|
||||
rpy="${roll} ${pitch} ${yaw}"/>
|
||||
<parent link="${parent}"/>
|
||||
<child link="${name}_link"/>
|
||||
</joint>
|
||||
|
||||
<link name="${name}_link">
|
||||
<inertial>
|
||||
<origin xyz="0 0 0"/>
|
||||
<mass value="0.0001"/>
|
||||
<inertia ixx="0.001" ixy="0.0" ixz="0.0" iyy="0.001" iyz="0.0" izz="0.001"/>
|
||||
</inertial>
|
||||
|
||||
<xacro:macro name="loop_links" params="link_i links_total">
|
||||
<visual>
|
||||
<origin xyz="${radius*cos(2 * pi * link_i / links_total)} ${radius*sin(2 * pi * link_i / links_total)} 0"
|
||||
rpy="0 0 ${2 * pi * link_i / links_total}"/>
|
||||
<geometry>
|
||||
<box size="${bulb_radius} ${bulb_radius} ${bulb_radius}"/>
|
||||
</geometry>
|
||||
</visual>
|
||||
<xacro:unless value="${link_i + 1 == links_total}">
|
||||
<xacro:loop_links link_i="${link_i + 1}" links_total="${links_total}"/>
|
||||
</xacro:unless>
|
||||
</xacro:macro>
|
||||
<xacro:loop_links link_i="0" links_total="${led_count}"/>
|
||||
</link>
|
||||
|
||||
<xacro:if value="${use_plugin}">
|
||||
<gazebo reference="${name}_link">
|
||||
<visual>
|
||||
<plugin name="${name}_controller" filename="libsim_leds.so">
|
||||
<robotNamespace></robotNamespace>
|
||||
<ledCount>${led_count}</ledCount>
|
||||
</plugin>
|
||||
<cast_shadows>false</cast_shadows>
|
||||
<material>
|
||||
<ambient>0 0 0 1</ambient>
|
||||
<diffuse>0 0 0 1</diffuse>
|
||||
<specular>0 0 0 1</specular>
|
||||
<emissive>0 0 0 1</emissive>
|
||||
</material>
|
||||
</visual>
|
||||
</gazebo>
|
||||
</xacro:if>
|
||||
<gazebo reference="${name}_joint">
|
||||
<disableFixedJointLumping>true</disableFixedJointLumping>
|
||||
<preserveFixedJoint>true</preserveFixedJoint>
|
||||
</gazebo>
|
||||
<!-- <gazebo>
|
||||
<static>true</static>
|
||||
</gazebo> -->
|
||||
</xacro:macro>
|
||||
|
||||
</robot>
|
||||
73
clover_description/urdf/sensors/distance_sensor.urdf.xacro
Normal file
@@ -0,0 +1,73 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<robot name="rpi_camera" xmlns:xacro="http://ros.org/wiki/xacro">
|
||||
<xacro:include filename="$(find clover_description)/urdf/camera_sensor.urdf.xacro"/>
|
||||
|
||||
<xacro: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">
|
||||
<origin xyz="${x} ${y} ${z}"
|
||||
rpy="${roll} ${pitch} ${yaw}"/>
|
||||
<parent link="${parent}"/>
|
||||
<child link="${name}_link"/>
|
||||
</joint>
|
||||
|
||||
<link name="${name}_link">
|
||||
<visual>
|
||||
<origin xyz="0 0 0" rpy="0 0 0"/>
|
||||
<geometry>
|
||||
<box size="0.001 0.005 0.005"/>
|
||||
</geometry>
|
||||
</visual>
|
||||
<collision>
|
||||
<origin xyz="0 0 0" rpy="0 0 0"/>
|
||||
<geometry>
|
||||
<box size="0.001 0.001 0.005"/>
|
||||
</geometry>
|
||||
</collision>
|
||||
<inertial>
|
||||
<mass value="0.015"/>
|
||||
<origin xyz="0 0 0"/>
|
||||
<inertia ixx="4.15e-6" iyy="2.407e-6" izz="2.407e-6" ixy="0" ixz="0" iyz="0"/>
|
||||
</inertial>
|
||||
</link>
|
||||
|
||||
<gazebo reference="${name}_joint">
|
||||
<disableFixedJointLumping>true</disableFixedJointLumping>
|
||||
<preserveFixedJoint>true</preserveFixedJoint>
|
||||
</gazebo>
|
||||
|
||||
<gazebo reference="${name}_link">
|
||||
<sensor type="ray" name="${name}">
|
||||
<pose>0 0 0 0 0 0</pose>
|
||||
<ray>
|
||||
<scan>
|
||||
<horizontal>
|
||||
<samples>1</samples>
|
||||
<resolution>1</resolution>
|
||||
<min_angle>-0</min_angle>
|
||||
<max_angle>0</max_angle>
|
||||
</horizontal>
|
||||
</scan>
|
||||
<range>
|
||||
<min>0.03</min> <!-- Note: This is a hack, since the rangefinder is inside the drone collision box -->
|
||||
<max>${range_max}</max>
|
||||
<resolution>${resolution}</resolution>
|
||||
</range>
|
||||
</ray>
|
||||
<plugin name="laser" filename="libgazebo_ros_range.so">
|
||||
<robotNamespace></robotNamespace> <!-- FIXME: fill namespace? -->
|
||||
<topicName>/rangefinder/range</topicName>
|
||||
<frameName>rangefinder</frameName>
|
||||
<radiation>infrared</radiation>
|
||||
<fov>0.01</fov>
|
||||
<gaussianNoise>0.001</gaussianNoise>
|
||||
<updateRate>${rate}</updateRate>
|
||||
<min_distance>${range_min}</min_distance>
|
||||
<max_distance>${range_max}</max_distance>
|
||||
</plugin>
|
||||
<always_on>1</always_on>
|
||||
<update_rate>${rate}</update_rate>
|
||||
<visualize>true</visualize>
|
||||
</sensor>
|
||||
</gazebo>
|
||||
</xacro:macro>
|
||||
</robot>
|
||||
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<robot name="pinhole_camera" xmlns:xacro="http://ros.org/wiki/xacro">
|
||||
<xacro:include filename="$(find clover_description)/urdf/camera_sensor.urdf.xacro"/>
|
||||
<xacro:camera_sensor name="main_camera" width="640" height="480" rate="30" horizontal_fov="${120.0*pi/180.0}"/>
|
||||
</robot>
|
||||
60
clover_description/urdf/sensors/rpi_cam.urdf.xacro
Normal file
@@ -0,0 +1,60 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<robot name="rpi_camera" xmlns:xacro="http://ros.org/wiki/xacro">
|
||||
<xacro:include filename="$(find clover_description)/urdf/camera_sensor.urdf.xacro"/>
|
||||
|
||||
<xacro: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">
|
||||
<origin xyz="${x} ${y} ${z}"
|
||||
rpy="${roll} ${pitch} ${yaw}"/>
|
||||
<parent link="${parent}"/>
|
||||
<child link="${name}_link"/>
|
||||
</joint>
|
||||
|
||||
<link name="${name}_link">
|
||||
<visual>
|
||||
<origin xyz="0 0 0" rpy="0 0 0"/>
|
||||
<geometry>
|
||||
<box size="0.005 0.005 0.005"/>
|
||||
</geometry>
|
||||
</visual>
|
||||
<collision>
|
||||
<origin xyz="0 0 0" rpy="0 0 0"/>
|
||||
<geometry>
|
||||
<box size="0.005 0.005 0.005"/>
|
||||
</geometry>
|
||||
</collision>
|
||||
<inertial>
|
||||
<mass value="0.015"/>
|
||||
<origin xyz="0 0 0"/>
|
||||
<inertia ixx="4.15e-6" iyy="2.407e-6" izz="2.407e-6" ixy="0" ixz="0" iyz="0"/>
|
||||
</inertial>
|
||||
</link>
|
||||
<gazebo reference="${name}_joint">
|
||||
<disableFixedJointLumping>true</disableFixedJointLumping>
|
||||
<preserveFixedJoint>true</preserveFixedJoint>
|
||||
</gazebo>
|
||||
<!-- <xacro:camera_sensor_base name="${name}"
|
||||
frame_name="/${name}_optical"
|
||||
width="${width}"
|
||||
height="${height}"
|
||||
rate="${rate}"
|
||||
horizontal_fov="1.6075201319067056"
|
||||
k1="-0.273377"
|
||||
k2="0.0642871"
|
||||
p1="-0.00086158"
|
||||
p2="-0.000443529"
|
||||
k3="-0.00599387"
|
||||
cx="158.0735"
|
||||
cy="108.513"
|
||||
cx_prime="158.0735"
|
||||
/> -->
|
||||
<xacro:camera_sensor name="${name}"
|
||||
width="${width}"
|
||||
height="${height}"
|
||||
rate="${rate}"
|
||||
horizontal_fov="${120 * pi / 180}"
|
||||
do_throttling="${do_throttling}"
|
||||
/>
|
||||
</xacro:macro>
|
||||
|
||||
</robot>
|
||||
60
clover_simulation/CMakeLists.txt
Normal file
@@ -0,0 +1,60 @@
|
||||
cmake_minimum_required(VERSION 3.0)
|
||||
project(clover_simulation)
|
||||
|
||||
find_package(catkin REQUIRED)
|
||||
|
||||
# Gazebo LED strip node
|
||||
|
||||
find_package(gazebo)
|
||||
|
||||
# Gazebo does not seem to export GAZEBO_FOUND, so here's a matching hack
|
||||
if (NOT "${GAZEBO_CONFIG_INCLUDED}")
|
||||
message(STATUS "Gazebo not found, skipping")
|
||||
return()
|
||||
endif()
|
||||
|
||||
find_package(catkin REQUIRED COMPONENTS
|
||||
roscpp
|
||||
led_msgs
|
||||
gazebo_ros
|
||||
gazebo_plugins
|
||||
rospy
|
||||
)
|
||||
|
||||
catkin_python_setup()
|
||||
|
||||
catkin_package(
|
||||
CATKIN_DEPENDS led_msgs gazebo_ros gazebo_plugins
|
||||
)
|
||||
|
||||
# Gazebo LED plugin
|
||||
|
||||
add_library(sim_leds src/sim_leds.cpp)
|
||||
|
||||
target_include_directories(sim_leds PRIVATE ${catkin_INCLUDE_DIRS} ${GAZEBO_INCLUDE_DIRS})
|
||||
target_link_libraries(sim_leds PRIVATE ${catkin_LIBRARIES} ${GAZEBO_LIBRARIES})
|
||||
target_compile_options(sim_leds PRIVATE -std=c++11)
|
||||
|
||||
add_dependencies(sim_leds ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
|
||||
|
||||
# Gazebo throttling camera plugin
|
||||
# for some reason, CMake does not support per-target link directories, and Gazebo does not put
|
||||
# CameraPlugin into ${GAZEBO_LIBRARIES}
|
||||
|
||||
link_directories(${GAZEBO_LIBRARY_DIRS})
|
||||
|
||||
add_library(throttling_camera src/throttling_camera.cpp)
|
||||
|
||||
target_include_directories(throttling_camera PRIVATE ${catkin_INCLUDE_DIRS} ${GAZEBO_INCLUDE_DIRS})
|
||||
target_link_libraries(throttling_camera PRIVATE ${catkin_LIBRARIES} ${GAZEBO_LIBRARIES} CameraPlugin)
|
||||
target_compile_options(throttling_camera PRIVATE -std=c++11)
|
||||
|
||||
add_dependencies(throttling_camera ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
|
||||
|
||||
install(DIRECTORY launch DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION})
|
||||
install(DIRECTORY meshes DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION})
|
||||
install(DIRECTORY resources DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION})
|
||||
|
||||
catkin_install_python(PROGRAMS scripts/aruco_gen
|
||||
DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
|
||||
)
|
||||
82
clover_simulation/README.md
Normal file
@@ -0,0 +1,82 @@
|
||||
# `clover_simulation` ROS package
|
||||
|
||||
This package provides resources necessary for launching Gazebo simulation with Clover, along with `.launch` files for convenience.
|
||||
|
||||
## Launching the simulation
|
||||
|
||||
Simulation is launched by [`simulator.launch` file](launch/simulator.launch). This `.launch` file assumes that `px4` and `sitl_gazebo` packages are reachable from your current workspace.
|
||||
|
||||
The simulation may be configured by a set of arguments:
|
||||
|
||||
* `mav_id` (*integer*, default: *0*) - MAVLink identifier of the vehicle. **Note**: Multi-vehicle simulation is possible, but requires extensive changes to launch files;
|
||||
* `est` (*string*, default: *lpe*, possible values: *lpe*, *ekf2*) - PX4 estimator selection. Note that this may be overriden in the startup scripts for your craft;
|
||||
* `vehicle` (*string*, default: *clover*) - PX4 vehicle name. Depending on this parameter, different PX4 presets will be loaded. **Note**: The default value, *clover*, requires you to use [Clover-specific PX4 branch](https://github.com/CopterExpress/Firmware/tree/v1.10.1-clever);
|
||||
* `main_camera` (*boolean*, default: *true*) - controls whether the drone will have a vision position estimation camera;
|
||||
* `rangefinder` (*boolean*, default: *true*) - controls whether the drone will have a laser rangefinder;
|
||||
* `led` (*boolean*, default: *true*) - controls whether the drone will have a programmable LED strip;
|
||||
* `gps` (*boolean*, default: *true*) - controls whether the drone will have a simulated GPS module;
|
||||
|
||||
In order to start the simulation, run:
|
||||
|
||||
```bash
|
||||
roslaunch clover_simulation simulator.launch
|
||||
```
|
||||
|
||||
This will start a new Gazebo instance (using `gazebo_ros` package), load a PX4 SITL instance, spawn a Clover model and start Clover ROS nodes. The PX4 console will be accessible in the terminal where `roslaunch` was performed.
|
||||
|
||||
### Changing simulation speed (PX4 1.9+)
|
||||
|
||||
In order to run simulation faster or slower than realtime, use the `PX4_SIM_SPEED_FACTOR` environment variable, [as stated in the PX4 docs](https://dev.px4.io/v1.9.0/en/simulation/#simulation_speed).
|
||||
|
||||
If `PX4_SIM_SPEED_FACTOR` is not set, it is assumed that it is equal to 1.0.
|
||||
|
||||
Note that Gazebo may slow the simulation down automatically. This may not be handled gracefully, so if you notice Gazebo's "Real Time Factor" being significantly lower than your `PX4_SIM_SPEED_FACTOR`, be sure to adjust it accordingly.
|
||||
|
||||
### Changing initial world
|
||||
|
||||
By default, the `simulator.launch` file will start the simulation with [`resources/worlds/clover.world`](resources/worlds/clover.world) as its base world. Note that the `real_time_update_rate` is set to 250 - this is required for PX4 lockstep simulation to work correctly.
|
||||
|
||||
If you wish to create your own world for the simulation, be sure to derive it from `clover.world` to avoid issues with PX4 plugins.
|
||||
|
||||
You may set the world name in `simulator.launch` as the `world_name` parameter for `gazebo_ros` instance.
|
||||
|
||||
### Configuring the vehicle
|
||||
|
||||
`simulator.launch` utilizes the same `clover.launch` file from the `clover` ROS package, so ROS node reconfiguration is the same as on the real drone.
|
||||
|
||||
PX4 may be reconfigured using QGroundControl, just like a real drone. Some parameters may require rebooting the drone, which is performed by shutting the simulated environment down and restarting it.
|
||||
|
||||
PX4 will write its parameters and logs to `${ROS_HOME}/eeprom/parameters` and `${ROS_HOME}/log`, respectively. Note that the log directory naming schema for PX4 logs is different from ROS: PX4 creates log directories based on the current date, which makes them relatively simple to find.
|
||||
|
||||
## LED plugin (sim_leds)
|
||||
|
||||
A visual Gazebo plugin is used for the LED strip. An example of the plugin usage is provided in [`led_strip.xacro`](../clover_description/urdf/leds/led_strip.xacro).
|
||||
|
||||
The plugin accepts the following parameters during instantiation:
|
||||
|
||||
* `robotNamespace` (*string*, default: "") - a ROS namespace for the plugin;
|
||||
* `ledCount` (*integer*, required) - total numer of LEDs in a strip.
|
||||
|
||||
The plugin will provide the following service:
|
||||
|
||||
`led/set_leds` ([*led_msgs/SetLEDs*](https://github.com/CopterExpress/ros_led/blob/v0.0.6/led_msgs/srv/SetLEDs.srv)) - set the LED colors to the provided values.
|
||||
|
||||
The plugin will provide the following topics:
|
||||
|
||||
`led/state` ([*led_msgs/LEDStateArray*](https://github.com/CopterExpress/ros_led/blob/v0.0.6/led_msgs/msg/LEDStateArray.msg)) - current LED strip state.
|
||||
|
||||
Other nodes are not expected to write to `led/state` topic.
|
||||
|
||||
All provided topics and services will be namespaced according to the `robotNamespace` parameter.
|
||||
|
||||
## Throttling camera plugin (throttling_camera)
|
||||
|
||||
By default, Gazebo camera sensors will use their `update_rate` parameter as an upper bound for the actual rate. This may result in much lower rates than expected. This may be fine for object recognition tasks where the camera is not the primary positioning sensor, but is not desirable in our case, when the camera is used for position calculation.
|
||||
|
||||
We provide a Gazebo-ROS plugin for the camera sensor that will throttle down the simulation to maintain update rate. The plugin API is based on the `gazebo_ros_camera` plugin, and respects the following parameters in SDF:
|
||||
|
||||
* `<minUpdateRate>` (*double*, default: same as `<updateRate>`) - least allowed publish/update rate for the camera (in Hz);
|
||||
* `<windowSize>` (*integer*, default: 10) - number of last update intervals that are considered for throttling;
|
||||
* `<maxStDev>` (*double*, default: 0.02) - maximum standard deviation value for update intervals.
|
||||
|
||||
The simulation will be slowed down if the average update rate (averaged over `<windowSize>` samples) is lower than `<minUpdateRate>` and is consistent (standard deviation over `<windowSize>` samples is less than `<maxStDev>`).
|
||||
41
clover_simulation/launch/simulator.launch
Normal file
@@ -0,0 +1,41 @@
|
||||
<launch>
|
||||
<arg name="mav_id" default="0"/>
|
||||
<arg name="est" default="ekf2"/> <!-- PX4 estimator: lpe, ekf2 -->
|
||||
<arg name="vehicle" default="clover"/> <!-- PX4 vehicle configuration: clover, clover_vpe -->
|
||||
<arg name="main_camera" default="true"/> <!-- Simulated vision position estimation camera (optical flow, ArUco) -->
|
||||
<arg name="rangefinder" default="true"/> <!-- Simulated downward-facing rangefinder, vl53l1x-like -->
|
||||
<arg name="led" default="true"/> <!-- Simulated LED strip, ws281x-like -->
|
||||
<arg name="gps" default="false"/> <!--Simulated GPS module -->
|
||||
<arg name="use_clover_physics" default="false"/> <!-- Use inertial parameters from CAD models -->
|
||||
|
||||
<!-- Gazebo instance -->
|
||||
<include file="$(find gazebo_ros)/launch/empty_world.launch">
|
||||
<!-- Workaround for crashes in VMware -->
|
||||
<env name="SVGA_VGPU10" value="0"/>
|
||||
<arg name="gui" value="true"/>
|
||||
<arg name="world_name" value="$(find clover_simulation)/resources/worlds/clover.world"/>
|
||||
<arg name="verbose" value="true"/>
|
||||
</include>
|
||||
|
||||
<!-- PX4 instance -->
|
||||
<node name="sitl_$(arg mav_id)" pkg="px4" type="px4" output="screen" args="$(find px4)/ROMFS/px4fmu_common -s etc/init.d-posix/rcS -i $(arg mav_id)">
|
||||
<env name="PX4_SIM_MODEL" value="$(arg vehicle)"/>
|
||||
<env name="PX4_ESTIMATOR" value="$(arg est)"/>
|
||||
</node>
|
||||
|
||||
<!-- Clover model -->
|
||||
<include file="$(find clover_description)/launch/spawn_drone.launch">
|
||||
<arg name="main_camera" value="$(arg main_camera)"/>
|
||||
<arg name="rangefinder" value="$(arg rangefinder)"/>
|
||||
<arg name="led" value="$(arg led)"/>
|
||||
<arg name="gps" value="$(arg gps)"/>
|
||||
<arg name="use_clover_physics" value="$(arg use_clover_physics)"/>
|
||||
</include>
|
||||
|
||||
<!-- Clover services -->
|
||||
<include file="$(find clover)/launch/clover.launch">
|
||||
<arg name="simulator" value="true"/>
|
||||
<arg name="fcu_conn" value="sitl"/>
|
||||
<arg name="fcu_ip" value="127.0.0.1"/>
|
||||
</include>
|
||||
</launch>
|
||||
36
clover_simulation/launch/simulator_1.8.2.launch
Normal file
@@ -0,0 +1,36 @@
|
||||
<launch>
|
||||
<arg name="mav_id" default="0"/>
|
||||
<arg name="est" default="lpe"/> <!-- PX4 estimator: lpe, ekf2 -->
|
||||
<arg name="vehicle" default="iris"/>
|
||||
<arg name="main_camera" default="true"/> <!-- Simulated vision position estimation camera (optical flow, ArUco) -->
|
||||
<arg name="rangefinder" default="true"/> <!-- Simulated downward-facing rangefinder, vl53l1x-like -->
|
||||
<arg name="led" default="true"/> <!-- Simulated LED strip, ws281x-like -->
|
||||
<arg name="gps" default="false"/> <!--Simulated GPS module -->
|
||||
|
||||
<!-- Gazebo instance -->
|
||||
<include file="$(find gazebo_ros)/launch/empty_world.launch">
|
||||
<!-- Workaround for crashes in VMware -->
|
||||
<env name="SVGA_VGPU10" value="0"/>
|
||||
<arg name="gui" value="true"/>
|
||||
<arg name="world_name" value="$(find clover_simulation)/resources/worlds/clover.world"/>
|
||||
<arg name="verbose" value="true"/>
|
||||
</include>
|
||||
|
||||
<!-- PX4 instance -->
|
||||
<node name="sitl" pkg="px4" type="px4" output="screen" args="$(find px4) $(find px4)/posix-configs/SITL/init/$(arg est)/$(arg vehicle)" required="true"/>
|
||||
|
||||
<!-- Clover model -->
|
||||
<include file="$(find clover_description)/launch/spawn_drone.launch">
|
||||
<arg name="main_camera" value="$(arg main_camera)"/>
|
||||
<arg name="rangefinder" value="$(arg rangefinder)"/>
|
||||
<arg name="led" value="$(arg led)"/>
|
||||
<arg name="gps" value="$(arg gps)"/>
|
||||
</include>
|
||||
|
||||
<!-- Clover services -->
|
||||
<include file="$(find clover)/launch/clover.launch">
|
||||
<arg name="simulator" value="true"/>
|
||||
<arg name="fcu_conn" value="udp"/>
|
||||
<arg name="fcu_ip" value="127.0.0.1"/>
|
||||
</include>
|
||||
</launch>
|
||||
0
clover_simulation/models/CATKIN_IGNORE
Normal file
24
clover_simulation/models/loop_line/loop_line.sdf
Normal file
@@ -0,0 +1,24 @@
|
||||
<?xml version="1.0"?>
|
||||
<sdf version="1.5">
|
||||
<model name="loop_line">
|
||||
<static>true</static>
|
||||
<link name="loop_link">
|
||||
<pose>0 0 1e-3 0 0 0</pose>
|
||||
<visual name="loop_texture">
|
||||
<cast_shadows>false</cast_shadows>
|
||||
<geometry>
|
||||
<box>
|
||||
<size>8.0 3.2 1e-3</size>
|
||||
</box>
|
||||
</geometry>
|
||||
<material>
|
||||
<script>
|
||||
<uri>model://loop_line/materials/scripts</uri>
|
||||
<uri>model://loop_line/materials/textures</uri>
|
||||
<name>loop_dashed</name>
|
||||
</script>
|
||||
</material>
|
||||
</visual>
|
||||
</link>
|
||||
</model>
|
||||
</sdf>
|
||||
@@ -0,0 +1,33 @@
|
||||
material loop_dashed
|
||||
{
|
||||
technique
|
||||
{
|
||||
pass
|
||||
{
|
||||
scene_blend alpha_blend
|
||||
texture_unit
|
||||
{
|
||||
texture loop_dashed.png
|
||||
filtering none
|
||||
scale 1.0 1.0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
material loop_solid
|
||||
{
|
||||
technique
|
||||
{
|
||||
pass
|
||||
{
|
||||
scene_blend alpha_blend
|
||||
texture_unit
|
||||
{
|
||||
texture loop_solid.png
|
||||
filtering none
|
||||
scale 1.0 1.0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 30 KiB |
|
After Width: | Height: | Size: 22 KiB |
13
clover_simulation/models/loop_line/model.config
Normal file
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0"?>
|
||||
<model>
|
||||
<name>Loop Line</name>
|
||||
<version>1.0</version>
|
||||
<sdf version="1.5">loop_line.sdf</sdf>
|
||||
<author>
|
||||
<name>Oleg Kalachev</name>
|
||||
<email>okalachev@gmail.com</email>
|
||||
</author>
|
||||
<description>
|
||||
Black loop line on the floor for recognizing by a drone
|
||||
</description>
|
||||
</model>
|
||||
@@ -0,0 +1,20 @@
|
||||
material parquet
|
||||
{
|
||||
technique
|
||||
{
|
||||
pass
|
||||
{
|
||||
ambient 0.5 0.5 0.5 1.0
|
||||
diffuse 0.5 0.5 0.5 1.0
|
||||
specular 0.2 0.2 0.2 1.0 12.5
|
||||
|
||||
texture_unit
|
||||
{
|
||||
texture parquet.jpg
|
||||
filtering anistropic
|
||||
max_anisotropy 16
|
||||
scale 0.01 0.01
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 2.4 MiB |
17
clover_simulation/models/parquet_plane/model.config
Normal file
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0"?>
|
||||
|
||||
<model>
|
||||
<name>Parquet Plane</name>
|
||||
<version>1.0</version>
|
||||
<sdf version="1.5">model.sdf</sdf>
|
||||
|
||||
<author>
|
||||
<name>Oleg Kalachev</name>
|
||||
<email>okalachev@gmail.com</email>
|
||||
</author>
|
||||
|
||||
<description>
|
||||
A parquet textured plane.
|
||||
</description>
|
||||
|
||||
</model>
|
||||
30
clover_simulation/models/parquet_plane/model.sdf
Normal file
@@ -0,0 +1,30 @@
|
||||
<?xml version="1.0" ?>
|
||||
<sdf version="1.5">
|
||||
<model name="parquet_plane">
|
||||
<static>true</static>
|
||||
<link name="link">
|
||||
<collision name="collision">
|
||||
<geometry>
|
||||
<box>
|
||||
<size>200 200 .02</size>
|
||||
</box>
|
||||
</geometry>
|
||||
</collision>
|
||||
<visual name="visual">
|
||||
<cast_shadows>false</cast_shadows>
|
||||
<geometry>
|
||||
<box>
|
||||
<size>200 200 .02</size>
|
||||
</box>
|
||||
</geometry>
|
||||
<material>
|
||||
<script>
|
||||
<uri>model://parquet_plane/materials/scripts</uri>
|
||||
<uri>model://parquet_plane/materials/textures</uri>
|
||||
<name>parquet</name>
|
||||
</script>
|
||||
</material>
|
||||
</visual>
|
||||
</link>
|
||||
</model>
|
||||
</sdf>
|
||||
@@ -0,0 +1,33 @@
|
||||
material square_dashed
|
||||
{
|
||||
technique
|
||||
{
|
||||
pass
|
||||
{
|
||||
scene_blend alpha_blend
|
||||
texture_unit
|
||||
{
|
||||
texture square_dashed.png
|
||||
filtering none
|
||||
scale 1.0 1.0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
material square_solid
|
||||
{
|
||||
technique
|
||||
{
|
||||
pass
|
||||
{
|
||||
scene_blend alpha_blend
|
||||
texture_unit
|
||||
{
|
||||
texture square_solid.png
|
||||
filtering none
|
||||
scale 1.0 1.0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 12 KiB |
13
clover_simulation/models/square_line/model.config
Normal file
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0"?>
|
||||
<model>
|
||||
<name>Square Line</name>
|
||||
<version>1.0</version>
|
||||
<sdf version="1.5">square_line.sdf</sdf>
|
||||
<author>
|
||||
<name>Oleg Kalachev</name>
|
||||
<email>okalachev@gmail.com</email>
|
||||
</author>
|
||||
<description>
|
||||
Black square line on the floor for recognizing by a drone
|
||||
</description>
|
||||
</model>
|
||||
24
clover_simulation/models/square_line/square_line.sdf
Normal file
@@ -0,0 +1,24 @@
|
||||
<?xml version="1.0"?>
|
||||
<sdf version="1.5">
|
||||
<model name="square_line">
|
||||
<static>true</static>
|
||||
<link name="square_link">
|
||||
<pose>0 0 1e-3 0 0 0</pose>
|
||||
<visual name="square_texture">
|
||||
<cast_shadows>false</cast_shadows>
|
||||
<geometry>
|
||||
<box>
|
||||
<size>4.0 4.0 1e-3</size>
|
||||
</box>
|
||||
</geometry>
|
||||
<material>
|
||||
<script>
|
||||
<uri>model://square_line/materials/scripts</uri>
|
||||
<uri>model://square_line/materials/textures</uri>
|
||||
<name>square_solid</name>
|
||||
</script>
|
||||
</material>
|
||||
</visual>
|
||||
</link>
|
||||
</model>
|
||||
</sdf>
|
||||
31
clover_simulation/package.xml
Normal file
@@ -0,0 +1,31 @@
|
||||
<package format="2">
|
||||
<name>clover_simulation</name>
|
||||
<version>0.0.1</version>
|
||||
<description>The clover_simulation package provides worlds and launch files for Gazebo.</description>
|
||||
|
||||
<maintainer email="okalachev@gmail.com">Oleg Kalachev</maintainer>
|
||||
|
||||
<author>Alexey Rogachevskiy</author>
|
||||
<author>Andrey Ryabov</author>
|
||||
<author>Arthur Golubtsov</author>
|
||||
<author>Oleg Kalachev</author>
|
||||
<author>Svyatoslav Zhuravlev</author>
|
||||
|
||||
<license>MIT</license>
|
||||
|
||||
<url type="website">https://github.com/CopterExpress/clover</url>
|
||||
<url type="bugtracker">https://github.com/CopterExpress/clover/issues</url>
|
||||
|
||||
<buildtool_depend>catkin</buildtool_depend>
|
||||
<depend>led_msgs</depend>
|
||||
<depend>xacro</depend>
|
||||
<depend>gazebo_ros</depend>
|
||||
<depend>gazebo_plugins</depend>
|
||||
<depend>rospy</depend>
|
||||
|
||||
<export>
|
||||
<gazebo_ros gazebo_media_path="${prefix}"/>
|
||||
<gazebo_ros gazebo_model_path="${prefix}/models"/>
|
||||
<gazebo_ros gazebo_resource_path="${prefix}/resources"/>
|
||||
</export>
|
||||
</package>
|
||||
42
clover_simulation/resources/worlds/clover.world
Normal file
@@ -0,0 +1,42 @@
|
||||
<?xml version="1.0" ?>
|
||||
<sdf version="1.5">
|
||||
<world name="default">
|
||||
<!-- A global light source -->
|
||||
<include>
|
||||
<uri>model://sun</uri>
|
||||
</include>
|
||||
<include>
|
||||
<uri>model://parquet_plane</uri>
|
||||
</include>
|
||||
|
||||
<scene>
|
||||
<ambient>0.8 0.8 0.8 1</ambient>
|
||||
<background>0.8 0.9 1 1</background>
|
||||
<shadows>false</shadows>
|
||||
<grid>false</grid>
|
||||
<origin_visual>false</origin_visual>
|
||||
</scene>
|
||||
|
||||
<physics name='default_physics' default='0' type='ode'>
|
||||
<gravity>0 0 -9.8066</gravity>
|
||||
<ode>
|
||||
<solver>
|
||||
<type>quick</type>
|
||||
<iters>10</iters>
|
||||
<sor>1.3</sor>
|
||||
<use_dynamic_moi_rescaling>0</use_dynamic_moi_rescaling>
|
||||
</solver>
|
||||
<constraints>
|
||||
<cfm>0</cfm>
|
||||
<erp>0.2</erp>
|
||||
<contact_max_correcting_vel>100</contact_max_correcting_vel>
|
||||
<contact_surface_layer>0.001</contact_surface_layer>
|
||||
</constraints>
|
||||
</ode>
|
||||
<max_step_size>0.004</max_step_size>
|
||||
<real_time_factor>1</real_time_factor>
|
||||
<real_time_update_rate>250</real_time_update_rate>
|
||||
<magnetic_field>6.0e-6 2.3e-5 -4.2e-5</magnetic_field>
|
||||
</physics>
|
||||
</world>
|
||||
</sdf>
|
||||
6
clover_simulation/scripts/aruco_gen
Executable file
@@ -0,0 +1,6 @@
|
||||
#! /usr/bin/env python
|
||||
|
||||
from clover_simulation import aruco_gen
|
||||
|
||||
if __name__ == "__main__":
|
||||
aruco_gen()
|
||||
11
clover_simulation/setup.py
Normal file
@@ -0,0 +1,11 @@
|
||||
## ! DO NOT MANUALLY INVOKE THIS setup.py, USE CATKIN INSTEAD
|
||||
|
||||
from distutils.core import setup
|
||||
from catkin_pkg.python_setup import generate_distutils_setup
|
||||
|
||||
setup_args = generate_distutils_setup(
|
||||
packages=['clover_simulation'],
|
||||
package_dir={'': 'src'}
|
||||
)
|
||||
|
||||
setup(**setup_args)
|
||||
93
clover_simulation/src/clover_simulation/__init__.py
Normal file
@@ -0,0 +1,93 @@
|
||||
from docopt import docopt
|
||||
from os import path
|
||||
from sys import stdout
|
||||
|
||||
from .map_parser import parse
|
||||
from .marker import Marker, generate_markers
|
||||
|
||||
from .world import load_world, add_model, save_world
|
||||
|
||||
|
||||
USAGE = '''
|
||||
|
||||
aruco_gen - Script for ArUco map/model generation.
|
||||
Generates ArUco models from their description and optionally
|
||||
adds them to an existing Gazebo world.
|
||||
|
||||
Usage:
|
||||
aruco_gen [--offset-x=<m>] [--offset-y=<m>] [--offset-z=<m>]
|
||||
[--offset-roll=<rad>] [--offset-pitch=<rad>] [--offset-yaw=<rad>]
|
||||
[--dictionary=<id>] [--single-model]
|
||||
[--source-world=<path>] [--inplace]
|
||||
[--model-path=<path>]
|
||||
<aruco_map_file>
|
||||
aruco_gen -h | --help
|
||||
|
||||
Options:
|
||||
-h, --help Show this screen.
|
||||
--offset-x=<m> X offset in meters [default: 0].
|
||||
--offset-y=<m> Y offset in meters [default: 0].
|
||||
--offset-z=<m> Z offset in meters [default: 0].
|
||||
--offset-roll=<rad> roll offset in radians [default: 0].
|
||||
--offset-pitch=<rad> pitch offset in radians [default: 0].
|
||||
--offset-yaw=<rad> yaw offset in radians [default: 0].
|
||||
--dictionary=<id> ArUco dictionary ID [default: 2].
|
||||
--single-model Generate a single model instead of individual
|
||||
markers.
|
||||
--source-world=<path> Path to existing Gazebo world.
|
||||
--inplace Modify source world.
|
||||
--model-path=<path> Folder where generated models will be saved
|
||||
[default: ~/.gazebo/models]
|
||||
aruco_map_file Full path to the ArUco map file
|
||||
|
||||
'''
|
||||
|
||||
|
||||
def aruco_gen():
|
||||
opts = docopt(USAGE)
|
||||
|
||||
dictionary_id = int(opts['--dictionary'])
|
||||
mapfile = opts['<aruco_map_file>']
|
||||
single_model = opts['--single-model']
|
||||
source_world = opts['--source-world']
|
||||
inplace = opts['--inplace']
|
||||
|
||||
off_x = float(opts['--offset-x'])
|
||||
off_y = float(opts['--offset-y'])
|
||||
off_z = float(opts['--offset-z'])
|
||||
off_roll = float(opts['--offset-roll'])
|
||||
off_pitch = float(opts['--offset-pitch'])
|
||||
off_yaw = float(opts['--offset-yaw'])
|
||||
|
||||
model_base_path = path.expanduser(opts['--model-path'])
|
||||
|
||||
markers = parse(mapfile)
|
||||
|
||||
if single_model:
|
||||
mapname = path.split(mapfile)[-1]
|
||||
model_path = path.join(model_base_path, 'aruco_{}'.format(mapname.replace('.', '_')))
|
||||
generate_markers(markers, model_path, dictionary_id=dictionary_id, map_source=mapname)
|
||||
else:
|
||||
for marker in markers:
|
||||
model_name = 'aruco_{}_{}'.format(dictionary_id, marker.id_)
|
||||
model_path = path.join(model_base_path, model_name)
|
||||
generate_markers([Marker(marker.id_, marker.size, 0, 0, 0, 0, 0, 0)],
|
||||
model_path, dictionary_id=dictionary_id)
|
||||
|
||||
if source_world is not None:
|
||||
world_tree = load_world(source_world)
|
||||
if single_model:
|
||||
world_tree = add_model(world_tree, 'aruco_{}'.format(mapname.replace('.', '_')),
|
||||
off_x, off_y, off_z,
|
||||
off_roll, off_pitch, off_yaw)
|
||||
else:
|
||||
if (abs(off_roll) > 0.001) or (abs(off_pitch) > 0.001) or (abs(off_yaw) > 0.001):
|
||||
raise NotImplementedError('Sorry, angular offsets are not currently implemented for multimodel generation')
|
||||
for marker in markers:
|
||||
world_tree = add_model(world_tree, 'aruco_{}_{}'.format(dictionary_id, marker.id_),
|
||||
off_x + marker.x, off_y + marker.y, off_z + marker.z,
|
||||
marker.roll, marker.pitch, marker.yaw)
|
||||
|
||||
output = open(source_world, 'w') if inplace else stdout
|
||||
|
||||
save_world(world_tree, output)
|
||||
45
clover_simulation/src/clover_simulation/map_parser.py
Normal file
@@ -0,0 +1,45 @@
|
||||
# ArUco map parser (should be kept in sync with aruco_pose)
|
||||
|
||||
from .marker import Marker
|
||||
|
||||
|
||||
def _parse_line(line):
|
||||
'''
|
||||
Parse a line of map data, returning a Marker object if
|
||||
parsing succeeded, or None if it failed.
|
||||
'''
|
||||
if line.startswith('#'):
|
||||
return None
|
||||
elems = line.split()
|
||||
if len(elems) < 4:
|
||||
return None
|
||||
try:
|
||||
id_ = int(elems[0])
|
||||
size = float(elems[1])
|
||||
x = float(elems[2])
|
||||
y = float(elems[3])
|
||||
z = float(elems[4]) if len(elems) > 4 else 0
|
||||
yaw = float(elems[5]) if len(elems) > 5 else 0
|
||||
pitch = float(elems[6]) if len(elems) > 6 else 0
|
||||
roll = float(elems[7]) if len(elems) > 7 else 0
|
||||
except:
|
||||
print('Warning - marformed line: {}'.format(line, sys.exc_info()[0]))
|
||||
return None
|
||||
return Marker(id_, size, x, y, z, roll, pitch, yaw)
|
||||
|
||||
|
||||
def parse(map_path):
|
||||
'''
|
||||
Parse a map at a given path.
|
||||
|
||||
map_path: Path to the ArUco map file.
|
||||
|
||||
Returns a list of Marker objects.
|
||||
'''
|
||||
markers = []
|
||||
with open(map_path, 'r') as map_contents:
|
||||
for line in map_contents.readlines():
|
||||
parser_result = _parse_line(line)
|
||||
if parser_result is not None:
|
||||
markers.append(parser_result)
|
||||
return markers
|
||||
163
clover_simulation/src/clover_simulation/marker.py
Normal file
@@ -0,0 +1,163 @@
|
||||
# Marker object definition and creation
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
from collections import namedtuple
|
||||
from string import Template
|
||||
from os import makedirs, path
|
||||
|
||||
import cv2
|
||||
import numpy as np
|
||||
import math
|
||||
|
||||
Marker = namedtuple('Marker',
|
||||
['id_', 'size', 'x', 'y', 'z', 'roll', 'pitch', 'yaw'])
|
||||
|
||||
|
||||
MARKER_MODEL_CFG_TEMPLATE = Template('''
|
||||
<?xml version="1.0"?>
|
||||
<model>
|
||||
<name>${model_name}</name>
|
||||
<version>1.0</version>
|
||||
<sdf version="1.6">${sdf_name}</sdf>
|
||||
<author>
|
||||
<name>Marker Generator script</name>
|
||||
<email>marker@generator.sh</email>
|
||||
</author>
|
||||
<description>
|
||||
${model_name}
|
||||
</description>
|
||||
</model>
|
||||
''')
|
||||
|
||||
|
||||
MARKER_VISUAL_TEMPLATE = Template('''
|
||||
<visual name="visual_marker_${marker_id}">
|
||||
<pose>${x} ${y} ${z} ${roll} ${pitch} ${yaw}</pose>
|
||||
<cast_shadows>false</cast_shadows>
|
||||
<geometry>
|
||||
<box>
|
||||
<size>${marker_full_size} ${marker_full_size} 1e-3</size>
|
||||
</box>
|
||||
</geometry>
|
||||
<material>
|
||||
<script>
|
||||
<uri>model://${model_directory}/materials/scripts</uri>
|
||||
<uri>model://${model_directory}/materials/textures</uri>
|
||||
<name>aruco/marker_${marker_id}</name>
|
||||
</script>
|
||||
</material>
|
||||
</visual>
|
||||
''')
|
||||
|
||||
|
||||
MARKER_MODEL_SDF_TEMPLATE = Template('''
|
||||
<?xml version="1.0"?>
|
||||
<sdf version="1.5">
|
||||
<model name="${model_name}">
|
||||
<static>true</static>
|
||||
<link name="${model_name}_link">
|
||||
${model_visuals}
|
||||
</link>
|
||||
</model>
|
||||
</sdf>
|
||||
''')
|
||||
|
||||
|
||||
MARKER_MATERIAL_TEMPLATE = Template('''
|
||||
material aruco/marker_${marker_id}
|
||||
{
|
||||
technique
|
||||
{
|
||||
pass
|
||||
{
|
||||
texture_unit
|
||||
{
|
||||
texture aruco_marker_${marker_id}.png
|
||||
filtering none
|
||||
scale 1.0 1.0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
''')
|
||||
|
||||
|
||||
def model_name(model_directory):
|
||||
'''
|
||||
Extract model name from model directory.
|
||||
|
||||
model_directory: Full path to the model.
|
||||
|
||||
Returns Gazebo-compatible model name (available through model:// URI schema)
|
||||
'''
|
||||
return path.split(model_directory)[1]
|
||||
|
||||
|
||||
def generate_markers(markers, model_directory, dictionary_id=2, map_source=''):
|
||||
'''
|
||||
Generate markers from a list. Result is a single Gazebo
|
||||
model with all markers inside it.
|
||||
|
||||
markers: A List-like object containing Marker objects.
|
||||
model_directory: Target directory for the model.
|
||||
dictionary_id: Predefined ArUco dictionary ID, as defined by OpenCV.
|
||||
map_source: An optional string with the name of the ArUco map file.
|
||||
'''
|
||||
script_directory = path.join(model_directory, 'materials', 'scripts')
|
||||
texture_directory = path.join(model_directory, 'materials', 'textures')
|
||||
|
||||
try:
|
||||
if not path.exists(script_directory):
|
||||
makedirs(script_directory)
|
||||
if not path.exists(texture_directory):
|
||||
makedirs(texture_directory)
|
||||
except:
|
||||
print('Could not create material/texture directories!')
|
||||
|
||||
aruco_dict = cv2.aruco.getPredefinedDictionary(dictionary_id)
|
||||
marker_bits = aruco_dict.markerSize
|
||||
marker_outer_bits = marker_bits + 2 # "black border" bits
|
||||
marker_border_bits = marker_bits + 4 # "white border" bits
|
||||
materials = []
|
||||
visuals = []
|
||||
|
||||
for marker in markers:
|
||||
texture_name = 'aruco_marker_{}_{}.png'.format(dictionary_id, marker.id_)
|
||||
|
||||
marker_image = np.zeros((marker_border_bits, marker_border_bits), dtype=np.uint8)
|
||||
marker_image[:,:] = 255
|
||||
marker_image[1:marker_border_bits - 1, 1:marker_border_bits - 1] = cv2.aruco.drawMarker(
|
||||
aruco_dict, marker.id_, marker_outer_bits)
|
||||
cv2.imwrite(path.join(texture_directory, texture_name), marker_image)
|
||||
materials.append(MARKER_MATERIAL_TEMPLATE.substitute(
|
||||
marker_id='{}_{}'.format(dictionary_id, marker.id_)
|
||||
))
|
||||
visuals.append(MARKER_VISUAL_TEMPLATE.substitute(
|
||||
marker_id='{}_{}'.format(dictionary_id, marker.id_),
|
||||
x=marker.x,
|
||||
y=marker.y,
|
||||
z=marker.z,
|
||||
roll=marker.roll,
|
||||
pitch=marker.pitch,
|
||||
yaw=marker.yaw + (math.pi / 2),
|
||||
model_directory=model_name(model_directory),
|
||||
marker_full_size=marker.size * marker_border_bits / marker_outer_bits
|
||||
))
|
||||
|
||||
with open(path.join(script_directory, 'aruco_materials.material'), 'w') as f:
|
||||
f.write(''.join(materials))
|
||||
|
||||
with open(path.join(model_directory, 'aruco_model.sdf'), 'w') as f:
|
||||
f.write(MARKER_MODEL_SDF_TEMPLATE.substitute(
|
||||
model_name='aruco_{}_{}'.format(dictionary_id, len(markers)),
|
||||
model_visuals=''.join(visuals)
|
||||
))
|
||||
|
||||
with open(path.join(model_directory, 'model.config'), 'w') as f:
|
||||
f.write(MARKER_MODEL_CFG_TEMPLATE.substitute(
|
||||
model_name='ArUco {} (dictionary {})'.format(
|
||||
markers[0].id_ if len(markers) == 1 else 'field from ' + map_source,
|
||||
dictionary_id),
|
||||
sdf_name='aruco_model.sdf'
|
||||
))
|
||||
43
clover_simulation/src/clover_simulation/world.py
Normal file
@@ -0,0 +1,43 @@
|
||||
# Gazebo world changer
|
||||
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
from string import Template
|
||||
|
||||
WORLD_INCLUDE = Template('''
|
||||
<include>
|
||||
<uri>model://${model_name}</uri>
|
||||
<pose>${x} ${y} ${z} ${roll} ${pitch} ${yaw}</pose>
|
||||
</include>
|
||||
''')
|
||||
|
||||
def load_world(world_file):
|
||||
'''
|
||||
Load Gazebo world as an ElementTree object
|
||||
'''
|
||||
return ET.parse(world_file)
|
||||
|
||||
|
||||
def add_model(world, model_name, x, y, z, roll, pitch, yaw, index=0):
|
||||
'''
|
||||
Create and add an element to the world
|
||||
'''
|
||||
world_elem = world.find('world')
|
||||
model_elem = ET.fromstring(WORLD_INCLUDE.substitute(
|
||||
model_name=model_name,
|
||||
x=x,
|
||||
y=y,
|
||||
z=z,
|
||||
roll=roll,
|
||||
pitch=pitch,
|
||||
yaw=yaw
|
||||
))
|
||||
world_elem.insert(index, model_elem)
|
||||
return world
|
||||
|
||||
|
||||
def save_world(world, file):
|
||||
'''
|
||||
Save the world to file-like object
|
||||
'''
|
||||
return world.write(file)
|
||||
232
clover_simulation/src/sim_leds.cpp
Normal file
@@ -0,0 +1,232 @@
|
||||
#include <led_msgs/SetLEDs.h>
|
||||
#include <led_msgs/LEDStateArray.h>
|
||||
|
||||
#include <ros/ros.h>
|
||||
#include <ros/callback_queue.h>
|
||||
#include <gazebo/common/Plugin.hh>
|
||||
#include <gazebo/rendering/rendering.hh>
|
||||
|
||||
#if GAZEBO_MAJOR_VERSION >= 9
|
||||
#include <ignition/math/Color.hh>
|
||||
using GazeboColor = ignition::math::Color;
|
||||
#else
|
||||
#include <gazebo/common/Color.hh>
|
||||
using GazeboColor = gazebo::common::Color;
|
||||
#endif
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
|
||||
#include <thread>
|
||||
|
||||
namespace sim_led
|
||||
{
|
||||
// Forward declaration of the plugin for led controller
|
||||
class LedVisualPlugin;
|
||||
} //
|
||||
|
||||
namespace led_controller
|
||||
{
|
||||
|
||||
/// LedController: a class that provides ROS interface for the LEDs.
|
||||
class LedController
|
||||
{
|
||||
private:
|
||||
/// Role for the current controller
|
||||
enum class Role
|
||||
{
|
||||
Client, // Client: runs on /gazebo_gui, responsible for "preview window"
|
||||
Server // Server: runs on /gazebo, responsible for renders on Gazebo sensors
|
||||
} role;
|
||||
|
||||
// Pointers to the LED plugins that we know about
|
||||
std::vector<sim_led::LedVisualPlugin*> registeredLeds;
|
||||
// Mutex protecting the vector
|
||||
std::mutex registryMutex;
|
||||
std::string robotNamespace;
|
||||
|
||||
std::unique_ptr<ros::NodeHandle> nh;
|
||||
|
||||
ros::ServiceServer setLedsSrv;
|
||||
// Note: LED state should only be published by the /gazebo node
|
||||
led_msgs::LEDStateArray ledState;
|
||||
ros::Publisher statePublisher;
|
||||
// LED state will be read from the topic to avoid creating more services
|
||||
ros::Subscriber stateSubscriber;
|
||||
|
||||
bool setLeds(led_msgs::SetLEDs::Request& req, led_msgs::SetLEDs::Response& resp);
|
||||
void handleLedsMsg(const led_msgs::LEDStateArrayConstPtr& leds);
|
||||
|
||||
public:
|
||||
LedController(const std::string& robotNamespace) : robotNamespace(robotNamespace)
|
||||
{
|
||||
// We need "libgazebo_ros_api.so" to be loaded
|
||||
if (!ros::isInitialized())
|
||||
{
|
||||
ROS_FATAL_NAMED("LedController", "Tried to load ROS plugin when ROS Gazebo API is not loaded. Please use gazebo_ros node to"
|
||||
"launch Gazebo.");
|
||||
}
|
||||
|
||||
role = (ros::this_node::getName() == "/gazebo") ? Role::Server : Role::Client;
|
||||
ROS_INFO_NAMED(("LedController_" + robotNamespace).c_str(), "LedController has started (as %s)", role == Role::Client ? "client" : "server");
|
||||
|
||||
nh.reset(new ros::NodeHandle(robotNamespace));
|
||||
if (role == Role::Server)
|
||||
{
|
||||
setLedsSrv = nh->advertiseService("led/set_leds", &LedController::setLeds, this);
|
||||
statePublisher = nh->advertise<led_msgs::LEDStateArray>("led/state", 1, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
// LED state should be published to the "led/state" topic, so we grab our data there
|
||||
stateSubscriber = nh->subscribe<led_msgs::LEDStateArray>("led/state", 1, &LedController::handleLedsMsg, this);
|
||||
}
|
||||
};
|
||||
|
||||
~LedController()
|
||||
{
|
||||
nh->shutdown();
|
||||
}
|
||||
|
||||
void registerPlugin(sim_led::LedVisualPlugin* plugin, int ledIdx = 0, int totalLeds = 0)
|
||||
{
|
||||
assert(ledIdx < totalLeds);
|
||||
std::lock_guard<std::mutex> lock(registryMutex);
|
||||
if (totalLeds > 0) {
|
||||
registeredLeds.resize(totalLeds);
|
||||
ledState.leds.resize(totalLeds);
|
||||
}
|
||||
ROS_DEBUG_NAMED(("LedController_" + robotNamespace).c_str(), "Registering LED visual plugin to %s (LED id=%d)", (role == Role::Client) ? "client" : "server", ledIdx);
|
||||
registeredLeds[ledIdx] = plugin;
|
||||
ledState.leds[ledIdx].index = ledIdx;
|
||||
if (role == Role::Server)
|
||||
statePublisher.publish(ledState);
|
||||
}
|
||||
|
||||
void unregisterPlugin(sim_led::LedVisualPlugin* plugin)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(registryMutex);
|
||||
auto it = std::find(registeredLeds.begin(), registeredLeds.end(), plugin);
|
||||
if (it != registeredLeds.end())
|
||||
{
|
||||
ROS_DEBUG_STREAM_NAMED(("LedController_" + robotNamespace).c_str(), "Unregistering LED visual plugin (LED id=" << (it - registeredLeds.begin()) << ")");
|
||||
*it = nullptr;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Guards controllers map (static to led_controller::get())
|
||||
std::mutex controllerMutex;
|
||||
|
||||
LedController& get(std::string robotNamespace)
|
||||
{
|
||||
static std::unordered_map<std::string, std::unique_ptr<LedController>> controllers;
|
||||
std::lock_guard<std::mutex> lock(controllerMutex);
|
||||
auto it = controllers.find(robotNamespace);
|
||||
if (it == controllers.end()) {
|
||||
gzwarn << "Creating new LED controller for namespace " << robotNamespace << "\n";
|
||||
controllers[robotNamespace].reset(new LedController(robotNamespace));
|
||||
return *controllers[robotNamespace];
|
||||
}
|
||||
return *(it->second);
|
||||
}
|
||||
|
||||
} // led_controller
|
||||
|
||||
|
||||
namespace sim_led
|
||||
{
|
||||
|
||||
class LedVisualPlugin : public gazebo::VisualPlugin {
|
||||
private:
|
||||
std::string ns;
|
||||
gazebo::rendering::VisualPtr vptr;
|
||||
|
||||
public:
|
||||
void Load(gazebo::rendering::VisualPtr visual, sdf::ElementPtr sdf) override {
|
||||
// FIXME: This is a fragile way to guess our index
|
||||
// FIXME: This is based on an assumption that the parent will have a mangled name
|
||||
// FIXME: (like led_strip::base_link::base_link_fixed_joint_lump__led_00_link_visual_1)
|
||||
// FIXME: This will obviously break if gazebo decides to go with something else
|
||||
auto parentName = sdf->GetParent()->GetAttribute("name")->GetAsString();
|
||||
auto lastDashPos = parentName.rfind('_');
|
||||
int myIndex = 0;
|
||||
if (lastDashPos != std::string::npos)
|
||||
{
|
||||
auto indexStr = parentName.substr(lastDashPos + 1);
|
||||
try {
|
||||
myIndex = std::stoi(indexStr);
|
||||
} catch(const std::exception &e) {
|
||||
gzwarn << "Failed to convert " << indexStr << " to integer: " << e.what() << ", assuming 0\n";
|
||||
myIndex = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
gzerr << "Failed to parse parent name: " << parentName << "; could not determine our index\n";
|
||||
}
|
||||
|
||||
ns = "";
|
||||
if (sdf->HasElement("robotNamespace")) {
|
||||
ns = sdf->Get<std::string>("robotNamespace");
|
||||
}
|
||||
if (!sdf->HasElement("ledCount")) {
|
||||
gzerr << "ledCount is not set, but is required for the plugin to function correctly\n";
|
||||
return;
|
||||
}
|
||||
int totalLeds = sdf->Get<int>("ledCount");
|
||||
|
||||
vptr = visual;
|
||||
led_controller::get(ns).registerPlugin(this, myIndex, totalLeds);
|
||||
}
|
||||
|
||||
~LedVisualPlugin()
|
||||
{
|
||||
led_controller::get(ns).unregisterPlugin(this);
|
||||
}
|
||||
|
||||
void SetColor(const GazeboColor& emissive)
|
||||
{
|
||||
vptr->SetEmissive(emissive);
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
// FIXME: These two functions basically do the same thing, maybe they can be merged?
|
||||
bool led_controller::LedController::setLeds(led_msgs::SetLEDs::Request &req, led_msgs::SetLEDs::Response &resp)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(registryMutex);
|
||||
for(const auto& led : req.leds)
|
||||
{
|
||||
if (led.index < registeredLeds.size()) {
|
||||
auto color = GazeboColor(led.r / 255.0f, led.g / 255.0f, led.b / 255.0f);
|
||||
auto ledPlugin = registeredLeds[led.index];
|
||||
if (ledPlugin) ledPlugin->SetColor(color);
|
||||
ledState.leds[led.index].r = led.r;
|
||||
ledState.leds[led.index].g = led.g;
|
||||
ledState.leds[led.index].b = led.b;
|
||||
}
|
||||
}
|
||||
statePublisher.publish(ledState);
|
||||
resp.success = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void led_controller::LedController::handleLedsMsg(const led_msgs::LEDStateArrayConstPtr& leds)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(registryMutex);
|
||||
for(const auto& led : leds->leds)
|
||||
{
|
||||
if (led.index < registeredLeds.size())
|
||||
{
|
||||
auto color = GazeboColor(led.r / 255.0f, led.g / 255.0f, led.b / 255.0f);
|
||||
auto ledPlugin = registeredLeds[led.index];
|
||||
if (ledPlugin) ledPlugin->SetColor(color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GZ_REGISTER_VISUAL_PLUGIN(sim_led::LedVisualPlugin);
|
||||
275
clover_simulation/src/throttling_camera.cpp
Normal file
@@ -0,0 +1,275 @@
|
||||
#include <gazebo/plugins/CameraPlugin.hh>
|
||||
// physics definitions and functions: Required to perform slowdown
|
||||
#include <gazebo/physics/physics.hh>
|
||||
// ROS simulated camera class with most of the boilerplate already in place
|
||||
#include <gazebo_plugins/gazebo_ros_camera_utils.h>
|
||||
#include <ros/ros.h>
|
||||
#include <memory>
|
||||
|
||||
#include <deque>
|
||||
#include <algorithm>
|
||||
|
||||
namespace
|
||||
{
|
||||
/**
|
||||
* Simple statistics-collecting class.
|
||||
*/
|
||||
class AverageStat
|
||||
{
|
||||
private:
|
||||
/** Currently collected samples. */
|
||||
std::deque<double> samples;
|
||||
/** Number of samples to store (also, the number of samples considered "adequate") */
|
||||
size_t maxSamples;
|
||||
/** Currently stored average value */
|
||||
double average = 0;
|
||||
/** Currently stored standard deviation value */
|
||||
double stdev = 0;
|
||||
/** Largest standard deviation that is considered adequate */
|
||||
double maxStDev;
|
||||
|
||||
public:
|
||||
AverageStat(size_t numSamples, double validStDev) :
|
||||
samples(),
|
||||
maxSamples(numSamples),
|
||||
maxStDev(validStDev)
|
||||
{}
|
||||
|
||||
/**
|
||||
* Add a sample and recalculate average and standard deviation.
|
||||
*
|
||||
* @param sample New sampled value.
|
||||
* @return New average value.
|
||||
*/
|
||||
double update(double sample)
|
||||
{
|
||||
samples.push_back(sample);
|
||||
if (samples.size() > maxSamples)
|
||||
{
|
||||
samples.pop_front();
|
||||
}
|
||||
average = std::accumulate(samples.begin(), samples.end(), 0.0) / samples.size();
|
||||
double stdevSquared = std::accumulate(samples.begin(), samples.end(), 0.0,
|
||||
[&](double sum, double xi) {
|
||||
return sum + (xi - average) * (xi - average);
|
||||
}) / samples.size();
|
||||
stdev = std::sqrt(stdevSquared);
|
||||
return average;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current average value of all samples.
|
||||
*
|
||||
* @note This function will return a result even if it is not considered valid.
|
||||
*/
|
||||
double getAverage() const
|
||||
{
|
||||
return average;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current standard deviation of all samples.
|
||||
*
|
||||
* @note This function will return a result even if it is not considered valid.
|
||||
*/
|
||||
double getStDev() const
|
||||
{
|
||||
return stdev;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the average value is considered "adequate".
|
||||
*
|
||||
* @return True if the number of samples is not less than required and standard deviation is within limits, false otherwise
|
||||
*/
|
||||
bool isAdequate() const
|
||||
{
|
||||
return (samples.size() >= maxSamples) && (stdev < maxStDev);
|
||||
}
|
||||
|
||||
/**
|
||||
* Drop all samples and start anew.
|
||||
*
|
||||
* @note This does not actually change average and stdev, but the stats are considered inadequate after this call.
|
||||
*/
|
||||
void reset()
|
||||
{
|
||||
samples.clear();
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace throttling_camera
|
||||
{
|
||||
|
||||
/**
|
||||
* Gazebo camera plugin for a world-throttling (rate-preserving) camera.
|
||||
*
|
||||
* This plugin will slow the simulation down to maintain average publishing rate. Note that
|
||||
* this plugin will *only* perform slowdown, it *will not* speed the simulation back up!
|
||||
*/
|
||||
class ThrottlingCamera : public gazebo::CameraPlugin, gazebo::GazeboRosCameraUtils
|
||||
{
|
||||
private:
|
||||
/** A pointer to the Gazebo camera sensor. */
|
||||
gazebo::sensors::SensorPtr camPtr;
|
||||
/** A pointer to the current simulated world (required to change world parameters) */
|
||||
gazebo::physics::WorldPtr world;
|
||||
/** A pointer to the physics preset manager. Used to actually slow the simulation down. */
|
||||
gazebo::physics::PresetManagerPtr presetManager;
|
||||
|
||||
/** Maximum update interval that is considered "okay". Should be higher than the "average" update interval to avoid extreme slowdowns */
|
||||
double maxUpdateInterval;
|
||||
|
||||
/** Statistics for publishing time intervals. */
|
||||
std::unique_ptr<AverageStat> timeSamples;
|
||||
public:
|
||||
ThrottlingCamera() = default;
|
||||
~ThrottlingCamera() override = default;
|
||||
|
||||
/**
|
||||
* Plugin load function. Called by Gazebo each time the plugin is instantiated.
|
||||
*
|
||||
* @param parent Gazebo sensor that this plugin connects to.
|
||||
* @param sdf SDF element containing this plugin.
|
||||
*/
|
||||
void Load(gazebo::sensors::SensorPtr parent, sdf::ElementPtr sdf) override
|
||||
{
|
||||
if (!ros::isInitialized())
|
||||
{
|
||||
ROS_FATAL_NAMED("throttling_camera", "ROS node for Gazebo has not been initialized, unable to load plugin");
|
||||
return;
|
||||
}
|
||||
ROS_DEBUG_NAMED("throttling_camera", "Initializing ROS throttling (stable-rate) camera");
|
||||
|
||||
CameraPlugin::Load(parent, sdf);
|
||||
|
||||
world = gazebo::physics::get_world(parent->WorldName());
|
||||
#if GAZEBO_MAJOR_VERSION >= 8
|
||||
presetManager = world->PresetMgr();
|
||||
#else
|
||||
presetManager = world->GetPresetManager();
|
||||
#endif /* GAZEBO_MAJOR_VERSION */
|
||||
|
||||
// Same as in PX4
|
||||
if (presetManager->CurrentProfile() != "default_physics")
|
||||
{
|
||||
gzwarn << "Current physics profile is not default_physics, but actually is " << presetManager->CurrentProfile() << "\n";
|
||||
if (!presetManager->CurrentProfile("default_physics"))
|
||||
{
|
||||
gzerr << "Could not set current profile to default_physics!\n";
|
||||
}
|
||||
}
|
||||
|
||||
double minUpdateRate = parent->UpdateRate();
|
||||
if (sdf->HasElement("minUpdateRate"))
|
||||
{
|
||||
minUpdateRate = sdf->Get<double>("minUpdateRate");
|
||||
}
|
||||
maxUpdateInterval = 1.0 / minUpdateRate;
|
||||
|
||||
size_t windowSize = 10;
|
||||
if (sdf->HasElement("windowSize"))
|
||||
{
|
||||
windowSize = sdf->Get<size_t>("windowSize");
|
||||
}
|
||||
|
||||
double maxStDev = 0.02;
|
||||
if (sdf->HasElement("maxStDev"))
|
||||
{
|
||||
maxStDev = sdf->Get<double>("maxStDev");
|
||||
}
|
||||
|
||||
timeSamples.reset(new AverageStat(windowSize, maxStDev));
|
||||
|
||||
camPtr = parent;
|
||||
|
||||
parentSensor_ = camPtr;
|
||||
width_ = width;
|
||||
height_ = height;
|
||||
depth_ = depth;
|
||||
format_ = format;
|
||||
camera_ = camera;
|
||||
|
||||
gazebo::GazeboRosCameraUtils::Load(parent, sdf);
|
||||
}
|
||||
|
||||
/**
|
||||
* Frame callback. Called every time a new frame is rendered by the camera.
|
||||
*
|
||||
* Checks whether we should slow simulation down and publishes a new image
|
||||
* message.
|
||||
*
|
||||
* @param image Image data.
|
||||
* @param width Image width, in pixels.
|
||||
* @param height Image height, in pixels.
|
||||
* @param depth Image depth, in bytes.
|
||||
* @param format Image format description string.
|
||||
*/
|
||||
void OnNewFrame(const unsigned char *image, unsigned int width, unsigned int height, unsigned int depth,
|
||||
const std::string &format) override {
|
||||
|
||||
// Note: sensorUpdateTime uses simulated time
|
||||
auto sensorUpdateTime = camPtr->LastMeasurementTime();
|
||||
// If sensor was not active for some reason, we allow it to get new data on next frame
|
||||
if (!camPtr->IsActive())
|
||||
{
|
||||
camPtr->SetActive(true);
|
||||
last_update_time_ = sensorUpdateTime;
|
||||
timeSamples->reset();
|
||||
return;
|
||||
}
|
||||
|
||||
boost::mutex::scoped_lock lock(*image_connect_count_lock_);
|
||||
if (*image_connect_count_ > 0)
|
||||
{
|
||||
if (sensorUpdateTime < last_update_time_)
|
||||
{
|
||||
ROS_WARN_NAMED("throttling_camera", "Negative sensor update time difference (world reset?)");
|
||||
last_update_time_ = sensorUpdateTime;
|
||||
timeSamples->reset();
|
||||
}
|
||||
|
||||
auto timeDelta = sensorUpdateTime - last_update_time_;
|
||||
timeSamples->update(timeDelta.Double());
|
||||
|
||||
// We want to throttle the simulation down if we have measurements too far apart
|
||||
if (timeSamples->isAdequate() && timeSamples->getAverage() > maxUpdateInterval)
|
||||
{
|
||||
ROS_INFO_STREAM_NAMED("throttling_camera", "Had average update period of "
|
||||
<< timeSamples->getAverage() << " (stdev: " << timeSamples->getStDev() << ")"
|
||||
<< ", but desired update period is " << update_period_
|
||||
<< ", throttling simulation down");
|
||||
boost::any currentRealTimeUpadteRateParam;
|
||||
if (!presetManager->GetCurrentProfileParam("real_time_update_rate", currentRealTimeUpadteRateParam))
|
||||
{
|
||||
gzerr << "Failed to get real time update rate parameter!\n";
|
||||
}
|
||||
auto currentRate = boost::any_cast<double>(currentRealTimeUpadteRateParam);
|
||||
// We are being somewhat aggressive here, maybe we could throttle the world
|
||||
// down in steps?
|
||||
double slowdownFactor = update_period_ / timeSamples->getAverage();
|
||||
auto nextRate = currentRate * slowdownFactor;
|
||||
if (!presetManager->SetCurrentProfileParam("real_time_update_rate", nextRate))
|
||||
{
|
||||
gzerr << "Failed to set real time update rate parameter!\n";
|
||||
}
|
||||
if (slowdownFactor < 0.5)
|
||||
{
|
||||
ROS_WARN_STREAM_NAMED("throttling_camera", "Simulation slowed down significantly; consider running"
|
||||
"the simulation with a lower PX4_SIM_SPEED_FACTOR value (slowed down from "
|
||||
<< currentRate << " to " << nextRate << " updates per second)");
|
||||
}
|
||||
// We're discarding old samples to avoid extensive slowdown
|
||||
timeSamples->reset();
|
||||
}
|
||||
PutCameraData(image, sensorUpdateTime);
|
||||
PublishCameraInfo(sensorUpdateTime);
|
||||
}
|
||||
last_update_time_ = sensorUpdateTime;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
GZ_REGISTER_SENSOR_PLUGIN(throttling_camera::ThrottlingCamera);
|
||||
0
docs/CATKIN_IGNORE
Normal file
BIN
docs/assets/5_D1_2.jpg
Normal file
|
After Width: | Height: | Size: 65 KiB |
BIN
docs/assets/assembling_clever4_2/coex_pix.png
Normal file
|
After Width: | Height: | Size: 463 KiB |
BIN
docs/assets/assembling_clever4_2/esc_1.png
Normal file
|
After Width: | Height: | Size: 433 KiB |
BIN
docs/assets/assembling_clever4_2/esc_2.png
Normal file
|
After Width: | Height: | Size: 591 KiB |
BIN
docs/assets/assembling_clever4_2/fc_connection_1.png
Normal file
|
After Width: | Height: | Size: 350 KiB |
BIN
docs/assets/assembling_clever4_2/fc_connection_2.png
Normal file
|
After Width: | Height: | Size: 435 KiB |
BIN
docs/assets/assembling_clever4_2/fc_connection_3.png
Normal file
|
After Width: | Height: | Size: 468 KiB |
BIN
docs/assets/assembling_clever4_2/fc_connection_4.png
Normal file
|
After Width: | Height: | Size: 476 KiB |
BIN
docs/assets/assembling_clever4_2/fc_connection_5.png
Normal file
|
After Width: | Height: | Size: 464 KiB |
BIN
docs/assets/assembling_clever4_2/fc_connection_6.png
Normal file
|
After Width: | Height: | Size: 472 KiB |
BIN
docs/assets/assembling_clever4_2/final_1.png
Normal file
|
After Width: | Height: | Size: 426 KiB |
BIN
docs/assets/assembling_clever4_2/final_2.png
Normal file
|
After Width: | Height: | Size: 777 KiB |
BIN
docs/assets/assembling_clever4_2/final_3.png
Normal file
|
After Width: | Height: | Size: 455 KiB |
BIN
docs/assets/assembling_clever4_2/final_4.png
Normal file
|
After Width: | Height: | Size: 446 KiB |
BIN
docs/assets/assembling_clever4_2/frame_1.png
Normal file
|
After Width: | Height: | Size: 304 KiB |
BIN
docs/assets/assembling_clever4_2/frame_2.png
Normal file
|
After Width: | Height: | Size: 320 KiB |
BIN
docs/assets/assembling_clever4_2/frame_3.png
Normal file
|
After Width: | Height: | Size: 307 KiB |
BIN
docs/assets/assembling_clever4_2/frame_4.png
Normal file
|
After Width: | Height: | Size: 152 KiB |
BIN
docs/assets/assembling_clever4_2/frame_5.png
Normal file
|
After Width: | Height: | Size: 348 KiB |
BIN
docs/assets/assembling_clever4_2/frame_6.png
Normal file
|
After Width: | Height: | Size: 342 KiB |
BIN
docs/assets/assembling_clever4_2/frame_7.png
Normal file
|
After Width: | Height: | Size: 346 KiB |
BIN
docs/assets/assembling_clever4_2/guard_1.png
Normal file
|
After Width: | Height: | Size: 175 KiB |
BIN
docs/assets/assembling_clever4_2/guard_2.png
Normal file
|
After Width: | Height: | Size: 224 KiB |
BIN
docs/assets/assembling_clever4_2/guard_3.png
Normal file
|
After Width: | Height: | Size: 558 KiB |
BIN
docs/assets/assembling_clever4_2/guard_4.png
Normal file
|
After Width: | Height: | Size: 426 KiB |
BIN
docs/assets/assembling_clever4_2/led_1.png
Normal file
|
After Width: | Height: | Size: 95 KiB |
BIN
docs/assets/assembling_clever4_2/led_2.png
Normal file
|
After Width: | Height: | Size: 155 KiB |
BIN
docs/assets/assembling_clever4_2/led_3.png
Normal file
|
After Width: | Height: | Size: 600 KiB |