Compare commits

...

19 Commits

Author SHA1 Message Date
Ramon Roche 58b0a1f90d mavsdk_tests: fix wall-clock sleeps in test_multicopter_basics
Replace std::this_thread::sleep_for() with tester.sleep_for() to use
simulation-aware sleep that accounts for speed factor under lockstep.
2026-02-18 11:04:06 +13:00
Ramon Roche 8e71a96cf7 mavsdk_tests: fix wall-clock timeouts to use simulation time
Replace 5 functions that used std::future::wait_for() (wall-clock time)
with poll_condition_with_timeout() (simulation time). This prevents
timeouts from expiring prematurely when running at high speed factors
under lockstep.

Affected functions:
- wait_until_altitude()
- wait_for_landed_state()
- wait_for_flight_mode()
- start_and_wait_for_mission_sequence()
- wait_until_speed_lower_than()

Also remove debug printf statements from start_checking_altitude().
2026-02-18 11:04:06 +13:00
Julian Oes 704c05c428 navigator: add to lockstep
This is an attempt to get the SIH CI tests more consistent.
2026-02-03 19:51:31 +13:00
Julian Oes 5c2c48f29d mavlink: include in lockstep
This is an attempt to get SIH CI tests to be more consistent.
2026-02-03 19:51:30 +13:00
Julian Oes 1fbe481a86 mavsdk_tests: reduce rate to avoid queueing of checks
When we run this at 10x (or higher) we end up with too many checks
queued up and by the time they execute the drone has slowed down.
2026-02-03 19:51:29 +13:00
Julian Oes 107393e6ad simulator_sih: reduce drag
According to Claude, the aerodynamic drag was way too high, and higher
than e.g. Gazebo Classic.

Another hint that it was too high is that the model
1102_tailsitter_duo_sih.hil had it at 0.2 as well.

Reducing this, makes acceleration of the quadx more realistic and it
starts passing CI tests where speed is checked. Without it, it tends to
be too slow.
2026-02-03 19:51:28 +13:00
Julian Oes 5824bdb5fb simulator_sih: implement lockstep by waiting for outputs
Without this, we see test failures in CI as soon as CPU load is higher.
2026-02-03 19:51:27 +13:00
Julian Oes 1c25698396 CI: try px4-dev container instead 2026-02-03 19:51:26 +13:00
Julian Oes f7af8f4528 mavlink: don't miss mission current updates
When a mission is finished we need to make sure to send the mission
current message and not drop it. Otherwise, MAVSDK tests might not get
the update that the mission has been finished.
2026-02-03 19:51:25 +13:00
Julian Oes d9865a19ee cmake: add missing include 2026-02-03 19:51:24 +13:00
Julian Oes e0ff60e827 mavsdk_tests: fix formatting 2026-02-03 19:51:23 +13:00
Julian Oes d2539ed9f9 rost_tests: add new model_prefix 2026-02-03 19:51:23 +13:00
Julian Oes ad82801583 CI: use existing container 2026-02-03 19:51:22 +13:00
Julian Oes cb6094ab59 CI: use SIH sim 2026-02-03 19:51:21 +13:00
Julian Oes ee1f316052 mavsdk_tests: try to satisfy speed check
By making the mission legs longer and checking later, we are more likely
to have SIH accelerated enough to pass the speed check.
2026-02-03 19:51:20 +13:00
Julian Oes a14de32d2e mavsdk_tests: leave tests backwards compatible but enable SIH
This reverts some of the changes to keep runners of classic Gazebo in
there but leave it optional, so we can still use it if required.
2026-02-03 19:51:19 +13:00
Julian Oes a41d35b57f simulation: implement required failure hooks 2026-02-03 19:51:18 +13:00
Matthias Grob debca7f517 Initial stab at running MAVSDK tests with SIH and fix the broken parts O.O 2026-02-03 19:51:17 +13:00
Matthias Grob f29eb2ee32 SensorGpsSim: follow naming convention, replace length valculation with vector library call 2026-02-03 19:51:16 +13:00
30 changed files with 509 additions and 184 deletions
+7 -16
View File
@@ -26,14 +26,13 @@ jobs:
name: Testing PX4 ${{ matrix.config.model }}
runs-on: [runs-on,runner=4cpu-linux-x64,image=ubuntu22-full-x64,"run-id=${{ github.run_id }}",spot=false]
container:
image: px4io/px4-dev-simulation-focal:2021-09-08
image: px4io/px4-dev:v1.16.0-rc1-258-g0369abd556
options: --privileged --ulimit core=-1 --security-opt seccomp=unconfined
strategy:
fail-fast: false
matrix:
config:
- {model: "iris", latitude: "59.617693", longitude: "-151.145316", altitude: "48", build_type: "RelWithDebInfo" } # Alaska
- {model: "tailsitter" , latitude: "29.660316", longitude: "-82.316658", altitude: "30", build_type: "RelWithDebInfo" } # Florida
- {model: "quadx", latitude: "59.617693", longitude: "-151.145316", altitude: "48", build_type: "RelWithDebInfo" } # Alaska
- {model: "standard_vtol", latitude: "47.397742", longitude: "8.545594", altitude: "488", build_type: "Coverage" } # Zurich
steps:
@@ -74,19 +73,11 @@ jobs:
- name: Cache Post-Run [px4_sitl_default]
run: ccache -s
- name: Build SITL Gazebo
env:
PX4_CMAKE_BUILD_TYPE: ${{matrix.config.build_type}}
run: make px4_sitl_default sitl_gazebo-classic
- name: Cache Post-Run [sitl_gazebo-classic]
run: ccache -s
- name: Download MAVSDK
run: wget "https://github.com/mavlink/MAVSDK/releases/download/v$(cat test/mavsdk_tests/MAVSDK_VERSION)/libmavsdk-dev_$(cat test/mavsdk_tests/MAVSDK_VERSION)_ubuntu20.04_amd64.deb"
run: wget "https://github.com/mavlink/MAVSDK/releases/download/v$(cat test/mavsdk_tests/MAVSDK_VERSION)/libmavsdk-dev_$(cat test/mavsdk_tests/MAVSDK_VERSION)_ubuntu22.04_amd64.deb"
- name: Install MAVSDK
run: dpkg -i "libmavsdk-dev_$(cat test/mavsdk_tests/MAVSDK_VERSION)_ubuntu20.04_amd64.deb"
run: dpkg -i "libmavsdk-dev_$(cat test/mavsdk_tests/MAVSDK_VERSION)_ubuntu22.04_amd64.deb"
- name: Check PX4 Environment Variables
env:
@@ -102,9 +93,9 @@ jobs:
env:
PX4_CMAKE_BUILD_TYPE: ${{matrix.config.build_type}}
DONT_RUN: 1
run: make px4_sitl_default sitl_gazebo-classic mavsdk_tests
run: make px4_sitl_default mavsdk_tests
- name: Cache Post-Run [px4_sitl_default sitl_gazebo-classic mavsdk_tests]
- name: Cache Post-Run [px4_sitl_default mavsdk_tests]
run: ccache -s
- name: Core Dump Settings
@@ -118,7 +109,7 @@ jobs:
PX4_HOME_LON: ${{matrix.config.longitude}}
PX4_HOME_ALT: ${{matrix.config.altitude}}
PX4_CMAKE_BUILD_TYPE: ${{matrix.config.build_type}}
run: test/mavsdk_tests/mavsdk_test_runner.py --speed-factor 10 --abort-early --model ${{matrix.config.model}} test/mavsdk_tests/configs/sitl.json --verbose --force-color
run: test/mavsdk_tests/mavsdk_test_runner.py --speed-factor 10 --abort-early --model ${{matrix.config.model}} test/mavsdk_tests/configs/sih-sitl.json --verbose --force-color
timeout-minutes: 45
- name: Upload failed logs
+2 -4
View File
@@ -429,16 +429,14 @@ rostest: px4_sitl_default
@$(MAKE) --no-print-directory px4_sitl_default sitl_gazebo-classic
tests_integration: px4_sitl_default
@$(MAKE) --no-print-directory px4_sitl_default sitl_gazebo-classic
@$(MAKE) --no-print-directory px4_sitl_default mavsdk_tests
@"$(SRC_DIR)"/test/mavsdk_tests/mavsdk_test_runner.py --speed-factor 20 test/mavsdk_tests/configs/sitl.json
@"$(SRC_DIR)"/test/mavsdk_tests/mavsdk_test_runner.py --speed-factor 20 test/mavsdk_tests/configs/sih-sitl.json
tests_integration_coverage:
@$(MAKE) clean
@$(MAKE) --no-print-directory px4_sitl_default PX4_CMAKE_BUILD_TYPE=Coverage
@$(MAKE) --no-print-directory px4_sitl_default sitl_gazebo-classic
@$(MAKE) --no-print-directory px4_sitl_default mavsdk_tests
@"$(SRC_DIR)"/test/mavsdk_tests/mavsdk_test_runner.py --speed-factor 20 test/mavsdk_tests/configs/sitl.json
@"$(SRC_DIR)"/test/mavsdk_tests/mavsdk_test_runner.py --speed-factor 20 test/mavsdk_tests/configs/sih-sitl.json
@mkdir -p coverage
@lcov --directory build/px4_sitl_default --base-directory build/px4_sitl_default --gcov-tool gcov --capture -o coverage/lcov.info
@@ -45,10 +45,10 @@ Install either from binaries or source:
## Run All PX4 Tests
To run all SITL tests as defined in [sitl.json](https://github.com/PX4/PX4-Autopilot/blob/main/test/mavsdk_tests/configs/sitl.json), do:
To run all SITL tests using the SIH simulator as defined in [sih-sitl.json](https://github.com/PX4/PX4-Autopilot/blob/main/test/mavsdk_tests/configs/sih-sitl.json), do:
```sh
test/mavsdk_tests/mavsdk_test_runner.py test/mavsdk_tests/configs/sitl.json --speed-factor 10
test/mavsdk_tests/mavsdk_test_runner.py test/mavsdk_tests/configs/sih-sitl.json --speed-factor 10
```
This will list all the tests and then run them sequentially.
@@ -87,10 +87,10 @@ options:
## Run a Single Test
Run a single test by specifying the `model` and test `case` as command line options.
For example, to test flying a tailsitter in a mission you might run:
For example, to test a multicopter mission using the SIH simulator:
```sh
test/mavsdk_tests/mavsdk_test_runner.py test/mavsdk_tests/configs/sitl.json --speed-factor 10 --model tailsitter --case 'Fly VTOL mission'
test/mavsdk_tests/mavsdk_test_runner.py test/mavsdk_tests/configs/sih-sitl.json --speed-factor 10 --model quadx --case 'Fly square Multicopter Missions including RTL'
```
The easiest way to find out the current set of models and their associated test cases is to run all PX4 tests [as shown above](#run-all-px4-tests) (note, you can then cancel the build if you wish to test just one).
@@ -45,10 +45,10 @@ The tests need the MAVSDK C++ library installed system-wide (e.g. in `/usr/lib`
## 모든 PX4 테스트 실행
To run all SITL tests as defined in [sitl.json](https://github.com/PX4/PX4-Autopilot/blob/main/test/mavsdk_tests/configs/sitl.json), do:
To run all SITL tests using the SIH simulator as defined in [sih-sitl.json](https://github.com/PX4/PX4-Autopilot/blob/main/test/mavsdk_tests/configs/sih-sitl.json), do:
```sh
test/mavsdk_tests/mavsdk_test_runner.py test/mavsdk_tests/configs/sitl.json --speed-factor 10
test/mavsdk_tests/mavsdk_test_runner.py test/mavsdk_tests/configs/sih-sitl.json --speed-factor 10
```
This will list all the tests and then run them sequentially.
@@ -87,10 +87,10 @@ options:
## 단일 테스트 실행
Run a single test by specifying the `model` and test `case` as command line options.
예를 들어, 임무에서 테일시터 비행을 테스트하려면, 다음을 실행합니다.
For example, to test a multicopter mission using the SIH simulator:
```sh
test/mavsdk_tests/mavsdk_test_runner.py test/mavsdk_tests/configs/sitl.json --speed-factor 10 --model tailsitter --case 'Fly square Multicopter Missions including RTL'
test/mavsdk_tests/mavsdk_test_runner.py test/mavsdk_tests/configs/sih-sitl.json --speed-factor 10 --model quadx --case 'Fly square Multicopter Missions including RTL'
```
The easiest way to find out the current set of models and their associated test cases is to run all PX4 tests [as shown above](#run-all-px4-tests) (note, you can then cancel the build if you wish to test just one).
@@ -45,10 +45,10 @@ The tests need the MAVSDK C++ library installed system-wide (e.g. in `/usr/lib`
## Запуск усіх PX4 тестів
To run all SITL tests as defined in [sitl.json](https://github.com/PX4/PX4-Autopilot/blob/main/test/mavsdk_tests/configs/sitl.json), do:
To run all SITL tests using the SIH simulator as defined in [sih-sitl.json](https://github.com/PX4/PX4-Autopilot/blob/main/test/mavsdk_tests/configs/sih-sitl.json), do:
```sh
test/mavsdk_tests/mavsdk_test_runner.py test/mavsdk_tests/configs/sitl.json --speed-factor 10
test/mavsdk_tests/mavsdk_test_runner.py test/mavsdk_tests/configs/sih-sitl.json --speed-factor 10
```
This will list all the tests and then run them sequentially.
@@ -87,10 +87,10 @@ options:
## Запуск одного тесту
Run a single test by specifying the `model` and test `case` as command line options.
Наприклад, щоб протестувати керування хвостовиком у місії, ви можете виконати:
For example, to test a multicopter mission using the SIH simulator:
```sh
test/mavsdk_tests/mavsdk_test_runner.py test/mavsdk_tests/configs/sitl.json --speed-factor 10 --model tailsitter --case 'Fly VTOL mission'
test/mavsdk_tests/mavsdk_test_runner.py test/mavsdk_tests/configs/sih-sitl.json --speed-factor 10 --model quadx --case 'Fly square Multicopter Missions including RTL'
```
The easiest way to find out the current set of models and their associated test cases is to run all PX4 tests [as shown above](#run-all-px4-tests) (note, you can then cancel the build if you wish to test just one).
@@ -45,10 +45,10 @@ The tests need the MAVSDK C++ library installed system-wide (e.g. in `/usr/lib`
## 准备 PX4 源码
To run all SITL tests as defined in [sitl.json](https://github.com/PX4/PX4-Autopilot/blob/main/test/mavsdk_tests/configs/sitl.json), do:
To run all SITL tests using the SIH simulator as defined in [sih-sitl.json](https://github.com/PX4/PX4-Autopilot/blob/main/test/mavsdk_tests/configs/sih-sitl.json), do:
```sh
test/mavsdk_tests/mavsdk_test_runner.py test/mavsdk_tests/configs/sitl.json --speed-factor 10
test/mavsdk_tests/mavsdk_test_runner.py test/mavsdk_tests/configs/sih-sitl.json --speed-factor 10
```
This will list all the tests and then run them sequentially.
@@ -87,10 +87,10 @@ options:
## 关于实现的说明
Run a single test by specifying the `model` and test `case` as command line options.
For example, to test flying a tailsitter in a mission you might run:
For example, to test a multicopter mission using the SIH simulator:
```sh
test/mavsdk_tests/mavsdk_test_runner.py test/mavsdk_tests/configs/sitl.json --speed-factor 10 --model tailsitter --case 'Fly VTOL mission'
test/mavsdk_tests/mavsdk_test_runner.py test/mavsdk_tests/configs/sih-sitl.json --speed-factor 10 --model quadx --case 'Fly square Multicopter Missions including RTL'
```
The easiest way to find out the current set of models and their associated test cases is to run all PX4 tests [as shown above](#run-all-px4-tests) (note, you can then cancel the build if you wish to test just one).
+11
View File
@@ -2384,11 +2384,18 @@ Mavlink::task_main(int argc, char *argv[])
/* main loop */
px4_usleep(_main_loop_delay);
#if defined(ENABLE_LOCKSTEP_SCHEDULER)
int lockstep_component = px4_lockstep_register_component();
#endif
if (!should_transmit()) {
check_requested_subscriptions();
handleStatus();
handleCommands();
handleAndGetCurrentCommandAck();
#if defined(ENABLE_LOCKSTEP_SCHEDULER)
px4_lockstep_unregister_component(lockstep_component);
#endif
continue;
}
@@ -2520,6 +2527,10 @@ Mavlink::task_main(int argc, char *argv[])
}
perf_end(_loop_perf);
#if defined(ENABLE_LOCKSTEP_SCHEDULER)
px4_lockstep_unregister_component(lockstep_component);
#endif
}
_receiver.stop();
+13 -1
View File
@@ -503,6 +503,8 @@ MavlinkMissionManager::send()
if (_mission_result_sub.update()) {
const mission_result_s &mission_result = _mission_result_sub.get();
bool send_current = false;
if (_current_seq != mission_result.seq_current) {
_current_seq = mission_result.seq_current;
@@ -511,7 +513,7 @@ MavlinkMissionManager::send()
if (mission_result.seq_total > 0) {
if (mission_result.seq_current < mission_result.seq_total) {
send_mission_current(_current_seq);
send_current = true;
} else {
_mavlink.send_statustext_critical("ERROR: wp index out of bounds\t");
@@ -521,6 +523,16 @@ MavlinkMissionManager::send()
}
}
// Send MISSION_CURRENT when finished state changes (to notify MISSION_STATE_COMPLETE)
if (_last_finished != mission_result.finished) {
_last_finished = mission_result.finished;
send_current = true;
}
if (send_current) {
send_mission_current(_current_seq);
}
if (_last_reached != mission_result.seq_reached) {
_last_reached = mission_result.seq_reached;
+1
View File
@@ -132,6 +132,7 @@ private:
static int32_t _current_seq; ///< Current item sequence in active mission
int32_t _last_reached{-1}; ///< Last reached waypoint in active mission (-1 means nothing reached)
bool _last_finished{false}; ///< Last mission finished state
dm_item_t _transfer_dataman_id{DM_KEY_WAYPOINTS_OFFBOARD_1}; ///< Dataman storage ID for current transmission
+7
View File
@@ -195,6 +195,9 @@ void Navigator::run()
continue;
}
#if defined(ENABLE_LOCKSTEP_SCHEDULER)
int lockstep_component = px4_lockstep_register_component();
#endif
perf_begin(_loop_perf);
orb_copy(ORB_ID(vehicle_local_position), _local_pos_sub, &_local_pos);
@@ -933,6 +936,10 @@ void Navigator::run()
_geofence.run();
perf_end(_loop_perf);
#if defined(ENABLE_LOCKSTEP_SCHEDULER)
px4_lockstep_unregister_component(lockstep_component);
#endif
}
}
@@ -86,6 +86,57 @@ float SensorBaroSim::generate_wgn()
return X;
}
void SensorBaroSim::check_failure_injections()
{
vehicle_command_s vehicle_command;
while (_vehicle_command_sub.update(&vehicle_command)) {
if (vehicle_command.command != vehicle_command_s::VEHICLE_CMD_INJECT_FAILURE) {
continue;
}
bool handled = false;
bool supported = false;
const int failure_unit = static_cast<int>(vehicle_command.param1 + 0.5f);
const int failure_type = static_cast<int>(vehicle_command.param2 + 0.5f);
if (failure_unit == vehicle_command_s::FAILURE_UNIT_SENSOR_BARO) {
handled = true;
if (failure_type == vehicle_command_s::FAILURE_TYPE_OFF) {
PX4_WARN("CMD_INJECT_FAILURE, baro off");
supported = true;
_baro_blocked = true;
_baro_stuck = false;
} else if (failure_type == vehicle_command_s::FAILURE_TYPE_STUCK) {
PX4_WARN("CMD_INJECT_FAILURE, baro stuck");
supported = true;
_baro_blocked = false;
_baro_stuck = true;
} else if (failure_type == vehicle_command_s::FAILURE_TYPE_OK) {
PX4_INFO("CMD_INJECT_FAILURE, baro ok");
supported = true;
_baro_blocked = false;
_baro_stuck = false;
}
}
if (handled) {
vehicle_command_ack_s ack{};
ack.command = vehicle_command.command;
ack.from_external = false;
ack.result = supported ?
vehicle_command_ack_s::VEHICLE_CMD_RESULT_ACCEPTED :
vehicle_command_ack_s::VEHICLE_CMD_RESULT_UNSUPPORTED;
ack.timestamp = hrt_absolute_time();
_command_ack_pub.publish(ack);
}
}
}
void SensorBaroSim::Run()
{
if (should_exit()) {
@@ -105,6 +156,27 @@ void SensorBaroSim::Run()
updateParams();
}
check_failure_injections();
if (_baro_blocked) {
perf_end(_loop_perf);
return;
}
if (_baro_stuck) {
// Publish stuck (last known) values
sensor_baro_s sensor_baro{};
sensor_baro.timestamp_sample = hrt_absolute_time();
sensor_baro.device_id = 6620172; // 6620172: DRV_BARO_DEVTYPE_BAROSIM, BUS: 1, ADDR: 4, TYPE: SIMULATION
sensor_baro.pressure = _last_pressure;
sensor_baro.temperature = _last_temperature;
sensor_baro.timestamp = hrt_absolute_time();
_sensor_baro_pub.publish(sensor_baro);
perf_end(_loop_perf);
return;
}
if (_vehicle_global_position_sub.updated()) {
vehicle_global_position_s gpos;
@@ -161,6 +233,10 @@ void SensorBaroSim::Run()
// calculate temperature in Celsius
float temperature = temperature_local - 273.0f + _sim_baro_off_t.get();
// Store values for stuck mode
_last_pressure = pressure;
_last_temperature = temperature;
// publish
sensor_baro_s sensor_baro{};
sensor_baro.timestamp_sample = gpos.timestamp;
@@ -44,6 +44,8 @@
#include <uORB/SubscriptionInterval.hpp>
#include <uORB/topics/parameter_update.h>
#include <uORB/topics/sensor_baro.h>
#include <uORB/topics/vehicle_command.h>
#include <uORB/topics/vehicle_command_ack.h>
#include <uORB/topics/vehicle_global_position.h>
using namespace time_literals;
@@ -67,17 +69,25 @@ public:
private:
void Run() override;
void check_failure_injections();
// generate white Gaussian noise sample with std=1
static float generate_wgn();
uORB::SubscriptionInterval _parameter_update_sub{ORB_ID(parameter_update), 1_s};
uORB::Subscription _vehicle_global_position_sub{ORB_ID(vehicle_global_position_groundtruth)};
uORB::Subscription _vehicle_command_sub{ORB_ID(vehicle_command)};
uORB::Publication<vehicle_command_ack_s> _command_ack_pub{ORB_ID(vehicle_command_ack)};
bool _baro_blocked{false};
bool _baro_stuck{false};
bool _baro_rnd_use_last{false};
double _baro_rnd_y2{0.0};
float _baro_drift_pa_per_sec{0.0};
float _baro_drift_pa{0.0};
float _last_pressure{0.0f};
float _last_temperature{0.0f};
hrt_abstime _last_update_time{0};
@@ -1,6 +1,6 @@
/****************************************************************************
*
* Copyright (c) 2021 PX4 Development Team. All rights reserved.
* Copyright (c) 2021-2025 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -87,6 +87,49 @@ float SensorGpsSim::generate_wgn()
return X;
}
void SensorGpsSim::check_failure_injections()
{
vehicle_command_s vehicle_command;
while (_vehicle_command_sub.update(&vehicle_command)) {
if (vehicle_command.command != vehicle_command_s::VEHICLE_CMD_INJECT_FAILURE) {
continue;
}
bool handled = false;
bool supported = false;
const int failure_unit = static_cast<int>(vehicle_command.param1 + 0.5f);
const int failure_type = static_cast<int>(vehicle_command.param2 + 0.5f);
if (failure_unit == vehicle_command_s::FAILURE_UNIT_SENSOR_GPS) {
handled = true;
if (failure_type == vehicle_command_s::FAILURE_TYPE_OFF) {
PX4_WARN("CMD_INJECT_FAILURE, GPS off");
supported = true;
_gps_blocked = true;
} else if (failure_type == vehicle_command_s::FAILURE_TYPE_OK) {
PX4_INFO("CMD_INJECT_FAILURE, GPS ok");
supported = true;
_gps_blocked = false;
}
}
if (handled) {
vehicle_command_ack_s ack{};
ack.command = vehicle_command.command;
ack.from_external = false;
ack.result = supported ?
vehicle_command_ack_s::VEHICLE_CMD_RESULT_ACCEPTED :
vehicle_command_ack_s::VEHICLE_CMD_RESULT_UNSUPPORTED;
ack.timestamp = hrt_absolute_time();
_command_ack_pub.publish(ack);
}
}
}
void SensorGpsSim::Run()
{
if (should_exit()) {
@@ -106,19 +149,26 @@ void SensorGpsSim::Run()
updateParams();
}
if (_vehicle_local_position_sub.updated() && _vehicle_global_position_sub.updated()) {
check_failure_injections();
vehicle_local_position_s lpos{};
_vehicle_local_position_sub.copy(&lpos);
if (_gps_blocked) {
perf_end(_loop_perf);
return;
}
vehicle_global_position_s gpos{};
_vehicle_global_position_sub.copy(&gpos);
if (_vehicle_local_position_groundtruth_sub.updated() && _vehicle_global_position_groundtruth_sub.updated()) {
double latitude = gpos.lat + math::degrees((double)generate_wgn() * 0.2 / CONSTANTS_RADIUS_OF_EARTH);
double longitude = gpos.lon + math::degrees((double)generate_wgn() * 0.2 / CONSTANTS_RADIUS_OF_EARTH);
double altitude = (double)(gpos.alt + (generate_wgn() * 0.5f));
vehicle_local_position_s vehicle_local_position{};
_vehicle_local_position_groundtruth_sub.copy(&vehicle_local_position);
Vector3f gps_vel = Vector3f{lpos.vx, lpos.vy, lpos.vz} + noiseGauss3f(0.06f, 0.077f, 0.158f);
vehicle_global_position_s vehicle_global_position{};
_vehicle_global_position_groundtruth_sub.copy(&vehicle_global_position);
double latitude = vehicle_global_position.lat + math::degrees((double)generate_wgn() * 0.2 / CONSTANTS_RADIUS_OF_EARTH);
double longitude = vehicle_global_position.lon + math::degrees((double)generate_wgn() * 0.2 / CONSTANTS_RADIUS_OF_EARTH);
double altitude = (double)(vehicle_global_position.alt + (generate_wgn() * 0.5f));
Vector3f gps_vel = Vector3f{vehicle_local_position.vx, vehicle_local_position.vy, vehicle_local_position.vz};// + noiseGauss3f(0.06f, 0.077f, 0.158f);
// device id
device::Device::DeviceId device_id;
@@ -150,7 +200,7 @@ void SensorGpsSim::Run()
sensor_gps.vdop = 100.f;
}
sensor_gps.timestamp_sample = gpos.timestamp_sample;
sensor_gps.timestamp_sample = vehicle_global_position.timestamp_sample;
sensor_gps.time_utc_usec = 0;
sensor_gps.device_id = device_id.devid;
sensor_gps.latitude_deg = latitude; // Latitude in degrees
@@ -159,7 +209,7 @@ void SensorGpsSim::Run()
sensor_gps.altitude_ellipsoid_m = altitude;
sensor_gps.noise_per_ms = 0;
sensor_gps.jamming_indicator = 0;
sensor_gps.vel_m_s = sqrtf(gps_vel(0) * gps_vel(0) + gps_vel(1) * gps_vel(1)); // GPS ground speed, (metres/sec)
sensor_gps.vel_m_s = Vector2f(gps_vel).length(); // GPS ground speed, (metres/sec)
sensor_gps.vel_n_m_s = gps_vel(0);
sensor_gps.vel_e_m_s = gps_vel(1);
sensor_gps.vel_d_m_s = gps_vel(2);
@@ -1,6 +1,6 @@
/****************************************************************************
*
* Copyright (c) 2021 PX4 Development Team. All rights reserved.
* Copyright (c) 2021-2025 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -43,6 +43,8 @@
#include <uORB/SubscriptionInterval.hpp>
#include <uORB/topics/parameter_update.h>
#include <uORB/topics/sensor_gps.h>
#include <uORB/topics/vehicle_command.h>
#include <uORB/topics/vehicle_command_ack.h>
#include <uORB/topics/vehicle_global_position.h>
#include <uORB/topics/vehicle_local_position.h>
@@ -67,6 +69,7 @@ public:
private:
void Run() override;
void check_failure_injections();
// generate white Gaussian noise sample with std=1
static float generate_wgn();
@@ -75,10 +78,14 @@ private:
matrix::Vector3f noiseGauss3f(float stdx, float stdy, float stdz) { return matrix::Vector3f(generate_wgn() * stdx, generate_wgn() * stdy, generate_wgn() * stdz); }
uORB::SubscriptionInterval _parameter_update_sub{ORB_ID(parameter_update), 1_s};
uORB::Subscription _vehicle_global_position_sub{ORB_ID(vehicle_global_position_groundtruth)};
uORB::Subscription _vehicle_local_position_sub{ORB_ID(vehicle_local_position_groundtruth)};
uORB::Subscription _vehicle_global_position_groundtruth_sub{ORB_ID(vehicle_global_position_groundtruth)};
uORB::Subscription _vehicle_local_position_groundtruth_sub{ORB_ID(vehicle_local_position_groundtruth)};
uORB::Subscription _vehicle_command_sub{ORB_ID(vehicle_command)};
uORB::PublicationMulti<sensor_gps_s> _sensor_gps_pub{ORB_ID(sensor_gps)};
uORB::Publication<vehicle_command_ack_s> _command_ack_pub{ORB_ID(vehicle_command_ack)};
bool _gps_blocked{false};
perf_counter_t _loop_perf{perf_alloc(PC_ELAPSED, MODULE_NAME": cycle")};
@@ -87,6 +87,49 @@ float SensorMagSim::generate_wgn()
return X;
}
void SensorMagSim::check_failure_injections()
{
vehicle_command_s vehicle_command;
while (_vehicle_command_sub.update(&vehicle_command)) {
if (vehicle_command.command != vehicle_command_s::VEHICLE_CMD_INJECT_FAILURE) {
continue;
}
bool handled = false;
bool supported = false;
const int failure_unit = static_cast<int>(vehicle_command.param1 + 0.5f);
const int failure_type = static_cast<int>(vehicle_command.param2 + 0.5f);
if (failure_unit == vehicle_command_s::FAILURE_UNIT_SENSOR_MAG) {
handled = true;
if (failure_type == vehicle_command_s::FAILURE_TYPE_OFF) {
PX4_WARN("CMD_INJECT_FAILURE, mag off");
supported = true;
_mag_blocked = true;
} else if (failure_type == vehicle_command_s::FAILURE_TYPE_OK) {
PX4_INFO("CMD_INJECT_FAILURE, mag ok");
supported = true;
_mag_blocked = false;
}
}
if (handled) {
vehicle_command_ack_s ack{};
ack.command = vehicle_command.command;
ack.from_external = false;
ack.result = supported ?
vehicle_command_ack_s::VEHICLE_CMD_RESULT_ACCEPTED :
vehicle_command_ack_s::VEHICLE_CMD_RESULT_UNSUPPORTED;
ack.timestamp = hrt_absolute_time();
_command_ack_pub.publish(ack);
}
}
}
void SensorMagSim::Run()
{
if (should_exit()) {
@@ -106,6 +149,13 @@ void SensorMagSim::Run()
updateParams();
}
check_failure_injections();
if (_mag_blocked) {
perf_end(_loop_perf);
return;
}
if (_vehicle_global_position_sub.updated()) {
vehicle_global_position_s gpos;
@@ -44,6 +44,8 @@
#include <uORB/SubscriptionInterval.hpp>
#include <uORB/topics/parameter_update.h>
#include <uORB/topics/vehicle_attitude.h>
#include <uORB/topics/vehicle_command.h>
#include <uORB/topics/vehicle_command_ack.h>
#include <uORB/topics/vehicle_global_position.h>
using namespace time_literals;
@@ -67,6 +69,7 @@ public:
private:
void Run() override;
void check_failure_injections();
// generate white Gaussian noise sample with std=1
static float generate_wgn();
@@ -77,9 +80,14 @@ private:
uORB::SubscriptionInterval _parameter_update_sub{ORB_ID(parameter_update), 1_s};
uORB::Subscription _vehicle_attitude_sub{ORB_ID(vehicle_attitude_groundtruth)};
uORB::Subscription _vehicle_global_position_sub{ORB_ID(vehicle_global_position_groundtruth)};
uORB::Subscription _vehicle_command_sub{ORB_ID(vehicle_command)};
uORB::Publication<vehicle_command_ack_s> _command_ack_pub{ORB_ID(vehicle_command_ack)};
PX4Magnetometer _px4_mag{197388, ROTATION_NONE}; // 197388: DRV_MAG_DEVTYPE_MAGSIM, BUS: 1, ADDR: 3, TYPE: SIMULATION
bool _mag_blocked{false};
bool _mag_earth_available{false};
matrix::Vector3f _mag_earth_pred{};
@@ -246,16 +246,17 @@ if(gazebo_FOUND)
add_custom_target(gazebo-classic DEPENDS gazebo-classic_iris) # alias
add_custom_target(gazebo DEPENDS gazebo-classic_iris) # alias
# mavsdk tests currently depend on sitl_gazebo
ExternalProject_Add(mavsdk_tests
SOURCE_DIR ${PX4_SOURCE_DIR}/test/mavsdk_tests
CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX}
BINARY_DIR ${PX4_BINARY_DIR}/mavsdk_tests
INSTALL_COMMAND ""
USES_TERMINAL_CONFIGURE true
USES_TERMINAL_BUILD true
EXCLUDE_FROM_ALL true
BUILD_ALWAYS 1
)
endif()
# mavsdk tests DO NOT depend on sitl_gazebo
include(ExternalProject)
ExternalProject_Add(mavsdk_tests
SOURCE_DIR ${PX4_SOURCE_DIR}/test/mavsdk_tests
CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX}
BINARY_DIR ${PX4_BINARY_DIR}/mavsdk_tests
INSTALL_COMMAND ""
USES_TERMINAL_CONFIGURE true
USES_TERMINAL_BUILD true
EXCLUDE_FROM_ALL true
BUILD_ALWAYS 1
)
+119 -3
View File
@@ -148,6 +148,22 @@ void Sih::lockstep_loop()
} else {
px4_lockstep_wait_for_components();
// Wait for the control pipeline to produce new actuator outputs.
// Without this, under CPU load the controllers may not run between
// SIH iterations, causing stale actuator data and sluggish response.
uint64_t wait_start_us = micros();
constexpr uint64_t actuator_wait_timeout_us = 10'000'000; // 10s wall time
while (!_actuator_out_sub.updated() && !should_exit()) {
if (micros() - wait_start_us > actuator_wait_timeout_us) {
PX4_WARN("SIH lockstep: timed out waiting for actuator_outputs_sim");
break;
}
usleep(100);
}
current_wall_time_us = micros();
sleep_time = math::max(0, rt_interval_us - (int)(current_wall_time_us - pre_compute_wall_time_us));
}
@@ -190,6 +206,91 @@ void Sih::realtime_loop()
px4_sem_destroy(&_data_semaphore);
}
void Sih::check_failure_injections()
{
vehicle_command_s vehicle_command;
while (_vehicle_command_sub.update(&vehicle_command)) {
if (vehicle_command.command != vehicle_command_s::VEHICLE_CMD_INJECT_FAILURE) {
continue;
}
bool handled = false;
bool supported = false;
const int failure_unit = static_cast<int>(vehicle_command.param1 + 0.5f);
const int failure_type = static_cast<int>(vehicle_command.param2 + 0.5f);
if (failure_unit == vehicle_command_s::FAILURE_UNIT_SENSOR_AIRSPEED) {
handled = true;
if (failure_type == vehicle_command_s::FAILURE_TYPE_OFF) {
PX4_WARN("CMD_INJECT_FAILURE, airspeed off");
supported = true;
_airspeed_blocked = true;
} else if (failure_type == vehicle_command_s::FAILURE_TYPE_OK) {
PX4_INFO("CMD_INJECT_FAILURE, airspeed ok");
supported = true;
_airspeed_blocked = false;
}
} else if (failure_unit == vehicle_command_s::FAILURE_UNIT_SENSOR_DISTANCE_SENSOR) {
handled = true;
if (failure_type == vehicle_command_s::FAILURE_TYPE_OFF) {
PX4_WARN("CMD_INJECT_FAILURE, distance sensor off");
supported = true;
_distance_sensor_blocked = true;
} else if (failure_type == vehicle_command_s::FAILURE_TYPE_OK) {
PX4_INFO("CMD_INJECT_FAILURE, distance sensor ok");
supported = true;
_distance_sensor_blocked = false;
}
} else if (failure_unit == vehicle_command_s::FAILURE_UNIT_SENSOR_ACCEL) {
handled = true;
if (failure_type == vehicle_command_s::FAILURE_TYPE_OFF) {
PX4_WARN("CMD_INJECT_FAILURE, accel off");
supported = true;
_accel_blocked = true;
} else if (failure_type == vehicle_command_s::FAILURE_TYPE_OK) {
PX4_INFO("CMD_INJECT_FAILURE, accel ok");
supported = true;
_accel_blocked = false;
}
} else if (failure_unit == vehicle_command_s::FAILURE_UNIT_SENSOR_GYRO) {
handled = true;
if (failure_type == vehicle_command_s::FAILURE_TYPE_OFF) {
PX4_WARN("CMD_INJECT_FAILURE, gyro off");
supported = true;
_gyro_blocked = true;
} else if (failure_type == vehicle_command_s::FAILURE_TYPE_OK) {
PX4_INFO("CMD_INJECT_FAILURE, gyro ok");
supported = true;
_gyro_blocked = false;
}
}
if (handled) {
vehicle_command_ack_s ack{};
ack.command = vehicle_command.command;
ack.from_external = false;
ack.result = supported ?
vehicle_command_ack_s::VEHICLE_CMD_RESULT_ACCEPTED :
vehicle_command_ack_s::VEHICLE_CMD_RESULT_UNSUPPORTED;
ack.timestamp = hrt_absolute_time();
_command_ack_pub.publish(ack);
}
}
}
void Sih::sensor_step()
{
// check for parameter updates
@@ -203,6 +304,8 @@ void Sih::sensor_step()
parameters_updated();
}
check_failure_injections();
perf_begin(_loop_perf);
const hrt_abstime now = hrt_absolute_time();
@@ -619,7 +722,7 @@ void Sih::reconstruct_sensors_signals(const hrt_abstime &time_now_us)
Vector3f accel_noise;
Vector3f gyro_noise;
if (_T_B.longerThan(FLT_EPSILON)) {
if (false && _T_B.longerThan(FLT_EPSILON)) { // too much noise at least for the low update rate O.O
accel_noise = noiseGauss3f(0.5f, 1.7f, 1.4f);
gyro_noise = noiseGauss3f(0.14f, 0.07f, 0.03f);
@@ -636,12 +739,21 @@ void Sih::reconstruct_sensors_signals(const hrt_abstime &time_now_us)
Vector3f gyro = _w_B + earth_spin_rate_B + gyro_noise;
// update IMU every iteration
_px4_accel.update(time_now_us, accel(0), accel(1), accel(2));
_px4_gyro.update(time_now_us, gyro(0), gyro(1), gyro(2));
if (!_accel_blocked) {
_px4_accel.update(time_now_us, accel(0), accel(1), accel(2));
}
if (!_gyro_blocked) {
_px4_gyro.update(time_now_us, gyro(0), gyro(1), gyro(2));
}
}
void Sih::send_airspeed(const hrt_abstime &time_now_us)
{
if (_airspeed_blocked) {
return;
}
// TODO: send differential pressure instead?
airspeed_s airspeed{};
airspeed.timestamp_sample = time_now_us;
@@ -656,6 +768,10 @@ void Sih::send_airspeed(const hrt_abstime &time_now_us)
void Sih::send_dist_snsr(const hrt_abstime &time_now_us)
{
if (_distance_sensor_blocked) {
return;
}
device::Device::DeviceId device_id;
device_id.devid_s.bus_type = device::Device::DeviceBusType::DeviceBusType_SIMULATION;
device_id.devid_s.bus = 0;
@@ -77,6 +77,8 @@
#include <uORB/topics/distance_sensor.h>
#include <uORB/topics/vehicle_angular_velocity.h>
#include <uORB/topics/vehicle_attitude.h>
#include <uORB/topics/vehicle_command.h>
#include <uORB/topics/vehicle_command_ack.h>
#include <uORB/topics/vehicle_global_position.h>
#include <uORB/topics/vehicle_local_position.h>
@@ -117,6 +119,7 @@ public:
private:
void parameters_updated();
void check_failure_injections();
// simulated sensors
PX4Accelerometer _px4_accel{1310988}; // 1310988: DRV_IMU_DEVTYPE_SIM, BUS: 1, ADDR: 1, TYPE: SIMULATION
@@ -132,6 +135,14 @@ private:
uORB::SubscriptionInterval _parameter_update_sub{ORB_ID(parameter_update), 1_s};
uORB::Subscription _actuator_out_sub{ORB_ID(actuator_outputs)};
uORB::Subscription _vehicle_command_sub{ORB_ID(vehicle_command)};
uORB::Publication<vehicle_command_ack_s> _command_ack_pub{ORB_ID(vehicle_command_ack)};
bool _airspeed_blocked{false};
bool _distance_sensor_blocked{false};
bool _accel_blocked{false};
bool _gyro_blocked{false};
// hard constants
static constexpr uint16_t NUM_ACTUATORS_MAX = 9;
@@ -212,7 +212,7 @@ PARAM_DEFINE_FLOAT(SIH_L_PITCH, 0.2f);
* @increment 0.05
* @group Simulation In Hardware
*/
PARAM_DEFINE_FLOAT(SIH_KDV, 1.0f);
PARAM_DEFINE_FLOAT(SIH_KDV, 0.15f);
/**
* First order angular damper coefficient
+28 -74
View File
@@ -225,18 +225,10 @@ void AutopilotTester::wait_until_hovering()
void AutopilotTester::wait_until_altitude(float rel_altitude_m, std::chrono::seconds timeout, float delta)
{
auto prom = std::promise<void> {};
auto fut = prom.get_future();
Telemetry::PositionHandle handle = _telemetry->subscribe_position([&prom, rel_altitude_m, delta, &handle,
this](Telemetry::Position new_position) {
if (fabs(rel_altitude_m - new_position.relative_altitude_m) <= delta) {
_telemetry->unsubscribe_position(handle);
prom.set_value();
}
});
REQUIRE(fut.wait_for(timeout) == std::future_status::ready);
REQUIRE(poll_condition_with_timeout(
[this, rel_altitude_m, delta]() {
return fabs(rel_altitude_m + _telemetry->position_velocity_ned().position.down_m) <= delta;
}, timeout));
}
void AutopilotTester::wait_until_fixedwing(std::chrono::seconds timeout)
@@ -441,7 +433,7 @@ void AutopilotTester::offboard_goto(const Offboard::PositionNedYaw &target, floa
void AutopilotTester::check_mission_item_speed_above(int item_index, float min_speed_m_s)
{
_telemetry->set_rate_velocity_ned(10);
_telemetry->set_rate_velocity_ned(1);
_telemetry->subscribe_velocity_ned([item_index, min_speed_m_s, this](Telemetry::VelocityNed velocity) {
float horizontal = std::hypot(velocity.north_m_s, velocity.east_m_s);
auto progress = _mission->mission_progress();
@@ -640,16 +632,16 @@ void AutopilotTester::start_checking_altitude(const float max_deviation_m)
std::array<float, 3> initial_position = get_current_position_ned();
float target_altitude = initial_position[2];
_check_altitude_handle = _telemetry->subscribe_position([target_altitude, max_deviation_m,
this](Telemetry::Position new_position) {
const float current_deviation = fabs((-target_altitude) - new_position.relative_altitude_m);
_check_altitude_handle = _telemetry->subscribe_position_velocity_ned([target_altitude, max_deviation_m,
this](Telemetry::PositionVelocityNed new_position) {
const float current_deviation = fabs(target_altitude - new_position.position.down_m);
CHECK(current_deviation <= max_deviation_m);
});
}
void AutopilotTester::stop_checking_altitude()
{
_telemetry->unsubscribe_position(_check_altitude_handle);
_telemetry->unsubscribe_position_velocity_ned(_check_altitude_handle);
}
void AutopilotTester::check_tracks_mission_raw(float corridor_radius_m, bool reverse)
@@ -871,22 +863,11 @@ bool AutopilotTester::ground_truth_horizontal_position_far_from(const Telemetry:
void AutopilotTester::start_and_wait_for_mission_sequence(int sequence_number)
{
auto prom = std::promise<void> {};
auto fut = prom.get_future();
Mission::MissionProgressHandle handle = _mission->subscribe_mission_progress(
[&prom, &handle, this, sequence_number](Mission::MissionProgress progress) {
std::cout << time_str() << "Progress: " << progress.current << "/" << progress.total << std::endl;
if (progress.current >= sequence_number) {
_mission->unsubscribe_mission_progress(handle);
prom.set_value();
}
});
REQUIRE(_mission->start_mission() == Mission::Result::Success);
REQUIRE(fut.wait_for(std::chrono::seconds(60)) == std::future_status::ready);
REQUIRE(poll_condition_with_timeout(
[this, sequence_number]() {
return _mission->mission_progress().current >= sequence_number;
}, std::chrono::seconds(60)));
}
void AutopilotTester::start_and_wait_for_mission_sequence_raw(int sequence_number)
@@ -911,56 +892,29 @@ void AutopilotTester::start_and_wait_for_mission_sequence_raw(int sequence_numbe
void AutopilotTester::wait_for_flight_mode(Telemetry::FlightMode flight_mode, std::chrono::seconds timeout)
{
auto prom = std::promise<void> {};
auto fut = prom.get_future();
Telemetry::FlightModeHandle handle = _telemetry->subscribe_flight_mode(
[&prom, &handle, flight_mode, this](Telemetry::FlightMode new_flight_mode) {
if (new_flight_mode == flight_mode) {
_telemetry->unsubscribe_flight_mode(handle);
prom.set_value();
}
});
REQUIRE(fut.wait_for(timeout) == std::future_status::ready);
REQUIRE(poll_condition_with_timeout(
[this, flight_mode]() {
return _telemetry->flight_mode() == flight_mode;
}, timeout));
}
void AutopilotTester::wait_for_landed_state(Telemetry::LandedState landed_state, std::chrono::seconds timeout)
{
auto prom = std::promise<void> {};
auto fut = prom.get_future();
Telemetry::LandedStateHandle handle = _telemetry->subscribe_landed_state(
[&prom, &handle, landed_state, this](Telemetry::LandedState new_landed_state) {
if (new_landed_state == landed_state) {
_telemetry->unsubscribe_landed_state(handle);
prom.set_value();
}
});
REQUIRE(fut.wait_for(timeout) == std::future_status::ready);
REQUIRE(poll_condition_with_timeout(
[this, landed_state]() {
return _telemetry->landed_state() == landed_state;
}, timeout));
}
void AutopilotTester::wait_until_speed_lower_than(float speed, std::chrono::seconds timeout)
{
auto prom = std::promise<void> {};
auto fut = prom.get_future();
Telemetry::PositionVelocityNedHandle handle = _telemetry->subscribe_position_velocity_ned(
[&prom, &handle, speed, this](Telemetry::PositionVelocityNed position_velocity_ned) {
std::array<float, 3> current_velocity;
current_velocity[0] = position_velocity_ned.velocity.north_m_s;
current_velocity[1] = position_velocity_ned.velocity.east_m_s;
current_velocity[2] = position_velocity_ned.velocity.down_m_s;
const float current_speed = norm(current_velocity);
if (current_speed <= speed) {
_telemetry->unsubscribe_position_velocity_ned(handle);
prom.set_value();
}
});
REQUIRE(fut.wait_for(timeout) == std::future_status::ready);
REQUIRE(poll_condition_with_timeout(
[this, speed]() {
auto vel = _telemetry->position_velocity_ned().velocity;
return std::sqrt(vel.north_m_s * vel.north_m_s +
vel.east_m_s * vel.east_m_s +
vel.down_m_s * vel.down_m_s) < speed;
}, timeout));
}
void AutopilotTester::wait_for_mission_finished(std::chrono::seconds timeout)
+1 -1
View File
@@ -298,7 +298,7 @@ private:
Telemetry::GroundTruth _home{NAN, NAN, NAN};
mavsdk::Telemetry::PositionHandle _check_altitude_handle{};
mavsdk::Telemetry::PositionVelocityNedHandle _check_altitude_handle{};
std::atomic<bool> _should_exit {false};
std::thread _real_time_report_thread {};
@@ -1,18 +1,17 @@
{
"mode": "sitl",
"simulator": "gazebo",
"model_prefix": "gazebo-classic_",
"mavlink_connection": "udpin://0.0.0.0:14540",
"tests":
[
{
"model": "iris",
"vehicle": "iris",
"test_filter": "[multicopter],[offboard],[offboard_attitude]",
"timeout_min": 10
},
{
"model": "iris",
"vehicle": "iris",
"test_filter": "[offboard_attitude]",
"timeout_min": 10,
"env": {
@@ -22,19 +21,16 @@
},
{
"model": "standard_vtol",
"vehicle": "standard_vtol",
"test_filter": "[vtol], [vtol_wind], [vtol_airspeed_fail]",
"timeout_min": 10
},
{
"model": "tailsitter",
"vehicle": "tailsitter",
"test_filter": "[vtol]",
"timeout_min": 10
},
{
"model": "typhoon_h480",
"vehicle": "typhoon_h480",
"test_filter": "[controlallocation]",
"timeout_min": 10
}
+27
View File
@@ -0,0 +1,27 @@
{
"mode": "sitl",
"model_prefix": "sihsim_",
"mavlink_connection": "udpin://0.0.0.0:14540",
"tests":
[
{
"model": "quadx",
"test_filter": "[multicopter],[offboard],[offboard_attitude]",
"timeout_min": 10
},
{
"model": "quadx",
"test_filter": "[offboard_attitude]",
"timeout_min": 10,
"env": {
"PX4_PARAM_EKF2_EN": 0,
"PX4_PARAM_ATT_EN": 1
}
},
{
"model": "standard_vtol",
"test_filter": "[vtol]",
"timeout_min": 10
}
]
}
@@ -168,7 +168,7 @@ class Px4Runner(Runner):
os.path.join(workspace_dir, "test_data"),
"-d"
]
self.env["PX4_SIM_MODEL"] = "gazebo-classic_" + self.model
self.env["PX4_SIM_MODEL"] = model
self.env["PX4_SIM_SPEED_FACTOR"] = str(speed_factor)
self.debugger = debugger
self.clear_rootfs()
@@ -302,7 +302,7 @@ class Tester:
self.active_runners = []
if self.config['mode'] == 'sitl':
if self.config['simulator'] == 'gazebo':
if self.config.get('simulator') == 'gazebo':
# Use RegEx to extract worldname.world from case name
match = re.search(r'\((.*?\.world)\)', case)
if match:
@@ -313,7 +313,7 @@ class Tester:
gzserver_runner = ph.GzserverRunner(
os.getcwd(),
log_dir,
test['vehicle'],
test['model'],
case,
self.get_max_speed_factor(test),
self.verbose,
@@ -324,7 +324,7 @@ class Tester:
gzmodelspawn_runner = ph.GzmodelspawnRunner(
os.getcwd(),
log_dir,
test['vehicle'],
test['model'],
case,
self.verbose,
self.build_dir)
@@ -339,24 +339,23 @@ class Tester:
self.verbose)
self.active_runners.append(gzclient_runner)
# We must start the PX4 instance at the end, as starting
# it in the beginning, then connecting Gazebo server freaks
# out the PX4 (it needs to have data coming in when started),
# and can lead to EKF to freak out, or the instance itself
# to die unexpectedly.
px4_runner = ph.Px4Runner(
os.getcwd(),
log_dir,
test['model'],
case,
self.get_max_speed_factor(test),
self.debugger,
self.verbose,
self.build_dir,
self.tester_interface.rootfs_base_dirname())
for env_key in test.get('env', []):
px4_runner.env[env_key] = str(test['env'][env_key])
self.active_runners.append(px4_runner)
# Determine PX4_SIM_MODEL using model_prefix from config
model_prefix = self.config.get('model_prefix', '')
px4_model = model_prefix + test['model']
px4_runner = ph.Px4Runner(
os.getcwd(),
log_dir,
px4_model,
case,
self.get_max_speed_factor(test),
self.debugger,
self.verbose,
self.build_dir,
self.tester_interface.rootfs_base_dirname())
for env_key in test.get('env', []):
px4_runner.env[env_key] = str(test['env'][env_key])
self.active_runners.append(px4_runner)
self.active_runners.append(self.tester_interface.create_test_runner(
os.getcwd(),
+3 -7
View File
@@ -136,11 +136,9 @@ def is_everything_ready(config: Dict[str, str], build_dir: str) -> bool:
result = False
if not os.path.isfile(os.path.join(build_dir, 'bin/px4')):
print("PX4 SITL is not built\n"
"run `DONT_RUN=1 "
"make px4_sitl gazebo mavsdk_tests` or "
"`DONT_RUN=1 make px4_sitl_default gazebo mavsdk_tests`")
"run `DONT_RUN=1 make px4_sitl_default mavsdk_tests`")
result = False
if config['simulator'] == 'gazebo':
if config.get('simulator') == 'gazebo':
if is_running('gzserver'):
print("gzserver process already running\n"
"run `killall gzserver` and try again")
@@ -153,9 +151,7 @@ def is_everything_ready(config: Dict[str, str], build_dir: str) -> bool:
if not os.path.isfile(os.path.join(build_dir,
'mavsdk_tests/mavsdk_tests')):
print("Test runner is not built\n"
"run `DONT_RUN=1 "
"make px4_sitl gazebo mavsdk_tests` or "
"`DONT_RUN=1 make px4_sitl_default gazebo mavsdk_tests`")
"run `DONT_RUN=1 make px4_sitl_default mavsdk_tests`")
result = False
return result
@@ -40,8 +40,6 @@
TEST_CASE("Takeoff and hold position", "[multicopter][vtol]")
{
const float takeoff_altitude = 10.f;
const float altitude_tolerance = 0.2f;
const int delay_seconds = 60.f;
AutopilotTester tester;
tester.connect(connection_url);
@@ -50,16 +48,20 @@ TEST_CASE("Takeoff and hold position", "[multicopter][vtol]")
tester.set_takeoff_altitude(takeoff_altitude);
tester.store_home();
// The sleep here is necessary for the takeoff altitude to be applied properly
std::this_thread::sleep_for(std::chrono::seconds(1));
tester.sleep_for(std::chrono::seconds(1));
// Capture altitude before takeoff
std::array<float, 3> initial_position = tester.get_current_position_ned();
float ground_altitude = -initial_position[2];
// Takeoff
tester.arm();
tester.takeoff();
tester.wait_until_hovering();
tester.wait_until_altitude(takeoff_altitude, std::chrono::seconds(30), altitude_tolerance);
tester.wait_until_altitude(ground_altitude + takeoff_altitude, std::chrono::seconds(15), 0.01f);
// Monitor altitude and fail if it exceeds the tolerance
tester.start_checking_altitude(altitude_tolerance + 0.1);
tester.start_checking_altitude(0.15);
std::this_thread::sleep_for(std::chrono::seconds(delay_seconds));
tester.sleep_for(std::chrono::seconds(15));
}
@@ -90,8 +90,9 @@ TEST_CASE("Fly straight Multicopter Mission", "[multicopter]")
AutopilotTester::MissionOptions mission_options;
mission_options.rtl_at_end = false;
mission_options.fly_through = true;
mission_options.leg_length_m = 40.0;
tester.prepare_straight_mission(mission_options);
tester.check_mission_item_speed_above(2, 4.0);
tester.check_mission_item_speed_above(3, 4.0);
tester.check_tracks_mission(5.f);
tester.arm();
tester.execute_mission();
+1
View File
@@ -1,6 +1,7 @@
{
"mode": "sitl",
"simulator": "gazebo",
"model_prefix": "gazebo-classic_",
"tests":
[
{