Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/ros2-add-launch-and-remappings' …
Browse files Browse the repository at this point in the history
…into ros2-add-ur-component

Signed-off-by: Jakub Delicat <[email protected]>
  • Loading branch information
delihus committed May 10, 2024
2 parents bc117d8 + e999c15 commit ca12cba
Show file tree
Hide file tree
Showing 17 changed files with 356 additions and 620 deletions.
5 changes: 1 addition & 4 deletions .github/workflows/colcon_test_build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,10 @@ jobs:
colcon-test-build-ubuntu-22-04:
strategy:
matrix:
build-type: [ignition-gazebo, gazebo-classic]
ros-distro: [humble]
env:
GAZEBO_VERSION: ${{ matrix.build-type }}
runs-on: ubuntu-22.04

name: ${{ matrix.ros-distro }}
name: Build ${{ matrix.ros-distro }}
steps:
- uses: ros-tooling/[email protected]
with:
Expand Down
10 changes: 5 additions & 5 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@ install(

if(BUILD_TESTING)
find_package(ament_cmake_pytest REQUIRED)
set(_pytest_tests
test/test_components_yaml_parse.py
set(pytest_tests
test/test_components_xacro.py
)
foreach(_test_path ${_pytest_tests})
get_filename_component(_test_name ${_test_path} NAME_WE)
ament_add_pytest_test(${_test_name} ${_test_path}
foreach(test_path ${pytest_tests})
get_filename_component(test_name ${test_path} NAME_WE)
ament_add_pytest_test(${test_name} ${test_path}
APPEND_ENV PYTHONPATH=${CMAKE_CURRENT_BINARY_DIR}
TIMEOUT 60
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
Expand Down
21 changes: 5 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,6 @@ git clone https://github.com/husarion/ros_components_description.git src/ros_com
# in case the package will be used within simulation
export HUSARION_ROS_BUILD_TYPE=simulation

# to specify which simulation engine will be used
# for gazebo classic
export SIMULATION_ENGINE=gazebo-classic
# for ignition gazebo
export SIMULATION_ENGINE=ignition-gazebo

rosdep update --rosdistro $ROS_DISTRO
rosdep install -i --from-path src --rosdistro $ROS_DISTRO -y
colcon build
Expand All @@ -36,22 +30,17 @@ To include the sensor, use the following code:
<xacro:lidar.slamtec_rplidar_s1
parent_link="cover_link"
xyz="0.0 0.0 0.0"
rpy="0.0 0.0 0.0"
use_gpu="true"
simulation_engine="gazebo-classic" />
rpy="0.0 0.0 0.0" />
```

A list of parameters can be found here:

- `parent_link` [*string*, default: **None**] parent link to which sensor should be attached.
- `xyz` [*float list*, default: **None**] 3 float values defining translation between base of a sensor and parent link. Values in **m**.
- `rpy` [*float list*, default: **None**] 3 float values define rotation between parent link and base of a sensor. Values in **rad**.
- `tf_prefix` [*string*, optional] tf prefix applied before all links created by sensor. If defined, applies `<tf_prefix>_<sensor_name>`. If not defined, leaves `<sensor_name>` intact. Applies also to `frame_id` parameter.
- `topic` [*string*, default: **same as manufacturer's default**] name of topic at which simulated sensor will publish data.
- `frame_id` [*string*, default: **same as manufacturer's default**] name of final tf to which sensor will be attached. Should match one from message published by sensor.
- `use_gpu` [*bool*, default: **false**] enable GPU acceleration for sensor. Available only if sensor can be accelerated.
- `simulation_engine` [*string*, default: **gazebo-classic**] selected for which simulation engine plugins should be loaded. Currently the only supported:
- **gazebo-classic** used to select [Gazebo Classic](https://classic.gazebosim.org/).
- **ignition-gazebo** used to select [Ignition Gazebo](https://gazebosim.org/home).
- `namespace` [*string*, default: **None**] global namespace common to the entire robot.
- `device_namespace` [*string*, default: **None**] local namespace allowing to distinguish two identical devices from each other.

- `model` [*string*, default: **None**] model argument that appears when you want to load the appropriate model from a given manufacturer.

Some sensors can define their specific parameters. Refer to their definition for more info.
27 changes: 4 additions & 23 deletions package.xml
Original file line number Diff line number Diff line change
Expand Up @@ -24,29 +24,10 @@
<depend>kortex_description</depend>
<depend>robotiq_description</depend>

<depend condition="($HUSARION_ROS_BUILD_TYPE == simulation) and ($SIMULATION_ENGINE == gazebo-classic)">
gazebo_plugins
</depend>

<depend condition="($HUSARION_ROS_BUILD_TYPE == simulation) and ($SIMULATION_ENGINE == ignition-gazebo)">
ros_gz_sim
</depend>

<depend condition="($HUSARION_ROS_BUILD_TYPE == simulation) and ($SIMULATION_ENGINE == ignition-gazebo)">
ros_gz_bridge
</depend>

<depend condition="($HUSARION_ROS_BUILD_TYPE == simulation) and ($SIMULATION_ENGINE == ignition-gazebo)">
launch
</depend>

<depend condition="($HUSARION_ROS_BUILD_TYPE == simulation) and ($SIMULATION_ENGINE == ignition-gazebo)">
nav2_common
</depend>

<depend condition="($HUSARION_ROS_BUILD_TYPE == simulation) and ($SIMULATION_ENGINE == ignition-gazebo)">
robotiq_controllers
</depend>
<depend condition="($HUSARION_ROS_BUILD_TYPE == simulation)">ros_gz_sim</depend>
<depend condition="($HUSARION_ROS_BUILD_TYPE == simulation)">ros_gz_bridge</depend>
<depend condition="($HUSARION_ROS_BUILD_TYPE == simulation)">launch</depend>
<depend condition="($HUSARION_ROS_BUILD_TYPE == simulation)">nav2_common</depend>

<test_depend>ament_index_python</test_depend>
<test_depend>ament_python_pytest</test_depend>
Expand Down
2 changes: 0 additions & 2 deletions test/component.urdf.xacro
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@

<xacro:husarion_components.create_components
components_config_path="${components_config_path_property}"
use_gpu="True"
simulation_engine="ignition-gazebo"
namespace="None"
/>
</xacro:unless>
Expand Down
137 changes: 137 additions & 0 deletions test/test_components_xacro.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
# Copyright 2024 Husarion sp. z o.o.
#
# 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.


import xml.dom
import xml.dom.minidom
import os
import xacro
import xml
import yaml
from ament_index_python.packages import get_package_share_directory

ros_components_description = get_package_share_directory("ros_components_description")
xacro_path = os.path.join(ros_components_description, "test/component.urdf.xacro")

# Type: [model_link, sensor_link, sensor_name]
components_types_with_names = {
"LDR01": ["slamtec_rplidar_s1", "laser", "slamtec_rplidar_s1_sensor"],
"LDR06": ["slamtec_rplidar_s3", "laser", "slamtec_rplidar_s3_sensor"],
"LDR13": ["ouster_os1_32", "os_lidar", "ouster_os1_32_sensor"],
"LDR20": ["velodyne_puck", "velodyne", "velodyne_puck_sensor"],
"CAM01": ["orbbec_astra", "link", "orbbec_astra_color"],
}


class ComponentsYamlParseUtils:
__test__ = False

def __init__(self, components_config_path: str) -> None:
self.components_config_path = components_config_path
self._urdf = xml.dom.minidom.Document()

def save_yaml(self, node: yaml.Node) -> None:
with open(self.components_config_path, mode="w", encoding="utf-8") as file:
yaml.dump(node, file, default_flow_style=False)

def create_component(
self,
type: str,
device_namespace: str,
parent_link="cover_link",
xyz="0.0 0.0 0.0",
rpy="0.0 0.0 0.0",
) -> dict:
return {
"type": type,
"parent_link": parent_link,
"xyz": xyz,
"rpy": rpy,
"device_namespace": device_namespace,
}

def does_urdf_parse(self) -> bool:
try:
self._urdf = xacro.process_file(
xacro_path, mappings={"components_config_path": self.components_config_path}
)
except xacro.XacroException as e:
return False
return True

def does_link_exist(self, doc: xml.dom.minidom.Document, link_name: str) -> bool:
links = doc.getElementsByTagName('link')
for link in links:
if link.getAttribute('name') == link_name:
return True
return False

def does_sensor_name_exist(
self, doc: xml.dom.minidom.Document, link_name: str, sensor_name: str
) -> bool:
gazebos_tags = doc.getElementsByTagName('gazebo')
for tag in gazebos_tags:
if tag.getAttribute('reference') == link_name:
sensors = doc.getElementsByTagName('sensor')
for sensor in sensors:
if sensor.getAttribute('name') == sensor_name:
return True

return False

def test_component(self, component: dict, expected_result: list, components_config_path: str):
names = components_types_with_names[component["type"]]
component_name = names[0]
sensor_reference = names[1]
sensor_name = names[2]

device_namespace = component["device_namespace"]
link_name = device_namespace + "_" + component_name + "_link"
sensor_name = device_namespace + "_" + sensor_name
sensor_link_name = device_namespace + "_" + sensor_reference

if self.does_urdf_parse() != expected_result[0]:
assert (
False
), f"Expected prase result {expected_result[0]} with file {components_config_path} and component {component_name}."

if self.does_link_exist(self._urdf, link_name) != expected_result[1]:
assert (
False
), f"Link name: {link_name}. Expected result {expected_result[1]} with file {components_config_path} and component {component_name}."

if (
self.does_sensor_name_exist(self._urdf, sensor_link_name, sensor_name)
!= expected_result[2]
):
assert (
False
), f"Sensor name: {sensor_name}, sensor link name: {sensor_link_name}. Expected result {expected_result[2]} with file {components_config_path} and component {component_name}."


def test_all_good_single_components(tmpdir_factory):
for type_name, value in components_types_with_names.items():
name = value[0]
dir = tmpdir_factory.mktemp(name)
components_config_path = dir.join(name + "_test_components.yaml")

utils = ComponentsYamlParseUtils(str(components_config_path))
components = {
"components": [utils.create_component(type_name, name)],
}

utils.save_yaml(components)

for component in components["components"]:
utils.test_component(component, [True, True, True], str(components_config_path))
Loading

0 comments on commit ca12cba

Please sign in to comment.