Table of Contents

Sound sensor and spectrum in Foxglove

Import one-axiom-sound.json to display all 15 scalar attributes from a Robotical Sound Sensor and its latest nine frequency-band levels. The spectrum user script is embedded in the layout: importing the JSON also installs the script in that layout. No custom Foxglove extension is needed. The readable source is sound-spectrum.ts.

For coloured bars with frequency labels instead of the built-in spectrum Plot, install the optional Axiom Spectrum panel. It reads the same existing sample topic directly, without the user script or a separate ROS bands topic. You can add it alongside these scalar plots or import its spectrum-only layout. Host setup and acquisition are the same for both options.

Prepare the host and select the sensor

Complete the repository's host setup and the Foxglove bridge installation. Use the current host driver from main, rebuilt and sourced after updating. The sound sensor's firmware profile must advertise type RoboticalSoundSensor, its scalar attributes, and the nine-band bands attribute. The layout uses existing decoded data; it requires no firmware change for that profile.

Use board key axiom1 in config/boards.yaml, retaining your actual firmware id or USB/Wi-Fi connection settings. Keep automatic connection, reconnection and acquisition enabled as described in automatic startup. If a board file exists, merge these aliases into its existing entry and preserve its identity/connection settings. For a new file, the following complete single-board example selects axiom1 by firmware ID. Replace the illustrative ID with yours from /axiom_discovery/boards, save it as config/boards.yaml, and do not create a second entry for the same board:

discovery:
  include_unconfigured: false
axioms:
  axiom1:
    id: '001122334455'
    topic_aliases:
      sound/sample:    {type: RoboticalSoundSensor, source: sample}
      sound/splSlow:   {type: RoboticalSoundSensor, source: splSlow}
      sound/splFast:   {type: RoboticalSoundSensor, source: splFast}
      sound/rms:       {type: RoboticalSoundSensor, source: rms}
      sound/peak:      {type: RoboticalSoundSensor, source: peak}
      sound/crest:     {type: RoboticalSoundSensor, source: crest}
      sound/zcr:       {type: RoboticalSoundSensor, source: zcr}
      sound/centroid:  {type: RoboticalSoundSensor, source: centroid}
      sound/spread:    {type: RoboticalSoundSensor, source: spread}
      sound/flatness:  {type: RoboticalSoundSensor, source: flatness}
      sound/rolloff85: {type: RoboticalSoundSensor, source: rolloff85}
      sound/flux:      {type: RoboticalSoundSensor, source: flux}
      sound/f0:        {type: RoboticalSoundSensor, source: f0}
      sound/seq:       {type: RoboticalSoundSensor, source: seq}
      sound/status:    {type: RoboticalSoundSensor, source: status}
      sound/clipCount: {type: RoboticalSoundSensor, source: clipCount}

When editing an existing file, preserve its other boards and settings. Each alias binds only when there is exactly one matching online, fresh sensor with resolved metadata on that board. If several sound sensors are connected, add the same bus: '...' and address: '...' selectors to every sound alias. Copy their exact strings from /axiom1/devices; firmware addresses are hexadecimal and may encode the slot as well as the I2C address. The aliases and script must all select the same sensor. Canonical model/location topics remain available alongside these aliases.

No ROS driver code changes are part of this layout. If your installed driver predates scalar outputs, build and source the current checkout before starting it. In a prepared terminal at the repository root:

# Load the ROS environment and built workspace in this terminal.
source scripts/setup_env.sh
# Build the updated packages before restarting ROS processes.
colcon build --base-paths src --symlink-install
# Load the ROS environment and built workspace in this terminal.
source scripts/setup_env.sh

A layout or TypeScript-source update alone needs no ROS rebuild. Board alias configuration changes require restarting the affected driver.

Start the demo

The commands below run on the ROS host. If your board file is elsewhere, substitute its actual path in the launch command. Source scripts/setup_env.sh in each terminal; use the same ROS domain and middleware settings throughout.

Terminal 1, start the driver:

# Load the ROS environment and built workspace in this terminal.
source scripts/setup_env.sh
# Start discovery using the selected board configuration.
ros2 launch axiom_driver axiom.launch.py boards_file:=config/boards.yaml

Terminal 2, inspect the discovered device and its aliases:

# Load the ROS environment and built workspace in this terminal.
source scripts/setup_env.sh
# Inspect messages using the shown type and QoS.
ros2 topic echo /axiom1/devices std_msgs/msg/String --once \
  --qos-reliability reliable --qos-durability transient_local --qos-depth 1 --full-length
# Check the topic type and its publisher count.
ros2 topic info /axiom1/sound/splSlow
# Inspect messages using the shown type and QoS.
ros2 topic echo /axiom1/sound/splSlow --once --qos-reliability best_effort
# Inspect messages using the shown type and QoS.
ros2 topic echo /axiom1/sound/sample --once --qos-reliability best_effort --full-length

Expect splSlow to be std_msgs/msg/Float64 with one publisher and a numeric data field. The other 14 scalars also use Float64. sample is axiom_interfaces/msg/DeviceSample; its values_json contains a bands entry with unit: "dB", nine decoded numbers in value, and validity information. The sample header provides its timestamp and sensor frame. Inventory output_info records the scalar message types and units.

Automatic startup connects and enables acquisition. An echo or Foxglove subscription alone does not start it. To use a 5 Hz board publication rate for this demo, optionally run in Terminal 2:

# Send this discrete request and check the returned acknowledgement.
ros2 service call /axiom1/publish_data_subscription \
  axiom_interfaces/srv/PublishedDataSubscription '{rate_hz: 5.0}'

This changes delivery for the board, not the sound sensor's audio sampling or spectral-analysis settings.

Terminal 3, start the bridge:

# Load the ROS environment and built workspace in this terminal.
source scripts/setup_env.sh
# Expose this ROS session to the Foxglove viewer.
ros2 launch foxglove_bridge foxglove_bridge_launch.xml

Open Foxglove and choose Open connection → Foxglove WebSocket. Connect to ws://localhost:8765 when the viewer runs on the ROS host, or ws://ROS_HOST_IP:8765 from another computer. Use the ROS host's address, not the Axiom board's address. Connection/account prerequisites are covered in the bridge guide.

Import and use the layout

  1. Copy examples/foxglove/one-axiom-sound.json to the viewer computer if the checkout is on another host. The script is already inside that JSON.
  2. Open Layouts → Import from file…, select the JSON, then open the newly imported layout and save it as Axiom: sound sensor.
  3. In the User Scripts sidebar, check that Axiom sound spectrum is present and has no errors once the sample topic is available. The script subscribes to /axiom1/sound/sample and produces /foxglove_script/axiom1/sound/spectrum inside Foxglove.
  4. Speak or clap near the sensor and check that level plots and the spectrum update. Use different sounds to compare the distribution across bands; the display represents nine measured bands, not a full-resolution FFT.

The left column contains the guide, A-weighted levels, signal levels and sensor status/counters. The right column contains the spectrum, spectral frequencies, ratios and flux. Time plots use a sliding 30-second window and receipt time, because Float64 messages have no header. Their aligned axes do not establish hardware clock synchronisation.

Plot Attributes Units
Sound pressure levels splSlow, splFast dBA
Signal levels rms, peak, crest dB
Spectral frequencies centroid, spread, rolloff85, f0 Hz
Spectral ratios zcr, flatness Dimensionless, 0–1
Spectral flux flux Firmware value, 0–255
Sequence, status and clipping seq, status, clipCount Firmware values, 0–255
Latest sound spectrum bands from the sample JSON dB against band index

The spectrum uses Foxglove's Array index mode and the expression /foxglove_script/axiom1/sound/spectrum.bands_db[:]. Each new sample replaces the displayed spectrum. The frequency key is also shown in the guide panel:

Index 0 1 2 3 4 5 6 7 8
Hz 31.5 63 125 250 500 1000 2000 4000 8000

These positions follow the current sound descriptor's bands.el labels. The script also exposes that order numerically as frequency_hz and retains the original sample header. It does not apply the firmware's offsets/divisors again or turn band levels into A-weighted readings. Calibration and the dB reference remain properties of the sensor. Status is shown as its numeric bitmask; sequence and clipping fields retain the firmware's 8-bit representation.

Invalid/nonfinite/null band readings become NaN gaps, including a whole invalid frame. Malformed JSON, a missing band attribute, a non-nine-band profile, a different unit, or another sensor type produces no script output. The script keeps no history or cached readings. Foxglove can still retain the last displayed frame when input stops or is skipped; check publisher count and inventory freshness before treating it as a current reading.

The derived topic exists only in Foxglove. It is not a ROS publisher and will not appear in ros2 topic list. The original sample retains everything needed to reproduce the spectrum from a recording. This sensor output contains features and band levels, so it cannot provide waveform playback in the Audio panel.

Another board namespace or existing layout

The supplied aliases avoid changing scripts when the sensor moves between slots. For a board namespace other than axiom1, replace every /axiom1/ occurrence in the JSON before import, including inside the embedded source and derived topic paths. This also changes /foxglove_script/axiom1/.... Keep panel expressions, the script's inputs, its Input<...> type and output topic consistent. Board configuration must use the matching namespace.

To add the spectrum to another layout, create a User Script and paste sound-spectrum.ts, adjusting both input-topic occurrences and the output topic as needed. Add a Plot panel with X-axis value type → Array index and the derived topic's .bands_db[:] field as Y. Editing the standalone file does not update an imported layout; edit the script in Foxglove or update the JSON's embedded sourceCode, then reimport. Replacing a JSON file on disk does not change a layout already saved in Foxglove.

Custom Axiom Spectrum panel

For frequency-labelled coloured bars, install the optional custom panel instead of relying on the built-in Plot's array display. It reads the same existing axiom_interfaces/msg/DeviceSample directly; it needs neither the embedded user script nor a new ROS bands topic. Add it beside the scalar plots or use the supplied spectrum-only layout.

Axiom Spectrum custom panel with synthetic sound data

Illustration of the extension using synthetic data; it is not a physical sound-sensor capture.

Build and install the extension

Use Node.js 24 or later on the computer building the extension. Building can happen on another computer; only the finished .foxe file needs to reach the Foxglove viewer. The ROS driver needs no Node runtime and no rebuild for an extension-only update. From the repository root:

# Enter the extension project inside the checkout.
cd examples/foxglove/spectrum-panel
# Confirm a suitable Node.js version before installing dependencies.
node --version
# Install the locked extension build dependencies.
npm ci
# Check the TypeScript interfaces.
npm run typecheck
# Run the panel's unit tests.
npm test
# Build and package the installable Foxglove extension.
npm run package
# Find the packaged extension to install or copy to the viewer computer.
ls dist/*.foxe

The current package is dist/robotical.axiom-spectrum-0.1.1.foxe. On the viewer computer, open Foxglove's user-profile menu, choose Extensions → Install local extension… and select that file, or drag the .foxe file into an open visualisation. A saved JSON layout does not install its required extension. After changing extension source, rebuild/repackage and install the updated .foxe; changing files in a checkout does not update an already installed extension.

Use the panel and layout

  1. Connect Foxglove to the running ROS bridge as above.
  2. Choose Add panel → Axiom Spectrum.
  3. In the panel's Source dropdown, select the sound sensor's actual canonical sample topic or the /axiom1/sound/sample alias. The alias is a convenience default, not a required generated topic.
  4. Set Spectrum scale minimum/maximum dB for the display; the default is −20 to 100 dB.
  5. Save the layout. Source and scale are stored with it.

For a spectrum-only view, import one-axiom-spectrum.json from examples/foxglove/spectrum-panel/ on the viewer computer, then open the imported layout. It initially selects the sound alias above. Choose a different source when using another board or sensor. Install the extension first so Foxglove can resolve its panel type.

Bars show already-decoded dB values, including valid negative readings. They clip visually at the selected scale limits while numeric labels/tooltips retain the actual values. Frequency labels come from bands.meta.el with bands.meta.eu: Hz, up to 64 bands; categories have equal visual spacing. The colours follow Axiom's teal/cream/blue/yellow/orange palette and Foxglove's light/dark theme. They distinguish frequencies, not calibrated noise-safety thresholds.

Invalid or nonfinite bins have no bar and show a dash. Malformed frames clear the old chart and explain the issue. Seeking playback or changing source clears previous readings. Samples more than five seconds behind the viewer timeline are dimmed as stale; paused/disconnected timelines can retain their last display. Use fresh samples and online inventory to confirm the sensor is live. The panel subscribes only and sends no device commands.

Try labelled dummy data

After building dependencies above, from the extension directory:

# Generate the bundled 45-second synthetic MCAP recording.
npm run demo

Open dist/axiom-spectrum-dummy.mcap as a local file in Foxglove, add the installed panel or import its layout, and play. This needs no ROS bridge or hardware. The recording includes speech-like levels, a bass-heavy spectrum, invalid bins/frames, malformed JSON and recovery. It is an explicitly synthetic UI fixture, not captured audio or a hardware calibration.

Official reference: installing local Foxglove extensions.

Troubleshoot and finish

Symptom Check
No sound aliases Board namespace, type RoboticalSoundSensor, sensor freshness, alias ambiguity and identical bus/address selectors
Generic sample exists, scalar topics absent Build/source/restart a driver containing scalar outputs
ROS echoes work, plots are empty Bridge host/domain, imported layout and the /axiom1/sound/... paths
Scalars update, spectrum does not sound/sample alias, script errors, nine bands.value entries, unit: "dB", validity and sensor type
Script reports an unknown input topic/type Connect the data source and ensure the sample publisher is discovered; check both input-topic strings in the script
Derived topic absent from ROS CLI Expected: it is local to Foxglove
Unexpected units or clipped trace Inspect output_info; dBA/dB are separate, ratio/counter/flux axes are fixed, other vertical scales are automatic
Frozen spectrum Check fresh samples and online inventory; Foxglove can retain the last frame after disconnect or rejected input
Custom panel is missing or unknown Install the packaged .foxe on the viewer before opening its layout; importing JSON does not install it.
Old script or extra panels Reimport and open the new layout; saved layouts do not follow file changes

Stop the bridge in Terminal 3. In Terminal 2, stop publication and disconnect:

# Send this discrete request and check the returned acknowledgement.
ros2 service call /axiom1/publish_data_subscription \
  axiom_interfaces/srv/PublishedDataSubscription '{rate_hz: 0.0}'
# Send this discrete request and check the returned acknowledgement.
ros2 service call /axiom1/disconnect axiom_interfaces/srv/Disconnect '{}'

Then stop the driver in Terminal 1.

Return to Foxglove dashboards or Axiom with ROS 2.