O ROS 2 oferece suporte nativo à filtragem de mensagens no lado do assinante (subscriber), permitindo que apenas dados que atendam a critérios específicos disparem o callback do nó. Essa funcionalidade, conhecida como Content Filtered Topics, é extremamente eficiente pois delega a lógica de seleção ao middleware (DDS), reduzindo o processamento desnecessário na aplicação.
Implementação do Publicador
Para demonstrar essa técnica, utilizaremos um nó que simula um sensor de temperatura. Este publicador envia valores de ponto flutuante de forma incremental em um intervalo definido.
// Configurações da simulação: Início em -100.0, Fim em 150.0, Salto de 10.0
const float VALOR_MIN = -100.0f;
const float VALOR_MAX = 150.0f;
const float PASSO = 10.0f;
// Definição do Publisher com QoS padrão
auto qos_config = rclcpp::QoS(rclcpp::KeepLast(5));
sensor_pub_ = this->create_publisher<std_msgs::msg::Float32>("dados_temperatura", qos_config);
// Timer para publicação cíclica
auto enviar_leitura = [this]() -> void {
auto mensagem = std::make_unique<std_msgs::msg::Float32>();
mensagem->data = temperatura_atual_;
RCLCPP_INFO(this->get_logger(), "Enviando temperatura: %.2f", mensagem->data);
sensor_pub_->publish(std::move(mensagem));
temperatura_atual_ += PASSO;
if (temperatura_atual_ > VALOR_MAX) {
temperatura_atual_ = VALOR_MIN;
}
};
timer_ = this->create_wall_timer(std::chrono::seconds(1), enviar_leitura);
Configuração do Assinante com Filtro
A filtragem é configurada através da estrutura SubscriptionOptions. O objetivo aqui é capturar apenas temperaturas consideradas críticas (abaixo de -30°C ou acima de 100°C).
Definição das Regras de Filtro
O ROS 2 utiliza expressões similares a cláusulas WHERE do SQL. A estrutura ContentFilterOptions possui dois campos principais:
filter_expression: A string com a lógica booleana.expression_parameters: Um vetor de strings que substitui os marcadores (tokens como%0,%1) na expressão.
// Definindo limites de emergência
float limite_frio = -30.0f;
float limite_calor = 100.0f;
rclcpp::SubscriptionOptions opcoes_sub;
// A expressão filtra dados onde o campo 'data' é menor que %0 OU maior que %1
opcoes_sub.content_filter_options.filter_expression = "data < %0 OR data > %1";
opcoes_sub.content_filter_options.expression_parameters = {
std::to_string(limite_frio),
std::to_string(limite_calor)
};
Criação da Inscrição
Ao criar o assinante, passamos as opções configuradas como o quarto argumento da função create_subscription.
auto callback_emergencia = [this](const std_msgs::msg::Float32 & msg) {
RCLCPP_WARN(this->get_logger(), "ALERTA: Temperatura crítica detectada: [%.2f]", msg.data);
};
sub_emergencia_ = this->create_subscription<std_msgs::msg::Float32>(
"dados_temperatura",
10,
callback_emergencia,
opcoes_sub
);
Exemplo Completo de Implementação
Abaixo, consolidamos a lógica em um nó de monitoramento especializado em segurança térmica.
#include <memory>
#include <string>
#include <vector>
#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/float32.hpp"
class MonitorTermico : public rclcpp::Node {
public:
MonitorTermico() : Node("monitor_emergencia_node") {
auto processar_dados = [this](const std_msgs::msg::Float32::SharedPtr msg) {
RCLCPP_INFO(this->get_logger(), "Recebido dado filtrado: %.2f", msg->data);
};
// Configuração do filtro: Captura valores fora da faixa segura (-30 a 100)
rclcpp::SubscriptionOptions sub_opts;
sub_opts.content_filter_options.filter_expression = "data < %0 OR data > %1";
sub_opts.content_filter_options.expression_parameters = {"-30.0", "100.0"};
assinante_ = this->create_subscription<std_msgs::msg::Float32>(
"dados_temperatura",
10,
processar_dados,
sub_opts
);
if (assinante_->is_cft_enabled()) {
RCLCPP_INFO(this->get_logger(), "Filtro de conteúdo ativado com sucesso.");
} else {
RCLCPP_WARN(this->get_logger(), "O middleware atual não suporta filtragem de conteúdo.");
}
}
private:
rclcpp::Subscription<std_msgs::msg::Float32>::SharedPtr assinante_;
};
int main(int argc, char ** argv) {
rclcpp::init(argc, argv);
rclcpp::spin(std::make_shared<MonitorTermico>());
rclcpp::shutdown();
return 0;
}
Nesta implementação, o callback processar_dados só será invocado se o valor recebido do tópico dados_temperatura for menor que -30.0 ou maior que 100.0. Caso o publicador envie um valor como 25.0, o middleware descartará a mensagem antes mesmo dela chegar à camada de aplicação do assinante, otimizando o consumo de recursos de rede e CPU.