diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..ca3cb2f --- /dev/null +++ b/.clang-format @@ -0,0 +1,17 @@ +Language: Cpp +BasedOnStyle: Google + +AccessModifierOffset: -2 +AlignAfterOpenBracket: AlwaysBreak +BraceWrapping: + AfterClass: true + AfterFunction: true + AfterNamespace: true + AfterStruct: true +BreakBeforeBraces: Custom +ColumnLimit: 100 +ConstructorInitializerIndentWidth: 0 +ContinuationIndentWidth: 2 +DerivePointerAlignment: false +PointerAlignment: Middle +ReflowComments: false \ No newline at end of file diff --git a/.github/workflows/build-docker-image.yaml b/.github/workflows/build-docker-image.yaml new file mode 100644 index 0000000..f23c190 --- /dev/null +++ b/.github/workflows/build-docker-image.yaml @@ -0,0 +1,55 @@ +name: Build/Publish Docker Image + +on: + push: + branches: + - 'ros2' + pull_request: + types: + - 'closed' + workflow_dispatch: + +jobs: + build: + runs-on: ubuntu-20.04 + strategy: + fail-fast: false + matrix: + include: + - ros-distro: galactic + platforms: "linux/amd64, linux/arm64" + - ros-distro: humble + platforms: "linux/amd64, linux/arm64" + + steps: + + - name: Checkout + uses: actions/checkout@v2 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v1 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v1 + with: + version: latest + + - name: Login to Docker Registry + uses: docker/login-action@v1 + with: + registry: docker.io + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Build and push + uses: docker/build-push-action@v2 + with: + context: . + file: ./Dockerfile + platforms: ${{ matrix.platforms }} + push: true + build-args: | + ROS_DISTRO=${{ matrix.ros-distro }} + tags: husarion/joy2twist:${{ matrix.ros-distro }} + cache-from: type=registry,ref=husarion/joy2twist:${{ matrix.ros-distro }} + cache-to: type=inline diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9529abf --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.vscode/** +demo/.env diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..2dbc18c --- /dev/null +++ b/Dockerfile @@ -0,0 +1,23 @@ +ARG ROS_DISTRO=galactic + +FROM ros:$ROS_DISTRO-ros-base + +# Use bash instead of sh +SHELL ["/bin/bash", "-c"] + +WORKDIR /ros2_ws + +COPY ./joy2twist ./src/joy2twist + +# Update Ubuntu Software repository and initialise ROS workspace +RUN apt update && \ + source /opt/ros/$ROS_DISTRO/setup.bash && \ + rosdep update --rosdistro $ROS_DISTRO && \ + rosdep install -i --from-path src --rosdistro $ROS_DISTRO -y && \ + colcon build --symlink-install && \ + apt autoremove -y && \ + apt clean && \ + rm -rf /var/lib/apt/lists/* + +COPY ./ros_entrypoint.sh / +ENTRYPOINT ["/ros_entrypoint.sh"] diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..4318721 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 Husarion + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..c758eb5 --- /dev/null +++ b/README.md @@ -0,0 +1,97 @@ +# Joy2Twist + +Dockerized ROS node allowing control of ROS-powered mobile robots with Logitech F710 gamepad. Joy2Twist node is converting `sensor_msgs/Joy` message to `geometry_msgs/Twist` in order to provide velocity commands for the mobile robot. Therefore this package is compliant (but not supported by Husarion) with any other gamepad controller which is able to publish the `sensor_msgs/Joy` message. + +## Setup joy + +Connect joy via nano USB receiver and make sure it is in **DirectInput Mode** (switch in front o the pad with letters **D** and **X**, select **D**). + +To test if joy works, use `jstest /dev/input/js0`. +If the output is: + +``` +jstest: No such file or directory +``` + +See `ls /dev/input | grep js` and find your joy number. If it differs, apply changes in *compose.yaml* and launch file. + +## Button mapping + +| Button | Function | +|:--------:|:------------------:| +| `LB` | enable driving | +| `RB` | slow driving mode | +| `RT` | fast driving mode | + +If neither `RB` nor `RT` is pressed, the robot operates in *regular* driving mode. + +To drive robot use sticks. +By default, linear `X` and `Y` are held by the right stick. Angular `Z` is controlled with the left stick. + +--- +## ROS node API + +ROS node is translating `/joy` topic to `/cmd_vel` topic. + + +### Publish + +- `/cmd_vel` *(geometry_msgs/Twist)* + +### Subscribe + +- `/joy` *(sensor_msgs/Joy)* + +### Parameters + +Following parameters change joystick axes mapped to given robot axes of freedom. For more information about parameter values, refer to the joy package [wiki page](http://wiki.ros.org/joy#Logitech_Wireless_Gamepad_F710_.28DirectInput_Mode.29). + +- `~axis_linear_x` *(int, default: 3)* +- `~axis_linear_y` *(int, default: 2)* +- `~axis_angular_z` *(int, default: 0)* + +The robot can be operated at 3 scales of speed depending on pressed buttons. It's possible to adjust velocity scaling factors using a [config file](./joy2twist/config/joy2twist.yaml). The Units are m/s for linear movement and rad/s for angular movement. + +- `fast` *(float, default: 1)* +- `regular` *(float, default: 0.5)* +- `slow` *(float, default: 0.2)* + +## Docker image + +[![Build/Publish Docker Image](https://github.com/husarion/joy2twist/actions/workflows/build-docker-image.yaml/badge.svg)](https://github.com/husarion/joy2twist/actions/workflows/build-docker-image.yaml) + +| ROS2 distro | Supported architectures | +| - | - | +| `galactic` | `linux/amd64`, `linux/arm64` | +| `humble` | `linux/amd64`, `linux/arm64` | + +Available on [Docker Hub](https://hub.docker.com/r/husarion/joy2twist/tags) + +### Demo + +#### Controlling ROSbot 2 with a Logitech F710 gamepad + +1. Clone this repo on your ROSbot: + + ```bash + git clone https://github.com/husarion/joy2twist.git + cd joy2twist/ + ``` + +2. Create `demo/.env` based on `demo/.env.template` file and modify it if needed (see comments) + + ```bash + #SBC <> STM32 serial connection. Set: + #SERIAL_PORT=/dev/ttyS1 # ROSbot 2 + #SERIAL_PORT=/dev/ttyS4 # ROSbot 2 PRO + SERIAL_PORT=/dev/ttyAMA0 # ROSbot 2R + ``` + +3. Launch on ROSbot + + Go to the `joy2twist/demo` folder and run: + + ```bash + cd joy2twist/demo + docker compose -f compose.rosbot.yaml up + ``` diff --git a/demo/.env.template b/demo/.env.template new file mode 100644 index 0000000..e1a34b3 --- /dev/null +++ b/demo/.env.template @@ -0,0 +1,4 @@ +# SBC <> STM32 serial connection. Set: +# SERIAL_PORT=/dev/ttyS1 # ROSbot 2 +# SERIAL_PORT=/dev/ttyS4 # ROSbot 2 PRO +SERIAL_PORT=/dev/ttyAMA0 # ROSbbot 2R diff --git a/demo/compose.panther.yaml b/demo/compose.panther.yaml new file mode 100644 index 0000000..b351924 --- /dev/null +++ b/demo/compose.panther.yaml @@ -0,0 +1,30 @@ +services: + + joy2twist: + image: husarion/joy2twist:galactic + restart: unless-stopped + tty: true + network_mode: host + ipc: host + devices: + - /dev/input/js0 + environment: + - ROS_MASTER_URI=http://10.15.20.2:11311 + volumes: + - ../joy2twist/config/joy2twist.yaml:/joy2twist.yaml + command: > + ros2 launch + joy2twist gamepad_controller.launch.py + joy2twist_params_file:=/joy2twist.yaml + + bridge: + image: husarion/ros:galactic-ros1-bridge + restart: unless-stopped + network_mode: host + ipc: host + environment: + - ROS_IP=10.15.20.2 + - ROS_MASTER_URI=http://10.15.20.2:11311 + command: > + ros2 run + ros1_bridge dynamic_bridge diff --git a/demo/compose.rosbot.yaml b/demo/compose.rosbot.yaml new file mode 100644 index 0000000..b0b4822 --- /dev/null +++ b/demo/compose.rosbot.yaml @@ -0,0 +1,43 @@ +services: + + joy2twist: + image: husarion/joy2twist:galactic + restart: unless-stopped + tty: true + devices: + - /dev/input/js0 + environment: + - ROS_MASTER_URI=http://ros-master:11311 + volumes: + - ../joy2twist/config/joy2twist.yaml:/joy2twist.yaml + command: > + ros2 launch + joy2twist gamepad_controller.launch.py + joy2twist_params_file:=/joy2twist.yaml + + bridge: + image: husarion/ros:galactic-ros1-bridge + restart: unless-stopped + environment: + - ROS_MASTER_URI=http://ros-master:11311 + command: > + ros2 run + ros1_bridge dynamic_bridge + + ros-master: + image: ros:melodic-ros-core + restart: unless-stopped + command: stdbuf -o L roscore + + rosbot: + image: husarion/rosbot:melodic + restart: unless-stopped + tty: true + devices: + - ${SERIAL_PORT} + environment: + - SERIAL_PORT + - ROS_MASTER_URI=http://ros-master:11311 + command: > + roslaunch --wait + rosbot_bringup rosbot_docker.launch diff --git a/joy2twist/CMakeLists.txt b/joy2twist/CMakeLists.txt new file mode 100644 index 0000000..e2218e2 --- /dev/null +++ b/joy2twist/CMakeLists.txt @@ -0,0 +1,54 @@ +cmake_minimum_required(VERSION 3.5) +project(joy2twist) + +if (NOT CMAKE_CXX_STANDARD) + set(CMAKE_CXX_STANDARD 17) +endif () + +if (CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") + add_compile_options(-Wall -Wextra -Wpedantic) +endif () + +set(GCC_COVERAGE_COMPILE_FLAGS "-pthread -DBOOST_ERROR_CODE_HEADER_ONLY -BOOST_SYSTEM_NO_DEPRECATED") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${GCC_COVERAGE_COMPILE_FLAGS}") + +find_package(ament_cmake REQUIRED) +find_package(ament_index_cpp REQUIRED) +find_package(rclcpp REQUIRED) +find_package(geometry_msgs REQUIRED) +find_package(sensor_msgs REQUIRED) +find_package(joy_linux REQUIRED) +include_directories(include) + +add_executable( + joy2twist + src/main.cpp + src/joy2twist_node.cpp +) + +ament_target_dependencies(joy2twist + ament_cmake + ament_index_cpp + rclcpp + geometry_msgs + sensor_msgs + joy_linux +) + +install(TARGETS joy2twist + DESTINATION lib/${PROJECT_NAME} +) + +install(DIRECTORY + launch + config + DESTINATION share/${PROJECT_NAME}/ +) + +if (BUILD_TESTING) + find_package(ament_lint_auto REQUIRED) + ament_lint_auto_find_test_dependencies() +endif() + +ament_export_include_directories(include) +ament_package() diff --git a/joy2twist/config/joy2twist.yaml b/joy2twist/config/joy2twist.yaml new file mode 100644 index 0000000..83e821c --- /dev/null +++ b/joy2twist/config/joy2twist.yaml @@ -0,0 +1,22 @@ +/**: + ros__parameters: + linear_velocity_factor: + fast: 1.0 + regular: 0.5 + slow: 0.2 + + angular_velocity_factor: + fast: 1.0 + regular: 0.5 + slow: 0.2 + + # This button mapping should be adjusted to the specific controller + # The following map is suited for Logitech F710 + button_index_map: + axis: + angular_z: 0 # Left joystick + linear_x: 3 # Right joystick + linear_y: 2 # Right joystick + dead_man_switch: 4 # LB + fast_mode: 7 # RT + slow_mode: 5 # RB diff --git a/joy2twist/include/joy2twist/joy2twist_node.hpp b/joy2twist/include/joy2twist/joy2twist_node.hpp new file mode 100644 index 0000000..631410d --- /dev/null +++ b/joy2twist/include/joy2twist/joy2twist_node.hpp @@ -0,0 +1,56 @@ +#ifndef JOY2TWIST_JOY2TWIST_NODE_HPP_ +#define JOY2TWIST_JOY2TWIST_NODE_HPP_ + +#include +#include +#include + +#include + +#include +#include + +namespace joy2twist +{ +using MsgJoy = sensor_msgs::msg::Joy; +using MsgTwist = geometry_msgs::msg::Twist; + +struct ButtonIndex +{ + int angular_z; + int linear_x; + int linear_y; + + int dead_man_switch; + int fast_mode; + int slow_mode; +}; + +class Joy2TwistNode : public rclcpp::Node +{ +public: + Joy2TwistNode(); + +private: + void declare_parameters(); + void load_parameters(); + + void joy_cb(const std::shared_ptr joy_msg); + void convert_joy_to_twist(const std::shared_ptr joy_msg, MsgTwist & twist_msg); + std::pair determine_velocity_factor(const std::shared_ptr joy_msg); + + std::map linear_velocity_factors_; + std::map angular_velocity_factors_; + + ButtonIndex button_index_; + + rclcpp::Subscription::SharedPtr joy_sub_; + rclcpp::Publisher::SharedPtr twist_pub_; +}; + +static constexpr char FAST[]{"fast"}; +static constexpr char REGULAR[]{"regular"}; +static constexpr char SLOW[]{"slow"}; +} // namespace joy2twist + +#endif // JOY2TWIST_JOY2TWIST_NODE_HPP_ diff --git a/joy2twist/launch/gamepad_controller.launch.py b/joy2twist/launch/gamepad_controller.launch.py new file mode 100644 index 0000000..401d22c --- /dev/null +++ b/joy2twist/launch/gamepad_controller.launch.py @@ -0,0 +1,49 @@ +from launch_ros.actions import Node +from launch_ros.substitutions import FindPackageShare + +from launch import LaunchDescription +from launch.actions import DeclareLaunchArgument, IncludeLaunchDescription +from launch.launch_description_sources import PythonLaunchDescriptionSource +from launch.substitutions import LaunchConfiguration, PathJoinSubstitution + + +def generate_launch_description(): + + joy2twist_cfg_path = PathJoinSubstitution( + [FindPackageShare("joy2twist"), "config", "joy2twist.yaml"] + ) + + joy2twist_params_file_argument = DeclareLaunchArgument( + "joy2twist_params_file", + default_value=joy2twist_cfg_path, + description="ROS2 parameters file to use with joy2twist node", + ) + + joy2twist_launch = create_include_launch( + package="joy2twist", + rel_launch_path="launch/joy2twist.launch.py", + arguments={ + "joy2twist_params_file": LaunchConfiguration("joy2twist_params_file") + }, + ) + + joy_linux_node = Node( + package="joy_linux", + executable="joy_linux_node", + output={"stdout": "screen", "stderr": "screen"}, + emulate_tty="true", + ) + + actions = [joy2twist_params_file_argument, joy2twist_launch, joy_linux_node] + + return LaunchDescription(actions) + + +def create_include_launch(package: str, rel_launch_path: str, arguments: dict): + included_launch = IncludeLaunchDescription( + PythonLaunchDescriptionSource( + [PathJoinSubstitution([FindPackageShare(package), rel_launch_path])] + ), + launch_arguments=arguments.items(), + ) + return included_launch diff --git a/joy2twist/launch/joy2twist.launch.py b/joy2twist/launch/joy2twist.launch.py new file mode 100644 index 0000000..4057264 --- /dev/null +++ b/joy2twist/launch/joy2twist.launch.py @@ -0,0 +1,31 @@ +from launch_ros.actions import Node +from launch_ros.substitutions import FindPackageShare + +from launch import LaunchDescription +from launch.actions import DeclareLaunchArgument +from launch.substitutions import LaunchConfiguration, PathJoinSubstitution + + +def generate_launch_description(): + + joy2twist_cfg_path = PathJoinSubstitution( + [FindPackageShare("joy2twist"), "config", "joy2twist.yaml"] + ) + + joy2twist_params_file_argument = DeclareLaunchArgument( + "joy2twist_params_file", + default_value=joy2twist_cfg_path, + description="ROS2 parameters file to use with joy1twist node", + ) + + joy2twist_node = Node( + package="joy2twist", + executable="joy2twist", + parameters=[LaunchConfiguration("joy2twist_params_file")], + output={"stdout": "screen", "stderr": "screen"}, + emulate_tty="true", + ) + + actions = [joy2twist_params_file_argument, joy2twist_node] + + return LaunchDescription(actions) diff --git a/joy2twist/package.xml b/joy2twist/package.xml new file mode 100644 index 0000000..843682a --- /dev/null +++ b/joy2twist/package.xml @@ -0,0 +1,25 @@ + + + + joy2twist + 1.0.0 + The joy2twist package + Paweł Irzyk + MIT + https://github.com/husarion/joy2twist + Paweł Irzyk + + ament_cmake + + ament_index_cpp + rclcpp + geometry_msgs + sensor_msgs + joy_linux + + ament_cmake_gtest + + + ament_cmake + + diff --git a/joy2twist/src/joy2twist_node.cpp b/joy2twist/src/joy2twist_node.cpp new file mode 100644 index 0000000..2fe2f43 --- /dev/null +++ b/joy2twist/src/joy2twist_node.cpp @@ -0,0 +1,91 @@ +#include "joy2twist/joy2twist_node.hpp" + +namespace joy2twist +{ +Joy2TwistNode::Joy2TwistNode() : Node("joy2twist_node") +{ + using namespace std::placeholders; + + declare_parameters(); + load_parameters(); + + auto sensor_qos = rclcpp::QoS( + rclcpp::QoSInitialization( + rmw_qos_profile_sensor_data.history, rmw_qos_profile_sensor_data.depth), + rmw_qos_profile_sensor_data); + + joy_sub_ = + create_subscription("joy", sensor_qos, std::bind(&Joy2TwistNode::joy_cb, this, _1)); + twist_pub_ = create_publisher("cmd_vel", sensor_qos); + + RCLCPP_INFO(get_logger(), "Initialized node!"); +} + +void Joy2TwistNode::declare_parameters() +{ + this->declare_parameter("linear_velocity_factor.fast", 1.0); + this->declare_parameter("linear_velocity_factor.regular", 0.5); + this->declare_parameter("linear_velocity_factor.slow", 0.2); + this->declare_parameter("angular_velocity_factor.fast", 1.0); + this->declare_parameter("angular_velocity_factor.regular", 0.5); + this->declare_parameter("angular_velocity_factor.slow", 0.2); + this->declare_parameter("button_index_map.axis.angular_z", 0); + this->declare_parameter("button_index_map.axis.linear_x", 3); + this->declare_parameter("button_index_map.axis.linear_y", 2); + this->declare_parameter("button_index_map.dead_man_switch", 4); + this->declare_parameter("button_index_map.fast_mode", 7); + this->declare_parameter("button_index_map.slow_mode", 5); +} + +void Joy2TwistNode::load_parameters() +{ + this->get_parameter("linear_velocity_factor.fast", linear_velocity_factors_[FAST]); + this->get_parameter("linear_velocity_factor.regular", linear_velocity_factors_[REGULAR]); + this->get_parameter("linear_velocity_factor.slow", linear_velocity_factors_[SLOW]); + this->get_parameter("angular_velocity_factor.fast", angular_velocity_factors_[FAST]); + this->get_parameter("angular_velocity_factor.regular", angular_velocity_factors_[REGULAR]); + this->get_parameter("angular_velocity_factor.slow", angular_velocity_factors_[SLOW]); + this->get_parameter("button_index_map.axis.angular_z", button_index_.angular_z); + this->get_parameter("button_index_map.axis.linear_x", button_index_.linear_x); + this->get_parameter("button_index_map.axis.linear_y", button_index_.linear_y); + this->get_parameter("button_index_map.dead_man_switch", button_index_.dead_man_switch); + this->get_parameter("button_index_map.fast_mode", button_index_.fast_mode); + this->get_parameter("button_index_map.slow_mode", button_index_.slow_mode); +} + +void Joy2TwistNode::joy_cb(const MsgJoy::SharedPtr joy_msg) +{ + MsgTwist twist_msg; + if (joy_msg->buttons.at(button_index_.dead_man_switch)) { + convert_joy_to_twist(joy_msg, twist_msg); + } + twist_pub_->publish(twist_msg); +} + +void Joy2TwistNode::convert_joy_to_twist(const MsgJoy::SharedPtr joy_msg, MsgTwist & twist_msg) +{ + float linear_velocity_factor{}, angular_velocity_factor{}; + std::tie(linear_velocity_factor, angular_velocity_factor) = determine_velocity_factor(joy_msg); + + twist_msg.angular.z = angular_velocity_factor * joy_msg->axes.at(button_index_.angular_z); + twist_msg.linear.x = linear_velocity_factor * joy_msg->axes.at(button_index_.linear_x); + twist_msg.linear.y = linear_velocity_factor * joy_msg->axes.at(button_index_.linear_y); +} + +std::pair Joy2TwistNode::determine_velocity_factor(const MsgJoy::SharedPtr joy_msg) +{ + float linear_velocity_factor = linear_velocity_factors_.at(REGULAR); + float angular_velocity_factor = angular_velocity_factors_.at(REGULAR); + if ( + joy_msg->buttons.at(button_index_.slow_mode) && !joy_msg->buttons.at(button_index_.fast_mode)) { + linear_velocity_factor = linear_velocity_factors_.at(SLOW); + angular_velocity_factor = angular_velocity_factors_.at(SLOW); + } else if ( + joy_msg->buttons.at(button_index_.fast_mode) && !joy_msg->buttons.at(button_index_.slow_mode)) { + linear_velocity_factor = linear_velocity_factors_.at(FAST); + angular_velocity_factor = angular_velocity_factors_.at(FAST); + } + return std::make_pair(linear_velocity_factor, angular_velocity_factor); +} + +} // namespace joy2twist diff --git a/joy2twist/src/main.cpp b/joy2twist/src/main.cpp new file mode 100644 index 0000000..b8533a1 --- /dev/null +++ b/joy2twist/src/main.cpp @@ -0,0 +1,24 @@ +#include + +#include "joy2twist/joy2twist_node.hpp" + +using namespace joy2twist; + +int main(int argc, char * argv[]) +{ + rclcpp::init(argc, argv); + rclcpp::executors::SingleThreadedExecutor executor; + + auto joy2twist_node = std::make_shared(); + executor.add_node(joy2twist_node); + + try { + executor.spin(); + } catch (const std::runtime_error & err) { + std::cerr << "[/joy2twist_node] Caught exception: " << err.what() << std::endl; + } + + std::cout << "[/joy2twist_node] Shutting down" << std::endl; + rclcpp::shutdown(); + return 0; +} diff --git a/ros_entrypoint.sh b/ros_entrypoint.sh new file mode 100755 index 0000000..be57f7a --- /dev/null +++ b/ros_entrypoint.sh @@ -0,0 +1,8 @@ +#!/bin/bash +set -e + +# setup ros environment +source "/opt/ros/$ROS_DISTRO/setup.bash" +source "/ros2_ws/install/setup.bash" + +exec "$@"