Code Generation
How HID ROS 2 generates code from YAML schemas.
Overview
The code generation process transforms your YAML schema into complete ROS 2 integration and firmware support files. This happens automatically during the build process via a CMake function.
How It Works
Build-Time Generation
Code generation is integrated into the CMake build system:
You add
hid_generate()to yourCMakeLists.txtWhen you run
colcon build, CMake invokes the generatorPython script reads your YAML schema
All necessary files are generated
Files are installed to the install directory
The CMake Function
In your package’s CMakeLists.txt:
# Find the generator package
find_package(hid_descriptor_generator REQUIRED)
# Include the CMake function
include("${hid_descriptor_generator_DIR}/hid_generate.cmake")
# Call the generator
hid_generate(
SCHEMA_FILE "schema/hid_device.yaml"
)
What This Does:
Creates custom build target
Sets up dependencies on schema file
Calls Python generator script when schema changes
Installs generated files to correct locations
Exports include directories for your package
Regeneration
Files are regenerated automatically when:
Schema file is modified
Clean build is performed
Generator script is updated
Incremental Builds: CMake tracks the schema file as a dependency, so changes trigger regeneration without full rebuild.
Generated Files
ROS 2 Side
URDF (urdf/hid_robot.urdf.xacro)
Hardware interface definition using
hid_hardwarepluginState interface declarations for each input field
Command interface declarations for each output field (if defined)
Robot description with proper parameters
Controller Config (config/controllers.yaml)
Controller manager configuration
hid_generic_broadcastersetupUpdate rates and publishing rates
Interface mappings
Launch File (launch/hid.launch.py)
Loads URDF into robot_state_publisher
Spawns hardware interface via controller_manager
Spawns controllers
Sets up TF static broadcaster (world → frame_id)
Firmware Side
HID Descriptor Header (include/
USB HID report descriptor array (
hid_report_descriptor[])Descriptor size constant (
HID_REPORT_DESCRIPTOR_SIZE)Input report structure with
__attribute__((packed))Output report structure (if outputs defined)
Report ID constants
Report size constants
Field documentation in comments
Generation Process Details
Step 1: Schema Parsing
YAML Schema → Python Parser → Internal Data Structure
The generator:
Validates YAML syntax
Checks required fields
Validates types
Resolves defaults
Step 2: HID Descriptor Generation
Field Definitions → HID Item Encoding → Descriptor Bytes
For each field:
Determines HID item type (Input/Output)
Calculates bit/byte sizes
Encodes as HID descriptor items
Adds report ID items
Step 3: URDF Generation
Fields + Configuration → URDF Template → Robot Description
Creates:
Hardware component with
hid_hardwarepluginParameters (VID, PID, report IDs)
State interfaces for each input field
Command interfaces for each output field
Step 4: Controller Configuration
Schema Metadata → YAML Template → Controller Config
Configures:
hid_generic_broadcasterwith all state interfacesPublishing rate
Controller manager parameters
Step 5: Launch File Generation
Package Info + Config → Python Template → Launch File
Generates launch file that:
Loads URDF
Starts controller_manager
Spawns controllers in correct order
Sets up TF tree
File Locations
Build Directory
During build, files are generated to:
build/<package_name>/generated/
├── include/<package_name>/hid_descriptor.h
├── urdf/hid_robot.urdf.xacro
├── config/controllers.yaml
└── launch/hid.launch.py
Install Directory
After build, files are installed to:
install/<package_name>/
├── include/<package_name>/hid_descriptor.h
├── share/<package_name>/
│ ├── urdf/hid_robot.urdf.xacro
│ ├── config/controllers.yaml
│ ├── launch/hid.launch.py
│ └── schema/hid_device.yaml
Type Mapping
YAML to HID Descriptor
YAML Type |
HID Report Size |
Bits |
Bytes |
|---|---|---|---|
|
8 |
8 |
1 |
|
8 |
8 |
1 |
|
16 |
16 |
2 |
|
16 |
16 |
2 |
|
32 |
32 |
4 |
|
32 |
32 |
4 |
|
32 |
32 |
4 |
|
64 |
64 |
8 |
YAML to C Types
YAML Type |
C Type (Firmware) |
ROS 2 Interface |
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Note: All ros2_control interfaces use double regardless of source type. Type conversion happens in the hardware interface.
Advanced Usage
Multiple Schemas
For packages with multiple devices:
hid_generate(SCHEMA_FILE "schema/device1.yaml")
hid_generate(SCHEMA_FILE "schema/device2.yaml")
Each generates to the same output directories - files are overwritten. For multiple devices, use separate packages.
Custom Output Directories
The generator uses fixed output paths relative to the build directory. To customize, create wrapper scripts.
Manual Invocation
The Python script can be called manually (advanced):
python3 /path/to/generate_hid_files.py \
--schema schema/device.yaml \
--output-dir output/ \
--package-name my_package
But this is not recommended - use the CMake function instead.
Customization
Modifying Generated Files
Not Recommended: Generated files are overwritten on every build.
Recommended Approaches:
Modify the schema - Change YAML and regenerate
Wrapper files - Import generated URDF/launch and extend
Custom controllers - Create controllers that use the generated interfaces
Example: Custom Launch File
Instead of modifying generated hid.launch.py, create your own:
# my_custom.launch.py
from launch import LaunchDescription
from launch.actions import IncludeLaunchDescription
from launch.launch_description_sources import PythonLaunchDescriptionSource
from ament_index_python.packages import get_package_share_directory
import os
def generate_launch_description():
# Include generated launch file
hid_launch = IncludeLaunchDescription(
PythonLaunchDescriptionSource([
os.path.join(get_package_share_directory('my_package'),
'launch', 'hid.launch.py')
])
)
# Add your custom nodes
# ...
return LaunchDescription([hid_launch, ...])
Debugging Generation
Verbose Output
colcon build --packages-select my_package --event-handlers console_direct+
Look for: Generating HID descriptor and ROS2 files from ...
Check Generated Files
# View what was generated
find build/my_package/generated -type f
# View build directory structure
tree build/my_package/generated
Validate Schema First
ros2 run hid_tools validate_schema schema/device.yaml --strict
Catches errors before build.
Troubleshooting
Generation Not Running
Check schema file path in CMakeLists.txt
Verify
hid_descriptor_generatoris foundLook for CMake errors in build output
Files Not Installed
Check
install()commands in CMakeLists.txtVerify build succeeded without errors
Check
install/directory structure
Wrong Content
Validate schema with
validate_schemaCheck for YAML syntax errors
Verify field types are valid
See Also
Schema Reference - Complete YAML reference
Complete Tutorial - Step-by-step walkthrough
API Reference - CMake function reference