Jacob Dahl ce3e62841f
module_base: remove CRTP template pattern to reduce flash bloat (#26476)
* module_base: claude rewrite to remove CRTP bloat

* module_base: apply to all drivers/modules

* format

* fix build errors

* fix missing syntax

* remove reference to module.h in files that need module_base.h

* remove old ModuleBase<T>

* add module_base.cpp to px4_protected_layers.cmake

* fix IridiumSBD can_stop()

* fix IridiumSBD.cpp

* clang-tidy: downcast static cast

* get_instance() template accessor, revert clang-tidy global

* rename module_base.h to module.h

* revert changes in zenoh/Kconfig.topics
2026-02-19 15:17:17 +13:00

1081 lines
40 KiB
C++

#include "mc_raptor.hpp"
#undef OK
#include <rl_tools/inference/applications/l2f/operations_generic.h>
#include <rl_tools/persist/backends/tar/operations_generic.h>
#include <sys/stat.h>
ModuleBase::Descriptor Raptor::desc{task_spawn, custom_command, print_usage};
Raptor::Raptor(): ModuleParams(nullptr), ScheduledWorkItem(MODULE_NAME, px4::wq_configurations::rate_ctrl)
{
// node state
timestamp_last_angular_velocity_set = false;
timestamp_last_local_position_set = false;
timestamp_last_attitude_set = false;
timestamp_last_trajectory_setpoint_set = false;
timestamp_last_vehicle_status_set = false;
previous_trajectory_setpoint_stale = false;
previous_active = false;
timeout_message_sent = false;
timestamp_last_policy_frequency_check_set = false;
last_intermediate_status_set = false;
last_native_status_set = false;
policy_frequency_check_counter = 0;
flightmode_state = FlightModeState::UNREGISTERED;
can_arm = false;
trajectory_setpoint_dt_index = 0;
trajectory_setpoint_dts_full = false;
trajectory_setpoint_invalid_count = 0;
trajectory_setpoint_dt_max_since_reset = 0;
internal_reference_activation_position[0] = 0.0f;
internal_reference_activation_position[1] = 0.0f;
internal_reference_activation_position[2] = 0.0f;
internal_reference_params_changed = false;
_actuator_motors_pub.advertise();
_tune_control_pub.advertise();
}
void Raptor::reset()
{
trajectory_setpoint_dt_index = 0;
trajectory_setpoint_dts_full = false;
trajectory_setpoint_invalid_count = 0;
trajectory_setpoint_dt_max_since_reset = 0;
timestamp_last_trajectory_setpoint_set = false;
for (TI action_i = 0; action_i < EXECUTOR_SPEC::OUTPUT_DIM; action_i++) {
this->previous_action[action_i] = RESET_PREVIOUS_ACTION_VALUE;
}
rlt::reset(device, executor, policy, rng);
}
Raptor::~Raptor()
{
perf_free(_loop_perf);
perf_free(_loop_interval_perf);
}
#ifdef MC_RAPTOR_EMBED_POLICY
bool Raptor::test_policy()
{
#else
bool Raptor::test_policy(FILE *f, TI input_offset, TI output_offset)
{
#endif
using namespace rl_tools::inference::applications::l2f;
#ifndef RL_TOOLS_DISABLE_TEST
// This tests the policy using a known input output pair that has been saved into the policy checkpoint to verify that it has been loaded correctly
using POLICY = EXECUTOR_CONFIG::POLICY_TEST;
POLICY::template Buffer<false> buffers_test;
POLICY::State<false> policy_state_test;
rl_tools::Tensor<rl_tools::tensor::Specification<EXECUTOR_CONFIG::TYPE_POLICY::DEFAULT, TI, rl_tools::tensor::Shape<TI, 1, POLICY::OUTPUT_SHAPE::LAST>, false>>
test_output;
rl_tools::Mode<rl_tools::mode::Evaluation<>> mode;
using EXAMPLE_INPUT_SPEC = MC_RAPTOR_EXAMPLE_NAMESPACE::input::SPEC;
using EXAMPLE_OUTPUT_SPEC = MC_RAPTOR_EXAMPLE_NAMESPACE::output::SPEC;
float acc = 0;
uint64_t num_values = 0;
rl_tools::inference::applications::l2f::Action<EXECUTOR_SPEC> action;
for (TI batch_i = 0; batch_i < EXECUTOR_CONFIG::TEST_BATCH_SIZE_ACTUAL; batch_i++) {
rl_tools::reset(device, policy, policy_state_test, rng);
for (TI step_i = 0; step_i < EXECUTOR_CONFIG::TEST_SEQUENCE_LENGTH_ACTUAL; step_i++) {
#ifdef MC_RAPTOR_EMBED_POLICY
const auto step_input = rl_tools::view(device, MC_RAPTOR_EXAMPLE_NAMESPACE::input::container, step_i);
const auto batch_input = rl_tools::view_range(device, step_input, batch_i, rlt::tensor::ViewSpec<0, 1> {});
const auto step_output_target = rl_tools::view(device, MC_RAPTOR_EXAMPLE_NAMESPACE::output::container, step_i);
const auto batch_output_target = rl_tools::view_range(device, step_output_target, batch_i, rlt::tensor::ViewSpec<0, 1> {});
#else
rl_tools::Tensor<rl_tools::tensor::Specification<EXECUTOR_CONFIG::TYPE_POLICY::DEFAULT, TI, rl_tools::tensor::Shape<TI, 1, EXAMPLE_INPUT_SPEC::SHAPE::LAST>, false>>
batch_input;
rl_tools::Tensor<rl_tools::tensor::Specification<EXECUTOR_CONFIG::TYPE_POLICY::DEFAULT, TI, rl_tools::tensor::Shape<TI, 1, EXAMPLE_OUTPUT_SPEC::SHAPE::LAST>, false>>
batch_output_target;
fseek(f, input_offset + (step_i * EXAMPLE_INPUT_SPEC::STRIDE::FIRST + batch_i * EXAMPLE_INPUT_SPEC::STRIDE::template GET<1>)*sizeof(
EXAMPLE_INPUT_SPEC::T), SEEK_SET);
fread(batch_input._data, sizeof(EXAMPLE_INPUT_SPEC::T), EXAMPLE_INPUT_SPEC::SHAPE::LAST, f);
fseek(f, output_offset + (step_i * EXAMPLE_OUTPUT_SPEC::STRIDE::FIRST + batch_i * EXAMPLE_OUTPUT_SPEC::STRIDE::template GET<1>)*sizeof(
EXAMPLE_OUTPUT_SPEC::T), SEEK_SET);
fread(batch_output_target._data, sizeof(EXAMPLE_OUTPUT_SPEC::T), EXAMPLE_OUTPUT_SPEC::SHAPE::LAST, f);
#endif
rl_tools::utils::assert_exit(device, !rl_tools::is_nan(device, batch_input), "input is nan");
rl_tools::evaluate_step(device, policy, batch_input, policy_state_test, test_output, buffers_test, rng, mode);
rl_tools::utils::assert_exit(device, !rl_tools::is_nan(device, test_output), "output is nan");
for (TI action_i = 0; action_i < EXECUTOR_CONFIG::OUTPUT_DIM; action_i++) {
acc += rl_tools::math::abs(device.math, rl_tools::get(device, test_output, 0, action_i) - rl_tools::get(device, batch_output_target, 0,
action_i));
num_values += 1;
rl_tools::utils::assert_exit(device, !rl_tools::math::is_nan(device.math, acc), "output is nan");
if (batch_i == 0 && step_i == EXECUTOR_CONFIG::TEST_SEQUENCE_LENGTH_ACTUAL - 1) {
action.action[action_i] = rl_tools::get(device, test_output, 0, action_i);
}
}
}
}
float abs_diff = acc / num_values;
PX4_INFO("Checkpoint test diff: %f", (double)abs_diff);
for (TI output_i = 0; output_i < EXECUTOR_CONFIG::OUTPUT_DIM; output_i++) {
PX4_INFO("output[%d]: %f", (int)output_i, (double)action.action[output_i]);
}
constexpr float EPSILON = 1e-5;
bool healthy = abs_diff < EPSILON;
if (!healthy) {
PX4_ERR("Checkpoint test failed with diff %.10f", (double)abs_diff);
return false;
} else {
PX4_INFO("Checkpoint test passed with diff %.10f", (double)abs_diff);
return true;
}
#else
return 0;
#endif
}
bool Raptor::init()
{
this->init_time = hrt_absolute_time();
if (!_vehicle_angular_velocity_sub.registerCallback()) {
PX4_ERR("vehicle_angular_velocity_sub callback registration failed");
return false;
}
#ifndef MC_RAPTOR_EMBED_POLICY
const char *path = PX4_STORAGEDIR "/raptor/policy.tar";
struct stat st;
bool file_exists = (stat(path, &st) == 0);
if (file_exists) {
PX4_INFO("Policy checkpoint %s exists", path);
FILE *f = fopen(path, "rb");
if (!f) {
PX4_ERR("Failed to open %s: %s", path, strerror(errno));
return false;
}
if (fseek(f, 0, SEEK_END) != 0) {
PX4_ERR("fseek failed: %s", strerror(errno));
fclose(f);
return false;
}
long size = ftell(f);
if (size < 0) {
PX4_ERR("ftell failed: %s", strerror(errno));
fclose(f);
return false;
} else {
rewind(f);
bool successfully_loaded = false;
using SPEC = rlt::persist::backends::tar::ReaderGroupSpecification<TI, rlt::persist::backends::tar::PosixFileData<TI>>;
rlt::persist::backends::tar::ReaderGroup<SPEC> reader_group;
reader_group.data.f = f;
reader_group.data.size = size;
auto actor_group = rlt::get_group(device, reader_group, "actor");
successfully_loaded = rlt::load(device, policy, actor_group);
constexpr TI METADATA_BUFFER_SIZE = 256;
char metadata_buffer[METADATA_BUFFER_SIZE];
TI read_size = 0;
rlt::persist::backends::tar::get(device, reader_group.data, "actor/meta", metadata_buffer, METADATA_BUFFER_SIZE, read_size);
TI checkpoint_name_position = 0;
TI checkpoint_name_len = 0;
if (rlt::persist::backends::tar::seek_in_metadata(device, metadata_buffer, METADATA_BUFFER_SIZE, "checkpoint_name",
checkpoint_name_position, checkpoint_name_len)) {
strncpy(checkpoint_name, metadata_buffer + checkpoint_name_position, CHECKPOINT_NAME_LENGTH);
checkpoint_name[checkpoint_name_len < CHECKPOINT_NAME_LENGTH ? checkpoint_name_len : CHECKPOINT_NAME_LENGTH - 1] = '\0';
} else {
PX4_ERR("Failed to get checkpoint name from metadata");
return false;
}
if (successfully_loaded) {
PX4_INFO("Policy loaded from file %s", path);
TI input_offset = 0;
TI input_size = 0;
rlt::persist::backends::tar::seek(device, reader_group.data, "example/input/data", input_offset, input_size);
PX4_INFO("Input offset: %d", (int)input_offset);
TI output_offset = 0;
TI output_size = 0;
rlt::persist::backends::tar::seek(device, reader_group.data, "example/output/data", output_offset, output_size);
PX4_INFO("Output offset: %d", (int)output_offset);
if (!test_policy(f, input_offset, output_offset)) {
PX4_ERR("Checkpoint test failed");
return false;
}
} else {
PX4_ERR("Failed to load policy from file %s", path);
return false;
}
fclose(f);
}
} else {
PX4_INFO("File %s does not exist", path);
return false;
}
#else
strncpy(checkpoint_name, MC_RAPTOR_META_NAMESPACE::name, CHECKPOINT_NAME_LENGTH);
if (!test_policy()) {
PX4_ERR("Checkpoint test failed");
return false;
}
#endif
PX4_INFO("Checkpoint name: %s", checkpoint_name);
register_ext_component_request_s register_ext_component_request{};
register_ext_component_request.timestamp = hrt_absolute_time();
strncpy(register_ext_component_request.name, "RAPTOR", sizeof(register_ext_component_request.name) - 1);
register_ext_component_request.request_id = Raptor::EXT_COMPONENT_REQUEST_ID;
register_ext_component_request.px4_ros2_api_version = 1;
register_ext_component_request.register_arming_check = true;
register_ext_component_request.register_mode = true;
register_ext_component_request.enable_replace_internal_mode = _param_mc_raptor_offboard.get();
register_ext_component_request.replace_internal_mode = vehicle_status_s::NAVIGATION_STATE_OFFBOARD;
_register_ext_component_request_pub.publish(register_ext_component_request);
int32_t imu_gyro_ratemax = _param_imu_gyro_ratemax.get();
if (imu_gyro_ratemax % POLICY_CONTROL_FREQUENCY_TRAINING != 0) {
PX4_WARN("IMU_GYRO_RATEMAX=%d Hz is not a multiple of the training frequency (%d Hz)", (int)imu_gyro_ratemax,
(int)POLICY_CONTROL_FREQUENCY_TRAINING);
}
int32_t force_sync_native = imu_gyro_ratemax / POLICY_CONTROL_FREQUENCY_TRAINING;
executor.executor.force_sync_native = force_sync_native;
executor.executor.force_sync_native_initialized = true;
PX4_INFO("IMU_GYRO_RATEMAX=%d Hz", (int)imu_gyro_ratemax);
PX4_INFO("POLICY_CONTROL_FREQUENCY_TRAINING=%d Hz", (int)POLICY_CONTROL_FREQUENCY_TRAINING);
PX4_INFO("Setting force_sync_native = %d Hz / %d Hz = %d", (int)imu_gyro_ratemax, (int)POLICY_CONTROL_FREQUENCY_TRAINING,
(int)force_sync_native);
this->use_internal_reference = _param_mc_raptor_intref.get();
this->reset();
return true;
}
template <typename T>
T clip(T x, T max, T min)
{
if (x > max) {
return max;
}
if (x < min) {
return min;
}
return x;
}
template <typename T>
void quaternion_multiplication(T q1[4], T q2[4], T q_res[4])
{
q_res[0] = q1[0] * q2[0] - q1[1] * q2[1] - q1[2] * q2[2] - q1[3] * q2[3];
q_res[1] = q1[0] * q2[1] + q1[1] * q2[0] + q1[2] * q2[3] - q1[3] * q2[2];
q_res[2] = q1[0] * q2[2] - q1[1] * q2[3] + q1[2] * q2[0] + q1[3] * q2[1];
q_res[3] = q1[0] * q2[3] + q1[1] * q2[2] - q1[2] * q2[1] + q1[3] * q2[0];
}
template <typename T>
void quaternion_conjugate(T q[4], T q_res[4])
{
q_res[0] = +q[0];
q_res[1] = -q[1];
q_res[2] = -q[2];
q_res[3] = -q[3];
}
template <typename T>
void quaternion_to_rotation_matrix(T q[4], T R[9])
{
// row-major
T qw = q[0];
T qx = q[1];
T qy = q[2];
T qz = q[3];
R[0] = 1 - 2 * qy * qy - 2 * qz * qz;
R[1] = 2 * qx * qy - 2 * qw * qz;
R[2] = 2 * qx * qz + 2 * qw * qy;
R[3] = 2 * qx * qy + 2 * qw * qz;
R[4] = 1 - 2 * qx * qx - 2 * qz * qz;
R[5] = 2 * qy * qz - 2 * qw * qx;
R[6] = 2 * qx * qz - 2 * qw * qy;
R[7] = 2 * qy * qz + 2 * qw * qx;
R[8] = 1 - 2 * qx * qx - 2 * qy * qy;
}
template <typename T>
void rotate_vector(T R[9], T v[3], T v_rotated[3])
{
v_rotated[0] = R[0] * v[0] + R[1] * v[1] + R[2] * v[2];
v_rotated[1] = R[3] * v[0] + R[4] * v[1] + R[5] * v[2];
v_rotated[2] = R[6] * v[0] + R[7] * v[1] + R[8] * v[2];
}
void Raptor::observe(rl_tools::inference::applications::l2f::Observation<EXECUTOR_SPEC> &observation)
{
// converting from FRD to FLU
T Rt_inv[9];
{
// Orientation
// FRD to FLU
T q_target[4];
q_target[0] = cosf(0.5f * _trajectory_setpoint.yaw); // minus because the setpoint yaw is in NED
q_target[1] = 0;
q_target[2] = 0;
q_target[3] = sinf(0.5f * _trajectory_setpoint.yaw);
T qt[4], qtc[4], qr[4];
qt[0] = +q_target[0]; // conjugate to build the difference between setpoint and current
qt[1] = +q_target[1];
qt[2] = -q_target[2];
qt[3] = -q_target[3];
quaternion_conjugate(qt, qtc);
quaternion_to_rotation_matrix(qtc, Rt_inv);
qr[0] = +_vehicle_attitude.q[0];
qr[1] = +_vehicle_attitude.q[1];
qr[2] = -_vehicle_attitude.q[2];
qr[3] = -_vehicle_attitude.q[3];
// qr = qt * qd
// qd = qt' * qr
T qd[4];
quaternion_multiplication(qtc, qr, qd);
observation.orientation[0] = qd[0];
observation.orientation[1] = qd[1];
observation.orientation[2] = qd[2];
observation.orientation[3] = qd[3];
}
{
// Position
T p[3], pt[3]; // FLU
p[0] = +(position[0] - _trajectory_setpoint.position[0]);
p[1] = -(position[1] - _trajectory_setpoint.position[1]);
p[2] = -(position[2] - _trajectory_setpoint.position[2]);
rotate_vector(Rt_inv, p, pt); // The position and velocity error are in the target frame
observation.position[0] = clip(pt[0], max_position_error, -max_position_error);
observation.position[1] = clip(pt[1], max_position_error, -max_position_error);
observation.position[2] = clip(pt[2], max_position_error, -max_position_error);
}
{
// Linear Velocity
T v[3], vt[3];
v[0] = +(linear_velocity[0] - _trajectory_setpoint.velocity[0]);
v[1] = -(linear_velocity[1] - _trajectory_setpoint.velocity[1]);
v[2] = -(linear_velocity[2] - _trajectory_setpoint.velocity[2]);
rotate_vector(Rt_inv, v, vt);
observation.linear_velocity[0] = clip(vt[0], max_velocity_error, -max_velocity_error);
observation.linear_velocity[1] = clip(vt[1], max_velocity_error, -max_velocity_error);
observation.linear_velocity[2] = clip(vt[2], max_velocity_error, -max_velocity_error);
}
{
// Angular Velocity
observation.angular_velocity[0] = +_vehicle_angular_velocity.xyz[0];
observation.angular_velocity[1] = -_vehicle_angular_velocity.xyz[1];
observation.angular_velocity[2] = -_vehicle_angular_velocity.xyz[2];
}
for (TI action_i = 0; action_i < EXECUTOR_CONFIG::OUTPUT_DIM; action_i++) {
observation.previous_action[action_i] = this->previous_action[action_i];
}
}
void Raptor::updateArmingCheckReply()
{
if (flightmode_state == FlightModeState::CONFIGURED) {
if (_arming_check_request_sub.updated()) {
arming_check_request_s arming_check_request;
_arming_check_request_sub.copy(&arming_check_request);
arming_check_reply_s arming_check_reply;
arming_check_reply.timestamp = hrt_absolute_time();
arming_check_reply.request_id = arming_check_request.request_id;
arming_check_reply.registration_id = ext_component_arming_check_id;
arming_check_reply.health_component_index = arming_check_reply.HEALTH_COMPONENT_INDEX_NONE;
arming_check_reply.num_events = 0;
arming_check_reply.can_arm_and_run = can_arm;
arming_check_reply.mode_req_angular_velocity = true;
arming_check_reply.mode_req_local_position = true;
arming_check_reply.mode_req_attitude = true;
arming_check_reply.mode_req_local_alt = true;
arming_check_reply.mode_req_home_position = false;
arming_check_reply.mode_req_mission = false;
arming_check_reply.mode_req_global_position = false;
arming_check_reply.mode_req_prevent_arming = false;
arming_check_reply.mode_req_manual_control = false;
_arming_check_reply_pub.publish(arming_check_reply);
}
}
}
void Raptor::Run()
{
if (should_exit()) {
_vehicle_angular_velocity_sub.unregisterCallback();
if (flightmode_state >= FlightModeState::REGISTERED) {
unregister_ext_component_s unregister_ext_component{};
unregister_ext_component.timestamp = hrt_absolute_time();
strncpy(unregister_ext_component.name, "RAPTOR", sizeof(unregister_ext_component.name) - 1);
unregister_ext_component.arming_check_id = ext_component_arming_check_id;
unregister_ext_component.mode_id = ext_component_mode_id;
unregister_ext_component.mode_executor_id = -1;
_unregister_ext_component_pub.publish(unregister_ext_component);
}
ScheduleClear();
exit_and_cleanup(desc);
return;
}
register_ext_component_reply_s register_ext_component_reply;
if (_register_ext_component_reply_sub.update(&register_ext_component_reply)) {
if (register_ext_component_reply.request_id == Raptor::EXT_COMPONENT_REQUEST_ID && register_ext_component_reply.success) {
ext_component_arming_check_id = register_ext_component_reply.arming_check_id;
ext_component_mode_id = register_ext_component_reply.mode_id;
flightmode_state = FlightModeState::REGISTERED;
PX4_INFO("Raptor mode registration successful, arming_check_id: %d, mode_id: %d", ext_component_arming_check_id, ext_component_mode_id);
}
}
if (flightmode_state == FlightModeState::REGISTERED) {
vehicle_control_mode_s config_control_setpoints{};
config_control_setpoints.timestamp = hrt_absolute_time();
config_control_setpoints.source_id = ext_component_mode_id;
config_control_setpoints.flag_multicopter_position_control_enabled = false;
config_control_setpoints.flag_control_manual_enabled = false;
config_control_setpoints.flag_control_offboard_enabled = false;
config_control_setpoints.flag_control_position_enabled = false;
config_control_setpoints.flag_control_climb_rate_enabled = false;
config_control_setpoints.flag_control_allocation_enabled = false;
config_control_setpoints.flag_control_termination_enabled = true;
_config_control_setpoints_pub.publish(config_control_setpoints);
flightmode_state = FlightModeState::CONFIGURED;
PX4_INFO("Raptor mode configuration sent");
}
perf_count(_loop_interval_perf);
perf_begin(_loop_perf);
hrt_abstime current_time = hrt_absolute_time();
raptor_status_s status{};
status.timestamp = current_time;
status.timestamp_sample = current_time;
status.exit_reason = raptor_status_s::EXIT_REASON_NONE;
status.substep = 0;
status.active = false;
status.control_interval = NAN;
status.trajectory_setpoint_dt_mean = NAN;
status.trajectory_setpoint_dt_max = NAN;
status.trajectory_setpoint_dt_max_since_activation = NAN;
if (trajectory_setpoint_dts_full || trajectory_setpoint_dt_index > 0) {
float trajectory_setpoint_dt_mean = 0;
float trajectory_setpoint_dt_max = 0;
for (TI i = 0; i < (trajectory_setpoint_dts_full ? NUM_TRAJECTORY_SETPOINT_DTS : trajectory_setpoint_dt_index); i++) {
TI index = trajectory_setpoint_dts_full ? i : trajectory_setpoint_dt_index - 1 - i;
trajectory_setpoint_dt_mean += trajectory_setpoint_dts[index];
if (trajectory_setpoint_dts[index] > trajectory_setpoint_dt_max) {
trajectory_setpoint_dt_max = trajectory_setpoint_dts[index];
}
}
if (trajectory_setpoint_dt_max > trajectory_setpoint_dt_max_since_reset) {
trajectory_setpoint_dt_max_since_reset = trajectory_setpoint_dt_max;
}
trajectory_setpoint_dt_mean /= NUM_TRAJECTORY_SETPOINT_DTS;
status.trajectory_setpoint_dt_mean = trajectory_setpoint_dt_mean;
status.trajectory_setpoint_dt_max = trajectory_setpoint_dt_max;
status.trajectory_setpoint_dt_max_since_activation = trajectory_setpoint_dt_max_since_reset;
}
status.subscription_update_vehicle_status = _vehicle_status_sub.update(&_vehicle_status);
if (status.subscription_update_vehicle_status) {
timestamp_last_vehicle_status = current_time;
timestamp_last_vehicle_status_set = true;
}
bool next_active = timestamp_last_vehicle_status_set && _vehicle_status.nav_state == ext_component_mode_id;
if (!previous_active && next_active) {
this->reset();
PX4_INFO("Resetting Inference Executor (Recurrent State)");
} else {
if (previous_active && !next_active) {
PX4_INFO("inactive");
}
}
bool angular_velocity_update = false;
status.subscription_update_angular_velocity = _vehicle_angular_velocity_sub.update(&_vehicle_angular_velocity);
if (status.subscription_update_angular_velocity) {
timestamp_last_angular_velocity = current_time;
timestamp_last_angular_velocity_set = true;
angular_velocity_update = true;
}
status.timestamp_last_vehicle_angular_velocity = current_time;
status.timestamp_sample = _vehicle_angular_velocity.timestamp_sample;
status.subscription_update_local_position = _vehicle_local_position_sub.update(&_vehicle_local_position);
if (status.subscription_update_local_position) {
timestamp_last_local_position = current_time;
timestamp_last_local_position_set = true;
}
status.timestamp_last_vehicle_local_position = current_time;
status.subscription_update_attitude = _vehicle_attitude_sub.update(&_vehicle_attitude);
if (status.subscription_update_attitude) {
timestamp_last_attitude = current_time;
timestamp_last_attitude_set = true;
}
status.timestamp_last_vehicle_attitude = timestamp_last_attitude;
trajectory_setpoint_s temp_trajectory_setpoint;
bool use_external_reference = !use_internal_reference;
status.subscription_update_trajectory_setpoint = use_external_reference && _trajectory_setpoint_sub.update(&temp_trajectory_setpoint);
if (status.subscription_update_trajectory_setpoint) {
if (
PX4_ISFINITE(temp_trajectory_setpoint.position[0]) &&
PX4_ISFINITE(temp_trajectory_setpoint.position[1]) &&
PX4_ISFINITE(temp_trajectory_setpoint.position[2]) &&
PX4_ISFINITE(temp_trajectory_setpoint.yaw) &&
PX4_ISFINITE(temp_trajectory_setpoint.velocity[0]) &&
PX4_ISFINITE(temp_trajectory_setpoint.velocity[1]) &&
PX4_ISFINITE(temp_trajectory_setpoint.velocity[2]) &&
PX4_ISFINITE(temp_trajectory_setpoint.yawspeed)
) {
if (timestamp_last_trajectory_setpoint_set) {
trajectory_setpoint_dts[trajectory_setpoint_dt_index] = current_time - timestamp_last_trajectory_setpoint;
trajectory_setpoint_dt_index++;
if (trajectory_setpoint_dt_index == NUM_TRAJECTORY_SETPOINT_DTS) {
if (next_active && !trajectory_setpoint_dts_full) {
PX4_INFO("trajectory_setpoint_dts_full");
}
trajectory_setpoint_dts_full = true;
trajectory_setpoint_dt_index = 0;
}
}
timestamp_last_trajectory_setpoint_set = true;
status.timestamp_last_trajectory_setpoint = current_time;
timestamp_last_trajectory_setpoint = current_time;
_trajectory_setpoint = temp_trajectory_setpoint;
} else {
trajectory_setpoint_invalid_count++;
if (next_active && trajectory_setpoint_invalid_count % TRAJECTORY_SETPOINT_INVALID_COUNT_WARNING_INTERVAL == 0) {
PX4_WARN("trajectory_setpoint invalid, count: %d", (int)trajectory_setpoint_invalid_count);
}
}
}
constexpr bool PUBLISH_NON_COMPLETE_STATUS = true;
if (!angular_velocity_update) {
status.exit_reason = raptor_status_s::EXIT_REASON_NO_ANGULAR_VELOCITY_UPDATE;
if constexpr(PUBLISH_NON_COMPLETE_STATUS) {
_raptor_status_pub.publish(status);
}
updateArmingCheckReply();
return;
}
if (!timestamp_last_angular_velocity_set || !timestamp_last_local_position_set || !timestamp_last_attitude_set) {
status.exit_reason = raptor_status_s::EXIT_REASON_NOT_ALL_OBSERVATIONS_SET;
status.vehicle_angular_velocity_stale = !timestamp_last_angular_velocity_set;
status.vehicle_local_position_stale = !timestamp_last_local_position_set;
status.vehicle_attitude_stale = !timestamp_last_attitude_set;
if constexpr(PUBLISH_NON_COMPLETE_STATUS) {
_raptor_status_pub.publish(status);
}
can_arm = false;
updateArmingCheckReply();
return;
}
if ((current_time - timestamp_last_angular_velocity) > OBSERVATION_TIMEOUT_ANGULAR_VELOCITY) {
status.exit_reason = raptor_status_s::EXIT_REASON_ANGULAR_VELOCITY_STALE;
if constexpr(PUBLISH_NON_COMPLETE_STATUS) {
_raptor_status_pub.publish(status);
}
if (!timeout_message_sent) {
PX4_ERR("angular velocity timeout");
timeout_message_sent = true;
}
can_arm = false;
updateArmingCheckReply();
return;
}
if ((current_time - timestamp_last_local_position) > OBSERVATION_TIMEOUT_LOCAL_POSITION) {
status.exit_reason = raptor_status_s::EXIT_REASON_LOCAL_POSITION_STALE;
if constexpr(PUBLISH_NON_COMPLETE_STATUS) {
_raptor_status_pub.publish(status);
}
if (!timeout_message_sent) {
PX4_ERR("local position timeout");
timeout_message_sent = true;
}
can_arm = false;
updateArmingCheckReply();
return;
} else {
position[0] = _vehicle_local_position.x;
position[1] = _vehicle_local_position.y;
position[2] = _vehicle_local_position.z;
linear_velocity[0] = _vehicle_local_position.vx;
linear_velocity[1] = _vehicle_local_position.vy;
linear_velocity[2] = _vehicle_local_position.vz;
}
// position and linear_velocity are guaranteed to be set after this point
auto previous_internal_reference = internal_reference;
internal_reference = static_cast<InternalReference>(_param_mc_raptor_intref.get());
bool internal_reference_changed = previous_internal_reference != internal_reference;
if (internal_reference_changed) {
PX4_INFO("internal reference changed from %d to %d", (int)previous_internal_reference, (int)internal_reference);
}
if (use_internal_reference && internal_reference != InternalReference::NONE) {
if (next_active && (!previous_active || internal_reference_changed || internal_reference_params_changed)) {
internal_reference_activation_position[0] = position[0];
internal_reference_activation_position[1] = - position[1];
internal_reference_activation_position[2] = - position[2];
internal_reference_activation_orientation[0] = _vehicle_attitude.q[0];
internal_reference_activation_orientation[1] = _vehicle_attitude.q[1];
internal_reference_activation_orientation[2] = -_vehicle_attitude.q[2];
internal_reference_activation_orientation[3] = -_vehicle_attitude.q[3];
internal_reference_activation_time = current_time;
PX4_INFO("internal reference activated at: %f %f %f", (double)internal_reference_activation_position[0],
(double)internal_reference_activation_position[1], (double)internal_reference_activation_position[2]);
internal_reference_params_changed = false;
}
Setpoint setpoint{};
if (internal_reference == InternalReference::LISSAJOUS) {
setpoint = lissajous(static_cast<T>(current_time - internal_reference_activation_time) / 1000000, lissajous_params);
} else {
PX4_ERR("internal reference type not supported");
}
auto &q = internal_reference_activation_orientation;
matrix::Quatf q_activation_frame(q[0], q[1], q[2], q[3]);
matrix::Vector3f position_activation_frame = q_activation_frame.rotateVector(matrix::Vector3f(setpoint.position[0], setpoint.position[1],
setpoint.position[2]));
matrix::Vector3f linear_velocity_activation_frame = q_activation_frame.rotateVector(matrix::Vector3f(setpoint.linear_velocity[0],
setpoint.linear_velocity[1], setpoint.linear_velocity[2]));
_trajectory_setpoint.position[0] = +(internal_reference_activation_position[0] + position_activation_frame(0));
_trajectory_setpoint.position[1] = -(internal_reference_activation_position[1] + position_activation_frame(1));
_trajectory_setpoint.position[2] = -(internal_reference_activation_position[2] + position_activation_frame(2));
_trajectory_setpoint.yaw = - atan2f(2.0f * (q[1] * q[2] + q[0] * q[3]), 1.0f - 2.0f * (q[2] * q[2] + q[3] * q[3])) - setpoint.yaw;
_trajectory_setpoint.velocity[0] = +linear_velocity_activation_frame(0);
_trajectory_setpoint.velocity[1] = -linear_velocity_activation_frame(1);
_trajectory_setpoint.velocity[2] = -linear_velocity_activation_frame(2);
_trajectory_setpoint.yawspeed = -setpoint.yaw_rate;
timestamp_last_trajectory_setpoint_set = true;
status.timestamp_last_trajectory_setpoint = current_time;
timestamp_last_trajectory_setpoint = current_time;
status.internal_reference_position[0] = _trajectory_setpoint.position[0];
status.internal_reference_position[1] = _trajectory_setpoint.position[1];
status.internal_reference_position[2] = _trajectory_setpoint.position[2];
status.internal_reference_linear_velocity[0] = _trajectory_setpoint.velocity[0];
status.internal_reference_linear_velocity[1] = _trajectory_setpoint.velocity[1];
status.internal_reference_linear_velocity[2] = _trajectory_setpoint.velocity[2];
}
if ((current_time - timestamp_last_attitude) > OBSERVATION_TIMEOUT_ATTITUDE) {
status.exit_reason = raptor_status_s::EXIT_REASON_ATTITUDE_STALE;
if constexpr(PUBLISH_NON_COMPLETE_STATUS) {
_raptor_status_pub.publish(status);
}
if (!timeout_message_sent) {
PX4_ERR("attitude timeout");
timeout_message_sent = true;
}
can_arm = false;
updateArmingCheckReply();
return;
}
timeout_message_sent = false;
// is ready to control at this point
can_arm = true;
updateArmingCheckReply();
if (!timestamp_last_trajectory_setpoint_set || use_internal_reference
|| (current_time - timestamp_last_trajectory_setpoint) > TRAJECTORY_SETPOINT_TIMEOUT) {
status.trajectory_setpoint_stale = true;
if (!previous_trajectory_setpoint_stale || (!previous_active && next_active)) {
_trajectory_setpoint.position[0] = position[0];
_trajectory_setpoint.position[1] = position[1];
_trajectory_setpoint.position[2] = position[2];
auto &q = _vehicle_attitude.q;
_trajectory_setpoint.yaw = atan2f(2.0f * (q[1] * q[2] + q[0] * q[3]), 1.0f - 2.0f * (q[2] * q[2] + q[3] * q[3]));
_trajectory_setpoint.velocity[0] = 0;
_trajectory_setpoint.velocity[1] = 0;
_trajectory_setpoint.velocity[2] = 0;
_trajectory_setpoint.yawspeed = 0;
if (!previous_trajectory_setpoint_stale) {
PX4_WARN("trajectory_setpoint turned stale at: %f %f %f, yaw: %f %llu / %llu us", (double)position[0], (double)position[1],
(double)position[2],
(double)_trajectory_setpoint.yaw, (unsigned long long)(current_time - timestamp_last_trajectory_setpoint),
(unsigned long long)(TRAJECTORY_SETPOINT_TIMEOUT));
} else {
PX4_WARN("trajectory_setpoint reset due to activation at: %f %f %f, yaw: %f", (double)position[0], (double)position[1], (double)position[2],
(double)_trajectory_setpoint.yaw);
}
}
previous_trajectory_setpoint_stale = true;
} else {
if (previous_trajectory_setpoint_stale) {
PX4_WARN("trajectory_setpoint turned non-stale at: %f %f %f", (double)position[0], (double)position[1], (double)position[2]);
previous_trajectory_setpoint_stale = false;
}
status.trajectory_setpoint_stale = false;
}
rl_tools::inference::applications::l2f::Observation<EXECUTOR_SPEC> observation;
rl_tools::inference::applications::l2f::Action<EXECUTOR_SPEC> action;
observe(observation);
hrt_abstime nanoseconds = current_time * 1000;
auto executor_status = rl_tools::control(device, executor, nanoseconds, policy, observation, action, rng);
if (!executor_status.OK) {
if (executor_status.TIMESTAMP_INVALID) {
PX4_ERR("RLtools executor error: Timestamp invalid");
}
if (executor_status.LAST_CONTROL_TIMESTAMP_GREATER_THAN_LAST_OBSERVATION_TIMESTAMP) {
PX4_ERR("RLtools executor error: Last control timestamp %llu greater than last observation timestamp %llu",
(unsigned long long)executor.executor.last_control_timestamp, (unsigned long long)executor.executor.last_observation_timestamp);
}
}
if (executor_status.source != decltype(executor_status.source)::CONTROL) {
// status.exit_reason = raptor_status_s::EXIT_REASON_EXECUTOR_STATUS_SOURCE_NOT_CONTROL;
// if constexpr(PUBLISH_NON_COMPLETE_STATUS){
// _raptor_status_pub.publish(status);
// }
// Exit early if it is not time to control
return;
}
status.active = next_active;
// no return after this point!
raptor_input_s input_msg;
input_msg.active = status.active;
static_assert(raptor_input_s::ACTION_DIM == EXECUTOR_CONFIG::OUTPUT_DIM);
input_msg.timestamp = current_time;
input_msg.timestamp_sample = _vehicle_angular_velocity.timestamp_sample;
for (TI dim_i = 0; dim_i < 3; dim_i++) {
input_msg.position[dim_i] = observation.position[dim_i];
input_msg.orientation[dim_i] = observation.orientation[dim_i];
input_msg.linear_velocity[dim_i] = observation.linear_velocity[dim_i];
input_msg.angular_velocity[dim_i] = observation.angular_velocity[dim_i];
}
input_msg.orientation[3] = observation.orientation[3];
for (TI dim_i = 0; dim_i < EXECUTOR_CONFIG::OUTPUT_DIM; dim_i++) {
input_msg.previous_action[dim_i] = observation.previous_action[dim_i];
}
_raptor_input_pub.publish(input_msg);
_raptor_status_pub.publish(status);
actuator_motors_s actuator_motors{};
actuator_motors.timestamp = hrt_absolute_time();
actuator_motors.timestamp_sample = _vehicle_angular_velocity.timestamp_sample;
for (TI action_i = 0; action_i < actuator_motors_s::NUM_CONTROLS; action_i++) {
if (action_i < EXECUTOR_CONFIG::OUTPUT_DIM) {
T value = action.action[action_i];
this->previous_action[action_i] = value;
value = (value + 1) / 2;
constexpr T training_min = 0;
constexpr T training_max = 1.0;
T scaled_value = (training_max - training_min) * value + training_min;
actuator_motors.control[action_i] = scaled_value;
} else {
actuator_motors.control[action_i] = NAN;
}
}
if constexpr(Raptor::REMAP_FROM_CRAZYFLIE) {
actuator_motors_s temp = actuator_motors;
temp.control[0] = actuator_motors.control[0];
temp.control[1] = actuator_motors.control[2];
temp.control[2] = actuator_motors.control[3];
temp.control[3] = actuator_motors.control[1];
actuator_motors = temp;
}
if (status.active) {
_actuator_motors_pub.publish(actuator_motors);
}
perf_end(_loop_perf);
previous_active = next_active;
if (executor_status.source == decltype(executor_status.source)::CONTROL) {
if (executor_status.step_type == decltype(executor_status.step_type)::INTERMEDIATE) {
this->last_intermediate_status = executor_status;
this->last_intermediate_status_set = true;
} else if (executor_status.step_type == decltype(executor_status.step_type)::NATIVE) {
this->last_native_status = executor_status;
this->last_native_status_set = true;
}
}
if (!this->timestamp_last_policy_frequency_check_set
|| (current_time - timestamp_last_policy_frequency_check) > POLICY_FREQUENCY_CHECK_INTERVAL) {
if (this->timestamp_last_policy_frequency_check_set) {
if (last_intermediate_status_set) {
if (!this->last_intermediate_status.timing_bias.OK || !this->last_intermediate_status.timing_jitter.OK) {
PX4_WARN("Raptor: INTERMEDIATE: BIAS %fx JITTER %fx", (double)this->last_intermediate_status.timing_bias.MAGNITUDE,
(double)this->last_intermediate_status.timing_jitter.MAGNITUDE);
} else {
if (ENABLE_CONTROL_FREQUENCY_INFO && this->policy_frequency_check_counter % POLICY_FREQUENCY_INFO_INTERVAL == 0) {
PX4_INFO("Raptor: INTERMEDIATE: BIAS %fx JITTER %fx", (double)this->last_intermediate_status.timing_bias.MAGNITUDE,
(double)this->last_intermediate_status.timing_jitter.MAGNITUDE);
}
}
}
if (last_native_status_set) {
if (!this->last_native_status.timing_bias.OK || !this->last_native_status.timing_jitter.OK) {
PX4_WARN("Raptor: NATIVE: BIAS %fx JITTER %fx", (double)this->last_native_status.timing_bias.MAGNITUDE,
(double)this->last_native_status.timing_jitter.MAGNITUDE);
} else {
if (ENABLE_CONTROL_FREQUENCY_INFO && this->policy_frequency_check_counter % POLICY_FREQUENCY_INFO_INTERVAL == 0) {
PX4_INFO("Raptor: NATIVE: BIAS %fx JITTER %fx", (double)this->last_native_status.timing_bias.MAGNITUDE,
(double)this->last_native_status.timing_jitter.MAGNITUDE);
}
}
}
}
this->num_healthy_executor_statii_intermediate = 0;
this->num_non_healthy_executor_statii_intermediate = 0;
this->num_healthy_executor_statii_native = 0;
this->num_non_healthy_executor_statii_native = 0;
this->num_statii = 0;
this->timestamp_last_policy_frequency_check = current_time;
this->timestamp_last_policy_frequency_check_set = true;
this->policy_frequency_check_counter++;
}
this->num_statii++;
this->num_healthy_executor_statii_intermediate += executor_status.OK && executor_status.source == decltype(executor_status.source)::CONTROL
&& executor_status.step_type == decltype(executor_status.step_type)::INTERMEDIATE;
this->num_non_healthy_executor_statii_intermediate += (!executor_status.OK)
&& executor_status.source == decltype(executor_status.source)::CONTROL
&& executor_status.step_type == decltype(executor_status.step_type)::INTERMEDIATE;
this->num_healthy_executor_statii_native += executor_status.OK && executor_status.source == decltype(executor_status.source)::CONTROL
&& executor_status.step_type == decltype(executor_status.step_type)::NATIVE;
this->num_non_healthy_executor_statii_native += (!executor_status.OK)
&& executor_status.source == decltype(executor_status.source)::CONTROL
&& executor_status.step_type == decltype(executor_status.step_type)::NATIVE;
}
int Raptor::task_spawn(int argc, char *argv[])
{
Raptor *instance = new Raptor();
if (instance) {
desc.object.store(instance);
desc.task_id = task_id_is_work_queue;
if (instance->init()) {
return PX4_OK;
}
} else {
PX4_ERR("alloc failed");
}
delete instance;
desc.object.store(nullptr);
desc.task_id = -1;
return PX4_ERROR;
}
int Raptor::print_status()
{
perf_print_counter(_loop_perf);
perf_print_counter(_loop_interval_perf);
perf_print_counter(_loop_interval_policy_perf);
PX4_INFO_RAW("Checkpoint: %s\n", checkpoint_name);
return 0;
}
int Raptor::custom_command(int argc, char *argv[])
{
if (argc >= 2 && strcmp(argv[0], "intref") == 0) {
if (strcmp(argv[1], "lissajous") == 0) {
// Usage: mc_raptor intref lissajous <A> <B> <C> <fa> <fb> <fc> <duration> <ramp>
if (argc != 10) {
PX4_ERR("Usage: mc_raptor intref lissajous <A> <B> <C> <fa> <fb> <fc> <duration> <ramp>");
return PX4_ERROR;
}
Raptor *instance = get_instance<Raptor>(desc);
if (instance == nullptr) {
PX4_ERR("mc_raptor is not running");
return PX4_ERROR;
}
instance->lissajous_params.A = strtof(argv[2], nullptr);
instance->lissajous_params.B = strtof(argv[3], nullptr);
instance->lissajous_params.C = strtof(argv[4], nullptr);
instance->lissajous_params.a = strtof(argv[5], nullptr);
instance->lissajous_params.b = strtof(argv[6], nullptr);
instance->lissajous_params.c = strtof(argv[7], nullptr);
instance->lissajous_params.duration = strtof(argv[8], nullptr);
instance->lissajous_params.ramp_duration = strtof(argv[9], nullptr);
instance->internal_reference_params_changed = true;
PX4_INFO("Lissajous params set: A=%.2f B=%.2f C=%.2f fa=%.2f fb=%.2f fc=%.2f duration=%.2f ramp=%.2f \n",
(double)instance->lissajous_params.A,
(double)instance->lissajous_params.B,
(double)instance->lissajous_params.C,
(double)instance->lissajous_params.a,
(double)instance->lissajous_params.b,
(double)instance->lissajous_params.c,
(double)instance->lissajous_params.duration,
(double)instance->lissajous_params.ramp_duration);
return PX4_OK;
}
}
return print_usage("unknown command");
}
int Raptor::print_usage(const char *reason)
{
if (reason) {
PX4_WARN("%s\n", reason);
}
PRINT_MODULE_DESCRIPTION(
R"DESCR_STR(
### Description
RAPTOR Policy Flight Mode
)DESCR_STR");
PRINT_MODULE_USAGE_NAME("mc_raptor", "template");
PRINT_MODULE_USAGE_COMMAND("start");
PRINT_MODULE_USAGE_COMMAND_DESCR("intref", "Modify internal reference");
PRINT_MODULE_USAGE_ARG("lissajous", "Set Lissajous trajectory parameters", false);
PRINT_MODULE_USAGE_ARG("<A>", "Amplitude X [m]", false);
PRINT_MODULE_USAGE_ARG("<B>", "Amplitude Y [m]", false);
PRINT_MODULE_USAGE_ARG("<C>", "Amplitude Z [m]", false);
PRINT_MODULE_USAGE_ARG("<fa>", "Frequency a", false);
PRINT_MODULE_USAGE_ARG("<fb>", "Frequency b", false);
PRINT_MODULE_USAGE_ARG("<fc>", "Frequency c", false);
PRINT_MODULE_USAGE_ARG("<duration>", "Total duration [s]", false);
PRINT_MODULE_USAGE_ARG("<ramp>", "Ramp duration [s]", false);
PRINT_MODULE_USAGE_DEFAULT_COMMANDS();
return 0;
}
extern "C" __EXPORT int mc_raptor_main(int argc, char *argv[])
{
return ModuleBase::main(Raptor::desc, argc, argv);
}