This page is a practical, copy/paste guide to validate the perception server, audio device routing, generated WAV files, and ROS 2 service interfaces from a terminal.
In one terminal, build, source, and launch the server:
export OPENAI_API_KEY=
export HUGGINGFACE_API_KEY=
source install/setup.bash
ros2 launch fp_perception server.launch.pyIn a second terminal, source the workspace:
cd ~/colcon_ws
source /opt/ros/jazzy/setup.bash
source install/setup.bashThe checked-in config currently has run_tests: false. If you want launch-time plugin self-tests, set run_tests: true in src/fp_perception/fp_perception/config/config.yaml, then rebuild fp_perception and re-source the workspace before relaunching.
Optional sanity checks:
ros2 service list | grep perception
ros2 interface show fp_perception_msgs/srv/PerceptionTranscribe
ros2 interface show fp_perception_msgs/srv/PerceptionSentiment
ros2 interface show fp_perception_msgs/srv/PerceptionSpeech
ros2 interface show fp_perception_msgs/srv/PerceptionImageAnalysisWhen the server starts, first verify that the microphone and speaker select the intended PortAudio device by name.
Expected log shape:
PortAudio device count: ...
PortAudio device 5 ['HD-Audio Generic: ALC285 Analog (hw:2,0)'] ... max_input=2 max_output=2 ...
Resolved microphone device_name 'ALC285 Analog' to device_id 5.
Assigned driver device: PortAudio device 5 ['HD-Audio Generic: ALC285 Analog (hw:2,0)'] ...
Resolved speaker device_name 'ALC285 Analog' to device_id 5.
Assigned driver device: PortAudio device 5 ['HD-Audio Generic: ALC285 Analog (hw:2,0)'] ...
Notes:
- ALSA/JACK warnings during PortAudio device enumeration are common in containers. They are not a failure if the driver later resolves the expected device and starts successfully.
- The
hw:2,0part tells you the ALSA hardware route. Use that asplughw:2,0for directaplaychecks. - The numeric PortAudio
device_idmay change between machines or boots; the config should preferdevice_name.
With run_tests: true, the launch log should also show the built-in checks:
Testing microphone driver...
Microphone test signal stats: samples=240000 ...
Audio data written to file: test/mic_test.wav
Testing speaker driver...
Audio data queued to stream: int16_48000_2
Testing transcription driver...
Transcription result: Hello
Testing speech synthesis driver...
Speech synthesis result received and saved to test/speech.wav
Testing sentiment driver...
Analysis results with sentiment: POSITIVE ...
Testing image analysis driver...
Image analysis result: ...
After the startup tests finish, verify the generated WAV files directly through ALSA from another terminal:
cd ~/colcon_ws
# find the device name and route for your speaker from the PortAudio log, e.g. 'ALC285 Analog' with 'hw:3,0' route
python3 src/fp_perception/fp_perception_driver_audio/find_devices.py
# Confirm the microphone test recording is audible.
aplay -D plughw:2,0 test/mic_test.wav
# Confirm the speech synthesis output file is audible.
aplay -D plughw:2,0 test/speech.wavExpected output looks like:
Playing WAVE 'test/mic_test.wav' : Signed 16 bit Little Endian, Rate 48000 Hz, Mono
Playing WAVE 'test/speech.wav' : Signed 16 bit Little Endian, Rate 24000 Hz, Mono
The microphone publisher should stream captured chunks on the configured audio topic.
ros2 topic hz /perception/microphone
ros2 topic echo /perception/microphone --onceYou should see a nonzero publish rate and messages with populated samples. The default config publishes at interface.audio_input.frequency, typically 10 Hz.
Default service name is perception/transcription.
This reads from the server's public audio buffer for audio_request_window seconds and transcribes it. Speak into the microphone immediately before or during the call.
It only works after the microphone driver has initialized the public audio buffer; otherwise the service returns Device audio not available: public audio buffer not initialized.
Latest-buffer smoke test:
source install/setup.bash
ros2 service call /perception/transcription fp_perception_msgs/srv/PerceptionTranscribe "{
audio: {
header: {stamp: {sec: 0, nanosec: 0}, frame_id: ''},
sample_rate: 0,
channels: 0,
chunk_size: 0,
chunk_count: 0,
samples: []
},
use_device_audio: true,
audio_request_window: 5
}"Timestamp-window smoke test:
STAMP_SEC=$(date +%s)
STAMP_NSEC=$(date +%N)
echo "Speak for the next 5 seconds..."
sleep 5
source install/setup.bash
ros2 service call /perception/transcription fp_perception_msgs/srv/PerceptionTranscribe "{
audio: {
header: {stamp: {sec: ${STAMP_SEC}, nanosec: ${STAMP_NSEC}}, frame_id: ''},
sample_rate: 0,
channels: 0,
chunk_size: 0,
chunk_count: 0,
samples: []
},
use_device_audio: true,
audio_request_window: 5
}"-
audiois ignored whenuse_device_audio: true, but it must still be present to satisfy the request type. -
audio_request_windowmust be ≤ the configured server ring buffer duration (interface.audio_input.audio_retention_window). -
If the request header stamp is zero, the server uses the latest buffered audio window.
-
If the timestamped window is only partially available, the server returns the available overlap and logs a warning.
-
If the timestamped window has no overlap with the ring buffer, the server logs a warning and falls back to the latest buffered audio instead of failing the node.
Default service name is perception/sentiment_analysis.
If use_device_audio: true, the server will:
- wait until the public audio buffer has
audio_request_windowseconds of new audio - transcribe it
- run sentiment on the transcribed text
The response includes analyzed_text, which is the exact text that was sent into sentiment analysis. This is useful for debugging device-audio runs where the sentiment label looks plausible but the upstream transcription may be wrong.
Latest-buffer smoke test:
source install/setup.bash
ros2 service call /perception/sentiment_analysis fp_perception_msgs/srv/PerceptionSentiment "{
header: {stamp: {sec: 0, nanosec: 0}, frame_id: ''},
text: '',
use_device_audio: true,
audio_request_window: 5
}"Timestamp-window smoke test:
STAMP_SEC=$(date +%s)
STAMP_NSEC=$(date +%N)
echo "Speak with a positive or negative phrase for the next 5 seconds..."
sleep 5
source install/setup.bash
ros2 service call /perception/sentiment_analysis fp_perception_msgs/srv/PerceptionSentiment "{
header: {stamp: {sec: ${STAMP_SEC}, nanosec: ${STAMP_NSEC}}, frame_id: ''},
text: '',
use_device_audio: true,
audio_request_window: 5
}"Default service name is perception/speech.
If use_device_audio: true, the server will synthesize speech and play it through the configured speaker driver.
Device-playback smoke test:
source install/setup.bash
ros2 service call /perception/speech fp_perception_msgs/srv/PerceptionSpeech "{
input: {
header: {stamp: {sec: 0, nanosec: 0}, frame_id: ''},
text: 'Hello from perception',
voice: '',
instructions: ''
},
use_device_audio: true
}"With use_device_audio: true, the main success signal is audible playback through the configured speaker. If playback is unclear, inspect the startup-generated test/speech.wav and replay it with aplay as shown above.
Expected outcome for these three service smoke tests:
- transcription returns
success: trueand a non-emptytranscription - sentiment returns a label such as
POSITIVEorNEGATIVEwith a confidence score, plusanalyzed_textfor debugging - speech returns
success: trueand audible playback through the configured speaker
- If device-audio calls time out, ensure the microphone driver is enabled and producing samples.
- If transcription returns
Device audio not available: public audio buffer not initialized, verify the microphone driver is running andros2 topic hz /perception/microphoneshows samples before calling the service. - If you request a longer
audio_request_windowthan the server buffer duration, increaseinterface.audio_input.audio_retention_window. - If you don’t hear speech output with
use_device_audio: true, ensure the speaker driver is enabled and the container can access an output device. - If
aplayworks but the speaker driver does not, compare theaplay -D plughw:X,Yroute against thehw:X,Yshown in the PortAudio resolved-device log. - If
test/mic_test.wavis silent, confirm the microphone input source is selected in the host audio settings and rerunros2 launch fp_perception server.launch.pywithrun_tests: true.
The system is tested with Realsense D435 Camera. So the current devcontainer includes the realsense2_camera ROS package and a launch file to start the camera node.
ros2 launch realsense2_camera rs_launch.pyDefault service name is perception/image_analysis.
This is the easiest way to test from the CLI because you don't need to embed image bytes into the request.
ros2 service call /perception/image_analysis fp_perception_msgs/srv/PerceptionImageAnalysis "{
header: {stamp: {sec: 0, nanosec: 0}, frame_id: ''},
image: {
header: {stamp: {sec: 0, nanosec: 0}, frame_id: ''},
height: 0,
width: 0,
encoding: '',
is_bigendian: 0,
step: 0,
data: []
},
prompt: 'Describe the most important objects in this image',
use_device_vision: true
}"Notes:
- Requires
interface.image_analysis.provide_service: trueand at least one enabled vision driver such asuse_ros_vision_driver: trueoruse_non_ros_vision_driver: truein config. imageis ignored whenuse_device_vision: true, but must still be present to satisfy the request type.- The checked-in config enables the ROS vision driver by default and reads from
driver.vision.DefaultDriver.topic, currently/camera/camera/color/image_raw. - After editing
src/fp_perception/fp_perception/config/config.yaml, rebuild withcolcon build --packages-select fp_perceptionso the installed launch-time config is updated.