Skip to content

Commit

Permalink
add px4-sitl command
Browse files Browse the repository at this point in the history
  • Loading branch information
PonomarevDA committed Nov 24, 2024
1 parent 208d656 commit b73a0e0
Show file tree
Hide file tree
Showing 10 changed files with 251 additions and 93 deletions.
18 changes: 17 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,22 @@ An example of connection to the whole system is show below.

<img src="https://github.com/ZilantRobotics/innopolis_vtol_dynamics/wiki/assets/welcome/use_case_3.png" alt="drawing" width="800"/>

### 1.4. SITL if you don't have the hardware

SITL mode is out of the scope of the interests of this simulator. But anyway, it happens that you need to test something and you don't have the required hardware in your hand. So, you can run the flight stack and the dynamics on your PC:

Run the HITL dynamics as usual, but choose MAVLink mode, for example:

```bash
./scripts/sim.py mq # mq stands for PX4 Mavlink Quadcopter
```

Run the PX4 flight stack. You can either build and run it according to PX4 official instructions, or use our Dockerfile to build and ru neverything in a single command:

```bash
./scripts/sim.py px4-sitl iris
```

## 2. USAGE

The simulator is distributed as a Docker image. To simplify the interraction with Docker, a `./scripts/sim.py` script was written. The script configures all the necessary Docker flags, performs automatic firmware upload, configuration, creates a CAN interface, and generally provides a simple interface to interact with the simulator.
Expand All @@ -151,7 +167,7 @@ pip install -r requirements.txt
To build docker image, type:

```bash
./scripts/sim.py b # build
./scripts/sim.py b # b stands for build
```

> An image on dockerhub usually is not up to date, so it's better to build manually
Expand Down
3 changes: 3 additions & 0 deletions configs/vehicles/px4_v1_15_0_mavlink_quadcopter.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
mode: mavlink
alias: mq
sim_config: px4_v1_15_0_mavlink_quadcopter
3 changes: 3 additions & 0 deletions configs/vehicles/px4_v1_15_0_mavlink_quadplane_vtol.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
mode: mavlink
alias: mv4
sim_config: px4_v1_15_0_mavlink_quadplane_vtol
38 changes: 31 additions & 7 deletions scripts/command.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ class SimCommand:
info: Optional[str]
sim_config: Optional[str] = None
args: Optional[list] = None
need_upload_firmware: bool = False
need_load_parameters: bool = False

def check(self, command: str) -> bool:
if isinstance(command, str):
Expand Down Expand Up @@ -58,10 +60,32 @@ def create_list_from_directory(dir_with_yaml_files) -> list:
commands.append(cmd)
return commands

COMMANDS = [
SimCommand(name="build", alias='b', mode=None, info="Build the Docker image"),
SimCommand(name="kill", alias='', mode=None, info="Kill the running Docker container"),
SimCommand(name="monitor", alias='', mode=None, info="Just monitor"),
SimCommand(name="rviz", alias='', mode=None, info="Run RVIZ"),
*SimCommand.create_list_from_directory(dir_with_yaml_files=VEHICLES_DIR)
]
class CommandsManager:
COMMANDS = [
SimCommand(name="build", alias='b', mode=None, info="Build the Docker image"),
SimCommand(name="kill", alias='', mode=None, info="Kill the running Docker container"),
SimCommand(name="monitor", alias='', mode=None, info="Just monitor"),
SimCommand(name="rviz", alias='', mode=None, info="Run RVIZ"),
SimCommand(name="px4-sitl", alias='', mode=None, info="Run PX4 SITL Flight Stack"),
*SimCommand.create_list_from_directory(dir_with_yaml_files=VEHICLES_DIR)
]

@staticmethod
def get_all_commands():
return CommandsManager.COMMANDS

@staticmethod
def get_help():
header = f'{"Command":<36} {"Alias":<10} {"Info"}\n'

commands_details = []
for cmd in CommandsManager.COMMANDS:
command_detail = f"{cmd.name:<36} {cmd.alias or '':<10} {cmd.info or ''}"
commands_details.append(command_detail)

return header + '\n'.join(commands_details)

@staticmethod
def parse_command(command: list) -> Optional[SimCommand]:
command = next((cmd for cmd in CommandsManager.COMMANDS if cmd.check(command)), None)
return command
8 changes: 4 additions & 4 deletions scripts/docker.sh
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ Supported modes (with aliases):
px4_v1_13_0_dronecan_vtol,dv1130 | DroneCAN PX4 v1.13 vtol 13070
-------------------------------------------------------------------------------
px4_v1_15_0_cyphal_octorotor,co | Cyphal PX4 v1.15-beta Octorotor Coaxial (12001)
px4_v1_12_0_mavlink_quadplane_vtol | MAVLink PX4 v1.12 vtol 13070
px4_v1_12_0_mavlink_quadcopter | MAVLink PX4 v1.12 Quadrotor (4001)
px4_v1_15_0_mavlink_quadplane_vtol | MAVLink PX4 v1.12 vtol 13070
px4_v1_15_0_mavlink_quadcopter | MAVLink PX4 v1.12 Quadrotor (4001)
cyphal_and_dronecan | 2 CAN AP v4.4.0 Copter
-------------------------------------------------------------------------------
Expand Down Expand Up @@ -262,8 +262,8 @@ elif [ "$CMD" = "px4_v1_15_0_cyphal_quadcopter" ] || \
docker_container_run_cyphal

# MAVLink commands:
elif [ "$CMD" = "px4_v1_12_0_mavlink_quadplane_vtol" ] || \
[ "$CMD" = "px4_v1_12_0_mavlink_quadcopter" ] ; then
elif [ "$CMD" = "px4_v1_15_0_mavlink_quadplane_vtol" ] || \
[ "$CMD" = "px4_v1_15_0_mavlink_quadcopter" ] ; then
vehicle=$CMD
docker_container_run_mavlink

Expand Down
31 changes: 23 additions & 8 deletions scripts/docker_wrapper.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#!/usr/bin/env python3
import os
import re
import sys
import time
import shlex
import logging
Expand Down Expand Up @@ -33,8 +34,8 @@ class DockerWrapper:
REPOSITORY_DIR = Path(__file__).parent.parent.absolute()
DOCKERFILE_DIR = REPOSITORY_DIR
@staticmethod
def build(full_image_name : str) -> None:
cmd = ['docker', 'build', '-t', full_image_name, DockerWrapper.DOCKERFILE_DIR]
def build(full_image_name : str, dockerfile_dir: str = DOCKERFILE_DIR) -> None:
cmd = ['docker', 'build', '-t', full_image_name, dockerfile_dir]
subprocess.run(cmd, check=True)

@staticmethod
Expand All @@ -56,11 +57,23 @@ def kill_container_by_id(container_identifier : str) -> None:
subprocess.run(cmd, check=True)

@staticmethod
def run_container(sniffer_path: str,
image_name: str,
sim_config: str,
mode: SimMode) -> Optional[subprocess.Popen]:
if not is_valid_sniffer_path(sniffer_path):
def interactive(image: str, flags: list = ["--rm", "-it", "--net=host"]) -> None:
command = ["docker", "container", "run", *flags, image, "/bin/bash"]

try:
with subprocess.Popen(command, stdin=sys.stdin, stdout=sys.stdout, stderr=sys.stderr) as process:
process.wait()
except KeyboardInterrupt:
print("\nScript interrupted.")
except Exception as err:
print(f"Error: {err}")

@staticmethod
def run_sim_container(mode: SimMode,
image_name: str,
argument: str,
sniffer_path: Optional[str]=None) -> Optional[subprocess.Popen]:
if mode.is_hitl() and not is_valid_sniffer_path(sniffer_path):
raise RuntimeError(f"CAN-Sniffer has not been found (sniffer_path={sniffer_path})")

if mode == SimMode.CYPHAL_HITL:
Expand All @@ -79,6 +92,8 @@ def run_container(sniffer_path: str,
flags = [
*DockerWrapper.COMMON_DOCKER_FLAGS,
]
else:
raise RuntimeError(f"Unknown mode: {mode}")

command = [
"docker",
Expand All @@ -87,7 +102,7 @@ def run_container(sniffer_path: str,
*flags,
image_name,
'./scripts/run_sim.sh',
sim_config
argument
]
logger.info(" ".join([shlex.quote(arg) for arg in command]))

Expand Down
3 changes: 3 additions & 0 deletions scripts/mode.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,6 @@ def create_from_string(string: str):
"monitor": SimMode.MONITOR,
}
return string_to_mode.get(string)

def is_hitl(self) -> bool:
return self in {SimMode.CYPHAL_HITL, SimMode.DRONECAN_HITL}
75 changes: 75 additions & 0 deletions scripts/px4-sitl/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# This Dockerfile is a hint about how to run PX4 ROS-noetic flight stack in Docker
# Tested in Ubuntu-22.04 and PX4-Autopolot v1.14.0.
# Suitable for Quadrotor MAVLink SITL mode of UAV HITL Simulator: https://github.com/RaccoonlabDev/innopolis_vtol_dynamics.git
# Origin: https://gist.github.com/PonomarevDA/1a600056a3c01cebae878b642dafe6ea
# Usage:
# 1. Download:
# mkdir PX4-Autopilot-Docker && cd PX4-Autopilot-Docker
# wget https://gist.githubusercontent.com/PonomarevDA/1a600056a3c01cebae878b642dafe6ea/raw/Dockerfile
# 2. Build the Docker image:
# docker build -t ponomarevda/px4-sitl .
# 3. Run in interactive mode:
# docker container run --rm -it --net=host ponomarevda/px4-sitl /bin/bash
# 4. Then run launch file:
# roslaunch px4 px4.launch vehicle:=iris
# Or run at once:
# docker run --rm -it --net=host ponomarevda/px4-sitl roslaunch px4 px4.launch vehicle:=iris

ARG ROS_DISTRO=noetic

FROM ros:$ROS_DISTRO
LABEL description="PX4 MAVLink SITL simulator"
SHELL ["/bin/bash", "-c"]
WORKDIR /root/catkin_ws/

# 1. Install basic requirements
RUN apt-get update && \
apt-get upgrade -y && \
apt-get install -y git \
ros-$ROS_DISTRO-catkin \
python3-pip \
python3-catkin-tools

# 2. Build ROS
RUN source /opt/ros/$ROS_DISTRO/setup.bash && \
git config --global http.sslverify false && \
mkdir ~/catkin_ws/src && \
catkin build

# 3. Install PX4 ubuntu requirements
RUN apt-get install -y wget
RUN wget https://raw.githubusercontent.com/PX4/PX4-Autopilot/main/Tools/setup/ubuntu.sh && \
wget https://raw.githubusercontent.com/PX4/PX4-Autopilot/main/Tools/setup/requirements.txt
RUN bash ubuntu.sh

# 4. Install PX4 ubuntu sim ros requirements
RUN wget https://raw.githubusercontent.com/PX4/Devguide/master/build_scripts/ubuntu_sim_ros_melodic.sh
RUN source /opt/ros/$ROS_DISTRO/setup.bash && \
source ~/catkin_ws/devel/setup.bash && \
bash ubuntu_sim_ros_melodic.sh

# 5. Clone PX4-Autopilot
RUN git clone --branch main https://github.com/PX4/PX4-Autopilot --recursive ~/PX4-Autopilot

# 6. Build PX4 for VTOL
RUN cd ~/PX4-Autopilot && DONT_RUN=1 make px4_sitl gazebo_standard_vtol

# 7. Append to .bashrc
RUN rm ~/.bashrc && cp /etc/skel/.bashrc ~/.bashrc
RUN source ~/.bashrc && \
cd ~/PX4-Autopilot && \
echo "source /opt/ros/$ROS_DISTRO/setup.bash" >> ~/.bashrc && \
echo "source ~/catkin_ws/devel/setup.bash" >> ~/.bashrc && \
echo "export ROS_PACKAGE_PATH=/opt/ros/noetic/share:~/PX4-Autopilot:~/PX4-Autopilot/Tools/sitl_gazebo" >> ~/.bashrc && \
echo "cd ~/PX4-Autopilot" >> ~/.bashrc && \
echo "echo Hello, there. This is PX4 Flight Stack container." >> ~/.bashrc && \
echo "echo Your next steps can be:" >> ~/.bashrc && \
echo "echo - roslaunch px4 px4.launch vehicle:=iris" >> ~/.bashrc && \
echo "echo - roslaunch px4 px4.launch vehicle:=standard_vtol" >> ~/.bashrc && \
echo "echo For details please visit: https://docs.px4.io/main/en/sim_gazebo_gz/vehicles.html" >> ~/.bashrc && \
echo "echo P.S. ROS and PX4 environment variables have been set." >> ~/.bashrc

CMD echo -e "\nContainer has been started!" && \
source ~/.bashrc && \
roslaunch px4 px4.launch vehicle:=iris && \
echo -e "\nContainer has been finished!"
16 changes: 8 additions & 8 deletions scripts/run_sim.sh
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ Commands:
px4_v1_15_0_cyphal_octoplane_vtol Run dynamics simulator in Cyphal HITL mode for vtol 8 motors airframe.
cyphal_and_dronecan_inno_vtol Run dynamics simulator in DroneCan + Cyphal mode for vtol airframe.
This mode uses 2 serial ports and is in the alpha testing stage yet.
px4_v1_12_0_mavlink_quadplane_vtol Run dynamics simulator in MAVLink SITL mode for vtol airframe
px4_v1_12_0_mavlink_quadcopter Run dynamics simulator in MAVLink SITL mode for flight_goggles airframe
px4_v1_15_0_mavlink_quadplane_vtol Run dynamics simulator in MAVLink SITL mode for vtol airframe
px4_v1_15_0_mavlink_quadcopter Run dynamics simulator in MAVLink SITL mode for flight_goggles airframe
Auxilliary commands:
ros Source ROS and catkin_ws setup.bash files
Expand Down Expand Up @@ -173,7 +173,7 @@ cyphal_and_dronecan_inno_vtol() {
dynamics:=quadcopter
}

px4_v1_12_0_mavlink_quadplane_vtol() {
px4_v1_15_0_mavlink_quadplane_vtol() {
setup_ros
roslaunch innopolis_vtol_dynamics sitl.launch \
logging_type:=standard_vtol \
Expand All @@ -184,7 +184,7 @@ px4_v1_12_0_mavlink_quadplane_vtol() {
run_sitl_flight_stack:="false"
}

px4_v1_12_0_mavlink_quadcopter() {
px4_v1_15_0_mavlink_quadcopter() {
setup_ros
roslaunch innopolis_vtol_dynamics sitl.launch \
logging_type:=quadcopter \
Expand Down Expand Up @@ -218,10 +218,10 @@ elif [ "$1" = "px4_v1_15_0_cyphal_octoplane_vtol" ]; then
px4_v1_15_0_cyphal_octoplane_vtol
elif [ "$1" = "cyphal_and_dronecan_inno_vtol" ]; then
cyphal_and_dronecan_inno_vtol
elif [ "$1" = "px4_v1_12_0_mavlink_quadplane_vtol" ]; then
px4_v1_12_0_mavlink_quadplane_vtol
elif [ "$1" = "px4_v1_12_0_mavlink_quadcopter" ]; then
px4_v1_12_0_mavlink_quadcopter
elif [ "$1" = "px4_v1_15_0_mavlink_quadplane_vtol" ]; then
px4_v1_15_0_mavlink_quadplane_vtol
elif [ "$1" = "px4_v1_15_0_mavlink_quadcopter" ]; then
px4_v1_15_0_mavlink_quadcopter
elif [ "$1" = "ros" ]; then
setup_ros
else
Expand Down
Loading

0 comments on commit b73a0e0

Please sign in to comment.