Scripts Reference - Forestry-Robotics-UC/fruc_dataset_apparatus GitHub Wiki
This document provides detailed reference information for all shell scripts and utilities in the FRUC Dataset Apparatus system.
Location: ./launch-system.sh
Purpose: Main interactive launch script for the system
Platform: Works with KDE Plasma (kdialog)
./launch-system.shlaunch-system.sh
├── Generate Recording Name
│ ├── Read random adjectives from .names_01 and .names_02
│ ├── Generate timestamp (YYYY-MM-DD_HH-MM-SS)
│ └── Present kdialog for user confirmation/modification
│
├── Select Topics for Recording
│ ├── Present kdialog checklist
│ ├── Pre-select common topics (on/off)
│ └── Return selected topics as space-separated list
│
├── Select Bag Splitting Strategy
│ ├── Option 1: Split by size (GB)
│ │ └── Convert GB to bytes: GB * 1073741824
│ └── Option 2: Split by duration (seconds)
│
├── Select Compression Profile
│ ├── none - no compression
│ ├── fastwrite - fast compression
│ ├── zstd_fast - ZSTD fast mode
│ └── zstd_small - ZSTD maximum compression
│
├── Confirmation Dialog
│ ├── Display all settings
│ └── User confirms to start
│
└── Launch System
├── Start Docker containers (compose up)
├── Execute recording command
├── Monitor recording progress
└── Save bag information to info.txt
| Variable | Description |
|---|---|
SCRIPT_DIR |
Directory containing the script |
name1, name2
|
Random names from .names_* files |
default_name |
Auto-generated recording name |
recording_name |
User-selected recording name |
topics |
Selected ROS topics for recording |
recording_limit_option |
size OR duration |
bag_limit_flag |
-b bytes OR -d seconds
|
storage_profile |
Compression profile |
- 0: Recording completed successfully
- 1: User cancelled or error occurred
$ ./launch-system.sh
# Dialog 1: Name prompt
[kdialog] "Enter a name for the recording:"
[default] "2026-02-20_15-30-45__excited_maman-brigitte"
# Dialog 2: Topics checklist (selected items marked with *)
* /imu/data - Xsens IMU Data
* /imu/mag - Xsens IMU Magnetometer
* /heading - Heading
* /tf_static - Static TF
...
# Dialog 3: Splitting strategy
[kdialog] "Size (in GB)" selected -> "5"
-> bag_limit_flag="-b 5368709120"
# Dialog 4: Compression
[kdialog] Storage profile: "zstd_fast" selected
# Dialog 5: Confirmation
[kdialog] Start recording named: '2026-02-20_15-30-45__excited_maman-brigitte'
# Start recording...
Recording Started!Located starting at line 25 in the script:
topics=$(kdialog --checklist "Topics to record" \
/imu/data "Xsens IMU Data" on \
/imu/mag "Xsens IMU Magnetometer" on \
/heading "Heading" on \
/tf_static "Static TF" on \
/tf "TF" on \
/robot_description "Robot Description" on \
/ouster/lidar_packets "Ouster LiDAR Packets" on \
/ouster/imu_packets "Ouster IMU Packets" on \
/ouster/metadata "Ouster Metadata" on \
...and more
)Add a new topic to recording dialog:
# Find the kdialog --checklist section and add:
/new_sensor/data "New Sensor Data" on \Change default compression:
# Find this line (around line 81):
storage_profile=$(kdialog --combobox "Select storage preset profile:" none fastwrite zstd_fast zstd_small --default none)
# Change --default to:
--default zstd_fastChange default recording size:
# Find this line (around line 72):
bag_limit_value=$(kdialog --inputbox "Enter max bag size (GB):" "5")
# Change "5" to different default:
"10" # For 10 GB defaultLocation: ./gnome-launch-system.sh
Purpose: Launch script for GNOME desktop environments
Platform: GNOME (Freedesktop standard)
./gnome-launch-system.sh- Uses
zenityinstead ofkdialogfor GUI dialogs - GNOME native dialog styling
- Same functionality and flow as
launch-system.sh
| Component | Command |
|---|---|
| Text input | zenity --entry |
| Checklist | zenity --list --checklist |
| Radio button | zenity --list --radiolist |
| Dropdown | zenity --list |
| Confirmation | zenity --question |
| Info message | zenity --info |
| Error message | zenity --error |
Location: ./stop-system.sh
Purpose: Gracefully stop the entire recording system
Platform: All Linux (Docker on desktop, Podman on SteamDeck)
./stop-system.shThe script uses Docker on desktop systems and Podman on SteamDeck. Example with Podman (SteamDeck):
#!/bin/bash
# 1. Send SIGINT to recording process (allows graceful shutdown)
podman exec recording pkill -SIGINT -f hector_recorder
# 2. Stop containers in dependency order
podman stop recording # Stop recording first (in-flight writes)
podman stop monitoring # Then monitoring
podman stop xsens # Sensor containers
podman stop emlid
podman stop foxglove-bridge
podman stop realsense
podman stop ousterFor Desktop (Docker), simply replace podman with docker:
docker exec recording pkill -SIGINT -f hector_recorder
docker stop recording
# ... etcThe order is important:
- Recording - Must finish writing current data
- Monitoring - Stops collecting metrics
- Sensor Containers - Stop publishing data
- Visualization - Stop the UI bridge
Quick stop (all at once):
./stop-system.shStop specific container (example for SteamDeck with Podman):
podman stop ros2-apparatus-realsense
# Or on Desktop with Docker:
docker stop ros2-apparatus-realsenseForce stop (ungraceful):
# SteamDeck (Podman):
podman kill ros2-apparatus-recording
# Desktop (Docker):
docker kill ros2-apparatus-recordingLocation: ./gnome-stop-system.sh
Purpose: GNOME variant of stop script
Usage: ./gnome-stop-system.sh
Same functionality as stop-system.sh with GNOME notification support. Automatically uses Docker on desktop or Podman on SteamDeck.
Location: ./tty-perms.sh
Purpose: Set serial port permissions for sensor access
Platform: Linux
sudo bash tty-perms.sh
# Or manually:
sudo chmod 666 /dev/ttyUSB0 # Xsens
sudo chmod 666 /dev/ttyUSB1 # Other serial devices (if present)#!/bin/bash
# Sets read/write permissions on USB serial devices
# Allows non-root access to /dev/ttyUSB* without sudo
chmod 666 /dev/ttyUSB* # All USB serial devices
chmod 666 /dev/ttyACM* # USB ACM (modem) devicesBy default, /dev/ttyUSB* devices have restricted permissions (rw-rw----) that only root or the dialout group can access. This script allows any user to access them.
# More permanent solution
sudo usermod -aG dialout $USER
# Then log out and back in
exitLocation: ./docker/pre_recording_test.sh
Purpose: Automated diagnostic test for sensor connectivity
Platform: Requires Docker/Podman
cd docker/
bash pre_recording_test.shpre_recording_test.sh
├── Generate test recording name
│ └── Format: YYYY-MM-DD_HH-MM-SS__pre_recording_check
│
├── Define test parameters
│ ├── Default topics: /imu/data, /ouster/*, /camera/*, /fix
│ ├── Split interval: 60 seconds per bag
│ ├── Total duration: 420 seconds (7 minutes)
│ └── Storage profile: zstd_fast
│
├── Start Docker containers
│ └── podman-compose up -d
│
├── Begin recording (background)
│ ├── Record for full duration
│ ├── Split bags every 60 seconds
│ └── Store in /rosbags/
│
├── Monitor recording progress
│ └── Display elapsed time
│
├── Stop recording gracefully
│ └── Collect final metrics
│
├── Analyze results
│ ├── List recorded bags
│ ├── Run: ros2 bag info
│ ├── Display topic frequencies
│ └── Check for errors
│
└── Generate diagnostic report
└── Save to console and logs
===============================================
Starting Pre-Recording System Check
Bag Name: 2026-03-30_10-15-22__pre_recording_check
Duration: 420s total
Split every: 60s
Storage: zstd_fast
Topics: /imu/data /ouster/lidar_packets ...
===============================================
Launching containers...
[OK] All containers started
Starting ROS2 bag recording...
Recording for 420s...
[████████░░] 50% complete (210s / 420s)
Recording finished.
Bag info and topic frequencies:
/imu/data: 200.0 Hz ✓
/ouster/lidar_packets: 10.0 Hz ✓
/camera/color/image_raw: 30.0 Hz ✓
...
Pre-recording check complete.
Before first major recording:
# Full system diagnostic
cd docker/
bash pre_recording_test.sh
# Wait for completion (~8 minutes)Quick connectivity check (modify script):
# Edit pre_recording_test.sh
# Change: total_duration=420 → total_duration=60
bash pre_recording_test.shTest after adding new sensor:
# Add new topic to `topics` variable
# Run pre_recording_test.sh
# Verify new sensor appears in outputThese scripts are called by Docker containers to start sensor drivers.
Purpose: Initialize Xsens IMU driver
Called by: Dockerfile.xsens
#!/bin/bash
set -e
# Source ROS2 setup
source /docker_ws/install/setup.bash
# Patch driver with shared scripts (custom modifications)
cat /ros2_ws/shared/scripts/mtdevice.py > \
/docker_ws/src/norlab_xsens_driver/xsens_driver/mtdevice.py
cat /ros2_ws/shared/scripts/mtnode.py > \
/docker_ws/src/norlab_xsens_driver/xsens_driver/mtnode.py
# Launch Xsens driver with parameters
ros2 launch xsens_driver xsens_driver.launch.xml \
baudrate:=460800 \ # Serial communication speed
device:=/dev/ttyUSB0 \ # Serial port
frame_id:=xsens_imu # ROS TF frameParameters:
-
baudrate: 460800 (standard for Xsens) -
device: Serial port (usually /dev/ttyUSB0) -
frame_id: ROS coordinate frame name
Purpose: Initialize RealSense camera driver
Called by: Dockerfile.realsense
Launches RealSense ROS2 driver with custom configuration.
# Sources workspace and executes:
ros2 launch rslaunch rs_launch.py \
use_sim_time:=FalsePurpose: Initialize Emlid GNSS driver
Called by: Dockerfile.emlid
Launches nmea_navsat_driver for GPS/GNSS positioning.
Location: docker/docker_shared/scripts/
Purpose: Xsens driver communication libraries
Language: Python 3
mtdevice.py:
- Low-level serial communication with Xsens IMU
- Device configuration and calibration
- Packet parsing and formatting
- Baud rate and protocol handling
mtnode.py:
- ROS2 node wrapper for Xsens device
- Topic publishing and subscription
- Frame ID management
- Data timestamp synchronization
Location: docker/docker_shared/scripts/
Purpose: ROS2 launch configuration for Ouster LiDAR
# Key configuration options:
hostname = "169.254.49.182" # Ouster IP address
lidar_port = 8308 # UDP lidar data port
imu_port = 8309 # UDP IMU data port
udp_dest = "<host_ip>" # Where to send UDP packetsUsage: Called by Ouster container via docker-compose
Location: docker/docker_build/entrypoint.sh
Purpose: Container initialization script
#!/bin/bash
set -e
# Source ROS2 environment
source /opt/ros/jazzy/setup.bash
# If workspace exists, source it
if [ -d /docker_ws/src ]; then
cd /docker_ws
source /docker_ws/install/setup.bash
fi
# Execute the command passed to container
exec "$@"Execution Flow:
- Set error exit on any failure
- Source system ROS2 setup
- Source container-built workspace (if exists)
- Execute the command specified in Docker CMD
This ensures all ROS2 environment variables are set before sensor drivers start.
Example 1: Add new topic to recording
Edit launch-system.sh:
# Find the kdialog --checklist section around line 25
topics=$(kdialog --checklist "Topics to record" \
/imu/data "Xsens IMU Data" on \
# ... existing topics ...
/new_topic/data "New Topic" on \ # Add this line
/fix "GPS" on)Example 2: Change default compression
Edit launch-system.sh:
# Find around line 91
--default none)
# Change to:
--default zstd_fast)Create auto_record.sh:
#!/bin/bash
RECORDING_DIR="rosbags/auto_$(date +%Y-%m-%d)"
mkdir -p "$RECORDING_DIR"
cd docker/
docker-compose up -d
sleep 5 # Wait for containers to initialize
podman run --rm -it --name recording --network docker_ros2-net \
-v ../rosbags:/rosbags \
localhost/docker_recording bash -c \
"ros2 run hector_recorder record -d 3600 \
--topics /imu/data /ouster/lidar_packets \
-o /rosbags/$RECORDING_DIR/auto_recording"
./stop-system.sh#!/bin/bash
# Process all recordings in parallel
for recording_dir in rosbags/*/; do
recording_name=$(basename "$recording_dir")
# Extract point cloud
ros2 bag convert -i "$recording_dir" \
--topics /ouster/points \
-f csv -o "pcd/${recording_name}.pcd" &
# Create info file
ros2 bag info "$recording_dir" \
> "${recording_dir}/analysis.txt" &
done
wait # Wait for all background jobs
echo "Processing complete"| Variable | Set By | Used For |
|---|---|---|
SCRIPT_DIR |
launch-system.sh | Script location detection |
ROS_DOMAIN_ID |
Container env | DDS domain isolation |
DISPLAY |
docker-compose.yml | X11 visualization |
OUSTER_HOSTNAME |
Dockerfile.ouster | LiDAR connection |
TOPICS |
launch-system.sh | Recording topic selection |
All scripts should be executable:
# Check current permissions
ls -la *.sh
# Make script executable
chmod +x launch-system.sh
chmod +x stop-system.sh
# For all scripts
chmod +x *.sh
chmod +x docker/*.sh
chmod +x docker/docker_shared/scripts/*.sh
chmod +x docker/docker_build/*.sh# Run with bash debug mode
bash -x launch-system.sh
# Or add to script header:
set -v # Print commands
set -x # Print commands with expansion# Verify script syntax before running
bash -n launch-system.sh
bash -n stop-system.sh
# Or use ShellCheck linter
shellcheck launch-system.sh# Save output to file
./launch-system.sh 2>&1 | tee recording.log
# Later, review:
cat recording.logLast Updated: 2026-03-30