Skip to content

c-lydia/ESP-ROS2

Repository files navigation

ESP-ROS2

License ROS2 micro-ROS FreeRTOS Python C++ C ESP-IDF Arduino Docker Docker Compose NVIDIA GPU CUDA PyTorch YOLO ONNX Runtime TensorRT KiCad STEP Docs venv Stars Forks Watchers Release Issues Visitors

micro-ROS workspace for ESP32 (FreeRTOS + ESP-IDF) with ROS 2 Humble, including both the Wall-E control stack and the Gassify gas-robot stack.

For a user-focused setup and operation guide, see user_manual.md.

This repository includes:

  • ESP32 firmware app: firmware/custom/esp32_controller
  • micro-ROS firmware workspace: firmware/mcu_ws
  • Host ROS 2 workspace: src, build, install
  • micro-ROS agent container (UDP 9999)
  • Gas robot ROS 2 workspace: gas_robot_ws/src
  • Gassify system model: gassify_model.md

Documentation Map

Gassify Stack Overview

The Gassify part of this repository focuses on gas-robot modeling and robot description assets alongside the existing Wall-E stack.

  • ROS 2 package set location: gas_robot_ws/src
  • Robot description package: gas_robot_ws/src/gas_robot_description
  • URDF/Xacro model includes a dedicated gas sensor link (gas_sensor_link) in:
    • gas_robot_ws/src/gas_robot_description/gas_robot_description/src/description/gas_robot.urdf.xacro
  • Full mathematical and control model reference: gassify_model.md

The Sphinx site includes the same high-level concepts and firmware details in a published format.

Prerequisites

  • Docker
  • Docker Compose
  • USB access for flashing (device usually /dev/ttyUSB0)

Optional for CUDA/GPU workflows:

  • NVIDIA GPU driver on host
  • NVIDIA Container Toolkit on host (nvidia-container-toolkit)

Quick Start

From the repository root:

  1. Start workspace and agent
docker compose up -d

GPU/CUDA-enabled start (keeps default pipeline unchanged when not used):

docker compose -f docker-compose.yaml -f docker-compose.gpu.yaml up --build -d

GPU image includes an object-detection-ready Python stack in this mode:

  • CUDA-enabled PyTorch (torch, torchvision, torchaudio)
  • ultralytics (YOLO)
  • opencv-python-headless
  • onnx and onnxruntime-gpu==1.18.1 (pinned)
  • pycocotools
  • tensorrt==8.6.1 (pinned)

To override TensorRT version, change TENSORRT_PIP_SPEC in docker/docker-compose.gpu.yaml. To override ONNX Runtime GPU version, change ONNXRUNTIME_GPU_PIP_SPEC in docker/docker-compose.gpu.yaml.

Quick GPU check inside container:

docker exec -it micro_ros_workspace bash -lc "nvidia-smi"

Quick Python GPU/object-detection check:

docker exec -it micro_ros_workspace bash -lc "python3 -c 'import torch, ultralytics, cv2; print(torch.__version__); print(torch.cuda.is_available()); print(ultralytics.__version__); print(cv2.__version__)'"

Quick TensorRT check:

docker exec -it micro_ros_workspace bash -lc "python3 -c 'import tensorrt as trt; print(trt.__version__)'"
  1. Enter workspace container
docker exec -it micro_ros_workspace bash
  1. Build host ROS 2 workspace (if needed)
cd /micro_ros_ws
colcon build
  1. Source the environment
source /opt/ros/humble/setup.bash
source /micro_ros_ws/install/setup.bash

If you launch RViz or Gazebo GUI from the container and see Authorization required, but no authorization protocol specified, run xhost +si:localuser:root on the host before starting the container. This workspace uses X11 forwarding, and the container runs as root by default.

Launch and Visualization Updates (2026-04-17)

  • wall_e.launch.py now generates robot_description using the Python xacro API (xacro.process_file(...).toxml()) instead of shelling out to the xacro executable.
  • gazebo_ros remains optional at launch time. If unavailable, launch continues with a clear log message.
  • Rear wheel TF visibility in RViz is now guaranteed even when joint_state_publisher is disabled:
    • rear wheel static TF publishers are started by default
    • they are automatically disabled when use_joint_state_publisher:=true
  • Fresh containers should include runtime ROS GUI/model dependencies from Dockerfile (ros-humble-xacro, ros-humble-rviz2, ros-humble-gazebo-ros-pkgs, ros-humble-joint-state-publisher).

If your currently running container was created before these package updates, rebuild it:

docker compose up --build -d

KiCad In Docker

KiCad is available in the workspace container and the PCB project is mounted at:

/micro_ros_ws/pcb/wall_e_pcb_v1.0
  1. Verify KiCad CLI:
docker exec micro_ros_workspace kicad-cli version
  1. Run DRC report:
docker exec micro_ros_workspace kicad-cli pcb drc /micro_ros_ws/pcb/wall_e_pcb_v1.0/wall_e.kicad_pcb
  1. Export Gerbers:
docker exec micro_ros_workspace kicad-cli pcb export gerbers /micro_ros_ws/pcb/wall_e_pcb_v1.0/wall_e.kicad_pcb --output /micro_ros_ws/pcb/wall_e_pcb_v1.0/gerbers
  1. Launch KiCad GUI from container (host X11):
xhost +si:localuser:root
docker exec -it micro_ros_workspace kicad

Build ESP32 Firmware

Inside the workspace container:

  1. Configure firmware app and transport
ros2 run micro_ros_setup configure_firmware.sh esp32_controller -t udp -i 192.168.1.2 -p 8888
  1. Build firmware
ros2 run micro_ros_setup build_firmware.sh
  1. Firmware output files
firmware/freertos_apps/microros_esp32_extensions/build/esp32_controller.bin
firmware/freertos_apps/microros_esp32_extensions/build/esp32_controller.elf

Flash ESP32 Firmware

Inside the workspace container (requires serial device access):

  1. Flash
ros2 run micro_ros_setup flash_firmware.sh

If your board is not /dev/ttyUSB0, update docker-compose.yaml device mapping or run a custom docker command with the correct --device mapping.

Monitor ESP32 Logs (ESP-IDF)

Inside the workspace container:

cd /micro_ros_ws/firmware/freertos_apps/microros_esp32_extensions
source /micro_ros_ws/firmware/toolchain/esp-idf/export.sh
idf.py -p /dev/ttyUSB0 monitor

You can also build then monitor:

idf.py build
idf.py -p /dev/ttyUSB0 monitor

Run In Wokwi (Simulation)

Wokwi does not use /dev/ttyUSB0. It loads your firmware .bin/.elf directly.

  1. Ensure wokwi.toml points to the generated firmware files:
[wokwi]
version = 1
firmware = "firmware/freertos_apps/microros_esp32_extensions/build/esp32_controller.bin"
elf = "firmware/freertos_apps/microros_esp32_extensions/build/esp32_controller.elf"
  1. Build firmware before starting simulator:
docker exec micro_ros_workspace bash -lc "cd /micro_ros_ws/firmware/freertos_apps/microros_esp32_extensions && source /micro_ros_ws/firmware/toolchain/esp-idf/export.sh && idf.py build"
  1. Start Wokwi from VS Code command palette: Wokwi: Start Simulator.

  2. Read logs in VS Code Output panel, Wokwi channel (or Wokwi Terminal, depending on extension version).

If logs still do not appear, stop simulation, reload VS Code window, and start simulator again.

WiFi Provisioning System

Both ESP32 controller apps (motor-enabled and sensor-only) include a browser-based WiFi provisioning system for easy credential setup without source code editing.

How It Works

  1. First Boot (No Credentials)

    • Device starts WiFi access point: ESP32-Setup (open, no password)
    • HTTP provisioning server launches on http://192.168.4.1/
  2. User Setup

    • Connect laptop/phone to ESP32-Setup network
    • Open browser to http://192.168.4.1/
    • Enter WiFi SSID and password via form
    • Device saves credentials to NVS flash (persistent)
  3. Subsequent Boots

    • Device reads stored credentials from NVS
    • Connects to configured WiFi in normal station mode
    • Proceeds with ROS 2 and sensor initialization

Credential Storage

  • Location: NVS flash partition, namespace "wifi_creds"
  • Persistence: Survives power cycles and resets
  • Fallback: Kconfig symbols CONFIG_ESP_WIFI_SSID / CONFIG_ESP_WIFI_PASSWORD (for build-time defaults)

Architecture

The provisioning system includes:

  • nvs_read_wifi_creds(): Load SSID/password from flash
  • nvs_write_wifi_creds(): Save credentials after form submission
  • SoftAP initialization for open access point
  • Simple HTML form with POST handler for credential entry
  • Automatic server shutdown after successful provisioning

Applicable Apps

  • firmware/custom/esp32_controller/: Motor + sensor controller with full provisioning
  • firmware/freertos_apps/apps/esp32_controller/: Sensor-only reference variant (provisioning included)

Run micro-ROS Agent

The compose service starts this automatically:

microros/micro-ros-agent:humble udp4 --port 9999

You can check logs with:

docker logs -f micro_ros_agent

Sensor Processing Pipeline (Host)

The firmware publishes raw sensor topics:

  • /imu/data (sensor_msgs/msg/Imu)
  • /range/data (sensor_msgs/msg/Range)

Host-side ROS 2 nodes can process these into cleaner and fused signals:

  • wall_e_ws/src/feedback/feedback/imu_filter.py
    • Input: /imu/data
    • Output: /imu/filter
  • wall_e_ws/src/feedback/feedback/ultrasonic_filter.py
    • Input: /range/data
    • Output: /range/filter
  • wall_e_ws/src/feedback/feedback/sensor_fusion.py
    • Inputs: /imu/filter, /range/filter
    • Outputs: /fusion/height, /fusion/vertical_velocity, /fusion/debug

PCB and Speed Sensing

The board design lives in pcb/wall_e_pcb_v1.0.

The current schematic includes the ESP32 module, motor driver stages, the MPU6050, the HC-SR04 range sensor, and paired hall-effect sensors for wheel-speed or rotation feedback.

Use the KiCad project as the source of truth for connector pinouts, board placement, and DRC/GERBER work before changing firmware wiring assumptions.

This keeps firmware responsibilities focused on sensor I/O and control while allowing filter tuning on the host.

Control and Localization Pipeline (Host)

Current host-side runtime flow:

  • control/gamepad_node
    • Publishes /gamepad
  • control/robot_control_node
    • Inputs: /gamepad, /range/filter, /imu/filter, /odom
    • Outputs: /cmd_vel, /servo_cmd
    • Supports manual mode and cascaded closed-loop mode:
      • outer P/PD position-to-speed setpoint
      • inner PI speed tracking loop
    • Also maps a gamepad axis to /servo_cmd (std_msgs/msg/Float32, angle in degrees)
  • localization/odometry_node
    • Inputs: /cmd_vel, /imu/filter, /range/filter, /fusion/height, /fusion/vertical_velocity
    • Output: /odom
  • localization/inverse_kinematic_node
    • Input: /cmd_vel
    • Output: /motor_cmd (4-wheel command array)

System Architecture Diagram

Wall-E system architecture

Flow summary:

  • esp32_controller publishes raw IMU and range data to ROS 2.
  • imu_filter and ultrasonic_filter produce cleaner feedback streams.
  • sensor_fusion combines filtered streams for downstream state estimation.
  • odometry_node estimates vehicle state and publishes /odom.
  • robot_control_node generates /cmd_vel using gamepad + safety + PI hold.
  • robot_control_node also publishes /servo_cmd from gamepad axis input (with configurable limits/default).
  • In closed-loop mode, PI acts on speed error (not direct position error).
  • inverse_kinematic_node converts /cmd_vel into /motor_cmd for firmware actuation.

Useful Commands

  • Rebuild host workspace
docker exec -it micro_ros_workspace bash -lc "cd /micro_ros_ws && colcon build"
  • Rebuild firmware
docker exec -it micro_ros_workspace bash -lc "source /opt/ros/humble/setup.bash && source /micro_ros_ws/install/setup.bash && ros2 run micro_ros_setup build_firmware.sh"

Latest Firmware Debug Notes (2026-04-10)

  • Confirmed firmware path in use is firmware/custom/esp32_controller.

  • MPU startup now includes gyro bias calibration (200 samples). Keep robot still at boot for best drift reduction.

  • Ultrasonic timeout message Ultrasonic timeout(wait high) means no rising edge was seen on ECHO.

  • After wiring/level correction, ultrasonic produced valid pulse widths around 344-401 us (~0.059-0.069 m in test setup).

  • Reboot right after Starting ROS node setup... was traced to watchdog interaction in startup; the firmware now logs watchdog delete/add status around ROS init.

  • NVS open for read failed: 0x1102 is expected when WiFi credentials are not yet stored in namespace wifi_creds.

  • Stop everything

docker compose down

Troubleshooting

  • Package micro_ros_setup not found Source both setup files:
source /opt/ros/humble/setup.bash
source /micro_ros_ws/install/setup.bash
  • Launch error: file not found: [Errno 2] No such file or directory: 'xacro' Install xacro in the running container (or rebuild image after pulling latest Dockerfile updates):
apt-get update && apt-get install -y ros-humble-xacro
  • Launch/CLI says Package not found for rviz2 or gazebo_ros Your container is missing GUI/simulation runtime packages. Rebuild with the updated image:
docker compose up --build -d
  • RViz RobotModel shows No transform from rear_left_wheel or rear_right_wheel This is now handled in the launch file by publishing rear-wheel static transforms when joint_state_publisher is off. If you still see it, restart launch and verify you are using the updated wall_e.launch.py.

  • Docker compose fails on /dev/ttyUSB0 Your host may expose a different serial device. Adjust docker-compose.yaml accordingly.

  • Build fails with Git safe.directory for esp-idf Run in container:

git config --global --add safe.directory /micro_ros_ws/firmware/toolchain/esp-idf
  • ESP-IDF monitor fails with Device or resource busy: '/dev/ttyUSB0' Another process is already using the serial port. Close any existing screen, idf.py monitor, or other serial tool and retry. To find the owner:
fuser -v /dev/ttyUSB0
lsof /dev/ttyUSB0
  • make menuconfig fails with python: No such file or directory Install Python alias in the container:
apt-get update && apt-get install -y python-is-python3
  • ESP-IDF Python dependency warnings (pkg_resources, setuptools) For ESP-IDF v4.1, pin setuptools in the IDF virtualenv:
VENV=/root/.espressif/python_env/idf4.1_py3.10_env
$VENV/bin/python -m pip install --upgrade pip
$VENV/bin/python -m pip install "setuptools<81" wheel
  • Runtime crash: assertion "netif" failed in esp_netif_create_default_wifi_sta Cause: duplicate default STA netif creation. Fix applied in:

    • firmware/freertos_apps/apps/esp32_controller/app.c
    • firmware/custom/esp32_controller/app.c

    If this reappears after syncing app sources, ensure Wi-Fi init runs once and esp_netif_create_default_wifi_sta() is not called multiple times.

  • Wi-Fi behavior on mobile hotspots If connection is unstable, force hotspot to 2.4 GHz WPA2 and disable battery-saving/idle sleep on the phone.

  • Wokwi simulator starts but no logs are visible Usually caused by stale build artifacts or viewing the wrong panel. Rebuild firmware, restart simulation, and check VS Code Output -> Wokwi.

Project Layout

  • src: ROS 2 packages
  • firmware/custom/esp32_controller: app source
  • firmware/freertos_apps/microros_esp32_extensions: generated ESP-IDF project
  • firmware/mcu_ws: micro-ROS cross-compiled workspace
  • docs: Sphinx documentation

References (Books and Research Papers)

  • Mahony, R., Hamel, T., and Pflimlin, J.-M. (2008). Nonlinear Complementary Filters on the Special Orthogonal Group. IEEE Transactions on Automatic Control, 53(5), 1203-1218.
  • Madgwick, S. O. H. (2010). An efficient orientation filter for inertial and inertial/magnetic sensor arrays. University of Bristol, Technical Report.
  • Thrun, S., Burgard, W., and Fox, D. (2005). Probabilistic Robotics. MIT Press.
  • Siciliano, B., Sciavicco, L., Villani, L., and Oriolo, G. (2009). Robotics: Modelling, Planning and Control. Springer.
  • Astrom, K. J., and Murray, R. M. (2008). Feedback Systems: An Introduction for Scientists and Engineers. Princeton University Press.

About

A tiny robot with IMU (MPU5060) and an ultrasonic sensor (HC-SR04), running on micro-ROS, communicating over UDP.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Sponsor this project

Packages

 
 
 

Contributors