Python SDK and command-line tool for NXS sensor co-processors. The library
covers personality authoring (a sensor personality is a Python class compiled to VM
bytecode) and integration (open_client(), decoded sample streams, parameter
access). The nxs CLI is built on the same public imports: personality compile and
upload, VM control, sample streaming, personality store management, and firmware
update over I2C, serial, or CAN.
from nxs import open_client
client = open_client("i2c", bus="/dev/i2c-2", address=0x30)
for sample in client.iter_samples():
handle(sample.values)Every YAML the tool reads (the suite manifest, unit personality descriptors, camera
descriptor packs) ships with a JSON Schema under nxs/schemas/, enforced by
editors through the files' yaml-language-server modelines and by CI on the
shipped personalities and packs. nxs status validates the suite manifest against
its schema before the laws run; the pack, descriptor, blob, and topology
loaders parse strictly (an unknown key or a malformed value is an error naming
the file). Every read verb also emits its document as JSON (nxs status --json,
nxs cam1 status --json, nxs cam1 caps --json), each conforming to the shipped
surface contract. Shape is the schema's job; the timing and feasibility laws
stay in code and answer through nxs status: a refusal is one fact line, then
the lawful alternatives under it.
nxs mcp serves the same verbs to an AI agent over the Model Context
Protocol (install the mcp extra). Hand-written YAML, the nxs tune personality
panel, and the MCP tools edit one declared config through the same laws; MCP
also operates the hardware, and a refused request carries its refusal. See
the MCP Tool Reference in the released documentation set.
Requires Python ≥ 3.10. The base install covers every wire, I2C, Cyphal/serial
and Cyphal/CAN, through the bundled library; the cyphal extra adds pyserial for
the serial port autodetect. The Cyphal DSDL ships vendored in the wheel for yakut
and yukon, so they need no repo checkout and no public_regulated_data_types clone.
uv, native with direct device access; the only option on macOS, where Docker cannot reach USB:
uv tool install 'aliensense-nxs[cyphal]' # omit [cyphal] for an I2C-only install
pip, from a release wheel:
python3 -m pip install 'aliensense_nxs-<version>-py3-none-manylinux_2_35_x86_64.whl[cyphal]'
Container, for fleet, hermetic, or locked-down hosts; ships no ROS. See
Dockerfile:
docker build --build-arg EXTRA='[cyphal]' -t nxs:cyphal .
docker run --rm --device /dev/ttyUSB0 nxs:cyphal --transport cyphal-serial \
--port /dev/ttyUSB0 --format json
nxs switch installs the platform's udev bus aliases and the tab completion
at /etc/bash_completion.d/nxs (the writes under /etc go through sudo);
zsh sources the same file after autoload -U bashcompinit && bashcompinit.
From a dev host to the target. The target keeps exactly one nxs, the
installed wheel. Never copy the source tree to the target and never
pip install -e there: a second copy shadows the wheel and personality
edits stop reaching the compiler.
1. Build the wheel (dev host, in this directory):
python3 -m pip install build
python3 -m build --wheel
The wheel lands in dist/. A standalone checkout carries the vendored
DSDL and builds as-is; in a source checkout, run ../scripts/vendor-dsdl.sh
first so the bundle is current.
2. Copy to the Jetson and install:
scp dist/aliensense_nxs-<version>-py3-none-manylinux_2_35_aarch64.whl <user>@<jetson>:/tmp/
ssh <user>@<jetson> "python3 -m pip install --user --force-reinstall 'aliensense-nxs[cyphal] @ file:///tmp/aliensense_nxs-<version>-py3-none-manylinux_2_35_aarch64.whl'"
--force-reinstall because dev wheels reuse the version number. The
PEP 508 name[extra] @ file:// form is required; pip does not apply
extras to a bare wheel path. For an I2C-only target install the bare
path with no extras: pip install --user --force-reinstall /tmp/aliensense_nxs-<version>-py3-none-manylinux_2_35_aarch64.whl.
3. Verify the single installed copy:
ssh <user>@<jetson> "nxs --version && python3 -c 'import nxs, os; print(os.path.dirname(nxs.__file__))'"
The printed path must be under ~/.local/lib/python3.*/site-packages.
Any other path (a home-directory source tree, an editable install) is
a shadowing copy: python3 -m pip uninstall nxs until none remains,
then reinstall the wheel.
Iterating on a personality needs no redeploy. Compile on the dev host, ship the image:
nxs upload fxos8700 --config bus=spi -o fxos8700.nxs # dev host: compile only
scp fxos8700.nxs <user>@<jetson>:
ssh <user>@<jetson> nxs upload fxos8700.nxs
An image whose format predates the installed tool is refused together with its rebuild command.
Then bring the sensor up with the Quick start commands below
(probe, upload iam20680 --param sample_rate=250, stream,
store save 0).
nxs probe
nxs upload iam20680 --param sample_rate=250
nxs stream
nxs store save 0
nxs push-fw zephyr.signed.bin
The default transport is I2C on a Linux host (Jetson/SBC) and
cyphal-serial on macOS or Windows, which have no /dev/i2c-*. $NXS_TRANSPORT
overrides the choice (i2c / cyphal-serial / cyphal-can); $NXS_BUS
sets the I²C bus and $NXS_PORT the serial port or SocketCAN interface,
or pass -t, -b, -p per command. nxs --help lists every command.
nxs ros2 maps the device-served field descriptors onto standard ROS 2
messages by semantic, with no per-sensor configuration. With a suite
manifest it bridges every unit under nxs/<unit>/...; with --unit or
explicit transport flags it bridges one device. Install the wheel with the
ros2 extra (aliensense-nxs[cyphal,ros2]) into a sourced ROS 2 environment (rclpy
comes from the distribution; only this verb imports it) and run:
nxs ros2 --plan # print the topic plan, no ROS required
nxs ros2 # publish; ros2 topic echo nxs/<unit>/imu
The topic table, timestamp and covariance conventions are in the integration manual's ROS 2 section.
nxs cam brings up and drives camera chains (an image sensor behind a
serializer behind a deserializer link) from descriptor packs:
runtime-discovered bundles that describe the hardware (register maps,
modes, limits, flow assembly). The tool is hardware-agnostic; the pack is
the camera's self-description.
Commands address the hardware node first: the port, then optionally one link:
nxs cam0 status # presence of every device on every link, then the readbacks
nxs cam0 caps # modes and knobs: feature discovery
nxs cam0 on # bring the port's links up (constructs as needed)
nxs cam0 A set exposure 4ms # a knob on one link, judged by the laws; a refused
# value names the lawful alternatives
nxs cam0 capture --frames 60 # delivery at the declared rate, headless
nxs cam0 off
cam0 is the connector's stable name (nxs switch installs the platform's
udev aliases); A/B are its links. The ports come from the suite
manifest's ports: section (which hub on which bus, which chains behind
it, the declared mode and sync); a host without a manifest follows the
booted device tree and the pack's default topology, and nxs tune --freeze --ports writes what it found. The pack ships inside the wheel (a development host names its own with
nxs --experimental and $NXS_CAM_DESCRIPTORS); without a pack for the
described deserializer, the tool says so and where it searched. Authoring a pack for
your own camera is the Camera Personality Reference (docs/nxs-camera-personalities.md
in a standalone checkout), shipped with the product documentation. The
guide set on the documentation site
(https://aliensense.github.io/nxs-docs/guides/) walks from a fresh Jetson
to a dual camera.
The full interface contract (register map, procedures, framing, limits) is the NXS host interface specification, shipped with the product documentation.
The Cyphal DSDL is vendored into the wheel but not committed. It is assembled
from the firmware's dsdl/aliensense types and the full uavcan namespace of
OpenCyphal's public_regulated_data_types, which yakut and yukon need beside
the vendor types (uavcan.node, uavcan.file, and their dependencies).
A standalone checkout carries the bundle in nxs/dsdl/ already, assembled
by the release pipeline before building the wheel. Run that step by hand before building the cyphal
container or running the cyphal tests from a fresh source checkout:
../scripts/vendor-dsdl.sh # needs `west update`, or PRDT=/path