Skip to main content

Base Session

The tmux session is the heart of UAV operation. Start from a known, well-tested base session and customize it for your specific UAV configuration.

Finding a Base Session

The following example session is designed for outdoor flight operations with a standard sensor setup. It includes the necessary tmux panes for running the MRS UAV system, monitoring topics, and recording data.

The important parts are highlighted in the session definition, and you can customize it by adding or modifying panes as needed, while keeping the core structure intact.

Show example tmux session (outdoor_session/tmux.sh)

tmux.sh

#!/bin/bash
### BEGIN INIT INFO
# Provides: tmux
# Required-Start: $local_fs $network dbus
# Required-Stop: $local_fs $network
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: start the uav
### END INIT INFO
if [ "$(id -u)" == "0" ]; then
exec sudo -u mrs "$0" "$@"
fi

source $HOME/.bashrc

# location for storing the bag files
# * do not change unless you know what you are doing
MAIN_DIR="$HOME/bag_files"

# the project name
# * is used to define folder name in ~/$MAIN_DIR
PROJECT_NAME=just_flying

# the name of the TMUX session
# * can be used for attaching as 'tmux a -t <session name>'
SESSION_NAME=mav

# following commands will be executed first in each window
# * do NOT put ; at the end
pre_input="export RMW_IMPLEMENTATION=rmw_zenoh_cpp"

# define commands
# 'name' 'command'
# * DO NOT PUT SPACES IN THE NAMES
# * "new line" after the command => the command will be called after start
# * NO "new line" after the command => the command will wait for user's <enter>
input=(
'Rosbag' 'waitForOffboard; ./record.sh
'
'HwApi' 'ros2 launch mrs_uav_px4_api api.launch.py
'
'Status' 'ros2 run mrs_uav_status status.sh
'
'Core' 'ros2 launch mrs_uav_core core.launch.py platform_config:=`ros2 pkg prefix mrs_uav_deployment`/share/mrs_uav_deployment/config/mrs_uav_system/$UAV_TYPE.yaml world_config:=`ros2 pkg prefix mrs_uav_deployment`/share/mrs_uav_deployment/config/worlds/world_$WORLD_NAME.yaml custom_config:=./config/custom_config.yaml network_config:=./config/network_config.yaml
'
'AutoStart' 'ros2 launch mrs_uav_autostart automatic_start.launch.py
'
# do NOT modify the command list below
'EstimDiag' 'waitForCore; ros2 topic echo /'"$UAV_NAME"'/estimation_manager/diagnostics --flow-style
'
'kernel_log' 'tail -f /var/log/kern.log -n 100
'
'zenoh' 'export ZENOH_ROUTER_CONFIG_URI=`ros2 pkg prefix mrs_uav_deployment`/share/mrs_uav_deployment/config/zenoh/uav_router.json5; ros2 run rmw_zenoh_cpp rmw_zenohd
'
)

# the name of the window to focus after start
init_window="Status"

# automatically attach to the new session?
# {true, false}, default true
attach=true

###########################
### DO NOT MODIFY BELOW ###
###########################

export TMUX_BIN="/usr/bin/tmux -L mrs -f /etc/ctu-mrs/tmux.conf"

# find the session
FOUND=$( $TMUX_BIN ls | grep $SESSION_NAME )

if [ $? == "0" ]; then
echo "The session already exists"
$TMUX_BIN -2 attach-session -t $SESSION_NAME
exit
fi

# Absolute path to this script. /home/user/bin/foo.sh
SCRIPT=$(readlink -f $0)
# Absolute path this script is in. /home/user/bin
SCRIPTPATH=`dirname $SCRIPT`

TMUX= $TMUX_BIN new-session -s "$SESSION_NAME" -d
echo "Starting new session."

# get the iterator
ITERATOR_FILE="$MAIN_DIR/$PROJECT_NAME"/iterator.txt
if [ -e "$ITERATOR_FILE" ]
then
ITERATOR=`cat "$ITERATOR_FILE"`
ITERATOR=$(($ITERATOR+1))
else
echo "iterator.txt does not exist, creating it"
mkdir -p "$MAIN_DIR/$PROJECT_NAME"
touch "$ITERATOR_FILE"
ITERATOR="1"
fi
echo "$ITERATOR" > "$ITERATOR_FILE"

# create file for logging terminals' output
LOG_DIR="$MAIN_DIR/$PROJECT_NAME/"
SUFFIX=$(date +"%Y_%m_%d_%H_%M_%S")
SUBLOG_DIR="$LOG_DIR/"$ITERATOR"_"$SUFFIX""
TMUX_DIR="$SUBLOG_DIR/tmux"
mkdir -p "$SUBLOG_DIR"
mkdir -p "$TMUX_DIR"

# link the "latest" folder to the recently created one
rm "$LOG_DIR/latest" > /dev/null 2>&1
rm "$MAIN_DIR/latest" > /dev/null 2>&1
ln -sf "$SUBLOG_DIR" "$LOG_DIR/latest"
ln -sf "$SUBLOG_DIR" "$MAIN_DIR/latest"

# create arrays of names and commands
for ((i=0; i < ${#input[*]}; i++));
do
((i%2==0)) && names[$i/2]="${input[$i]}"
((i%2==1)) && cmds[$i/2]="${input[$i]}"
done

# run tmux windows
for ((i=0; i < ${#names[*]}; i++));
do
$TMUX_BIN new-window -t $SESSION_NAME:$(($i+1)) -n "${names[$i]}"
done

sleep 3

# start loggers
for ((i=0; i < ${#names[*]}; i++));
do
$TMUX_BIN pipe-pane -t $SESSION_NAME:$(($i+1)) -o "ts | cat >> $TMUX_DIR/$(($i+1))_${names[$i]}.log"
done

# send commands
for ((i=0; i < ${#cmds[*]}; i++));
do
$TMUX_BIN send-keys -t $SESSION_NAME:$(($i+1)) "cd $SCRIPTPATH;
${pre_input};
${cmds[$i]}"
done

# identify the index of the init window
init_index=0
for ((i=0; i < ((${#names[*]})); i++));
do
if [ ${names[$i]} == "$init_window" ]; then
init_index=$(expr $i + 1)
fi
done

$TMUX_BIN select-window -t $SESSION_NAME:$init_index

if $attach; then

if [ -z ${TMUX} ];
then
$TMUX_BIN -2 attach-session -t $SESSION_NAME
else
tmux detach-client -E "tmux -L mrs a -t $SESSION_NAME"
fi
else
echo "The session was started"
echo "You can later attach by calling:"
echo " tmux -L mrs a -t $SESSION_NAME"
fi

The MRS UAV Deployment repository includes ready-made tmux sessions:

~/git/mrs_uav_deployment/tmux/

A good starting point is the just_flying session, which contains the minimal setup for basic flight operations.

Creating the Example Session

Copy the base session to the home directory and rename it:

cp -r ~/git/mrs_uav_deployment/tmux/just_flying ~/example_session

This ~/example_session folder becomes your customized flight session.

Multiple Scenarios

If the UAV needs to operate in different modes (e.g., indoor vs outdoor, different sensor configurations), create multiple sessions:

cp -r ~/example_session ~/session_indoor
cp -r ~/example_session ~/session_outdoor

Name them descriptively based on their use case.

Session Structure

A typical session folder contains:

example_session/
├── tmux.sh # Tmux session definition
├── config/ # Custom configuration files
│ ├── custom_config.yaml
│ └── camera_calibrations/
├── launch/ # Custom ROS2 launch files
│ └── static_tfs.launch.xml # Static transforms for custom sensors
├── record.sh # Rosbag recording script
└── ...

The following pages explain how to set up each component.

Adding Custom Panes

To add your own sensors or software to the session, add entries to the input array in tmux.sh. Here's the array from the example session — your custom panes go before the # do NOT modify comment:

tmux.sh (lines 37-55)

input=(
'Rosbag' 'waitForOffboard; ./record.sh
'
'HwApi' 'ros2 launch mrs_uav_px4_api api.launch.py
'
'Status' 'ros2 run mrs_uav_status status.sh
'
'Core' 'ros2 launch mrs_uav_core core.launch.py platform_config:=`ros2 pkg prefix mrs_uav_deployment`/share/mrs_uav_deployment/config/mrs_uav_system/$UAV_TYPE.yaml world_config:=`ros2 pkg prefix mrs_uav_deployment`/share/mrs_uav_deployment/config/worlds/world_$WORLD_NAME.yaml custom_config:=./config/custom_config.yaml network_config:=./config/network_config.yaml
'
'AutoStart' 'ros2 launch mrs_uav_autostart automatic_start.launch.py
'
# do NOT modify the command list below
'EstimDiag' 'waitForCore; ros2 topic echo /'"$UAV_NAME"'/estimation_manager/diagnostics --flow-style
'
'kernel_log' 'tail -f /var/log/kern.log -n 100
'
'zenoh' 'export ZENOH_ROUTER_CONFIG_URI=`ros2 pkg prefix mrs_uav_deployment`/share/mrs_uav_deployment/config/zenoh/uav_router.json5; ros2 run rmw_zenoh_cpp rmw_zenohd
'
)

For example, to add a LiDAR driver and your custom static transforms, you would insert them before line 48 (# do NOT modify):

# Launch a LiDAR driver
'Livox' 'waitForRos; ros2 launch livox_ros_driver2 mid360.launch.py
'
# Publish all custom static transforms for this UAV
'StaticTFs' 'waitForTime; ros2 launch ./launch/static_tfs.launch.xml
'

Each entry is a pair of 'name' and 'command'. A few things to note:

  • The newline after the command matters — if the command is followed by a newline (before the closing '), it runs automatically when the session starts. Without the newline, it waits for you to press Enter.
  • waitForRos — waits until ROS2 is ready before running the command. Use this for nodes that depend on ROS.
  • waitForTime — waits until the system clock is synchronized. Use this for transforms and time-sensitive nodes.
  • waitForHw — waits until the hardware API is running. Use this for nodes that depend on the flight controller.
  • Don't put spaces in the pane names — use CamelCase or underscores.
warning

Never modify files in the base MRS system installation or in git repositories directly. All customizations should go in the session folder. This ensures that clients can safely update the MRS system without losing their custom configurations, and it keeps the base system clean and maintainable.