Merge branch 'master' into ros-book
@@ -114,7 +114,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
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
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);
|
||||
BIN
docs/assets/simulation_setup_vm/01_import_vm.png
Normal file
|
After Width: | Height: | Size: 86 KiB |
BIN
docs/assets/simulation_setup_vm/02_import_failure.png
Normal file
|
After Width: | Height: | Size: 88 KiB |
BIN
docs/assets/simulation_setup_vm/03_max_memory.png
Normal file
|
After Width: | Height: | Size: 71 KiB |
BIN
docs/assets/simulation_setup_vm/04_core_count.png
Normal file
|
After Width: | Height: | Size: 53 KiB |
BIN
docs/assets/simulation_setup_vm/05_3d_acceleration.png
Normal file
|
After Width: | Height: | Size: 61 KiB |
BIN
docs/assets/simulation_setup_vm/06_usb_3_0.png
Normal file
|
After Width: | Height: | Size: 59 KiB |
BIN
docs/assets/simulation_setup_vm/07_bridge_networking.png
Normal file
|
After Width: | Height: | Size: 60 KiB |
BIN
docs/assets/simulation_setup_vm/08_no_3d_acceleration.png
Normal file
|
After Width: | Height: | Size: 63 KiB |
BIN
docs/assets/simulation_setup_vm/09_netcfg.png
Normal file
|
After Width: | Height: | Size: 66 KiB |
BIN
docs/assets/simulation_usage/01_running_gazebo.jpg
Normal file
|
After Width: | Height: | Size: 189 KiB |
BIN
docs/assets/simulation_usage/02_gazebo_qgc.jpg
Normal file
|
After Width: | Height: | Size: 160 KiB |
BIN
docs/assets/simulation_usage/03_gazebo_rqt.jpg
Normal file
|
After Width: | Height: | Size: 147 KiB |
BIN
docs/assets/simulation_usage/04_vscode_config.jpg
Normal file
|
After Width: | Height: | Size: 272 KiB |
BIN
docs/assets/simulation_usage/05_real_time_factor.png
Normal file
|
After Width: | Height: | Size: 3.1 KiB |
BIN
docs/assets/simulation_usage/06_gazebo_px4_launcher.png
Normal file
|
After Width: | Height: | Size: 37 KiB |
@@ -47,6 +47,11 @@
|
||||
* [Using JavaScript](javascript.md)
|
||||
* [ROS](ros.md)
|
||||
* [MAVROS](mavros.md)
|
||||
* Simulation
|
||||
* [Overview](simulation.md)
|
||||
* [Native setup](simulation_native.md)
|
||||
* [VM setup](simulation_vm.md)
|
||||
* [Usage](simulation_usage.md)
|
||||
* Supplementary materials
|
||||
* [COEX Pix](coex_pix.md)
|
||||
* [Guide on autonomous flight](auto_setup.md)
|
||||
|
||||
@@ -160,7 +160,7 @@ Install the damper struts, fix *COEX Pix* on them with nylon nuts.
|
||||
<img src="../assets/assembling_clever4_2/fc_connection_6.png" width=300 class="zoom border">
|
||||
</div>
|
||||
|
||||
2. On a mounting deck, install 6mm racks and 30mm racks, fasten them with the M3x8 and M3x12 bolts, respectively.
|
||||
2. On a mounting deck, install 6mm racks and 30mm racks, fasten them with the M3x5 and M3x12 bolts, respectively.
|
||||
|
||||
<img src="../assets/assembling_clever4_2/raspberry_1.png" width=300 class="zoom border center">
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ Our [Raspberry Pi image](image.md) contains preinstalled modules for interfacing
|
||||
## High-level control
|
||||
|
||||
1. Connect the +5v and GND leads of your LED to a power source and the DIN (data in) lead to GPIO21. Consult the [assembly instructions](assemble_4_2.md#installing-led-strip) for details.
|
||||
2. Enable LED strip support in `~/catkin_ws/src/clever/clever/launch/clever.launch`:
|
||||
2. Enable LED strip support in `~/catkin_ws/src/clover/clover/launch/clover.launch`:
|
||||
|
||||
```xml
|
||||
<arg name="led" default="true"/>
|
||||
|
||||
32
docs/en/simulation.md
Normal file
@@ -0,0 +1,32 @@
|
||||
# Simulation overview
|
||||
|
||||
The Clover simulation environment allows users to run and debug their code within a simulator while using most of the features available on the real drone. The simulation utilizes [PX4 SITL mode](sitl.md) and uses the same ROS code as the real drone. Most hardware is simulated as well.
|
||||
|
||||
## Features
|
||||
|
||||
Basic, user-installable environment includes:
|
||||
|
||||
* high-quality Clover 4 visual model;
|
||||
* Gazebo plugins for Clover-specific hardware (e.g. LED strip);
|
||||
* modification-friendly [`xacro`](https://wiki.ros.org/xacro) drone descriptions;
|
||||
* sample models and worlds;
|
||||
* [`roslaunch`](https://wiki.ros.org/roslaunch) files for quick simulation launching and configuration.
|
||||
|
||||
Additionally, a [virtual machine image](simulation_vm.md) that mimics the real drone as closely as possible is provided with the following features:
|
||||
|
||||
* easy access to the simulation environment;
|
||||
* Visual Studio Code editor, preconfigured to work with ROS packages;
|
||||
* Monkey web server for web-based Clover plugins;
|
||||
* always-running `roscore` service;
|
||||
* ROS GUI tools (`rviz`, `rqt`).
|
||||
|
||||
## Architecture
|
||||
|
||||
The simulation environment is based on the following components:
|
||||
|
||||
* [Gazebo](http://gazebosim.org/), a state-of-the-art robotics simulator;
|
||||
* [PX4](https://px4.io/), specifically its SITL (software-in-the-loop) components;
|
||||
* [`sitl_gazebo`](https://github.com/PX4/sitl_gazebo) package containing Gazebo plugins for PX4;
|
||||
* ROS packages and Gazebo plugins.
|
||||
|
||||
<!-- TODO: Write more, add a diagram, etc -->
|
||||
96
docs/en/simulation_native.md
Normal file
@@ -0,0 +1,96 @@
|
||||
# Native setup
|
||||
|
||||
Setting up the simulation environment from scratch requires some effort, but results in the most performant setup, with less chance of driver issues.
|
||||
|
||||
Prerequisites: Ubuntu 18.04, [native ROS installation](ros-install.md).
|
||||
|
||||
## Create a workspace for the simulation
|
||||
|
||||
Throughout this guide we will be using the `catkin_ws` as the workspace name. Feel free to change it in your setup. We will be creating it in the home directory of the current user (`~`).
|
||||
|
||||
Create the workspace and clone Clover sources:
|
||||
|
||||
```bash
|
||||
mkdir -p ~/catkin_ws/src
|
||||
cd ~/catkin_ws/src
|
||||
git clone https://github.com/CopterExpress/clover
|
||||
git clone https://github.com/CopterExpress/ros_led
|
||||
```
|
||||
|
||||
Install all prerequisites using `rosdep`:
|
||||
|
||||
```bash
|
||||
cd ~/catkin_ws
|
||||
rosdep update
|
||||
rosdep install --from-paths src --ignore-src -y
|
||||
```
|
||||
|
||||
## Get PX4 sources
|
||||
|
||||
PX4 will be built along with the other packages in our workspace. You may clone it directly into the workspace or put it somewhere and symlink to `~/catkin_ws/src`. We will need to put its `sitl_gazebo` submodule in `~/catkin_ws/src` as well. For simplicity's sake we will clone the firmware directly to the workspace:
|
||||
|
||||
```bash
|
||||
cd ~/catkin_ws/src
|
||||
git clone --recursive https://github.com/CopterExpress/Firmware -b v1.10.1-clever
|
||||
ln -s Firmware/Tools/sitl_gazebo ./sitl_gazebo
|
||||
```
|
||||
|
||||
## Install PX4 prerequisites
|
||||
|
||||
PX4 comes with its own script for dependency installation. We may as well leverage it:
|
||||
|
||||
```bash
|
||||
cd ~/catkin_ws/src/Firmware/Tools/setup
|
||||
sudo ./ubuntu.sh
|
||||
```
|
||||
|
||||
This will install everything required to build PX4 and its SITL environment.
|
||||
|
||||
You may want to skip installing the ARM toolchain if you're not planning on compiling PX4 for your flight controller. To do this, use the `--no-nuttx` flag:
|
||||
|
||||
```
|
||||
sudo ./ubuntu.sh --no-nuttx
|
||||
```
|
||||
|
||||
## Patch Gazebo plugins
|
||||
|
||||
The `sitl_gazebo` package containing required Gazebo plugins needs patching due to recent changes in MAVLink. These patches are already preapplied in the [virtual machine image](simulation_vm.md) and are stored in the VM repository. Run the following commands to download and apply the patches:
|
||||
|
||||
```bash
|
||||
cd ~/catkin_ws/src/Firmware/Tools/sitl_gazebo
|
||||
wget https://raw.githubusercontent.com/CopterExpress/clover_vm/master/assets/patches/sitl_gazebo.patch
|
||||
patch -p1 < sitl_gazebo.patch
|
||||
rm sitl_gazebo.patch
|
||||
```
|
||||
|
||||
## Install geographiclib datasets
|
||||
|
||||
`mavros` requires geographiclib datasets to be present:
|
||||
|
||||
```bash
|
||||
cd ~
|
||||
wget https://raw.githubusercontent.com/mavlink/mavros/6f5bd5a1a67c19c2e605f33de296b1b1be9d02fc/mavros/scripts/install_geographiclib_datasets.sh
|
||||
chmod +x ./install_geographiclib_datasets.sh
|
||||
sudo ./install_geographiclib_datasets.sh
|
||||
rm ./install_geographiclib_datasets.sh
|
||||
```
|
||||
|
||||
## Build the simulator
|
||||
|
||||
With all dependencies installed, you can build your workspace:
|
||||
|
||||
```bash
|
||||
cd ~/catkin_ws
|
||||
catkin_make
|
||||
```
|
||||
|
||||
> **Note** Some of the files - particularly Gazebo plugins - require large amounts of RAM to be built. You may wish to reduce the number of parallel jobs; the number of parallel jobs should be equal to the amount of RAM in gigabytes divided by 2 - so a 16GB machine should use no more than 8 jobs. You can specify the number of jobs using the `-j` flag: `catkin_make -j8`
|
||||
|
||||
## Run the simulator
|
||||
|
||||
In order to be sure that everything was built correctly, try running the simulator for the first time:
|
||||
|
||||
```bash
|
||||
source ~/catkin_ws/devel/setup.bash
|
||||
roslaunch clover_simulation simulator.launch
|
||||
```
|
||||
89
docs/en/simulation_usage.md
Normal file
@@ -0,0 +1,89 @@
|
||||
# Using the simulator
|
||||
|
||||
The Clover simulation environment allows the user to test their code without any risk of equipment damage. Additionally, the [virtual machine](simulation_vm.md)-based environment has additional (non-ROS) services that are present on a real drone, like Monkey web server.
|
||||
|
||||
## Running the simulation
|
||||
|
||||
After [setting up the simulation packages](simulation_native.md) or [importing and running the VM](simulation_vm.md), you can use `roslaunch` to start Gazebo simulation:
|
||||
|
||||
```bash
|
||||
# Be sure to activate your workspace first
|
||||
source ~/catkin_ws/devel/setup.bash
|
||||
roslaunch clover_simulation simulator.launch
|
||||
```
|
||||
|
||||
> **Note** Alternatively, if you are using the VM, just double-click on the `Gazebo PX4` icon on the desktop.
|
||||
|
||||
This will launch Gazebo server and client, the PX4 SITL binary and Clover nodes. The terminal in which the command was run will display diagnostic messages from the nodes and PX4, and will accept input for the PX4 command interpreter:
|
||||
|
||||

|
||||
|
||||
You can use QGroundControl to configure the simulated drone parameters, plan missions (if GPS is simulated) and control the drone using a joystick:
|
||||
|
||||

|
||||
|
||||
You can also use [our simplified OFFBOARD control](simple_offboard.md) to control the drone, and traditional ROS GUI utilities like [rviz and rqt](rviz.md) to analyze the drone state:
|
||||
|
||||

|
||||
|
||||
## Configuring the simulation
|
||||
|
||||
The simulation can be configured by passing additional arguments to the `roslaunch` command or by changing the `~/catkin_ws/src/clover/clover_simulation/launch/simulator.launch` file. Nodes that provide [ArUco detection](aruco.md), [optical flow calculation](optical_flow.md) and other services can be configured by changing their respective `.launch` files, just like on a real drone.
|
||||
|
||||
### Changing the drone parameters
|
||||
|
||||

|
||||
|
||||
You can enable or disable some of the drone sensors by changing parameters in the `simulator.launch` file. For example, in order to enable GPS, set the `gps` argument to `true`:
|
||||
|
||||
```xml
|
||||
<arg name="gps" value="true"/>
|
||||
```
|
||||
|
||||
Note that this will simply enable the sensor, you will have to change the PX4 estimator parameters to enable GPS fusion.
|
||||
|
||||
If you wish to add additional sensors or change their placement, you will have to change the drone description. The description file is located in `~/catkin_ws/src/clover/clover_description/urdf/clover/clover4.xacro`, and uses the [xacro](http://wiki.ros.org/xacro) format to build URDF description.
|
||||
|
||||
### Changing the default world
|
||||
|
||||
Gazebo plugins for the drone currently require the `real_time_update_rate` world parameter to be 250, and `max_step_size` to be 0.004. Using other values will not work. Consider using `~/catkin_ws/src/clover/clover_simulation/resources/worlds/clover.world` as a base.
|
||||
|
||||
## Performance suggestions
|
||||
|
||||
Gazebo simulation environment is resource-intensive, and requires a fast CPU and a decent GPU for real-time execution. However, the simulation may still work on less powerful systems at slower-than-realtime rate. Below are some suggestions for running Gazebo on hardware that does not allow realtime execution.
|
||||
|
||||
### Use `throttling_camera` plugin
|
||||
|
||||
By default, Gazebo does not slow simulation down for visual sensors. This can be remedied by using the `throttling_camera` plugin from `clover_simulation`.
|
||||
|
||||
You can enable it for the drone by changing the `maintain_camera_rate` argument to `true` in `clover_description/launch/spawn_drone.launch`:
|
||||
|
||||
```xml
|
||||
<!-- Slow simulation down to maintain camera rate -->
|
||||
<arg name="maintain_camera_rate" default="true"/>
|
||||
```
|
||||
|
||||
The plugin will collect publishing rate statistics and slow the simulation down so that the camera publishing rate is no less than requested. However, large slowdowns may negatively affect non-ROS software that connects to PX4. If your simulation is being slowed down to values lower than 0.5 of realtime, consider using the next suggestion.
|
||||
|
||||
### Set simulation speed
|
||||
|
||||
Since v1.9 the PX4 SITL setup supports [setting the simulation speed](https://dev.px4.io/v1.9.0/en/simulation/#simulation_speed) by setting the `PX4_SIM_SPEED_FACTOR` environment variable. Its value is picked up by PX4 startup scripts, which in turn reconfigure it to expect a certain speedup/slowdown.
|
||||
|
||||
You should set its value to the actual real time factor that you get with `throttling_camera`. The real time factor may be found in the Gazebo GUI window at the bottom:
|
||||
|
||||

|
||||
|
||||
In this example you should set `PX4_SIM_SPEED_FACTOR` to `0.42` when launching the simulation:
|
||||
|
||||
```bash
|
||||
PX4_SIM_SPEED_FACTOR=0.42 roslaunch clover_simulation simulator.launch
|
||||
```
|
||||
|
||||
> **Note** If you are using the VM, it may be convenient to put the value in the Gazebo desktop shortcut. Right-click on the Gazebo icon, select "Properties..." and add `PX4_SIM_SPEED_FACTOR=0.42` to the Command field as follows:
|
||||

|
||||
|
||||
### Allocate more resources to the VM
|
||||
|
||||
The virtual machine may benefit from several CPU cores, especially if the cores are not very performant. In our tests, a four-core machine with only a single core allocated to the VM was unable to run the simulation, with constant interface freezes and dropped ROS messages. The same machine with all four cores available to the VM was able to run the simulation at 0.25 real-time speed.
|
||||
|
||||
Do note that you should not allocate more resources than you have on your host hardware.
|
||||
64
docs/en/simulation_vm.md
Normal file
@@ -0,0 +1,64 @@
|
||||
# Simulation VM setup
|
||||
|
||||
In addition to [native installation instructions](simulation_native.md), we provide a [preconfigured developer virtual machine image](https://github.com/CopterExpress/clover_vm/releases). The image contains:
|
||||
|
||||
* Ubuntu 18.04 with XFCE lightweight desktop environment;
|
||||
* ROS packages required to develop for the Clover platform;
|
||||
* QGroundControl;
|
||||
* preconfigured Gazebo simulation environment;
|
||||
* Visual Studio Code with C++ and Python plugins.
|
||||
|
||||
The VM is an easy way to set up a simulation environment, but can be used as a development environment for a real drone as well.
|
||||
|
||||
You can download the latest VM image [in the VM releases repository](https://github.com/CopterExpress/clover_vm/releases).
|
||||
|
||||
> **Note** The virtual machine should be used when native installation is not feasible or possible. You may experience reduced performance in programs that use 3D rendering, like rviz and Gazebo.
|
||||
|
||||
## Setting up the VM
|
||||
|
||||
You need to use a VM manager that supports OVF format, like [Virtualbox](https://www.virtualbox.org/wiki/Downloads), [VMware Player](https://www.vmware.com/products/workstation-player.html) or [VMware Workstation](https://www.vmware.com/products/workstation-pro.html).
|
||||
|
||||
> **Note** At the time of writing Virtualbox had issues running the VM, particularly with 3D applications. We recommend using VMware Player or VMware Workstation if possible. The following steps assume you're using VMware Player.
|
||||
|
||||
Make sure that you have hardware virtualization enabled in your BIOS/UEFI (it may be supported by your hardware but turned off by default). The steps to enable virtualization differ from manufacturer to manufacturer, but should be described in your system manual. Consult your system's manufacturer if you're having trouble turning virtualization on.
|
||||
|
||||
1. Import the OVA archive into your virtualization environment. Use the **Open a Virtual Machine** option in VMware Player:
|
||||
|
||||

|
||||
|
||||
> **Note** You may see a dialog box with a warning about the VM format:
|
||||

|
||||
You can safely ignore the warning and press **Retry**.
|
||||
|
||||
2. Right-click on the VM name and select **Virtual Machine Settings**. In the new window, set the following parameters:
|
||||
|
||||
* increase the amount of memory available to the virtual machine (a good rule of thumb is 2048 MB per CPU core, but no less than 4 GB):
|
||||

|
||||
* increase the amount available CPU cores:
|
||||

|
||||
* enable 3D acceleration:
|
||||

|
||||
* enable USB 2.0/3.0 controller (if you plan to connect external devices to the VM):
|
||||

|
||||
* optionally enable the "bridged" network connection (if you plan to connect to a real drone):
|
||||

|
||||
|
||||
> **Note** Some host network adapters may not work well with the bridged network. Consider using external USB Wi-Fi adapters managed by your VM to connect to a real drone.
|
||||
|
||||
3. "Power on" the virtual machine. You may see a warning message about your host system not providing 3D acceleration:
|
||||
|
||||

|
||||
|
||||
Make sure you have the latest GPU drivers for your host system. If the warnings persist, add the following line to `clover-devel.vmx` (actual file name may differ based on the VM name):
|
||||
|
||||
```
|
||||
mks.gl.allowBlacklistedDrivers = "TRUE"
|
||||
```
|
||||
|
||||
You can find this file in a folder where the VM is imported to.
|
||||
|
||||
4. (Bridged networking only) Set up network bridge configuration in VM settings or using `vmware-netcfg` utility (in Linux):
|
||||
|
||||

|
||||
|
||||
Select `vmnet0` in the networks list, set it to *Bridged*, and choose the adapter you are planning to use to connect to drone in the drop-down menu.
|
||||
@@ -1,6 +1,8 @@
|
||||
PX4 Simulation
|
||||
===
|
||||
|
||||
> **Hint** This article is about running a standalone PX4 simulation with a generic quadcopter. Consider using [our configuration](simulation.md) for a more Clover-like experience.
|
||||
|
||||
Main article: https://dev.px4.io/en/simulation/
|
||||
|
||||
PX4 simulation is possible in Linux and macOS with the use of physical environment simulation systems [jMAVSim](https://pixhawk.org/dev/hil/jmavsim) and [the Gazebo](http://gazebosim.org).
|
||||
|
||||
@@ -47,11 +47,18 @@
|
||||
* [Автозапуск ПО](autolaunch.md)
|
||||
* [Использование JavaScript](javascript.md)
|
||||
* [`mavros`](mavros.md)
|
||||
* Симулятор
|
||||
* [Общая информация](simulation.md)
|
||||
* [Сборка на собственной машине](simulation_native.md)
|
||||
* [Установка виртуальной машины](simulation_vm.md)
|
||||
* [Использование симулятора](simulation_usage.md)
|
||||
* ROS: учебник
|
||||
* [Общая информация](ros.md)
|
||||
* [Графические инструменты](ros_gui.md)
|
||||
* [Соглашения ROS](ros_conventions.md)
|
||||
* [Продвинутое использование](ros_advanced.md)
|
||||
* [ROS](ros.md)
|
||||
* [MAVROS](mavros.md)
|
||||
* Дополнительные материалы
|
||||
* [COEX Pix](coex_pix.md)
|
||||
* [Гид по автономному полету](auto_setup.md)
|
||||
|
||||
@@ -160,7 +160,7 @@
|
||||
<img src="../assets/assembling_clever4_2/fc_connection_6.png" width=300 class="zoom border">
|
||||
</div>
|
||||
|
||||
2. На монтажную деку установите стойки 6мм и стойки 30мм, закрепите их с помощью винт М3х8 и М3х12 соответственно.
|
||||
2. На монтажную деку установите стойки 6мм и стойки 30мм, закрепите их с помощью винт М3х5 и М3х12 соответственно.
|
||||
|
||||
<img src="../assets/assembling_clever4_2/raspberry_1.png" width=300 class="zoom border center">
|
||||
|
||||
@@ -204,6 +204,10 @@
|
||||
|
||||
<img src="../assets/assembling_clever4_2/raspberry_11.png" width=300 class="zoom border center">
|
||||
|
||||
13. Подключите полетный контроллер к *Raspberry Pi* с помощью USB кабеля.
|
||||
|
||||
<img src="../assets/assembling_clever4_2/final_2.png" width=300 class="zoom border">
|
||||
|
||||
## Установка LED ленты
|
||||
|
||||
1. Соберите обруч для светодиодной ленты, объединив замок на концах.
|
||||
@@ -252,21 +256,4 @@
|
||||
|
||||
<img src="../assets/assembling_clever4_2/guard_4.png" width=300 class="zoom border center">
|
||||
|
||||
## Подготовка к полету
|
||||
|
||||
1. Установите ремешок для аккумулятора и подключите полетный контроллер к *Raspberry Pi* с помощью USB кабеля.
|
||||
|
||||
<div class="image-group">
|
||||
<img src="../assets/assembling_clever4_2/final_1.png" width=300 class="zoom border">
|
||||
<img src="../assets/assembling_clever4_2/final_2.png" width=300 class="zoom border">
|
||||
</div>
|
||||
|
||||
2. Установите пропеллеры в соответствии со [схемой направления движения моторов](#prop_rotation).
|
||||
|
||||
<img src="../assets/assembling_clever4_2/final_3.png" width=300 class="zoom border center">
|
||||
|
||||
3. Установите аккумулятор.
|
||||
|
||||
<img src="../assets/assembling_clever4_2/final_4.png" width=300 class="zoom border center">
|
||||
|
||||
> **Success** Дрон собран, далее произведите ["настройку"](setup.md).
|
||||
|
||||
34
docs/ru/simulation.md
Normal file
@@ -0,0 +1,34 @@
|
||||
# Общая информация
|
||||
|
||||
Среда симуляции Клевера позволяет пользователям запускать и отлаживать свой код в симуляторе, используя большинство функций, доступных на реальном дроне. Симулятор использует [режим PX4 SITL](sitl.md) и тот же код, использующий ROS, что и настоящий дрон. Большинство железа также симулируется.
|
||||
|
||||
## Особенности
|
||||
|
||||
Устанавливаемая пользователем среда включает в себя:
|
||||
|
||||
* высококачественную модель Клевера 4;
|
||||
* плагины Gazebo для железа Клевера (например, для светодиодной ленты);
|
||||
* легко изменяемые файлы описания дрона в формате [`xacro`](https://wiki.ros.org/xacro);
|
||||
* примеры моделей и миров;
|
||||
* [`roslaunch`](https://wiki.ros.org/roslaunch) файлы для быстрого запуска и настройки.
|
||||
|
||||
Кроме того, предоставляется [образ виртуальной машины](simulation_vm.md), который максимально точно имитирует реальный дрон.
|
||||
|
||||
Особенности:
|
||||
|
||||
* легкий доступ к симулятору;
|
||||
* установлен и настроен для работы с ROS Visual Studio Code;
|
||||
* веб-сервер (Monkey) для плагинов Клевера, работающих в браузере;
|
||||
* постоянно работающий сервис `roscore`;
|
||||
* средства визуализации (`rviz`, `rqt`).
|
||||
|
||||
## Состав симулятора
|
||||
|
||||
Симулятор основан на следующих элементах:
|
||||
|
||||
* [Gazebo](http://gazebosim.org/), универсальная среда симуляции для любых типов роботов;
|
||||
* [PX4](https://px4.io/), в частности, его компонент SITL (software-in-the-loop);
|
||||
* [`sitl_gazebo`](https://github.com/PX4/sitl_gazebo) пакет, содержащий плагины Gazebo для PX4;
|
||||
* пакеты ROS и плагины Gazebo;
|
||||
|
||||
<!-- TODO: Write more, add a diagram, etc -->
|
||||
96
docs/ru/simulation_native.md
Normal file
@@ -0,0 +1,96 @@
|
||||
# Сборка на собственной машине
|
||||
|
||||
Настройка среды для симуляции с нуля требует некоторых усилий, однако это приведет к улучшению производительности и к уменьшению вероятности появления проблем с драйверами.
|
||||
|
||||
Требования для сборки: установлены Ubuntu 18.04 и [ROS](ros-install.md).
|
||||
|
||||
## Создание рабочего пространства для симулятора
|
||||
|
||||
В этой статье мы будем использовать `catkin_ws` как имя рабочего пространства (вы можете поменять её). Мы создадим её в домашнем каталоге текущего пользователя (`~`).
|
||||
|
||||
Создайте рабочее пространство и загрузите исходный код Клевера:
|
||||
|
||||
```bash
|
||||
mkdir -p ~/catkin_ws/src
|
||||
cd ~/catkin_ws/src
|
||||
git clone https://github.com/CopterExpress/clover
|
||||
git clone https://github.com/CopterExpress/ros_led
|
||||
```
|
||||
|
||||
Установите все зависимости, используя `rosdep`:
|
||||
|
||||
```bash
|
||||
cd ~/catkin_ws
|
||||
rosdep update
|
||||
rosdep install --from-paths src --ignore-src -y
|
||||
```
|
||||
|
||||
## Загрузка исходного кода PX4
|
||||
|
||||
Сборка PX4 будет осуществлена вместе с другими пакетами в нашем рабочем пространстве. Вы можете загрузить его прямо в рабочее пространство или поместить куда-нибудь и создать симлинк к `~/catkin_ws/src`. Нам также нужно будет поместить его подмодуль `sitl_gazebo` в `~/catkin_ws/src`. Для упрощения мы загрузим прошивку прямо в рабочее пространство:
|
||||
|
||||
```bash
|
||||
cd ~/catkin_ws/src
|
||||
git clone --recursive https://github.com/CopterExpress/Firmware -b v1.10.1-clever
|
||||
ln -s Firmware/Tools/sitl_gazebo ./sitl_gazebo
|
||||
```
|
||||
|
||||
## Установка зависимостей PX4
|
||||
|
||||
PX4 имеет свой собственный скрипт для установки зависимостей. Воспользуемся им:
|
||||
|
||||
```bash
|
||||
cd ~/catkin_ws/src/Firmware/Tools/setup
|
||||
sudo ./ubuntu.sh
|
||||
```
|
||||
|
||||
Он установит все, что нужно для сборки PX4 и SITL.
|
||||
|
||||
Также вы можете пропустить установку ARM тулчейна, если вы не планируете компилировать PX4 для вашего полетного контроллера. Для этого воспользуйтесь флагом `--no-nuttx`:
|
||||
|
||||
```
|
||||
sudo ./ubuntu.sh --no-nuttx
|
||||
```
|
||||
|
||||
## Патчинг плагинов Gazebo
|
||||
|
||||
Пакет `sitl_gazebo`, содержащий плагины нужно пропатчить, из-за недавних изменений в MAVLink. Эти патчи уже применены в [образе виртуальной машины](simulation_vm.md) и хранятся в репозитории CopterExpress/VM. Запустите следующие команды для загрузки и применения патчей:
|
||||
|
||||
```bash
|
||||
cd ~/catkin_ws/src/Firmware/Tools/sitl_gazebo
|
||||
wget https://raw.githubusercontent.com/CopterExpress/clover_vm/master/assets/patches/sitl_gazebo.patch
|
||||
patch -p1 < sitl_gazebo.patch
|
||||
rm sitl_gazebo.patch
|
||||
```
|
||||
|
||||
## Установка датасетов geographiclib
|
||||
|
||||
Для `mavros` нужны датасеты geographiclib:
|
||||
|
||||
```bash
|
||||
cd ~
|
||||
wget https://raw.githubusercontent.com/mavlink/mavros/6f5bd5a1a67c19c2e605f33de296b1b1be9d02fc/mavros/scripts/install_geographiclib_datasets.sh
|
||||
chmod +x ./install_geographiclib_datasets.sh
|
||||
sudo ./install_geographiclib_datasets.sh
|
||||
rm ./install_geographiclib_datasets.sh
|
||||
```
|
||||
|
||||
## Сборка симулятора
|
||||
|
||||
После установки всех зависимостей можно начинать сборку рабочего пространства:
|
||||
|
||||
```bash
|
||||
cd ~/catkin_ws
|
||||
catkin_make
|
||||
```
|
||||
|
||||
> **Note** Некоторые файлы, особенно плагины Gazebo, требуют большого объема оперативной памяти для сборки. Вы можете уменьшить количество параллельных процессов; количество параллельных процессов должно быть равно объёму RAM в гигабайтах, поделенному на 2. Например, для машины с 16Гб следует указывать не более 8 процессов. Вы можете указать количество процессов, используя флаг `-j` : ```catkin_make -j8```
|
||||
|
||||
## Запуск симулятора
|
||||
|
||||
Чтобы удостовериться в том, что все было собрано корректно, попробуйте запустить симулятор:
|
||||
|
||||
```bash
|
||||
source ~/catkin_ws/devel/setup.bash
|
||||
roslaunch clover_simulation simulator.launch
|
||||
```
|
||||
89
docs/ru/simulation_usage.md
Normal file
@@ -0,0 +1,89 @@
|
||||
# Использование симулятора
|
||||
|
||||
Среда симуляции Клевера позволяет пользователям тестировать свой код без какого-либо риска повреждения оборудования. Кроме того, в [виртуальной машине](simulation_vm.md) по умолчанию запущены дополнительные (не относящиеся к ROS) сервисы, которые присутствуют на реальном дроне, например, веб-сервер Monkey.
|
||||
|
||||
## Запуск симулятора
|
||||
|
||||
После [сборки симулятора с нуля](simulation_native.md) или [запуска виртуальной машины](simulation_vm.md), вы можете использовать `roslaunch` для запуска симулятора Gazebo:
|
||||
|
||||
```bash
|
||||
# Не забудьте сначала активировать ваше рабочее пространство
|
||||
source ~/catkin_ws/devel/setup.bash
|
||||
roslaunch clover_simulation simulator.launch
|
||||
```
|
||||
|
||||
> **Note** Кроме того, если вы используете виртуальную машину, просто дважды щелкните `Gazebo PX4` на рабочем столе.
|
||||
|
||||
Это запустит Gazebo сервер и клиент, бинарные файлы PX4 и ноды Клевера. Терминал, в котором была запущена команда, будет отображать отладочные сообщения от нод и PX4, а также принимать входные данные для интерпретатора команд PX4:
|
||||
|
||||

|
||||
|
||||
Вы также можете использовать QGroundControl для настройки параметров дрона в симуляторе, планирование миссий полета (если GPS также симулируется) и управление дроном с пульта:
|
||||
|
||||

|
||||
|
||||
Также вы можете использовать [simplified OFFBOARD](simple_offboard.md) для управления дроном, и средства визуализации ROS, например, [rviz and rqt](rviz.md) для анализа состояния дрона:
|
||||
|
||||

|
||||
|
||||
## Настройка симулятора
|
||||
|
||||
Симулятор можно настроить, передав дополнительные аргументы команде `roslaunch` или изменив файл `~/catkin_ws/src/clover/clover_simulation/launch/simulator.launch`. Ноды, обеспечивающие [распознавание ArUco](aruco.md), [расчет optical flow](optical_flow.md) и другие сервисы могут быть настроены изменением соответствующих `.launch` файлов, как на реальном дроне.
|
||||
|
||||
### Изменение параметров дрона
|
||||
|
||||

|
||||
|
||||
Вы можете включить или отключить некоторые датчики дрона, изменив параметры в файле `simulator.launch`. Например, чтобы включить GPS, установите аргумент `gps` в значение `true`:
|
||||
|
||||
```xml
|
||||
<arg name="gps" value="true"/>
|
||||
```
|
||||
|
||||
Обратите внимание, что это просто включит датчик, вам придется также изменить параметры PX4.
|
||||
|
||||
Если вы хотите добавить датчики или изменить их расположение, вам придется изменить файл описания дрона. Этот файл находится в `~/catkin_ws/src/clover/clover_description/urdf/clover/clover4.xacro`, и использует формат [xacro](http://wiki.ros.org/xacro) для сборки описания URDF.
|
||||
|
||||
### Изменение мира по умолчанию
|
||||
|
||||
Плагины Gazebo для дрона на данный момент требуют, чтобы значение параметра мира `real_time_update_rate` было равно 250, а значение `max_step_size` было равно 0.004. С другими параметрами симулятор не заработает. Используйте файл `~/catkin_ws/src/clover/clover_simulation/resources/worlds/clover.world` как шаблон.
|
||||
|
||||
## Повышение производительности
|
||||
|
||||
Симуляция с использованием Gazebo требовательна к ресурсам, и для нормальной скорости работы требуются мощный процессор и видеокарта. При этом симуляции можно запускать и на менее мощном оборудовании, жертвуя при этом скоростью работы. Ниже приведены рекомендации для компьютеров, на которых симуляция не может работать в реальном времени.
|
||||
|
||||
### Использование плагина `throttling_camera`
|
||||
|
||||
По умолчанию Gazebo не замедляет симуляцию для достижения требуемой частоты работы визуальных сенсоров. Это можно исправить с помощью плагина `throttling_camera` из пакета `clover_simulation`.
|
||||
|
||||
Включение этого плагина происходит путём выставления параметра `maintain_camera_rate` в значение `true` в файле `clover_description/launch/spawn_drone.launch`:
|
||||
|
||||
```xml
|
||||
<!-- Slow simulation down to maintain camera rate -->
|
||||
<arg name="maintain_camera_rate" default="true"/>
|
||||
```
|
||||
|
||||
Этот плагин будет собирать статистику по частоте публикации изображений, и будет замедлять симуляцию до тех пор, пока частота публикации не станет соответствовать или превышать требуемую. При этом значительные замедления симуляции могут негативно сказаться на программном обеспечении, которое подключается к PX4 (например, QGroundControl). Если скорость симуляции опускается ниже, чем 0.5 от реального времени, следует воспользоваться следующей рекомендацией.
|
||||
|
||||
### Выставление скорости симуляции
|
||||
|
||||
PX4, начиная с версии 1.9, поддерживает [принудительную установку скорости симуляции](https://dev.px4.io/v1.9.0/en/simulation/#simulation_speed) с помощью переменной окружения `PX4_SIM_SPEED_FACTOR`. Выставление этой переменной подготавливает все компоненты симулятора к соответствующему ускорению/замедлению.
|
||||
|
||||
Значение этой переменной должно соответствовать величине Real Time Factor (скорости симуляции по отношению к реальному времени), получаемой при использовании `throttling_camera`. Величина Real Time Factor отображается в окне Gazebo на нижней панели:
|
||||
|
||||

|
||||
|
||||
В данном случае `PX4_SIM_SPEED_FACTOR` следует выставить в значение `0.42` перед запуском симулятора:
|
||||
|
||||
```bash
|
||||
PX4_SIM_SPEED_FACTOR=0.42 roslaunch clover_simulation simulator.launch
|
||||
```
|
||||
|
||||
> **Note** При использовании виртуальной машины удобнее добавить эту переменную в ярлык запуска Gazebo на рабочем столе. Нажмите правой кнопкой на значок Gazebo, выберите "Properties..." и добавьте `PX4_SIM_SPEED_FACTOR=0.42` в поле Command, как показано на иллюстрации:
|
||||

|
||||
|
||||
### Выделение ресурсов для виртуальной машины
|
||||
|
||||
Выделение нескольких процессорных ядер для виртуальной машины может значительно повысить производительность симуляции. В наших испытаниях виртуальная машина, для которой было выделено одно ядро, не позволяла работать в симуляторе: окно Gazebo не реагировало на пользовательский ввод, сообщения ROS терялись. После выделения четырёх ядер для этой же виртуальной машины симуляция стала работать со скоростью 0.25 от реального времени.
|
||||
|
||||
При этом не следует пытаться выделить для виртуальной машины больше ресурсов, чем доступно на основной системе.
|
||||
66
docs/ru/simulation_vm.md
Normal file
@@ -0,0 +1,66 @@
|
||||
# Установка виртуальной машины
|
||||
|
||||
Для работы с платформой Клевер рекомендуется иметь [установленное окружение ROS](ros.md) на своём компьютере. К сожалению, [установка ROS](ros-install.md) сопряжена с рядом трудностей: требуется использовать операционную систему Ubuntu 18.04, процесс установки длительный и требует выполнения большого количества команд в терминале.
|
||||
|
||||
Для облегчения процесса настройки окружения мы предлагаем использовать виртуальную машину со всем необходимым для работы с платформой Клевер. В состав виртуальной машины входят:
|
||||
|
||||
* операционная система Ubuntu 18.04 с легковесной графической оболочкой XFCE;
|
||||
* предустановленные пакеты ROS для работы с Клевером;
|
||||
* QGroundControl;
|
||||
* предварительно настроенный симулятор Gazebo;
|
||||
* среда разработки Visual Studio Code с плагинами для разработки на Python и C++.
|
||||
|
||||
Виртуальная машина может использоваться как для запуска симуляторов, так и для работы с настоящим дроном.
|
||||
|
||||
Скачать текущую версию виртуальной машины можно [в релизах репозитория виртуальной машины](https://github.com/CopterExpress/clover_vm/releases)
|
||||
|
||||
> **Warning** Виртуальную машину следует использовать только в тех случаях, когда по каким-то причинам использование Ubuntu 18.04 напрямую невозможно. Производительность всех программ, особенно тех, которые используют 3D-графику - jMAVSim, Gazebo, rviz - будет существенно ниже; кроме того, в ряде случаев будут возникать графические ошибки, приводящие к частичной или полной неработоспособности указанных программ.
|
||||
|
||||
## Установка виртуальной машины
|
||||
|
||||
Для запуска виртуальной машины разработчика требуется использовать одну из совместимых сред виртуализации: [VirtualBox](https://www.virtualbox.org/wiki/Downloads), [VMware Player](https://www.vmware.com/products/workstation-player.html), [VMware Workstation](https://www.vmware.com/products/workstation-pro.html).
|
||||
|
||||
> **Note** На момент написания данной статьи VirtualBox не обеспечивал достаточный уровень совместимости с виртуальной машиной. Рекомендуется по возможности использовать VMware Player или VMware Workstation; дальнейшая инструкция будет преимущественно написана для VMware Player.
|
||||
|
||||
Убедитесь, что поддержка аппаратной виртуализации включена в настройках BIOS/UEFI вашего компьютера. Шаги для включения аппаратной виртуализации, как правило, описаны в руководстве пользователя компьютера. Проконсультируйтесь с производителем компьютера, если включить виртуализацию не получается.
|
||||
|
||||
1. Импортируйте архив виртуальной машины в среду виртуализации. Для VMware Player используйте опцию **Open a Virtual Machine**:
|
||||
|
||||

|
||||
|
||||
> **Note** При импорте архива, скорее всего, появится окно с предупреждением о формате виртуальной машины:
|
||||

|
||||
Это предупреждение можно игнорировать и нажать кнопку **Retry**.
|
||||
|
||||
2. Откройте окно настроек виртуальной машины и измените параметры для наилучшего соответствия основной системе:
|
||||
|
||||
* увеличьте объём оперативной памяти, отводимый для виртуальной машины:
|
||||

|
||||
* увеличьте количество доступных процессорных ядер:
|
||||

|
||||
* включите 3D-ускорение:
|
||||

|
||||
* включите использование USB 2.0/3.0:
|
||||

|
||||
* опционально включите режим "мост" для виртуального сетевого адаптера:
|
||||

|
||||
|
||||
> **Note** Режим "мост" может некорректно работать с некоторыми сетевыми адаптерами. Если в режиме "мост" вы не можете подключиться к дрону, используйте USB Wi-Fi-адаптеры, "проброшенные" в виртуальную машину.
|
||||
|
||||
3. Запустите виртуальную машину. Возможно, при первом запуске справа появятся сообщения об отсутствии поддержки 3D-ускорения со стороны основной системы:
|
||||
|
||||

|
||||
|
||||
В этом случае убедитесь, что у вас установлены самые последние драйверы для видеокарты в основной системе. Если сообщения появляются при повторных запусках виртуальной машины, добавьте строку
|
||||
|
||||
```
|
||||
mks.gl.allowBlacklistedDrivers = "TRUE"
|
||||
```
|
||||
|
||||
в файл `clever-devel.vmx`, находящийся в папке, в которую был импортирован архив в п. 1.
|
||||
|
||||
4. Настройте режим моста через настройки виртуальной машины (если используется VMware Player для Windows) или с помощью утилиты `vmware-netcfg` (если используется версия для Linux-дистрибутивов):
|
||||
|
||||

|
||||
|
||||
В списке сетей выберите `vmnet0`, ниже - режим *Bridged*, в выпадающем списке *Bridged to* - название беспроводного адаптера, с помощью которого будет производиться подключение к дрону.
|
||||
@@ -1,6 +1,6 @@
|
||||
# Симуляция PX4
|
||||
|
||||
> **Hint** Мы также предоставляем [предварительно настроенный](sitl_docker.md) симулятор, поставляемый в виде Docker-контейнера!
|
||||
> **Hint** Мы также предоставляем [конфигурации для Gazebo](simulation.md) и [образ виртуальной машины](simulation_vm.md) со всем необходимым для запуска симуляции Клевера.
|
||||
|
||||
Основная статья: https://dev.px4.io/en/simulation/
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# Docker-контейнер с преднастроенным SITL
|
||||
|
||||
> **Warning** Рекомендуется использовать [образ виртуальной машины](simulation_vm.md) или [нативную установку](simulation_native.md) для работы в симуляторе.
|
||||
|
||||

|
||||
|
||||
Для упрощения запуска симулятора предлагается использовать предварительно настроенный [Docker-контейнер](https://hub.docker.com/r/sfalexrog/clever-sitl) с симулятором [Gazebo](http://gazebosim.org/), автопилотом [PX4](https://px4.io/) и предустановленными пакетами Клевера.
|
||||
|
||||