This document provides essential information for agents working in this ROS2 workspace for ROV (Remotely Operated Vehicle) control and simulation.
- Type: ROS2 workspace (Humble distribution)
- Target: Remotely Operated Vehicle (ROV) control system
- Domain: Underwater robotics simulation and control
- Architecture: Distributed control system with simulation, trajectory generation, localization, and autonomy components
# Build the entire workspace
colcon build --symlink-install
# Build specific package
colcon build --symlink-install --packages-select <package_name>
# Build with diagnostics
colcon build --symlink-install --event-handlers console_direct+# Source ROS2 (run this first)
source /opt/ros/humble/setup.bash
# Source workspace (after building)
source /home/dev/ros2_ws/install/setup.bash
# Both are automatically sourced in .bashrc# Run a specific node
ros2 run <package> <executable>
# Run with verbose logging
ros2 run <package> <executable> --ros-args --log-level debug
# Examples:
ros2 run rov_pid rov_pid_node
ros2 run traj_gen min_snap_traj_generator# Launch a system using Python launch files
ros2 launch <package> <launch_file.py>
# Example:
ros2 launch rov_pid pid.launch.py
ros2 launch rov_stonefish sea_bluerov2_nogpu.launch.py# Run all tests
colcon test
# Run specific package tests
colcon test --packages-select <package_name>
# View test results
colcon test --packages-select <package_name> --event-handlers console_direct+
# Run pytest directly (from package directory)
pytest test/# Run flake8
flake8 <file_or_directory>
# Run ament linting (ROS2 style)
ament_flake8 <directory>
ament_pep257 <directory>
ament_copyright <directory>
# Use .flake8 configuration (max line length: 125, ignore W191)ros2_ws/
├── src/ # All packages
│ ├── control_system/ # Control algorithms (PID, MFSMC, thruster management)
│ ├── controlers/ # Controller drivers (DS4)
│ ├── trajectory/ # Trajectory generation and waypoint management
│ ├── simulation/ # Physics simulation (Stonefish)
│ ├── localization/ # State estimation and sensor fusion
│ ├── autonomy_system/ # High-level autonomy and behavior trees
│ ├── autonomy_system_interfaces/ # Custom messages and actions
│ ├── mechanical/ # Robot description and hardware interface
│ └── utils/ # Utilities (diagnostics, ros2_numpy)
├── build/ # Generated by colcon
├── install/ # Installation directory
└── log/ # Build logs
Python Packages (ament_python):
<package>/
├── package.xml # Package metadata and dependencies
├── setup.cfg # Installation configuration (script_dir, install_scripts)
├── setup.py # setuptools configuration with entry_points
├── <package>/ # Python module directory
│ ├── __init__.py # Empty module file
│ └── <module>.py # Implementation code
├── nodes/ # Node entry points (main functions)
│ └── <node_name>.py
├── config/ # YAML configuration files
│ └── params.yaml
├── launch/ # Python launch files
│ └── <launch_file>.py
├── test/ # Tests
│ ├── test_copyright.py # Copyright linting
│ ├── test_flake8.py # Python style linting
│ └── test_pep257.py # Docstring linting
├── rviz/ # RViz configuration files
│ └── <config>.rviz
└── resource/ # Package resource marker
└── <package>
C++ Packages (ament_cmake):
<package>/
├── CMakeLists.txt # CMake build configuration
├── package.xml # Package metadata
├── include/<package>/ # Header files
├── src/ # Source files
├── msg/ # Custom message definitions
├── srv/ # Custom service definitions
└── launch/ # Launch files
- rov_pid: PID control for ROV
- rov_thruster_manager: Thruster allocation matrix management
- rov_passthrough_control: Joystick-to-wrench conversion
- traj_gen: Trajectory generation (minimum snap, analytical)
- rov_position_snapshot: Waypoint recording from DS4 controller
- rov_description: URDF/xacro robot description
- rov_bridge: Hardware interface (STM32 communication)
- rov_stonefish: Stonefish simulation scenarios and configurations
- stonefish_ros2: Stonefish simulation interface (C++)
- robot_localization: EKF/UKF state estimation
- autonomy_system: Behavior tree-based autonomy using py_trees
- rov_ds4_driver: DS4 controller driver
- diagnostic_updater, diagnostic_aggregator: ROS diagnostics
- Format: Lowercase with underscores
- Examples:
rov_pid,traj_gen,rov_thruster_manager,rov_passthrough_control - Pattern:
rov_<component>for ROV-specific packages
- Python Classes: PascalCase (e.g.,
PositionSnapshot,ThrusterManager,PassthroughControl) - Node Names: lowercase with underscores (e.g.,
rov_position_snapshot,rov_pid_node,thruster_manager) - Entry Points: lowercase with underscores (matching node names)
- Format: Hierarchical with underscores
- Examples:
/bluerov2/odometry/joy_wrench_stmp/calculated_wrench/target_pose,/target_twist,/target_accel/traj_gen/waypoints
- Prefixes:
/bluerov2/for robot-specific topics
- Format: snake_case
- Examples:
thruster_prefixoutput_typesub_input_wrench_topic_namemax_force,max_torque
- World Frame:
world_ned(North-East-Down coordinate system) - Robot Frame:
base_link - Sensor Frames:
bluerov2/imu_filter,bluerov2/multibeam,bluerov2/fls - Trajectory Frame:
traj_gen(for trajectory generation reference)
- Line Length: Maximum 125 characters (configured in
.flake8) - Indentation: 4 spaces (configured to ignore W191/tabs in
.flake8) - Linting: flake8, ament_pep257 (docstrings), ament_copyright
Most Python nodes follow this structure:
import rclpy
from rclpy.node import Node
from <package> import <Class>
def main():
rclpy.init()
node = <Class>() # Class inherits from Node
rclpy.spin(node)
node.destroy_node()
rclpy.shutdown()
if __name__ == "__main__":
main()# Declare and retrieve parameters
self.declare_parameter('param_name', default_value)
param_value = self.get_parameter('param_name').value
# For parameters with type specification
param_value = self.get_parameter('param_name').get_parameter_value().string_valueclass MyNode(Node):
def __init__(self):
Node.__init__(self=self, node_name='my_node_name') # Explicit parent init
# Declare parameters
self.declare_parameter('param', default)
# Create publishers/subscribers
self.pub = self.create_publisher(MsgType, 'topic', qos_profile)
self.sub = self.create_subscription(MsgType, 'topic', callback, qos_profile)
# Timer-based operations
self.create_timer(period, self.callback)from launch_ros.actions import Node
from launch import LaunchDescription
from launch.actions import IncludeLaunchDescription, TimerAction, DeclareLaunchArgument
from launch.substitutions import LaunchConfiguration, PathJoinSubstitution
from launch_ros.substitutions import FindPackageShare
from ament_index_python import get_package_share_directory
def generate_launch_description():
# Declare arguments
config_arg = DeclareLaunchArgument(name="config", default_value=default_path)
# Define nodes
my_node = Node(
package='my_package',
executable='my_executable',
parameters=[LaunchConfiguration('config')],
output='screen'
)
# Use TimerAction for delayed startup
node_timer = TimerAction(period=1.0, actions=[my_node])
return LaunchDescription([
config_arg,
node_timer
])Each Python package has three standard tests in test/:
test_copyright.py: Validates Apache 2.0 license headers (many skip with@pytest.mark.skip)test_flake8.py: Validates Python code styletest_pep257.py: Validates docstring format
# All tests in workspace
colcon test
# Specific package
colcon test --packages-select rov_pid
# With verbose output
colcon test --packages-select rov_pid --event-handlers console_direct+
# Direct pytest
cd src/<package>
pytest test/GitHub Actions workflows in src/utils/diagnostics/.github/workflows/:
test.yaml: Builds and tests on ROS2 Rollinglint.yaml: Runs cppcheck, cpplint, flake8, uncrustify, xmllint
All launch files use Python format (.launch.py) with launch_ros actions.
Common pattern for sequential node startup:
# Delay rov_state_publisher by 1 second
description_timer = TimerAction(period=1.0, actions=[rov_state_publisher_node])
# Delay stonefish simulator by 2 seconds
stonefish_timer = TimerAction(period=2.0, actions=[launch_include])
# Delay trajectory generator by 4 seconds
traj_gen_timer = TimerAction(period=4.0, actions=[traj_gen_node, tf_traj_gen])# Pass config file to nodes
my_node = Node(
package='my_package',
executable='my_executable',
parameters=[LaunchConfiguration('config')]
)
# Pass parameters from another launch
passthrough_launch = IncludeLaunchDescription(
launch_description_source=PathJoinSubstitution([share, 'launch', 'base.launch.py']),
launch_arguments={
"config": LaunchConfiguration('config')
}.items()
)Located in config/ directories within packages. Structure:
<node_name>:
ros__parameters:
param1: value
param2: value
# Example from rov_pid/config/params.yaml
rov_state_publisher:
ros__parameters:
robot_name: bluerov2
rov_thruster_manager:
ros__parameters:
thruster_prefix: t200
output_type: 'STONEFISH'
thruster_voltage: 20Thruster Manager Output Types:
PWM: Single topic with PWM valuesRPM: Single topic with RPM valuesFRC: Single topic with force valuesSEP_PWM,SEP_RPM,SEP_FRC: Individual topics per thrusterSTONEFISH: Normalized values for Stonefish simulation
Trajectory Generator:
use_traj_from_file: Load trajectory from JSON filetraj_file: JSON filename containing trajectory data
Passthrough Control:
controller: Input device type (ds4, joy, station)max_force,max_torque: Force/torque limitsequalization_type: Linearization function (LINEAR, SQUARE, CUBE, INV_SQUARE, INV_CUBE)
- world_ned: Global NED (North-East-Down) reference frame
- base_link: Robot body frame
- odom: Odometry frame (local navigation)
- map: Global map frame (for absolute positioning)
world_ned (or map)
└── base_link
└── bluerov2/imu_filter
└── bluerov2/multibeam
└── bluerov2/fls
└── t200_<N> (thrusters)
Use tf2_ros static transform publisher:
ros2 run tf2_ros static_transform_publisher x y z yaw pitch roll parent_frame child_frameIn launch files:
Node(
package='tf2_ros',
executable='static_transform_publisher',
arguments=["0", "0", "0", "0", "0", "0", "world_ned", "traj_gen"]
)- Python packages use
setup.cfgwithscript_dir=$base/lib/<package> setup.pydefinesentry_pointsfor console scripts- Use
colcon build --symlink-installfor faster development
- Thruster allocation matrix (TAM) requires exactly 6 thrusters
- Thruster discovery uses
tf_statictopic with child frame prefix - Order of thrusters is sorted alphabetically
- Supports two types: Analytical (pre-defined shapes) and Minimum Snap (waypoint-based)
- Minimum snap uses 7th-order polynomials
- Outputs derivatives up to snap: pose, twist, accel, jerk, snap
- Uses
py_treesandpy_trees_roslibraries - Actions defined in
autonomy_system_interfaces/action/ ActionBehaviourclass wraps ROS2 actions as behavior tree nodes- Multi-threaded executor for concurrent action handling
- Uses
robot_localizationpackage (EKF/UKF) - Configuration in YAML with detailed sensor fusion options
- Supports multiple odom, pose, twist, and IMU inputs
- Parameters include
odom0_config,imu0_config, etc. for sensor fusion
- Stonefish physics engine for underwater simulation
- Two modes: GPU-accelerated and no-GPU
- Scenarios defined in
.scnfiles - DVL (Doppler Velocity Log) simulation for underwater velocity measurement
- Uses
ds4drvPython package for DualShock 4 - Provides
Statusmessage with button and axis states - Button/axis mapping configurable in YAML
- Example axes:
axis_left_y,axis_left_x,axis_right_y,axis_right_x
- Records waypoints by pressing DS4 buttons
- Saves to JSON in
output/directory - Uses transforms between world_ned and base_link
- Timestamping for time-ratio based trajectory generation
- Uses
ros:humble-desktop-fullbase image - NVIDIA GPU support enabled
- Stonefish library pre-installed
- ROS2 aliases sourced from
/home/dev/ros2-aliases/ros2_aliases.bash
ROS2 Packages:
ros-humble-joint-state-publisherros-humble-tf-transformationsros-humble-imu-toolsros-humble-plotjuggler-rosros-humble-py-treesros-humble-py-trees-ros
Python Packages:
ds4drv: DS4 controller driverosqp: Quadratic programming solvercasadi: Optimization frameworknumpy,ros2_numpy: Numerical computing
Simulation:
- Stonefish 1.3.0+ (installed from AGH-Marines fork)
- Default:
colcon build --symlink-install - ROS_DISTRO: humble
- RMW_IMPLEMENTATION: rmw_fastrtps_cpp
- Create package structure
- Write
package.xmlwith dependencies - Write
setup.cfgwith script directories - Write
setup.pywith entry_points - Add standard tests:
test_copyright.py,test_flake8.py,test_pep257.py - Build:
colcon build --symlink-install - Test:
colcon test
- Create node file in
nodes/or module directory - Inherit from
rclpy.node.Node - Follow node pattern:
__init__,main,if __name__ - Add entry point in
setup.py:entry_points={ 'console_scripts': [ 'node_name = package.module:main' ] }
- Rebuild and test
- Create
.launch.pyinlaunch/directory - Import necessary launch components
- Define nodes with
Node()action - Use
TimerActionfor startup delays if needed - Add
DeclareLaunchArgumentfor configurable parameters - Return
LaunchDescriptionwith all actions
- Edit
.urdf.xacrofiles inrov_description/xacro/ - Rebuild workspace
- Verify with
ros2 run rov_description rov_state_publisher - Check in RViz2 with provided configuration
- Python Path:
PYTHONPATHis set to includesrc:in devcontainer, but after building, use workspace setup - Copyright Headers: Many files skip copyright test with
@pytest.mark.skip- this is intentional during development - Thruster Discovery: Thruster manager waits for tf_static - ensure robot state publisher is running
- Simulation Timing: Stonefish requires sufficient startup time - use TimerAction delays in launch files
- Parameter Updates: Changing parameters requires node restart (no dynamic reconfigure in most nodes)
- Coordinate Frames: NED (North-East-Down) is standard - z-axis points DOWN
- Stonefish Integration: Requires specific output_type ('STONEFISH') for thruster manager
- Waypoint Recording: Position snapshot uses DS4 button presses - ensure controller is connected
- Test Dependencies: Some tests require ROS2 bag files in
test/directory (robot_localization) - Git Submodules: Project uses git submodules (see
.gitmodules)
- Configuration files in
rviz/directories - Launch with:
rviz2 -d <config_file> - Common topics to visualize:
/tf: Transform tree/traj_gen/path: Trajectory path/traj_gen/waypoints: Waypoints for RViz/bluerov2/odometry: Robot position
- Layouts in
plotjuggler_layouts/directories - Useful for visualizing trajectory derivatives
- Load with PlotJuggler application
# Clean build
rm -rf build install log
colcon build --symlink-install# Ensure workspace is sourced
source /home/dev/ros2_ws/install/setup.bash
# Check PYTHONPATH
echo $PYTHONPATH# Check node status
ros2 node list
ros2 topic list
ros2 topic echo /topic_name
# Check parameters
ros2 param list /node_name
ros2 param get /node_name /parameter_name# View transform tree
ros2 run tf2_tools view_frames
# Check transform
ros2 run tf2_ros tf2_echo source_frame target_frame.launch.py: Python launch files.yaml: ROS2 parameter files.urdf.xacro: Robot description files (Xacro macro format).rviz: RViz2 configuration files.scn: Stonefish simulation scenarios.obj,.dae: 3D mesh files.msg: ROS2 message definitions.srv: ROS2 service definitions.action: ROS2 action definitions
NEVER add new markdown files with changes status, instead you can update README.md file after asking for permission