Docs menu

Add your own code

Decide whether new code is driving code or support code, where its package goes, and what every package must contain.

Every new feature or tool is its own package under src/. Before writing one, decide which of two categories it is in, because the category decides how it must be built and tested.

Driving code or support code

A package is driving code if it publishes on /drive, /ackermann_cmd, /commands/motor/* or /commands/servo/*. It can move the car, so it must follow the full contract in Write a driving node, including its own LB deadman check.

Everything else is support code: nodes that only subscribe, or publish things that cannot move the car, such as visualizations, logs, diagnostics and recordings. It needs a well-formed package and must not publish on topics it does not own. The dashboard, the run recorder and imu_bridge are support code.

The test is whether a message the package sends can make the wheels turn, not whether it contains a driving algorithm. A logging tool that republishes /drive is driving code. If in doubt, treat the package as driving code. The deadman check costs a few lines, and leaving it out of something that turns out to touch /drive leaves a car that cannot be stopped by releasing LB.

Where it goes

One package per feature, in src/<package_name>/, named in snake_case to match its folder and Python module. Add to an existing package only when the code extends that package’s own purpose. Separate packages keep --packages-select builds fast, keep each package’s dependencies honest, and let a feature be removed without touching anything else.

What every package contains

src/<package_name>/
├── package.xml               name, version and every dependency
├── setup.py                  executables and installed data files
├── setup.cfg                 points the installer at the right folder
├── resource/<package_name>   empty marker file that ament_python requires
├── <package_name>/
│   ├── __init__.py
│   ├── <logic>.py            mathematics or parsing, with no rclpy import
│   └── <node>.py             the ROS node, which calls <logic>.py
├── launch/<node>_launch.py   starts the node with its configuration
├── config/<node>.yaml        every parameter value
└── test/                     tests

Four rules matter most:

  1. Declare every dependency in package.xml, including rclpy, each message package and any system Python library. An undeclared dependency works on your machine and fails on a fresh clone.
  2. Make every setting a parameter. Topic names and tuning values are declare_parameter() calls, set from config/<node>.yaml by the launch file. Nobody should need to edit code to retune or retarget a node.
  3. Write a launch file, even for one node. It loads the YAML from the package’s installed share folder.
  4. Keep the logic importable without ROS. Put mathematics, protocol handling and parsing in a module that does not import rclpy, as gap_logic.pyGitHub, racing_math.pyGitHub and drive_intentGitHub do. Its tests then run with plain pytest, and the no-ROS simulator can call it.

Build and run it

cd ~/racerbot-ws
colcon build --symlink-install --packages-select <package_name>
source install/setup.bash
ros2 launch <package_name> <node>_launch.py

Summary: 1 package finished means the build worked. If the summary lists more than one package, the name was mistyped and colcon built everything. After a symlink build, edits to Python, launch and YAML files take effect at the next launch; changes to package.xml or setup.py, and new or removed files, need another build.

Examples

Package Category Copy it for
gap_follow Driving The deadman pattern, logic split from the node, a YAML for every setting, drive intent
pure_pursuit Driving A timer-driven control loop, layered safety checks, offline tools as console scripts
race_diagnostics Support A node that only subscribes
racerbot_doctor Support A command-line tool with a static test that it never publishes
drive_intent Library A ROS-free package used by other packages

Checked against car 1 commit 881c409 and car 2 commit 779c060, before the two workspaces merged.