Defining a native module
Python side native module is just a definition of a config dataclass and module class specifying pubsub I/O. Both the config dataclass and pubsub topics get converted to CLI args passed down to your executable once the module is started.no-result session=nativemodule
MyLidar is a full DimOS module. You can use it with autoconnect, blueprints, transport overrides, and specs. Once this module is started, your ./build/my_lidar will get called with specific CLI args.
How it works
Whenstart() is called, NativeModule:
- Builds the executable if it doesnโt exist and
build_commandis set. - Collects topics from blueprint-assigned transports on each declared port.
- Builds the command line:
<executable> --<port> <topic> ... --<config_field> <value> ... - Launches the subprocess with
Popen, piping stdout/stderr. - Starts a watchdog thread that calls
stop()if the process crashes.
skip
ansi=false session=nativemodule skip
/<name>#<msg_type>, which is the LCM channel name that Python LCMTransport subscribers use. The native binary publishes on these exact channels.
When stop() is called, the process receives SIGTERM. If it doesnโt exit within shutdown_timeout seconds (default 10), it gets SIGKILL.
Config
NativeModuleConfig extends ModuleConfig with subprocess fields:
Auto CLI arg generation
Any field you add to your config subclass automatically becomes a--name value CLI arg. Fields from NativeModuleConfig itself (like executable, extra_args, cwd) are not passed โ theyโre for Python-side orchestration only.
skip
Nonevalues are skipped.- Booleans are lowercased (
true/false). - Lists are comma-joined.
Excluding fields
If a config field shouldnโt be a CLI arg, add it tocli_exclude:
skip
Using with blueprints
Native modules work withautoconnect exactly like Python modules:
skip
autoconnect matches ports by (name, type), assigns LCM topics, and passes them to the native binary as CLI args. You can override transports as usual:
skip
Logging
NativeModule pipes subprocess stdout and stderr through structlog:- stdout is logged at
infolevel. - stderr is logged at
warninglevel.
JSON log format
If your native binary outputs structured JSON lines, setlog_format=LogFormat.JSON:
skip
event key becomes the log message:
Writing the C++ side
A header-only helper is provided atdimos/hardware/sensors/lidar/common/dimos_native_module.hpp:
It also includes
make_header() and time_from_seconds() for building ROS-compatible stamped messages.
Examples
For language interop examples (subscribing to DimOS topics from C++, TypeScript, Lua), see /examples/language-interop/.Livox Mid-360 Module
The Livox Mid-360 LiDAR driver is a complete example atdimos/hardware/sensors/lidar/livox/module.py:
skip
skip
Auto Building
Ifbuild_command is set in the module config, and the executable doesnโt exist when start() is called, NativeModule runs the build command automatically.
Build output is piped through structlog (stdout at info, stderr at warning).
skip
cwd is used for both the build command and the runtime subprocess. Relative paths are resolved against the directory of the Python file that defines the module
If the executable already exists, the build step is skipped entirely.
Faster builds via the Cachix substituter
CI pre-builds thecmu_nav native modules and pushes the Nix store paths to the dimensionalos Cachix cache. Opt in locally to skip cold compiles when the cache has them:
