Architecture

Cubs2 follows a layered architecture separating dynamics, control, and ROS integration.

System Overview

┌─────────────────────────────────────────────┐
│          cubs2_bringup (Launch)             │
└──────────────┬──────────────────────────────┘
               │
┌──────────────┴──────────────────────────────┐
│                                              │
│  cubs2_simulation (ROS2 Runtime)            │
│  ┌───────────────────────────────────────┐  │
│  │  cubs2_control (Controllers)          │  │
│  │  ┌────────────────────────────────┐   │  │
│  │  │  cubs2_dynamics (Physics)      │   │  │
│  │  │  ┌─────────────────────────┐   │   │  │
│  │  │  │  cyecca (Core Models)   │   │   │  │
│  │  │  └─────────────────────────┘   │   │  │
│  │  └────────────────────────────────┘   │  │
│  └───────────────────────────────────────┘  │
└──────────────────────────────────────────────┘

Layer Responsibilities

Cyecca Layer

Location: External library (pip install cyecca)

Responsibilities:

  • Core ModelSX framework

  • Symbolic differentiation (CasADi)

  • Numerical integrators

  • Linearization tools

  • Generic dynamics utilities

Dependencies: numpy, scipy, casadi

ROS: None

cubs2_dynamics

Responsibilities:

  • Aircraft-specific models (SportCub)

  • Trim computation

  • Aerodynamic coefficients

  • Ground contact dynamics

Dependencies: cyecca, numpy, scipy, casadi

ROS: None

cubs2_control

Responsibilities:

  • Attitude controllers

  • PID controllers

  • Closed-loop compositions

  • Control algorithms

Dependencies: cubs2_dynamics, cyecca

ROS: None

cubs2_simulation

Responsibilities:

  • ROS2 node implementation

  • Message publishing/subscribing

  • Real-time scheduling

  • TF broadcasting

  • Gamepad integration

Dependencies: cubs2_dynamics, cubs2_control, cubs2_msgs

ROS: rclpy, geometry_msgs, sensor_msgs

cubs2_planning

Responsibilities:

  • Dubins path planning

  • Racecourse navigation

  • Waypoint generation

  • Path visualization

Dependencies: cubs2_msgs

ROS: rclpy, nav_msgs, visualization_msgs

cubs2_rviz

Responsibilities:

  • Custom RViz panels

  • Video streaming

  • HUD display

  • Joystick widget

  • Simulation controls

Dependencies: cubs2_msgs, Qt5, GStreamer

ROS: rclcpp, rviz_common, pluginlib

cubs2_bringup

Responsibilities:

  • System launch files

  • Parameter configuration

  • Node orchestration

Dependencies: All other cubs2 packages

ROS: launch, launch_ros

Data Flow

Simulation Loop

┌─────────────────┐
│  User Input     │ (/joy, /control)
└────────┬────────┘
         │
         v
┌─────────────────┐
│  Controller     │ (cubs2_control)
└────────┬────────┘
         │
         v
┌─────────────────┐
│  Dynamics       │ (cubs2_dynamics)
└────────┬────────┘
         │
         v
┌─────────────────┐
│  Integration    │ (RK4)
└────────┬────────┘
         │
         v
┌─────────────────┐
│  State Update   │
└────────┬────────┘
         │
         v
┌─────────────────┐
│  ROS Messages   │ (/pose, /velocity, /imu)
└─────────────────┘

Message Types

Custom Messages

  • cubs2_msgs/AircraftControl - Control surface commands

Standard Messages

  • geometry_msgs/PoseStamped - Position and orientation

  • geometry_msgs/TwistStamped - Velocities

  • sensor_msgs/Imu - IMU measurements

  • nav_msgs/Path - Planned trajectory

  • visualization_msgs/MarkerArray - Visual markers

Hierarchical Composition

ModelSX Framework

Cubs2 uses Cyecca’s ModelSX for hierarchical model composition:

# Create submodels
aircraft = sportcub()
controller = autolevel_controller()

# Compose
system = ModelSX.compose({
    "plant": aircraft,
    "controller": controller
})

# Connect signals
system.connect("controller.u.q", "plant.x.r")
system.connect("plant.u.ail", "controller.y.ail")

# Build with single integrator
system.build_composed(integrator="rk4")

Benefits:

  • Single integration loop (numerical accuracy)

  • Type-safe state access (x.plant.p not x[0:3])

  • Reusable subsystems

  • Clear signal flow

Plugin Architecture

RViz Panels

Panels use Qt5 and RViz2 API:

class MyPanel : public rviz_common::Panel {
  Q_OBJECT
public:
  MyPanel(QWidget* parent = nullptr);
private:
  rclcpp::Node::SharedPtr node_;
  rclcpp::Publisher<...>::SharedPtr pub_;
};

PLUGINLIB_EXPORT_CLASS(cubs2::MyPanel, rviz_common::Panel)

Design Principles

Separation of Concerns

  • Dynamics ≠ ROS

  • Control ≠ ROS

  • Only runtime nodes use ROS

Benefit: Reusable in non-ROS contexts (MATLAB, hardware)

Composition over Inheritance

  • Build systems from subsystems

  • Connect via explicit signals

  • Avoid deep inheritance hierarchies

Type Safety

  • Dataclass-based state definitions

  • Named fields instead of indices

  • Compile-time validation

Testing

  • Unit tests for each layer

  • Integration tests for compositions

  • Hardware-in-loop tests

Performance Considerations

Simulation Speed

Factors affecting real-time performance:

  • Integration timestep (smaller = slower)

  • Model complexity

  • Visualization overhead

  • Python vs compiled code

Optimization Strategies:

  • JIT compilation (CasADi)

  • Vectorized operations (NumPy)

  • Reduced visualization rate

  • C++ critical paths

Memory

  • CasADi symbolic expressions cached

  • ROS messages zero-copy when possible

  • Preallocated state vectors

Extensibility

Adding Aircraft

  1. Create new module in cubs2_dynamics

  2. Inherit from or use ModelSX pattern

  3. Define state/input/output dataclasses

  4. Implement dynamics equations

Adding Controller

  1. Create module in cubs2_control

  2. Follow ModelSX pattern for composition

  3. Add unit tests

  4. Document tuning parameters

Adding RViz Panel

  1. Create header in cubs2_rviz/include

  2. Implement in cubs2_rviz/src

  3. Register in plugin_description.xml

  4. Update RViz config

See Also