diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..9e92cf7 --- /dev/null +++ b/.clang-format @@ -0,0 +1,14 @@ +--- +Language: Cpp +BasedOnStyle: Google + +ColumnLimit: 100 +AccessModifierOffset: -2 +AlignAfterOpenBracket: AlwaysBreak +BreakBeforeBraces: Allman +ConstructorInitializerIndentWidth: 0 +ContinuationIndentWidth: 2 +DerivePointerAlignment: false +PointerAlignment: Middle +ReflowComments: true +IncludeBlocks: Preserve diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cd704eb..7786201 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,26 +1,35 @@ -name: CI +name: interactive_marker_twist_server_ci on: push: - branches: - - humble-devel pull_request: - branches: - - humble-devel + schedule: + - cron: "0 0 * * *" # every day at midnight jobs: humble_ci: - name: Humble CI Job + name: Humble OSRF Industrial + strategy: + matrix: + env: + - {ROS_REPO: testing, ROS_DISTRO: humble} + - {ROS_REPO: main, ROS_DISTRO: humble} + fail-fast: false runs-on: ubuntu-22.04 steps: - - name: Clone Repository - uses: actions/checkout@v3 - - name: Setup ROS - uses: ros-tooling/setup-ros@v0.3 - with: - required-ros-distributions: humble - - name: Build and Test - uses: ros-tooling/action-ros-ci@v0.2 - with: - package-name: interactive_marker_twist_server - target-ros2-distro: humble + - uses: actions/checkout@v3 + - uses: 'ros-industrial/industrial_ci@master' + env: ${{matrix.env}} + jazzy_ci: + name: Jazzy OSRF Industrial + strategy: + matrix: + env: + - {ROS_REPO: testing, ROS_DISTRO: jazzy} + - {ROS_REPO: main, ROS_DISTRO: jazzy} + fail-fast: false + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v3 + - uses: 'ros-industrial/industrial_ci@master' + env: ${{matrix.env}} \ No newline at end of file diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..d10126d --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,143 @@ + +# To use: +# +# pre-commit run -a +# +# Or: +# +# pre-commit install # (runs every time you commit in git) +# +# To update this file: +# +# pre-commit autoupdate +# +# See https://github.com/pre-commit/pre-commit + +repos: + # Standard hooks + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v5.0.0 + hooks: + - id: check-added-large-files + - id: check-ast + - id: check-case-conflict + - id: check-docstring-first + - id: check-merge-conflict + - id: check-symlinks + - id: check-xml + - id: check-yaml + args: ["--allow-multiple-documents"] + - id: debug-statements + - id: end-of-file-fixer + - id: mixed-line-ending + - id: trailing-whitespace + exclude_types: [rst] + - id: fix-byte-order-marker + + + # Python hooks + - repo: https://github.com/asottile/pyupgrade + rev: v3.19.0 + hooks: + - id: pyupgrade + args: [--py36-plus] + + # PyDocStyle + - repo: https://github.com/PyCQA/pydocstyle + rev: 6.3.0 + hooks: + - id: pydocstyle + args: ["--ignore=D100,D101,D102,D103,D104,D105,D106,D107,D203,D212,D404"] + + - repo: https://github.com/psf/black + rev: 24.10.0 + hooks: + - id: black + args: ["--line-length=99"] + + - repo: https://github.com/pycqa/flake8 + rev: 7.1.1 + hooks: + - id: flake8 + args: ["--extend-ignore=E501"] + + # CPP hooks + - repo: https://github.com/pre-commit/mirrors-clang-format + rev: v19.1.3 + hooks: + - id: clang-format + args: ['-fallback-style=none', '-i'] + + - repo: local + hooks: + - id: ament_cppcheck + name: ament_cppcheck + description: Static code analysis of C/C++ files. + entry: env AMENT_CPPCHECK_ALLOW_SLOW_VERSIONS=1 ament_cppcheck + language: system + files: \.(h\+\+|h|hh|hxx|hpp|cuh|c|cc|cpp|cu|c\+\+|cxx|tpp|txx)$ + + - repo: local + hooks: + - id: ament_cpplint + name: ament_cpplint + description: Static code analysis of C/C++ files. + entry: ament_cpplint + language: system + files: \.(h\+\+|h|hh|hxx|hpp|cuh|c|cc|cpp|cu|c\+\+|cxx|tpp|txx)$ + args: ["--linelength=100", "--filter=-whitespace/newline"] + + # Cmake hooks + - repo: local + hooks: + - id: ament_lint_cmake + name: ament_lint_cmake + description: Check format of CMakeLists.txt files. + entry: ament_lint_cmake + language: system + files: CMakeLists\.txt$ + + # Copyright + - repo: local + hooks: + - id: ament_copyright + name: ament_copyright + description: Check if copyright notice is available in all files. + entry: ament_copyright + language: system + exclude: .*/conf\.py$ + + # Docs - RestructuredText hooks + - repo: https://github.com/PyCQA/doc8 + rev: v1.1.2 + hooks: + - id: doc8 + args: ['--max-line-length=100', '--ignore=D001'] + exclude: CHANGELOG\.rst$ + + - repo: https://github.com/pre-commit/pygrep-hooks + rev: v1.10.0 + hooks: + - id: rst-backticks + exclude: CHANGELOG\.rst$ + - id: rst-directive-colons + - id: rst-inline-touching-normal + + # Spellcheck in comments and docs + # skipping of *.svg files is not working... + - repo: https://github.com/codespell-project/codespell + rev: v2.3.0 + hooks: + - id: codespell + args: ['--write-changes', '--uri-ignore-words-list=ist', '-L manuel'] + exclude: CHANGELOG\.rst|\.(svg|pyc|drawio)$ + + - repo: https://github.com/python-jsonschema/check-jsonschema + rev: 0.29.4 + hooks: + - id: check-github-workflows + args: ["--verbose"] + - id: check-github-actions + args: ["--verbose"] + - id: check-dependabot + args: ["--verbose"] \ No newline at end of file diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 6190ee0..ac61aa7 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -2,6 +2,13 @@ Changelog for package interactive_marker_twist_server ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +2.1.1 (2024-11-19) +------------------ +* Fixed linting. +* Updated CI for Humble and Jazzy. +* Add the use_stamped_msgs param to allow support for publishing TwistStamped messages instead of Twist +* Contributors: Chris Iverach-Brereton, Tony Baltovski + 2.1.0 (2023-03-06) ------------------ * Updated CI for Humble. diff --git a/CMakeLists.txt b/CMakeLists.txt index 177b717..55010f3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -40,7 +40,13 @@ install(DIRECTORY if(BUILD_TESTING) find_package(ament_lint_auto REQUIRED) + list(APPEND AMENT_LINT_AUTO_EXCLUDE + ament_cmake_cpplint + ament_cmake_uncrustify + ) + set(ament_cmake_clang_format_CONFIG_FILE .clang-format) ament_lint_auto_find_test_dependencies() endif() + ament_package() \ No newline at end of file diff --git a/package.xml b/package.xml index 28c91d1..39ca9ac 100644 --- a/package.xml +++ b/package.xml @@ -1,6 +1,6 @@ interactive_marker_twist_server - 2.1.0 + 2.1.1 Interactive control for generic Twist-based robots using interactive markers Mike Purvis BSD diff --git a/rpm/template.spec.em b/rpm/template.spec.em deleted file mode 100644 index bebb96b..0000000 --- a/rpm/template.spec.em +++ /dev/null @@ -1,88 +0,0 @@ -%bcond_without tests -%bcond_without weak_deps - -%global __os_install_post %(echo '%{__os_install_post}' | sed -e 's!/usr/lib[^[:space:]]*/brp-python-bytecompile[[:space:]].*$!!g') -%global __provides_exclude_from ^@(InstallationPrefix)/.*$ -%global __requires_exclude_from ^@(InstallationPrefix)/.*$ - -Name: @(Package) -Version: @(Version) -Release: @(RPMInc)%{?dist}%{?release_suffix} -Summary: ROS @(Name) package - -License: @(License) -@[if Homepage and Homepage != '']URL: @(Homepage)@\n@[end if]@ -Source0: %{name}-%{version}.tar.gz -@[if NoArch]@\nBuildArch: noarch@\n@[end if]@ - -@[for p in Depends]Requires: @p@\n@[end for]@ -@[for p in BuildDepends]BuildRequires: @p@\n@[end for]@ -@[for p in Conflicts]Conflicts: @p@\n@[end for]@ -@[for p in Replaces]Obsoletes: @p@\n@[end for]@ -@[for p in Provides]Provides: @p@\n@[end for]@ -@[if TestDepends]@\n%if 0%{?with_tests} -@[for p in TestDepends]BuildRequires: @p@\n@[end for]@ -%endif@\n@[end if]@ -@[if Supplements]@\n%if 0%{?with_weak_deps} -@[for p in Supplements]Supplements: @p@\n@[end for]@ -%endif@\n@[end if]@ - -%description -@(Description) - -%prep -%autosetup -p1 - -%build -# In case we're installing to a non-standard location, look for a setup.sh -# in the install tree and source it. It will set things like -# CMAKE_PREFIX_PATH, PKG_CONFIG_PATH, and PYTHONPATH. -if [ -f "@(InstallationPrefix)/setup.sh" ]; then . "@(InstallationPrefix)/setup.sh"; fi -mkdir -p .obj-%{_target_platform} && cd .obj-%{_target_platform} -%cmake3 \ - -UINCLUDE_INSTALL_DIR \ - -ULIB_INSTALL_DIR \ - -USYSCONF_INSTALL_DIR \ - -USHARE_INSTALL_PREFIX \ - -ULIB_SUFFIX \ - -DCMAKE_INSTALL_PREFIX="@(InstallationPrefix)" \ - -DAMENT_PREFIX_PATH="@(InstallationPrefix)" \ - -DCMAKE_PREFIX_PATH="@(InstallationPrefix)" \ - -DSETUPTOOLS_DEB_LAYOUT=OFF \ -%if !0%{?with_tests} - -DBUILD_TESTING=OFF \ -%endif - .. - -%make_build - -%install -# In case we're installing to a non-standard location, look for a setup.sh -# in the install tree and source it. It will set things like -# CMAKE_PREFIX_PATH, PKG_CONFIG_PATH, and PYTHONPATH. -if [ -f "@(InstallationPrefix)/setup.sh" ]; then . "@(InstallationPrefix)/setup.sh"; fi -%make_install -C .obj-%{_target_platform} - -%if 0%{?with_tests} -%check -# Look for a Makefile target with a name indicating that it runs tests -TEST_TARGET=$(%__make -qp -C .obj-%{_target_platform} | sed "s/^\(test\|check\):.*/\\1/;t f;d;:f;q0") -if [ -n "$TEST_TARGET" ]; then -# In case we're installing to a non-standard location, look for a setup.sh -# in the install tree and source it. It will set things like -# CMAKE_PREFIX_PATH, PKG_CONFIG_PATH, and PYTHONPATH. -if [ -f "@(InstallationPrefix)/setup.sh" ]; then . "@(InstallationPrefix)/setup.sh"; fi -CTEST_OUTPUT_ON_FAILURE=1 \ - %make_build -C .obj-%{_target_platform} $TEST_TARGET || echo "RPM TESTS FAILED" -else echo "RPM TESTS SKIPPED"; fi -%endif - -%files -@[for lf in LicenseFiles]%license @lf@\n@[end for]@ -@(InstallationPrefix) - -%changelog@ -@[for change_version, (change_date, main_name, main_email) in changelogs] -* @(change_date) @(main_name) <@(main_email)> - @(change_version) -- Autogenerated by Bloom -@[end for] diff --git a/src/marker_server.cpp b/src/marker_server.cpp index 1435a79..4b37262 100644 --- a/src/marker_server.cpp +++ b/src/marker_server.cpp @@ -30,6 +30,7 @@ #include #include +#include #include #include #include @@ -60,14 +61,18 @@ class TwistServerNode : public rclcpp::Node private: void getParameters(); void createInteractiveMarkers(); + void stampAndPublish(geometry_msgs::msg::Twist &msg); rclcpp::Publisher::SharedPtr vel_pub; + rclcpp::Publisher::SharedPtr vel_stamped_pub; std::unique_ptr server; std::map linear_drive_scale_map; std::map max_positive_linear_velocity_map; std::map max_negative_linear_velocity_map; + bool use_stamped_msgs; + double angular_drive_scale; double max_angular_velocity; double marker_size_scale; @@ -85,7 +90,14 @@ TwistServerNode::TwistServerNode() get_node_topics_interface(), get_node_services_interface())) { getParameters(); - vel_pub = create_publisher("cmd_vel", 1); + if (use_stamped_msgs) + { + vel_stamped_pub = create_publisher("cmd_vel", 1); + } + else + { + vel_pub = create_publisher("cmd_vel", 1); + } createInteractiveMarkers(); RCLCPP_INFO(get_logger(), "[interactive_marker_twist_server] Initialized."); } @@ -94,24 +106,43 @@ void TwistServerNode::getParameters() { rclcpp::Parameter link_name_param; rclcpp::Parameter robot_name_param; + rclcpp::Parameter use_stamped_msgs_param; - if (this->get_parameter("link_name", link_name_param)) { + if (this->get_parameter("link_name", link_name_param)) + { link_name = link_name_param.as_string(); - } else { + } + else + { link_name = "base_link"; } - if (this->get_parameter("robot_name", robot_name_param)) { + if (this->get_parameter("robot_name", robot_name_param)) + { robot_name = robot_name_param.as_string(); - } else { + } + else + { robot_name = "robot"; } + if (this->get_parameter("use_stamped_msgs", use_stamped_msgs_param)) + { + use_stamped_msgs = use_stamped_msgs_param.as_bool(); + } + else + { + use_stamped_msgs = false; + } + // Ensure parameters are loaded correctly, otherwise, manually set values for linear config - if (this->get_parameters("linear_scale", linear_drive_scale_map)) { + if (this->get_parameters("linear_scale", linear_drive_scale_map)) + { this->get_parameters("max_positive_linear_velocity", max_positive_linear_velocity_map); this->get_parameters("max_negative_linear_velocity", max_negative_linear_velocity_map); - } else { + } + else + { linear_drive_scale_map["x"] = 1.0; max_positive_linear_velocity_map["x"] = 1.0; max_negative_linear_velocity_map["x"] = -1.0; @@ -134,7 +165,8 @@ void TwistServerNode::createInteractiveMarkers() control.orientation_mode = visualization_msgs::msg::InteractiveMarkerControl::FIXED; - if (linear_drive_scale_map.find("x") != linear_drive_scale_map.end()) { + if (linear_drive_scale_map.find("x") != linear_drive_scale_map.end()) + { control.orientation.w = 1; control.orientation.x = 1; control.orientation.y = 0; @@ -144,7 +176,8 @@ void TwistServerNode::createInteractiveMarkers() interactive_marker.controls.push_back(control); } - if (linear_drive_scale_map.find("y") != linear_drive_scale_map.end()) { + if (linear_drive_scale_map.find("y") != linear_drive_scale_map.end()) + { control.orientation.w = 1; control.orientation.x = 0; control.orientation.y = 0; @@ -154,7 +187,8 @@ void TwistServerNode::createInteractiveMarkers() interactive_marker.controls.push_back(control); } - if (linear_drive_scale_map.find("z") != linear_drive_scale_map.end()) { + if (linear_drive_scale_map.find("z") != linear_drive_scale_map.end()) + { control.orientation.w = 1; control.orientation.x = 0; control.orientation.y = 1; @@ -192,31 +226,51 @@ void TwistServerNode::processFeedback( vel_msg.angular.z = std::min(vel_msg.angular.z, max_angular_velocity); vel_msg.angular.z = std::max(vel_msg.angular.z, -max_angular_velocity); - if (linear_drive_scale_map.find("x") != linear_drive_scale_map.end()) { + if (linear_drive_scale_map.find("x") != linear_drive_scale_map.end()) + { vel_msg.linear.x = linear_drive_scale_map["x"] * feedback->pose.position.x; vel_msg.linear.x = std::min(vel_msg.linear.x, max_positive_linear_velocity_map["x"]); vel_msg.linear.x = std::max(vel_msg.linear.x, max_negative_linear_velocity_map["x"]); } - if (linear_drive_scale_map.find("y") != linear_drive_scale_map.end()) { + if (linear_drive_scale_map.find("y") != linear_drive_scale_map.end()) + { vel_msg.linear.y = linear_drive_scale_map["y"] * feedback->pose.position.y; vel_msg.linear.y = std::min(vel_msg.linear.y, max_positive_linear_velocity_map["y"]); vel_msg.linear.y = std::max(vel_msg.linear.y, max_negative_linear_velocity_map["y"]); } - if (linear_drive_scale_map.find("z") != linear_drive_scale_map.end()) { + if (linear_drive_scale_map.find("z") != linear_drive_scale_map.end()) + { vel_msg.linear.z = linear_drive_scale_map["z"] * feedback->pose.position.z; vel_msg.linear.z = std::min(vel_msg.linear.z, max_positive_linear_velocity_map["z"]); vel_msg.linear.z = std::max(vel_msg.linear.z, max_negative_linear_velocity_map["z"]); } - vel_pub->publish(vel_msg); + if (use_stamped_msgs) + { + stampAndPublish(vel_msg); + } + else + { + vel_pub->publish(vel_msg); + } // Make the marker snap back to robot server->setPose(robot_name + "_twist_marker", geometry_msgs::msg::Pose()); server->applyChanges(); } +void TwistServerNode::stampAndPublish(geometry_msgs::msg::Twist &msg) +{ + geometry_msgs::msg::TwistStamped stamped_msg; + + stamped_msg.twist = msg; + stamped_msg.header.stamp = this->get_clock()->now(); + + vel_stamped_pub->publish(stamped_msg); +} + } // namespace interactive_marker_twist_server int main(int argc, char ** argv)