Skip to content

Commit 9cfc773

Browse files
authored
feat: update offboard command message to reflect new vector notation
* feat: move mixing matrix print logic to rosflight_io and add a service call
1 parent a5b5d70 commit 9cfc773

6 files changed

Lines changed: 119 additions & 43 deletions

File tree

rosflight_io/include/rosflight_io/rosflight_io.hpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -519,6 +519,18 @@ class ROSflightIO : public rclcpp::Node,
519519
*/
520520
bool checkIfAllParamsReceivedCallback(const std_srvs::srv::Trigger::Request::SharedPtr & req,
521521
const std_srvs::srv::Trigger::Response::SharedPtr & res);
522+
/**
523+
* @brief "print_mixing_matrix" service callback.
524+
*
525+
* This function is called anytime the "print_mixing_matrix" ROS service is called. It returns
526+
* true.
527+
*
528+
* @param req ROS Trigger service request.
529+
* @param res ROS Trigger service response.
530+
* @return True
531+
*/
532+
bool printMixingMatrixCallback(const std_srvs::srv::Trigger::Request::SharedPtr & req,
533+
const std_srvs::srv::Trigger::Response::SharedPtr & res);
522534

523535
// timer callbacks
524536
/**
@@ -587,6 +599,13 @@ class ROSflightIO : public rclcpp::Node,
587599
* as a convenience for easy read/write access from ROS2
588600
*/
589601
void load_convenience_parameters();
602+
/**
603+
* @brief logs the mixing matrix parameters to the ROS2 logger. This usually prints
604+
* to the screen and is a helpful debugging tool.
605+
*
606+
* @return true if all mixer params are received from the firmware.
607+
*/
608+
bool log_mixer_params();
590609
/**
591610
* @brief Handles any parameter changes to the node.
592611
*
@@ -664,6 +683,8 @@ class ROSflightIO : public rclcpp::Node,
664683
rclcpp::Service<std_srvs::srv::Trigger>::SharedPtr reboot_bootloader_srv_;
665684
/// "all_params_received" ROS service.
666685
rclcpp::Service<std_srvs::srv::Trigger>::SharedPtr check_if_all_params_received_srv_;
686+
/// "print_mixing_matrix" ROS service.
687+
rclcpp::Service<std_srvs::srv::Trigger>::SharedPtr print_mixing_matrix_srv_;
667688

668689
/// Handle for the ROS2 parameter callback
669690
OnSetParametersCallbackHandle::SharedPtr parameter_callback_handle_;

rosflight_io/src/rosflight_io.cpp

Lines changed: 70 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,10 @@ ROSflightIO::ROSflightIO()
125125
"all_params_received",
126126
std::bind(&ROSflightIO::checkIfAllParamsReceivedCallback, this, std::placeholders::_1,
127127
std::placeholders::_2));
128+
print_mixing_matrix_srv_ = this->create_service<std_srvs::srv::Trigger>(
129+
"print_mixing_matrix",
130+
std::bind(&ROSflightIO::printMixingMatrixCallback, this, std::placeholders::_1,
131+
std::placeholders::_2));
128132

129133
declare_parameters();
130134
parameter_callback_handle_ = this->add_on_set_parameters_callback(std::bind(&ROSflightIO::parameters_callback, this, std::placeholders::_1));
@@ -276,6 +280,58 @@ void ROSflightIO::load_convenience_parameters()
276280
}
277281
}
278282

283+
bool ROSflightIO::log_mixer_params()
284+
{
285+
// Get the primary and secondary mixer type
286+
std::string param_name = "PRIMARY_MIXER";
287+
double primary_mixer{0};
288+
if (!mavrosflight_->param.get_param_value(param_name, &primary_mixer)) {
289+
RCLCPP_WARN(this->get_logger(), "Failed to get parameter '%s' from the firmware!", param_name.c_str());
290+
return false;
291+
}
292+
293+
param_name = "SECONDARY_MIXER";
294+
double secondary_mixer{0};
295+
if (!mavrosflight_->param.get_param_value(param_name, &secondary_mixer)) {
296+
RCLCPP_WARN(this->get_logger(), "Failed to get parameter '%s' from the firmware!", param_name.c_str());
297+
return false;
298+
}
299+
300+
// Get the relevant mixer header parameters
301+
Eigen::Matrix<double, 1, 10> mixer_header_vals{Eigen::Matrix<double,1,10>::Zero()};
302+
for (int i=0; i<10; ++i) {
303+
param_name = "PRI_MIXER_OUT_" + std::to_string(i);
304+
if (!mavrosflight_->param.get_param_value(param_name, &mixer_header_vals[i])) {
305+
RCLCPP_WARN(this->get_logger(), "Failed to get parameter '%s' from the firmware!", param_name.c_str());
306+
return false;
307+
}
308+
}
309+
310+
// Get the mixer matrix parameters
311+
Eigen::Matrix<double, 10, 10> primary_mixing_matrix{Eigen::Matrix<double,10,10>::Zero()};
312+
Eigen::Matrix<double, 10, 10> secondary_mixing_matrix{Eigen::Matrix<double,10,10>::Zero()};
313+
for (int i=0; i<10; ++i) {
314+
for (int j=0; j<10; ++j) {
315+
param_name = "PRI_MIXER_" + std::to_string(i) + "_" + std::to_string(j);
316+
if (!mavrosflight_->param.get_param_value(param_name, &primary_mixing_matrix(i,j))) {
317+
RCLCPP_WARN(this->get_logger(), "Failed to get parameter '%s' from the firmware!", param_name.c_str());
318+
return false;
319+
}
320+
321+
param_name = "SEC_MIXER_" + std::to_string(i) + "_" + std::to_string(j);
322+
if (!mavrosflight_->param.get_param_value(param_name, &secondary_mixing_matrix(i,j))) {
323+
RCLCPP_WARN(this->get_logger(), "Failed to get parameter '%s' from the firmware!", param_name.c_str());
324+
return false;
325+
}
326+
}
327+
}
328+
329+
RCLCPP_INFO_STREAM(this->get_logger(), "Mixer output channel types: " << mixer_header_vals);
330+
RCLCPP_INFO_STREAM(this->get_logger(), "Primary mixer:\n" << primary_mixing_matrix);
331+
RCLCPP_INFO_STREAM(this->get_logger(), "Secondary mixer:\n" << secondary_mixing_matrix);
332+
return true;
333+
}
334+
279335
ROSflightIO::~ROSflightIO()
280336
{
281337
delete mavrosflight_;
@@ -872,15 +928,13 @@ void ROSflightIO::commandCallback(const rosflight_msgs::msg::Command::ConstShare
872928
auto mode = (OFFBOARD_CONTROL_MODE) msg->mode;
873929
auto ignore = (OFFBOARD_CONTROL_IGNORE) msg->ignore;
874930

875-
float Qx = msg->qx;
876-
float Qy = msg->qy;
877-
float Qz = msg->qz;
878-
float Fx = msg->fx;
879-
float Fy = msg->fy;
880-
float Fz = msg->fz;
931+
float command_vect[10] = {0,0,0,0,0,0,0,0,0,0};
932+
for (int i=0; i<10; ++i) {
933+
command_vect[i] = msg->u[i];
934+
}
881935

882936
mavlink_message_t mavlink_msg;
883-
mavlink_msg_offboard_control_pack(1, 50, &mavlink_msg, mode, ignore, Qx, Qy, Qz, Fx, Fy, Fz);
937+
mavlink_msg_offboard_control_pack(1, 50, &mavlink_msg, mode, ignore, command_vect);
884938
mavrosflight_->comm.send_message(mavlink_msg);
885939
}
886940

@@ -984,6 +1038,7 @@ void ROSflightIO::paramTimerCallback()
9841038
RCLCPP_INFO(this->get_logger(), "Received all parameters");
9851039

9861040
load_convenience_parameters();
1041+
log_mixer_params();
9871042
} else {
9881043
mavrosflight_->param.request_params();
9891044
RCLCPP_ERROR(this->get_logger(),
@@ -1142,4 +1197,12 @@ bool ROSflightIO::checkIfAllParamsReceivedCallback(
11421197
return true;
11431198
}
11441199

1200+
bool ROSflightIO::printMixingMatrixCallback(
1201+
const std_srvs::srv::Trigger::Request::SharedPtr & req,
1202+
const std_srvs::srv::Trigger::Response::SharedPtr & res)
1203+
{
1204+
res->success = log_mixer_params();
1205+
return true;
1206+
}
1207+
11451208
} // namespace rosflight_io

rosflight_msgs/msg/Command.msg

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,19 @@ uint8 MODE_ROLLRATE_PITCHRATE_YAWRATE_THROTTLE = 1
66
uint8 MODE_ROLL_PITCH_YAWRATE_THROTTLE = 2
77

88
# ignore field bitmasks
9-
uint8 IGNORE_NONE = 0
10-
uint8 IGNORE_QX = 1
11-
uint8 IGNORE_QY = 2
12-
uint8 IGNORE_QZ = 4
13-
uint8 IGNORE_FX = 8
14-
uint8 IGNORE_FY = 16
15-
uint8 IGNORE_FZ = 32
9+
uint16 IGNORE_NONE = 0x0
10+
uint16 IGNORE_VALUE0 = 0x1
11+
uint16 IGNORE_VALUE1 = 0x2
12+
uint16 IGNORE_VALUE2 = 0x4
13+
uint16 IGNORE_VALUE3 = 0x8
14+
uint16 IGNORE_VALUE4 = 0x10
15+
uint16 IGNORE_VALUE5 = 0x20
16+
uint16 IGNORE_VALUE6 = 0x40
17+
uint16 IGNORE_VALUE7 = 0x80
18+
uint16 IGNORE_VALUE8 = 0x100
19+
uint16 IGNORE_VALUE9 = 0x200
1620

1721
std_msgs/Header header
18-
uint8 mode # offboard control mode for interpreting value fields
19-
uint8 ignore # bitmask for ignore specific setpoint values
20-
float32 qx
21-
float32 qy
22-
float32 qz
23-
float32 fx
24-
float32 fy
25-
float32 fz
22+
uint8 mode # offboard control mode for interpreting value fields
23+
uint16 ignore # bitmask for ignore specific setpoint values
24+
float32[10] u # Vector of control commands

rosflight_sim/include/rosflight_sim/forces_and_moments_interface.hpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,8 @@ class ForcesAndMomentsInterface : public rclcpp::Node
6464
static constexpr uint8_t NUM_MIXER_OUTPUTS = 10;
6565

6666
// Matrix to store the current mixing matrix
67-
Eigen::Matrix<double, 6, NUM_MIXER_OUTPUTS> primary_mixing_matrix_;
68-
Eigen::Matrix<double, 6, NUM_MIXER_OUTPUTS> secondary_mixing_matrix_;
67+
Eigen::Matrix<double, NUM_MIXER_OUTPUTS, NUM_MIXER_OUTPUTS> primary_mixing_matrix_;
68+
Eigen::Matrix<double, NUM_MIXER_OUTPUTS, NUM_MIXER_OUTPUTS> secondary_mixing_matrix_;
6969
Eigen::Matrix<int, 1, NUM_MIXER_OUTPUTS> mixer_header_vals_;
7070
int primary_mixer_ = 255;
7171
int secondary_mixer_ = 255;
@@ -131,7 +131,7 @@ class ForcesAndMomentsInterface : public rclcpp::Node
131131
void get_mixer_firmware_parameters();
132132
double send_get_param_service_to_firmware(std::string param_name);
133133
bool send_check_params_service_to_firmware();
134-
void invert_matrix(Eigen::Matrix<double, 6, NUM_MIXER_OUTPUTS> &mixer_to_invert);
134+
void invert_matrix(Eigen::Matrix<double, NUM_MIXER_OUTPUTS, NUM_MIXER_OUTPUTS> &mixer_to_invert);
135135

136136
public:
137137

rosflight_sim/src/forces_and_moments_interface.cpp

Lines changed: 10 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,8 @@ namespace rosflight_sim
4040

4141
ForcesAndMomentsInterface::ForcesAndMomentsInterface()
4242
: rclcpp::Node("mav_forces_and_moments")
43-
, primary_mixing_matrix_(Eigen::Matrix<double, 6, NUM_MIXER_OUTPUTS>::Zero())
44-
, secondary_mixing_matrix_(Eigen::Matrix<double, 6, NUM_MIXER_OUTPUTS>::Zero())
43+
, primary_mixing_matrix_(Eigen::Matrix<double, NUM_MIXER_OUTPUTS, NUM_MIXER_OUTPUTS>::Zero())
44+
, secondary_mixing_matrix_(Eigen::Matrix<double, NUM_MIXER_OUTPUTS, NUM_MIXER_OUTPUTS>::Zero())
4545
, mixer_header_vals_(Eigen::Matrix<int, 1, NUM_MIXER_OUTPUTS>::Zero())
4646
{
4747
// Note that we don't define the parameter callback routine here.
@@ -149,7 +149,7 @@ void ForcesAndMomentsInterface::get_mixer_firmware_parameters()
149149
}
150150

151151
// Get the mixer matrix parameters and relevant header parameters
152-
for (int i=0; i<6; ++i) {
152+
for (int i=0; i<NUM_MIXER_OUTPUTS; ++i) {
153153
for (int j=0; j<NUM_MIXER_OUTPUTS; ++j) {
154154
param_name = "PRI_MIXER_" + std::to_string(i) + "_" + std::to_string(j);
155155
param_val = send_get_param_service_to_firmware(param_name);
@@ -160,10 +160,6 @@ void ForcesAndMomentsInterface::get_mixer_firmware_parameters()
160160
secondary_mixing_matrix_(i,j) = param_val;
161161
}
162162
}
163-
164-
RCLCPP_INFO_STREAM(this->get_logger(), "Primary mixer used in sim:\n" << primary_mixing_matrix_);
165-
RCLCPP_INFO_STREAM(this->get_logger(), "Secondary mixer used in sim:\n" << secondary_mixing_matrix_);
166-
RCLCPP_INFO_STREAM(this->get_logger(), "Mixer headers used in sim:\n" << mixer_header_vals_);
167163
}
168164

169165
double ForcesAndMomentsInterface::send_get_param_service_to_firmware(std::string param_name)
@@ -225,25 +221,22 @@ bool ForcesAndMomentsInterface::send_check_params_service_to_firmware()
225221
return response->success;
226222
}
227223

228-
void ForcesAndMomentsInterface::invert_matrix(Eigen::Matrix<double, 6, NUM_MIXER_OUTPUTS> &mixer_to_invert)
224+
void ForcesAndMomentsInterface::invert_matrix(Eigen::Matrix<double, NUM_MIXER_OUTPUTS, NUM_MIXER_OUTPUTS> &mixer_to_invert)
229225
{
230226
// Calculate the pseudoinverse of the mixing matrix using the SVD
231-
Eigen::JacobiSVD<Eigen::Matrix<double, 6, NUM_MIXER_OUTPUTS>> svd(
227+
Eigen::JacobiSVD<Eigen::Matrix<double, NUM_MIXER_OUTPUTS, NUM_MIXER_OUTPUTS>> svd(
232228
mixer_to_invert,
233229
Eigen::FullPivHouseholderQRPreconditioner | Eigen::ComputeFullU | Eigen::ComputeFullV);
234-
Eigen::Matrix<double, NUM_MIXER_OUTPUTS, 6> Sig;
230+
Eigen::Matrix<double, NUM_MIXER_OUTPUTS, NUM_MIXER_OUTPUTS> Sig;
235231
Sig.setZero();
236232

237233
// Avoid dividing by zero in the Sigma matrix
238-
if (svd.singularValues()[0] != 0.0) { Sig(0, 0) = 1.0 / svd.singularValues()[0]; }
239-
if (svd.singularValues()[1] != 0.0) { Sig(1, 1) = 1.0 / svd.singularValues()[1]; }
240-
if (svd.singularValues()[2] != 0.0) { Sig(2, 2) = 1.0 / svd.singularValues()[2]; }
241-
if (svd.singularValues()[3] != 0.0) { Sig(3, 3) = 1.0 / svd.singularValues()[3]; }
242-
if (svd.singularValues()[4] != 0.0) { Sig(4, 4) = 1.0 / svd.singularValues()[4]; }
243-
if (svd.singularValues()[5] != 0.0) { Sig(5, 5) = 1.0 / svd.singularValues()[5]; }
234+
for (int i=0; i<NUM_MIXER_OUTPUTS; ++i) {
235+
if (svd.singularValues()[i] != 0.0) { Sig(i, i) = 1.0 / svd.singularValues()[i]; }
236+
}
244237

245238
// Pseudoinverse of the mixing matrix
246-
Eigen::Matrix<double, NUM_MIXER_OUTPUTS, 6> mixer_pinv = svd.matrixV() * Sig * svd.matrixU().transpose();
239+
Eigen::Matrix<double, NUM_MIXER_OUTPUTS, NUM_MIXER_OUTPUTS> mixer_pinv = svd.matrixV() * Sig * svd.matrixU().transpose();
247240

248241
// Save the inverted matrix
249242
mixer_to_invert = mixer_pinv.transpose();

0 commit comments

Comments
 (0)