diff --git a/ros1_bridge_ws/README.md b/ros1_bridge_ws/README.md index 4d79f5ec..47b1b953 100644 --- a/ros1_bridge_ws/README.md +++ b/ros1_bridge_ws/README.md @@ -4,7 +4,7 @@ This workspace is utilized to create a bridge between ROS1 and ROS2-humble. # ◻️ Introduction ◻️ -ros1_bridge provides a network bridge that enables the exchange of messages between ROS 1 and ROS 2. +`ros1_bridge` provides a network bridge that enables the exchange of messages between ROS 1 and ROS 2. You can locate the original repository [here](https://github.com/ros2/ros1_bridge). Within this workspace, you'll find a Dockerfile specifically crafted to build both ros-humble and ros1_bridge from their source code. @@ -29,6 +29,8 @@ ros1_bridge_ws ├── .devcontainer | └── devcontainer.json ├── docker +| ├── .dockerignore +| ├── .env | ├── compose.yaml | ├── compose.debug.yaml | ├── Dockerfile @@ -38,17 +40,14 @@ ros1_bridge_ws # 🚩 How to use 🚩 -> There are two services in the docker compose, one is `ros1-bridge`, and the other one is `ros1-bridge-build`. -> `ros1-bridge` should be sufficient for normal usage. -> `ros1-bridge-build`, which contains all the necessary build tools, is used for debugging purposes. +> The docker compose includes two services: `ros1-bridge` and `ros1-bridge-build`. The `ros1-bridge` service is typically sufficient for regular use, while `ros1-bridge-build` is intended for debugging, as it retains all the necessary build tools in the docker image. > -> If you are not debugging ros1-bridge, it is recommended to use the terminal rather than VScode-devcontainer. +> If you are not debugging `ros1-bridge`, it is recommended to use the terminal rather than VScode-devcontainer. > By default, the VScode-devcontainer uses the `ros1-bridge-build` service. -## 1. Get the docker image +## 1. Build the docker image -While building the image directly from the Dockerfile is possible, it may not be the most efficient choice. -To save time, you can pull the image from Dockerhub instead of compiling it from the source: +While building the image directly from the Dockerfile is possible, it may not be the most efficient choice. To save time, you can pull the image from Dockerhub instead of compiling it from the source. If you still prefer to build the image yourself, please follow the instructions below: @@ -57,9 +56,15 @@ If you still prefer to build the image yourself, please follow the instructions - Terminal user - Open a terminal, change the directory to the docker folder, and type `docker compose build`. -> Just so you know, the building process may take approximately 1 hour to complete. +> Please note that the build process may take approximately 1 hour to complete, with potential delays depending on your computer's performance and network conditions. -## 2. Start the container +## 2. Adjust the parameters in the `.env` file + +We've placed all ROS-related parameters in the `.env` file. Please adjust these parameters according to your environment. By default, we set the `ROS1` master at `127.0.0.1`, and the `ROS2` domain ID to `0`. These settings should work for most scenarios. + +Please note that if these parameters are not configured correctly, the `ros1_bridge` will not function properly! + +## 3. Start the container - VScode user - If you build the image through the devcontainer, it will automatically start the container. @@ -67,122 +72,87 @@ If you still prefer to build the image yourself, please follow the instructions - Terminal user - Open a terminal, change the directory to the docker folder, and type `docker compose up`. -## 3. Launch rosmaster in ROS1 +## 4. Launch rosmaster in ROS1 + +> This step is automatically executed when you run `docker compose up`. As mentioned in https://github.com/ros2/ros1_bridge/issues/391, you should avoid using `roscore` in ROS1 to prevent the issue of not bridging `/rosout`. Instead, use `rosmaster --core` as an alternative. -This command is automatically executed when you run `docker compose up`. - -## 4. Begin communication +## 5. Begin communication You have successfully executed all the instructions. Now, you can proceed to initiate communication between ROS1 and ROS2-humble. -Please keep in mind that the bridge will be established only when -there are matching publisher-subscriber pairs active for a topic on either side of the bridge. +Please keep in mind that the bridge will be established only when there are matching publisher-subscriber pairs active for a topic on either side of the bridge. # ✨ Example ✨ ## Run the bridge and the example talker and listener -> Before beginning the example, ensure you have three containers ready: -> -> - `ros1_bridge` -> - `ROS1` -> - `ROS2` -> -> All containers are automatically launched when you run `docker compose up`. -> -> Furthermore, ensure that you mount `/dev/shm` into both the `ros1_bridge` and `ROS2` containers, -> and that all containers share the host network. +Before beginning the example, ensure you have four containers ready: + +- `ros-core` +- `ros2-ros1-bridge-ws` +- `ros1` +- `ros2` -### 1. Start the rosmaster in `ROS1` container +> When using `ros1-bridge` in your application scenarios, you only need the `ros-core` and `ros2-ros1-bridge-ws` containers. Please replace the `ros1` and `ros2` containers with your application containers, as those are only included for demonstration purposes and are not required for using `ros1-bridge`. +> +> Furthermore, ensure that you mount `/dev/shm` into both the `ros2-ros1-bridge-ws` and `ros2` containers, and that all containers share the host network. -> This container is automatically launched when you run `docker compose up`. No further action is required. +### 1. Start the `ros1_bridge` and other container ```bash -# In ROS 1 container -rosmaster --core & +# In docker folder +docker compose up ``` -### 2. Start the `ros1_bridge` container - -> This container is automatically launched when you run `docker compose up`. No further action is required. - -(Same as [here](#2-start-the-container)) - -- VScode user - - After getting into the container, type `./start-bridge.sh` in the terminal. -- Terminal user - - ```bash - # In docker folder - docker compose run ros1-bridge - ``` +This command will start the four containers mentioned above. -### 3. Run the talker and listener node +### 2. Run the talker and listener node -> These containers are automatically launched when you run `docker compose up`. You only need to execeute the commands below. +We run the listener node in the `ros1` container and the talker node in the `ros2` container. You can run the talker node in `ros1` and the listener node in `ros2` if you'd like. To achieve this, modify the command provided below. -Run the listener node in the `ROS1` container and the talker node in the `ROS2` container. +#### ROS1 ```bash docker exec -it ros1 /ros_entrypoint.sh bash -# In ROS 1 container +# Inside ros1 container rosrun roscpp_tutorials listener # or # rosrun roscpp_tutorials talker ``` +#### ROS2 + ```bash docker exec -it ros2 /ros_entrypoint.sh bash -# In ROS 2 container -# Use the same UID as ros1_bridge to prevent Fast-DDS Shared Memory permission issues. +# Inside ros2 container +# Use the same UID as ros1_bridge to prevent Fast-DDS shared memory permission issues. # Ref: https://github.com/j3soon/ros2-essentials/pull/9#issuecomment-1795743063 useradd -ms /bin/bash user su user -source /ros_entrypoint.sh +source /opt/ros/humble/setup.bash ros2 run demo_nodes_cpp talker # or # ros2 run demo_nodes_cpp listener ``` -> You can run the talker node in `ROS1` and the listener node in `ROS2` if you'd like. -> To achieve this, simply modify the command provided above. - -Certainly, you can try the [example](https://github.com/ros2/ros1_bridge#example-1-run-the-bridge-and-the-example-talker-and-listener) provided by `ros1_bridge`. -However, there's no need to source the setup script within the `ros1_bridge` container, -simply starting the container will suffice. +Certainly, you can try the [example](https://github.com/ros2/ros1_bridge#example-1-run-the-bridge-and-the-example-talker-and-listener) provided by `ros1_bridge`. However, there's no need to source the setup script within the `ros2-ros1-bridge-ws` container, simply starting the container will suffice. # 🔍 Troubleshooting 🔍 -> If you are trying to debug ros1_bridge, it is recommended to use the `ros1-bridge-build` service in docker compose. -> It contains all the necessary build tools, which should be helpful for you. - -## Failed to contact master - -Before launching the master, make sure you have set the `ROS_MASTER_URI`. -Execute the following command to configure it. +> If you are trying to debug `ros1_bridge`, it is recommended to use the `ros1-bridge-build` service in docker compose. It contains all the necessary build tools, which should be helpful for you. -```bash -export ROS_MASTER_URI=http://localhost:11311 -``` - -You can replace localhost with the actual IP address or hostname of your ROS Master. -This command ensures that your ROS nodes know where to find the ROS Master for communication. +## Failed to contact ros master -If you've modified the default URI, please execute the `start-bridge.sh` script with the new URI address to ensure proper configuration and communication. +Before launching `ros-core`, make sure to adjust the `ROS_MASTER_URI` correctly. For more information, please check the `.env` file and [this section](#2-adjust-the-parameters-in-the-env-file). -For example, if you've changed the default URI to `127.0.0.1`, you should make the following modification to the `compose.yaml`: - -```bash -command: ./start-bridge.sh http://127.0.0.1:11311 -``` +You can replace `127.0.0.1` with the actual IP address or hostname of your ros master. This configuration ensures that your ros nodes know where to find the ros master for communication. Remember, in addition to modifying the parameters for `ros1_bridge`, you also need to adjust the parameters for your own container! ## ROS2 can't receive the topic -The latest releases of Fast-DDS come with the SharedMemory transport enabled by default. -Therefore, you need to mount shared memory, also known as `/dev/shm`, into every container you intend to communicate with when using Fast-DDS. This ensures proper communication between containers. -Ensure that you use the same UID as `ros1-bridge` to avoid Fast-DDS shared memory permission issues. +The latest releases of Fast-DDS come with the shared memory transport enabled by default. Therefore, you need to mount shared memory, also known as `/dev/shm`, into every container you intend to communicate with when using Fast-DDS. This ensures proper communication between containers. Ensure that you use the same UID as `ros2-ros1-bridge-ws` to avoid Fast-DDS shared memory permission issues. Reference: https://github.com/eProsima/Fast-DDS/issues/1698#issuecomment-778039676 diff --git a/ros1_bridge_ws/docker/.dockerignore b/ros1_bridge_ws/docker/.dockerignore index 750062cf..b8262b83 100644 --- a/ros1_bridge_ws/docker/.dockerignore +++ b/ros1_bridge_ws/docker/.dockerignore @@ -1,2 +1,3 @@ * !start-bridge.sh +!.env diff --git a/ros1_bridge_ws/docker/.env b/ros1_bridge_ws/docker/.env new file mode 100644 index 00000000..c9b9846a --- /dev/null +++ b/ros1_bridge_ws/docker/.env @@ -0,0 +1,6 @@ +# ROS1 +ROS_HOSTNAME: 127.0.0.1 +ROS_MASTER_URI: http://127.0.0.1:11311 +# ROS2 +ROS_DOMAIN_ID: 0 +ROS_LOCALHOST_ONLY: 0 \ No newline at end of file diff --git a/ros1_bridge_ws/docker/Dockerfile b/ros1_bridge_ws/docker/Dockerfile index 56efc62f..b781e255 100644 --- a/ros1_bridge_ws/docker/Dockerfile +++ b/ros1_bridge_ws/docker/Dockerfile @@ -9,14 +9,33 @@ FROM ubuntu:22.04 AS base LABEL org.opencontainers.image.authors="yuzhong1214@gmail.com" -# Reference: https://docs.ros.org/en/humble/Installation/Alternatives/Ubuntu-Development-Setup.html#set-locale -ENV LC_ALL=en_US.UTF-8 -ENV LANG=en_US.UTF-8 +# Arguments for the default user +ARG USERNAME=user +ARG USER_UID=1000 +# Use bash as the default shell SHELL ["/bin/bash", "-c"] -# Add user -RUN useradd -ms /bin/bash user +# Keep downloaded packages for caching purposes +# Ref: https://github.com/moby/buildkit/blob/master/frontend/dockerfile/docs/reference.md#example-cache-apt-packages +RUN rm -f /etc/apt/apt.conf.d/docker-clean; echo 'Binary::apt::APT::Keep-Downloaded-Packages "true";' > /etc/apt/apt.conf.d/keep-cache + +# Upgrade packages +# Ref: https://pythonspeed.com/articles/security-updates-in-docker/ +# Ref: https://github.com/moby/buildkit/blob/master/frontend/dockerfile/docs/reference.md#example-cache-apt-packages +# Ref: https://github.com/moby/buildkit/issues/1673#issuecomment-1264502398 +# Ref: https://github.com/moby/buildkit/issues/1673#issuecomment-1987107404 +RUN --mount=type=cache,target=/var/cache/apt,sharing=private \ + apt-get update && apt-get upgrade -y \ + && rm -rf /var/lib/apt/lists/* + +# Install sudo and create a user with sudo privileges +# Ref: https://stackoverflow.com/a/65434659 +RUN --mount=type=cache,target=/var/cache/apt,sharing=private \ + apt-get update && apt-get install -y sudo \ + && useradd -m -s /bin/bash -u $USER_UID -G sudo $USERNAME \ + && echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers \ + && rm -rf /var/lib/apt/lists/* ################################################################################################ # - Build stage @@ -29,30 +48,36 @@ RUN useradd -ms /bin/bash user FROM base AS build # Install the required packages for the following command. -RUN apt-get update && \ - apt-get install -y \ +RUN --mount=type=cache,target=/var/cache/apt,sharing=private \ + apt-get update && apt-get install -y \ curl \ locales \ software-properties-common \ - lsb-release + lsb-release \ + && rm -rf /var/lib/apt/lists/* # Set locale. # Reference: https://docs.ros.org/en/humble/Installation/Alternatives/Ubuntu-Development-Setup.html#set-locale -RUN locale-gen en_US en_US.UTF-8 && \ - update-locale LC_ALL=en_US.UTF-8 LANG=en_US.UTF-8 +RUN locale-gen en_US en_US.UTF-8 \ + && update-locale LC_ALL=en_US.UTF-8 LANG=en_US.UTF-8 # Add the ROS 2 apt repository. # Reference: https://docs.ros.org/en/humble/Installation/Alternatives/Ubuntu-Development-Setup.html#add-the-ros-2-apt-repository -RUN add-apt-repository universe && \ - curl -sSL https://raw.githubusercontent.com/ros/rosdistro/master/ros.key -o /usr/share/keyrings/ros-archive-keyring.gpg && \ - echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/ros-archive-keyring.gpg] http://packages.ros.org/ros2/ubuntu $(. /etc/os-release && echo $UBUNTU_CODENAME) main" | tee /etc/apt/sources.list.d/ros2.list > /dev/null +RUN add-apt-repository universe \ + && curl -sSL https://raw.githubusercontent.com/ros/rosdistro/master/ros.key -o /usr/share/keyrings/ros-archive-keyring.gpg \ + && echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/ros-archive-keyring.gpg] http://packages.ros.org/ros2/ubuntu $(. /etc/os-release && echo $UBUNTU_CODENAME) main" | tee /etc/apt/sources.list.d/ros2.list > /dev/null + +# Install tzdata in noninteractive mode. +RUN --mount=type=cache,target=/var/cache/apt,sharing=private \ + DEBIAN_FRONTEND=noninteractive apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y \ + tzdata \ + && rm -rf /var/lib/apt/lists/* # Install development tools and ROS tools. -# Note that we declare noninteractive mode to avoid apt asking for user input. -# Additionally, install colcon from PyPI, rather than using apt packages. +# Note that I install colcon from PyPI, rather than using apt packages. # Reference: https://docs.ros.org/en/humble/How-To-Guides/Using-ros1_bridge-Jammy-upstream.html#install-development-tools-and-ros-tools -RUN DEBIAN_FRONTEND=noninteractive apt-get update && \ - DEBIAN_FRONTEND=noninteractive apt-get install -y \ +RUN --mount=type=cache,target=/var/cache/apt,sharing=private \ + apt-get update && apt-get install -y \ build-essential \ cmake \ git \ @@ -72,7 +97,8 @@ RUN DEBIAN_FRONTEND=noninteractive apt-get update && \ python3-pytest-rerunfailures \ python3-rosdep \ python3-setuptools \ - ros-dev-tools + ros-dev-tools \ + && rm -rf /var/lib/apt/lists/* RUN python3 -m pip install -U colcon-common-extensions vcstool # Create ros2_humble workspace, and clone all repositories from the list. @@ -83,9 +109,12 @@ RUN vcs import --input https://raw.githubusercontent.com/ros2/ros2/humble/ros2.r # Install dependencies. # Reference: https://docs.ros.org/en/humble/Installation/Alternatives/Ubuntu-Development-Setup.html#install-dependencies-using-rosdep -RUN rosdep init && \ - rosdep update && \ - rosdep install --from-paths src --ignore-src -y --rosdistro humble --skip-keys "fastcdr rti-connext-dds-6.0.1 urdfdom_headers" +RUN --mount=type=cache,target=/var/cache/apt,sharing=private \ + apt-get update \ + && rosdep init \ + && rosdep update \ + && rosdep install --from-paths src --ignore-src -y --rosdistro humble --skip-keys "fastcdr rti-connext-dds-6.0.1 urdfdom_headers" \ + && rm -rf /var/lib/apt/lists/* # Build the ros2_humble workspace. (This will take a few minutes) # Note that we are using colcon build without the flag --symlink-install. @@ -100,26 +129,28 @@ RUN colcon build # Reference: # - https://docs.ros.org/en/humble/How-To-Guides/Using-ros1_bridge-Jammy-upstream.html#ros-2-via-debian-packages # - https://github.com/osrf/docker_images/issues/635#issuecomment-1217505552 -RUN rm -rf /etc/apt/sources.list.d/ros2.list && \ - apt-get remove -y \ +RUN rm -rf /etc/apt/sources.list.d/ros2.list \ + && apt-get remove -y \ python3-catkin-pkg \ python3-catkin-pkg-modules # Install ROS 1 from Ubuntu packages. # Reference: https://docs.ros.org/en/humble/How-To-Guides/Using-ros1_bridge-Jammy-upstream.html#install-ros-1-from-ubuntu-packages -RUN apt-get update && \ - apt-get install -y ros-core-dev +RUN --mount=type=cache,target=/var/cache/apt,sharing=private \ + apt-get update && apt-get install -y \ + ros-core-dev \ + && rm -rf /var/lib/apt/lists/* # Build ros1_bridge from source. # Reference: https://github.com/ros2/ros1_bridge/tree/3d5328dc21564d2130b4ded30afe5cd1c41cf033#building-the-bridge-from-source RUN git clone https://github.com/ros2/ros1_bridge src/ros1_bridge -RUN source install/setup.bash && \ - colcon build --packages-select ros1_bridge --cmake-force-configure +RUN source install/setup.bash \ + && colcon build --packages-select ros1_bridge --cmake-force-configure # Other configurations +USER $USERNAME COPY ./start-bridge.sh /start-bridge.sh -USER user -WORKDIR / +ENTRYPOINT [] CMD ["/bin/bash"] ################################################################################################ @@ -132,18 +163,25 @@ CMD ["/bin/bash"] FROM base AS release +# Install tzdata in noninteractive mode. +RUN --mount=type=cache,target=/var/cache/apt,sharing=private \ + DEBIAN_FRONTEND=noninteractive apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y \ + tzdata \ + && rm -rf /var/lib/apt/lists/* + # Install ROS core and packages with their dependencies. -RUN DEBIAN_FRONTEND=noninteractive apt-get update && \ - DEBIAN_FRONTEND=noninteractive apt-get install -y \ +RUN --mount=type=cache,target=/var/cache/apt,sharing=private \ + apt-get update && apt-get install -y \ ros-core-dev \ python3-packaging \ - libspdlog-dev + libspdlog-dev \ + && rm -rf /var/lib/apt/lists/* -# Copy files from host and build stage +# Copy files from build stage COPY --from=build /ros2_humble/install /ros2_humble/install # Other configurations +USER $USERNAME COPY ./start-bridge.sh /start-bridge.sh -USER user -WORKDIR / +ENTRYPOINT [] CMD ["./start-bridge.sh"] \ No newline at end of file diff --git a/ros1_bridge_ws/docker/compose.debug.yaml b/ros1_bridge_ws/docker/compose.debug.yaml index d9d6c1e2..c7c1687e 100644 --- a/ros1_bridge_ws/docker/compose.debug.yaml +++ b/ros1_bridge_ws/docker/compose.debug.yaml @@ -9,9 +9,10 @@ services: service: ros1-bridge build: context: . + dockerfile: Dockerfile target: build - image: j3soon/ros1-bridge-build-ws - container_name: ros1-bridge-build-ws + image: j3soon/ros2-ros1-bridge-build-ws + container_name: ros2-ros1-bridge-build-ws command: /bin/bash ros1: extends: diff --git a/ros1_bridge_ws/docker/compose.yaml b/ros1_bridge_ws/docker/compose.yaml index 5390ab16..60343fb6 100644 --- a/ros1_bridge_ws/docker/compose.yaml +++ b/ros1_bridge_ws/docker/compose.yaml @@ -1,9 +1,12 @@ services: ros-core: + # TODO: Change the image based on your platform. image: osrf/ros:noetic-desktop-full + # image: arm64v8/ros:noetic container_name: ros-core command: "rosmaster --core" network_mode: host + env_file: .env healthcheck: # The healthcheck is required for ros1-bridge to wait until roscore is ready. test: /ros_entrypoint.sh bash -c "rostopic list || exit 1" @@ -13,42 +16,54 @@ services: ros1-bridge: build: context: . + dockerfile: Dockerfile target: release - image: j3soon/ros1-bridge-ws - container_name: ros1-bridge-ws + # TODO: Specify the target platform to build the image, otherwise it will build for the host platform. + # Reference: https://docs.docker.com/compose/compose-file/build/#platforms + # platforms: + # - "linux/arm64" + image: j3soon/ros2-ros1-bridge-ws + container_name: ros2-ros1-bridge-ws depends_on: ros-core: # The healthcheck is required for ros1-bridge to wait until roscore is ready. condition: service_healthy - command: ./start-bridge.sh + command: ./start-bridge.sh $ROS_MASTER_URI stdin_open: true tty: true network_mode: host working_dir: / + env_file: .env volumes: - # Mount local timezone into container. ( Readonly ) + # Mount local timezone into container. # Reference: https://stackoverflow.com/questions/57607381/how-do-i-change-timezone-in-a-docker-container - /etc/timezone:/etc/timezone:ro - /etc/localtime:/etc/localtime:ro - # Direct Rendering Infrastructure + # Mount Direct Rendering Infrastructure (DRI) for hardware acceleration support such as OpenGL. - /dev/dri:/dev/dri - # Shared Memory + # Mount shared memory for ROS2 communication. - /dev/shm:/dev/shm ros1: + # TODO: Change the image based on your platform. image: osrf/ros:noetic-desktop-full + # image: arm64v8/ros:noetic container_name: ros1 stdin_open: true tty: true network_mode: host + env_file: .env depends_on: ros-core: condition: service_healthy ros2: + # TODO: Change the image based on your platform. image: osrf/ros:humble-desktop-full + # image: arm64v8/ros:humble container_name: ros2 stdin_open: true tty: true network_mode: host + env_file: .env volumes: - # Shared Memory + # Mount shared memory for ROS2 communication. - /dev/shm:/dev/shm