add error figures

This commit is contained in:
liuhaoyan 2026-08-24 18:05:46 +08:00
parent 8d2cc5cff6
commit 5c0bc20d74
8 changed files with 43569 additions and 33 deletions

View File

@ -10,6 +10,7 @@ find_package(rviz_common REQUIRED)
find_package(rviz_rendering REQUIRED) find_package(rviz_rendering REQUIRED)
find_package(pluginlib REQUIRED) find_package(pluginlib REQUIRED)
find_package(geometry_msgs REQUIRED) find_package(geometry_msgs REQUIRED)
find_package(nav_msgs REQUIRED)
find_package(tf2 REQUIRED) find_package(tf2 REQUIRED)
find_package(Qt5 REQUIRED COMPONENTS Widgets) find_package(Qt5 REQUIRED COMPONENTS Widgets)
@ -18,11 +19,13 @@ set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTORCC ON) set(CMAKE_AUTORCC ON)
set(HEADERS set(HEADERS
include/sweet_rviz_panel/qcustomplot.h
include/sweet_rviz_panel/sweet_rviz_panel.hpp include/sweet_rviz_panel/sweet_rviz_panel.hpp
) )
set(SOURCES set(SOURCES
src/sweet_rviz_panel.cpp src/sweet_rviz_panel.cpp
src/qcustomplot.cpp
) )
add_library(${PROJECT_NAME} SHARED add_library(${PROJECT_NAME} SHARED
@ -42,6 +45,7 @@ ament_target_dependencies(${PROJECT_NAME}
rviz_rendering rviz_rendering
pluginlib pluginlib
geometry_msgs geometry_msgs
nav_msgs
tf2 tf2
) )

BIN
QCustomPlot.tar.gz Normal file

Binary file not shown.

View File

@ -5,8 +5,8 @@ This package aims to add another panel in rviz to make some essential parametres
![final result](./rviz2.png) ![final result](./rviz2.png)
# installation # installation
sudo apt install libcurl4-openssl-dev 1. sudo apt install libcurl4-openssl-dev
sudo apt install nlohmann-json3-dev 2. sudo apt install nlohmann-json3-dev
# build and run # build and run
1. colcon build 1. colcon build

File diff suppressed because it is too large Load Diff

View File

@ -2,20 +2,26 @@
#define SWEET_RVIZ_PANEL_HPP #define SWEET_RVIZ_PANEL_HPP
#include <memory> #include <memory>
#include <QTimer>
#include <QLabel> #include <QLabel>
#include <QPushButton> #include <QPushButton>
#include <QVBoxLayout> #include <QVBoxLayout>
#include <QHBoxLayout> #include <QHBoxLayout>
#include <QWidget>
#include <QVector>
#include <QElapsedTimer>
#include <rviz_common/panel.hpp> #include <rviz_common/panel.hpp>
#include "sweet_rviz_panel/qcustomplot.h"
#include <rclcpp/rclcpp.hpp> #include <rclcpp/rclcpp.hpp>
#include "geometry_msgs/msg/twist.hpp" #include "geometry_msgs/msg/twist.hpp"
#include "geometry_msgs/msg/pose.hpp" #include "geometry_msgs/msg/pose.hpp"
#include <nav_msgs/msg/path.hpp>
#include <std_msgs/msg/float64.hpp>
#include <rviz_common/display_context.hpp> #include <rviz_common/display_context.hpp>
#include <rviz_common/ros_integration/ros_node_abstraction_iface.hpp> #include <rviz_common/ros_integration/ros_node_abstraction_iface.hpp>
class QCustomPlot;
class QCPGraph;
namespace sweet_rviz_panel namespace sweet_rviz_panel
{ {
@ -34,10 +40,13 @@ private Q_SLOTS:
void onStop(); void onStop();
private: private:
void poseCallback( void poseCallback(const geometry_msgs::msg::Pose::SharedPtr msg);
const geometry_msgs::msg::Pose::SharedPtr msg); void velCmdCallback(const geometry_msgs::msg::Twist::SharedPtr msg);
void velCmdCallback( void pathCallback(const nav_msgs::msg::Path::SharedPtr msg);
const geometry_msgs::msg::Twist::SharedPtr msg); void calculateErrors();
void updateErrorPlots();
void initPlots();
QLabel *x_label_; QLabel *x_label_;
QLabel *y_label_; QLabel *y_label_;
QLabel *yaw_label_; QLabel *yaw_label_;
@ -45,15 +54,32 @@ private:
QLabel *vel_cmd_label_; QLabel *vel_cmd_label_;
QLabel *yaw_cmd_label_; QLabel *yaw_cmd_label_;
QPushButton *start_button_; QPushButton *start_button_;
QPushButton *stop_button_; QPushButton *stop_button_;
QTimer *timer_;
QCustomPlot *lateral_plot_;
QCustomPlot *heading_plot_;
rclcpp::Node::SharedPtr node_; rclcpp::Node::SharedPtr node_;
std::shared_ptr< std::shared_ptr<
rviz_common::ros_integration::RosNodeAbstractionIface> node_ptr_; rviz_common::ros_integration::RosNodeAbstractionIface> node_ptr_;
rclcpp::Subscription<geometry_msgs::msg::Pose>::SharedPtr pose_sub_; rclcpp::Subscription<geometry_msgs::msg::Pose>::SharedPtr pose_sub_;
rclcpp::Subscription<geometry_msgs::msg::Twist>::SharedPtr twist_cmd_sub_; rclcpp::Subscription<geometry_msgs::msg::Twist>::SharedPtr twist_cmd_sub_;
rclcpp::Subscription<nav_msgs::msg::Path>::SharedPtr odom_path_sub_;
double current_x_ = 0.0;
double current_y_ = 0.0;
double current_yaw_ = 0.0;
bool pose_received_ = false;
bool path_received_ = false;
nav_msgs::msg::Path current_path_;
QVector<double> error_time_;
QVector<double> lateral_errors_;
QVector<double> heading_errors_;
QElapsedTimer error_timer_;
static constexpr int MAX_ERROR_POINTS = 1000;
double search_distance_ = 10.0;
int start_index_=0;
int forward_index_ = 100;
bool running_; bool running_;
}; };

View File

@ -15,6 +15,7 @@
<depend>rviz_rendering</depend> <depend>rviz_rendering</depend>
<depend>pluginlib</depend> <depend>pluginlib</depend>
<depend>geometry_msgs</depend> <depend>geometry_msgs</depend>
<depend>nav_msgs</depend>
<export> <export>
<build_type>ament_cmake</build_type> <build_type>ament_cmake</build_type>

35529
src/qcustomplot.cpp Normal file

File diff suppressed because it is too large Load Diff

View File

@ -39,7 +39,7 @@ SweetRvizPanel::SweetRvizPanel(QWidget *parent)
// command // command
auto *vel_cmd_box = new QGroupBox("Command Info"); auto *vel_cmd_box = new QGroupBox("Command Info");
auto *vel_cmd_layout = new QVBoxLayout; auto *vel_cmd_layout = new QVBoxLayout;
vel_cmd_label_ = new QLabel("Velocity Command: 0.00 m/s"); vel_cmd_label_ = new QLabel("Vel Command: 0.00 m/s");
yaw_cmd_label_ = new QLabel("Yaw Command: 0.00 rad/s"); yaw_cmd_label_ = new QLabel("Yaw Command: 0.00 rad/s");
vel_cmd_layout->addWidget(vel_cmd_label_); vel_cmd_layout->addWidget(vel_cmd_label_);
@ -56,14 +56,19 @@ SweetRvizPanel::SweetRvizPanel(QWidget *parent)
status_label_ = new QLabel("Status: STOPPED"); status_label_ = new QLabel("Status: STOPPED");
lateral_plot_ = new QCustomPlot(this);
heading_plot_ = new QCustomPlot(this);
main_layout->addWidget(pose_box); main_layout->addWidget(pose_box);
main_layout->addWidget(vel_cmd_box); main_layout->addWidget(vel_cmd_box);
main_layout->addLayout(button_layout); main_layout->addWidget(lateral_plot_,1);
main_layout->addWidget(heading_plot_,1);
//main_layout->addLayout(button_layout);
main_layout->addWidget(status_label_); main_layout->addWidget(status_label_);
main_layout->addStretch(); main_layout->addStretch();
setLayout(main_layout); setLayout(main_layout);
initPlots();
connect( connect(
start_button_, start_button_,
SIGNAL(clicked()), SIGNAL(clicked()),
@ -75,8 +80,66 @@ SweetRvizPanel::SweetRvizPanel(QWidget *parent)
SIGNAL(clicked()), SIGNAL(clicked()),
this, this,
SLOT(onStop())); SLOT(onStop()));
}
error_timer_.start();
timer_ = new QTimer(this);
connect(
timer_,
&QTimer::timeout,
this,
&SweetRvizPanel::calculateErrors);
timer_->start(100);
}
void SweetRvizPanel::initPlots()
{
lateral_plot_->addGraph();
lateral_plot_->graph(0)->setName(
"Lateral Error");
lateral_plot_->xAxis->setLabel(
"Time (s)");
lateral_plot_->yAxis->setLabel(
"Lateral Error (m)");
lateral_plot_->xAxis->setRange(
0,
30);
lateral_plot_->yAxis->setRange(
-1,
1);
QPen lateral_pen(Qt::blue);
lateral_pen.setWidth(3);
lateral_plot_->graph(0)->setPen(lateral_pen);
lateral_plot_->legend->setVisible(true);
heading_plot_->addGraph();
heading_plot_->graph(0)->setName(
"Heading Error");
heading_plot_->xAxis->setLabel(
"Time (s)");
heading_plot_->yAxis->setLabel(
"Heading Error (rad)");
heading_plot_->xAxis->setRange(
0,
30);
heading_plot_->yAxis->setRange(
-0.5,
0.5);
QPen heading_pen(Qt::red);
heading_pen.setWidth(3);
heading_plot_->graph(0)->setPen(heading_pen);
heading_plot_->legend->setVisible(true);
}
void SweetRvizPanel::onInitialize() void SweetRvizPanel::onInitialize()
{ {
node_ = std::make_shared<rclcpp::Node>("sweet_rviz_panel"); node_ = std::make_shared<rclcpp::Node>("sweet_rviz_panel");
@ -90,18 +153,18 @@ void SweetRvizPanel::onInitialize()
} }
auto node = node_ptr_->get_raw_node(); auto node = node_ptr_->get_raw_node();
pose_sub_ = node->create_subscription< pose_sub_ = node->create_subscription<geometry_msgs::msg::Pose>(
geometry_msgs::msg::Pose>( "/local_pose",10,
"/rtk_pose",
10,
std::bind(&SweetRvizPanel::poseCallback,this,std::placeholders::_1)); std::bind(&SweetRvizPanel::poseCallback,this,std::placeholders::_1));
twist_cmd_sub_ = node->create_subscription< twist_cmd_sub_ = node->create_subscription<geometry_msgs::msg::Twist>(
geometry_msgs::msg::Twist>( "/twist_cmd_vel",10,
"/twist_cmd_vel",
10,
std::bind(&SweetRvizPanel::velCmdCallback,this,std::placeholders::_1)); std::bind(&SweetRvizPanel::velCmdCallback,this,std::placeholders::_1));
odom_path_sub_ = node->create_subscription<nav_msgs::msg::Path>(
"/odom_path",10,
std::bind(&SweetRvizPanel::pathCallback,this,std::placeholders::_1));
running_ = true; running_ = true;
status_label_->setText("Status: RUNNING"); status_label_->setText("Status: RUNNING");
} }
@ -115,7 +178,6 @@ void SweetRvizPanel::poseCallback(
const double x = msg->position.x; const double x = msg->position.x;
const double y = msg->position.y; const double y = msg->position.y;
const auto &q = msg->orientation; const auto &q = msg->orientation;
tf2::Quaternion quaternion( tf2::Quaternion quaternion(
@ -132,15 +194,14 @@ void SweetRvizPanel::poseCallback(
roll, roll,
pitch, pitch,
yaw); yaw);
pose_received_ = true;
current_x_ = x;
current_y_ = y;
current_yaw_ = yaw;
x_label_->setText(QString("X: %1 m").arg(x, 0, 'f', 3));
y_label_->setText(QString("Y: %1 m").arg(y, 0, 'f', 3));
yaw_label_->setText(QString("Yaw: %1 rad").arg(yaw, 0, 'f', 3));
x_label_->setText(
QString("X: %1 m").arg(x, 0, 'f', 3));
y_label_->setText(
QString("Y: %1 m").arg(y, 0, 'f', 3));
yaw_label_->setText(
QString("Yaw: %1 rad").arg(yaw, 0, 'f', 3));
} }
void SweetRvizPanel::velCmdCallback( void SweetRvizPanel::velCmdCallback(
@ -156,6 +217,147 @@ void SweetRvizPanel::velCmdCallback(
yaw_cmd_label_->setText( yaw_cmd_label_->setText(
QString("Yaw Command: %1 rad/s").arg(msg->angular.z, 0, 'f', 3)); QString("Yaw Command: %1 rad/s").arg(msg->angular.z, 0, 'f', 3));
} }
void SweetRvizPanel::pathCallback(
const nav_msgs::msg::Path::SharedPtr msg)
{
if (msg->poses.empty())
return;
current_path_ = *msg;
path_received_ = true;
calculateErrors();
}
void SweetRvizPanel::calculateErrors()
{
if (!pose_received_ || !path_received_)
return;
if (current_path_.poses.empty())
return;
double min_distance =std::numeric_limits<double>::max();
int nearest_index = -1;
for (size_t i = start_index_;i < current_path_.poses.size() &&
i < start_index_ + forward_index_;++i)
{
const auto &pose =current_path_.poses[i].pose;
const double dx =pose.position.x - current_x_;
const double dy =pose.position.y - current_y_;
const double distance =std::sqrt(dx * dx + dy * dy);
if (distance > search_distance_)
continue;
if (distance < min_distance)
{
min_distance = distance;
nearest_index =
static_cast<int>(i);
start_index_ = nearest_index;
}
}
if (nearest_index < 0)
return;
const auto &target =current_path_.poses[nearest_index].pose;
const auto &q =target.orientation;
tf2::Quaternion target_q(
q.x,
q.y,
q.z,
q.w);
double roll;
double pitch;
double target_yaw;
tf2::Matrix3x3(target_q).getRPY(
roll,
pitch,
target_yaw);
const double dx =current_x_ - target.position.x;
const double dy =current_y_ - target.position.y;
const double lateral_error =
-std::sin(target_yaw) * dx
+ std::cos(target_yaw) * dy;
double heading_error =
current_yaw_ - target_yaw;
while (heading_error > M_PI)
heading_error -= 2.0 * M_PI;
while (heading_error < -M_PI)
heading_error += 2.0 * M_PI;
const double t =
error_timer_.elapsed() / 1000.0;
error_time_.push_back(t);
lateral_errors_.push_back(lateral_error);
heading_errors_.push_back(heading_error);
if (error_time_.size() > MAX_ERROR_POINTS)
{
error_time_.remove(0);
lateral_errors_.remove(0);
heading_errors_.remove(0);
}
updateErrorPlots();
}
void SweetRvizPanel::updateErrorPlots()
{
if (!pose_received_) {
RCLCPP_INFO_THROTTLE(
node_->get_logger(),
*node_->get_clock(),
1000,
"NO POSE");
return;
}
if (error_time_.isEmpty())
return;
lateral_plot_->graph(0)->setData(error_time_,lateral_errors_);
heading_plot_->graph(0)->setData(error_time_,heading_errors_);
const double t_end =error_time_.last();
const double t_start = std::max(0.0, t_end - 30.0);
lateral_plot_->xAxis->setRange(t_start,std::max(t_end, t_start + 1.0));
heading_plot_->xAxis->setRange(t_start,std::max(t_end, t_start + 1.0));
double max_lateral = 0.1;
for (double e : lateral_errors_)
{
if (std::abs(e) > max_lateral)
max_lateral = std::abs(e);
}
lateral_plot_->yAxis->setRange(-max_lateral * 1.2,max_lateral * 1.2);
double max_heading = 0.01;
for (double e : heading_errors_)
{
if (std::abs(e) > max_heading)
max_heading = std::abs(e);
}
heading_plot_->yAxis->setRange(
-max_heading * 1.2,
max_heading * 1.2);
lateral_plot_->replot(QCustomPlot::rpQueuedReplot);
heading_plot_->replot(QCustomPlot::rpQueuedReplot);
}
void SweetRvizPanel::onStart() void SweetRvizPanel::onStart()
{ {