diff --git a/Dockerfile.gazebo b/Dockerfile.gazebo index e7dd775..d281b69 100644 --- a/Dockerfile.gazebo +++ b/Dockerfile.gazebo @@ -1,6 +1,36 @@ ARG ROS_DISTRO=humble ARG PREFIX= +## =========================== Healthcheck builder =============================== + +FROM husarnet/ros:${PREFIX}${ROS_DISTRO}-ros-base AS healthcheck_builder + +ARG ROS_DISTRO +ARG PREFIX + +SHELL ["/bin/bash", "-c"] + +WORKDIR /ros2_ws + +RUN mkdir src && \ + cd src && \ + MYDISTRO=${PREFIX:-ros}; MYDISTRO=${MYDISTRO//-/} && \ + source /opt/$MYDISTRO/$ROS_DISTRO/setup.bash && \ + # Create health check package + ros2 pkg create healthcheck_pkg --build-type ament_cmake --dependencies rclcpp nav_msgs && \ + sed -i '/find_package(nav_msgs REQUIRED)/a \ + add_executable(healthcheck_node src/healthcheck.cpp)\n \ + ament_target_dependencies(healthcheck_node rclcpp nav_msgs)\n \ + install(TARGETS healthcheck_node DESTINATION lib/${PROJECT_NAME})' \ + /ros2_ws/src/healthcheck_pkg/CMakeLists.txt + +COPY healthcheck.cpp src/healthcheck_pkg/src/ + +RUN MYDISTRO=${PREFIX:-ros}; MYDISTRO=${MYDISTRO//-/} && \ + source /opt/$MYDISTRO/$ROS_DISTRO/setup.bash && \ + colcon build --cmake-args -DCMAKE_BUILD_TYPE=Release && \ + rm -rf build log src + ## =========================== ROS builder =============================== FROM husarnet/ros:${PREFIX}${ROS_DISTRO}-ros-base AS ros_builder @@ -22,28 +52,20 @@ RUN apt-get update --fix-missing && apt-get install -y \ ros-dev-tools && \ apt-get upgrade -y && \ # Clone source - git clone --depth 1 https://github.com/husarion/rosbot_ros.git src && \ + git clone --depth 1 -b humble https://github.com/husarion/rosbot_ros.git src && \ vcs import src < src/rosbot/rosbot_hardware.repos && \ vcs import src < src/rosbot/rosbot_simulation.repos && \ + cp -r src/ros2_controllers/diff_drive_controller src/ && \ + cp -r src/ros2_controllers/imu_sensor_broadcaster src/ && \ + rm -rf src/ros2_controllers && \ # Install dependencies rm -rf /etc/ros/rosdep/sources.list.d/20-default.list && \ rosdep init && \ rosdep update --rosdistro $ROS_DISTRO && \ rosdep install --from-paths src --ignore-src -y -RUN cd src/ && \ - MYDISTRO=${PREFIX:-ros}; MYDISTRO=${MYDISTRO//-/} && \ - source /opt/$MYDISTRO/$ROS_DISTRO/setup.bash && \ - # Create healthcheck package - ros2 pkg create healthcheck_pkg --build-type ament_cmake --dependencies rclcpp nav_msgs && \ - sed -i '/find_package(nav_msgs REQUIRED)/a \ - add_executable(healthcheck_node src/healthcheck.cpp)\n \ - ament_target_dependencies(healthcheck_node rclcpp nav_msgs)\n \ - install(TARGETS healthcheck_node DESTINATION lib/${PROJECT_NAME})' \ - /ros2_ws/src/healthcheck_pkg/CMakeLists.txt && \ - mv /healthcheck.cpp /ros2_ws/src/healthcheck_pkg/src/ && \ - cd .. && \ - # Build +RUN MYDISTRO=${PREFIX:-ros}; MYDISTRO=${MYDISTRO//-/} && \ + source /opt/$MYDISTRO/$ROS_DISTRO/setup.bash && \ colcon build --cmake-args -DCMAKE_BUILD_TYPE=Release && \ rm -rf build log @@ -62,6 +84,7 @@ ENV HUSARION_ROS_BUILD simulation WORKDIR /ros2_ws COPY --from=ros_builder /ros2_ws /ros2_ws +COPY --from=healthcheck_builder /ros2_ws /ros2_ws_healthcheck RUN apt-get update && apt-get install -y \ python3-rosdep \ @@ -74,6 +97,9 @@ RUN apt-get update && apt-get install -y \ # Save version echo $(cat /ros2_ws/src/rosbot_gazebo/package.xml | grep '' | sed -r 's/.*([0-9]+.[0-9]+.[0-9]+)<\/version>/\1/g') > /version.txt && \ # Size optimalization + apt-get remove -y \ + python3-rosdep \ + python3-pip && \ apt-get clean && \ rm -rf src && \ rm -rf /var/lib/apt/lists/* @@ -83,4 +109,4 @@ COPY vulcanexus_entrypoint.sh / COPY healthcheck.sh / HEALTHCHECK --interval=5s --timeout=2s --start-period=5s --retries=4 \ - CMD ["/healthcheck.sh"] \ No newline at end of file + CMD ["/healthcheck.sh"] diff --git a/Dockerfile.hardware b/Dockerfile.hardware index 3492afb..98f292f 100644 --- a/Dockerfile.hardware +++ b/Dockerfile.hardware @@ -1,36 +1,10 @@ ARG ROS_DISTRO=humble ARG PREFIX= -ARG ROSBOT_FW_RELEASE=0.8.0 -## ============================ STM32FLASH ================================= -# stm32flash needs an older version of glibc (2.28), which is why ubuntu 18.04 was used -FROM ubuntu:18.04 AS stm32flash_builder - -ARG ROS_DISTRO -ARG ROSBOT_FW_RELEASE -ARG TARGETARCH - -SHELL ["/bin/bash", "-c"] - -# official releases are only for intel archs, so we need to build stm32flash from sources -RUN apt-get update && apt-get install -y \ - curl - -RUN echo ros_distro=$ROS_DISTRO firmware_release=$ROSBOT_FW_RELEASE - -# Copy firmware binaries -RUN curl -L https://github.com/husarion/stm32flash/releases/download/2023-12-05/stm32flash-$TARGETARCH -o /stm32flash && \ - chmod +x /stm32flash - -# Copy firmware binaries -RUN curl -L https://github.com/husarion/rosbot_ros2_firmware/releases/download/$ROSBOT_FW_RELEASE/firmware.bin -o /firmware.bin && \ - curl -L https://github.com/husarion/rosbot_ros2_firmware/releases/download/$ROSBOT_FW_RELEASE/firmware.hex -o /firmware.hex ## =========================== Firmware CPU ID ================================ FROM ubuntu:20.04 AS cpu_id_builder -ARG ROSBOT_FW_RELEASE - SHELL ["/bin/bash", "-c"] # official releases are only for intel archs, so we need to build stm32flash from sources @@ -91,13 +65,9 @@ RUN mkdir src COPY ./healthcheck.cpp / RUN apt-get update && apt-get install -y \ - git \ - python3-pip \ - python3-sh \ - python3-periphery && \ - pip3 install pyserial + python3-pip -RUN git clone --depth 1 https://github.com/husarion/rosbot_ros.git src -b humble && \ +RUN git clone --depth 1 -b humble https://github.com/husarion/rosbot_ros.git src && \ vcs import src < src/rosbot/rosbot_hardware.repos && \ cp -r src/ros2_controllers/diff_drive_controller src/ && \ cp -r src/ros2_controllers/imu_sensor_broadcaster src/ && \ @@ -127,12 +97,17 @@ WORKDIR /ros2_ws COPY --from=ros_builder /ros2_ws /ros2_ws COPY --from=healthcheck_builder /ros2_ws /ros2_ws_healthcheck +# for backward compatibility +RUN cp src/rosbot_utils/rosbot_utils/flash-firmware.py /usr/bin/ && \ + cp src/rosbot_utils/rosbot_utils/flash-firmware.py / && \ + cp install/rosbot_utils/share/rosbot_utils/firmware/firmware-*.bin /root/firmware.bin + RUN apt-get update && apt-get install -y \ python3-pip \ python3-rosdep \ - usbutils \ + stm32flash \ ros-$ROS_DISTRO-teleop-twist-keyboard && \ - pip3 install pyftdi sh && \ + pip3 install pyftdi && \ rm -rf /etc/ros/rosdep/sources.list.d/20-default.list && \ rosdep init && \ rosdep update --rosdistro $ROS_DISTRO && \ @@ -140,28 +115,22 @@ RUN apt-get update && apt-get install -y \ # Save version echo $(cat /ros2_ws/src/rosbot/package.xml | grep '' | sed -r 's/.*([0-9]+.[0-9]+.[0-9]+)<\/version>/\1/g') >> /version.txt && \ # Size optimalization + apt-get remove -y \ + python3-rosdep \ + python3-pip && \ apt-get clean && \ rm -rf src && \ rm -rf /var/lib/apt/lists/* -# copy firmware built in previous stage and downloaded repository -COPY --from=stm32flash_builder /firmware.bin /root/firmware.bin -COPY --from=stm32flash_builder /firmware.hex /root/firmware.hex -COPY --from=stm32flash_builder /stm32flash /usr/bin/stm32flash - COPY --from=cpu_id_builder /read_cpu_id/.pio/build/olimex_e407/firmware.bin /firmware_read_cpu_id.bin COPY ros_entrypoint.sh / COPY vulcanexus_entrypoint.sh / COPY healthcheck.sh / -HEALTHCHECK --interval=5s --timeout=2s --start-period=10s --retries=4 \ - CMD ["/healthcheck.sh"] +COPY print-serial-number.py /usr/bin/ -COPY microros_localhost_only.xml / +ENV ROS_NAMESPACE= -# copy scripts -COPY flash-firmware.py / -COPY flash-firmware.py /usr/bin/ -COPY flash-firmware-usb.py /usr/bin/ -COPY print-serial-number.py /usr/bin/ +HEALTHCHECK --interval=5s --timeout=2s --start-period=10s --retries=6 \ + CMD ["/healthcheck.sh"] diff --git a/README.md b/README.md index 0d8bbf4..7202b4e 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,26 @@ Docker Image for ROS Humble Node providing interface for STM32 firmware over [Mi With *docker-compose* configuration shown in [demo](./demo) it can communicate with hardware of ROSbot 2R, ROSbot 2 PRO and ROSbot 2.0. +## Quick Start + +```yaml +services: + + rosbot: + image: husarion/rosbot:humble + devices: + - ${SERIAL_PORT:?err} + - /dev/bus/usb/ # FTDI (if connecting over USB port with STM32) + environment: + - ROS_DOMAIN_ID=30 + command: > + ros2 launch rosbot_bringup combined.launch.py + mecanum:=${MECANUM:-False} + serial_port:=$SERIAL_PORT + serial_baudrate:=576000 + namespace:=robot1 +``` + ## Flashing the firmware Firmware if flashed from inside of the container running on the ROSbot: diff --git a/demo/namespace/compose.yaml b/demo/namespace/compose.yaml index 6435479..cd43ed2 100644 --- a/demo/namespace/compose.yaml +++ b/demo/namespace/compose.yaml @@ -12,26 +12,25 @@ services: rosbot: - # image: husarion/rosbot:humble-ros2-combined-microros build: context: ../.. dockerfile: Dockerfile.hardware + # image: husarion/rosbot:humble-ros2-robot-utils network_mode: host ipc: host devices: - ${SERIAL_PORT:?err} - - /dev/bus/usb/ - volumes: - - ../../flash-firmware-usb.py:/usr/bin/flash-firmware-usb.py + - /dev/bus/usb/ # FTDI environment: - FASTRTPS_DEFAULT_PROFILES_FILE=/shm-only.xml - command: tail -f /dev/null - # command: > - # ros2 launch rosbot_bringup combined.launch.py - # mecanum:=${MECANUM:-False} - # serial_port:=$SERIAL_PORT - # serial_baudrate:=576000 - # namespace:=robot1 + - ROS_DOMAIN_ID=30 + - ROS_NAMESPACE=robot1 # For healthckeck node + command: > + ros2 launch rosbot_bringup combined.launch.py + mecanum:=${MECANUM:-False} + serial_port:=$SERIAL_PORT + serial_baudrate:=576000 + namespace:=robot1 ros2router: image: husarnet/ros2router:1.4.0 diff --git a/flash-firmware-usb.py b/flash-firmware-usb.py deleted file mode 100755 index 75a606c..0000000 --- a/flash-firmware-usb.py +++ /dev/null @@ -1,109 +0,0 @@ -#!/usr/bin/python3 - -import sh -import time -import sys -import argparse -from pyftdi.ftdi import Ftdi - -# CBUS0 - BOOT0 -# CBUS1 - RST - -class FirmwareFlasher: - def __init__(self, binary_file, port): - - # ftdi.show_devices() - # self.ftdi = Ftdi.create_from_url('ftdi://ftdi:ft-x:DK0AM0V0/1') - self.device = 'ftdi://ftdi:ft-x:/1' - self.ftdi = Ftdi() - - self.binary_file = binary_file - self.max_approach_no = 3 - self.port = port - - def enter_bootloader_mode(self): - - self.ftdi.open_from_url(url=self.device) - self.ftdi.set_cbus_direction(0b11,0b11) # set CBUS0 and CBUS1 to output - time.sleep(0.1) - self.ftdi.set_cbus_gpio(0b11) #set CBUS0 to 1 and RST to 1 - time.sleep(0.1) - self.ftdi.set_cbus_gpio(0b01) #set CBUS0 to 1 and RST to 0 - time.sleep(0.1) - # self.ftdi.set_cbus_direction(0b11,0b00) # set CBUS0 and CBUS1 to input - time.sleep(0.1) - self.ftdi.close() - - def exit_bootloader_mode(self): - - self.ftdi.open_from_url(url=self.device) - self.ftdi.set_cbus_direction(0b11,0b11) # set CBUS0 and CBUS1 to output - time.sleep(0.1) - self.ftdi.set_cbus_gpio(0b10) #set CBUS0 to 1 and RST to 1 - time.sleep(0.1) - self.ftdi.set_cbus_gpio(0b00) #set CBUS0 to 1 and RST to 0 - time.sleep(0.1) - # self.ftdi.set_cbus_direction(0b11,0b00) # set CBUS0 and CBUS1 to input - time.sleep(0.1) - self.ftdi.close() - - def try_flash_operation(self, operation_name, flash_command, flash_args): - for i in range(self.max_approach_no): - try: - self.enter_bootloader_mode() - sh.usbreset("0403:6015") - flash_command(self.port, *flash_args, _out=sys.stdout) - self.exit_bootloader_mode() - time.sleep(0.2) - break - except Exception as e: - print(f"{operation_name} error! Trying again.") - print(f"Error: {e}") - print("---------------------------------------") - else: - print(f"WARNING! {operation_name} went wrong.") - - def flash_firmware(self): - # Disable the flash write-protection - self.try_flash_operation("Write-UnProtection", sh.stm32flash, ["-u"]) - - # Disable the flash read-protection - self.try_flash_operation("Read-UnProtection", sh.stm32flash, ["-k"]) - - # Flashing the firmware - # /usr/bin/stm32flash /dev/ttyUSB0 -v -w /root/firmware.bin -b 115200 - flash_args = ["-v", "-w", self.binary_file, "-b", "115200"] - self.try_flash_operation("Flashing", sh.stm32flash, flash_args) - - - sh.usbreset("0403:6015") - - -def main(): - - parser = argparse.ArgumentParser( - description='Flashing the firmware on STM32 microcontroller in ROSbot XL') - - parser.add_argument( - "-f", - "--file", - nargs='?', - default="/root/firmware.bin", - help="Path to a firmware file. Default: /root/firmware.bin") - parser.add_argument( - "-p", - "--port", - nargs='?', - default="/dev/ttyUSB0", - help="Path to serial connection. Default: /dev/ttyUSB0") - - binary_file = parser.parse_args().file - port = parser.parse_args().port - - flasher = FirmwareFlasher(binary_file, port) - flasher.flash_firmware() - print("Done.") - - -if __name__ == "__main__": - main() \ No newline at end of file diff --git a/flash-firmware.py b/flash-firmware.py old mode 100755 new mode 100644 diff --git a/healthcheck.cpp b/healthcheck.cpp index 31bb431..8218c90 100644 --- a/healthcheck.cpp +++ b/healthcheck.cpp @@ -1,6 +1,7 @@ #include "fstream" #include "nav_msgs/msg/odometry.hpp" #include "rclcpp/rclcpp.hpp" +#include using namespace std::chrono_literals; @@ -33,9 +34,15 @@ void healthy_check() { int main(int argc, char *argv[]) { rclcpp::init(argc, argv); + + std::string topic = "odometry/filtered"; + if(const char* ns = std::getenv("ROS_NAMESPACE")) { + topic = std::string(ns) + "/" + topic; + } + auto node = rclcpp::Node::make_shared("healthcheck_node"); auto sub = node->create_subscription( - "odometry/filtered", rclcpp::SensorDataQoS().keep_last(1), msg_callback); + topic, rclcpp::SensorDataQoS().keep_last(1), msg_callback); while (rclcpp::ok()) { rclcpp::spin_some(node); diff --git a/microros_localhost_only.xml b/microros_localhost_only.xml deleted file mode 100644 index d433142..0000000 --- a/microros_localhost_only.xml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - CustomUdpTransport - UDPv4 - -
127.0.0.1
-
-
-
- - - - - CustomUdpTransport - - - false - - -
-
\ No newline at end of file